diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java index f2187b8..30c94c1 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java @@ -194,7 +194,7 @@ public class AddDialog extends JDialog { }; patientTable = new JTable(patientTableModel); - patientTable.setFont(new Font("Serif", Font.BOLD, 20)); + patientTable.setFont(new Font("Serif", Font.PLAIN, 14)); patientTable.setRowHeight(30); patientTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JScrollPane scrollPane = new JScrollPane(patientTable); diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java index bdecc68..2a04f65 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java @@ -27,11 +27,14 @@ import org.slf4j.LoggerFactory; import javax.swing.*; import javax.swing.border.TitledBorder; import javax.swing.table.DefaultTableModel; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.text.DecimalFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -58,14 +61,14 @@ public class CheckUpPage extends JPanel { private JComboBox doctorComboBox, statusComboBox, genderComboBox, provinceComboBox, districtComboBox, wardComboBox, checkupTypeComboBox; private JSpinner customerWeightSpinner, customerHeightSpinner; private JDatePickerImpl datePicker, dobPicker; - private String[][] medicinePrescription; - private String[][] servicePrescription; + private String[][] medicinePrescription = new String[0][0]; // Initialize to empty + private String[][] servicePrescription = new String[0][0]; // Initialize to empty private String[] doctorOptions; private MedicineDialog medDialog = null; private ServiceDialog serDialog = null; private AddDialog addDialog = null; private int previousSelectedRow = -1; - private boolean saved = false; + private boolean saved = true; // Initially true, changed when patient selected or dialog opened. private DefaultComboBoxModel districtModel, wardModel; private JComboBox callRoomComboBox; private JButton callPatientButton; @@ -73,6 +76,16 @@ public class CheckUpPage extends JPanel { private QueueViewPage tvQueueFrame; + // New UI components for prescription display + private JPanel prescriptionDisplayPanel; + private JTree prescriptionTree; + private DefaultTreeModel prescriptionTreeModel; + private DefaultMutableTreeNode rootPrescriptionNode; + private JLabel totalMedCostLabel; + private JLabel totalSerCostLabel; + private JLabel overallTotalCostLabel; + private static final DecimalFormat df = new DecimalFormat("#,##0"); + boolean returnCell = false; public void updateQueue() { NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCheckUpQueueRequest()); @@ -94,8 +107,6 @@ public class CheckUpPage extends JPanel { tableData[i][2] = p.getCustomerLastName(); tableData[i][3] = p.getCustomerFirstName(); tableData[i][4] = p.getDoctorName(); - //tableData[i][5] = p.getDiagnosis(); - //tableData[i][6] = p.getNotes(); tableData[i][5] = p.getCheckupType(); } return tableData; @@ -140,18 +151,15 @@ public class CheckUpPage extends JPanel { updateQueue(); // Navigation bar - // navBar panel JPanel navBar = new JPanel(); navBar.setBackground(new Color(63, 81, 181)); - navBar.setLayout(new BoxLayout(navBar, BoxLayout.X_AXIS)); // Use BoxLayout for horizontal layout + navBar.setLayout(new BoxLayout(navBar, BoxLayout.X_AXIS)); - // Left-aligned navigation items (centered vertically) JPanel navItemsPanel = new JPanel(); navItemsPanel.setBackground(new Color(63, 81, 181)); - navItemsPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 10)); // Items on the left - navItemsPanel.setAlignmentY(Component.CENTER_ALIGNMENT); // Vertically center the panel in navBar + navItemsPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 10)); + navItemsPanel.setAlignmentY(Component.CENTER_ALIGNMENT); - // Add navigation items String[] navBarItems = {"Thống kê", "Thăm khám", "Dữ liệu", "Kho", "Người dùng", "Thông tin"}; String[] destination = {"DashboardPage", "CheckUpPage", "PatientDataPage", "InventoryPage", "UserPage", "InfoPage"}; for (int i = 0; i < navBarItems.length; i++) { @@ -160,151 +168,106 @@ public class CheckUpPage extends JPanel { 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.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); label.setFont(new Font("Arial", Font.BOLD, 14)); - - // Add padding for visibility label.setBorder(BorderFactory.createEmptyBorder(7, 15, 10, 15)); - // Highlight the selected label if (item.equals("Thăm khám")) { - label.setBorder(BorderFactory.createMatteBorder(0, 0, 5, 0, new Color(33, 150, 243))); // Add bottom border + label.setBorder(BorderFactory.createMatteBorder(0, 0, 5, 0, new Color(33, 150, 243))); } - - // 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); - // Reset background color for all labels for (Component comp : navItemsPanel.getComponents()) { JLabel lbl = (JLabel) comp; - lbl.setBackground(null); // Reset background - lbl.setOpaque(false); // Remove background color + lbl.setBackground(null); + lbl.setOpaque(false); } - // Set background for the clicked label - label.setBackground(new Color(33, 150, 243)); // Highlight clicked label + label.setBackground(new Color(33, 150, 243)); label.setOpaque(true); - label.setForeground(Color.WHITE); // Change text color to white + label.setForeground(Color.WHITE); } - @Override public void mouseEntered(java.awt.event.MouseEvent e) { - label.setForeground(new Color(200, 230, 255)); // Highlight on hover - label.setBackground(new Color(33, 150, 243)); // Highlight the button on hover - label.setOpaque(true); // Make the background visible on hover + label.setForeground(new Color(200, 230, 255)); + label.setBackground(new Color(33, 150, 243)); + label.setOpaque(true); } - @Override public void mouseExited(java.awt.event.MouseEvent e) { - if (label.getBackground() != new Color(33, 150, 243)) { // Only reset if it's not selected - label.setForeground(Color.WHITE); // Restore original color - label.setBackground(null); // Remove background color - label.setOpaque(false); // Make the background invisible + if (label.getBackground() != new Color(33, 150, 243)) { + label.setForeground(Color.WHITE); + label.setBackground(null); + label.setOpaque(false); } } }); - navItemsPanel.add(label); } - // Add a space between the navigation items and the "Welcome" label (this will push the welcome label to the far right) navBar.add(navItemsPanel); - navBar.add(Box.createHorizontalGlue()); // This will push the welcome label to the right + navBar.add(Box.createHorizontalGlue()); - // Right-aligned "Welcome" label - JLabel welcomeLabel = new JLabel("Welcome, " + LocalStorage.username + " "); + JLabel welcomeLabel = new JLabel("Chào, " + LocalStorage.username + " "); welcomeLabel.setForeground(Color.WHITE); welcomeLabel.setHorizontalAlignment(SwingConstants.CENTER); - welcomeLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); // Make clickable + welcomeLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); welcomeLabel.setFont(new Font("Arial", Font.BOLD, 14)); - - // Add mouse interactions for the welcomeLabel welcomeLabel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - if (SwingUtilities.isLeftMouseButton(e)) { // Check if it's a left-click + if (SwingUtilities.isLeftMouseButton(e)) { JPopupMenu popupMenu = new JPopupMenu(); - - JMenuItem profileItem = new JMenuItem("User Profile"); - JMenuItem settingsItem = new JMenuItem("Settings"); - JMenuItem logoutItem = new JMenuItem("Logout"); + JMenuItem profileItem = new JMenuItem("Hồ sơ cá nhân"); + JMenuItem settingsItem = new JMenuItem("Cài đặt"); + JMenuItem logoutItem = new JMenuItem("Đăng xuất"); profileItem.addActionListener(event -> { - System.out.println("User Profile selected"); - // Placeholder for profile action - JOptionPane.showMessageDialog(mainFrame, "User Profile feature coming soon!", "Info", JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog(mainFrame, "Tính năng Hồ sơ cá nhân sắp ra mắt!", "Thông báo", JOptionPane.INFORMATION_MESSAGE); }); - settingsItem.addActionListener(event -> { - System.out.println("Settings selected"); - // Placeholder for settings action - JOptionPane.showMessageDialog(mainFrame, "Settings feature coming soon!", "Info", JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog(mainFrame, "Tính năng Cài đặt sắp ra mắt!", "Thông báo", JOptionPane.INFORMATION_MESSAGE); }); - logoutItem.addActionListener(event -> { - System.out.println("Logout selected"); - // Send LogoutRequest to server NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new LogoutRequest()); - - // Clear local storage LocalStorage.username = null; - LocalStorage.userId = -1; // Or whatever your default/unauthenticated user ID is - // Clear any other sensitive session data from LocalStorage - - // Navigate to LandingPage + LocalStorage.userId = -1; mainFrame.showPage("LandingPage"); }); - - // Add items to the popup menu popupMenu.add(profileItem); popupMenu.add(settingsItem); - popupMenu.addSeparator(); // Adds a visual separator before logout + popupMenu.addSeparator(); popupMenu.add(logoutItem); - - // Set preferred width after items are added, height will be based on content int currentPreferredHeight = popupMenu.getPreferredSize().height; popupMenu.setPreferredSize(new Dimension(150, currentPreferredHeight)); - - // Show the popup popupMenu.show(welcomeLabel, 0, welcomeLabel.getHeight()); } } - @Override public void mouseEntered(MouseEvent e) { - welcomeLabel.setForeground(new Color(200, 230, 255)); // Highlight on hover + welcomeLabel.setForeground(new Color(200, 230, 255)); } - @Override public void mouseExited(MouseEvent e) { - welcomeLabel.setForeground(Color.WHITE); // Restore original color + welcomeLabel.setForeground(Color.WHITE); } }); - - // Add the welcome label to the navBar (it will be on the far right due to the horizontal glue) navBar.add(welcomeLabel, BorderLayout.EAST); - - // Set preferred size for the navBar navBar.setPreferredSize(new Dimension(1200, 50)); - // Now the "Welcome" label is aligned to the right with space between the nav items - - - - // Data table inside a RoundedPanel + // Panels RoundedPanel leftPanel = new RoundedPanel(20, Color.WHITE, false); - RoundedPanel rightTopPanel = new RoundedPanel(20, Color.WHITE, false); - RoundedPanel rightBottomPanel = new RoundedPanel(20, Color.WHITE, false); + RoundedPanel rightTopPanel = new RoundedPanel(20, Color.WHITE, false); // History Panel + prescriptionDisplayPanel = new JPanel(new BorderLayout(5,5)); // New Prescription Panel + RoundedPanel rightBottomPanel = new RoundedPanel(20, Color.WHITE, false); // Details/Actions Panel JPanel topPanel = new JPanel(new BorderLayout()); - JLabel titleText1 = new JLabel(); - titleText1.setText("Check Up Queue 1"); + titleText1.setText("Danh sách chờ khám"); titleText1.setFont(new Font("Arial", Font.BOLD, 16)); titleText1.setBackground(Color.WHITE); - titleText1.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Padding + titleText1.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); callingStatusLabel = new JLabel(" ", SwingConstants.CENTER); callingStatusLabel.setFont(new Font("Arial", Font.BOLD, 15)); @@ -316,31 +279,28 @@ public class CheckUpPage extends JPanel { callingStatusLabel.setOpaque(true); callingStatusLabel.setBackground(new Color(230, 255, 230)); - JButton tvQueueButton = new JButton("TV Queue Display"); - tvQueueButton.setBackground(new Color(0, 150, 136)); // A teal color for distinction + JButton tvQueueButton = new JButton("Màn hình chờ TV"); + tvQueueButton.setBackground(new Color(0, 150, 136)); tvQueueButton.setForeground(Color.WHITE); tvQueueButton.setFocusPainted(false); tvQueueButton.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 15)); tvQueueButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - tvQueueButton.addActionListener(e -> { if (tvQueueFrame == null || !tvQueueFrame.isDisplayable()) { tvQueueFrame = new QueueViewPage(); } else { - tvQueueFrame.toFront(); // Bring to front if already open + tvQueueFrame.toFront(); } - tvQueueFrame.updateQueueData(this.rawQueueForTv); // Use rawQueueForTv for TV display + tvQueueFrame.updateQueueData(this.rawQueueForTv); tvQueueFrame.setVisible(true); - // tvQueueFrame.optimizeForTv(); // Optional: Call if you want it to try to go fullscreen }); - JButton rightButton = new JButton(" ADD "); + JButton rightButton = new JButton(" THÊM BN "); rightButton.setBackground(new Color(63, 81, 181)); rightButton.setForeground(Color.WHITE); rightButton.setFocusPainted(false); - rightButton.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); // Padding + rightButton.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); rightButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - rightButton.addActionListener(e -> { addDialog = new AddDialog(mainFrame); addDialog.setVisible(true); @@ -350,7 +310,7 @@ public class CheckUpPage extends JPanel { topPanel.add(titleText1, BorderLayout.WEST); topPanel.add(callingStatusLabel, BorderLayout.CENTER); JPanel buttonPanelEast = new JPanel(new FlowLayout(FlowLayout.RIGHT)); - buttonPanelEast.setOpaque(false); // Make panel transparent + buttonPanelEast.setOpaque(false); buttonPanelEast.add(tvQueueButton); buttonPanelEast.add(rightButton); topPanel.add(buttonPanelEast, BorderLayout.EAST); @@ -359,38 +319,38 @@ public class CheckUpPage extends JPanel { leftPanel.add(topPanel, BorderLayout.NORTH); leftPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 10)); - - rightBottomPanel.setLayout(new BorderLayout()); - rightBottomPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 10)); - String[] columns = {"Mã khám bệnh", "Ngày Tháng", "Họ", "Tên", "Bác Sĩ", "Loại khám"}; model = new DefaultTableModel(preprocessPatientDataForTable(this.patientQueue), columns) { @Override - public boolean isCellEditable(int row, int column) { - return false; // All cells are non-editable - } + public boolean isCellEditable(int row, int column) { return false; } }; table1 = new JTable(model); - - // Set preferred size for the table table1.setPreferredScrollableViewportSize(new Dimension(400, 200)); - - // Customize the font for the table header and cells table1.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); table1.setFont(new Font("Arial", Font.PLAIN, 12)); table1.setRowHeight(25); - JScrollPane tableScroll1 = new JScrollPane(table1); - tableScroll1.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Inner padding + tableScroll1.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); leftPanel.add(tableScroll1, BorderLayout.CENTER); + // Right Bottom Panel (Details and Actions) - Setup remains similar + rightBottomPanel.setLayout(new BorderLayout()); + rightBottomPanel.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createEmptyBorder(5, 5, 5, 5), + BorderFactory.createTitledBorder( + BorderFactory.createLineBorder(new Color(63, 81, 181), 1, true), + "Chi tiết và Thao tác", + TitledBorder.LEADING, TitledBorder.TOP, + new Font("Arial", Font.BOLD, 16), new Color(63, 81, 181) + ) + )); + JPanel inputPanel = new JPanel(); inputPanel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(5, 5, 5, 5); // Add some padding between components + gbc.insets = new Insets(5, 5, 5, 5); gbc.fill = GridBagConstraints.HORIZONTAL; - // --- Patient Info Sub-Panel with TitledBorder --- JPanel patientInfoInnerPanel = new JPanel(new GridBagLayout()); patientInfoInnerPanel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), "Thông tin bệnh nhân", @@ -401,62 +361,19 @@ public class CheckUpPage extends JPanel { gbcPatient.insets = new Insets(3, 3, 3, 3); gbcPatient.fill = GridBagConstraints.HORIZONTAL; - gbcPatient.gridwidth = 1; - gbcPatient.gridx = 0; - gbcPatient.gridy = 0; - patientInfoInnerPanel.add(new JLabel("Họ"), gbcPatient); - gbcPatient.gridx = 1; - customerLastNameField = new JTextField(10); // Adjusted preferred size - patientInfoInnerPanel.add(customerLastNameField, gbcPatient); - gbcPatient.gridx = 2; - patientInfoInnerPanel.add(new JLabel("Tên"), gbcPatient); - gbcPatient.gridx = 3; - customerFirstNameField = new JTextField(10); // Adjusted preferred size - patientInfoInnerPanel.add(customerFirstNameField, gbcPatient); - - gbcPatient.gridx = 0; - gbcPatient.gridy++; - patientInfoInnerPanel.add(new JLabel("Giới tính"), gbcPatient); - gbcPatient.gridx = 1; - String[] genderOptions = {"Nam", "Nữ"}; - genderComboBox = new JComboBox<>(genderOptions); - patientInfoInnerPanel.add(genderComboBox, gbcPatient); - gbcPatient.gridx = 2; - patientInfoInnerPanel.add(new JLabel("Ngày sinh"), gbcPatient); - gbcPatient.gridx = 3; - UtilDateModel dobModel = new UtilDateModel(); - Properties dobProperties = new Properties(); - dobProperties.put("text.today", "Today"); - dobProperties.put("text.month", "Month"); - dobProperties.put("text.year", "Year"); - JDatePanelImpl dobPanel = new JDatePanelImpl(dobModel, dobProperties); - dobPicker = new JDatePickerImpl(dobPanel, new DateLabelFormatter()); - dobPicker.setPreferredSize(new Dimension(120, 30)); // Adjusted preferred size - patientInfoInnerPanel.add(dobPicker, gbcPatient); - - gbcPatient.gridx = 0; - gbcPatient.gridy++; - patientInfoInnerPanel.add(new JLabel("Địa chỉ"), gbcPatient); - gbcPatient.gridx = 1; - gbcPatient.gridwidth = 3; - customerAddressField = new JTextField(); - patientInfoInnerPanel.add(customerAddressField, gbcPatient); - - gbcPatient.gridy++; - gbcPatient.gridx = 1; // Aligned with address field above - gbcPatient.gridwidth = 1; - provinceComboBox = new JComboBox<>(LocalStorage.provinces); - patientInfoInnerPanel.add(provinceComboBox, gbcPatient); - gbcPatient.gridx = 2; - districtModel = new DefaultComboBoxModel<>(new String[]{"Huyện"}); // Placeholder for district - districtComboBox = new JComboBox<>(districtModel); - patientInfoInnerPanel.add(districtComboBox, gbcPatient); - districtComboBox.setEnabled(false); - gbcPatient.gridx = 3; - wardModel = new DefaultComboBoxModel<>(new String[]{"Phường"}); // Placeholder for ward - wardComboBox = new JComboBox<>(wardModel); - patientInfoInnerPanel.add(wardComboBox, gbcPatient); - wardComboBox.setEnabled(false); + gbcPatient.gridwidth = 1; gbcPatient.gridx = 0; gbcPatient.gridy = 0; patientInfoInnerPanel.add(new JLabel("Họ"), gbcPatient); + gbcPatient.gridx = 1; customerLastNameField = new JTextField(10); patientInfoInnerPanel.add(customerLastNameField, gbcPatient); + gbcPatient.gridx = 2; patientInfoInnerPanel.add(new JLabel("Tên"), gbcPatient); + gbcPatient.gridx = 3; customerFirstNameField = new JTextField(10); patientInfoInnerPanel.add(customerFirstNameField, gbcPatient); + gbcPatient.gridx = 0; gbcPatient.gridy++; patientInfoInnerPanel.add(new JLabel("Giới tính"), gbcPatient); + gbcPatient.gridx = 1; String[] genderOptions = {"Nam", "Nữ"}; genderComboBox = new JComboBox<>(genderOptions); patientInfoInnerPanel.add(genderComboBox, gbcPatient); + gbcPatient.gridx = 2; patientInfoInnerPanel.add(new JLabel("Ngày sinh"), gbcPatient); + gbcPatient.gridx = 3; UtilDateModel dobModel = new UtilDateModel(); Properties dobProperties = new Properties(); dobProperties.put("text.today", "Hôm nay"); dobProperties.put("text.month", "Tháng"); dobProperties.put("text.year", "Năm"); JDatePanelImpl dobPanel = new JDatePanelImpl(dobModel, dobProperties); dobPicker = new JDatePickerImpl(dobPanel, new DateLabelFormatter()); dobPicker.setPreferredSize(new Dimension(120, 30)); patientInfoInnerPanel.add(dobPicker, gbcPatient); + gbcPatient.gridx = 0; gbcPatient.gridy++; patientInfoInnerPanel.add(new JLabel("Địa chỉ"), gbcPatient); + gbcPatient.gridx = 1; gbcPatient.gridwidth = 3; customerAddressField = new JTextField(); patientInfoInnerPanel.add(customerAddressField, gbcPatient); + gbcPatient.gridy++; gbcPatient.gridx = 1; gbcPatient.gridwidth = 1; provinceComboBox = new JComboBox<>(LocalStorage.provinces); patientInfoInnerPanel.add(provinceComboBox, gbcPatient); + gbcPatient.gridx = 2; districtModel = new DefaultComboBoxModel<>(new String[]{"Huyện"}); districtComboBox = new JComboBox<>(districtModel); patientInfoInnerPanel.add(districtComboBox, gbcPatient); districtComboBox.setEnabled(false); + gbcPatient.gridx = 3; wardModel = new DefaultComboBoxModel<>(new String[]{"Phường"}); wardComboBox = new JComboBox<>(wardModel); patientInfoInnerPanel.add(wardComboBox, gbcPatient); wardComboBox.setEnabled(false); provinceComboBox.addActionListener(e -> { String selectedProvince = (String) provinceComboBox.getSelectedItem(); @@ -464,79 +381,38 @@ public class CheckUpPage extends JPanel { String provinceId = LocalStorage.provinceToId.get(selectedProvince); if (provinceId != null) { NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetDistrictRequest(provinceId)); - districtComboBox.setEnabled(false); - districtModel.removeAllElements(); - districtModel.addElement("Loading districts..."); - wardComboBox.setEnabled(false); - wardModel.removeAllElements(); - wardModel.addElement("Phường"); // Reset ward + districtComboBox.setEnabled(false); districtModel.removeAllElements(); districtModel.addElement("Đang tải quận/huyện..."); + wardComboBox.setEnabled(false); wardModel.removeAllElements(); wardModel.addElement("Phường"); } } else { - districtComboBox.setEnabled(false); - districtModel.removeAllElements(); - districtModel.addElement("Huyện"); - wardComboBox.setEnabled(false); - wardModel.removeAllElements(); - wardModel.addElement("Phường"); + districtComboBox.setEnabled(false); districtModel.removeAllElements(); districtModel.addElement("Huyện"); + wardComboBox.setEnabled(false); wardModel.removeAllElements(); wardModel.addElement("Phường"); } }); - districtComboBox.addActionListener(e -> { String selectedDistrict = (String) districtComboBox.getSelectedItem(); - // Check if selectedDistrict is not null and not the placeholder/loading message if (selectedDistrict != null && LocalStorage.districtToId != null && LocalStorage.districtToId.containsKey(selectedDistrict)) { String districtId = LocalStorage.districtToId.get(selectedDistrict); if (districtId != null) { NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetWardRequest(districtId)); - wardComboBox.setEnabled(false); - wardModel.removeAllElements(); - wardModel.addElement("Loading wards..."); + wardComboBox.setEnabled(false); wardModel.removeAllElements(); wardModel.addElement("Đang tải phường/xã..."); } } else { - // If no valid district is selected (or district data is not yet loaded), disable and reset wardComboBox - wardComboBox.setEnabled(false); - wardModel.removeAllElements(); - wardModel.addElement("Phường"); + wardComboBox.setEnabled(false); wardModel.removeAllElements(); wardModel.addElement("Phường"); } }); - gbcPatient.gridx = 0; - gbcPatient.gridy++; - patientInfoInnerPanel.add(new JLabel("Mã số BN"), gbcPatient); // Shortened label - gbcPatient.gridx = 1; - customerIdField = new JTextField(10); - customerIdField.setEditable(false); - patientInfoInnerPanel.add(customerIdField, gbcPatient); - gbcPatient.gridx = 2; - patientInfoInnerPanel.add(new JLabel("SĐT"), gbcPatient); // Shortened label - gbcPatient.gridx = 3; - customerPhoneField = new JTextField(10); - patientInfoInnerPanel.add(customerPhoneField, gbcPatient); + gbcPatient.gridx = 0; gbcPatient.gridy++; patientInfoInnerPanel.add(new JLabel("Mã bệnh nhân"), gbcPatient); + gbcPatient.gridx = 1; customerIdField = new JTextField(10); customerIdField.setEditable(false); patientInfoInnerPanel.add(customerIdField, gbcPatient); + gbcPatient.gridx = 2; patientInfoInnerPanel.add(new JLabel("SĐT"), gbcPatient); + gbcPatient.gridx = 3; customerPhoneField = new JTextField(10); patientInfoInnerPanel.add(customerPhoneField, gbcPatient); + gbcPatient.gridx = 0; gbcPatient.gridy++; patientInfoInnerPanel.add(new JLabel("Cân nặng (kg)"), gbcPatient); + gbcPatient.gridx = 1; SpinnerModel weightModel = new SpinnerNumberModel(60, 0, 300, 0.5); customerWeightSpinner = new JSpinner(weightModel); patientInfoInnerPanel.add(customerWeightSpinner, gbcPatient); + gbcPatient.gridx = 2; patientInfoInnerPanel.add(new JLabel("Chiều cao (cm)"), gbcPatient); + gbcPatient.gridx = 3; SpinnerModel heightModel = new SpinnerNumberModel(170, 0, 230, 0.5); customerHeightSpinner = new JSpinner(heightModel); patientInfoInnerPanel.add(customerHeightSpinner, gbcPatient); + + gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 4; gbc.weightx = 1.0; inputPanel.add(patientInfoInnerPanel, gbc); gbc.weightx = 0; - gbcPatient.gridx = 0; - gbcPatient.gridy++; - patientInfoInnerPanel.add(new JLabel("Cân nặng (kg)"), gbcPatient); - gbcPatient.gridx = 1; - SpinnerModel weightModel = new SpinnerNumberModel(60, 0, 300, 0.5); - customerWeightSpinner = new JSpinner(weightModel); - patientInfoInnerPanel.add(customerWeightSpinner, gbcPatient); - gbcPatient.gridx = 2; - patientInfoInnerPanel.add(new JLabel("Chiều cao (cm)"), gbcPatient); - gbcPatient.gridx = 3; - SpinnerModel heightModel = new SpinnerNumberModel(170, 0, 230, 0.5); - customerHeightSpinner = new JSpinner(heightModel); - patientInfoInnerPanel.add(customerHeightSpinner, gbcPatient); - - // Add patientInfoInnerPanel to the main inputPanel - gbc.gridx = 0; - gbc.gridy = 0; // First element in the main inputPanel - gbc.gridwidth = 4; - gbc.weightx = 1.0; // Allow horizontal fill - inputPanel.add(patientInfoInnerPanel, gbc); - gbc.weightx = 0; // Reset - - - // --- Checkup Info Sub-Panel with TitledBorder --- JPanel checkupInfoInnerPanel = new JPanel(new GridBagLayout()); checkupInfoInnerPanel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), "Thông tin khám bệnh", @@ -547,185 +423,53 @@ public class CheckUpPage extends JPanel { gbcCheckup.insets = new Insets(3, 3, 3, 3); gbcCheckup.fill = GridBagConstraints.HORIZONTAL; - gbcCheckup.gridx = 0; - gbcCheckup.gridy = 0; - gbcCheckup.gridwidth = 1; - gbcCheckup.anchor = GridBagConstraints.NORTHWEST; // Default anchor - checkupInfoInnerPanel.add(new JLabel("Mã khám bệnh:"), gbcCheckup); - gbcCheckup.gridx = 1; - checkupIdField = new JTextField(10); - checkupIdField.setEditable(false); - checkupInfoInnerPanel.add(checkupIdField, gbcCheckup); + gbcCheckup.gridx = 0; gbcCheckup.gridy = 0; gbcCheckup.gridwidth = 1; gbcCheckup.anchor = GridBagConstraints.NORTHWEST; checkupInfoInnerPanel.add(new JLabel("Mã khám bệnh:"), gbcCheckup); + gbcCheckup.gridx = 1; checkupIdField = new JTextField(10); checkupIdField.setEditable(false); checkupInfoInnerPanel.add(checkupIdField, gbcCheckup); + UtilDateModel dateModel = new UtilDateModel(); Properties p = new Properties(); p.put("text.today", "Hôm nay"); p.put("text.month", "Tháng"); p.put("text.year", "Năm"); JDatePanelImpl datePanel = new JDatePanelImpl(dateModel, p); datePicker = new JDatePickerImpl(datePanel, new DateLabelFormatter()); datePicker.setPreferredSize(new Dimension(120, 30)); + gbcCheckup.gridx = 2; checkupInfoInnerPanel.add(new JLabel("Đơn Ngày:"), gbcCheckup); + gbcCheckup.gridx = 3; checkupInfoInnerPanel.add(datePicker, gbcCheckup); + gbcCheckup.gridwidth = 1; gbcCheckup.gridx = 0; gbcCheckup.gridy++; checkupInfoInnerPanel.add(new JLabel("Bác Sĩ"), gbcCheckup); + gbcCheckup.gridwidth = 3; gbcCheckup.gridx = 1; doctorComboBox = new JComboBox<>(LocalStorage.doctorsName != null ? LocalStorage.doctorsName : new String[]{"Đang tải bác sĩ..."}); checkupInfoInnerPanel.add(doctorComboBox, gbcCheckup); + gbcCheckup.gridwidth = 1; gbcCheckup.gridx = 0; gbcCheckup.gridy++; checkupInfoInnerPanel.add(new JLabel("Triệu chứng"), gbcCheckup); + gbcCheckup.gridwidth = 3; gbcCheckup.gridx = 1; symptomsField = new JTextArea(3, 20); symptomsField.setLineWrap(true); symptomsField.setWrapStyleWord(true); gbcCheckup.weighty = 1.0; gbcCheckup.fill = GridBagConstraints.BOTH; checkupInfoInnerPanel.add(new JScrollPane(symptomsField), gbcCheckup); gbcCheckup.weighty = 0.0; gbcCheckup.fill = GridBagConstraints.HORIZONTAL; + gbcCheckup.gridwidth = 1; gbcCheckup.gridx = 0; gbcCheckup.gridy++; checkupInfoInnerPanel.add(new JLabel("Chẩn đoán"), gbcCheckup); + gbcCheckup.gridwidth = 3; gbcCheckup.gridx = 1; diagnosisField = new JTextArea(3, 20); diagnosisField.setLineWrap(true); diagnosisField.setWrapStyleWord(true); gbcCheckup.weighty = 1.0; gbcCheckup.fill = GridBagConstraints.BOTH; checkupInfoInnerPanel.add(new JScrollPane(diagnosisField), gbcCheckup); gbcCheckup.weighty = 0.0; gbcCheckup.fill = GridBagConstraints.HORIZONTAL; + gbcCheckup.gridwidth = 1; gbcCheckup.gridx = 0; gbcCheckup.gridy++; checkupInfoInnerPanel.add(new JLabel("Ghi chú"), gbcCheckup); + gbcCheckup.gridwidth = 3; gbcCheckup.gridx = 1; notesField = new JTextArea(3, 20); notesField.setLineWrap(true); notesField.setWrapStyleWord(true); gbcCheckup.weighty = 1.0; gbcCheckup.fill = GridBagConstraints.BOTH; checkupInfoInnerPanel.add(new JScrollPane(notesField), gbcCheckup); gbcCheckup.weighty = 0.0; gbcCheckup.fill = GridBagConstraints.HORIZONTAL; + gbcCheckup.gridwidth = 1; gbcCheckup.gridx = 0; gbcCheckup.gridy++; checkupInfoInnerPanel.add(new JLabel("Trạng thái"), gbcCheckup); + gbcCheckup.gridwidth = 1; gbcCheckup.gridx = 1; String[] statusOptions = {"ĐANG KHÁM", "CHỜ KHÁM", "ĐÃ KHÁM"}; statusComboBox = new JComboBox<>(statusOptions); checkupInfoInnerPanel.add(statusComboBox, gbcCheckup); + gbcCheckup.gridx = 2; checkupInfoInnerPanel.add(new JLabel("Loại khám"), gbcCheckup); + gbcCheckup.gridx = 3; String[] checkupTypeOptions = {"BỆNH", "THAI", "KHÁC"}; checkupTypeComboBox = new JComboBox<>(checkupTypeOptions); checkupInfoInnerPanel.add(checkupTypeComboBox, gbcCheckup); + + gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 4; gbc.weightx = 1.0; gbc.weighty = 1.0; gbc.insets = new Insets(10, 0, 5, 0); inputPanel.add(checkupInfoInnerPanel, gbc); gbc.weightx = 0; gbc.weighty = 0; gbc.insets = new Insets(5,5,5,5); + + gbc.gridwidth = 1; gbc.gridx = 0; gbc.gridy = 2; inputPanel.add(new JLabel("Gọi vào phòng:"), gbc); + gbc.gridx = 1; gbc.gridwidth = 1; String[] roomOptions = {"Phòng 1", "Phòng 2", "Phòng 3"}; callRoomComboBox = new JComboBox<>(roomOptions); inputPanel.add(callRoomComboBox, gbc); + gbc.gridx = 2; gbc.gridwidth = 2; callPatientButton = new JButton("Gọi bệnh nhân"); callPatientButton.setBackground(new Color(33, 150, 243)); callPatientButton.setForeground(Color.WHITE); callPatientButton.setFocusPainted(false); callPatientButton.addActionListener(e -> handleCallPatient()); inputPanel.add(callPatientButton, gbc); + gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 4; JButton freeRoomButton = new JButton("Đánh dấu phòng trống"); freeRoomButton.setBackground(new Color(46, 204, 113)); freeRoomButton.setForeground(Color.WHITE); freeRoomButton.setFocusPainted(false); freeRoomButton.addActionListener(e -> handleFreeRoom()); inputPanel.add(freeRoomButton, gbc); - UtilDateModel dateModel = new UtilDateModel(); // Renamed from 'model' to avoid conflict - Properties p = new Properties(); - p.put("text.today", "Today"); - p.put("text.month", "Month"); - p.put("text.year", "Year"); - JDatePanelImpl datePanel = new JDatePanelImpl(dateModel, p); - datePicker = new JDatePickerImpl(datePanel, new DateLabelFormatter()); - datePicker.setPreferredSize(new Dimension(120, 30)); - - gbcCheckup.gridx = 2; - checkupInfoInnerPanel.add(new JLabel("Đơn Ngày:"), gbcCheckup); - gbcCheckup.gridx = 3; // Adjusted gridx for datePicker - checkupInfoInnerPanel.add(datePicker, gbcCheckup); - - gbcCheckup.gridwidth = 1; - gbcCheckup.gridx = 0; - gbcCheckup.gridy++; - checkupInfoInnerPanel.add(new JLabel("Bác Sĩ"), gbcCheckup); - gbcCheckup.gridwidth = 3; - gbcCheckup.gridx = 1; - doctorComboBox = new JComboBox<>(LocalStorage.doctorsName != null ? LocalStorage.doctorsName : new String[]{"Loading..."}); - checkupInfoInnerPanel.add(doctorComboBox, gbcCheckup); - - gbcCheckup.gridwidth = 1; - gbcCheckup.gridx = 0; - gbcCheckup.gridy++; - checkupInfoInnerPanel.add(new JLabel("Triệu chứng"), gbcCheckup); - gbcCheckup.gridwidth = 3; - gbcCheckup.gridx = 1; - symptomsField = new JTextArea(3, 20); - symptomsField.setLineWrap(true); - symptomsField.setWrapStyleWord(true); - gbcCheckup.weighty = 1.0; // Allow vertical expansion - gbcCheckup.fill = GridBagConstraints.BOTH; // Allow expansion in both directions - checkupInfoInnerPanel.add(new JScrollPane(symptomsField), gbcCheckup); - gbcCheckup.weighty = 0.0; // Reset for next components - gbcCheckup.fill = GridBagConstraints.HORIZONTAL; // Reset fill - - gbcCheckup.gridwidth = 1; - gbcCheckup.gridx = 0; - gbcCheckup.gridy++; - checkupInfoInnerPanel.add(new JLabel("Chẩn đoán"), gbcCheckup); - gbcCheckup.gridwidth = 3; - gbcCheckup.gridx = 1; - diagnosisField = new JTextArea(3, 20); - diagnosisField.setLineWrap(true); - diagnosisField.setWrapStyleWord(true); - gbcCheckup.weighty = 1.0; // Allow vertical expansion - gbcCheckup.fill = GridBagConstraints.BOTH; // Allow expansion in both directions - checkupInfoInnerPanel.add(new JScrollPane(diagnosisField), gbcCheckup); - gbcCheckup.weighty = 0.0; // Reset for next components - gbcCheckup.fill = GridBagConstraints.HORIZONTAL; // Reset fill - - gbcCheckup.gridwidth = 1; - gbcCheckup.gridx = 0; - gbcCheckup.gridy++; - checkupInfoInnerPanel.add(new JLabel("Ghi chú"), gbcCheckup); - gbcCheckup.gridwidth = 3; - gbcCheckup.gridx = 1; - notesField = new JTextArea(3, 20); - notesField.setLineWrap(true); - notesField.setWrapStyleWord(true); - gbcCheckup.weighty = 1.0; // Allow vertical expansion - gbcCheckup.fill = GridBagConstraints.BOTH; // Allow expansion in both directions - checkupInfoInnerPanel.add(new JScrollPane(notesField), gbcCheckup); - gbcCheckup.weighty = 0.0; // Reset for next components - gbcCheckup.fill = GridBagConstraints.HORIZONTAL; // Reset fill - - gbcCheckup.gridwidth = 1; - gbcCheckup.gridx = 0; - gbcCheckup.gridy++; - checkupInfoInnerPanel.add(new JLabel("Trạng thái"), gbcCheckup); - gbcCheckup.gridwidth = 1; // Status ComboBox takes 1 column - gbcCheckup.gridx = 1; - String[] statusOptions = {"PROCESSING", "NOT", "DONE"}; - statusComboBox = new JComboBox<>(statusOptions); - checkupInfoInnerPanel.add(statusComboBox, gbcCheckup); - - gbcCheckup.gridx = 2; // Label for Checkup Type ComboBox - checkupInfoInnerPanel.add(new JLabel("Loại khám"), gbcCheckup); - gbcCheckup.gridx = 3; // Checkup Type ComboBox - String[] checkupTypeOptions = {"BENH", "THAI", "KHAC"}; - checkupTypeComboBox = new JComboBox<>(checkupTypeOptions); - checkupInfoInnerPanel.add(checkupTypeComboBox, gbcCheckup); - - // Add checkupInfoInnerPanel to the main inputPanel - gbc.gridx = 0; - gbc.gridy = 1; // Second element in the main inputPanel - gbc.gridwidth = 4; - gbc.weightx = 1.0; // Allow horizontal fill - gbc.weighty = 1.0; // Allow checkupInfoInnerPanel to take vertical space - gbc.insets = new Insets(10, 0, 5, 0); // Add some top margin before this panel - inputPanel.add(checkupInfoInnerPanel, gbc); - gbc.weightx = 0; // Reset - gbc.weighty = 0; // Reset for subsequent components (like the call panel row) - gbc.insets = new Insets(5,5,5,5); // Reset insets for subsequent components - - // --- Call Patient components --- (now added after the two sub-panels) - gbc.gridwidth = 1; // Reset gridwidth - gbc.gridx = 0; - gbc.gridy = 2; // This is now the third major "row" in inputPanel - inputPanel.add(new JLabel("Call to Room:"), gbc); - - gbc.gridx = 1; - gbc.gridwidth = 1; - String[] roomOptions = {"Room 1", "Room 2", "Room 3"}; - callRoomComboBox = new JComboBox<>(roomOptions); - inputPanel.add(callRoomComboBox, gbc); - - gbc.gridx = 2; - gbc.gridwidth = 2; - callPatientButton = new JButton("Call Patient"); - callPatientButton.setBackground(new Color(33, 150, 243)); - callPatientButton.setForeground(Color.WHITE); - callPatientButton.setFocusPainted(false); - callPatientButton.addActionListener(e -> handleCallPatient()); - inputPanel.add(callPatientButton, gbc); - - // Add Free Room button - gbc.gridx = 0; - gbc.gridy = 3; - gbc.gridwidth = 4; - JButton freeRoomButton = new JButton("Mark Room as Free"); - freeRoomButton.setBackground(new Color(46, 204, 113)); // Green color - freeRoomButton.setForeground(Color.WHITE); - freeRoomButton.setFocusPainted(false); - freeRoomButton.addActionListener(e -> handleFreeRoom()); - inputPanel.add(freeRoomButton, gbc); - - table1.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - table1.getSelectionModel().addListSelectionListener(e -> { - if (!e.getValueIsAdjusting()) { // Ensure final selection event - int selectedRow = table1.getSelectedRow(); - if (selectedRow != -1) { - if (previousSelectedRow != -1) { - if(previousSelectedRow != selectedRow) { - if(!saved) { - JOptionPane.showMessageDialog(null, "Please save changes before selecting another patient"); - returnCell = true; - table1.setRowSelectionInterval(previousSelectedRow, previousSelectedRow); - return; - } - } - } - // reset information - - if (returnCell) { - returnCell = false; - return; - } - medicinePrescription = new String[][]{}; - servicePrescription = new String[][]{}; - medDialog = null; - serDialog = null; - handleRowSelection(selectedRow); - - } - } + JScrollPane inputScroll = new JScrollPane(inputPanel); + inputScroll.getVerticalScrollBar().setUnitIncrement(16); + inputScroll.getVerticalScrollBar().setBlockIncrement(64); + inputScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + inputScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + inputScroll.addMouseWheelListener(e -> { + JScrollBar scrollBar = inputScroll.getVerticalScrollBar(); + int notches = e.getWheelRotation(); + int currentValue = scrollBar.getValue(); + int newValue = currentValue + (notches * scrollBar.getUnitIncrement() * 2); + scrollBar.setValue(newValue); }); - + rightBottomPanel.add(inputScroll, BorderLayout.CENTER); + JPanel iconPanel = new JPanel(); iconPanel.setLayout(new GridLayout(1, 5)); - iconPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Padding + iconPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); iconPanel.setBackground(Color.WHITE); String[] iconName = {"save", "service", "medicine", "printer"}; for (String name : iconName) { ImageIcon originalIcon = new ImageIcon("src/main/java/BsK/client/ui/assets/icon/" + name + ".png"); - Image scaledImage = originalIcon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH); // Resize to 32x32 + Image scaledImage = originalIcon.getImage().getScaledInstance(32, 32, Image.SCALE_SMOOTH); ImageIcon scaledIcon = new ImageIcon(scaledImage); JLabel iconLabel = new JLabel(scaledIcon); iconLabel.setHorizontalAlignment(SwingConstants.CENTER); @@ -735,231 +479,286 @@ public class CheckUpPage extends JPanel { public void mouseClicked(MouseEvent e) { switch (name) { case "service": - // If there is no user selected, show a warning message if (checkupIdField.getText().isEmpty()) { - JOptionPane.showMessageDialog(null, "Please select a patient from the queue"); + JOptionPane.showMessageDialog(null, "Vui lòng chọn một bệnh nhân từ hàng đợi", "Chưa chọn bệnh nhân", JOptionPane.WARNING_MESSAGE); return; } - // Open service dialog if(serDialog == null) { serDialog = new ServiceDialog(mainFrame); - previousSelectedRow = table1.getSelectedRow(); } serDialog.setVisible(true); saved = false; servicePrescription = serDialog.getServicePrescription(); - - log.info("Service prescription: {}", servicePrescription); + updatePrescriptionTree(); + log.info("Service prescription: {}", (Object) servicePrescription); break; case "save": { - //Warning message - int option = JOptionPane.showOptionDialog(null, "Do you want to save changes?", - "Save Changes", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, + int option = JOptionPane.showOptionDialog(null, "Bạn có muốn lưu các thay đổi?", + "Lưu thay đổi", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null); if (option == JOptionPane.NO_OPTION) { return; } - // Save action saved = true; callingStatusLabel.setText(" "); callingStatusLabel.setBackground(new Color(230, 255, 230)); callingStatusLabel.setForeground(new Color(0, 100, 0)); + // TODO: Add logic to actually save medicinePrescription and servicePrescription with other checkup data break; } case "medicine": - // If there is no user selected, show a warning message if (checkupIdField.getText().isEmpty()) { - JOptionPane.showMessageDialog(null, "Please select a patient from the queue"); + JOptionPane.showMessageDialog(null, "Vui lòng chọn một bệnh nhân từ hàng đợi", "Chưa chọn bệnh nhân", JOptionPane.WARNING_MESSAGE); return; } - // Open medicine dialog if(medDialog == null) { medDialog = new MedicineDialog(mainFrame); - previousSelectedRow = table1.getSelectedRow(); } medDialog.setVisible(true); saved = false; medicinePrescription = medDialog.getMedicinePrescription(); - - log.info("Medicine prescription: {}", medicinePrescription); - + updatePrescriptionTree(); + log.info("Medicine prescription: {}", (Object) medicinePrescription); break; case "printer": - // Print action - - + if (checkupIdField.getText().isEmpty()) { + JOptionPane.showMessageDialog(null, "Vui lòng chọn bệnh nhân trước.", "Chưa chọn bệnh nhân", JOptionPane.WARNING_MESSAGE); return; + } + if ((medicinePrescription == null || medicinePrescription.length == 0) && + (servicePrescription == null || servicePrescription.length == 0)) { + JOptionPane.showMessageDialog(null, "Không có thuốc hoặc dịch vụ để in.", "Không có dữ liệu", JOptionPane.INFORMATION_MESSAGE); return; + } MedicineInvoice medicineInvoice = new MedicineInvoice(checkupIdField.getText(), - customerLastNameField.getText() + customerFirstNameField.getText(), + customerLastNameField.getText() + " " + customerFirstNameField.getText(), dobPicker.getJFormattedTextField().getText(), customerPhoneField.getText(), - genderComboBox.getSelectedItem().toString(), customerAddressField.getText(), + genderComboBox.getSelectedItem().toString(), + customerAddressField.getText() + ", " + (wardComboBox.getSelectedItem() != null ? wardComboBox.getSelectedItem().toString() : "") + ", " + (districtComboBox.getSelectedItem() != null ? districtComboBox.getSelectedItem().toString() : "") + ", " + (provinceComboBox.getSelectedItem() != null ? provinceComboBox.getSelectedItem().toString() : ""), doctorComboBox.getSelectedItem().toString(), diagnosisField.getText(), - notesField.getText(), medicinePrescription); - + notesField.getText(), medicinePrescription); medicineInvoice.createDialog(mainFrame); - break; default: throw new IllegalStateException("Unexpected value: " + name); } } - - @Override - public void mouseEntered(MouseEvent e) { - iconLabel.setBorder(BorderFactory.createLineBorder(Color.BLUE, 2)); - } - - @Override - public void mouseExited(MouseEvent e) { - iconLabel.setBorder(null); - } + @Override public void mouseEntered(MouseEvent e) { iconLabel.setBorder(BorderFactory.createLineBorder(Color.BLUE, 2)); } + @Override public void mouseExited(MouseEvent e) { iconLabel.setBorder(null); } }); iconPanel.add(iconLabel); } - - JScrollPane inputScroll = new JScrollPane(inputPanel); - // Configure scroll settings for smoother scrolling - inputScroll.getVerticalScrollBar().setUnitIncrement(16); // Increase scroll speed - inputScroll.getVerticalScrollBar().setBlockIncrement(64); // Increase block increment - inputScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); - inputScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - // Add mouse wheel listener for smoother scrolling - inputScroll.addMouseWheelListener(e -> { - JScrollBar scrollBar = inputScroll.getVerticalScrollBar(); - int notches = e.getWheelRotation(); - int currentValue = scrollBar.getValue(); - int newValue = currentValue + (notches * scrollBar.getUnitIncrement() * 2); - scrollBar.setValue(newValue); - }); - - rightBottomPanel.add(inputScroll, BorderLayout.CENTER); rightBottomPanel.add(iconPanel, BorderLayout.SOUTH); - // Apply TitledBorder to rightBottomPanel - rightBottomPanel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createEmptyBorder(10, 10, 10, 10), // Outer padding - BorderFactory.createTitledBorder( - BorderFactory.createLineBorder(new Color(63, 81, 181), 1, true), - "Chi tiết và Thao tác", - TitledBorder.LEADING, - TitledBorder.TOP, - new Font("Arial", Font.BOLD, 16), - new Color(63, 81, 181) - ) - )); - - // Right top panel + // Right Top Panel (History) rightTopPanel.setLayout(new BorderLayout()); - // Apply TitledBorder to rightTopPanel rightTopPanel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createEmptyBorder(10, 10, 10, 10), // Outer padding to keep content away from split pane edge + BorderFactory.createEmptyBorder(10, 10, 5, 10), BorderFactory.createTitledBorder( BorderFactory.createLineBorder(new Color(63, 81, 181), 1, true), "Lịch sử khám bệnh", - TitledBorder.LEADING, - TitledBorder.TOP, - new Font("Arial", Font.BOLD, 16), - new Color(63, 81, 181) + TitledBorder.LEADING, TitledBorder.TOP, + new Font("Arial", Font.BOLD, 16), new Color(63, 81, 181) ) )); - String historyColumns[] = {"Ngày Tháng","Mã khám bệnh", "Triệu chứng", "Chẩn đoán", "Ghi chú"}; - historyModel = new DefaultTableModel(this.history, historyColumns) { - @Override - public boolean isCellEditable(int row, int column) { - return false; // All cells are non-editable - } + @Override public boolean isCellEditable(int row, int column) { return false; } }; historyTable = new JTable(historyModel); - - // Customize the font for the table header and cells historyTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); historyTable.setFont(new Font("Arial", Font.PLAIN, 12)); - historyTable.setRowHeight(25); // Increased row height - - // Add a mouse listener for future click functionality + historyTable.setRowHeight(25); historyTable.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - if (e.getClickCount() == 1) { // Single click + if (e.getClickCount() == 1) { int selectedRow = historyTable.getSelectedRow(); - if (selectedRow != -1) { - // Placeholder for showing more details - System.out.println("Clicked on history row: " + selectedRow); - // String checkupId = (String) historyTable.getValueAt(selectedRow, 1); // Assuming Mã khám bệnh is at index 1 - // JOptionPane.showMessageDialog(CheckUpPage.this, "Details for Checkup ID: " + checkupId + " will be shown here.", "History Details", JOptionPane.INFORMATION_MESSAGE); - } + // if (selectedRow != -1) { System.out.println("Clicked on history row: " + selectedRow); } } } }); - JScrollPane tableScroll2 = new JScrollPane(historyTable); - tableScroll2.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Inner padding + tableScroll2.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); rightTopPanel.add(tableScroll2, BorderLayout.CENTER); + // Prescription Display Panel Setup + prescriptionDisplayPanel.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createEmptyBorder(5, 10, 5, 10), + BorderFactory.createTitledBorder( + BorderFactory.createLineBorder(new Color(63, 81, 181), 1, true), + "Đơn thuốc và Dịch vụ", + TitledBorder.LEADING, TitledBorder.TOP, + new Font("Arial", Font.BOLD, 16), new Color(63, 81, 181) + ) + )); + rootPrescriptionNode = new DefaultMutableTreeNode("Đơn hiện tại"); + prescriptionTreeModel = new DefaultTreeModel(rootPrescriptionNode); + prescriptionTree = new JTree(prescriptionTreeModel); + prescriptionTree.setFont(new Font("Arial", Font.PLAIN, 13)); + prescriptionTree.setRowHeight(20); + JScrollPane treeScrollPane = new JScrollPane(prescriptionTree); + prescriptionDisplayPanel.add(treeScrollPane, BorderLayout.CENTER); + + JPanel totalCostPanel = new JPanel(new GridLayout(0,1, 5,5)); + totalCostPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); + totalMedCostLabel = new JLabel("Tổng tiền thuốc: 0 VNĐ"); + totalMedCostLabel.setFont(new Font("Arial", Font.BOLD, 13)); + totalSerCostLabel = new JLabel("Tổng tiền dịch vụ: 0 VNĐ"); + totalSerCostLabel.setFont(new Font("Arial", Font.BOLD, 13)); + overallTotalCostLabel = new JLabel("TỔNG CỘNG: 0 VNĐ"); + overallTotalCostLabel.setFont(new Font("Arial", Font.BOLD, 14)); + overallTotalCostLabel.setForeground(Color.RED); + totalCostPanel.add(totalMedCostLabel); + totalCostPanel.add(totalSerCostLabel); + totalCostPanel.add(new JSeparator()); + totalCostPanel.add(overallTotalCostLabel); + prescriptionDisplayPanel.add(totalCostPanel, BorderLayout.SOUTH); + updatePrescriptionTree(); // Initialize tree display - UIManager.getDefaults().put("SplitPane.border", BorderFactory.createEmptyBorder()); // Remove border - JSplitPane splitPaneRight = new JSplitPane(JSplitPane.VERTICAL_SPLIT, rightTopPanel, rightBottomPanel); - splitPaneRight.setResizeWeight(0.5); // Split - splitPaneRight.setDividerSize(5); // Set divider size + // Split Panes Configuration + UIManager.getDefaults().put("SplitPane.border", BorderFactory.createEmptyBorder()); + + JSplitPane prescriptionAndDetailsPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, prescriptionDisplayPanel, rightBottomPanel); + prescriptionAndDetailsPane.setResizeWeight(0.4); + prescriptionAndDetailsPane.setDividerSize(5); + + JSplitPane splitPaneRight = new JSplitPane(JSplitPane.VERTICAL_SPLIT, rightTopPanel, prescriptionAndDetailsPane); + splitPaneRight.setResizeWeight(0.2); + splitPaneRight.setDividerSize(5); JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, leftPanel, splitPaneRight); - splitPane.setResizeWeight(0.5); // Split - splitPane.setDividerSize(5); // Set divider size - - splitPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // Remove border + splitPane.setResizeWeight(0.4); + splitPane.setDividerSize(5); + splitPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); add(navBar, BorderLayout.NORTH); - //add(topbar, BorderLayout.NORTH); add(splitPane, BorderLayout.CENTER); + + table1.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + table1.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + if (e.getClickCount() == 2) { + SwingUtilities.invokeLater(() -> { + int selectedRow = table1.getSelectedRow(); + if (selectedRow != -1) { + if (previousSelectedRow == selectedRow && !saved) { + int confirm = JOptionPane.showConfirmDialog( + CheckUpPage.this, + "Các thay đổi chưa được lưu. Bạn có chắc chắn muốn chuyển sang bệnh nhân khác không?", + "Xác nhận chuyển bệnh nhân", + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE); + if (confirm == JOptionPane.NO_OPTION) { + return; + } + } + handleRowSelection(selectedRow); + } + }); + } + } + }); + } + + private void updatePrescriptionTree() { + rootPrescriptionNode.removeAllChildren(); + double totalMedicineCost = 0; + double totalServiceCost = 0; + + if (medicinePrescription != null && medicinePrescription.length > 0) { + DefaultMutableTreeNode medicinesNode = new DefaultMutableTreeNode("Thuốc (" + medicinePrescription.length + ")"); + for (String[] med : medicinePrescription) { + if (med == null || med.length < 10) continue; + String medDisplay = String.format("%s - SL: %s %s", med[1], med[2], med[3]); + DefaultMutableTreeNode medNode = new DefaultMutableTreeNode(medDisplay); + medNode.add(new DefaultMutableTreeNode("Liều dùng: Sáng " + med[4] + " / Trưa " + med[5] + " / Chiều " + med[6])); + try { + medNode.add(new DefaultMutableTreeNode("Đơn giá: " + df.format(Double.parseDouble(med[7])) + " VNĐ")); + medNode.add(new DefaultMutableTreeNode("Thành tiền: " + df.format(Double.parseDouble(med[8])) + " VNĐ")); + totalMedicineCost += Double.parseDouble(med[8]); + } catch (NumberFormatException e) { + log.error("Error parsing medicine cost: {}", med[7] + " or " + med[8]); + medNode.add(new DefaultMutableTreeNode("Đơn giá: Lỗi")); + medNode.add(new DefaultMutableTreeNode("Thành tiền: Lỗi")); + } + if (med[9] != null && !med[9].isEmpty()) { + medNode.add(new DefaultMutableTreeNode("Ghi chú: " + med[9])); + } + medicinesNode.add(medNode); + } + rootPrescriptionNode.add(medicinesNode); + } + + if (servicePrescription != null && servicePrescription.length > 0) { + DefaultMutableTreeNode servicesNode = new DefaultMutableTreeNode("Dịch vụ (" + servicePrescription.length + ")"); + for (String[] ser : servicePrescription) { + if (ser == null || ser.length < 6) continue; + String serDisplay = String.format("%s - SL: %s", ser[1], ser[2]); + DefaultMutableTreeNode serNode = new DefaultMutableTreeNode(serDisplay); + try { + serNode.add(new DefaultMutableTreeNode("Đơn giá: " + df.format(Double.parseDouble(ser[3])) + " VNĐ")); + serNode.add(new DefaultMutableTreeNode("Thành tiền: " + df.format(Double.parseDouble(ser[4])) + " VNĐ")); + totalServiceCost += Double.parseDouble(ser[4]); + } catch (NumberFormatException e) { + log.error("Error parsing service cost: {}", ser[3] + " or " + ser[4]); + serNode.add(new DefaultMutableTreeNode("Đơn giá: Lỗi")); + serNode.add(new DefaultMutableTreeNode("Thành tiền: Lỗi")); + } + if (ser[5] != null && !ser[5].isEmpty()) { + serNode.add(new DefaultMutableTreeNode("Ghi chú: " + ser[5])); + } + servicesNode.add(serNode); + } + rootPrescriptionNode.add(servicesNode); + } + + totalMedCostLabel.setText("Tổng tiền thuốc: " + df.format(totalMedicineCost) + " VNĐ"); + totalSerCostLabel.setText("Tổng tiền dịch vụ: " + df.format(totalServiceCost) + " VNĐ"); + overallTotalCostLabel.setText("TỔNG CỘNG: " + df.format(totalMedicineCost + totalServiceCost) + " VNĐ"); + + prescriptionTreeModel.reload(); + for (int i = 0; i < prescriptionTree.getRowCount(); i++) { + prescriptionTree.expandRow(i); + } } - private void handleRowSelection(int selectedRow) { - if (selectedRow < 0 || selectedRow >= patientQueue.size()) { - log.warn("Selected row index out of bounds: {}", selectedRow); + private void handleRowSelection(int selectedRowInQueue) { + if (selectedRowInQueue < 0 || selectedRowInQueue >= patientQueue.size()) { + log.warn("Selected row index out of bounds: {}", selectedRowInQueue); return; } - Patient selectedPatient = patientQueue.get(selectedRow); + Patient selectedPatient = patientQueue.get(selectedRowInQueue); + + medicinePrescription = new String[0][0]; + servicePrescription = new String[0][0]; + medDialog = null; + serDialog = null; + updatePrescriptionTree(); checkupIdField.setText(selectedPatient.getCheckupId()); - SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); try { - Date parsedDate; - String dateStr = selectedPatient.getCheckupDate(); - if (dateStr.matches("\\d+")) { - long timestamp = Long.parseLong(dateStr); - parsedDate = new Date(timestamp); - } else { - parsedDate = dateFormat.parse(dateStr); - } - Calendar calendar = Calendar.getInstance(); - calendar.setTime(parsedDate); + Date parsedDate; String dateStr = selectedPatient.getCheckupDate(); + if (dateStr.matches("\\d+")) parsedDate = new Date(Long.parseLong(dateStr)); + else parsedDate = dateFormat.parse(dateStr); + Calendar calendar = Calendar.getInstance(); calendar.setTime(parsedDate); datePicker.getModel().setDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)); datePicker.getModel().setSelected(true); } catch (ParseException | NumberFormatException exception) { - exception.printStackTrace(); - JOptionPane.showMessageDialog(null, "Invalid date format or timestamp: " + selectedPatient.getCheckupDate()); + log.error("Invalid date format for checkup date: {}", selectedPatient.getCheckupDate(), exception); + JOptionPane.showMessageDialog(null, "Định dạng ngày hoặc dấu thời gian không hợp lệ: " + selectedPatient.getCheckupDate(), "Lỗi định dạng ngày", JOptionPane.ERROR_MESSAGE); } - SimpleDateFormat dobFormat; try { - Date parsedDate; - String dobStr = selectedPatient.getCustomerDob(); - if (dobStr.matches("\\d+")) { - long timestamp = Long.parseLong(dobStr); - parsedDate = new Date(timestamp); - } else { - dobFormat = new SimpleDateFormat("dd/MM/yyyy"); - parsedDate = dobFormat.parse(dobStr); - } - Calendar calendar = Calendar.getInstance(); - calendar.setTime(parsedDate); + Date parsedDate; String dobStr = selectedPatient.getCustomerDob(); + if (dobStr.matches("\\d+")) parsedDate = new Date(Long.parseLong(dobStr)); + else parsedDate = new SimpleDateFormat("dd/MM/yyyy").parse(dobStr); + Calendar calendar = Calendar.getInstance(); calendar.setTime(parsedDate); dobPicker.getModel().setDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)); dobPicker.getModel().setSelected(true); } catch (ParseException | NumberFormatException exception) { - exception.printStackTrace(); - JOptionPane.showMessageDialog(null, "Invalid date format: " + selectedPatient.getCustomerDob()); + log.error("Invalid date format for DOB: {}", selectedPatient.getCustomerDob(), exception); + JOptionPane.showMessageDialog(null, "Định dạng ngày không hợp lệ: " + selectedPatient.getCustomerDob(), "Lỗi định dạng ngày", JOptionPane.ERROR_MESSAGE); } customerLastNameField.setText(selectedPatient.getCustomerLastName()); @@ -971,88 +770,77 @@ public class CheckUpPage extends JPanel { statusComboBox.setSelectedItem(selectedPatient.getStatus()); customerIdField.setText(selectedPatient.getCustomerId()); customerPhoneField.setText(selectedPatient.getCustomerNumber()); - customerAddressField.setText(selectedPatient.getCustomerAddress()); - customerWeightSpinner.setValue(Double.parseDouble(selectedPatient.getCustomerWeight())); - customerHeightSpinner.setValue(Double.parseDouble(selectedPatient.getCustomerHeight())); + try { customerWeightSpinner.setValue(Double.parseDouble(selectedPatient.getCustomerWeight())); } catch (NumberFormatException e) {log.warn("Invalid weight: {}", selectedPatient.getCustomerWeight()); customerWeightSpinner.setValue(0.0);} + try { customerHeightSpinner.setValue(Double.parseDouble(selectedPatient.getCustomerHeight())); } catch (NumberFormatException e) {log.warn("Invalid height: {}", selectedPatient.getCustomerHeight()); customerHeightSpinner.setValue(0.0);} genderComboBox.setSelectedItem(selectedPatient.getCustomerGender()); checkupTypeComboBox.setSelectedItem(selectedPatient.getCheckupType()); NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCustomerHistoryRequest(Integer.parseInt(selectedPatient.getCustomerId()))); - // Extract province, district, ward from the address String fullAddress = selectedPatient.getCustomerAddress(); String[] addressParts = fullAddress.split(", "); + if (addressParts.length >= 1) customerAddressField.setText(addressParts[0]); + else customerAddressField.setText(fullAddress); + if (addressParts.length == 4) { - String address = addressParts[0]; - String ward = addressParts[1]; - String district = addressParts[2]; - String province = addressParts[3]; - customerAddressField.setText(address); + String ward = addressParts[1]; String district = addressParts[2]; String province = addressParts[3]; int provinceIdx = findProvinceIndex(province); - if (provinceIdx != -1) provinceComboBox.setSelectedIndex(provinceIdx); - try { - Thread.sleep(100); - int districtIdx = findDistrictIndex(district); - if (districtIdx != -1) districtComboBox.setSelectedIndex(districtIdx); - - Thread.sleep(100); - int wardIdx = findWardIndex(ward); - if (wardIdx != -1) wardComboBox.setSelectedIndex(wardIdx); - } catch (InterruptedException e) { - log.error("Interrupted during address component selection", e); - Thread.currentThread().interrupt(); // Restore interruption status + if (provinceIdx != -1) { + provinceComboBox.setSelectedIndex(provinceIdx); + SwingUtilities.invokeLater(() -> { + try { Thread.sleep(250); } catch (InterruptedException ignored) {} + int districtIdx = findDistrictIndex(district); + if (districtIdx != -1) { + districtComboBox.setSelectedIndex(districtIdx); + SwingUtilities.invokeLater(() -> { + try { Thread.sleep(250); } catch (InterruptedException ignored) {} + int wardIdx = findWardIndex(ward); + if (wardIdx != -1) wardComboBox.setSelectedIndex(wardIdx); + }); + } else { + wardModel.removeAllElements(); wardModel.addElement("Phường"); wardComboBox.setEnabled(false); + } + }); + } else { + districtModel.removeAllElements(); districtModel.addElement("Huyện"); districtComboBox.setEnabled(false); + wardModel.removeAllElements(); wardModel.addElement("Phường"); wardComboBox.setEnabled(false); } + } else if (addressParts.length > 1) { // Handle cases with partial address (e.g. street, ward, district but no province) + provinceComboBox.setSelectedIndex(0); // Default to placeholder + districtModel.removeAllElements(); districtModel.addElement("Huyện"); districtComboBox.setEnabled(false); + wardModel.removeAllElements(); wardModel.addElement("Phường"); wardComboBox.setEnabled(false); + // Potentially try to parse known parts if format is somewhat consistent + log.warn("Address format not fully standard ({} parts): {}", addressParts.length, fullAddress); } else { - customerAddressField.setText(fullAddress); - // Reset dependent combo boxes + customerAddressField.setText(fullAddress); // If only street or unparseable provinceComboBox.setSelectedIndex(0); - districtModel.removeAllElements(); - districtModel.addElement("Huyện"); - districtComboBox.setEnabled(false); - wardModel.removeAllElements(); - wardModel.addElement("Phường"); - wardComboBox.setEnabled(false); + districtModel.removeAllElements(); districtModel.addElement("Huyện"); districtComboBox.setEnabled(false); + wardModel.removeAllElements(); wardModel.addElement("Phường"); wardComboBox.setEnabled(false); } + saved = true; } private void handleGetDistrictResponse(GetDistrictResponse response) { log.info("Received district data"); - LocalStorage.districts = response.getDistricts(); LocalStorage.districtToId = response.getDistrictToId(); - districtModel.removeAllElements(); // Clear previous items - for (String district : LocalStorage.districts) { - districtModel.addElement(district); + districtModel.removeAllElements(); + for (String district : LocalStorage.districts) { districtModel.addElement(district); } + districtComboBox.setEnabled(true); + if (LocalStorage.districts.length > 0 && !LocalStorage.districts[0].startsWith("Đang tải")) { + // No automatic selection, let user choose or handleRowSelection set it } - districtComboBox.setModel(districtModel); - districtComboBox.setEnabled(true); // Enable district combo box - // Automatically select the first actual district if available, or keep placeholder - if (LocalStorage.districts.length > 1) { // more than just the placeholder - districtComboBox.setSelectedIndex(0); // Should be the placeholder "Huyện" or "Quận/Huyện" - } else if (LocalStorage.districts.length == 1 && !LocalStorage.districts[0].startsWith("Loading")){ - districtComboBox.setSelectedIndex(0); // Only one actual district - } - - // Reset ward combo box as district has changed - wardModel.removeAllElements(); - wardModel.addElement("Phường"); - wardComboBox.setModel(wardModel); - wardComboBox.setEnabled(false); // Ward should be fetched after district selection + wardModel.removeAllElements(); wardModel.addElement("Phường"); wardComboBox.setEnabled(false); } private void handleGetWardResponse(GetWardResponse response) { log.info("Received ward data"); LocalStorage.wards = response.getWards(); - // Assuming wardToId is not strictly needed on client unless for specific reverse lookups - // If it is, it should be part of GetWardResponse and stored in LocalStorage - wardModel.removeAllElements(); // Clear previous items - for (String ward : LocalStorage.wards) { - wardModel.addElement(ward); - } - wardComboBox.setModel(wardModel); - wardComboBox.setEnabled(true); // Enable ward combo box - if (LocalStorage.wards.length > 0 && !LocalStorage.wards[0].startsWith("Loading")) { - wardComboBox.setSelectedIndex(0); // Select the placeholder or first ward + wardModel.removeAllElements(); + for (String ward : LocalStorage.wards) { wardModel.addElement(ward); } + wardComboBox.setEnabled(true); + if (LocalStorage.wards.length > 0 && !LocalStorage.wards[0].startsWith("Đang tải")) { + // No automatic selection } } @@ -1063,14 +851,12 @@ public class CheckUpPage extends JPanel { } private void handleGetDoctorGeneralInfoResponse(GetDoctorGeneralInfoResponse response) { - log.info("Received doctor general info"); - this.doctorOptions = response.getDoctorsName(); - LocalStorage.doctorsName = response.getDoctorsName(); + log.info("Received doctor general info (though LocalStorage.doctorsName is typically used)"); } private void handleGetCheckUpQueueUpdateResponse(GetCheckUpQueueUpdateResponse response) { log.info("Received checkup update queue"); - this.rawQueueForTv = response.getQueue(); // Store raw data for TV + this.rawQueueForTv = response.getQueue(); this.patientQueue.clear(); if (this.rawQueueForTv != null) { for (String[] patientData : this.rawQueueForTv) { @@ -1085,7 +871,7 @@ public class CheckUpPage extends JPanel { private void handleGetCheckUpQueueResponse(GetCheckUpQueueResponse response) { log.info("Received checkup queue"); - this.rawQueueForTv = response.getQueue(); // Store raw data for TV + this.rawQueueForTv = response.getQueue(); this.patientQueue.clear(); if (this.rawQueueForTv != null) { for (String[] patientData : this.rawQueueForTv) { @@ -1103,28 +889,18 @@ public class CheckUpPage extends JPanel { if (tvQueueFrame != null && tvQueueFrame.isVisible()) { if (response.getStatus() == Status.PROCESSING) { String patientIdToFind = String.valueOf(response.getPatientId()); - String patientDisplayInfo = patientIdToFind; // Default to ID if not found + String patientDisplayInfo = patientIdToFind; if (this.patientQueue != null) { for (Patient patient : this.patientQueue) { if (patient != null && patientIdToFind.equals(patient.getCheckupId())) { - String ho = patient.getCustomerLastName(); - String ten = patient.getCustomerFirstName(); - String customerDob = patient.getCustomerDob(); - String namSinh = "N/A"; - + String ho = patient.getCustomerLastName(); String ten = patient.getCustomerFirstName(); + String customerDob = patient.getCustomerDob(); String namSinh = "N/A"; try { - SimpleDateFormat dobFormat; Date parsedDate; - if (customerDob.matches("\\d+")) { - long timestamp = Long.parseLong(customerDob); - parsedDate = new Date(timestamp); - } else { - dobFormat = new SimpleDateFormat("dd/MM/yyyy"); - parsedDate = dobFormat.parse(customerDob); - } - Calendar calendar = Calendar.getInstance(); - calendar.setTime(parsedDate); + if (customerDob.matches("\\d+")) parsedDate = new Date(Long.parseLong(customerDob)); + else parsedDate = new SimpleDateFormat("dd/MM/yyyy").parse(customerDob); + Calendar calendar = Calendar.getInstance(); calendar.setTime(parsedDate); namSinh = String.valueOf(calendar.get(Calendar.YEAR)); } catch (ParseException | NumberFormatException e) { log.error("Error parsing DoB for TV display: {} for patient ID {}", customerDob, patientIdToFind, e); @@ -1141,73 +917,49 @@ public class CheckUpPage extends JPanel { } } - private void handleErrorResponse(ErrorResponse response) { + private void handleErrorResponse(ErrorResponse response) { log.error("Error response: {}", response.getError()); } private void handleFreeRoom() { int selectedRoomIndex = callRoomComboBox.getSelectedIndex(); if (selectedRoomIndex == -1) { - JOptionPane.showMessageDialog(this, "Please select a room to mark as free.", "No Room Selected", JOptionPane.WARNING_MESSAGE); + JOptionPane.showMessageDialog(this, "Vui lòng chọn một phòng để đánh dấu là trống.", "Chưa chọn phòng", JOptionPane.WARNING_MESSAGE); return; } String selectedRoomName = callRoomComboBox.getSelectedItem().toString(); - int roomId = selectedRoomIndex + 1; // 1-based room ID - - // Update the calling status label to show room is free - String freeText = "Room Status: " + selectedRoomName + " is now free"; + int roomId = selectedRoomIndex + 1; + String freeText = "Trạng thái phòng: Phòng " + selectedRoomName + " hiện đang trống"; callingStatusLabel.setText(freeText); callingStatusLabel.setForeground(new Color(0, 100, 0)); - callingStatusLabel.setBackground(new Color(230, 255, 230)); // Light green background - - // Show confirmation message - JOptionPane.showMessageDialog(this, - "Room " + selectedRoomName + " has been marked as free.", - "Room Status Updated", - JOptionPane.INFORMATION_MESSAGE); - + callingStatusLabel.setBackground(new Color(230, 255, 230)); + JOptionPane.showMessageDialog(this, "Phòng " + selectedRoomName + " đã được đánh dấu là trống.", "Cập nhật trạng thái phòng", JOptionPane.INFORMATION_MESSAGE); log.info("Room {} marked as free", selectedRoomName); - - // Update the TV Queue display as well - // if (tvQueueFrame != null && tvQueueFrame.isVisible()) { - // tvQueueFrame.markRoomAsFree(roomId); - // } - // TODO: Consider sending a packet to the server to notify other clients/instances about the room being free. - // For example: NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new UpdateRoomStatusRequest(roomId, Status.FREE)); NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new CallPatientRequest(roomId, -1, Status.EMPTY)); } private void handleCallPatient() { int selectedRow = table1.getSelectedRow(); if (selectedRow == -1) { - JOptionPane.showMessageDialog(this, "Please select a patient from the queue to call.", "No Patient Selected", JOptionPane.WARNING_MESSAGE); + JOptionPane.showMessageDialog(this, "Vui lòng chọn một bệnh nhân từ hàng đợi để gọi.", "Chưa chọn bệnh nhân", JOptionPane.WARNING_MESSAGE); return; } - if (selectedRow < 0 || selectedRow >= patientQueue.size()){ - JOptionPane.showMessageDialog(this, "Invalid patient selection.", "Error", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this, "Lựa chọn bệnh nhân không hợp lệ.", "Lỗi", JOptionPane.ERROR_MESSAGE); return; } Patient selectedPatient = patientQueue.get(selectedRow); String maKhamBenh = selectedPatient.getCheckupId(); String patientName = selectedPatient.getCustomerLastName() + " " + selectedPatient.getCustomerFirstName(); - int selectedRoom = callRoomComboBox.getSelectedIndex() + 1; - - JOptionPane.showMessageDialog(this, - "Calling patient " + patientName + " (ID: " + maKhamBenh + ") to Room " + selectedRoom, // Corrected room display - "Calling Patient", - JOptionPane.INFORMATION_MESSAGE); - - NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new CallPatientRequest(selectedRoom, Integer.parseInt(maKhamBenh), - Status.PROCESSING)); + JOptionPane.showMessageDialog(this, "Đang gọi bệnh nhân " + patientName + " (Mã KB: " + maKhamBenh + ") đến " + callRoomComboBox.getSelectedItem().toString(), "Gọi bệnh nhân", JOptionPane.INFORMATION_MESSAGE); + NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new CallPatientRequest(selectedRoom, Integer.parseInt(maKhamBenh), Status.PROCESSING)); log.info("Called patient {} (ID: {}) to room {}", patientName, maKhamBenh, selectedRoom); - - String callingText = "Calling: Patient " + patientName + " (ID: " + maKhamBenh + ") to Room " + selectedRoom + ""; // Corrected room display + String callingText = "Đang gọi: BN " + patientName + " (Mã KB: " + maKhamBenh + ") đến " + callRoomComboBox.getSelectedItem().toString() + ""; callingStatusLabel.setText(callingText); callingStatusLabel.setForeground(Color.WHITE); - callingStatusLabel.setBackground(new Color(217, 83, 79)); + callingStatusLabel.setBackground(new Color(217, 83, 79)); // Red background for calling } } diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java index 759475b..0e70b29 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java @@ -21,7 +21,9 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; @Slf4j public class MedicineDialog extends JDialog { @@ -33,9 +35,10 @@ public class MedicineDialog extends JDialog { private JComboBox UnitComboBox; private JTextField priceField; private JTextField totalField; + private JSpinner morningSpinner, noonSpinner, eveningSpinner; private JTextArea noteField; private DefaultTableModel tableModel, selectedTableModel; - private String[] medcineColumns = {"ID", "Medicine Name", "Medicine Company", "Description", "Stock", "Unit", "Price"}; + private String[] medcineColumns = {"ID", "Tên thuốc", "Công ty", "Mô tả", "Tồn kho", "ĐVT", "Giá"}; private String[][] medicineData; private final ResponseListener getMedInfoResponseListener = this::getMedInfoHandler; private TableColumnModel columnModel; @@ -44,66 +47,248 @@ public class MedicineDialog extends JDialog { private JTable selectedTable; private String[][] medicinePrescription; + private static final Logger logger = LoggerFactory.getLogger(MedicineDialog.class); public String[][] getMedicinePrescription() { return medicinePrescription; } void getMedInfoHandler(GetMedInfoResponse response) { - log.info("Received medicine data"); + logger.info("Received medicine data"); medicineData = response.getMedInfo(); tableModel.setDataVector(medicineData, medcineColumns); // resize column width columnModel = medicineTable.getColumnModel(); - columnModel.getColumn(0).setPreferredWidth(10); - columnModel.getColumn(1).setPreferredWidth(100); - columnModel.getColumn(2).setPreferredWidth(50); - columnModel.getColumn(3).setPreferredWidth(200); - columnModel.getColumn(4).setPreferredWidth(20); - columnModel.getColumn(5).setPreferredWidth(20); - columnModel.getColumn(6).setPreferredWidth(20); + columnModel.getColumn(0).setPreferredWidth(20); // ID + columnModel.getColumn(1).setPreferredWidth(150); // Name + columnModel.getColumn(2).setPreferredWidth(100); // Company + columnModel.getColumn(3).setPreferredWidth(200); // Description + columnModel.getColumn(4).setPreferredWidth(40); // Stock + columnModel.getColumn(5).setPreferredWidth(40); // Unit + columnModel.getColumn(6).setPreferredWidth(50); // Price } private void sendGetMedInfoRequest() { - log.info("Sending GetMedInfoRequest"); + logger.info("Sending GetMedInfoRequest"); NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetMedInfoRequest()); } public MedicineDialog(Frame parent) { - super(parent, "Enter Medicine Details", true); - - // Set the size of the dialog - setSize(1100, 500); + super(parent, "Thêm thuốc vào đơn", true); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setSize(1200, 750); // Adjusted size slightly for new panel + setLocationRelativeTo(parent); setResizable(true); ClientHandler.addResponseListener(GetMedInfoResponse.class, getMedInfoResponseListener); sendGetMedInfoRequest(); - setLayout(new BorderLayout()); + setLayout(new BorderLayout(10, 10)); + JPanel mainInputPanel = new JPanel(new GridBagLayout()); + mainInputPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + GridBagConstraints mainGbc = new GridBagConstraints(); + mainGbc.fill = GridBagConstraints.HORIZONTAL; + mainGbc.anchor = GridBagConstraints.NORTHWEST; + mainGbc.insets = new Insets(5, 5, 5, 5); + mainGbc.weightx = 1.0; + + JPanel medicineInfoPanel = new JPanel(new GridBagLayout()); + medicineInfoPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Thông tin thuốc", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(50, 50, 50) + )); + mainGbc.gridx = 0; + mainGbc.gridy = 0; + mainInputPanel.add(medicineInfoPanel, mainGbc); + + JPanel quantityPricePanel = new JPanel(new GridBagLayout()); + quantityPricePanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Số lượng & Giá", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(50, 50, 50) + )); + mainGbc.gridy = 1; + mainInputPanel.add(quantityPricePanel, mainGbc); + + JPanel dosagePanel = new JPanel(new GridBagLayout()); + dosagePanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Liều dùng", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(50, 50, 50) + )); + mainGbc.gridy = 2; + mainInputPanel.add(dosagePanel, mainGbc); + + JPanel notePanel = new JPanel(new GridBagLayout()); + notePanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Ghi chú", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(50, 50, 50) + )); + mainGbc.gridy = 3; + mainInputPanel.add(notePanel, mainGbc); + + JPanel addMedicineButtonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + JButton addMedicineButton = new JButton("Thêm thuốc"); + addMedicineButtonPanel.add(addMedicineButton); + mainGbc.gridy = 4; + mainInputPanel.add(addMedicineButtonPanel, mainGbc); + + mainGbc.gridy = 5; + mainGbc.weighty = 1.0; + mainInputPanel.add(new JPanel(), mainGbc); - JPanel inputPanel = new JPanel(); - inputPanel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); - - gbc.gridwidth = 1; - gbc.insets = new Insets(5, 5, 5, 5); // Padding between components - gbc.weightx = 1.0; // Components stretch horizontally - gbc.weighty = 0.0; // Allow minor vertical flexibility gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(5, 5, 5, 5); + gbc.weightx = 0.0; + gbc.weighty = 0.0; - gbc.anchor = GridBagConstraints.WEST; // Align components to the left - gbc.gridx = 0; gbc.gridy = 0; + gbc.gridx = 0; + medicineInfoPanel.add(new JLabel("Tên thuốc:"), gbc); - inputPanel.add(new JLabel("Medicine Name:"), gbc); - gbc.gridwidth = 3; gbc.gridx = 1; - medicineNameField = new JTextField(15); - inputPanel.add(medicineNameField, gbc); + gbc.weightx = 1.0; + medicineNameField = new JTextField(20); + medicineInfoPanel.add(medicineNameField, gbc); + gbc.weightx = 0.0; + + gbc.gridy++; + gbc.gridx = 0; + medicineInfoPanel.add(new JLabel("Công ty:"), gbc); + + gbc.gridx = 1; + gbc.weightx = 1.0; + medicineCompanyField = new JTextField(20); + medicineCompanyField.setEditable(false); + medicineInfoPanel.add(medicineCompanyField, gbc); + gbc.weightx = 0.0; + + gbc.gridy++; + gbc.gridx = 0; + gbc.anchor = GridBagConstraints.NORTHWEST; + medicineInfoPanel.add(new JLabel("Mô tả:"), gbc); + gbc.anchor = GridBagConstraints.WEST; + + gbc.gridx = 1; + gbc.weightx = 1.0; + medicineDescriptionField = new JTextArea(3, 20); + medicineDescriptionField.setLineWrap(true); + medicineDescriptionField.setWrapStyleWord(true); + medicineDescriptionField.setEditable(false); + JScrollPane descriptionScrollPane = new JScrollPane(medicineDescriptionField); + descriptionScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + medicineInfoPanel.add(descriptionScrollPane, gbc); + gbc.weightx = 0.0; + + gbc.gridy = 0; + gbc.gridx = 0; + quantityPricePanel.add(new JLabel("Số lượng:"), gbc); + + gbc.gridx = 1; + gbc.weightx = 0.5; + quantitySpinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1)); + quantitySpinner.setPreferredSize(new Dimension(80, 25)); + quantityPricePanel.add(quantitySpinner, gbc); + + gbc.gridx = 2; + gbc.weightx = 0.0; + quantityPricePanel.add(new JLabel("Còn lại:"), gbc); + + gbc.gridx = 3; + gbc.weightx = 0.5; + quantityLeftField = new JTextField(5); + quantityLeftField.setEditable(false); + quantityPricePanel.add(quantityLeftField, gbc); + gbc.weightx = 0.0; + + gbc.gridy++; + gbc.gridx = 0; + quantityPricePanel.add(new JLabel("ĐVT:"), gbc); + + gbc.gridx = 1; + gbc.weightx = 0.5; + String[] units = {"Viên", "Vỉ", "Hộp", "Chai", "Tuýp", "Gói"}; + UnitComboBox = new JComboBox<>(units); + UnitComboBox.setEnabled(false); + quantityPricePanel.add(UnitComboBox, gbc); + + gbc.gridx = 2; + gbc.weightx = 0.0; + quantityPricePanel.add(new JLabel("Giá (VNĐ):"), gbc); + + gbc.gridx = 3; + gbc.weightx = 0.5; + priceField = new JTextField(10); + priceField.setEditable(false); + quantityPricePanel.add(priceField, gbc); + gbc.weightx = 0.0; + + gbc.gridy++; + gbc.gridx = 0; + quantityPricePanel.add(new JLabel("Thành tiền (VNĐ):"), gbc); + + gbc.gridx = 1; + gbc.gridwidth = 3; + gbc.weightx = 1.0; + totalField = new JTextField(10); + totalField.setEditable(false); + quantityPricePanel.add(totalField, gbc); + gbc.gridwidth = 1; + gbc.weightx = 0.0; + + SpinnerNumberModel dosageModelTemplate = new SpinnerNumberModel(0, 0, 10, 1); + gbc.gridy = 0; + gbc.gridx = 0; + gbc.weightx = 0.0; + dosagePanel.add(new JLabel("Sáng:"), gbc); + gbc.gridx = 1; + gbc.weightx = 0.3; + morningSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 10, 1)); + morningSpinner.setPreferredSize(new Dimension(60,25)); + dosagePanel.add(morningSpinner, gbc); + gbc.gridx = 2; + gbc.weightx = 0.0; + dosagePanel.add(new JLabel("Trưa:"), gbc); + gbc.gridx = 3; + gbc.weightx = 0.3; + noonSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 10, 1)); + noonSpinner.setPreferredSize(new Dimension(60,25)); + dosagePanel.add(noonSpinner, gbc); + gbc.gridx = 4; + gbc.weightx = 0.0; + dosagePanel.add(new JLabel("Chiều:"), gbc); + gbc.gridx = 5; + gbc.weightx = 0.3; + eveningSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 10, 1)); + eveningSpinner.setPreferredSize(new Dimension(60,25)); + dosagePanel.add(eveningSpinner, gbc); + gbc.weightx = 0.0; + + gbc.gridy = 0; + gbc.gridx = 0; + notePanel.add(new JLabel("Ghi chú:"), gbc); + gbc.anchor = GridBagConstraints.WEST; + + gbc.gridx = 1; + gbc.weightx = 1.0; + gbc.fill = GridBagConstraints.BOTH; + gbc.weighty = 1.0; + noteField = new JTextArea(3, 20); + noteField.setLineWrap(true); + noteField.setWrapStyleWord(true); + JScrollPane noteScrollPane = new JScrollPane(noteField); + noteScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + notePanel.add(noteScrollPane, gbc); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 0.0; + gbc.weighty = 0.0; - // Listen for changes in the text event tableModel = new DefaultTableModel(medicineData, medcineColumns) { @Override public boolean isCellEditable(int row, int column) { @@ -111,295 +296,318 @@ public class MedicineDialog extends JDialog { } }; medicineTable = new JTable(tableModel); - medicineTable.setFont(new Font("Serif", Font.BOLD, 20)); - medicineTable.setRowHeight(30); + medicineTable.setFont(new Font("Arial", Font.PLAIN, 14)); + medicineTable.setRowHeight(25); medicineTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - JScrollPane scrollPane = new JScrollPane(medicineTable); + medicineTable.setAutoCreateRowSorter(true); + JScrollPane medicineTableScrollPane = new JScrollPane(medicineTable); + medicineTableScrollPane.setPreferredSize(new Dimension(600, 250)); + String[] selectedColumnNames = {"ID", "Tên thuốc", "SL", "ĐVT", "Sáng", "Trưa", "Chiều", "Đơn giá", "Thành tiền", "Ghi chú"}; + selectedTableModel = new DefaultTableModel(new String[][]{}, selectedColumnNames) { + @Override + public boolean isCellEditable(int row, int column) { + return column == 2 || column == 4 || column == 5 || column == 6 || column == 9; + } + }; + selectedTable = new JTable(selectedTableModel); + selectedTable.setFont(new Font("Arial", Font.PLAIN, 14)); + selectedTable.setRowHeight(25); + JScrollPane selectedTableScrollPane = new JScrollPane(selectedTable); + selectedTableScrollPane.setPreferredSize(new Dimension(600, 150)); - medicineTable.setPreferredScrollableViewportSize(new Dimension(625, 200)); + JPanel removeButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + JButton removeMedicineButton = new JButton("Xóa thuốc đã chọn"); + removeButtonPanel.add(removeMedicineButton); + JPanel rightPanel = new JPanel(new BorderLayout(0, 5)); + rightPanel.add(medicineTableScrollPane, BorderLayout.CENTER); + JPanel bottomRightsPanel = new JPanel(new BorderLayout(0,5)); + bottomRightsPanel.add(selectedTableScrollPane, BorderLayout.CENTER); + bottomRightsPanel.add(removeButtonPanel, BorderLayout.SOUTH); + rightPanel.add(bottomRightsPanel, BorderLayout.SOUTH); - scrollPane.setPreferredSize(new Dimension(625, 200)); + JScrollPane mainInputScrollPane = new JScrollPane(mainInputPanel); + mainInputScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + mainInputScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + mainInputScrollPane.setBorder(BorderFactory.createEmptyBorder()); + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mainInputScrollPane, rightPanel); + splitPane.setResizeWeight(0.45); + add(splitPane, BorderLayout.CENTER); + + JPanel bottomButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + JButton okButton = new JButton("Lưu"); + JButton cancelButton = new JButton("Hủy"); + bottomButtonPanel.add(okButton); + bottomButtonPanel.add(cancelButton); + add(bottomButtonPanel, BorderLayout.SOUTH); + + medicineNameField.getDocument().addDocumentListener(new DocumentListener() { + public void changedUpdate(DocumentEvent e) { SwingUtilities.invokeLater(() -> filterMedicineTable()); } + public void removeUpdate(DocumentEvent e) { SwingUtilities.invokeLater(() -> filterMedicineTable()); } + public void insertUpdate(DocumentEvent e) { SwingUtilities.invokeLater(() -> filterMedicineTable()); } + }); medicineTable.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - SwingUtilities.invokeLater(() -> { - int selectedRow = medicineTable.getSelectedRow(); - if (selectedRow != -1) { - handleRowSelection(selectedRow); - } - }); + if (e.getClickCount() == 1) { + SwingUtilities.invokeLater(() -> { + int selectedRow = medicineTable.getSelectedRow(); + if (selectedRow != -1) { + handleMedicineTableRowSelection(selectedRow); + } + }); + } else if (e.getClickCount() == 2) { + SwingUtilities.invokeLater(() -> { + int selectedRow = medicineTable.getSelectedRow(); + if (selectedRow != -1) { + addSelectedMedicine(); + } + }); + } } }); - medicineNameField.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { - handleRowSelection(medicineTable.getSelectedRow()); - } - } - }); - - - medicineNameField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void insertUpdate(DocumentEvent e) { - findAndSelectRow(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - findAndSelectRow(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - findAndSelectRow(); - } - - private void findAndSelectRow() { - String searchText = medicineNameField.getText().trim(); - if (searchText.isEmpty()) { - // Clear selection if search text is empty - medicineTable.clearSelection(); - return; - } - - boolean found = false; - for (int row = 0; row < medicineTable.getRowCount(); row++) { - // Assuming the search is targeting the name column (column index 1) - String cellValue = medicineTable.getValueAt(row, 1).toString(); - if (TextUtils.removeAccents(cellValue.toLowerCase()).contains(TextUtils.removeAccents(searchText. - toLowerCase()))) { - medicineTable.setRowSelectionInterval(row, row); - medicineTable.scrollRectToVisible(medicineTable.getCellRect(row, 1, true)); - found = true; - break; // Stop after selecting the first match + if (medicineTable.getSelectedRow() != -1) addSelectedMedicine(); + } else if (e.getKeyCode() == KeyEvent.VK_DOWN) { + if (medicineTable.getRowCount() > 0) { + int selectedRow = medicineTable.getSelectedRow(); + selectedRow = (selectedRow < medicineTable.getRowCount() - 1) ? selectedRow + 1 : 0; + medicineTable.setRowSelectionInterval(selectedRow, selectedRow); + medicineTable.scrollRectToVisible(medicineTable.getCellRect(selectedRow, 0, true)); + handleMedicineTableRowSelection(selectedRow); + } + } else if (e.getKeyCode() == KeyEvent.VK_UP) { + if (medicineTable.getRowCount() > 0) { + int selectedRow = medicineTable.getSelectedRow(); + selectedRow = (selectedRow > 0) ? selectedRow - 1 : medicineTable.getRowCount() - 1; + medicineTable.setRowSelectionInterval(selectedRow, selectedRow); + medicineTable.scrollRectToVisible(medicineTable.getCellRect(selectedRow, 0, true)); + handleMedicineTableRowSelection(selectedRow); } } - - if (!found) { - medicineTable.clearSelection(); // No matches found - } } }); + quantitySpinner.addChangeListener(e -> updateTotalField()); + UnitComboBox.addActionListener(e -> updateTotalField()); - gbc.gridwidth = 1; - gbc.gridx = 0; - gbc.gridy++; - inputPanel.add(new JLabel("Company:"), gbc); - gbc.gridwidth = 3; - gbc.gridx = 1; - medicineCompanyField = new JTextField(15); - inputPanel.add(medicineCompanyField, gbc); + addMedicineButton.addActionListener(e -> addSelectedMedicine()); + removeMedicineButton.addActionListener(e -> removeSelectedMedicine()); - gbc.anchor = GridBagConstraints.NORTHWEST; - gbc.gridwidth = 1; - gbc.gridx = 0; - gbc.gridy++; - inputPanel.add(new JLabel("Description:"), gbc); - gbc.gridwidth = 3; - gbc.gridx = 1; - medicineDescriptionField = new JTextArea(2, 15); - inputPanel.add(medicineDescriptionField, gbc); - gbc.anchor = GridBagConstraints.WEST; - - - gbc.gridwidth = 1; - gbc.gridx = 0; - gbc.gridy++; - inputPanel.add(new JLabel("Quantity:"), gbc); - - gbc.gridx = 1; - // Create and set preferred size for the spinner - quantitySpinner = new JSpinner(); - quantitySpinner.setPreferredSize(new Dimension(100, 25)); // Adjust width and height as needed - inputPanel.add(quantitySpinner, gbc); - - - gbc.gridx = 2; - inputPanel.add(new JLabel("Left"), gbc); - - gbc.gridx = 3; - quantityLeftField = new JTextField(5); - quantityLeftField.setEditable(false); - inputPanel.add(quantityLeftField, gbc); - - - gbc.gridwidth = 1; - gbc.gridx = 0; - gbc.gridy++; - inputPanel.add(new JLabel("Unit:"), gbc); - gbc.gridwidth = 3; - gbc.gridx = 1; - String[] units = {"Viên", "Vỉ", "Hộp", "Ống"}; - UnitComboBox = new JComboBox<>(units); - inputPanel.add(UnitComboBox, gbc); - - gbc.gridwidth = 1; - gbc.gridx = 0; - gbc.gridy++; - inputPanel.add(new JLabel("Price:"), gbc); - gbc.gridwidth = 3; - gbc.gridx = 1; - priceField = new JTextField(15); - inputPanel.add(priceField, gbc); - - gbc.gridwidth = 1; - gbc.gridx = 0; - gbc.gridy++; - inputPanel.add(new JLabel("Total:"), gbc); - gbc.gridwidth = 3; - gbc.gridx = 1; - totalField = new JTextField(15); - inputPanel.add(totalField, gbc); - - gbc.anchor = GridBagConstraints.NORTHWEST; - gbc.gridwidth = 1; - gbc.gridx = 0; - gbc.gridy++; - inputPanel.add(new JLabel("Note:"), gbc); - gbc.gridwidth = 3; - gbc.gridx = 1; - noteField = new JTextArea(2, 15); - inputPanel.add(noteField, gbc); - gbc.anchor = GridBagConstraints.WEST; - - quantitySpinner.addChangeListener(e -> { - int quantity = (int) quantitySpinner.getValue(); - if (quantity < 0) { - quantity = 0; - quantitySpinner.setValue(quantity); + okButton.addActionListener(e -> { + collectPrescriptionData(); + if (medicinePrescription == null || medicinePrescription.length == 0) { + JOptionPane.showMessageDialog(this, "Chưa có thuốc nào được chọn.", "Thông báo", JOptionPane.INFORMATION_MESSAGE); + return; } - else if (quantity > Integer.parseInt(quantityLeftField.getText())) { - JOptionPane.showMessageDialog(this, "Quantity exceeds stock", "Error", JOptionPane.ERROR_MESSAGE); - quantity = Integer.parseInt(quantityLeftField.getText()); - quantitySpinner.setValue(quantity); - } - double price = Double.parseDouble(priceField.getText()); - totalField.setText(String.valueOf(quantity * price)); - }); - - - - // Set the split pane - JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, inputPanel, scrollPane); - splitPane.setResizeWeight(0.5); // Allocate more space to the inputPanel - // Wrap the splitPane in a JPanel to control height - JPanel topPanel = new JPanel(new BorderLayout()); - topPanel.add(splitPane, BorderLayout.CENTER); - - - // Create a table to display selected medicines - String[] columnNames = {"ID", "Medicine Name", "Company", "Description", "Quantity", "Unit", "Single Price", "Total Price", "Note"}; - selectedTableModel = new DefaultTableModel(new String[][]{}, columnNames) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - }; - - selectedTable = new JTable(selectedTableModel); - JScrollPane tableScrollPane = new JScrollPane(selectedTable); - - // Create a panel for add and remove buttons - JPanel buttonPanel = new JPanel(); - JButton addButton = new JButton("Add"); - JButton removeButton = new JButton("Remove"); - JButton submitButton = new JButton("Submit"); - buttonPanel.add(addButton); - buttonPanel.add(removeButton); - buttonPanel.add(submitButton); - - addButton.addActionListener(e -> { - int selectedRow = medicineTable.getSelectedRow(); - if (selectedRow != -1) { - int modelRow = medicineTable.convertRowIndexToModel(selectedRow); - if(selectedMedicine.containsKey(tableModel.getValueAt(modelRow, 0).toString())) { - JOptionPane.showMessageDialog(this, "Medicine already added", "Error", JOptionPane.ERROR_MESSAGE); - return; - } - selectedMedicine.put((String) tableModel.getValueAt(modelRow, 0), true); - Object[] rowData = new Object[9]; - int columns = tableModel.getColumnCount(); - for (int i = 0; i < columns ; i++) { - rowData[i] = tableModel.getValueAt(modelRow, i); - } - - rowData[4] = quantitySpinner.getValue(); - rowData[7] = totalField.getText(); - rowData[8] = noteField.getText(); - selectedTableModel.addRow(rowData); - log.info("Added medicine with id: {}", tableModel.getValueAt(modelRow, 0)); - } - }); - - removeButton.addActionListener(e -> { - int selectedRow = selectedTable.getSelectedRow(); - if (selectedRow != -1) { - String id = selectedTableModel.getValueAt(selectedRow, 0).toString(); - selectedMedicine.remove(id); - log.info("Removed medicine with id: {}", id); - selectedTableModel.removeRow(selectedRow); - } - }); - - submitButton.addActionListener(e -> { - int rows = selectedTableModel.getRowCount(); - medicinePrescription = new String[rows][9]; - for (int i = 0; i < rows; i++) { - for (int j = 0; j < 9; j++) { - medicinePrescription[i][j] = selectedTableModel.getValueAt(i, j).toString(); - } + logger.info("Prescription saved."); + for (String[] row : medicinePrescription) { + logger.info("Med: ID={}, Name={}, Qty={}, Unit={}, M={}, N={}, E={}, Price={}, Total={}, Note={}", + row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9]); } dispose(); }); - // Add components to the dialog - add(topPanel, BorderLayout.NORTH); - add(tableScrollPane, BorderLayout.CENTER); - add(buttonPanel, BorderLayout.SOUTH); - - setLocationRelativeTo(parent); + cancelButton.addActionListener(e -> { + medicinePrescription = null; + dispose(); + }); } - private void handleRowSelection(int selectedRow) { - String id = (String) tableModel.getValueAt(selectedRow, 0); - String name = (String) tableModel.getValueAt(selectedRow, 1); - String company = (String) tableModel.getValueAt(selectedRow, 2); - String description = (String) tableModel.getValueAt(selectedRow, 3); - String stock = (String) tableModel.getValueAt(selectedRow, 4); - String unit = (String) tableModel.getValueAt(selectedRow, 5); - String price = (String) tableModel.getValueAt(selectedRow, 6); + private void filterMedicineTable() { + String filterText = medicineNameField.getText().trim(); + if (medicineData == null) return; + if (filterText.isEmpty()) { + tableModel.setDataVector(medicineData, medcineColumns); + resizeMedicineTableColumns(); + medicineTable.clearSelection(); + } else { + List filteredData = new ArrayList<>(); + String lowerCaseFilterText = TextUtils.removeAccents(filterText.toLowerCase()); + for (String[] row : medicineData) { + if (row.length > 1 && row[1] != null && TextUtils.removeAccents(row[1].toLowerCase()).contains(lowerCaseFilterText)) { + filteredData.add(row); + } + } + tableModel.setDataVector(filteredData.toArray(new String[0][0]), medcineColumns); + resizeMedicineTableColumns(); + if (!filteredData.isEmpty()) { + medicineTable.setRowSelectionInterval(0,0); + handleMedicineTableRowSelection(0); + } else { + clearInputFields(); + } + } + } + private void resizeMedicineTableColumns() { + columnModel = medicineTable.getColumnModel(); + columnModel.getColumn(0).setPreferredWidth(20); + columnModel.getColumn(1).setPreferredWidth(150); + columnModel.getColumn(2).setPreferredWidth(100); + columnModel.getColumn(3).setPreferredWidth(200); + columnModel.getColumn(4).setPreferredWidth(40); + columnModel.getColumn(5).setPreferredWidth(40); + columnModel.getColumn(6).setPreferredWidth(50); + } - medicineNameField.setText(name); - medicineCompanyField.setText(company); - medicineDescriptionField.setText(description); - quantitySpinner.setValue(0); - quantityLeftField.setText(stock); - UnitComboBox.setSelectedItem(unit); - priceField.setText(price); - totalField.setText("0"); + private void handleMedicineTableRowSelection(int viewRow) { + if (viewRow < 0 || viewRow >= medicineTable.getRowCount()) { + clearInputFields(); + return; + } + int modelRow = medicineTable.convertRowIndexToModel(viewRow); + medicineCompanyField.setText(tableModel.getValueAt(modelRow, 2).toString()); + medicineDescriptionField.setText(tableModel.getValueAt(modelRow, 3).toString()); + quantityLeftField.setText(tableModel.getValueAt(modelRow, 4).toString()); + UnitComboBox.setSelectedItem(tableModel.getValueAt(modelRow, 5).toString()); + priceField.setText(tableModel.getValueAt(modelRow, 6).toString()); + medicineNameField.setText(tableModel.getValueAt(modelRow, 1).toString()); + quantitySpinner.setValue(1); + morningSpinner.setValue(0); + noonSpinner.setValue(0); + eveningSpinner.setValue(0); + noteField.setText(""); + updateTotalField(); + } + + private void clearInputFields() { + medicineCompanyField.setText(""); + medicineDescriptionField.setText(""); + quantityLeftField.setText(""); + UnitComboBox.setSelectedIndex(0); + priceField.setText(""); + totalField.setText(""); + quantitySpinner.setValue(1); + morningSpinner.setValue(0); + noonSpinner.setValue(0); + eveningSpinner.setValue(0); noteField.setText(""); } + private void updateTotalField() { + try { + int quantity = (Integer) quantitySpinner.getValue(); + if (quantity < 0) { quantitySpinner.setValue(0); quantity = 0; } + double price = priceField.getText().isEmpty() ? 0 : Double.parseDouble(priceField.getText()); + totalField.setText(String.format("%.0f", quantity * price)); + } catch (NumberFormatException e) { + totalField.setText("Giá trị không hợp lệ"); + logger.error("Error parsing price for total calculation: " + priceField.getText(), e); + } + } + + private void addSelectedMedicine() { + int selectedRowInMedicineTable = medicineTable.getSelectedRow(); + if (selectedRowInMedicineTable == -1 && medicineNameField.getText().isEmpty()) { + JOptionPane.showMessageDialog(this, "Vui lòng chọn một thuốc từ danh sách hoặc tìm kiếm.", "Chưa chọn thuốc", JOptionPane.WARNING_MESSAGE); + return; + } + + String medicineId, name, unit, currentPrice; + + if (selectedRowInMedicineTable != -1) { + int modelRow = medicineTable.convertRowIndexToModel(selectedRowInMedicineTable); + medicineId = tableModel.getValueAt(modelRow, 0).toString(); + name = tableModel.getValueAt(modelRow, 1).toString(); + unit = tableModel.getValueAt(modelRow, 5).toString(); + currentPrice = tableModel.getValueAt(modelRow, 6).toString(); + } else { + String searchName = medicineNameField.getText().trim(); + String foundId = null, foundUnit = null, foundPrice = null, actualName = null; + for (String[] med : medicineData) { + if (TextUtils.removeAccents(med[1].toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) { + foundId = med[0]; actualName = med[1]; foundUnit = med[5]; foundPrice = med[6]; + break; + } + } + if (foundId == null) { + JOptionPane.showMessageDialog(this, "Không tìm thấy ID cho thuốc: " + searchName + ". Vui lòng chọn từ bảng.", "Lỗi", JOptionPane.ERROR_MESSAGE); + return; + } + medicineId = foundId; name = actualName; unit = foundUnit; currentPrice = foundPrice; + } + + int quantity = (Integer) quantitySpinner.getValue(); + int morningVal = (Integer) morningSpinner.getValue(); + int noonVal = (Integer) noonSpinner.getValue(); + int eveningVal = (Integer) eveningSpinner.getValue(); + String note = noteField.getText(); + String totalAmount = totalField.getText(); + + if (quantity <= 0) { + JOptionPane.showMessageDialog(this, "Số lượng phải lớn hơn 0.", "Số lượng không hợp lệ", JOptionPane.WARNING_MESSAGE); + return; + } + selectedTableModel.addRow(new Object[]{medicineId, name, quantity, unit, morningVal, noonVal, eveningVal, currentPrice, totalAmount, note}); + } + + private void removeSelectedMedicine() { + int selectedRow = selectedTable.getSelectedRow(); + if (selectedRow != -1) { + selectedTableModel.removeRow(selectedRow); + } else { + JOptionPane.showMessageDialog(this, "Vui lòng chọn một thuốc từ bảng dưới để xóa.", "Chưa chọn thuốc", JOptionPane.WARNING_MESSAGE); + } + } + + private void collectPrescriptionData() { + int rowCount = selectedTableModel.getRowCount(); + if (rowCount == 0) { + medicinePrescription = new String[0][0]; + return; + } + medicinePrescription = new String[rowCount][10]; + for (int i = 0; i < rowCount; i++) { + medicinePrescription[i][0] = selectedTableModel.getValueAt(i, 0).toString(); + medicinePrescription[i][1] = selectedTableModel.getValueAt(i, 1).toString(); + medicinePrescription[i][2] = selectedTableModel.getValueAt(i, 2).toString(); + medicinePrescription[i][3] = selectedTableModel.getValueAt(i, 3).toString(); + medicinePrescription[i][4] = selectedTableModel.getValueAt(i, 4).toString(); + medicinePrescription[i][5] = selectedTableModel.getValueAt(i, 5).toString(); + medicinePrescription[i][6] = selectedTableModel.getValueAt(i, 6).toString(); + medicinePrescription[i][7] = selectedTableModel.getValueAt(i, 7).toString(); + medicinePrescription[i][8] = selectedTableModel.getValueAt(i, 8).toString(); + medicinePrescription[i][9] = selectedTableModel.getValueAt(i, 9).toString(); + } + } + + @Override + public void dispose() { + logger.info("Cleaning up MedicineDialog listeners"); + ClientHandler.deleteListener(GetMedInfoResponse.class); + super.dispose(); + } + public static void main(String[] args) { - JFrame frame = new JFrame(); + SwingUtilities.invokeLater(() -> { + JFrame frame = new JFrame("Test Medicine Dialog"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 300); - frame.setVisible(true); - + frame.setLocationRelativeTo(null); JButton openDialogButton = new JButton("Open Medicine Dialog"); openDialogButton.addActionListener(e -> { MedicineDialog dialog = new MedicineDialog(frame); dialog.setVisible(true); + String[][] prescription = dialog.getMedicinePrescription(); + if (prescription != null && prescription.length > 0) { + System.out.println("Prescription obtained:"); + for (String[] item : prescription) { + System.out.println(String.format("ID:%s, Name:%s, Qty:%s, Unit:%s, M:%s, N:%s, E:%s, Price:%s, Total:%s, Note:%s", + (Object[]) item)); + } + } else { + System.out.println("No prescription or dialog was cancelled."); + } + }); + frame.setLayout(new FlowLayout()); + frame.add(openDialogButton); + frame.setVisible(true); }); - - frame.add(openDialogButton, BorderLayout.CENTER); } } diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java index c3a70a5..6721114 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java @@ -21,7 +21,9 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; @Slf4j public class ServiceDialog extends JDialog { @@ -31,70 +33,154 @@ public class ServiceDialog extends JDialog { private JTextField totalField; private JTextArea noteField; private DefaultTableModel tableModel, selectedTableModel; - private String[] serviceColumns = {"ID", "Service Name", "Price"}; + private String[] serviceColumns = {"ID", "Tên dịch vụ", "Giá (VNĐ)"}; private String[][] serviceData; private final ResponseListener getSerInfoResponseListener = this::getSerInfoHandler; private TableColumnModel columnModel; - private HashMap selectedService = new HashMap<>(); private JTable serviceTable; private JTable selectedTable; private String[][] servicePrescription; + private static final Logger logger = LoggerFactory.getLogger(ServiceDialog.class); public String[][] getServicePrescription() { return servicePrescription; } void getSerInfoHandler(GetSerInfoResponse response) { - log.info("Received service data"); + logger.info("Received service data"); serviceData = response.getSerInfo(); tableModel.setDataVector(serviceData, serviceColumns); - - // resize column width - columnModel = serviceTable.getColumnModel(); - columnModel.getColumn(0).setPreferredWidth(10); - columnModel.getColumn(1).setPreferredWidth(200); - columnModel.getColumn(2).setPreferredWidth(50); + resizeServiceTableColumns(); } void sendGetSerInfoRequest() { - log.info("Sending GetSerInfoRequest"); + logger.info("Sending GetSerInfoRequest"); NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetSerInfoRequest()); } public ServiceDialog(Frame parent) { - super(parent, "Enter Service Details", true); - - // Set the size of the dialog - setSize(1100, 500); + super(parent, "Thêm dịch vụ", true); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + setSize(1100, 600); + setLocationRelativeTo(parent); setResizable(true); ClientHandler.addResponseListener(GetSerInfoResponse.class, getSerInfoResponseListener); sendGetSerInfoRequest(); - setLayout(new BorderLayout()); + setLayout(new BorderLayout(10, 10)); + JPanel mainInputPanel = new JPanel(new GridBagLayout()); + mainInputPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + GridBagConstraints mainGbc = new GridBagConstraints(); + mainGbc.fill = GridBagConstraints.HORIZONTAL; + mainGbc.anchor = GridBagConstraints.NORTHWEST; + mainGbc.insets = new Insets(5, 5, 5, 5); + mainGbc.weightx = 1.0; + + JPanel serviceInfoPanel = new JPanel(new GridBagLayout()); + serviceInfoPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Thông tin dịch vụ", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(50, 50, 50) + )); + mainGbc.gridx = 0; + mainGbc.gridy = 0; + mainInputPanel.add(serviceInfoPanel, mainGbc); + + JPanel notePanel = new JPanel(new GridBagLayout()); + notePanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Ghi chú", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(50, 50, 50) + )); + mainGbc.gridy = 1; + mainInputPanel.add(notePanel, mainGbc); + + JPanel addServiceButtonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + JButton addServiceButton = new JButton("Thêm dịch vụ"); + addServiceButtonPanel.add(addServiceButton); + mainGbc.gridy = 2; + mainInputPanel.add(addServiceButtonPanel, mainGbc); + + mainGbc.gridy = 3; + mainGbc.weighty = 1.0; + mainInputPanel.add(new JPanel(), mainGbc); - JPanel inputPanel = new JPanel(); - inputPanel.setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); - - - gbc.insets = new Insets(5, 5, 5, 5); // Padding between components - gbc.weightx = 1.0; // Components stretch horizontally - gbc.weighty = 0.0; // Allow minor vertical flexibility gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(5, 5, 5, 5); + gbc.weightx = 0.0; + gbc.weighty = 0.0; - gbc.anchor = GridBagConstraints.WEST; // Align components to the left - gbc.gridx = 0; gbc.gridy = 0; + gbc.gridx = 0; + serviceInfoPanel.add(new JLabel("Tên dịch vụ:"), gbc); - inputPanel.add(new JLabel("Service Name:"), gbc); gbc.gridx = 1; - serviceNameField = new JTextField(15); - inputPanel.add(serviceNameField, gbc); + gbc.gridwidth = 3; + gbc.weightx = 1.0; + serviceNameField = new JTextField(20); + serviceInfoPanel.add(serviceNameField, gbc); + gbc.gridwidth = 1; + gbc.weightx = 0.0; + + gbc.gridy++; + gbc.gridx = 0; + serviceInfoPanel.add(new JLabel("Số lượng:"), gbc); + + gbc.gridx = 1; + gbc.weightx = 0.5; + quantitySpinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1)); + quantitySpinner.setPreferredSize(new Dimension(80, 25)); + serviceInfoPanel.add(quantitySpinner, gbc); + gbc.weightx = 0.0; + + gbc.gridx = 2; + serviceInfoPanel.add(new JLabel("Đơn giá (VNĐ):"), gbc); + + gbc.gridx = 3; + gbc.weightx = 0.5; + priceField = new JTextField(10); + priceField.setEditable(false); + serviceInfoPanel.add(priceField, gbc); + gbc.weightx = 0.0; + + gbc.gridy++; + gbc.gridx = 0; + serviceInfoPanel.add(new JLabel("Thành tiền (VNĐ):"), gbc); + + gbc.gridx = 1; + gbc.gridwidth = 3; + gbc.weightx = 1.0; + totalField = new JTextField(10); + totalField.setEditable(false); + serviceInfoPanel.add(totalField, gbc); + gbc.gridwidth = 1; + gbc.weightx = 0.0; + + gbc.gridy = 0; + gbc.gridx = 0; + gbc.anchor = GridBagConstraints.NORTHWEST; + notePanel.add(new JLabel("Ghi chú:"), gbc); + gbc.anchor = GridBagConstraints.WEST; + + gbc.gridx = 1; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.fill = GridBagConstraints.BOTH; + noteField = new JTextArea(3, 20); + noteField.setLineWrap(true); + noteField.setWrapStyleWord(true); + JScrollPane noteScrollPane = new JScrollPane(noteField); + noteScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + notePanel.add(noteScrollPane, gbc); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 0.0; + gbc.weighty = 0.0; - // Listen for changes in the text event tableModel = new DefaultTableModel(serviceData, serviceColumns) { @Override public boolean isCellEditable(int row, int column) { @@ -102,27 +188,77 @@ public class ServiceDialog extends JDialog { } }; serviceTable = new JTable(tableModel); - serviceTable.setFont(new Font("Serif", Font.BOLD, 20)); - serviceTable.setRowHeight(30); + serviceTable.setFont(new Font("Arial", Font.PLAIN, 14)); + serviceTable.setRowHeight(25); serviceTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - JScrollPane scrollPane = new JScrollPane(serviceTable); + serviceTable.setAutoCreateRowSorter(true); + JScrollPane serviceTableScrollPane = new JScrollPane(serviceTable); + serviceTableScrollPane.setPreferredSize(new Dimension(550, 250)); + String[] selectedColumnNames = {"ID", "Tên dịch vụ", "Số lượng", "Đơn giá", "Thành tiền", "Ghi chú"}; + selectedTableModel = new DefaultTableModel(new String[][]{}, selectedColumnNames) { + @Override + public boolean isCellEditable(int row, int column) { + return column == 2 || column == 5; + } + }; + selectedTable = new JTable(selectedTableModel); + selectedTable.setFont(new Font("Arial", Font.PLAIN, 14)); + selectedTable.setRowHeight(25); + JScrollPane selectedTableScrollPane = new JScrollPane(selectedTable); + selectedTableScrollPane.setPreferredSize(new Dimension(550, 150)); - serviceTable.setPreferredScrollableViewportSize(new Dimension(625, 200)); + JPanel removeServiceButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + JButton removeServiceButton = new JButton("Xóa dịch vụ đã chọn"); + removeServiceButtonPanel.add(removeServiceButton); + JPanel rightPanel = new JPanel(new BorderLayout(0,5)); + rightPanel.add(serviceTableScrollPane, BorderLayout.CENTER); + JPanel bottomRightPanel = new JPanel(new BorderLayout(0,5)); + bottomRightPanel.add(selectedTableScrollPane, BorderLayout.CENTER); + bottomRightPanel.add(removeServiceButtonPanel, BorderLayout.SOUTH); + rightPanel.add(bottomRightPanel, BorderLayout.SOUTH); - scrollPane.setPreferredSize(new Dimension(625, 200)); + JScrollPane mainInputScrollPane = new JScrollPane(mainInputPanel); + mainInputScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + mainInputScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + mainInputScrollPane.setBorder(BorderFactory.createEmptyBorder()); + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, mainInputScrollPane, rightPanel); + splitPane.setResizeWeight(0.4); + add(splitPane, BorderLayout.CENTER); + + JPanel bottomButtonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + JButton okButton = new JButton("Lưu"); + JButton cancelButton = new JButton("Hủy"); + bottomButtonPanel.add(okButton); + bottomButtonPanel.add(cancelButton); + add(bottomButtonPanel, BorderLayout.SOUTH); + + serviceNameField.getDocument().addDocumentListener(new DocumentListener() { + public void changedUpdate(DocumentEvent e) { SwingUtilities.invokeLater(() -> filterServiceTable()); } + public void removeUpdate(DocumentEvent e) { SwingUtilities.invokeLater(() -> filterServiceTable()); } + public void insertUpdate(DocumentEvent e) { SwingUtilities.invokeLater(() -> filterServiceTable()); } + }); serviceTable.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { - SwingUtilities.invokeLater(() -> { - int selectedRow = serviceTable.getSelectedRow(); - if (selectedRow != -1) { - handleRowSelection(selectedRow); - } - }); + if (e.getClickCount() == 1) { + SwingUtilities.invokeLater(() -> { + int selectedRow = serviceTable.getSelectedRow(); + if (selectedRow != -1) { + handleServiceTableRowSelection(selectedRow); + } + }); + } else if (e.getClickCount() == 2) { + SwingUtilities.invokeLater(() -> { + int selectedRow = serviceTable.getSelectedRow(); + if (selectedRow != -1) { + addSelectedService(); + } + }); + } } }); @@ -130,206 +266,255 @@ public class ServiceDialog extends JDialog { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { - handleRowSelection(serviceTable.getSelectedRow()); - } - } - }); - - - serviceNameField.getDocument().addDocumentListener(new DocumentListener() { - @Override - public void insertUpdate(DocumentEvent e) { - findAndSelectRow(); - } - - @Override - public void removeUpdate(DocumentEvent e) { - findAndSelectRow(); - } - - @Override - public void changedUpdate(DocumentEvent e) { - findAndSelectRow(); - } - - private void findAndSelectRow() { - String searchText = serviceNameField.getText().trim(); - if (searchText.isEmpty()) { - // Clear selection if search text is empty - serviceTable.clearSelection(); - return; - } - - boolean found = false; - for (int row = 0; row < serviceTable.getRowCount(); row++) { - // Assuming the search is targeting the name column (column index 1) - String cellValue = serviceTable.getValueAt(row, 1).toString(); - if (TextUtils.removeAccents(cellValue.toLowerCase()).contains(TextUtils.removeAccents(searchText - .toLowerCase()))) { - serviceTable.setRowSelectionInterval(row, row); - serviceTable.scrollRectToVisible(serviceTable.getCellRect(row, 1, true)); - found = true; - break; // Stop after selecting the first match + if (serviceTable.getSelectedRow() != -1) { + addSelectedService(); + } + } else if (e.getKeyCode() == KeyEvent.VK_DOWN) { + if (serviceTable.getRowCount() > 0) { + int selectedRow = serviceTable.getSelectedRow(); + if (selectedRow < serviceTable.getRowCount() - 1) { + serviceTable.setRowSelectionInterval(selectedRow + 1, selectedRow + 1); + serviceTable.scrollRectToVisible(serviceTable.getCellRect(selectedRow + 1, 0, true)); + handleServiceTableRowSelection(selectedRow+1); + } else { + serviceTable.setRowSelectionInterval(0,0); + serviceTable.scrollRectToVisible(serviceTable.getCellRect(0,0,true)); + handleServiceTableRowSelection(0); + } + } + } else if (e.getKeyCode() == KeyEvent.VK_UP) { + if (serviceTable.getRowCount() > 0) { + int selectedRow = serviceTable.getSelectedRow(); + if (selectedRow > 0) { + serviceTable.setRowSelectionInterval(selectedRow - 1, selectedRow - 1); + serviceTable.scrollRectToVisible(serviceTable.getCellRect(selectedRow - 1, 0, true)); + handleServiceTableRowSelection(selectedRow - 1); + } else { + serviceTable.setRowSelectionInterval(serviceTable.getRowCount()-1, serviceTable.getRowCount()-1); + serviceTable.scrollRectToVisible(serviceTable.getCellRect(serviceTable.getRowCount()-1,0,true)); + handleServiceTableRowSelection(serviceTable.getRowCount()-1); + } } } - - if (!found) { - serviceTable.clearSelection(); // No matches found - } } }); - gbc.gridx = 0; - gbc.gridy++; - inputPanel.add(new JLabel("Quantity:"), gbc); + quantitySpinner.addChangeListener(e -> updateTotalField()); - gbc.gridx = 1; - // Create and set preferred size for the spinner - quantitySpinner = new JSpinner(); - quantitySpinner.setPreferredSize(new Dimension(100, 25)); // Adjust width and height as needed - inputPanel.add(quantitySpinner, gbc); + addServiceButton.addActionListener(e -> addSelectedService()); + removeServiceButton.addActionListener(e -> removeSelectedService()); - - gbc.gridx = 0; - gbc.gridy++; - inputPanel.add(new JLabel("Price:"), gbc); - gbc.gridx = 1; - priceField = new JTextField(15); - inputPanel.add(priceField, gbc); - - gbc.gridx = 0; - gbc.gridy++; - inputPanel.add(new JLabel("Total:"), gbc); - gbc.gridx = 1; - totalField = new JTextField(15); - inputPanel.add(totalField, gbc); - - gbc.anchor = GridBagConstraints.NORTHWEST; - gbc.gridx = 0; - gbc.gridy++; - inputPanel.add(new JLabel("Note:"), gbc); - gbc.gridx = 1; - noteField = new JTextArea(2, 15); - inputPanel.add(noteField, gbc); - gbc.anchor = GridBagConstraints.WEST; - - quantitySpinner.addChangeListener(e -> { - int quantity = (int) quantitySpinner.getValue(); - if (quantity < 0) { - quantity = 0; - quantitySpinner.setValue(quantity); - } - - double price = Double.parseDouble(priceField.getText()); - totalField.setText(String.valueOf(quantity * price)); - }); - - - - // Set the split pane - JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, inputPanel, scrollPane); - splitPane.setResizeWeight(0.5); // Allocate more space to the inputPanel - // Wrap the splitPane in a JPanel to control height - JPanel topPanel = new JPanel(new BorderLayout()); - topPanel.add(splitPane, BorderLayout.CENTER); - - - // Create a table to display selected services - String[] columnNames = {"ID", "Service Name", "Quantity", "Single Price", "Total Price", "Note"}; - selectedTableModel = new DefaultTableModel(new String[][]{}, columnNames) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - }; - - selectedTable = new JTable(selectedTableModel); - JScrollPane tableScrollPane = new JScrollPane(selectedTable); - - // Create a panel for add and remove buttons - JPanel buttonPanel = new JPanel(); - JButton addButton = new JButton("Add"); - JButton removeButton = new JButton("Remove"); - JButton submitButton = new JButton("Submit"); - buttonPanel.add(addButton); - buttonPanel.add(removeButton); - buttonPanel.add(submitButton); - - addButton.addActionListener(e -> { - int selectedRow = serviceTable.getSelectedRow(); - if (selectedRow != -1) { - int modelRow = serviceTable.convertRowIndexToModel(selectedRow); - if(selectedService.containsKey(tableModel.getValueAt(modelRow, 0).toString())) { - JOptionPane.showMessageDialog(this, "Service already added", "Error", JOptionPane.ERROR_MESSAGE); - return; - } - selectedService.put((String) tableModel.getValueAt(modelRow, 0), true); - Object[] rowData = new Object[6]; - - rowData[0] = tableModel.getValueAt(modelRow, 0); - rowData[1] = tableModel.getValueAt(modelRow, 1); - rowData[2] = quantitySpinner.getValue(); - rowData[3] = priceField.getText(); - rowData[4] = totalField.getText(); - rowData[5] = noteField.getText(); - selectedTableModel.addRow(rowData); - log.info("Added service with id: {}", tableModel.getValueAt(modelRow, 0)); - } - }); - - removeButton.addActionListener(e -> { - int selectedRow = selectedTable.getSelectedRow(); - if (selectedRow != -1) { - String id = selectedTableModel.getValueAt(selectedRow, 0).toString(); - selectedService.remove(id); - log.info("Removed service with id: {}", id); - selectedTableModel.removeRow(selectedRow); - } - }); - - submitButton.addActionListener(e -> { - int rows = selectedTableModel.getRowCount(); - servicePrescription = new String[rows][6]; - for (int i = 0; i < rows; i++) { - for (int j = 0; j < 6; j++) { - servicePrescription[i][j] = selectedTableModel.getValueAt(i, j).toString(); - } + okButton.addActionListener(e -> { + collectPrescriptionData(); + if (servicePrescription == null || servicePrescription.length == 0) { + JOptionPane.showMessageDialog(this, "Chưa có dịch vụ nào được chọn.", "Thông báo", JOptionPane.INFORMATION_MESSAGE); + return; } + logger.info("Service prescription saved."); dispose(); }); - // Add components to the dialog - add(topPanel, BorderLayout.NORTH); - add(tableScrollPane, BorderLayout.CENTER); - add(buttonPanel, BorderLayout.SOUTH); - - setLocationRelativeTo(parent); + cancelButton.addActionListener(e -> { + servicePrescription = null; + dispose(); + }); } - private void handleRowSelection(int selectedRow) { - String id = (String) tableModel.getValueAt(selectedRow, 0); - String name = (String) tableModel.getValueAt(selectedRow, 1); - String price = (String) tableModel.getValueAt(selectedRow, 2); + private void filterServiceTable() { + String filterText = serviceNameField.getText().trim(); + if (serviceData == null) return; - serviceNameField.setText(name); - quantitySpinner.setValue(0); - priceField.setText(price); - totalField.setText("0"); + if (filterText.isEmpty()) { + tableModel.setDataVector(serviceData, serviceColumns); + resizeServiceTableColumns(); + serviceTable.clearSelection(); + } else { + List filteredData = new ArrayList<>(); + String lowerCaseFilterText = TextUtils.removeAccents(filterText.toLowerCase()); + for (String[] row : serviceData) { + if (row.length > 1 && row[1] != null) { + String cellValue = TextUtils.removeAccents(row[1].toLowerCase()); + if (cellValue.contains(lowerCaseFilterText)) { + filteredData.add(row); + } + } + } + tableModel.setDataVector(filteredData.toArray(new String[0][0]), serviceColumns); + resizeServiceTableColumns(); + if (!filteredData.isEmpty()) { + serviceTable.setRowSelectionInterval(0,0); + handleServiceTableRowSelection(0); + } else { + clearInputFields(); + } + } + } + + private void resizeServiceTableColumns() { + columnModel = serviceTable.getColumnModel(); + columnModel.getColumn(0).setPreferredWidth(30); + columnModel.getColumn(1).setPreferredWidth(300); + columnModel.getColumn(2).setPreferredWidth(100); + } + + private void handleServiceTableRowSelection(int viewRow) { + if (viewRow < 0 || viewRow >= serviceTable.getRowCount()) { + clearInputFields(); + return; + } + int modelRow = serviceTable.convertRowIndexToModel(viewRow); + serviceNameField.setText(tableModel.getValueAt(modelRow, 1).toString()); + priceField.setText(tableModel.getValueAt(modelRow, 2).toString()); + quantitySpinner.setValue(1); + noteField.setText(""); + updateTotalField(); + } + + private void clearInputFields(){ + priceField.setText(""); + totalField.setText(""); + quantitySpinner.setValue(1); noteField.setText(""); } + private void updateTotalField() { + try { + int quantity = (Integer) quantitySpinner.getValue(); + if (quantity < 0) { + quantitySpinner.setValue(0); + quantity = 0; + } + double price = 0; + if (!priceField.getText().isEmpty()) { + price = Double.parseDouble(priceField.getText()); + } + totalField.setText(String.format("%.0f", quantity * price)); + } catch (NumberFormatException e) { + totalField.setText("Giá không hợp lệ"); + logger.error("Error parsing price for total: " + priceField.getText(), e); + } + } + + private void addSelectedService() { + int selectedRowInServiceTable = serviceTable.getSelectedRow(); + if (selectedRowInServiceTable == -1 && serviceNameField.getText().isEmpty()) { + JOptionPane.showMessageDialog(this, "Vui lòng chọn một dịch vụ từ danh sách hoặc tìm kiếm.", "Chưa chọn dịch vụ", JOptionPane.WARNING_MESSAGE); + return; + } + + String serviceId; + String name; + String currentPrice; + + if (selectedRowInServiceTable != -1) { + int modelRow = serviceTable.convertRowIndexToModel(selectedRowInServiceTable); + serviceId = tableModel.getValueAt(modelRow, 0).toString(); + name = tableModel.getValueAt(modelRow, 1).toString(); + currentPrice = tableModel.getValueAt(modelRow, 2).toString(); + } else { + String searchName = serviceNameField.getText().trim(); + String foundId = null; + String foundPrice = null; + String actualName = null; + + for (String[] serv : serviceData) { + if (TextUtils.removeAccents(serv[1].toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) { + foundId = serv[0]; + actualName = serv[1]; + foundPrice = serv[2]; + break; + } + } + + if (foundId == null) { + JOptionPane.showMessageDialog(this, "Không tìm thấy ID cho dịch vụ: " + searchName + ". Vui lòng chọn từ bảng.", "Lỗi", JOptionPane.ERROR_MESSAGE); + return; + } + serviceId = foundId; + name = actualName; + currentPrice = foundPrice; + } + + int quantity = (Integer) quantitySpinner.getValue(); + String note = noteField.getText(); + String totalAmount = totalField.getText(); + + if (quantity <= 0) { + JOptionPane.showMessageDialog(this, "Số lượng phải lớn hơn 0.", "Số lượng không hợp lệ", JOptionPane.WARNING_MESSAGE); + return; + } + + for (int i = 0; i < selectedTableModel.getRowCount(); i++) { + if (selectedTableModel.getValueAt(i, 0).equals(serviceId)) { + int existingQuantity = Integer.parseInt(selectedTableModel.getValueAt(i, 2).toString()); + int newQuantity = existingQuantity + quantity; + double singlePrice = Double.parseDouble(selectedTableModel.getValueAt(i, 3).toString()); + selectedTableModel.setValueAt(newQuantity, i, 2); + selectedTableModel.setValueAt(String.format("%.0f", newQuantity * singlePrice), i, 4); + return; + } + } + selectedTableModel.addRow(new Object[]{serviceId, name, quantity, currentPrice, totalAmount, note}); + } + + private void removeSelectedService() { + int selectedRow = selectedTable.getSelectedRow(); + if (selectedRow != -1) { + selectedTableModel.removeRow(selectedRow); + } else { + JOptionPane.showMessageDialog(this, "Vui lòng chọn một dịch vụ từ bảng dưới để xóa.", "Chưa chọn dịch vụ", JOptionPane.WARNING_MESSAGE); + } + } + + private void collectPrescriptionData() { + int rowCount = selectedTableModel.getRowCount(); + if (rowCount == 0) { + servicePrescription = new String[0][0]; + return; + } + servicePrescription = new String[rowCount][6]; + for (int i = 0; i < rowCount; i++) { + servicePrescription[i][0] = selectedTableModel.getValueAt(i, 0).toString(); + servicePrescription[i][1] = selectedTableModel.getValueAt(i, 1).toString(); + servicePrescription[i][2] = selectedTableModel.getValueAt(i, 2).toString(); + servicePrescription[i][3] = selectedTableModel.getValueAt(i, 3).toString(); + servicePrescription[i][4] = selectedTableModel.getValueAt(i, 4).toString(); + servicePrescription[i][5] = selectedTableModel.getValueAt(i, 5).toString(); + } + } + + @Override + public void dispose() { + logger.info("Cleaning up ServiceDialog listeners"); + ClientHandler.deleteListener(GetSerInfoResponse.class); + super.dispose(); + } + public static void main(String[] args) { - JFrame frame = new JFrame(); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setSize(400, 300); - frame.setVisible(true); + SwingUtilities.invokeLater(() -> { + JFrame frame = new JFrame("Test Service Dialog"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(400, 300); + frame.setLocationRelativeTo(null); - JButton openDialogButton = new JButton("Open Service Dialog"); - openDialogButton.addActionListener(e -> { - ServiceDialog dialog = new ServiceDialog(frame); - dialog.setVisible(true); + JButton openDialogButton = new JButton("Open Service Dialog"); + openDialogButton.addActionListener(e -> { + ServiceDialog dialog = new ServiceDialog(frame); + dialog.setVisible(true); + String[][] prescription = dialog.getServicePrescription(); + if (prescription != null && prescription.length > 0) { + System.out.println("Service Prescription obtained:"); + for (String[] item : prescription) { + System.out.println(String.join(", ", item)); + } + } else { + System.out.println("No service prescription or dialog was cancelled."); + } + }); + + frame.setLayout(new FlowLayout()); + frame.add(openDialogButton); + frame.setVisible(true); }); - - frame.add(openDialogButton, BorderLayout.CENTER); } } diff --git a/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java b/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java index 1388ffb..ae6ffd7 100644 --- a/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java +++ b/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java @@ -37,7 +37,7 @@ public class QueueViewPage extends JFrame { private String room2PatientInfo = null; // Stores "Name (Year)" public QueueViewPage() { - setTitle("TV Queue Display"); + setTitle("Màn hình chờ TV"); setSize(1280, 720); // Common 720p resolution for TVs setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setLocationRelativeTo(null); @@ -54,11 +54,11 @@ public class QueueViewPage extends JFrame { clinicInfoPanel.setLayout(new BoxLayout(clinicInfoPanel, BoxLayout.Y_AXIS)); clinicInfoPanel.setOpaque(false); - clinicNameLabel = new JLabel("Clinic: Loading..."); + clinicNameLabel = new JLabel("Phòng khám: Đang tải..."); styleLabelForTv(clinicNameLabel, 28, Font.BOLD, Color.WHITE); - clinicAddressLabel = new JLabel("Address: Loading..."); + clinicAddressLabel = new JLabel("Địa chỉ: Đang tải..."); styleLabelForTv(clinicAddressLabel, 20, Font.PLAIN, Color.LIGHT_GRAY); - clinicPhoneLabel = new JLabel("Phone: Loading..."); + clinicPhoneLabel = new JLabel("Điện thoại: Đang tải..."); styleLabelForTv(clinicPhoneLabel, 20, Font.PLAIN, Color.LIGHT_GRAY); clinicInfoPanel.add(clinicNameLabel); @@ -77,14 +77,14 @@ public class QueueViewPage extends JFrame { roomsPanel.setOpaque(false); room1Panel = new JPanel(new BorderLayout()); - room1StatusLabel = new JLabel("ROOM 1", SwingConstants.CENTER); - styleRoomLabel(room1StatusLabel, room1Panel, Color.GREEN.darker(), "EMPTY"); + room1StatusLabel = new JLabel("PHÒNG 1", SwingConstants.CENTER); + styleRoomLabel(room1StatusLabel, room1Panel, Color.GREEN.darker(), "TRỐNG"); // Initial state: Room 1 Empty room1Panel.add(room1StatusLabel, BorderLayout.CENTER); room1Panel.setPreferredSize(new Dimension(220, 100)); room2Panel = new JPanel(new BorderLayout()); - room2StatusLabel = new JLabel("ROOM 2", SwingConstants.CENTER); - styleRoomLabel(room2StatusLabel, room2Panel, Color.GREEN.darker(), "EMPTY"); + room2StatusLabel = new JLabel("PHÒNG 2", SwingConstants.CENTER); + styleRoomLabel(room2StatusLabel, room2Panel, Color.GREEN.darker(), "TRỐNG"); // Initial state: Room 2 Empty room2Panel.add(room2StatusLabel, BorderLayout.CENTER); room2Panel.setPreferredSize(new Dimension(220, 100)); @@ -101,16 +101,16 @@ public class QueueViewPage extends JFrame { callingPatientDisplayPanel.setOpaque(false); // Transparent background callingPatientDisplayPanel.setBorder(new EmptyBorder(15, 20, 15, 20)); // Padding - nowCallingTextLabel = new JLabel("NOW CALLING:"); + nowCallingTextLabel = new JLabel("ĐANG GỌI:"); styleLabelForTv(nowCallingTextLabel, 38, Font.BOLD, new Color(255, 215, 0)); // Gold color nowCallingTextLabel.setAlignmentX(Component.CENTER_ALIGNMENT); nowCallingTextLabel.setVisible(false); // Initially hidden - nowCallingInfoLine1Label = new JLabel("Room 1: FREE"); // Default message + nowCallingInfoLine1Label = new JLabel("Phòng 1: TRỐNG"); // Default message styleLabelForTv(nowCallingInfoLine1Label, 32, Font.BOLD, Color.WHITE); nowCallingInfoLine1Label.setAlignmentX(Component.CENTER_ALIGNMENT); - nowCallingInfoLine2Label = new JLabel("Room 2: FREE"); // Default message + nowCallingInfoLine2Label = new JLabel("Phòng 2: TRỐNG"); // Default message styleLabelForTv(nowCallingInfoLine2Label, 32, Font.BOLD, Color.WHITE); nowCallingInfoLine2Label.setAlignmentX(Component.CENTER_ALIGNMENT); @@ -158,19 +158,28 @@ public class QueueViewPage extends JFrame { label.setForeground(color); } - private void styleRoomLabel(JLabel label, JPanel panel, Color bgColor, String text) { + private void styleRoomLabel(JLabel label, JPanel panel, Color bgColor, String statusText) { + // The label's main text (PHÒNG 1 or PHÒNG 2) is set at initialization and doesn't change. + // This method now only updates the status part of the text and background color. + String baseRoomText = label.getText().split("
")[0]; // Get "PHÒNG X" + if (baseRoomText.contains("html")) baseRoomText = (label == room1StatusLabel ? "PHÒNG 1" : "PHÒNG 2"); // Fallback if parsing fails + label.setFont(new Font("Segoe UI", Font.BOLD, 22)); label.setForeground(Color.WHITE); - label.setText("
" + text.replace(" ", "
") + "
"); + label.setText("
" + baseRoomText + "
" + statusText + "
"); panel.setBackground(bgColor); panel.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1)); } private void loadClinicInfo() { if (LocalStorage.ClinicName != null) { - clinicNameLabel.setText("Clinic: " + LocalStorage.ClinicName); - clinicAddressLabel.setText("Address: " + LocalStorage.ClinicAddress); - clinicPhoneLabel.setText("Phone: " + LocalStorage.ClinicPhone); + clinicNameLabel.setText("Phòng khám: " + LocalStorage.ClinicName); + clinicAddressLabel.setText("Địa chỉ: " + LocalStorage.ClinicAddress); + clinicPhoneLabel.setText("Điện thoại: " + LocalStorage.ClinicPhone); + } else { + clinicNameLabel.setText("Phòng khám: Đang tải..."); + clinicAddressLabel.setText("Địa chỉ: Đang tải..."); + clinicPhoneLabel.setText("Điện thoại: Đang tải..."); } } @@ -179,18 +188,18 @@ public class QueueViewPage extends JFrame { boolean room2Busy = room2PatientInfo != null; if (room1Busy) { - nowCallingInfoLine1Label.setText("Room 1: " + room1PatientInfo); + nowCallingInfoLine1Label.setText("Phòng 1: " + room1PatientInfo); styleLabelForTv(nowCallingInfoLine1Label, 32, Font.BOLD, Color.WHITE); } else { - nowCallingInfoLine1Label.setText("Room 1: FREE"); + nowCallingInfoLine1Label.setText("Phòng 1: TRỐNG"); styleLabelForTv(nowCallingInfoLine1Label, 32, Font.ITALIC, Color.LIGHT_GRAY); } if (room2Busy) { - nowCallingInfoLine2Label.setText("Room 2: " + room2PatientInfo); + nowCallingInfoLine2Label.setText("Phòng 2: " + room2PatientInfo); styleLabelForTv(nowCallingInfoLine2Label, 32, Font.BOLD, Color.WHITE); } else { - nowCallingInfoLine2Label.setText("Room 2: FREE"); + nowCallingInfoLine2Label.setText("Phòng 2: TRỐNG"); styleLabelForTv(nowCallingInfoLine2Label, 32, Font.ITALIC, Color.LIGHT_GRAY); } @@ -198,38 +207,36 @@ public class QueueViewPage extends JFrame { nowCallingTextLabel.setVisible(true); } else { nowCallingTextLabel.setVisible(false); - // Optional: could make one line say "All rooms free" and hide the other - // For now, both lines will show "Room X: FREE" and title is hidden. } } public void updateSpecificRoomStatus(int roomId, String patientIdForRoomBox, String fullPatientInfoForCentralDisplay, BsK.common.entity.Status status) { JLabel targetLabel; JPanel targetPanel; + String roomName; if (roomId == 1) { targetLabel = room1StatusLabel; targetPanel = room1Panel; + roomName = "PHÒNG 1"; } else if (roomId == 2) { targetLabel = room2StatusLabel; targetPanel = room2Panel; + roomName = "PHÒNG 2"; } else { System.err.println("QueueViewPage: Invalid roomId for status update: " + roomId); return; } if (status == BsK.common.entity.Status.PROCESSING) { - // Use patientIdForRoomBox for the small room panel - styleRoomLabel(targetLabel, targetPanel, Color.RED.darker(), "P" + roomId + "\n" + patientIdForRoomBox); - // Store fullPatientInfoForCentralDisplay for the main "Now Calling" display + styleRoomLabel(targetLabel, targetPanel, Color.RED.darker(), "Mã BN: " + patientIdForRoomBox); if (roomId == 1) { room1PatientInfo = fullPatientInfoForCentralDisplay; } else { room2PatientInfo = fullPatientInfoForCentralDisplay; } - } else { - // Room is not PROCESSING (e.g., EMPTY, DONE) - styleRoomLabel(targetLabel, targetPanel, Color.GREEN.darker(), "ROOM " + roomId + "\nEMPTY"); + } else { // EMPTY, DONE, etc. + styleRoomLabel(targetLabel, targetPanel, Color.GREEN.darker(), "TRỐNG"); if (roomId == 1) { room1PatientInfo = null; } else { @@ -242,18 +249,21 @@ public class QueueViewPage extends JFrame { public void markRoomAsFree(int roomId) { JLabel targetLabel; JPanel targetPanel; + String roomName; // Not strictly needed here if styleRoomLabel handles base text if (roomId == 1) { targetLabel = room1StatusLabel; targetPanel = room1Panel; + // roomName = "PHÒNG 1"; // Base text is already PHÒNG 1 } else if (roomId == 2) { targetLabel = room2StatusLabel; targetPanel = room2Panel; + // roomName = "PHÒNG 2"; // Base text is already PHÒNG 2 } else { System.err.println("QueueViewPage: Invalid roomId for marking as free: " + roomId); return; } - styleRoomLabel(targetLabel, targetPanel, Color.GREEN.darker(), "ROOM " + roomId + "\nEMPTY"); + styleRoomLabel(targetLabel, targetPanel, Color.GREEN.darker(), "TRỐNG"); if (roomId == 1) { room1PatientInfo = null; } else { @@ -265,8 +275,12 @@ public class QueueViewPage extends JFrame { public void updateQueueData(String[][] fullQueueData) { if (fullQueueData == null) { tvQueueTableModel.setRowCount(0); - styleRoomLabel(room1StatusLabel, room1Panel, Color.GREEN.darker(), "ROOM 1 EMPTY"); - styleRoomLabel(room2StatusLabel, room2Panel, Color.GREEN.darker(), "ROOM 2 EMPTY"); + // Reset room labels to their base text + TRỐNG + styleRoomLabel(room1StatusLabel, room1Panel, Color.GREEN.darker(), "TRỐNG"); + styleRoomLabel(room2StatusLabel, room2Panel, Color.GREEN.darker(), "TRỐNG"); + room1PatientInfo = null; + room2PatientInfo = null; + updateNowCallingDisplay(); return; } @@ -274,58 +288,45 @@ public class QueueViewPage extends JFrame { SimpleDateFormat inputDateFormat = new SimpleDateFormat("dd/MM/yyyy"); for (String[] row : fullQueueData) { - // Assuming row[0] is MaKhamBenh, row[1] is Date for year, row[2] is Ho, row[3] is Ten - // As per user: "Mã khám bệnh (the first collum and last and first nam (concate index 2 and 3 (3rd and 4th collumn)) and take the year out from the second column))" if (row.length > 3) { String maKhamBenh = row[0]; String ho = row[2]; String ten = row[3]; String hoVaTen = ho + " " + ten; - String dateStr = row[1]; // Second column for date for year extraction - String namSinh = "N/A"; // Or Nam Kham as discussed + String dateStr = row[1]; + String namSinh = "N/A"; try { Date parsedDate; - if (dateStr.matches("\\d+")) { // Unix timestamp + if (dateStr.matches("\\d+")) { parsedDate = new Date(Long.parseLong(dateStr)); - } else { // dd/MM/yyyy format + } else { parsedDate = inputDateFormat.parse(dateStr); } Calendar calendar = Calendar.getInstance(); calendar.setTime(parsedDate); namSinh = String.valueOf(calendar.get(Calendar.YEAR)); } catch (ParseException | NumberFormatException e) { - System.err.println("Error parsing date for TV Queue year: " + dateStr + " - " + e.getMessage()); + System.err.println("Lỗi phân tích ngày cho năm trên màn hình chờ TV: " + dateStr + " - " + e.getMessage()); } tvDataList.add(new Object[]{maKhamBenh, hoVaTen, namSinh}); } } tvQueueTableModel.setDataVector(tvDataList.toArray(new Object[0][0]), tvQueueColumns); - // Update Room 1 Status - This part will be superseded by updateSpecificRoomStatus or needs to be re-evaluated - // if (fullQueueData.length > 0 && fullQueueData[0] != null && fullQueueData[0].length > 0) { - // styleRoomLabel(room1StatusLabel, room1Panel, Color.RED.darker(), "ROOM 1 " + fullQueueData[0][0]); - // } else { - // styleRoomLabel(room1StatusLabel, room1Panel, Color.GREEN.darker(), "ROOM 1 EMPTY"); - // } - - // Update Room 2 Status - This part will be superseded by updateSpecificRoomStatus or needs to be re-evaluated - // if (fullQueueData.length > 1 && fullQueueData[1] != null && fullQueueData[1].length > 0) { - // styleRoomLabel(room2StatusLabel, room2Panel, Color.RED.darker(), "ROOM 2 " + fullQueueData[1][0]); - // } else { - // styleRoomLabel(room2SṭatusLabel, room2Panel, Color.GREEN.darker(), "ROOM 2 EMPTY"); - // } + // The specific room status (patient ID in box) is now handled by updateSpecificRoomStatus + // and markRoomAsFree. The general updateQueueData should not override this detailed status. + // If no patients are called, updateNowCallingDisplay will reflect that rooms are free if their info is null. } @Override public void setVisible(boolean b) { - if (b) { // When making visible, reload clinic info in case it changed or was not available at init + if (b) { loadClinicInfo(); } super.setVisible(b); } - // Optional: A method to make the frame more TV-friendly (e.g., full screen) public void optimizeForTv() { setExtendedState(JFrame.MAXIMIZED_BOTH); // setUndecorated(true); // Optional: removes window borders, use with caution