From 45a0a2d7922cff783591f4ccf7eae3e26de1c313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20Th=C3=A0nh=20=C4=90=E1=BA=A1t?= <91755733+lds217@users.noreply.github.com> Date: Sun, 27 Jul 2025 01:07:43 +0700 Subject: [PATCH] [update] Introduce new medical unit conversion mapping in LocalStorage. Enhance AddDialog with improved UI for province and ward selection, including additional labels for better user guidance. Remove CheckoutPage and InventoryPage components to streamline the application structure. --- src/main/java/BsK/client/LocalStorage.java | 1 + .../CheckUpPage/AddDialog/AddDialog.java | 18 +- .../component/CheckoutPage/CheckoutPage.java | 1085 ----------------- .../DashboardPage/DashboardPage.java | 490 ++++++-- .../ui/component/InfoPage/InfoDialog.java | 158 +++ .../InventoryPage/GoodsReceiptPanel.java | 570 --------- .../InventoryPage/InventoryDashboard.java | 228 ---- .../InventoryPage/InventoryPage.java | 281 ----- .../InventoryPage/InventoryViewPanel.java | 437 ------- .../InventoryPage/ProductMasterPanel.java | 526 -------- .../component/InventoryPage/ReportsPanel.java | 473 ------- .../InventoryPage/StockAdjustmentPanel.java | 498 -------- .../InventoryPage/SupplierMasterPanel.java | 537 -------- .../common/InventoryButtonFactory.java | 296 ----- .../common/InventoryColorScheme.java | 187 --- .../client/ui/component/common/NavBar.java | 15 +- 16 files changed, 599 insertions(+), 5201 deletions(-) delete mode 100644 src/main/java/BsK/client/ui/component/CheckoutPage/CheckoutPage.java create mode 100644 src/main/java/BsK/client/ui/component/InfoPage/InfoDialog.java delete mode 100644 src/main/java/BsK/client/ui/component/InventoryPage/GoodsReceiptPanel.java delete mode 100644 src/main/java/BsK/client/ui/component/InventoryPage/InventoryDashboard.java delete mode 100644 src/main/java/BsK/client/ui/component/InventoryPage/InventoryPage.java delete mode 100644 src/main/java/BsK/client/ui/component/InventoryPage/InventoryViewPanel.java delete mode 100644 src/main/java/BsK/client/ui/component/InventoryPage/ProductMasterPanel.java delete mode 100644 src/main/java/BsK/client/ui/component/InventoryPage/ReportsPanel.java delete mode 100644 src/main/java/BsK/client/ui/component/InventoryPage/StockAdjustmentPanel.java delete mode 100644 src/main/java/BsK/client/ui/component/InventoryPage/SupplierMasterPanel.java delete mode 100644 src/main/java/BsK/client/ui/component/InventoryPage/common/InventoryButtonFactory.java delete mode 100644 src/main/java/BsK/client/ui/component/InventoryPage/common/InventoryColorScheme.java diff --git a/src/main/java/BsK/client/LocalStorage.java b/src/main/java/BsK/client/LocalStorage.java index 83e53e0..515121e 100644 --- a/src/main/java/BsK/client/LocalStorage.java +++ b/src/main/java/BsK/client/LocalStorage.java @@ -20,4 +20,5 @@ public class LocalStorage { public static String[] wards; public static HashMap provinceToId; public static HashMap cachedWards; // cache local areas later will do + public static HashMap medUnitConversion; // hash by med id inside is unit id and unit name and conversion rate and unit price } \ No newline at end of file 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 fc93fa2..700482d 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 @@ -484,12 +484,28 @@ public class AddDialog extends JDialog { gbc.gridx = 0; // Start from the first column for the combo boxes gbc.gridwidth = 1; // Each combo box takes 1 logical column in this setup gbc.weightx = 0.33; // Distribute space among the three combo boxes + // Add Tỉnh/Thành phố label before provinceComboBox + gbc.gridx = 0; + JLabel provinceLabel = new JLabel("Tỉnh/Thành phố", SwingConstants.RIGHT); + provinceLabel.setFont(labelFont); + addressInfoPanel.add(provinceLabel, gbc); + + gbc.gridx = 1; provinceComboBox = new JComboBox<>(LocalStorage.provinces != null ? LocalStorage.provinces : new String[]{"Tỉnh/TP"}); provinceComboBox.setFont(textFont); provinceComboBox.setPreferredSize(comboBoxSize); addressInfoPanel.add(provinceComboBox, gbc); - gbc.gridx = 1; + gbc.gridx = 2; + gbc.gridwidth = 1; + gbc.weightx = 0.33; + JLabel wardLabel = new JLabel("Xã/Phường", SwingConstants.RIGHT); + wardLabel.setFont(labelFont); + addressInfoPanel.add(wardLabel, gbc); + + gbc.gridx = 3; + gbc.gridwidth = 1; + gbc.weightx = 0.33; wardModel = new DefaultComboBoxModel<>(new String[]{"Xã/Phường"}); wardComboBox = new JComboBox<>(wardModel); wardComboBox.setFont(textFont); diff --git a/src/main/java/BsK/client/ui/component/CheckoutPage/CheckoutPage.java b/src/main/java/BsK/client/ui/component/CheckoutPage/CheckoutPage.java deleted file mode 100644 index 0b56e1c..0000000 --- a/src/main/java/BsK/client/ui/component/CheckoutPage/CheckoutPage.java +++ /dev/null @@ -1,1085 +0,0 @@ -package BsK.client.ui.component.CheckoutPage; - -import BsK.client.LocalStorage; -import BsK.client.ui.component.MainFrame; -import BsK.client.ui.component.common.NavBar; -import BsK.client.ui.component.common.RoundedPanel; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.DefaultTableCellRenderer; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.text.NumberFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -@Slf4j -public class CheckoutPage extends JPanel { - private MainFrame mainFrame; - - // Top Panel Components - private JTextField searchPatientField; - private JLabel patientInfoLabel; - - // Left Panel - Prescription Checklist (TO-DO) - private JTable prescriptionChecklistTable; - private DefaultTableModel prescriptionChecklistModel; - private JTextArea doctorNotesArea; - - // Middle Panel - Dispensed Items (DONE) - private JTable dispensedItemsTable; - private DefaultTableModel dispensedItemsModel; - - // Right Panel - Payment Summary - private JLabel medicineSubtotalLabel; - private JLabel serviceSubtotalLabel; - private JLabel grandTotalLabel; - private JTextField cashReceivedField; - private JLabel changeLabel; - private JButton cancelButton; - private JButton payAndPrintButton; - - // Dispensing workflow components - private JDialog configureItemDialog; - private JComboBox unitComboBox; - private JLabel unitPriceLabel; - private JSpinner quantitySpinner; - private JLabel lineTotalLabel; - private JTable batchTable; - private DefaultTableModel batchTableModel; - private JButton confirmAddButton; - private JButton cancelDialogButton; - - // Current dispensing data - private String currentDispensingDrug = ""; - private double currentUnitPrice = 0.0; - - // Current patient data - private String currentPatientId = ""; - private String currentPatientName = ""; - private String currentDoctorName = ""; - private double medicineTotal = 0.0; - private double serviceTotal = 0.0; - - // Number formatter for currency - private NumberFormat currencyFormatter; - - public CheckoutPage(MainFrame mainFrame) { - this.mainFrame = mainFrame; - this.currencyFormatter = NumberFormat.getNumberInstance(new Locale("vi", "VN")); - - setLayout(new BorderLayout()); - setBackground(new Color(245, 245, 245)); - - // --- Navigation Bar --- - NavBar navBar = new NavBar(mainFrame, "Thanh Toán & Cấp Phát Thuốc"); - add(navBar, BorderLayout.NORTH); - - initializeComponents(); - layoutComponents(); - setupEventListeners(); - setupKeyboardShortcuts(); - - // Focus on search field by default - SwingUtilities.invokeLater(() -> searchPatientField.requestFocusInWindow()); - } - - private void initializeComponents() { - // Top Panel Components - searchPatientField = new JTextField(35); - searchPatientField.setFont(new Font("Arial", Font.PLAIN, 18)); // Increased font size - searchPatientField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(63, 81, 181), 2), - BorderFactory.createEmptyBorder(10, 15, 10, 15) - )); - - patientInfoLabel = new JLabel("Chưa chọn bệnh nhân"); - patientInfoLabel.setFont(new Font("Arial", Font.BOLD, 16)); // Increased font size - patientInfoLabel.setForeground(new Color(63, 81, 181)); - - // Left Panel - Prescription Checklist (NO SELECT ALL CHECKBOX) - String[] prescriptionColumns = {"✓", "Tên thuốc/Dịch vụ", "SL Cần", "Thao tác"}; - prescriptionChecklistModel = new DefaultTableModel(prescriptionColumns, 0) { - @Override - public Class getColumnClass(int column) { - if (column == 0) return Boolean.class; - if (column == 3) return JButton.class; // Button column - return String.class; - } - @Override - public boolean isCellEditable(int row, int column) { - return column == 0 || column == 3; // Checkbox and button are editable - } - }; - prescriptionChecklistTable = new JTable(prescriptionChecklistModel); - setupPrescriptionTable(); - - doctorNotesArea = new JTextArea(3, 20); - doctorNotesArea.setEditable(false); - doctorNotesArea.setBackground(new Color(250, 250, 250)); - doctorNotesArea.setFont(new Font("Arial", Font.ITALIC, 14)); // Increased font size - doctorNotesArea.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createTitledBorder("Ghi chú của Bác sĩ"), - BorderFactory.createEmptyBorder(8, 8, 8, 8) - )); - doctorNotesArea.setLineWrap(true); - doctorNotesArea.setWrapStyleWord(true); - - // Middle Panel - Dispensed Items - String[] dispensedColumns = {"Tên thuốc/Dịch vụ", "Lô", "HSD", "SL Đã lấy", "Đơn giá", "Thành tiền"}; - dispensedItemsModel = new DefaultTableModel(dispensedColumns, 0) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - }; - dispensedItemsTable = new JTable(dispensedItemsModel); - setupDispensedTable(); - - // Right Panel - Payment Summary (RIGHT-ALIGNED VALUES) - medicineSubtotalLabel = new JLabel("0 VNĐ", SwingConstants.RIGHT); - medicineSubtotalLabel.setFont(new Font("Arial", Font.PLAIN, 16)); - - serviceSubtotalLabel = new JLabel("0 VNĐ", SwingConstants.RIGHT); - serviceSubtotalLabel.setFont(new Font("Arial", Font.PLAIN, 16)); - - grandTotalLabel = new JLabel("0 VNĐ", SwingConstants.RIGHT); - grandTotalLabel.setFont(new Font("Arial", Font.BOLD, 22)); // Increased font size - grandTotalLabel.setForeground(new Color(244, 67, 54)); - - cashReceivedField = new JTextField(15); - cashReceivedField.setFont(new Font("Arial", Font.PLAIN, 18)); // Increased font size - cashReceivedField.setHorizontalAlignment(JTextField.RIGHT); - - changeLabel = new JLabel("0 VNĐ", SwingConstants.RIGHT); - changeLabel.setFont(new Font("Arial", Font.BOLD, 18)); // Increased font size - changeLabel.setForeground(new Color(76, 175, 80)); - - // DE-EMPHASIZED CANCEL BUTTON (Grey outline style) - cancelButton = new JButton("HỦY BỎ"); - cancelButton.setFont(new Font("Arial", Font.PLAIN, 14)); // Not bold - cancelButton.setBackground(Color.WHITE); - cancelButton.setForeground(new Color(120, 120, 120)); - cancelButton.setBorder(BorderFactory.createLineBorder(new Color(180, 180, 180), 1)); - cancelButton.setFocusPainted(false); - cancelButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - - payAndPrintButton = new JButton("THANH TOÁN & IN HÓA ĐƠN (F9)"); - payAndPrintButton.setFont(new Font("Arial", Font.BOLD, 16)); - payAndPrintButton.setBackground(new Color(76, 175, 80)); - payAndPrintButton.setForeground(Color.WHITE); - payAndPrintButton.setFocusPainted(false); - payAndPrintButton.setPreferredSize(new Dimension(300, 55)); - payAndPrintButton.setEnabled(false); // Initially disabled - payAndPrintButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - } - - private void setupPrescriptionTable() { - prescriptionChecklistTable.setRowHeight(35); // Increased row height - prescriptionChecklistTable.setFont(new Font("Arial", Font.PLAIN, 15)); // Increased font size - prescriptionChecklistTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 15)); - prescriptionChecklistTable.getTableHeader().setBackground(new Color(63, 81, 181)); - prescriptionChecklistTable.getTableHeader().setForeground(Color.WHITE); - - // Set column widths - adjusted for Dispense button column - prescriptionChecklistTable.getColumnModel().getColumn(0).setPreferredWidth(40); - prescriptionChecklistTable.getColumnModel().getColumn(1).setPreferredWidth(200); - prescriptionChecklistTable.getColumnModel().getColumn(2).setPreferredWidth(70); - prescriptionChecklistTable.getColumnModel().getColumn(3).setPreferredWidth(80); // Thao tác column - - // Button renderer and editor for Dispense column (column 3) - prescriptionChecklistTable.getColumnModel().getColumn(3).setCellRenderer(new javax.swing.table.DefaultTableCellRenderer() { - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { - JButton button = new JButton("Cấp thuốc"); - button.setFont(new Font("Arial", Font.PLAIN, 11)); - button.setBackground(new Color(33, 150, 243)); - button.setForeground(Color.WHITE); - button.setFocusPainted(false); - - // Disable button if item is already checked - Boolean checked = (Boolean) table.getValueAt(row, 0); - if (checked != null && checked) { - button.setText("Đã cấp"); - button.setBackground(new Color(158, 158, 158)); - button.setEnabled(false); - } - - return button; - } - }); - - prescriptionChecklistTable.getColumnModel().getColumn(3).setCellEditor(new javax.swing.DefaultCellEditor(new JCheckBox()) { - @Override - public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { - JButton button = new JButton("Cấp thuốc"); - button.addActionListener(e -> { - String drugName = table.getValueAt(row, 1).toString(); - CheckoutPage.this.openConfigureItemDialog(drugName, row); - fireEditingStopped(); - }); - return button; - } - }); - - // COLOR-CODED STATUS RENDERER (highlight entire row) - prescriptionChecklistTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() { - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, - boolean hasFocus, int row, int column) { - - // Skip button column - if (column == 3) { - return prescriptionChecklistTable.getColumnModel().getColumn(3).getCellRenderer() - .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - } - - Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - // Get the status from the hidden status field (we'll store it in the model) - String itemName = table.getValueAt(row, 1).toString(); - String status = getItemStatus(itemName); // Helper method to get status - - if (status.contains("Hết hàng") || status.contains("Out of Stock")) { - c.setBackground(new Color(255, 230, 230)); // Light red - if (column == 1) { // Add warning icon to item name - setText("⚠️ " + value.toString()); - } - } else if (status.contains("Sắp hết") || status.contains("Low Stock")) { - c.setBackground(new Color(255, 250, 220)); // Light yellow - if (column == 1) { // Add warning icon to item name - setText("⚠️ " + value.toString()); - } - } else { - if (isSelected) { - c.setBackground(table.getSelectionBackground()); - } else { - c.setBackground(Color.WHITE); - } - if (column == 1 && !value.toString().startsWith("⚠️")) { - setText(value.toString()); - } - } - - c.setForeground(isSelected ? table.getSelectionForeground() : Color.BLACK); - return c; - } - }); - } - - private void setupDispensedTable() { - dispensedItemsTable.setRowHeight(30); // Increased row height - dispensedItemsTable.setFont(new Font("Arial", Font.PLAIN, 14)); // Increased font size - dispensedItemsTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); - dispensedItemsTable.getTableHeader().setBackground(new Color(76, 175, 80)); - dispensedItemsTable.getTableHeader().setForeground(Color.WHITE); - - // Set column widths - dispensedItemsTable.getColumnModel().getColumn(0).setPreferredWidth(200); - dispensedItemsTable.getColumnModel().getColumn(1).setPreferredWidth(70); - dispensedItemsTable.getColumnModel().getColumn(2).setPreferredWidth(90); - dispensedItemsTable.getColumnModel().getColumn(3).setPreferredWidth(70); - dispensedItemsTable.getColumnModel().getColumn(4).setPreferredWidth(90); - dispensedItemsTable.getColumnModel().getColumn(5).setPreferredWidth(110); - - // Right-align numeric columns - DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer(); - rightRenderer.setHorizontalAlignment(SwingConstants.RIGHT); - dispensedItemsTable.getColumnModel().getColumn(3).setCellRenderer(rightRenderer); // SL - dispensedItemsTable.getColumnModel().getColumn(4).setCellRenderer(rightRenderer); // Đơn giá - dispensedItemsTable.getColumnModel().getColumn(5).setCellRenderer(rightRenderer); // Thành tiền - } - - private void layoutComponents() { - JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); - mainPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); - - // Top Panel - Search and Patient Info - JPanel topPanel = createTopPanel(); - mainPanel.add(topPanel, BorderLayout.NORTH); - - // Main Content - Three Panel Layout - JPanel contentPanel = new JPanel(new GridBagLayout()); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(8, 8, 8, 8); - - // Left Panel - Prescription Checklist - gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 0.35; gbc.weighty = 1.0; - contentPanel.add(createPrescriptionPanel(), gbc); - - // Middle Panel - Dispensed Items - gbc.gridx = 1; gbc.weightx = 0.4; - contentPanel.add(createDispensedPanel(), gbc); - - // Right Panel - Payment Summary - gbc.gridx = 2; gbc.weightx = 0.25; - contentPanel.add(createPaymentPanel(), gbc); - - mainPanel.add(contentPanel, BorderLayout.CENTER); - add(mainPanel, BorderLayout.CENTER); - } - - private JPanel createTopPanel() { - JPanel topPanel = new JPanel(new BorderLayout(15, 15)); - topPanel.setBackground(Color.WHITE); - topPanel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(224, 224, 224)), - BorderFactory.createEmptyBorder(20, 20, 20, 20) - )); - - JPanel searchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - searchPanel.setOpaque(false); - - JLabel searchLabel = new JLabel("Tìm bệnh nhân (ID/Tên) hoặc quét mã (F2):"); - searchLabel.setFont(new Font("Arial", Font.BOLD, 16)); // Increased font size - searchPanel.add(searchLabel); - searchPanel.add(Box.createHorizontalStrut(15)); - searchPanel.add(searchPatientField); - - topPanel.add(searchPanel, BorderLayout.WEST); - topPanel.add(patientInfoLabel, BorderLayout.EAST); - - return topPanel; - } - - private JPanel createPrescriptionPanel() { - RoundedPanel panel = new RoundedPanel(12, Color.WHITE, true); - panel.setLayout(new BorderLayout(8, 8)); - panel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(63, 81, 181), 2), - BorderFactory.createEmptyBorder(15, 15, 15, 15) - )); - - JLabel titleLabel = new JLabel("ĐƠN THUỐC CẦN XUẤT", SwingConstants.CENTER); - titleLabel.setFont(new Font("Arial", Font.BOLD, 18)); // Increased font size - titleLabel.setForeground(new Color(63, 81, 181)); - titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0,15, 0)); - - JScrollPane tableScroll = new JScrollPane(prescriptionChecklistTable); - tableScroll.setPreferredSize(new Dimension(420, 320)); - - panel.add(titleLabel, BorderLayout.NORTH); - panel.add(tableScroll, BorderLayout.CENTER); - panel.add(doctorNotesArea, BorderLayout.SOUTH); - - return panel; - } - - private JPanel createDispensedPanel() { - RoundedPanel panel = new RoundedPanel(12, Color.WHITE, true); - panel.setLayout(new BorderLayout(8, 8)); - panel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(76, 175, 80), 2), - BorderFactory.createEmptyBorder(15, 15, 15, 15) - )); - - JLabel titleLabel = new JLabel("THUỐC ĐÃ LẤY", SwingConstants.CENTER); - titleLabel.setFont(new Font("Arial", Font.BOLD, 18)); // Increased font size - titleLabel.setForeground(new Color(76, 175, 80)); - titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0)); - - JScrollPane tableScroll = new JScrollPane(dispensedItemsTable); - tableScroll.setPreferredSize(new Dimension(480, 380)); - - panel.add(titleLabel, BorderLayout.NORTH); - panel.add(tableScroll, BorderLayout.CENTER); - - return panel; - } - - private JPanel createPaymentPanel() { - RoundedPanel panel = new RoundedPanel(12, Color.WHITE, true); - panel.setLayout(new BorderLayout(8, 8)); - panel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(244, 67, 54), 2), - BorderFactory.createEmptyBorder(15, 15, 15, 15) - )); - - JLabel titleLabel = new JLabel("THANH TOÁN", SwingConstants.CENTER); - titleLabel.setFont(new Font("Arial", Font.BOLD, 18)); // Increased font size - titleLabel.setForeground(new Color(244, 67, 54)); - - JPanel summaryPanel = new JPanel(new GridBagLayout()); - summaryPanel.setOpaque(false); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(10, 8, 10, 8); - gbc.fill = GridBagConstraints.HORIZONTAL; - - // Cost breakdown with RIGHT-ALIGNED values - gbc.gridx = 0; gbc.gridy = 0; gbc.anchor = GridBagConstraints.WEST; gbc.weightx = 0.6; - JLabel medicineLabel = new JLabel("Tổng tiền thuốc:"); - medicineLabel.setFont(new Font("Arial", Font.PLAIN, 16)); - summaryPanel.add(medicineLabel, gbc); - gbc.gridx = 1; gbc.anchor = GridBagConstraints.EAST; gbc.weightx = 0.4; - summaryPanel.add(medicineSubtotalLabel, gbc); - - gbc.gridx = 0; gbc.gridy = 1; gbc.anchor = GridBagConstraints.WEST; gbc.weightx = 0.6; - JLabel serviceLabel = new JLabel("Tổng tiền dịch vụ:"); - serviceLabel.setFont(new Font("Arial", Font.PLAIN, 16)); - summaryPanel.add(serviceLabel, gbc); - gbc.gridx = 1; gbc.anchor = GridBagConstraints.EAST; gbc.weightx = 0.4; - summaryPanel.add(serviceSubtotalLabel, gbc); - - // Separator line - gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 2; gbc.insets = new Insets(15, 0, 15, 0); - JSeparator separator = new JSeparator(); - separator.setForeground(Color.GRAY); - summaryPanel.add(separator, gbc); - - // Grand total - gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 1; gbc.insets = new Insets(10, 8, 10, 8); - gbc.anchor = GridBagConstraints.WEST; gbc.weightx = 0.6; - JLabel totalLabel = new JLabel("TỔNG CỘNG:"); - totalLabel.setFont(new Font("Arial", Font.BOLD, 18)); - summaryPanel.add(totalLabel, gbc); - gbc.gridx = 1; gbc.anchor = GridBagConstraints.EAST; gbc.weightx = 0.4; - summaryPanel.add(grandTotalLabel, gbc); - - // Payment input - gbc.gridx = 0; gbc.gridy = 4; gbc.anchor = GridBagConstraints.WEST; gbc.weightx = 0.6; - JLabel cashLabel = new JLabel("Tiền khách đưa:"); - cashLabel.setFont(new Font("Arial", Font.PLAIN, 16)); - summaryPanel.add(cashLabel, gbc); - gbc.gridx = 1; gbc.anchor = GridBagConstraints.EAST; gbc.weightx = 0.4; - summaryPanel.add(cashReceivedField, gbc); - - gbc.gridx = 0; gbc.gridy = 5; gbc.anchor = GridBagConstraints.WEST; gbc.weightx = 0.6; - JLabel changeTextLabel = new JLabel("Tiền thối lại:"); - changeTextLabel.setFont(new Font("Arial", Font.BOLD, 16)); - summaryPanel.add(changeTextLabel, gbc); - gbc.gridx = 1; gbc.anchor = GridBagConstraints.EAST; gbc.weightx = 0.4; - summaryPanel.add(changeLabel, gbc); - - // Action buttons - JPanel buttonPanel = new JPanel(new GridLayout(2, 1, 0, 15)); - buttonPanel.setOpaque(false); - buttonPanel.setBorder(BorderFactory.createEmptyBorder(25, 0, 0, 0)); - buttonPanel.add(payAndPrintButton); - buttonPanel.add(cancelButton); - - panel.add(titleLabel, BorderLayout.NORTH); - panel.add(summaryPanel, BorderLayout.CENTER); - panel.add(buttonPanel, BorderLayout.SOUTH); - - return panel; - } - - private void setupEventListeners() { - // Search field - Enter key or barcode scan - searchPatientField.addKeyListener(new KeyListener() { - @Override - public void keyTyped(KeyEvent e) {} - - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - searchAndLoadPatient(); - } - } - - @Override - public void keyReleased(KeyEvent e) {} - }); - - // Cash received field - calculate change - cashReceivedField.addKeyListener(new KeyListener() { - @Override - public void keyTyped(KeyEvent e) {} - - @Override - public void keyPressed(KeyEvent e) {} - - @Override - public void keyReleased(KeyEvent e) { - calculateChange(); - } - }); - - // Prescription checklist - monitor completion - prescriptionChecklistModel.addTableModelListener(e -> { - updatePayButtonState(); - }); - - // Cancel button - cancelButton.addActionListener(e -> clearCurrentTransaction()); - - // Pay and print button - payAndPrintButton.addActionListener(e -> processPaymentAndPrint()); - } - - // KEYBOARD SHORTCUTS IMPLEMENTATION - private void setupKeyboardShortcuts() { - InputMap inputMap = this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - ActionMap actionMap = this.getActionMap(); - - // F2 or Ctrl+F: Focus search field - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), "focusSearch"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_DOWN_MASK), "focusSearch"); - actionMap.put("focusSearch", new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - searchPatientField.requestFocusInWindow(); - searchPatientField.selectAll(); - } - }); - - // F9 or Ctrl+P: Activate payment button - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0), "processPayment"); - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK), "processPayment"); - actionMap.put("processPayment", new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - if (payAndPrintButton.isEnabled()) { - processPaymentAndPrint(); - } - } - }); - - // ESC: Cancel current transaction - inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancelTransaction"); - actionMap.put("cancelTransaction", new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - clearCurrentTransaction(); - } - }); - } - - private void searchAndLoadPatient() { - String searchTerm = searchPatientField.getText().trim(); - if (searchTerm.isEmpty()) { - playErrorSound(); - JOptionPane.showMessageDialog(this, "Vui lòng nhập ID bệnh nhân hoặc quét mã.", "Lỗi", JOptionPane.WARNING_MESSAGE); - return; - } - - // BARCODE SCAN DETECTION: If term looks like a drug barcode and we have a patient loaded - if (searchTerm.length() > 8 && searchTerm.matches("\\d+") && !currentPatientId.isEmpty()) { - handleBarcodeScanned(searchTerm); - return; - } - - // TODO: Network request to search patient by ID/name/barcode - log.info("Searching for patient: " + searchTerm); - - // Mock data for demonstration - loadMockPatientData(searchTerm); - playSuccessSound(); - } - - private void handleBarcodeScanned(String barcode) { - log.info("Barcode scanned: " + barcode); - - // TODO: Network request to identify drug from barcode - String drugName = identifyDrugFromBarcode(barcode); - - if (drugName == null) { - playErrorSound(); - JOptionPane.showMessageDialog(this, - "Không tìm thấy thuốc với mã vạch: " + barcode, - "Mã vạch không hợp lệ", - JOptionPane.ERROR_MESSAGE); - searchPatientField.setText(""); - return; - } - - // SAFETY CHECK: Verify drug exists in prescription - int prescriptionRow = findDrugInPrescription(drugName); - if (prescriptionRow == -1) { - playErrorSound(); - JOptionPane.showMessageDialog(this, - "CẢNH BÁO: Thuốc '" + drugName + "' KHÔNG có trong đơn thuốc của bệnh nhân!\n\n" + - "Vui lòng kiểm tra lại hoặc liên hệ bác sĩ.", - "Thuốc không có trong đơn", - JOptionPane.ERROR_MESSAGE); - searchPatientField.setText(""); - return; - } - - // Check if already dispensed - Boolean isChecked = (Boolean) prescriptionChecklistModel.getValueAt(prescriptionRow, 0); - if (isChecked != null && isChecked) { - playErrorSound(); - JOptionPane.showMessageDialog(this, - "Thuốc '" + drugName + "' đã được cấp rồi!", - "Thuốc đã cấp", - JOptionPane.WARNING_MESSAGE); - searchPatientField.setText(""); - return; - } - - // Valid drug - open configure dialog - playSuccessSound(); - searchPatientField.setText(""); - openConfigureItemDialog(drugName, prescriptionRow); - } - - private String identifyDrugFromBarcode(String barcode) { - // TODO: Implement actual barcode lookup - // Mock implementation - switch (barcode) { - case "1234567890": return "Paracetamol 500mg"; - case "0987654321": return "Amoxicillin 250mg"; - default: return null; - } - } - - private int findDrugInPrescription(String drugName) { - for (int i = 0; i < prescriptionChecklistModel.getRowCount(); i++) { - String prescriptionDrug = prescriptionChecklistModel.getValueAt(i, 1).toString(); - // Remove warning icons for comparison - prescriptionDrug = prescriptionDrug.replace("⚠️ ", ""); - if (prescriptionDrug.equals(drugName)) { - return i; - } - } - return -1; - } - - private void openConfigureItemDialog(String drugName, int prescriptionRow) { - currentDispensingDrug = drugName; - - if (configureItemDialog == null) { - createConfigureItemDialog(); - } - - // Populate dialog with drug data - populateConfigureDialog(drugName); - - // Center dialog on parent - configureItemDialog.setLocationRelativeTo(this); - configureItemDialog.setVisible(true); - } - - private void createConfigureItemDialog() { - configureItemDialog = new JDialog((Frame) SwingUtilities.getWindowAncestor(this), - "Xác nhận chi tiết thuốc", true); - configureItemDialog.setSize(500, 400); - configureItemDialog.setLayout(new BorderLayout(10, 10)); - - // Title panel - JPanel titlePanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); - titlePanel.setBackground(new Color(63, 81, 181)); - JLabel titleLabel = new JLabel("Xác nhận chi tiết: "); - titleLabel.setFont(new Font("Arial", Font.BOLD, 16)); - titleLabel.setForeground(Color.WHITE); - titlePanel.add(titleLabel); - configureItemDialog.add(titlePanel, BorderLayout.NORTH); - - // Main content panel - JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); - mainPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); - - // Part 1: Unit & Quantity Selection - JPanel part1Panel = new JPanel(new GridBagLayout()); - part1Panel.setBorder(BorderFactory.createTitledBorder( - BorderFactory.createLineBorder(new Color(63, 81, 181)), - "PHẦN 1: CHỌN ĐƠN VỊ & SỐ LƯỢNG", - javax.swing.border.TitledBorder.LEFT, - javax.swing.border.TitledBorder.TOP, - new Font("Arial", Font.BOLD, 14), - new Color(63, 81, 181) - )); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(8, 8, 8, 8); - gbc.anchor = GridBagConstraints.WEST; - - // Unit selection - gbc.gridx = 0; gbc.gridy = 0; - part1Panel.add(new JLabel("Đơn vị:"), gbc); - gbc.gridx = 1; - unitComboBox = new JComboBox<>(new String[]{"Hộp (10 vỉ)", "Vỉ (10 viên)", "Viên"}); - unitComboBox.setPreferredSize(new Dimension(150, 25)); - unitComboBox.addActionListener(e -> updateUnitPrice()); - part1Panel.add(unitComboBox, gbc); - - gbc.gridx = 2; - part1Panel.add(new JLabel("Đơn giá:"), gbc); - gbc.gridx = 3; - unitPriceLabel = new JLabel("100,000 VNĐ"); - unitPriceLabel.setFont(new Font("Arial", Font.BOLD, 14)); - part1Panel.add(unitPriceLabel, gbc); - - // Quantity selection - gbc.gridx = 0; gbc.gridy = 1; - part1Panel.add(new JLabel("Số lượng:"), gbc); - gbc.gridx = 1; - quantitySpinner = new JSpinner(new SpinnerNumberModel(1, 1, 999, 1)); - quantitySpinner.setPreferredSize(new Dimension(80, 25)); - quantitySpinner.addChangeListener(e -> updateLineTotal()); - part1Panel.add(quantitySpinner, gbc); - - gbc.gridx = 2; - part1Panel.add(new JLabel("Thành tiền:"), gbc); - gbc.gridx = 3; - lineTotalLabel = new JLabel("100,000 VNĐ"); - lineTotalLabel.setFont(new Font("Arial", Font.BOLD, 14)); - lineTotalLabel.setForeground(new Color(244, 67, 54)); - part1Panel.add(lineTotalLabel, gbc); - - // Part 2: Batch Selection - JPanel part2Panel = new JPanel(new BorderLayout(5, 5)); - part2Panel.setBorder(BorderFactory.createTitledBorder( - BorderFactory.createLineBorder(new Color(76, 175, 80)), - "PHẦN 2: CHỌN LÔ SẢN XUẤT (Hệ thống đề xuất lô sắp hết hạn trước - FEFO)", - javax.swing.border.TitledBorder.LEFT, - javax.swing.border.TitledBorder.TOP, - new Font("Arial", Font.BOLD, 14), - new Color(76, 175, 80) - )); - - String[] batchColumns = {"Chọn", "Lô", "HSD", "Tồn kho"}; - batchTableModel = new DefaultTableModel(batchColumns, 0) { - @Override - public Class getColumnClass(int column) { - return column == 0 ? Boolean.class : String.class; - } - @Override - public boolean isCellEditable(int row, int column) { - return column == 0; // Only radio button is editable - } - }; - - batchTable = new JTable(batchTableModel); - batchTable.setRowHeight(25); - batchTable.setFont(new Font("Arial", Font.PLAIN, 13)); - batchTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 13)); - batchTable.getColumnModel().getColumn(0).setPreferredWidth(50); - batchTable.getColumnModel().getColumn(1).setPreferredWidth(100); - batchTable.getColumnModel().getColumn(2).setPreferredWidth(100); - batchTable.getColumnModel().getColumn(3).setPreferredWidth(100); - - // Single selection for batches - batchTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - JScrollPane batchScroll = new JScrollPane(batchTable); - batchScroll.setPreferredSize(new Dimension(400, 120)); - part2Panel.add(batchScroll, BorderLayout.CENTER); - - // Combine parts - JPanel contentPanel = new JPanel(new BorderLayout(10, 10)); - contentPanel.add(part1Panel, BorderLayout.NORTH); - contentPanel.add(part2Panel, BorderLayout.CENTER); - - mainPanel.add(contentPanel, BorderLayout.CENTER); - - // Button panel - JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 10)); - cancelDialogButton = new JButton("Hủy Bỏ"); - cancelDialogButton.setFont(new Font("Arial", Font.PLAIN, 14)); - cancelDialogButton.addActionListener(e -> configureItemDialog.setVisible(false)); - - confirmAddButton = new JButton("Xác Nhận & Thêm"); - confirmAddButton.setFont(new Font("Arial", Font.BOLD, 14)); - confirmAddButton.setBackground(new Color(76, 175, 80)); - confirmAddButton.setForeground(Color.WHITE); - confirmAddButton.setFocusPainted(false); - confirmAddButton.addActionListener(e -> confirmAndAddItem()); - - buttonPanel.add(cancelDialogButton); - buttonPanel.add(confirmAddButton); - - mainPanel.add(buttonPanel, BorderLayout.SOUTH); - configureItemDialog.add(mainPanel, BorderLayout.CENTER); - } - - private void populateConfigureDialog(String drugName) { - // Update title - JLabel titleLabel = (JLabel) ((JPanel) configureItemDialog.getContentPane() - .getComponent(0)).getComponent(0); - titleLabel.setText("Xác nhận chi tiết: " + drugName); - - // TODO: Load actual unit options and prices from database - // Mock data for demonstration - currentUnitPrice = 100000.0; // Default price for first unit - updateUnitPrice(); - updateLineTotal(); - - // Load available batches with FEFO logic - loadAvailableBatches(drugName); - } - - private void loadAvailableBatches(String drugName) { - // Clear existing data - batchTableModel.setRowCount(0); - - // TODO: Load actual batches from inventory database - // Mock data - sorted by expiry date (FEFO) - if (drugName.contains("Paracetamol")) { - batchTableModel.addRow(new Object[]{true, "PA202408", "15/08/2025", "45 hộp"}); // FEFO selected - batchTableModel.addRow(new Object[]{false, "PA202411", "30/11/2025", "18 hộp"}); - } else if (drugName.contains("Amoxicillin")) { - batchTableModel.addRow(new Object[]{true, "AM202407", "20/07/2025", "12 hộp"}); // FEFO selected - batchTableModel.addRow(new Object[]{false, "AM202410", "15/10/2025", "35 hộp"}); - } - - // Ensure only one batch is selected (radio button behavior) - setupSingleBatchSelection(); - } - - private void setupSingleBatchSelection() { - batchTableModel.addTableModelListener(e -> { - if (e.getColumn() == 0) { // Selection column - int changedRow = e.getFirstRow(); - Boolean newValue = (Boolean) batchTableModel.getValueAt(changedRow, 0); - - if (newValue != null && newValue) { - // Uncheck all other rows - for (int i = 0; i < batchTableModel.getRowCount(); i++) { - if (i != changedRow) { - batchTableModel.setValueAt(false, i, 0); - } - } - } - } - }); - } - - private void updateUnitPrice() { - // TODO: Get actual price from database based on selected unit - String selectedUnit = (String) unitComboBox.getSelectedItem(); - if (selectedUnit.contains("Hộp")) { - currentUnitPrice = 100000.0; - } else if (selectedUnit.contains("Vỉ")) { - currentUnitPrice = 10000.0; - } else { - currentUnitPrice = 1000.0; - } - - unitPriceLabel.setText(currencyFormatter.format(currentUnitPrice) + " VNĐ"); - updateLineTotal(); - } - - private void updateLineTotal() { - int quantity = (Integer) quantitySpinner.getValue(); - double lineTotal = currentUnitPrice * quantity; - lineTotalLabel.setText(currencyFormatter.format(lineTotal) + " VNĐ"); - } - - private void confirmAndAddItem() { - // Validate batch selection - int selectedBatchRow = -1; - for (int i = 0; i < batchTableModel.getRowCount(); i++) { - Boolean selected = (Boolean) batchTableModel.getValueAt(i, 0); - if (selected != null && selected) { - selectedBatchRow = i; - break; - } - } - - if (selectedBatchRow == -1) { - JOptionPane.showMessageDialog(configureItemDialog, - "Vui lòng chọn lô sản xuất!", "Chưa chọn lô", JOptionPane.WARNING_MESSAGE); - return; - } - - // Get selected data - String drugName = currentDispensingDrug; - String unit = (String) unitComboBox.getSelectedItem(); - int quantity = (Integer) quantitySpinner.getValue(); - String batch = batchTableModel.getValueAt(selectedBatchRow, 1).toString(); - String expiry = batchTableModel.getValueAt(selectedBatchRow, 2).toString(); - double unitPrice = currentUnitPrice; - double lineTotal = unitPrice * quantity; - - // Add to dispensed items table - dispensedItemsModel.addRow(new Object[]{ - drugName, - batch, - expiry, - quantity + " " + unit.split(" ")[0], // Extract unit name - currencyFormatter.format(unitPrice) + " VNĐ", - currencyFormatter.format(lineTotal) + " VNĐ" - }); - - // Mark prescription item as completed - int prescriptionRow = findDrugInPrescription(drugName); - if (prescriptionRow != -1) { - prescriptionChecklistModel.setValueAt(true, prescriptionRow, 0); - } - - // Update totals - if (drugName.contains("Dịch vụ")) { - serviceTotal += lineTotal; - } else { - medicineTotal += lineTotal; - } - updateTotals(); - updatePayButtonState(); - - // Close dialog - configureItemDialog.setVisible(false); - - // Success feedback - playSuccessSound(); - - // Refresh prescription table to update button states - prescriptionChecklistTable.repaint(); - - log.info("Added to dispensed items: " + drugName + " (Batch: " + batch + ", Qty: " + quantity + ")"); - } - - private void loadMockPatientData(String patientId) { - // Mock patient data - currentPatientId = patientId; - currentPatientName = "Nguyễn Văn A"; - currentDoctorName = "BS. Trần Thị Mai"; - - // IMPROVED PATIENT INFO DISPLAY - patientInfoLabel.setText(String.format( - "Bệnh nhân: %s | Mã BN: %s | Bác sĩ: %s", - currentPatientName, currentPatientId, currentDoctorName - )); - - // Clear previous data - prescriptionChecklistModel.setRowCount(0); - dispensedItemsModel.setRowCount(0); - doctorNotesArea.setText(""); - - // Mock prescription data (some automatically marked for services) - prescriptionChecklistModel.addRow(new Object[]{false, "Paracetamol 500mg", "2 hộp", "Cấp thuốc"}); - prescriptionChecklistModel.addRow(new Object[]{false, "Amoxicillin 250mg", "1 hộp", "Cấp thuốc"}); - prescriptionChecklistModel.addRow(new Object[]{true, "Dịch vụ khám tổng quát", "1 lần", "Đã cấp"}); // Auto-completed - - doctorNotesArea.setText("Uống thuốc sau ăn. Tránh uống cùng với sữa. Nếu có triệu chứng dị ứng, ngừng thuốc ngay. Tái khám sau 1 tuần."); - - updateTotals(); - updatePayButtonState(); - } - - private void calculateChange() { - try { - String cashText = cashReceivedField.getText().replace(",", "").trim(); - if (cashText.isEmpty()) { - changeLabel.setText("0 VNĐ"); - return; - } - - double cashReceived = Double.parseDouble(cashText); - double grandTotal = medicineTotal + serviceTotal; - double change = cashReceived - grandTotal; - - if (change >= 0) { - changeLabel.setText(currencyFormatter.format(change) + " VNĐ"); - changeLabel.setForeground(new Color(76, 175, 80)); - } else { - changeLabel.setText("THIẾU " + currencyFormatter.format(Math.abs(change)) + " VNĐ"); - changeLabel.setForeground(new Color(244, 67, 54)); - } - } catch (NumberFormatException e) { - changeLabel.setText("Số tiền không hợp lệ"); - changeLabel.setForeground(new Color(244, 67, 54)); - } - } - - private void updateTotals() { - medicineTotal = 0.0; - serviceTotal = 0.0; - - for (int i = 0; i < dispensedItemsModel.getRowCount(); i++) { - String drugName = dispensedItemsModel.getValueAt(i, 0).toString(); - double lineTotal = Double.parseDouble(dispensedItemsModel.getValueAt(i, 5).toString().replace(" VNĐ", "").replace(",", "")); - - if (drugName.contains("Dịch vụ")) { - serviceTotal += lineTotal; - } else { - medicineTotal += lineTotal; - } - } - - medicineSubtotalLabel.setText(currencyFormatter.format(medicineTotal) + " VNĐ"); - serviceSubtotalLabel.setText(currencyFormatter.format(serviceTotal) + " VNĐ"); - grandTotalLabel.setText(currencyFormatter.format(medicineTotal + serviceTotal) + " VNĐ"); - - calculateChange(); - } - - private void updatePayButtonState() { - // Check if all prescription items are completed - boolean allCompleted = true; - for (int i = 0; i < prescriptionChecklistModel.getRowCount(); i++) { - Boolean checked = (Boolean) prescriptionChecklistModel.getValueAt(i, 0); - if (checked == null || !checked) { - allCompleted = false; - break; - } - } - - payAndPrintButton.setEnabled(allCompleted && !currentPatientId.isEmpty()); - } - - private void clearCurrentTransaction() { - currentPatientId = ""; - currentPatientName = ""; - currentDoctorName = ""; - patientInfoLabel.setText("Chưa chọn bệnh nhân"); - searchPatientField.setText(""); - - prescriptionChecklistModel.setRowCount(0); - dispensedItemsModel.setRowCount(0); - doctorNotesArea.setText(""); - - cashReceivedField.setText(""); - changeLabel.setText("0 VNĐ"); - - medicineTotal = 0.0; - serviceTotal = 0.0; - updateTotals(); - updatePayButtonState(); - - searchPatientField.requestFocusInWindow(); - } - - private void processPaymentAndPrint() { - // TODO: Implement payment processing and receipt printing - log.info("Processing payment for patient: " + currentPatientName + " (ID: " + currentPatientId + ")"); - log.info("Total amount: " + currencyFormatter.format(medicineTotal + serviceTotal) + " VNĐ"); - - playSuccessSound(); - - JOptionPane.showMessageDialog(this, - "THANH TOÁN THÀNH CÔNG!\n\n" + - "Bệnh nhân: " + currentPatientName + "\n" + - "Tổng tiền: " + currencyFormatter.format(medicineTotal + serviceTotal) + " VNĐ\n\n" + - "TODO: Lưu hóa đơn và cập nhật tồn kho...", - "Xác nhận thanh toán", - JOptionPane.INFORMATION_MESSAGE); - - // Clear transaction after successful payment - clearCurrentTransaction(); - } - - // HELPER METHODS - private String getItemStatus(String itemName) { - // TODO: Get actual status from inventory system - if (itemName.contains("Amoxicillin")) { - return "Sắp hết hàng"; - } - return "Còn hàng"; - } - - // AUDIBLE FEEDBACK PLACEHOLDERS - private void playSuccessSound() { - // TODO: Implement pleasant beep sound - log.debug("Playing success sound"); - Toolkit.getDefaultToolkit().beep(); // Basic system beep for now - } - - private void playErrorSound() { - // TODO: Implement distinct error sound - log.debug("Playing error sound"); - for (int i = 0; i < 2; i++) { - Toolkit.getDefaultToolkit().beep(); - try { Thread.sleep(100); } catch (InterruptedException e) {} - } - } -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/DashboardPage/DashboardPage.java b/src/main/java/BsK/client/ui/component/DashboardPage/DashboardPage.java index 2616742..bfaa40f 100644 --- a/src/main/java/BsK/client/ui/component/DashboardPage/DashboardPage.java +++ b/src/main/java/BsK/client/ui/component/DashboardPage/DashboardPage.java @@ -9,108 +9,444 @@ import lombok.extern.slf4j.Slf4j; import javax.swing.*; import javax.swing.border.Border; +import javax.swing.table.DefaultTableModel; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.ActionListener; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; @Slf4j public class DashboardPage extends JPanel { private final MainFrame mainFrame; + + // TODO: Add backend request handlers + private JLabel todayPatientsLabel; + private JLabel recheckPatientsLabel; + private JLabel totalPatientsLabel; + private JLabel pendingResultsLabel; + private JTable recentActivitiesTable; + private JLabel lastUpdateLabel; public DashboardPage(MainFrame mainFrame) { this.mainFrame = mainFrame; setLayout(new BorderLayout()); - setBackground(new Color(240, 240, 240)); // A light background for the page + setBackground(new Color(245, 247, 250)); // --- Navigation Bar --- NavBar navBar = new NavBar(mainFrame, "Thống kê"); add(navBar, BorderLayout.NORTH); - // --- Main Content Panel (from existing DashboardPage) --- + // --- Main Content Panel --- JPanel mainContent = new JPanel(); mainContent.setLayout(new BorderLayout()); - mainContent.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - mainContent.setOpaque(false); // Make it transparent to see page background + mainContent.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); + mainContent.setOpaque(false); - // Top cards - JPanel topCards = new JPanel(); - topCards.setOpaque(false); // Make transparent - topCards.setLayout(new GridLayout(1, 4, 20, 20)); - String[] cardTitles = {"Add Check", "Income", "Expense", "Other Income"}; - // Using slightly more professional/subtle colors - Color[] cardColors = {new Color(100, 181, 246), new Color(77, 208, 225), new Color(255, 183, 77), new Color(129, 199, 132)}; + // Create main sections + JPanel topSection = createTopSection(); + JPanel centerSection = createCenterSection(); + JPanel bottomSection = createBottomSection(); - for (int i = 0; i < cardTitles.length; i++) { - RoundedPanel card = new RoundedPanel(15, cardColors[i], true); // Slightly smaller radius - card.setBackground(cardColors[i]); // Ensure background is set - card.setLayout(new BorderLayout(10,10)); - card.setBorder(BorderFactory.createEmptyBorder(20, 15, 20, 15)); // Adjusted padding - - JLabel cardTitleLabel = new JLabel(cardTitles[i]); - cardTitleLabel.setForeground(Color.WHITE); - cardTitleLabel.setFont(new Font("Arial", Font.BOLD, 16)); // Larger title - cardTitleLabel.setHorizontalAlignment(SwingConstants.CENTER); - card.add(cardTitleLabel, BorderLayout.NORTH); - - JLabel cardValueLabel = new JLabel("N/A"); // Placeholder value - cardValueLabel.setForeground(Color.WHITE); - cardValueLabel.setFont(new Font("Arial", Font.PLAIN, 24)); // Larger value text - cardValueLabel.setHorizontalAlignment(SwingConstants.CENTER); - card.add(cardValueLabel, BorderLayout.CENTER); - - topCards.add(card); - } - mainContent.add(topCards, BorderLayout.NORTH); - - JPanel wrapperPanel = new JPanel(new BorderLayout()); - wrapperPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0)); - wrapperPanel.setOpaque(false); - - RoundedPanel dataTablePanel = new RoundedPanel(15, Color.WHITE, false); - dataTablePanel.setLayout(new BorderLayout()); - dataTablePanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); - - String[] columns = {"Name", "Gender", "Course", "Fees", "Action"}; - String[][] data = { - {"John", "Male", "Java", "$300", "Edit/Delete"}, - {"Dara", "Male", "C++", "$300", "Edit/Delete"}, - {"Bora", "Male", "C#", "$300", "Edit/Delete"}, - }; - JTable table = new JTable(data, columns); - table.setRowHeight(25); - table.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); - table.setFont(new Font("Arial", Font.PLAIN, 12)); - JScrollPane tableScroll = new JScrollPane(table); - tableScroll.setBorder(BorderFactory.createEmptyBorder(10,0,0,0)); // Top padding for header - dataTablePanel.add(tableScroll, BorderLayout.CENTER); - - wrapperPanel.add(dataTablePanel, BorderLayout.CENTER); - mainContent.add(wrapperPanel, BorderLayout.CENTER); - - // Notice board (keeping it commented as in original) - /* - JPanel noticeBoard = new RoundedPanel(20, Color.WHITE, false); - noticeBoard.setLayout(new BorderLayout()); - noticeBoard.setBorder(BorderFactory.createTitledBorder("Notice Board")); - noticeBoard.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - - JTextArea noticeText = new JTextArea("04/10/2021\nHidemode Now\n\n03/10/2021\nFurther Reading\n"); - noticeText.setEditable(false); - noticeBoard.add(new JScrollPane(noticeText), BorderLayout.CENTER); - mainContent.add(noticeBoard, BorderLayout.EAST); - */ + mainContent.add(topSection, BorderLayout.NORTH); + mainContent.add(centerSection, BorderLayout.CENTER); + mainContent.add(bottomSection, BorderLayout.SOUTH); add(mainContent, BorderLayout.CENTER); + + // TODO: Initialize with real data from backend + loadDashboardData(); + } + + private JPanel createTopSection() { + JPanel topSection = new JPanel(new BorderLayout()); + topSection.setOpaque(false); + topSection.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0)); + + // Welcome header + JPanel headerPanel = createHeaderPanel(); + + // Patient metrics cards + JPanel metricsPanel = createMetricsPanel(); + + topSection.add(headerPanel, BorderLayout.NORTH); + topSection.add(metricsPanel, BorderLayout.CENTER); + + return topSection; + } + + private JPanel createHeaderPanel() { + JPanel headerPanel = new JPanel(new BorderLayout()); + headerPanel.setOpaque(false); + headerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0)); + + JLabel welcomeLabel = new JLabel("Chào mừng, " + LocalStorage.username); + welcomeLabel.setFont(new Font("Segoe UI", Font.BOLD, 28)); + welcomeLabel.setForeground(new Color(44, 82, 130)); + + lastUpdateLabel = new JLabel("Cập nhật lần cuối: " + getCurrentTime()); + lastUpdateLabel.setFont(new Font("Segoe UI", Font.PLAIN, 14)); + lastUpdateLabel.setForeground(new Color(107, 114, 128)); + + JButton refreshButton = new JButton("Làm mới"); + refreshButton.setFont(new Font("Segoe UI", Font.BOLD, 12)); + refreshButton.setBackground(new Color(59, 130, 246)); + refreshButton.setForeground(Color.WHITE); + refreshButton.setFocusPainted(false); + refreshButton.setBorder(BorderFactory.createEmptyBorder(8, 16, 8, 16)); + refreshButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + refreshButton.addActionListener(e -> { + // TODO: Implement refresh data from backend + refreshDashboardData(); + }); + + JPanel leftPanel = new JPanel(new BorderLayout()); + leftPanel.setOpaque(false); + leftPanel.add(welcomeLabel, BorderLayout.NORTH); + leftPanel.add(lastUpdateLabel, BorderLayout.SOUTH); + + headerPanel.add(leftPanel, BorderLayout.WEST); + headerPanel.add(refreshButton, BorderLayout.EAST); + + return headerPanel; + } + + private JPanel createMetricsPanel() { + JPanel metricsPanel = new JPanel(new GridLayout(1, 4, 20, 0)); + metricsPanel.setOpaque(false); + + // Patient metrics cards + String[] cardTitles = {"Bệnh nhân hôm nay", "Cần tái khám", "Tổng bệnh nhân", "Chờ kết quả"}; + Color[] cardColors = { + new Color(59, 130, 246), // Blue - Today's patients + new Color(245, 158, 11), // Orange - Recheck patients + new Color(16, 185, 129), // Green - Total patients + new Color(139, 92, 246) // Purple - Pending results + }; + String[] cardIcons = {"👥", "🔄", "📊", "⏳"}; + + JLabel[] valueLabels = new JLabel[4]; + + for (int i = 0; i < cardTitles.length; i++) { + RoundedPanel card = createMetricCard(cardTitles[i], cardColors[i], cardIcons[i]); + + // Store references to value labels for updating + Component[] components = card.getComponents(); + for (Component comp : components) { + if (comp instanceof JPanel) { + Component[] subComps = ((JPanel) comp).getComponents(); + for (Component subComp : subComps) { + if (subComp instanceof JLabel && ((JLabel) subComp).getFont().getSize() == 32) { + valueLabels[i] = (JLabel) subComp; + break; + } + } + } + } + + metricsPanel.add(card); + } + + // Store references for updating data + todayPatientsLabel = valueLabels[0]; + recheckPatientsLabel = valueLabels[1]; + totalPatientsLabel = valueLabels[2]; + pendingResultsLabel = valueLabels[3]; + + return metricsPanel; + } + + private RoundedPanel createMetricCard(String title, Color color, String icon) { + RoundedPanel card = new RoundedPanel(15, color, true); + card.setLayout(new BorderLayout()); + card.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + card.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + + // Icon and title panel + JPanel topPanel = new JPanel(new BorderLayout()); + topPanel.setOpaque(false); + + JLabel iconLabel = new JLabel(icon); + iconLabel.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 24)); + + JLabel titleLabel = new JLabel(title); + titleLabel.setForeground(Color.WHITE); + titleLabel.setFont(new Font("Segoe UI", Font.BOLD, 14)); + + topPanel.add(iconLabel, BorderLayout.WEST); + topPanel.add(titleLabel, BorderLayout.CENTER); + + // Value label + JLabel valueLabel = new JLabel("--"); + valueLabel.setForeground(Color.WHITE); + valueLabel.setFont(new Font("Segoe UI", Font.BOLD, 32)); + valueLabel.setHorizontalAlignment(SwingConstants.CENTER); + + card.add(topPanel, BorderLayout.NORTH); + card.add(valueLabel, BorderLayout.CENTER); + + // Add hover effect + card.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + card.setBackground(color.darker()); + } + + @Override + public void mouseExited(MouseEvent e) { + card.setBackground(color); + } + }); + + return card; + } + + private JPanel createCenterSection() { + JPanel centerSection = new JPanel(new GridLayout(1, 2, 20, 0)); + centerSection.setOpaque(false); + centerSection.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0)); + + // Recent activities panel + JPanel activitiesPanel = createRecentActivitiesPanel(); + + // Quick actions panel + JPanel actionsPanel = createQuickActionsPanel(); + + centerSection.add(activitiesPanel); + centerSection.add(actionsPanel); + + return centerSection; + } + + private JPanel createRecentActivitiesPanel() { + RoundedPanel panel = new RoundedPanel(15, Color.WHITE, false); + panel.setLayout(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + JLabel titleLabel = new JLabel("Hoạt động gần đây"); + titleLabel.setFont(new Font("Segoe UI", Font.BOLD, 18)); + titleLabel.setForeground(new Color(31, 41, 55)); + + // Table for recent activities + String[] columns = {"Thời gian", "Bệnh nhân", "Hoạt động", "Trạng thái"}; + DefaultTableModel tableModel = new DefaultTableModel(columns, 0) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + }; + + recentActivitiesTable = new JTable(tableModel); + recentActivitiesTable.setRowHeight(35); + recentActivitiesTable.getTableHeader().setFont(new Font("Segoe UI", Font.BOLD, 12)); + recentActivitiesTable.setFont(new Font("Segoe UI", Font.PLAIN, 12)); + recentActivitiesTable.setSelectionBackground(new Color(219, 234, 254)); + recentActivitiesTable.setGridColor(new Color(229, 231, 235)); + + JScrollPane tableScroll = new JScrollPane(recentActivitiesTable); + tableScroll.setBorder(BorderFactory.createEmptyBorder()); + tableScroll.getViewport().setBackground(Color.WHITE); + + panel.add(titleLabel, BorderLayout.NORTH); + panel.add(Box.createVerticalStrut(15), BorderLayout.NORTH); + panel.add(tableScroll, BorderLayout.CENTER); + + return panel; + } + + private JPanel createQuickActionsPanel() { + RoundedPanel panel = new RoundedPanel(15, Color.WHITE, false); + panel.setLayout(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + JLabel titleLabel = new JLabel("Thao tác nhanh"); + titleLabel.setFont(new Font("Segoe UI", Font.BOLD, 18)); + titleLabel.setForeground(new Color(31, 41, 55)); + + JPanel actionsGrid = new JPanel(new GridLayout(3, 2, 15, 15)); + actionsGrid.setOpaque(false); + actionsGrid.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0)); + + // Quick action buttons + String[] actionTitles = {"Thêm bệnh nhân", "Xem hàng đợi", "Báo cáo ngày", "Tìm bệnh nhân", "Cài đặt", "Khẩn cấp"}; + String[] actionIcons = {"➕", "📋", "📊", "🔍", "⚙️", "🚨"}; + Color[] actionColors = { + new Color(34, 197, 94), // Green - Add patient + new Color(59, 130, 246), // Blue - View queue + new Color(168, 85, 247), // Purple - Reports + new Color(245, 158, 11), // Orange - Search + new Color(107, 114, 128), // Gray - Settings + new Color(239, 68, 68) // Red - Emergency + }; + + for (int i = 0; i < actionTitles.length; i++) { + JButton actionButton = createQuickActionButton(actionTitles[i], actionIcons[i], actionColors[i]); + final int index = i; + actionButton.addActionListener(e -> handleQuickAction(index)); + actionsGrid.add(actionButton); + } + + panel.add(titleLabel, BorderLayout.NORTH); + panel.add(actionsGrid, BorderLayout.CENTER); + + return panel; + } + + private JButton createQuickActionButton(String title, String icon, Color color) { + JButton button = new JButton(); + button.setLayout(new BorderLayout()); + button.setBackground(color); + button.setForeground(Color.WHITE); + button.setFont(new Font("Segoe UI", Font.BOLD, 12)); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createEmptyBorder(15, 10, 15, 10)); + button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + + JLabel iconLabel = new JLabel(icon); + iconLabel.setFont(new Font("Segoe UI Emoji", Font.PLAIN, 20)); + iconLabel.setForeground(Color.WHITE); + iconLabel.setHorizontalAlignment(SwingConstants.CENTER); + + JLabel titleLabel = new JLabel(title); + titleLabel.setForeground(Color.WHITE); + titleLabel.setHorizontalAlignment(SwingConstants.CENTER); + + button.add(iconLabel, BorderLayout.CENTER); + button.add(titleLabel, BorderLayout.SOUTH); + + // Hover effect + button.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + button.setBackground(color.darker()); + } + + @Override + public void mouseExited(MouseEvent e) { + button.setBackground(color); + } + }); + + return button; + } + + private JPanel createBottomSection() { + RoundedPanel panel = new RoundedPanel(15, Color.WHITE, false); + panel.setLayout(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + JLabel titleLabel = new JLabel("Lịch hẹn hôm nay"); + titleLabel.setFont(new Font("Segoe UI", Font.BOLD, 18)); + titleLabel.setForeground(new Color(31, 41, 55)); + + // TODO: Create appointment calendar widget + JPanel appointmentPanel = new JPanel(new BorderLayout()); + appointmentPanel.setOpaque(false); + appointmentPanel.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0)); + + JLabel placeholderLabel = new JLabel("Tính năng lịch hẹn đang được phát triển..."); + placeholderLabel.setFont(new Font("Segoe UI", Font.ITALIC, 14)); + placeholderLabel.setForeground(new Color(107, 114, 128)); + placeholderLabel.setHorizontalAlignment(SwingConstants.CENTER); + + appointmentPanel.add(placeholderLabel, BorderLayout.CENTER); + + panel.add(titleLabel, BorderLayout.NORTH); + panel.add(appointmentPanel, BorderLayout.CENTER); + + return panel; + } + + // TODO: Implement backend data loading + private void loadDashboardData() { + // Placeholder data - replace with real backend calls + updateMetrics(12, 3, 156, 5); + loadRecentActivities(); + } + + private void updateMetrics(int todayPatients, int recheckPatients, int totalPatients, int pendingResults) { + if (todayPatientsLabel != null) todayPatientsLabel.setText(String.valueOf(todayPatients)); + if (recheckPatientsLabel != null) recheckPatientsLabel.setText(String.valueOf(recheckPatients)); + if (totalPatientsLabel != null) totalPatientsLabel.setText(String.valueOf(totalPatients)); + if (pendingResultsLabel != null) pendingResultsLabel.setText(String.valueOf(pendingResults)); + } + + private void loadRecentActivities() { + DefaultTableModel model = (DefaultTableModel) recentActivitiesTable.getModel(); + model.setRowCount(0); // Clear existing data + + // TODO: Replace with real data from backend + String[][] sampleData = { + {"10:30", "Nguyễn Văn A", "Khám tổng quát", "Hoàn thành"}, + {"10:15", "Trần Thị B", "Siêu âm", "Đang thực hiện"}, + {"09:45", "Lê Văn C", "Tái khám", "Chờ kết quả"}, + {"09:30", "Phạm Thị D", "Khám thai", "Hoàn thành"}, + {"09:00", "Hoàng Văn E", "Khám tổng quát", "Hoàn thành"} + }; + + for (String[] row : sampleData) { + model.addRow(row); + } + } + + private void refreshDashboardData() { + lastUpdateLabel.setText("Cập nhật lần cuối: " + getCurrentTime()); + // TODO: Implement real data refresh from backend + loadDashboardData(); + + JOptionPane.showMessageDialog(this, + "Đã làm mới dữ liệu thành công!", + "Thông báo", + JOptionPane.INFORMATION_MESSAGE); + } + + private void handleQuickAction(int actionIndex) { + String[] actions = {"AddPatient", "CheckUpPage", "Reports", "SearchPatient", "SettingsPage", "Emergency"}; + String[] messages = { + "Chuyển đến trang thêm bệnh nhân...", + "Chuyển đến trang xem hàng đợi...", + "Mở báo cáo ngày...", + "Mở tìm kiếm bệnh nhân...", + "Chuyển đến trang cài đặt...", + "Kích hoạt chế độ khẩn cấp!" + }; + + // TODO: Implement real navigation and actions + switch (actionIndex) { + case 0: // Add Patient - Navigate to patient data page + mainFrame.showPage("PatientDataPage"); + break; + case 1: // View Queue - Navigate to checkup page + mainFrame.showPage("CheckUpPage"); + break; + case 4: // Settings - Navigate to settings page + mainFrame.showPage("SettingsPage"); + break; + default: + JOptionPane.showMessageDialog(this, + messages[actionIndex], + "Thông báo", + JOptionPane.INFORMATION_MESSAGE); + } + } + + private String getCurrentTime() { + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss")); } - // Main method (usually for testing, can be kept or removed) public static void main(String[] args) { -// JFrame frame = new JFrame("Dashboard"); -// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); -// frame.setSize(1200, 800); -// DashboardPage d = new DashboardPage(null); // Pass null or a mock MainFrame for testing -// frame.add(d); -// frame.setVisible(true); + // Test frame for development + SwingUtilities.invokeLater(() -> { + JFrame frame = new JFrame("Dashboard Test"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(1400, 900); + frame.setLocationRelativeTo(null); + + LocalStorage.username = "Test User"; // Set test username + DashboardPage dashboard = new DashboardPage(null); + frame.add(dashboard); + frame.setVisible(true); + }); } } \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/InfoPage/InfoDialog.java b/src/main/java/BsK/client/ui/component/InfoPage/InfoDialog.java new file mode 100644 index 0000000..e792cbe --- /dev/null +++ b/src/main/java/BsK/client/ui/component/InfoPage/InfoDialog.java @@ -0,0 +1,158 @@ +package BsK.client.ui.component.InfoPage; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Desktop; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JEditorPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.border.EmptyBorder; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; + +public class InfoDialog extends JDialog { + + public InfoDialog() { + setTitle("Thông tin dự án BSK Clinic Management System"); + setModal(true); + setSize(900, 900); + setLocationRelativeTo(null); + setLayout(new BorderLayout()); + setResizable(true); + + // Create main content panel with better styling + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.setBackground(Color.WHITE); + mainPanel.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createRaisedBevelBorder(), + new EmptyBorder(20, 20, 20, 20) + )); + + String htmlContent = "" + + "" + + "
" + + "

BSK CLINIC MANAGEMENT SYSTEM

" + + "
" + + "
" + + + "
" + + "

Giới thiệu

" + + "

BSK là phần mềm quản lý phòng khám toàn diện, chuyên biệt hỗ trợ theo dõi bệnh nhân và in kết quả siêu âm. " + + "Dự án được phát triển bởi Lê Thành Đạt " + + "(lds) " + + "trong những năm đầu đại học với mục tiêu rèn luyện kỹ năng lập trình Java và xử lý backend, " + + "cùng với sự hỗ trợ quý báu từ Nguyễn Minh Tiến " + + "(tiennm99).

" + + "

Phần mềm hướng tới mục tiêu hiện đại hóa quy trình khám chữa bệnh, đặc biệt trong lĩnh vực siêu âm và sản khoa, " + + "giúp bệnh nhân dễ dàng tiếp cận thông tin và dịch vụ y tế chất lượng cao.

" + + "
" + + + "
" + + "

Tính năng nổi bật

" + + "
" + + "
    " + + "
  • Kết nối mạng nội bộ: Cho phép các máy tính trong phòng khám kết nối liền mạch và đồng bộ dữ liệu.
  • " + + "
  • Tự động phân phối ảnh siêu âm: Tự động theo dõi và gửi hình ảnh siêu âm đến đúng bệnh nhân một cách chính xác.
  • " + + "
  • Giao diện hiện đại: Thiết kế giao diện thân thiện, trực quan và dễ sử dụng cho mọi đối tượng người dùng.
  • " + + "
  • Tích hợp Google Drive: Dễ dàng tải kết quả khám lên Google Drive và chia sẻ trực tiếp với bệnh nhân.
  • " + + "
  • Thông báo tái khám thông minh: Hệ thống nhắc nhở bán tự động giúp bệnh nhân không bỏ lỡ lịch tái khám.
  • " + + "
" + + "
" + + "
" + + + "
" + + "

Sứ mệnh

" + + "
" + + "

Đây là một dự án mã nguồn mở được xây dựng cho mục đích sử dụng nội bộ và nghiên cứu. " + + "Chúng tôi khuyến khích các phòng khám, bệnh viện ứng dụng công nghệ hiện đại để nâng cao chất lượng dịch vụ, " + + "cải thiện trải nghiệm của bệnh nhân, từ đó góp phần vào sự phát triển bền vững của nền y tế Việt Nam.

" + + "
" + + "
" + + + "
" + + "

Bản quyền và Liên hệ

" + + "
" + + "

Dự án được phát triển và sở hữu bởi Lê Thành Đạt (lds). " + + "Mọi ý kiến đóng góp, phản hồi hoặc câu hỏi xin vui lòng liên hệ qua:

" + + "" + + "
" + + "
" + + + "
" + + "

Cách đóng góp

" + + "
" + + "

Chúng tôi luôn chào đón và trân trọng mọi sự đóng góp để cải thiện phần mềm. Bạn có thể hỗ trợ dự án bằng cách:

" + + "
    " + + "
  • Đề xuất tính năng mới hoặc báo cáo lỗi thông qua GitHub Issues.
  • " + + "
  • Đóng góp code thông qua Pull Requests.
  • " + + "
  • Ủng hộ dự án một ly cà phê qua MB Bank: 0908308878
  • " + + "
" + + "
" + + "
" + + ""; + + JEditorPane editorPane = new JEditorPane("text/html", htmlContent); + editorPane.setEditable(false); + editorPane.setOpaque(true); + editorPane.setBackground(Color.WHITE); + editorPane.setFont(new Font("Segoe UI", Font.PLAIN, 14)); + editorPane.addHyperlinkListener(new HyperlinkListener() { + @Override + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + if (Desktop.isDesktopSupported()) { + try { + Desktop.getDesktop().browse(e.getURL().toURI()); + } catch (IOException | URISyntaxException ex) { + ex.printStackTrace(); + } + } + } + } + }); + + JScrollPane scrollPane = new JScrollPane(editorPane); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + scrollPane.setBackground(Color.WHITE); + scrollPane.getViewport().setBackground(Color.WHITE); + scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + + mainPanel.add(scrollPane, BorderLayout.CENTER); + + // Create button panel with better styling + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + buttonPanel.setBackground(Color.WHITE); + buttonPanel.setBorder(new EmptyBorder(15, 0, 0, 0)); + + JButton closeButton = new JButton("Đóng"); + closeButton.setFont(new Font("Segoe UI", Font.BOLD, 14)); + closeButton.setPreferredSize(new Dimension(100, 35)); + closeButton.setBackground(new Color(44, 82, 130)); + closeButton.setForeground(Color.WHITE); + closeButton.setFocusPainted(false); + closeButton.setBorder(BorderFactory.createRaisedBevelBorder()); + closeButton.addActionListener(e -> dispose()); + + buttonPanel.add(closeButton); + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + + add(mainPanel, BorderLayout.CENTER); + } +} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/InventoryPage/GoodsReceiptPanel.java b/src/main/java/BsK/client/ui/component/InventoryPage/GoodsReceiptPanel.java deleted file mode 100644 index 5fb820c..0000000 --- a/src/main/java/BsK/client/ui/component/InventoryPage/GoodsReceiptPanel.java +++ /dev/null @@ -1,570 +0,0 @@ -package BsK.client.ui.component.InventoryPage; - -import BsK.client.ui.component.common.RoundedPanel; -import BsK.client.ui.component.InventoryPage.common.InventoryButtonFactory; -import BsK.client.ui.component.InventoryPage.common.InventoryColorScheme; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableColumn; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.text.NumberFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -@Slf4j -public class GoodsReceiptPanel extends JPanel { - private InventoryPage parentPage; - - // Header information - private JComboBox supplierCombo; - private JTextField invoiceNumberField; - private JTextField receiptDateField; - - // Medicine search and add - private JTextField medicineSearchField; - private JButton addMedicineButton; - - // Receipt items table - private JTable receiptTable; - private DefaultTableModel receiptTableModel; - - // Total and actions - private JLabel totalAmountLabel; - private JButton saveReceiptButton; - private JButton cancelButton; - private JButton printButton; - - // Formatters - private SimpleDateFormat dateFormatter; - private NumberFormat currencyFormatter; - - // Current receipt data - private double totalAmount = 0.0; - - public GoodsReceiptPanel(InventoryPage parentPage) { - this.parentPage = parentPage; - this.dateFormatter = new SimpleDateFormat("dd/MM/yyyy"); - this.currencyFormatter = NumberFormat.getNumberInstance(new Locale("vi", "VN")); - - setLayout(new BorderLayout()); - setBackground(new Color(245, 245, 245)); - - initializeComponents(); - layoutComponents(); - setupEventListeners(); - } - - private void initializeComponents() { - // Supplier combo - supplierCombo = new JComboBox<>(new String[]{ - "Chọn nhà cung cấp...", - "Công ty TNHH Dược phẩm ABC", - "Công ty Cổ phần Dược Hậu Giang", - "Công ty TNHH Dược phẩm Traphaco", - "Công ty Cổ phần Dược phẩm OPC", - "Công ty TNHH Dược phẩm Imexpharm" - }); - supplierCombo.setFont(new Font("Arial", Font.PLAIN, 14)); - supplierCombo.setPreferredSize(new Dimension(300, 35)); - - // Invoice number field - invoiceNumberField = new JTextField(); - invoiceNumberField.setFont(new Font("Arial", Font.PLAIN, 14)); - invoiceNumberField.setPreferredSize(new Dimension(200, 35)); - invoiceNumberField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(5, 10, 5, 10) - )); - - // Receipt date field (auto-filled with today) - receiptDateField = new JTextField(dateFormatter.format(new Date())); - receiptDateField.setFont(new Font("Arial", Font.PLAIN, 14)); - receiptDateField.setPreferredSize(new Dimension(150, 35)); - receiptDateField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(5, 10, 5, 10) - )); - - // Medicine search field with placeholder - medicineSearchField = new JTextField("Tìm thuốc theo tên hoặc quét mã vạch..."); - medicineSearchField.setFont(new Font("Arial", Font.ITALIC, 14)); - medicineSearchField.setForeground(InventoryColorScheme.TEXT_DISABLED); - medicineSearchField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(InventoryColorScheme.BORDER_LIGHT, 2), - BorderFactory.createEmptyBorder(12, 15, 12, 15) - )); - - // Add focus listener for placeholder behavior - medicineSearchField.addFocusListener(new java.awt.event.FocusAdapter() { - @Override - public void focusGained(java.awt.event.FocusEvent e) { - if (medicineSearchField.getText().equals("Tìm thuốc theo tên hoặc quét mã vạch...")) { - medicineSearchField.setText(""); - medicineSearchField.setFont(new Font("Arial", Font.PLAIN, 14)); - medicineSearchField.setForeground(InventoryColorScheme.TEXT_PRIMARY); - medicineSearchField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(InventoryColorScheme.PRIMARY_BLUE, 2), - BorderFactory.createEmptyBorder(12, 15, 12, 15) - )); - } - } - - @Override - public void focusLost(java.awt.event.FocusEvent e) { - if (medicineSearchField.getText().trim().isEmpty()) { - medicineSearchField.setText("Tìm thuốc theo tên hoặc quét mã vạch..."); - medicineSearchField.setFont(new Font("Arial", Font.ITALIC, 14)); - medicineSearchField.setForeground(InventoryColorScheme.TEXT_DISABLED); - medicineSearchField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(InventoryColorScheme.BORDER_LIGHT, 2), - BorderFactory.createEmptyBorder(12, 15, 12, 15) - )); - } - } - }); - - // Receipt table - String[] columns = {"Tên Thuốc", "Lô", "Hạn Sử Dụng", "Số Lượng", "Đơn Giá Nhập", "Thành Tiền", "Xóa"}; - receiptTableModel = new DefaultTableModel(columns, 0) { - @Override - public boolean isCellEditable(int row, int column) { - return column == 6; // Only delete column is editable - } - }; - receiptTable = new JTable(receiptTableModel); - setupReceiptTable(); - - // Total amount label - totalAmountLabel = new JLabel("0 VNĐ"); - totalAmountLabel.setFont(new Font("Arial", Font.BOLD, 24)); - totalAmountLabel.setForeground(new Color(76, 175, 80)); - totalAmountLabel.setHorizontalAlignment(SwingConstants.RIGHT); - - // Action buttons using standardized factory - saveReceiptButton = InventoryButtonFactory.createPrimaryButton("💾 Lưu Phiếu Nhập", InventoryButtonFactory.LARGE_BUTTON_SIZE); - cancelButton = InventoryButtonFactory.createSecondaryButton("❌ Hủy Bỏ", InventoryButtonFactory.LARGE_BUTTON_SIZE); - printButton = InventoryButtonFactory.createSpecialButton("🖨️ In Phiếu", InventoryButtonFactory.LARGE_BUTTON_SIZE); - printButton.setEnabled(false); - } - - private void setupReceiptTable() { - receiptTable.setFont(new Font("Arial", Font.PLAIN, 14)); - receiptTable.setRowHeight(40); - receiptTable.setBackground(Color.WHITE); - receiptTable.setGridColor(new Color(230, 230, 230)); - receiptTable.setShowVerticalLines(true); - receiptTable.setShowHorizontalLines(true); - receiptTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); - receiptTable.getTableHeader().setBackground(InventoryColorScheme.TABLE_HEADER_BG); - receiptTable.getTableHeader().setForeground(InventoryColorScheme.TABLE_HEADER_TEXT); - receiptTable.getTableHeader().setReorderingAllowed(false); - - // Set column widths - TableColumn col0 = receiptTable.getColumnModel().getColumn(0); // Tên Thuốc - col0.setPreferredWidth(200); - TableColumn col1 = receiptTable.getColumnModel().getColumn(1); // Lô - col1.setPreferredWidth(100); - TableColumn col2 = receiptTable.getColumnModel().getColumn(2); // HSD - col2.setPreferredWidth(100); - TableColumn col3 = receiptTable.getColumnModel().getColumn(3); // Số Lượng - col3.setPreferredWidth(100); - TableColumn col4 = receiptTable.getColumnModel().getColumn(4); // Đơn giá - col4.setPreferredWidth(120); - TableColumn col5 = receiptTable.getColumnModel().getColumn(5); // Thành tiền - col5.setPreferredWidth(140); - TableColumn col6 = receiptTable.getColumnModel().getColumn(6); // Xóa - col6.setPreferredWidth(80); - - // Center align specific columns - DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer(); - centerRenderer.setHorizontalAlignment(SwingConstants.CENTER); - receiptTable.getColumnModel().getColumn(1).setCellRenderer(centerRenderer); - receiptTable.getColumnModel().getColumn(2).setCellRenderer(centerRenderer); - receiptTable.getColumnModel().getColumn(3).setCellRenderer(centerRenderer); - - // Right align money columns - DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer(); - rightRenderer.setHorizontalAlignment(SwingConstants.RIGHT); - receiptTable.getColumnModel().getColumn(4).setCellRenderer(rightRenderer); - receiptTable.getColumnModel().getColumn(5).setCellRenderer(rightRenderer); - - // Delete button column - receiptTable.getColumnModel().getColumn(6).setCellRenderer(new DeleteButtonRenderer()); - receiptTable.getColumnModel().getColumn(6).setCellEditor(new DeleteButtonEditor()); - } - - private void layoutComponents() { - JPanel mainPanel = new JPanel(new BorderLayout(0, 20)); - mainPanel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - mainPanel.setOpaque(false); - - // Title - JLabel titleLabel = new JLabel("PHIẾU NHẬP KHO", SwingConstants.CENTER); - titleLabel.setFont(new Font("Arial", Font.BOLD, 28)); - titleLabel.setForeground(new Color(37, 47, 63)); - titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0)); - mainPanel.add(titleLabel, BorderLayout.NORTH); - - // Content area - JPanel contentPanel = new JPanel(new BorderLayout(0, 20)); - contentPanel.setOpaque(false); - - // Header info panel - RoundedPanel headerPanel = createHeaderPanel(); - contentPanel.add(headerPanel, BorderLayout.NORTH); - - // Medicine items panel - RoundedPanel itemsPanel = createItemsPanel(); - contentPanel.add(itemsPanel, BorderLayout.CENTER); - - // Footer with total and actions - RoundedPanel footerPanel = createFooterPanel(); - contentPanel.add(footerPanel, BorderLayout.SOUTH); - - mainPanel.add(contentPanel, BorderLayout.CENTER); - add(mainPanel, BorderLayout.CENTER); - } - - private RoundedPanel createHeaderPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new GridBagLayout()); - panel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(5, 5, 5, 15); - gbc.anchor = GridBagConstraints.WEST; - - // Row 1: Supplier - gbc.gridx = 0; gbc.gridy = 0; - panel.add(new JLabel("Nhà cung cấp:"), gbc); - gbc.gridx = 1; gbc.gridwidth = 2; - panel.add(supplierCombo, gbc); - - // Row 2: Invoice number and date - gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; - panel.add(new JLabel("Số Hóa Đơn:"), gbc); - gbc.gridx = 1; - panel.add(invoiceNumberField, gbc); - - gbc.gridx = 2; - panel.add(new JLabel("Ngày nhập:"), gbc); - gbc.gridx = 3; - panel.add(receiptDateField, gbc); - - return panel; - } - - private RoundedPanel createItemsPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout(0, 15)); - panel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - - // Simplified medicine search section - JPanel searchPanel = new JPanel(new BorderLayout(10, 0)); - searchPanel.setOpaque(false); - searchPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0)); - - JLabel searchIcon = new JLabel("🔍"); - searchIcon.setFont(new Font("Arial", Font.PLAIN, 16)); - searchPanel.add(searchIcon, BorderLayout.WEST); - - medicineSearchField.setPreferredSize(new Dimension(0, 45)); - searchPanel.add(medicineSearchField, BorderLayout.CENTER); - - panel.add(searchPanel, BorderLayout.NORTH); - - // Items table - JScrollPane scrollPane = new JScrollPane(receiptTable); - scrollPane.setBorder(BorderFactory.createTitledBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - " Danh sách thuốc nhập kho ", - 0, 0, new Font("Arial", Font.BOLD, 14), new Color(37, 47, 63) - )); - scrollPane.getViewport().setBackground(Color.WHITE); - scrollPane.setPreferredSize(new Dimension(0, 300)); - - panel.add(scrollPane, BorderLayout.CENTER); - - return panel; - } - - private RoundedPanel createFooterPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout(20, 0)); - panel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - - // Total section - JPanel totalPanel = new JPanel(new BorderLayout()); - totalPanel.setOpaque(false); - - JLabel totalLabel = new JLabel("TỔNG TIỀN NHẬP KHO:"); - totalLabel.setFont(new Font("Arial", Font.BOLD, 18)); - totalLabel.setForeground(new Color(37, 47, 63)); - totalPanel.add(totalLabel, BorderLayout.WEST); - totalPanel.add(totalAmountLabel, BorderLayout.EAST); - - // Actions panel with split layout (Cancel left, Print/Save right) - JButton[] leftButtons = {cancelButton}; - JButton[] rightButtons = {printButton, saveReceiptButton}; - JPanel actionsPanel = InventoryButtonFactory.createSplitButtonGroup(leftButtons, rightButtons); - - panel.add(totalPanel, BorderLayout.WEST); - panel.add(actionsPanel, BorderLayout.EAST); - - return panel; - } - - private void setupEventListeners() { - // Medicine search field - Enter key adds medicine - medicineSearchField.addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == KeyEvent.VK_ENTER) { - addMedicineToReceipt(); - } - } - }); - - - - // Action buttons - saveReceiptButton.addActionListener(e -> saveReceipt()); - cancelButton.addActionListener(e -> cancelReceipt()); - printButton.addActionListener(e -> printReceipt()); - } - - private void addMedicineToReceipt() { - String searchText = medicineSearchField.getText().trim(); - if (searchText.isEmpty() || searchText.equals("Tìm thuốc theo tên hoặc quét mã vạch...")) { - JOptionPane.showMessageDialog(this, "Vui lòng nhập tên thuốc hoặc quét mã vạch!", - "Thông báo", JOptionPane.WARNING_MESSAGE); - return; - } - - // TODO: Search medicine in database and show selection dialog - showAddMedicineDialog(searchText); - } - - private void showAddMedicineDialog(String searchText) { - // Mock implementation - show a simple dialog for adding medicine details - JDialog dialog = new JDialog((Frame) SwingUtilities.getWindowAncestor(this), "Thêm Thuốc Vào Phiếu Nhập", true); - dialog.setLayout(new BorderLayout()); - dialog.setSize(500, 400); - dialog.setLocationRelativeTo(this); - - JPanel formPanel = new JPanel(new GridBagLayout()); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(10, 10, 10, 10); - gbc.anchor = GridBagConstraints.WEST; - - // Medicine name (read-only, from search) - gbc.gridx = 0; gbc.gridy = 0; - formPanel.add(new JLabel("Tên thuốc:"), gbc); - gbc.gridx = 1; - JTextField nameField = new JTextField(searchText, 20); - nameField.setEditable(false); - formPanel.add(nameField, gbc); - - // Batch number - gbc.gridx = 0; gbc.gridy = 1; - formPanel.add(new JLabel("Số lô:"), gbc); - gbc.gridx = 1; - JTextField batchField = new JTextField(20); - formPanel.add(batchField, gbc); - - // Expiry date - gbc.gridx = 0; gbc.gridy = 2; - formPanel.add(new JLabel("Hạn sử dụng:"), gbc); - gbc.gridx = 1; - JTextField expiryField = new JTextField("dd/MM/yyyy", 20); - formPanel.add(expiryField, gbc); - - // Quantity - gbc.gridx = 0; gbc.gridy = 3; - formPanel.add(new JLabel("Số lượng:"), gbc); - gbc.gridx = 1; - JSpinner quantitySpinner = new JSpinner(new SpinnerNumberModel(1, 1, 9999, 1)); - formPanel.add(quantitySpinner, gbc); - - // Unit price - gbc.gridx = 0; gbc.gridy = 4; - formPanel.add(new JLabel("Đơn giá nhập:"), gbc); - gbc.gridx = 1; - JTextField priceField = new JTextField("0", 20); - formPanel.add(priceField, gbc); - - // Buttons - JPanel buttonPanel = new JPanel(new FlowLayout()); - JButton addBtn = new JButton("Thêm"); - JButton cancelBtn = new JButton("Hủy"); - - addBtn.addActionListener(e -> { - try { - String medicineName = nameField.getText(); - String batch = batchField.getText(); - String expiry = expiryField.getText(); - int quantity = (Integer) quantitySpinner.getValue(); - double price = Double.parseDouble(priceField.getText().replace(",", "")); - double lineTotal = quantity * price; - - // Add to table - receiptTableModel.addRow(new Object[]{ - medicineName, - batch, - expiry, - quantity, - currencyFormatter.format(price), - currencyFormatter.format(lineTotal), - "🗑️" - }); - - updateTotalAmount(); - medicineSearchField.setText(""); - dialog.dispose(); - - } catch (NumberFormatException ex) { - JOptionPane.showMessageDialog(dialog, "Vui lòng nhập đúng định dạng số!", - "Lỗi", JOptionPane.ERROR_MESSAGE); - } - }); - - cancelBtn.addActionListener(e -> dialog.dispose()); - - buttonPanel.add(addBtn); - buttonPanel.add(cancelBtn); - - dialog.add(formPanel, BorderLayout.CENTER); - dialog.add(buttonPanel, BorderLayout.SOUTH); - dialog.setVisible(true); - } - - private void updateTotalAmount() { - totalAmount = 0.0; - for (int i = 0; i < receiptTableModel.getRowCount(); i++) { - String lineTotalStr = (String) receiptTableModel.getValueAt(i, 5); - try { - double lineTotal = Double.parseDouble(lineTotalStr.replace(",", "")); - totalAmount += lineTotal; - } catch (NumberFormatException e) { - log.warn("Could not parse line total: " + lineTotalStr); - } - } - totalAmountLabel.setText(currencyFormatter.format(totalAmount) + " VNĐ"); - } - - private void saveReceipt() { - if (receiptTableModel.getRowCount() == 0) { - JOptionPane.showMessageDialog(this, "Vui lòng thêm ít nhất một loại thuốc!", - "Thông báo", JOptionPane.WARNING_MESSAGE); - return; - } - - if (supplierCombo.getSelectedIndex() == 0) { - JOptionPane.showMessageDialog(this, "Vui lòng chọn nhà cung cấp!", - "Thông báo", JOptionPane.WARNING_MESSAGE); - return; - } - - // TODO: Save receipt to database - JOptionPane.showMessageDialog(this, "Lưu phiếu nhập kho thành công!\nTổng tiền: " + totalAmountLabel.getText(), - "Thành công", JOptionPane.INFORMATION_MESSAGE); - - printButton.setEnabled(true); - clearForm(); - } - - private void cancelReceipt() { - int confirm = JOptionPane.showConfirmDialog(this, - "Bạn có chắc chắn muốn hủy phiếu nhập này?", - "Xác nhận", JOptionPane.YES_NO_OPTION); - - if (confirm == JOptionPane.YES_OPTION) { - clearForm(); - } - } - - private void printReceipt() { - // TODO: Implement print functionality - JOptionPane.showMessageDialog(this, "Chức năng in phiếu nhập kho đang được phát triển!", - "Thông báo", JOptionPane.INFORMATION_MESSAGE); - } - - private void clearForm() { - supplierCombo.setSelectedIndex(0); - invoiceNumberField.setText(""); - receiptDateField.setText(dateFormatter.format(new Date())); - - // Reset search field to placeholder - medicineSearchField.setText("Tìm thuốc theo tên hoặc quét mã vạch..."); - medicineSearchField.setFont(new Font("Arial", Font.ITALIC, 14)); - medicineSearchField.setForeground(InventoryColorScheme.TEXT_DISABLED); - medicineSearchField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(InventoryColorScheme.BORDER_LIGHT, 2), - BorderFactory.createEmptyBorder(12, 15, 12, 15) - )); - - receiptTableModel.setRowCount(0); - totalAmount = 0.0; - totalAmountLabel.setText("0 VNĐ"); - printButton.setEnabled(false); - } - - public void refreshData() { - // TODO: Refresh supplier list and other master data - log.info("Goods receipt panel data refreshed"); - } - - // Custom cell renderer for delete button - private class DeleteButtonRenderer extends DefaultTableCellRenderer { - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, - boolean hasFocus, int row, int column) { - return InventoryButtonFactory.createIconButton( - InventoryButtonFactory.ICON_DELETE, - InventoryColorScheme.DANGER_RED, - InventoryColorScheme.darken(InventoryColorScheme.DANGER_RED, 0.2f), - "Xóa dòng này" - ); - } - } - - // Custom cell editor for delete button - private class DeleteButtonEditor extends DefaultCellEditor { - private JButton button; - private int currentRow; - - public DeleteButtonEditor() { - super(new JCheckBox()); - button = InventoryButtonFactory.createIconButton( - InventoryButtonFactory.ICON_DELETE, - InventoryColorScheme.DANGER_RED, - InventoryColorScheme.darken(InventoryColorScheme.DANGER_RED, 0.2f), - "Xóa dòng này" - ); - button.addActionListener(e -> { - receiptTableModel.removeRow(currentRow); - updateTotalAmount(); - fireEditingStopped(); - }); - } - - @Override - public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { - currentRow = row; - return button; - } - - @Override - public Object getCellEditorValue() { - return "🗑️"; - } - } -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/InventoryPage/InventoryDashboard.java b/src/main/java/BsK/client/ui/component/InventoryPage/InventoryDashboard.java deleted file mode 100644 index a6bd4f8..0000000 --- a/src/main/java/BsK/client/ui/component/InventoryPage/InventoryDashboard.java +++ /dev/null @@ -1,228 +0,0 @@ -package BsK.client.ui.component.InventoryPage; - -import BsK.client.ui.component.common.RoundedPanel; -import BsK.client.ui.component.InventoryPage.common.InventoryButtonFactory; -import BsK.client.ui.component.InventoryPage.common.InventoryColorScheme; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -@Slf4j -public class InventoryDashboard extends JPanel { - private InventoryPage parentPage; - - // Dashboard metrics - private JLabel lowStockCountLabel; - private JLabel expiringCountLabel; - private JLabel totalValueLabel; - private JLabel totalItemsLabel; - - // Quick action buttons - private JButton newReceiptButton; - private JButton viewInventoryButton; - private JButton lowStockReportButton; - private JButton expiryReportButton; - - public InventoryDashboard(InventoryPage parentPage) { - this.parentPage = parentPage; - setLayout(new BorderLayout()); - setBackground(new Color(245, 245, 245)); - - initializeComponents(); - layoutComponents(); - setupEventListeners(); - } - - private void initializeComponents() { - // Initialize metric labels with refined colors - lowStockCountLabel = new JLabel("0"); - lowStockCountLabel.setFont(new Font("Arial", Font.BOLD, 42)); - lowStockCountLabel.setForeground(InventoryColorScheme.DANGER_RED); - lowStockCountLabel.setHorizontalAlignment(SwingConstants.CENTER); - - expiringCountLabel = new JLabel("0"); - expiringCountLabel.setFont(new Font("Arial", Font.BOLD, 42)); - expiringCountLabel.setForeground(InventoryColorScheme.WARNING_ORANGE); - expiringCountLabel.setHorizontalAlignment(SwingConstants.CENTER); - - totalValueLabel = new JLabel("0 VNĐ"); - totalValueLabel.setFont(new Font("Arial", Font.BOLD, 28)); - totalValueLabel.setForeground(InventoryColorScheme.SUCCESS_GREEN); - totalValueLabel.setHorizontalAlignment(SwingConstants.CENTER); - - totalItemsLabel = new JLabel("0"); - totalItemsLabel.setFont(new Font("Arial", Font.BOLD, 42)); - totalItemsLabel.setForeground(InventoryColorScheme.PRIMARY_BLUE); - totalItemsLabel.setHorizontalAlignment(SwingConstants.CENTER); - - // Initialize quick action buttons using standardized factory - newReceiptButton = InventoryButtonFactory.createSuccessButton("📥 Nhập Kho Mới", InventoryButtonFactory.LARGE_BUTTON_SIZE); - viewInventoryButton = InventoryButtonFactory.createPrimaryButton("📦 Xem Tồn Kho", InventoryButtonFactory.LARGE_BUTTON_SIZE); - lowStockReportButton = InventoryButtonFactory.createDestructiveButton("⚠️ Thuốc Sắp Hết", InventoryButtonFactory.LARGE_BUTTON_SIZE); - expiryReportButton = InventoryButtonFactory.createSpecialButton("📅 Sắp Hết Hạn", InventoryButtonFactory.LARGE_BUTTON_SIZE); - } - - - - private void layoutComponents() { - JPanel mainPanel = new JPanel(new BorderLayout(20, 20)); - mainPanel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30)); - mainPanel.setOpaque(false); - - // Title - JLabel titleLabel = new JLabel("Tổng Quan Kho Thuốc", SwingConstants.CENTER); - titleLabel.setFont(new Font("Arial", Font.BOLD, 28)); - titleLabel.setForeground(new Color(37, 47, 63)); - titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0)); - mainPanel.add(titleLabel, BorderLayout.NORTH); - - // Metrics cards - JPanel metricsPanel = createMetricsPanel(); - mainPanel.add(metricsPanel, BorderLayout.CENTER); - - // Quick actions - JPanel actionsPanel = createActionsPanel(); - mainPanel.add(actionsPanel, BorderLayout.SOUTH); - - add(mainPanel, BorderLayout.CENTER); - } - - private JPanel createMetricsPanel() { - JPanel panel = new JPanel(new GridLayout(2, 2, 20, 20)); - panel.setOpaque(false); - - // Low Stock Card - RoundedPanel lowStockCard = createMetricCard( - "Thuốc Sắp Hết", - lowStockCountLabel, - "sản phẩm dưới mức tối thiểu", - InventoryColorScheme.CARD_LOW_STOCK_BORDER, - InventoryColorScheme.CARD_LOW_STOCK_BG - ); - - // Expiring Soon Card - RoundedPanel expiringCard = createMetricCard( - "Sắp Hết Hạn", - expiringCountLabel, - "lô thuốc trong 3 tháng tới", - InventoryColorScheme.CARD_EXPIRING_BORDER, - InventoryColorScheme.CARD_EXPIRING_BG - ); - - // Total Value Card - RoundedPanel valueCard = createMetricCard( - "Tổng Giá Trị Kho", - totalValueLabel, - "tại thời điểm hiện tại", - InventoryColorScheme.CARD_VALUE_BORDER, - InventoryColorScheme.CARD_VALUE_BG - ); - - // Total Items Card - RoundedPanel itemsCard = createMetricCard( - "Tổng Số Mặt Hàng", - totalItemsLabel, - "loại thuốc đang quản lý", - InventoryColorScheme.CARD_ITEMS_BORDER, - InventoryColorScheme.CARD_ITEMS_BG - ); - - panel.add(lowStockCard); - panel.add(expiringCard); - panel.add(valueCard); - panel.add(itemsCard); - - return panel; - } - - private RoundedPanel createMetricCard(String title, JLabel valueLabel, String subtitle, Color accentColor, Color bgColor) { - RoundedPanel card = new RoundedPanel(15, bgColor, true); - card.setLayout(new BorderLayout(5, 5)); - card.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(accentColor, 2), - BorderFactory.createEmptyBorder(25, 20, 25, 20) - )); - card.setPreferredSize(new Dimension(280, 160)); - - // Title - JLabel titleLabel = new JLabel(title, SwingConstants.CENTER); - titleLabel.setFont(new Font("Arial", Font.BOLD, 16)); - titleLabel.setForeground(accentColor); - titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 10, 0)); - card.add(titleLabel, BorderLayout.NORTH); - - // Value (centered with more space) - JPanel valuePanel = new JPanel(new GridBagLayout()); - valuePanel.setOpaque(false); - valueLabel.setHorizontalAlignment(SwingConstants.CENTER); - valueLabel.setVerticalAlignment(SwingConstants.CENTER); - valuePanel.add(valueLabel); - card.add(valuePanel, BorderLayout.CENTER); - - // Subtitle - JLabel subtitleLabel = new JLabel(subtitle, SwingConstants.CENTER); - subtitleLabel.setFont(new Font("Arial", Font.PLAIN, 12)); - subtitleLabel.setForeground(InventoryColorScheme.TEXT_SECONDARY); - subtitleLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0)); - card.add(subtitleLabel, BorderLayout.SOUTH); - - return card; - } - - private JPanel createActionsPanel() { - JPanel panel = new JPanel(new BorderLayout(20, 20)); - panel.setOpaque(false); - panel.setBorder(BorderFactory.createEmptyBorder(30, 0, 0, 0)); - - // Actions title - JLabel actionsTitle = new JLabel("Thao Tác Nhanh", SwingConstants.CENTER); - actionsTitle.setFont(new Font("Arial", Font.BOLD, 20)); - actionsTitle.setForeground(new Color(37, 47, 63)); - panel.add(actionsTitle, BorderLayout.NORTH); - - // Action buttons with improved layout - JPanel buttonsPanel = new JPanel(new GridLayout(2, 2, 20, 20)); - buttonsPanel.setOpaque(false); - buttonsPanel.setBorder(BorderFactory.createEmptyBorder(20, 30, 10, 30)); - - buttonsPanel.add(newReceiptButton); - buttonsPanel.add(viewInventoryButton); - buttonsPanel.add(lowStockReportButton); - buttonsPanel.add(expiryReportButton); - - panel.add(buttonsPanel, BorderLayout.CENTER); - - return panel; - } - - private void setupEventListeners() { - newReceiptButton.addActionListener(e -> parentPage.navigateToGoodsReceipt()); - viewInventoryButton.addActionListener(e -> parentPage.navigateToInventoryView()); - lowStockReportButton.addActionListener(e -> { - parentPage.navigateToReports(); - // TODO: Auto-generate low stock report - }); - expiryReportButton.addActionListener(e -> { - parentPage.navigateToReports(); - // TODO: Auto-generate expiry report - }); - } - - public void refreshData() { - // TODO: Load actual data from database - loadMockDashboardData(); - } - - private void loadMockDashboardData() { - // Mock data for demonstration - lowStockCountLabel.setText("7"); - expiringCountLabel.setText("12"); - totalValueLabel.setText(parentPage.getCurrencyFormatter().format(45600000) + " VNĐ"); - totalItemsLabel.setText("156"); - - log.info("Dashboard metrics refreshed"); - } -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/InventoryPage/InventoryPage.java b/src/main/java/BsK/client/ui/component/InventoryPage/InventoryPage.java deleted file mode 100644 index 50a25f8..0000000 --- a/src/main/java/BsK/client/ui/component/InventoryPage/InventoryPage.java +++ /dev/null @@ -1,281 +0,0 @@ -package BsK.client.ui.component.InventoryPage; - -import BsK.client.LocalStorage; -import BsK.client.ui.component.MainFrame; -import BsK.client.ui.component.common.NavBar; -import BsK.client.ui.component.common.RoundedPanel; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.DefaultTableCellRenderer; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.text.NumberFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -@Slf4j -public class InventoryPage extends JPanel { - private MainFrame mainFrame; - - // Navigation components - private JPanel sideNavPanel; - private JPanel contentPanel; - private CardLayout cardLayout; - - // Navigation buttons - private JButton dashboardButton; - private JButton inventoryViewButton; - private JButton goodsReceiptButton; - private JButton stockAdjustmentButton; - private JButton reportsButton; - private JButton productMasterButton; - private JButton supplierMasterButton; - - // Sub-panels for each section - private InventoryDashboard dashboardPanel; - private InventoryViewPanel inventoryViewPanel; - private GoodsReceiptPanel goodsReceiptPanel; - private StockAdjustmentPanel stockAdjustmentPanel; - private ReportsPanel reportsPanel; - private ProductMasterPanel productMasterPanel; - private SupplierMasterPanel supplierMasterPanel; - - // Common utilities - private NumberFormat currencyFormatter; - private SimpleDateFormat dateFormatter; - - // Current selected section - private String currentSection = "dashboard"; - - public InventoryPage(MainFrame mainFrame) { - this.mainFrame = mainFrame; - this.currencyFormatter = NumberFormat.getNumberInstance(new Locale("vi", "VN")); - this.dateFormatter = new SimpleDateFormat("dd/MM/yyyy"); - - setLayout(new BorderLayout()); - setBackground(new Color(245, 245, 245)); - - // Navigation Bar - NavBar navBar = new NavBar(mainFrame, "Quản Lý Kho Thuốc"); - add(navBar, BorderLayout.NORTH); - - initializeComponents(); - layoutComponents(); - setupEventListeners(); - - // Show dashboard by default - showSection("dashboard"); - } - - private void initializeComponents() { - // Initialize side navigation - createSideNavigation(); - - // Initialize content area with card layout - cardLayout = new CardLayout(); - contentPanel = new JPanel(cardLayout); - contentPanel.setBackground(Color.WHITE); - - // Initialize all panels - dashboardPanel = new InventoryDashboard(this); - inventoryViewPanel = new InventoryViewPanel(this); - goodsReceiptPanel = new GoodsReceiptPanel(this); - stockAdjustmentPanel = new StockAdjustmentPanel(this); - reportsPanel = new ReportsPanel(this); - productMasterPanel = new ProductMasterPanel(this); - supplierMasterPanel = new SupplierMasterPanel(this); - - // Add panels to card layout - contentPanel.add(dashboardPanel, "dashboard"); - contentPanel.add(inventoryViewPanel, "inventory"); - contentPanel.add(goodsReceiptPanel, "receipt"); - contentPanel.add(stockAdjustmentPanel, "adjustment"); - contentPanel.add(reportsPanel, "reports"); - contentPanel.add(productMasterPanel, "products"); - contentPanel.add(supplierMasterPanel, "suppliers"); - } - - private void createSideNavigation() { - sideNavPanel = new JPanel(); - sideNavPanel.setLayout(new BoxLayout(sideNavPanel, BoxLayout.Y_AXIS)); - sideNavPanel.setBackground(new Color(37, 47, 63)); - sideNavPanel.setPreferredSize(new Dimension(220, 0)); - sideNavPanel.setBorder(BorderFactory.createEmptyBorder(20, 0, 0, 0)); - - // Navigation title - JLabel titleLabel = new JLabel("QUẢN LÝ KHO"); - titleLabel.setFont(new Font("Arial", Font.BOLD, 16)); - titleLabel.setForeground(Color.WHITE); - titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT); - titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0)); - sideNavPanel.add(titleLabel); - - // Create navigation buttons - dashboardButton = createNavButton("📊 Tổng Quan", "dashboard"); - inventoryViewButton = createNavButton("📦 Tồn Kho", "inventory"); - goodsReceiptButton = createNavButton("📥 Nhập Kho", "receipt"); - stockAdjustmentButton = createNavButton("📋 Kiểm Kê Kho", "adjustment"); - reportsButton = createNavButton("📈 Báo Cáo", "reports"); - - // Add separator - sideNavPanel.add(Box.createVerticalStrut(15)); - JLabel masterDataLabel = new JLabel("DANH MỤC"); - masterDataLabel.setFont(new Font("Arial", Font.BOLD, 12)); - masterDataLabel.setForeground(new Color(150, 150, 150)); - masterDataLabel.setAlignmentX(Component.CENTER_ALIGNMENT); - sideNavPanel.add(masterDataLabel); - sideNavPanel.add(Box.createVerticalStrut(10)); - - productMasterButton = createNavButton("💊 Thuốc", "products"); - supplierMasterButton = createNavButton("🏢 Nhà Cung Cấp", "suppliers"); - - // Add buttons to panel - sideNavPanel.add(dashboardButton); - sideNavPanel.add(inventoryViewButton); - sideNavPanel.add(goodsReceiptButton); - sideNavPanel.add(stockAdjustmentButton); - sideNavPanel.add(reportsButton); - sideNavPanel.add(Box.createVerticalStrut(10)); - sideNavPanel.add(productMasterButton); - sideNavPanel.add(supplierMasterButton); - - // Add flexible space at bottom - sideNavPanel.add(Box.createVerticalGlue()); - } - - private JButton createNavButton(String text, String section) { - JButton button = new JButton(text); - button.setFont(new Font("Arial", Font.PLAIN, 14)); - button.setForeground(Color.WHITE); - button.setBackground(new Color(37, 47, 63)); - button.setBorder(BorderFactory.createEmptyBorder(12, 20, 12, 20)); - button.setFocusPainted(false); - button.setAlignmentX(Component.LEFT_ALIGNMENT); - button.setMaximumSize(new Dimension(220, 45)); - button.setHorizontalAlignment(SwingConstants.LEFT); - button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - - // Hover effect - button.addMouseListener(new java.awt.event.MouseAdapter() { - @Override - public void mouseEntered(java.awt.event.MouseEvent e) { - if (!section.equals(currentSection)) { - button.setBackground(new Color(52, 62, 78)); - } - } - - @Override - public void mouseExited(java.awt.event.MouseEvent e) { - if (!section.equals(currentSection)) { - button.setBackground(new Color(37, 47, 63)); - } - } - }); - - return button; - } - - private void layoutComponents() { - // Main layout: side navigation + content - add(sideNavPanel, BorderLayout.WEST); - add(contentPanel, BorderLayout.CENTER); - } - - private void setupEventListeners() { - dashboardButton.addActionListener(e -> showSection("dashboard")); - inventoryViewButton.addActionListener(e -> showSection("inventory")); - goodsReceiptButton.addActionListener(e -> showSection("receipt")); - stockAdjustmentButton.addActionListener(e -> showSection("adjustment")); - reportsButton.addActionListener(e -> showSection("reports")); - productMasterButton.addActionListener(e -> showSection("products")); - supplierMasterButton.addActionListener(e -> showSection("suppliers")); - } - - public void showSection(String section) { - // Update button states - resetNavButtonColors(); - currentSection = section; - - // Highlight active button - JButton activeButton = getButtonForSection(section); - if (activeButton != null) { - activeButton.setBackground(new Color(63, 81, 181)); - } - - // Show corresponding panel - cardLayout.show(contentPanel, section); - - // Refresh panel data if needed - refreshCurrentPanel(section); - - log.info("Switched to inventory section: " + section); - } - - private void resetNavButtonColors() { - Color defaultColor = new Color(37, 47, 63); - dashboardButton.setBackground(defaultColor); - inventoryViewButton.setBackground(defaultColor); - goodsReceiptButton.setBackground(defaultColor); - stockAdjustmentButton.setBackground(defaultColor); - reportsButton.setBackground(defaultColor); - productMasterButton.setBackground(defaultColor); - supplierMasterButton.setBackground(defaultColor); - } - - private JButton getButtonForSection(String section) { - switch (section) { - case "dashboard": return dashboardButton; - case "inventory": return inventoryViewButton; - case "receipt": return goodsReceiptButton; - case "adjustment": return stockAdjustmentButton; - case "reports": return reportsButton; - case "products": return productMasterButton; - case "suppliers": return supplierMasterButton; - default: return null; - } - } - - private void refreshCurrentPanel(String section) { - // TODO: Implement panel refresh logic - switch (section) { - case "dashboard": - dashboardPanel.refreshData(); - break; - case "inventory": - inventoryViewPanel.refreshData(); - break; - case "receipt": - goodsReceiptPanel.refreshData(); - break; - case "adjustment": - stockAdjustmentPanel.refreshData(); - break; - case "reports": - reportsPanel.refreshData(); - break; - case "products": - productMasterPanel.refreshData(); - break; - case "suppliers": - supplierMasterPanel.refreshData(); - break; - } - } - - // Getters for child panels - public MainFrame getMainFrame() { return mainFrame; } - public NumberFormat getCurrencyFormatter() { return currencyFormatter; } - public SimpleDateFormat getDateFormatter() { return dateFormatter; } - - // Navigation methods for child panels - public void navigateToInventoryView() { showSection("inventory"); } - public void navigateToGoodsReceipt() { showSection("receipt"); } - public void navigateToStockAdjustment() { showSection("adjustment"); } - public void navigateToReports() { showSection("reports"); } - public void navigateToProductMaster() { showSection("products"); } -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/InventoryPage/InventoryViewPanel.java b/src/main/java/BsK/client/ui/component/InventoryPage/InventoryViewPanel.java deleted file mode 100644 index 12f47ca..0000000 --- a/src/main/java/BsK/client/ui/component/InventoryPage/InventoryViewPanel.java +++ /dev/null @@ -1,437 +0,0 @@ -package BsK.client.ui.component.InventoryPage; - -import BsK.client.ui.component.common.RoundedPanel; -import BsK.client.ui.component.InventoryPage.common.InventoryButtonFactory; -import BsK.client.ui.component.InventoryPage.common.InventoryColorScheme; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableColumn; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.io.File; -import java.text.SimpleDateFormat; -import java.util.Date; - -@Slf4j -public class InventoryViewPanel extends JPanel { - private InventoryPage parentPage; - - // Left panel components - private JTextField searchField; - private JComboBox categoryFilter; - private JTable medicineTable; - private DefaultTableModel medicineTableModel; - - // Right panel components - private JLabel selectedMedicineLabel; - private JTable batchTable; - private DefaultTableModel batchTableModel; - private JButton adjustStockButton; - private JButton exportButton; - - // Current selection - private String selectedMedicineName = ""; - private int selectedBatchRow = -1; - - // Date formatter - private SimpleDateFormat dateFormatter; - - public InventoryViewPanel(InventoryPage parentPage) { - this.parentPage = parentPage; - this.dateFormatter = new SimpleDateFormat("dd/MM/yyyy"); - - setLayout(new BorderLayout()); - setBackground(new Color(245, 245, 245)); - - initializeComponents(); - layoutComponents(); - setupEventListeners(); - } - - private void initializeComponents() { - // Search and filter components - searchField = new JTextField(); - searchField.setFont(new Font("Arial", Font.PLAIN, 14)); - searchField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(8, 12, 8, 12) - )); - - categoryFilter = new JComboBox<>(new String[]{ - "Tất cả danh mục", "Thuốc kê đơn", "Thuốc không kê đơn", - "Vitamin & Thực phẩm chức năng", "Dụng cụ y tế" - }); - categoryFilter.setFont(new Font("Arial", Font.PLAIN, 14)); - - exportButton = InventoryButtonFactory.createSpecialButton("📤 Xuất Excel"); - - // Medicine table (left panel) - String[] medicineColumns = {"Tên Thuốc", "ĐVT", "Tổng Tồn", "Trạng Thái"}; - medicineTableModel = new DefaultTableModel(medicineColumns, 0) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - }; - medicineTable = new JTable(medicineTableModel); - setupMedicineTable(); - - // Batch table (right panel) - String[] batchColumns = {"Lô", "Hạn Sử Dụng", "Số Lượng", "Giá Nhập", "Ngày Nhập"}; - batchTableModel = new DefaultTableModel(batchColumns, 0) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - }; - batchTable = new JTable(batchTableModel); - setupBatchTable(); - - // Selected medicine label - selectedMedicineLabel = new JLabel("Chọn một loại thuốc để xem chi tiết lô"); - selectedMedicineLabel.setFont(new Font("Arial", Font.BOLD, 16)); - selectedMedicineLabel.setForeground(new Color(100, 100, 100)); - - // Adjust stock button - adjustStockButton = InventoryButtonFactory.createSecondaryButton("⚙️ Kiểm Kê"); - adjustStockButton.setEnabled(false); - } - - private void setupMedicineTable() { - medicineTable.setFont(new Font("Arial", Font.PLAIN, 14)); - medicineTable.setRowHeight(35); - medicineTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - medicineTable.setBackground(Color.WHITE); - medicineTable.setGridColor(new Color(230, 230, 230)); - medicineTable.setShowVerticalLines(true); - medicineTable.setShowHorizontalLines(true); - medicineTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); - medicineTable.getTableHeader().setBackground(InventoryColorScheme.TABLE_HEADER_BG); - medicineTable.getTableHeader().setForeground(InventoryColorScheme.TABLE_HEADER_TEXT); - medicineTable.getTableHeader().setReorderingAllowed(false); - - // Set column widths - TableColumn col0 = medicineTable.getColumnModel().getColumn(0); // Tên Thuốc - col0.setPreferredWidth(250); - TableColumn col1 = medicineTable.getColumnModel().getColumn(1); // ĐVT - col1.setPreferredWidth(80); - TableColumn col2 = medicineTable.getColumnModel().getColumn(2); // Tổng Tồn - col2.setPreferredWidth(100); - TableColumn col3 = medicineTable.getColumnModel().getColumn(3); // Trạng Thái - col3.setPreferredWidth(120); - - // Custom renderer for status column - medicineTable.getColumnModel().getColumn(3).setCellRenderer(new DefaultTableCellRenderer() { - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, - boolean hasFocus, int row, int column) { - Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - if (!isSelected) { - String status = (String) value; - c.setBackground(InventoryColorScheme.getStatusBackgroundColor(status)); - setForeground(InventoryColorScheme.getStatusColor(status)); - } - setHorizontalAlignment(SwingConstants.CENTER); - return c; - } - }); - - // Center align numeric columns - DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer(); - centerRenderer.setHorizontalAlignment(SwingConstants.CENTER); - medicineTable.getColumnModel().getColumn(1).setCellRenderer(centerRenderer); - medicineTable.getColumnModel().getColumn(2).setCellRenderer(centerRenderer); - } - - private void setupBatchTable() { - batchTable.setFont(new Font("Arial", Font.PLAIN, 14)); - batchTable.setRowHeight(35); - batchTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - batchTable.setBackground(Color.WHITE); - batchTable.setGridColor(new Color(230, 230, 230)); - batchTable.setShowVerticalLines(true); - batchTable.setShowHorizontalLines(true); - batchTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); - batchTable.getTableHeader().setBackground(InventoryColorScheme.TABLE_HEADER_BG); - batchTable.getTableHeader().setForeground(InventoryColorScheme.TABLE_HEADER_TEXT); - batchTable.getTableHeader().setReorderingAllowed(false); - - // Set column widths - TableColumn col0 = batchTable.getColumnModel().getColumn(0); // Lô - col0.setPreferredWidth(120); - TableColumn col1 = batchTable.getColumnModel().getColumn(1); // HSD - col1.setPreferredWidth(100); - TableColumn col2 = batchTable.getColumnModel().getColumn(2); // Số Lượng - col2.setPreferredWidth(100); - TableColumn col3 = batchTable.getColumnModel().getColumn(3); // Giá Nhập - col3.setPreferredWidth(120); - TableColumn col4 = batchTable.getColumnModel().getColumn(4); // Ngày Nhập - col4.setPreferredWidth(100); - - // Center align all columns - DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer(); - centerRenderer.setHorizontalAlignment(SwingConstants.CENTER); - for (int i = 0; i < batchTable.getColumnCount(); i++) { - batchTable.getColumnModel().getColumn(i).setCellRenderer(centerRenderer); - } - - // Right align price column - DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer(); - rightRenderer.setHorizontalAlignment(SwingConstants.RIGHT); - batchTable.getColumnModel().getColumn(3).setCellRenderer(rightRenderer); - } - - private void layoutComponents() { - // Top panel with search and filters - JPanel topPanel = createTopPanel(); - add(topPanel, BorderLayout.NORTH); - - // Main content: split pane with medicine list and batch details - JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); - splitPane.setDividerLocation(550); - splitPane.setResizeWeight(0.55); - splitPane.setBorder(null); - - // Left panel: Medicine list - JPanel leftPanel = createMedicineListPanel(); - splitPane.setLeftComponent(leftPanel); - - // Right panel: Batch details - JPanel rightPanel = createBatchDetailsPanel(); - splitPane.setRightComponent(rightPanel); - - add(splitPane, BorderLayout.CENTER); - } - - private JPanel createTopPanel() { - RoundedPanel topPanel = new RoundedPanel(10, Color.WHITE, true); - topPanel.setLayout(new BorderLayout(15, 0)); - topPanel.setBorder(BorderFactory.createEmptyBorder(20, 25, 20, 25)); - - // Left side: Search and filter - JPanel leftControls = new JPanel(new FlowLayout(FlowLayout.LEFT, 15, 0)); - leftControls.setOpaque(false); - - // Search field with placeholder effect - JPanel searchPanel = new JPanel(new BorderLayout()); - searchPanel.setOpaque(false); - searchPanel.setPreferredSize(new Dimension(300, 35)); - JLabel searchIcon = new JLabel("🔍"); - searchIcon.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); - searchPanel.add(searchIcon, BorderLayout.WEST); - searchPanel.add(searchField, BorderLayout.CENTER); - - categoryFilter.setPreferredSize(new Dimension(200, 35)); - - leftControls.add(new JLabel("Tìm kiếm:")); - leftControls.add(searchPanel); - leftControls.add(new JLabel("Danh mục:")); - leftControls.add(categoryFilter); - - // Right side: Grouped action buttons - JButton printReportButton = InventoryButtonFactory.createSecondaryButton("📊 In Báo Cáo"); - JPanel rightControls = InventoryButtonFactory.createButtonGroup(FlowLayout.RIGHT, exportButton, printReportButton); - - topPanel.add(leftControls, BorderLayout.WEST); - topPanel.add(rightControls, BorderLayout.EAST); - - return topPanel; - } - - private JPanel createMedicineListPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout()); - panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - - // Title - JLabel titleLabel = new JLabel("DANH SÁCH THUỐC TRONG KHO"); - titleLabel.setFont(new Font("Arial", Font.BOLD, 16)); - titleLabel.setForeground(new Color(37, 47, 63)); - titleLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0)); - panel.add(titleLabel, BorderLayout.NORTH); - - // Table with scroll - JScrollPane scrollPane = new JScrollPane(medicineTable); - scrollPane.setBorder(BorderFactory.createLineBorder(new Color(200, 200, 200))); - scrollPane.getViewport().setBackground(Color.WHITE); - panel.add(scrollPane, BorderLayout.CENTER); - - return panel; - } - - private JPanel createBatchDetailsPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout()); - panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - - // Header with medicine name and actions - JPanel headerPanel = new JPanel(new BorderLayout()); - headerPanel.setOpaque(false); - headerPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 15, 0)); - - selectedMedicineLabel.setText("CHI TIẾT LÔ THUỐC"); - headerPanel.add(selectedMedicineLabel, BorderLayout.WEST); - headerPanel.add(adjustStockButton, BorderLayout.EAST); - - panel.add(headerPanel, BorderLayout.NORTH); - - // Table with scroll - JScrollPane scrollPane = new JScrollPane(batchTable); - scrollPane.setBorder(BorderFactory.createLineBorder(new Color(200, 200, 200))); - scrollPane.getViewport().setBackground(Color.WHITE); - panel.add(scrollPane, BorderLayout.CENTER); - - return panel; - } - - private void setupEventListeners() { - // Search field - searchField.addKeyListener(new KeyAdapter() { - @Override - public void keyReleased(KeyEvent e) { - filterMedicineList(); - } - }); - - // Category filter - categoryFilter.addActionListener(e -> filterMedicineList()); - - // Medicine table selection - medicineTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - if (!e.getValueIsAdjusting()) { - onMedicineSelected(); - } - } - }); - - // Batch table selection - batchTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - if (!e.getValueIsAdjusting()) { - onBatchSelected(); - } - } - }); - - // Export button - exportButton.addActionListener(e -> exportToExcel()); - - // Adjust stock button - adjustStockButton.addActionListener(e -> openStockAdjustment()); - } - - private void filterMedicineList() { - // TODO: Implement filtering logic - log.info("Filtering medicine list with search: '" + searchField.getText() + - "' and category: '" + categoryFilter.getSelectedItem() + "'"); - } - - private void onMedicineSelected() { - int selectedRow = medicineTable.getSelectedRow(); - if (selectedRow >= 0) { - selectedMedicineName = (String) medicineTableModel.getValueAt(selectedRow, 0); - selectedMedicineLabel.setText("CHI TIẾT LÔ: " + selectedMedicineName); - loadBatchDetails(selectedMedicineName); - adjustStockButton.setEnabled(false); // Will be enabled when batch is selected - } - } - - private void onBatchSelected() { - selectedBatchRow = batchTable.getSelectedRow(); - adjustStockButton.setEnabled(selectedBatchRow >= 0); - } - - private void loadBatchDetails(String medicineName) { - // Clear existing data - batchTableModel.setRowCount(0); - - // TODO: Load actual batch data from database - loadMockBatchData(medicineName); - } - - private void loadMockBatchData(String medicineName) { - // Mock batch data - Object[][] batchData = { - {medicineName.substring(0, 2).toUpperCase() + "202408", "15/08/2025", "45", "52.000", "10/01/2024"}, - {medicineName.substring(0, 2).toUpperCase() + "202411", "30/11/2025", "18", "55.000", "15/03/2024"}, - {medicineName.substring(0, 2).toUpperCase() + "202501", "15/02/2026", "30", "58.000", "20/05/2024"} - }; - - for (Object[] row : batchData) { - batchTableModel.addRow(row); - } - - log.info("Loaded batch details for: " + medicineName); - } - - private void exportToExcel() { - // TODO: Implement Excel export functionality - JFileChooser fileChooser = new JFileChooser(); - fileChooser.setDialogTitle("Xuất danh sách tồn kho"); - fileChooser.setSelectedFile(new File("TonKho_" + dateFormatter.format(new Date()) + ".xlsx")); - - int result = fileChooser.showSaveDialog(this); - if (result == JFileChooser.APPROVE_OPTION) { - File selectedFile = fileChooser.getSelectedFile(); - // TODO: Export logic here - JOptionPane.showMessageDialog(this, - "Xuất file thành công!\nĐường dẫn: " + selectedFile.getAbsolutePath(), - "Thông báo", JOptionPane.INFORMATION_MESSAGE); - } - } - - private void openStockAdjustment() { - if (selectedBatchRow >= 0) { - String batchNumber = (String) batchTableModel.getValueAt(selectedBatchRow, 0); - String currentQuantity = (String) batchTableModel.getValueAt(selectedBatchRow, 2); - - // TODO: Open stock adjustment dialog - log.info("Opening stock adjustment for batch: " + batchNumber + " (current: " + currentQuantity + ")"); - - parentPage.navigateToStockAdjustment(); - } - } - - public void refreshData() { - // TODO: Load actual data from database - loadMockMedicineData(); - } - - private void loadMockMedicineData() { - // Clear existing data - medicineTableModel.setRowCount(0); - - // Mock medicine data with enhanced status showing quantity ratios - Object[][] medicineData = { - {"Paracetamol 500mg", "Hộp", "63", "63/20 - Bình thường"}, - {"Amoxicillin 250mg", "Hộp", "112", "112/30 - Bình thường"}, - {"Vitamin C 1000mg", "Chai", "8", "8/20 - Sắp hết"}, - {"Ibuprofen 400mg", "Hộp", "45", "45/25 - Bình thường"}, - {"Aspirin 100mg", "Hộp", "5", "5/15 - Sắp hết"}, - {"Omeprazole 20mg", "Hộp", "78", "78/30 - Bình thường"}, - {"Metformin 500mg", "Hộp", "92", "92/40 - Bình thường"}, - {"Atorvastatin 20mg", "Hộp", "34", "34/25 - Bình thường"}, - {"Amlodipine 5mg", "Hộp", "67", "67/30 - Bình thường"}, - {"Losartan 50mg", "Hộp", "12", "12/25 - Sắp hết"} - }; - - for (Object[] row : medicineData) { - medicineTableModel.addRow(row); - } - - log.info("Medicine inventory data refreshed - " + medicineData.length + " items loaded"); - } -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/InventoryPage/ProductMasterPanel.java b/src/main/java/BsK/client/ui/component/InventoryPage/ProductMasterPanel.java deleted file mode 100644 index dbe9596..0000000 --- a/src/main/java/BsK/client/ui/component/InventoryPage/ProductMasterPanel.java +++ /dev/null @@ -1,526 +0,0 @@ -package BsK.client.ui.component.InventoryPage; - -import BsK.client.ui.component.common.RoundedPanel; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -@Slf4j -public class ProductMasterPanel extends JPanel { - private InventoryPage parentPage; - - // Product list - private JTextField searchField; - private JTable productTable; - private DefaultTableModel productTableModel; - - // Product form - private JTextField nameField; - private JTextField activeIngredientField; - private JComboBox categoryCombo; - private JTextField manufacturerField; - private JTextField descriptionField; - - // Units and pricing - private JTextField unit1Field, unit2Field, unit3Field; - private JTextField conversion1Field, conversion2Field; - private JTextField price1Field, price2Field, price3Field; - private JSpinner minStockSpinner; - - // Actions - private JButton addButton; - private JButton editButton; - private JButton saveButton; - private JButton cancelButton; - private JButton deleteButton; - - private boolean isEditing = false; - private int editingRow = -1; - - public ProductMasterPanel(InventoryPage parentPage) { - this.parentPage = parentPage; - - setLayout(new BorderLayout()); - setBackground(new Color(245, 245, 245)); - - initializeComponents(); - layoutComponents(); - setupEventListeners(); - } - - private void initializeComponents() { - // Search field - searchField = new JTextField(); - searchField.setFont(new Font("Arial", Font.PLAIN, 14)); - searchField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(8, 12, 8, 12) - )); - - // Product table - String[] columns = {"Tên Thuốc", "Hoạt Chất", "Danh Mục", "ĐVT Chính", "Giá Bán", "Tồn Tối Thiểu"}; - productTableModel = new DefaultTableModel(columns, 0) { - @Override - public boolean isCellEditable(int row, int column) { return false; } - }; - productTable = new JTable(productTableModel); - setupProductTable(); - - // Form fields - nameField = createFormField(); - activeIngredientField = createFormField(); - manufacturerField = createFormField(); - descriptionField = createFormField(); - - categoryCombo = new JComboBox<>(new String[]{ - "Thuốc kê đơn", "Thuốc không kê đơn", "Vitamin & TPCN", - "Thuốc tiêm", "Dụng cụ y tế", "Khác" - }); - categoryCombo.setFont(new Font("Arial", Font.PLAIN, 14)); - - // Units and conversions - unit1Field = createFormField(); // Main unit - unit2Field = createFormField(); // Secondary unit - unit3Field = createFormField(); // Tertiary unit - - conversion1Field = createFormField(); // 1 unit1 = ? unit2 - conversion2Field = createFormField(); // 1 unit2 = ? unit3 - - // Pricing - price1Field = createFormField(); - price2Field = createFormField(); - price3Field = createFormField(); - - minStockSpinner = new JSpinner(new SpinnerNumberModel(10, 0, 9999, 1)); - minStockSpinner.setFont(new Font("Arial", Font.PLAIN, 14)); - - // Action buttons - addButton = createActionButton("➕ Thêm Mới", new Color(76, 175, 80)); - editButton = createActionButton("✏️ Sửa", new Color(255, 152, 0)); - saveButton = createActionButton("💾 Lưu", new Color(33, 150, 243)); - cancelButton = createActionButton("❌ Hủy", new Color(158, 158, 158)); - deleteButton = createActionButton("🗑️ Xóa", new Color(244, 67, 54)); - - saveButton.setEnabled(false); - cancelButton.setEnabled(false); - editButton.setEnabled(false); - deleteButton.setEnabled(false); - } - - private JTextField createFormField() { - JTextField field = new JTextField(); - field.setFont(new Font("Arial", Font.PLAIN, 14)); - field.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(5, 10, 5, 10) - )); - field.setEnabled(false); - return field; - } - - private JButton createActionButton(String text, Color color) { - JButton button = new JButton(text); - button.setFont(new Font("Arial", Font.BOLD, 14)); - button.setBackground(color); - button.setForeground(Color.WHITE); - button.setFocusPainted(false); - button.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 15)); - button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - return button; - } - - private void setupProductTable() { - productTable.setFont(new Font("Arial", Font.PLAIN, 14)); - productTable.setRowHeight(35); - productTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - productTable.setBackground(Color.WHITE); - productTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); - productTable.getTableHeader().setBackground(new Color(37, 47, 63)); - productTable.getTableHeader().setForeground(Color.WHITE); - - // Right align price column - DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer(); - rightRenderer.setHorizontalAlignment(SwingConstants.RIGHT); - productTable.getColumnModel().getColumn(4).setCellRenderer(rightRenderer); - - // Center align numeric columns - DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer(); - centerRenderer.setHorizontalAlignment(SwingConstants.CENTER); - productTable.getColumnModel().getColumn(3).setCellRenderer(centerRenderer); - productTable.getColumnModel().getColumn(5).setCellRenderer(centerRenderer); - } - - private void layoutComponents() { - JPanel mainPanel = new JPanel(new BorderLayout(0, 20)); - mainPanel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - mainPanel.setOpaque(false); - - // Title - JLabel titleLabel = new JLabel("QUẢN LÝ DANH MỤC THUỐC", SwingConstants.CENTER); - titleLabel.setFont(new Font("Arial", Font.BOLD, 28)); - titleLabel.setForeground(new Color(37, 47, 63)); - mainPanel.add(titleLabel, BorderLayout.NORTH); - - // Split pane: list + form - JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); - splitPane.setDividerLocation(600); - splitPane.setResizeWeight(0.6); - - // Left: Product list - RoundedPanel listPanel = createProductListPanel(); - splitPane.setLeftComponent(listPanel); - - // Right: Product form - RoundedPanel formPanel = createProductFormPanel(); - splitPane.setRightComponent(formPanel); - - mainPanel.add(splitPane, BorderLayout.CENTER); - add(mainPanel, BorderLayout.CENTER); - } - - private RoundedPanel createProductListPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout(0, 15)); - panel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - - // Header with search - JPanel headerPanel = new JPanel(new BorderLayout(10, 0)); - headerPanel.setOpaque(false); - - JLabel headerLabel = new JLabel("DANH SÁCH THUỐC"); - headerLabel.setFont(new Font("Arial", Font.BOLD, 16)); - headerLabel.setForeground(new Color(37, 47, 63)); - - JPanel searchPanel = new JPanel(new BorderLayout(5, 0)); - searchPanel.setOpaque(false); - searchPanel.add(new JLabel("🔍"), BorderLayout.WEST); - searchPanel.add(searchField, BorderLayout.CENTER); - searchPanel.setPreferredSize(new Dimension(250, 35)); - - headerPanel.add(headerLabel, BorderLayout.WEST); - headerPanel.add(searchPanel, BorderLayout.EAST); - panel.add(headerPanel, BorderLayout.NORTH); - - // Table - JScrollPane scrollPane = new JScrollPane(productTable); - scrollPane.setBorder(BorderFactory.createLineBorder(new Color(200, 200, 200))); - panel.add(scrollPane, BorderLayout.CENTER); - - return panel; - } - - private RoundedPanel createProductFormPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout(0, 15)); - panel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - - // Header - JLabel headerLabel = new JLabel("THÔNG TIN THUỐC"); - headerLabel.setFont(new Font("Arial", Font.BOLD, 16)); - headerLabel.setForeground(new Color(37, 47, 63)); - panel.add(headerLabel, BorderLayout.NORTH); - - // Form fields - JPanel formPanel = new JPanel(new GridBagLayout()); - formPanel.setOpaque(false); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(8, 5, 8, 10); - gbc.anchor = GridBagConstraints.WEST; - - // Basic info - addFormRow(formPanel, gbc, 0, "Tên thuốc:", nameField); - addFormRow(formPanel, gbc, 1, "Hoạt chất:", activeIngredientField); - addFormRow(formPanel, gbc, 2, "Danh mục:", categoryCombo); - addFormRow(formPanel, gbc, 3, "Nhà sản xuất:", manufacturerField); - addFormRow(formPanel, gbc, 4, "Mô tả:", descriptionField); - - // Separator - gbc.gridx = 0; gbc.gridy = 5; gbc.gridwidth = 2; - gbc.fill = GridBagConstraints.HORIZONTAL; - JSeparator separator1 = new JSeparator(); - separator1.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); - formPanel.add(separator1, gbc); - - // Units section - gbc.gridwidth = 1; gbc.fill = GridBagConstraints.NONE; - JLabel unitsLabel = new JLabel("ĐƠNN VỊ & QUY ĐỔI"); - unitsLabel.setFont(new Font("Arial", Font.BOLD, 14)); - unitsLabel.setForeground(new Color(37, 47, 63)); - gbc.gridx = 0; gbc.gridy = 6; gbc.gridwidth = 2; - formPanel.add(unitsLabel, gbc); - - gbc.gridwidth = 1; - addFormRow(formPanel, gbc, 7, "ĐVT chính:", unit1Field); - addFormRow(formPanel, gbc, 8, "ĐVT phụ:", unit2Field); - addFormRow(formPanel, gbc, 9, "ĐVT nhỏ:", unit3Field); - addFormRow(formPanel, gbc, 10, "1 chính = ? phụ:", conversion1Field); - addFormRow(formPanel, gbc, 11, "1 phụ = ? nhỏ:", conversion2Field); - - // Separator - gbc.gridx = 0; gbc.gridy = 12; gbc.gridwidth = 2; - gbc.fill = GridBagConstraints.HORIZONTAL; - JSeparator separator2 = new JSeparator(); - separator2.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0)); - formPanel.add(separator2, gbc); - - // Pricing section - gbc.gridwidth = 1; gbc.fill = GridBagConstraints.NONE; - JLabel pricingLabel = new JLabel("GIÁ BÁN & TỒN KHO"); - pricingLabel.setFont(new Font("Arial", Font.BOLD, 14)); - pricingLabel.setForeground(new Color(37, 47, 63)); - gbc.gridx = 0; gbc.gridy = 13; gbc.gridwidth = 2; - formPanel.add(pricingLabel, gbc); - - gbc.gridwidth = 1; - addFormRow(formPanel, gbc, 14, "Giá bán chính:", price1Field); - addFormRow(formPanel, gbc, 15, "Giá bán phụ:", price2Field); - addFormRow(formPanel, gbc, 16, "Giá bán nhỏ:", price3Field); - addFormRow(formPanel, gbc, 17, "Tồn tối thiểu:", minStockSpinner); - - panel.add(formPanel, BorderLayout.CENTER); - - // Action buttons - JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0)); - buttonPanel.setOpaque(false); - buttonPanel.add(addButton); - buttonPanel.add(editButton); - buttonPanel.add(saveButton); - buttonPanel.add(cancelButton); - buttonPanel.add(deleteButton); - panel.add(buttonPanel, BorderLayout.SOUTH); - - return panel; - } - - private void addFormRow(JPanel parent, GridBagConstraints gbc, int row, String label, JComponent component) { - gbc.gridx = 0; gbc.gridy = row; - parent.add(new JLabel(label), gbc); - gbc.gridx = 1; - component.setPreferredSize(new Dimension(200, 30)); - parent.add(component, gbc); - } - - private void setupEventListeners() { - // Table selection - productTable.getSelectionModel().addListSelectionListener(e -> { - if (!e.getValueIsAdjusting()) { - onProductSelected(); - } - }); - - // Action buttons - addButton.addActionListener(e -> startAdd()); - editButton.addActionListener(e -> startEdit()); - saveButton.addActionListener(e -> saveProduct()); - cancelButton.addActionListener(e -> cancelOperation()); - deleteButton.addActionListener(e -> deleteProduct()); - } - - private void onProductSelected() { - int row = productTable.getSelectedRow(); - boolean hasSelection = row >= 0; - - editButton.setEnabled(hasSelection && !isEditing); - deleteButton.setEnabled(hasSelection && !isEditing); - - if (hasSelection) { - loadProductToForm(row); - } - } - - private void loadProductToForm(int row) { - // TODO: Load actual product data - nameField.setText((String) productTableModel.getValueAt(row, 0)); - activeIngredientField.setText("Paracetamol"); // Mock data - categoryCombo.setSelectedItem(productTableModel.getValueAt(row, 2)); - manufacturerField.setText("Công ty ABC"); - descriptionField.setText("Thuốc giảm đau, hạ sốt"); - - unit1Field.setText("Hộp"); - unit2Field.setText("Vỉ"); - unit3Field.setText("Viên"); - conversion1Field.setText("10"); - conversion2Field.setText("10"); - - price1Field.setText("100000"); - price2Field.setText("10000"); - price3Field.setText("1000"); - minStockSpinner.setValue(20); - } - - private void startAdd() { - isEditing = true; - editingRow = -1; - enableFormFields(true); - clearForm(); - - addButton.setEnabled(false); - editButton.setEnabled(false); - deleteButton.setEnabled(false); - saveButton.setEnabled(true); - cancelButton.setEnabled(true); - } - - private void startEdit() { - int row = productTable.getSelectedRow(); - if (row < 0) return; - - isEditing = true; - editingRow = row; - enableFormFields(true); - - addButton.setEnabled(false); - editButton.setEnabled(false); - deleteButton.setEnabled(false); - saveButton.setEnabled(true); - cancelButton.setEnabled(true); - } - - private void saveProduct() { - if (!validateForm()) return; - - Object[] rowData = { - nameField.getText(), - activeIngredientField.getText(), - categoryCombo.getSelectedItem(), - unit1Field.getText(), - parentPage.getCurrencyFormatter().format(Double.parseDouble(price1Field.getText())), - minStockSpinner.getValue().toString() - }; - - if (editingRow >= 0) { - // Update existing - for (int i = 0; i < rowData.length; i++) { - productTableModel.setValueAt(rowData[i], editingRow, i); - } - JOptionPane.showMessageDialog(this, "Cập nhật thuốc thành công!", - "Thành công", JOptionPane.INFORMATION_MESSAGE); - } else { - // Add new - productTableModel.addRow(rowData); - JOptionPane.showMessageDialog(this, "Thêm thuốc mới thành công!", - "Thành công", JOptionPane.INFORMATION_MESSAGE); - } - - // TODO: Save to database - cancelOperation(); - } - - private void cancelOperation() { - isEditing = false; - editingRow = -1; - enableFormFields(false); - - addButton.setEnabled(true); - saveButton.setEnabled(false); - cancelButton.setEnabled(false); - onProductSelected(); // Refresh button states - } - - private void deleteProduct() { - int row = productTable.getSelectedRow(); - if (row < 0) return; - - String productName = (String) productTableModel.getValueAt(row, 0); - int confirm = JOptionPane.showConfirmDialog(this, - "Bạn có chắc chắn muốn xóa thuốc '" + productName + "'?", - "Xác nhận xóa", JOptionPane.YES_NO_OPTION); - - if (confirm == JOptionPane.YES_OPTION) { - productTableModel.removeRow(row); - clearForm(); - JOptionPane.showMessageDialog(this, "Xóa thuốc thành công!", - "Thành công", JOptionPane.INFORMATION_MESSAGE); - // TODO: Delete from database - } - } - - private void enableFormFields(boolean enabled) { - nameField.setEnabled(enabled); - activeIngredientField.setEnabled(enabled); - categoryCombo.setEnabled(enabled); - manufacturerField.setEnabled(enabled); - descriptionField.setEnabled(enabled); - unit1Field.setEnabled(enabled); - unit2Field.setEnabled(enabled); - unit3Field.setEnabled(enabled); - conversion1Field.setEnabled(enabled); - conversion2Field.setEnabled(enabled); - price1Field.setEnabled(enabled); - price2Field.setEnabled(enabled); - price3Field.setEnabled(enabled); - minStockSpinner.setEnabled(enabled); - } - - private void clearForm() { - nameField.setText(""); - activeIngredientField.setText(""); - categoryCombo.setSelectedIndex(0); - manufacturerField.setText(""); - descriptionField.setText(""); - unit1Field.setText(""); - unit2Field.setText(""); - unit3Field.setText(""); - conversion1Field.setText(""); - conversion2Field.setText(""); - price1Field.setText(""); - price2Field.setText(""); - price3Field.setText(""); - minStockSpinner.setValue(10); - } - - private boolean validateForm() { - if (nameField.getText().trim().isEmpty()) { - JOptionPane.showMessageDialog(this, "Vui lòng nhập tên thuốc!", - "Thông báo", JOptionPane.WARNING_MESSAGE); - nameField.requestFocus(); - return false; - } - - if (unit1Field.getText().trim().isEmpty()) { - JOptionPane.showMessageDialog(this, "Vui lòng nhập đơn vị chính!", - "Thông báo", JOptionPane.WARNING_MESSAGE); - unit1Field.requestFocus(); - return false; - } - - try { - Double.parseDouble(price1Field.getText()); - } catch (NumberFormatException e) { - JOptionPane.showMessageDialog(this, "Vui lòng nhập giá bán hợp lệ!", - "Thông báo", JOptionPane.WARNING_MESSAGE); - price1Field.requestFocus(); - return false; - } - - return true; - } - - public void refreshData() { - // TODO: Load actual data from database - loadMockProductData(); - } - - private void loadMockProductData() { - productTableModel.setRowCount(0); - Object[][] mockData = { - {"Paracetamol 500mg", "Paracetamol", "Thuốc không kê đơn", "Hộp", "100.000", "20"}, - {"Amoxicillin 250mg", "Amoxicillin", "Thuốc kê đơn", "Hộp", "150.000", "15"}, - {"Vitamin C 1000mg", "Acid Ascorbic", "Vitamin & TPCN", "Chai", "85.000", "10"}, - {"Ibuprofen 400mg", "Ibuprofen", "Thuốc không kê đơn", "Hộp", "120.000", "25"}, - {"Omeprazole 20mg", "Omeprazole", "Thuốc kê đơn", "Hộp", "200.000", "30"} - }; - - for (Object[] row : mockData) { - productTableModel.addRow(row); - } - - log.info("Product master data loaded - " + mockData.length + " products"); - } -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/InventoryPage/ReportsPanel.java b/src/main/java/BsK/client/ui/component/InventoryPage/ReportsPanel.java deleted file mode 100644 index a689d9f..0000000 --- a/src/main/java/BsK/client/ui/component/InventoryPage/ReportsPanel.java +++ /dev/null @@ -1,473 +0,0 @@ -package BsK.client.ui.component.InventoryPage; - -import BsK.client.ui.component.common.RoundedPanel; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.text.SimpleDateFormat; -import java.util.Date; - -@Slf4j -public class ReportsPanel extends JPanel { - private InventoryPage parentPage; - - // Report type selection - private JComboBox reportTypeCombo; - private JButton generateButton; - private JButton exportButton; - - // Report results - private JTable reportTable; - private DefaultTableModel reportTableModel; - private JLabel reportTitleLabel; - private JLabel reportSummaryLabel; - - // Date filter (for some reports) - private JTextField fromDateField; - private JTextField toDateField; - - private SimpleDateFormat dateFormatter; - - public ReportsPanel(InventoryPage parentPage) { - this.parentPage = parentPage; - this.dateFormatter = new SimpleDateFormat("dd/MM/yyyy"); - - setLayout(new BorderLayout()); - setBackground(new Color(245, 245, 245)); - - initializeComponents(); - layoutComponents(); - setupEventListeners(); - } - - private void initializeComponents() { - // Report type selection - reportTypeCombo = new JComboBox<>(new String[]{ - "Chọn loại báo cáo...", - "Thuốc Sắp Hết (Low Stock)", - "Thuốc Sắp Hết Hạn", - "Lịch Sử Xuất Nhập Kho", - "Tồn Kho Theo Nhóm", - "Báo Cáo Tổng Hợp" - }); - reportTypeCombo.setFont(new Font("Arial", Font.PLAIN, 14)); - reportTypeCombo.setPreferredSize(new Dimension(250, 35)); - - generateButton = new JButton("📊 Tạo Báo Cáo"); - generateButton.setFont(new Font("Arial", Font.BOLD, 14)); - generateButton.setBackground(new Color(33, 150, 243)); - generateButton.setForeground(Color.WHITE); - generateButton.setFocusPainted(false); - generateButton.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); - generateButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - - exportButton = new JButton("📤 Xuất Excel"); - exportButton.setFont(new Font("Arial", Font.BOLD, 14)); - exportButton.setBackground(new Color(76, 175, 80)); - exportButton.setForeground(Color.WHITE); - exportButton.setFocusPainted(false); - exportButton.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); - exportButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - exportButton.setEnabled(false); - - // Date filters - fromDateField = new JTextField(dateFormatter.format(new Date())); - fromDateField.setFont(new Font("Arial", Font.PLAIN, 14)); - fromDateField.setPreferredSize(new Dimension(120, 35)); - fromDateField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(5, 10, 5, 10) - )); - - toDateField = new JTextField(dateFormatter.format(new Date())); - toDateField.setFont(new Font("Arial", Font.PLAIN, 14)); - toDateField.setPreferredSize(new Dimension(120, 35)); - toDateField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(5, 10, 5, 10) - )); - - // Report results - reportTableModel = new DefaultTableModel(); - reportTable = new JTable(reportTableModel); - setupReportTable(); - - reportTitleLabel = new JLabel("Chọn loại báo cáo để hiển thị kết quả"); - reportTitleLabel.setFont(new Font("Arial", Font.BOLD, 18)); - reportTitleLabel.setForeground(new Color(37, 47, 63)); - - reportSummaryLabel = new JLabel(""); - reportSummaryLabel.setFont(new Font("Arial", Font.PLAIN, 14)); - reportSummaryLabel.setForeground(new Color(100, 100, 100)); - } - - private void setupReportTable() { - reportTable.setFont(new Font("Arial", Font.PLAIN, 14)); - reportTable.setRowHeight(35); - reportTable.setBackground(Color.WHITE); - reportTable.setGridColor(new Color(230, 230, 230)); - reportTable.setShowVerticalLines(true); - reportTable.setShowHorizontalLines(true); - reportTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); - reportTable.getTableHeader().setBackground(new Color(37, 47, 63)); - reportTable.getTableHeader().setForeground(Color.WHITE); - reportTable.getTableHeader().setReorderingAllowed(false); - } - - private void layoutComponents() { - JPanel mainPanel = new JPanel(new BorderLayout(0, 20)); - mainPanel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - mainPanel.setOpaque(false); - - // Title - JLabel titleLabel = new JLabel("BÁO CÁO QUẢN LÝ KHO", SwingConstants.CENTER); - titleLabel.setFont(new Font("Arial", Font.BOLD, 28)); - titleLabel.setForeground(new Color(37, 47, 63)); - mainPanel.add(titleLabel, BorderLayout.NORTH); - - // Controls panel - RoundedPanel controlsPanel = createControlsPanel(); - mainPanel.add(controlsPanel, BorderLayout.CENTER); - - // Results panel - RoundedPanel resultsPanel = createResultsPanel(); - mainPanel.add(resultsPanel, BorderLayout.SOUTH); - - add(mainPanel, BorderLayout.CENTER); - } - - private RoundedPanel createControlsPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new GridBagLayout()); - panel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - panel.setPreferredSize(new Dimension(0, 200)); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(10, 10, 10, 10); - gbc.anchor = GridBagConstraints.WEST; - - // Report type selection - gbc.gridx = 0; gbc.gridy = 0; - panel.add(new JLabel("Loại báo cáo:"), gbc); - gbc.gridx = 1; gbc.gridwidth = 2; - panel.add(reportTypeCombo, gbc); - - // Date range (initially hidden) - gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; - JLabel fromLabel = new JLabel("Từ ngày:"); - panel.add(fromLabel, gbc); - gbc.gridx = 1; - panel.add(fromDateField, gbc); - - gbc.gridx = 2; - JLabel toLabel = new JLabel("Đến ngày:"); - panel.add(toLabel, gbc); - gbc.gridx = 3; - panel.add(toDateField, gbc); - - // Buttons - gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 2; - JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 15, 0)); - buttonPanel.setOpaque(false); - buttonPanel.add(generateButton); - buttonPanel.add(exportButton); - panel.add(buttonPanel, gbc); - - return panel; - } - - private RoundedPanel createResultsPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout(0, 15)); - panel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - panel.setPreferredSize(new Dimension(0, 400)); - - // Header - JPanel headerPanel = new JPanel(new BorderLayout()); - headerPanel.setOpaque(false); - headerPanel.add(reportTitleLabel, BorderLayout.WEST); - headerPanel.add(reportSummaryLabel, BorderLayout.EAST); - panel.add(headerPanel, BorderLayout.NORTH); - - // Table - JScrollPane scrollPane = new JScrollPane(reportTable); - scrollPane.setBorder(BorderFactory.createLineBorder(new Color(200, 200, 200))); - scrollPane.getViewport().setBackground(Color.WHITE); - panel.add(scrollPane, BorderLayout.CENTER); - - return panel; - } - - private void setupEventListeners() { - generateButton.addActionListener(e -> generateReport()); - exportButton.addActionListener(e -> exportReport()); - - reportTypeCombo.addActionListener(e -> { - String selectedType = (String) reportTypeCombo.getSelectedItem(); - boolean needsDateRange = "Lịch Sử Xuất Nhập Kho".equals(selectedType); - fromDateField.setVisible(needsDateRange); - toDateField.setVisible(needsDateRange); - }); - } - - private void generateReport() { - String reportType = (String) reportTypeCombo.getSelectedItem(); - if (reportType == null || reportType.startsWith("Chọn")) { - JOptionPane.showMessageDialog(this, "Vui lòng chọn loại báo cáo!", - "Thông báo", JOptionPane.WARNING_MESSAGE); - return; - } - - reportTitleLabel.setText("Đang tạo báo cáo..."); - reportSummaryLabel.setText(""); - reportTableModel.setRowCount(0); - reportTableModel.setColumnCount(0); - - // Generate report based on type - switch (reportType) { - case "Thuốc Sắp Hết (Low Stock)": - generateLowStockReport(); - break; - case "Thuốc Sắp Hết Hạn": - generateExpiryReport(); - break; - case "Lịch Sử Xuất Nhập Kho": - generateInventoryHistoryReport(); - break; - case "Tồn Kho Theo Nhóm": - generateStockByGroupReport(); - break; - case "Báo Cáo Tổng Hợp": - generateSummaryReport(); - break; - } - - exportButton.setEnabled(reportTableModel.getRowCount() > 0); - } - - private void generateLowStockReport() { - String[] columns = {"Tên Thuốc", "ĐVT", "Tồn Hiện Tại", "Mức Tối Thiểu", "Cần Nhập", "Nhà Cung Cấp"}; - reportTableModel.setColumnIdentifiers(columns); - - // Mock data - Object[][] data = { - {"Vitamin C 1000mg", "Chai", "8", "20", "12", "Công ty ABC"}, - {"Aspirin 100mg", "Hộp", "5", "15", "10", "Công ty DEF"}, - {"Cefuroxime 250mg", "Hộp", "3", "10", "7", "Công ty GHI"}, - {"Omeprazole 20mg", "Hộp", "12", "25", "13", "Công ty ABC"}, - {"Metformin 500mg", "Hộp", "18", "30", "12", "Công ty JKL"} - }; - - for (Object[] row : data) { - reportTableModel.addRow(row); - } - - reportTitleLabel.setText("Báo Cáo Thuốc Sắp Hết"); - reportSummaryLabel.setText(data.length + " sản phẩm cần nhập thêm"); - - // Color coding for urgent items - reportTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() { - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, - boolean hasFocus, int row, int column) { - Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - if (!isSelected && column == 2) { // Current stock column - try { - int current = Integer.parseInt(value.toString()); - if (current <= 5) { - c.setBackground(new Color(255, 235, 238)); - setForeground(new Color(244, 67, 54)); - } else if (current <= 10) { - c.setBackground(new Color(255, 248, 225)); - setForeground(new Color(255, 152, 0)); - } else { - c.setBackground(Color.WHITE); - setForeground(Color.BLACK); - } - } catch (NumberFormatException e) { - c.setBackground(Color.WHITE); - setForeground(Color.BLACK); - } - } else if (!isSelected) { - c.setBackground(Color.WHITE); - setForeground(Color.BLACK); - } - - setHorizontalAlignment(column >= 1 && column <= 4 ? SwingConstants.CENTER : SwingConstants.LEFT); - return c; - } - }); - - log.info("Generated low stock report with " + data.length + " items"); - } - - private void generateExpiryReport() { - String[] columns = {"Tên Thuốc", "Lô", "Hạn Sử Dụng", "Số Lượng", "Ngày Còn Lại", "Mức Độ"}; - reportTableModel.setColumnIdentifiers(columns); - - // Mock data - Object[][] data = { - {"Paracetamol 500mg", "PA202408", "15/08/2024", "25", "45", "Nguy hiểm"}, - {"Vitamin B1", "VB202409", "20/09/2024", "15", "78", "Cảnh báo"}, - {"Amoxicillin 250mg", "AM202410", "10/10/2024", "30", "98", "Cảnh báo"}, - {"Ibuprofen 400mg", "IB202411", "05/11/2024", "18", "124", "Bình thường"}, - {"Cefalexin 500mg", "CE202412", "25/12/2024", "22", "174", "Bình thường"} - }; - - for (Object[] row : data) { - reportTableModel.addRow(row); - } - - reportTitleLabel.setText("Báo Cáo Thuốc Sắp Hết Hạn"); - reportSummaryLabel.setText(data.length + " lô thuốc cần theo dõi"); - - // Color coding for expiry urgency - reportTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() { - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, - boolean hasFocus, int row, int column) { - Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - if (!isSelected && column == 5) { // Risk level column - String risk = value.toString(); - if ("Nguy hiểm".equals(risk)) { - c.setBackground(new Color(255, 235, 238)); - setForeground(new Color(244, 67, 54)); - } else if ("Cảnh báo".equals(risk)) { - c.setBackground(new Color(255, 248, 225)); - setForeground(new Color(255, 152, 0)); - } else { - c.setBackground(new Color(232, 245, 233)); - setForeground(new Color(76, 175, 80)); - } - } else if (!isSelected) { - c.setBackground(Color.WHITE); - setForeground(Color.BLACK); - } - - setHorizontalAlignment(column >= 3 && column <= 4 ? SwingConstants.CENTER : SwingConstants.LEFT); - return c; - } - }); - - log.info("Generated expiry report with " + data.length + " batches"); - } - - private void generateInventoryHistoryReport() { - String[] columns = {"Ngày", "Loại", "Thuốc", "Lô", "Số Lượng", "Đơn Giá", "Người Thực Hiện"}; - reportTableModel.setColumnIdentifiers(columns); - - // Mock data - Object[][] data = { - {"25/06/2024", "Nhập", "Paracetamol 500mg", "PA202408", "+100", "52.000", "Nguyễn Văn A"}, - {"25/06/2024", "Xuất", "Vitamin C 1000mg", "VC202405", "-5", "85.000", "Hệ thống"}, - {"24/06/2024", "Kiểm kê", "Amoxicillin 250mg", "AM202409", "+12", "-", "Trần Thị B"}, - {"24/06/2024", "Xuất", "Ibuprofen 400mg", "IB202407", "-8", "45.000", "Hệ thống"}, - {"23/06/2024", "Nhập", "Omeprazole 20mg", "OM202410", "+50", "38.000", "Lê Văn C"} - }; - - for (Object[] row : data) { - reportTableModel.addRow(row); - } - - reportTitleLabel.setText("Lịch Sử Xuất Nhập Kho"); - reportSummaryLabel.setText("Từ " + fromDateField.getText() + " đến " + toDateField.getText()); - - log.info("Generated inventory history report with " + data.length + " transactions"); - } - - private void generateStockByGroupReport() { - String[] columns = {"Nhóm Thuốc", "Số Loại", "Tổng Tồn", "Giá Trị (VNĐ)", "% Tổng Kho"}; - reportTableModel.setColumnIdentifiers(columns); - - // Mock data - Object[][] data = { - {"Thuốc kê đơn", "45", "2,847", "156,890,000", "34.5%"}, - {"Thuốc không kê đơn", "32", "1,923", "98,450,000", "21.6%"}, - {"Vitamin & TPCN", "28", "1,456", "76,230,000", "16.7%"}, - {"Thuốc tiêm", "15", "578", "89,670,000", "19.7%"}, - {"Dụng cụ y tế", "12", "234", "33,760,000", "7.4%"} - }; - - for (Object[] row : data) { - reportTableModel.addRow(row); - } - - reportTitleLabel.setText("Tồn Kho Theo Nhóm Thuốc"); - reportSummaryLabel.setText("Tổng: 132 loại thuốc, giá trị 455.000.000 VNĐ"); - - // Right align numeric columns - DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer(); - rightRenderer.setHorizontalAlignment(SwingConstants.RIGHT); - for (int i = 1; i < reportTable.getColumnCount(); i++) { - reportTable.getColumnModel().getColumn(i).setCellRenderer(rightRenderer); - } - - log.info("Generated stock by group report"); - } - - private void generateSummaryReport() { - String[] columns = {"Chỉ Số", "Giá Trị", "Đơn Vị", "Ghi Chú"}; - reportTableModel.setColumnIdentifiers(columns); - - // Mock summary data - Object[][] data = { - {"Tổng số loại thuốc", "156", "loại", "Đang quản lý"}, - {"Tổng giá trị kho", "455.000.000", "VNĐ", "Tại thời điểm hiện tại"}, - {"Thuốc sắp hết", "7", "loại", "Dưới mức tối thiểu"}, - {"Thuốc sắp hết hạn", "12", "lô", "Trong 3 tháng tới"}, - {"Giao dịch hôm nay", "23", "lần", "Xuất + Nhập"}, - {"Doanh thu thuốc", "12.450.000", "VNĐ", "Tháng này"}, - {"Chi phí nhập hàng", "8.900.000", "VNĐ", "Tháng này"}, - {"Tỷ lệ hết hạn", "0.8%", "%", "Tháng trước"} - }; - - for (Object[] row : data) { - reportTableModel.addRow(row); - } - - reportTitleLabel.setText("Báo Cáo Tổng Hợp Kho Thuốc"); - reportSummaryLabel.setText("Cập nhật lúc: " + new SimpleDateFormat("dd/MM/yyyy HH:mm").format(new Date())); - - log.info("Generated summary report"); - } - - private void exportReport() { - String reportType = (String) reportTypeCombo.getSelectedItem(); - - JFileChooser fileChooser = new JFileChooser(); - fileChooser.setDialogTitle("Xuất báo cáo Excel"); - String fileName = reportType.replaceAll("[^a-zA-Z0-9]", "_") + "_" + - dateFormatter.format(new Date()) + ".xlsx"; - fileChooser.setSelectedFile(new File(fileName)); - - int result = fileChooser.showSaveDialog(this); - if (result == JFileChooser.APPROVE_OPTION) { - File selectedFile = fileChooser.getSelectedFile(); - - // TODO: Implement actual Excel export - JOptionPane.showMessageDialog(this, - "Xuất báo cáo thành công!\nFile: " + selectedFile.getName() + - "\nSố dòng: " + reportTableModel.getRowCount(), - "Thông báo", JOptionPane.INFORMATION_MESSAGE); - - log.info("Exported report: " + selectedFile.getAbsolutePath()); - } - } - - public void refreshData() { - reportTitleLabel.setText("Chọn loại báo cáo để hiển thị kết quả"); - reportSummaryLabel.setText(""); - reportTableModel.setRowCount(0); - reportTableModel.setColumnCount(0); - exportButton.setEnabled(false); - log.info("Reports panel refreshed"); - } -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/InventoryPage/StockAdjustmentPanel.java b/src/main/java/BsK/client/ui/component/InventoryPage/StockAdjustmentPanel.java deleted file mode 100644 index 463da13..0000000 --- a/src/main/java/BsK/client/ui/component/InventoryPage/StockAdjustmentPanel.java +++ /dev/null @@ -1,498 +0,0 @@ -package BsK.client.ui.component.InventoryPage; - -import BsK.client.ui.component.common.RoundedPanel; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.text.SimpleDateFormat; -import java.util.Date; - -@Slf4j -public class StockAdjustmentPanel extends JPanel { - private InventoryPage parentPage; - - // Search and selection - private JTextField medicineSearchField; - private JTable medicineTable; - private DefaultTableModel medicineTableModel; - private JTable batchTable; - private DefaultTableModel batchTableModel; - - // Adjustment form - private JLabel selectedMedicineLabel; - private JLabel selectedBatchLabel; - private JLabel currentQuantityLabel; - private JSpinner actualQuantitySpinner; - private JTextArea reasonTextArea; - private JButton adjustButton; - private JButton cancelButton; - - // Adjustment history - private JTable historyTable; - private DefaultTableModel historyTableModel; - - private SimpleDateFormat dateFormatter; - - public StockAdjustmentPanel(InventoryPage parentPage) { - this.parentPage = parentPage; - this.dateFormatter = new SimpleDateFormat("dd/MM/yyyy HH:mm"); - - setLayout(new BorderLayout()); - setBackground(new Color(245, 245, 245)); - - initializeComponents(); - layoutComponents(); - setupEventListeners(); - } - - private void initializeComponents() { - // Medicine search - medicineSearchField = new JTextField(); - medicineSearchField.setFont(new Font("Arial", Font.PLAIN, 14)); - medicineSearchField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(8, 12, 8, 12) - )); - - // Medicine table - String[] medicineColumns = {"Tên Thuốc", "Tổng Tồn", "Trạng Thái"}; - medicineTableModel = new DefaultTableModel(medicineColumns, 0) { - @Override - public boolean isCellEditable(int row, int column) { return false; } - }; - medicineTable = new JTable(medicineTableModel); - setupMedicineTable(); - - // Batch table - String[] batchColumns = {"Lô", "HSD", "Tồn Hiện Tại"}; - batchTableModel = new DefaultTableModel(batchColumns, 0) { - @Override - public boolean isCellEditable(int row, int column) { return false; } - }; - batchTable = new JTable(batchTableModel); - setupBatchTable(); - - // Adjustment form components - selectedMedicineLabel = new JLabel("Chưa chọn thuốc"); - selectedMedicineLabel.setFont(new Font("Arial", Font.BOLD, 16)); - - selectedBatchLabel = new JLabel("Chưa chọn lô"); - selectedBatchLabel.setFont(new Font("Arial", Font.PLAIN, 14)); - selectedBatchLabel.setForeground(new Color(100, 100, 100)); - - currentQuantityLabel = new JLabel("0"); - currentQuantityLabel.setFont(new Font("Arial", Font.BOLD, 18)); - currentQuantityLabel.setForeground(new Color(63, 81, 181)); - - actualQuantitySpinner = new JSpinner(new SpinnerNumberModel(0, 0, 99999, 1)); - actualQuantitySpinner.setFont(new Font("Arial", Font.PLAIN, 16)); - ((JSpinner.DefaultEditor) actualQuantitySpinner.getEditor()).getTextField().setHorizontalAlignment(JTextField.CENTER); - - reasonTextArea = new JTextArea(3, 30); - reasonTextArea.setFont(new Font("Arial", Font.PLAIN, 14)); - reasonTextArea.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(8, 8, 8, 8) - )); - reasonTextArea.setLineWrap(true); - reasonTextArea.setWrapStyleWord(true); - - adjustButton = new JButton("⚙️ Thực Hiện Kiểm Kê"); - adjustButton.setFont(new Font("Arial", Font.BOLD, 16)); - adjustButton.setBackground(new Color(255, 152, 0)); - adjustButton.setForeground(Color.WHITE); - adjustButton.setFocusPainted(false); - adjustButton.setBorder(BorderFactory.createEmptyBorder(12, 20, 12, 20)); - adjustButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - adjustButton.setEnabled(false); - - cancelButton = new JButton("Hủy"); - cancelButton.setFont(new Font("Arial", Font.PLAIN, 14)); - cancelButton.setBackground(new Color(158, 158, 158)); - cancelButton.setForeground(Color.WHITE); - cancelButton.setFocusPainted(false); - cancelButton.setBorder(BorderFactory.createEmptyBorder(12, 20, 12, 20)); - cancelButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - - // History table - String[] historyColumns = {"Ngày", "Thuốc", "Lô", "Trước", "Sau", "Chênh Lệch", "Lý Do"}; - historyTableModel = new DefaultTableModel(historyColumns, 0) { - @Override - public boolean isCellEditable(int row, int column) { return false; } - }; - historyTable = new JTable(historyTableModel); - setupHistoryTable(); - } - - private void setupMedicineTable() { - medicineTable.setFont(new Font("Arial", Font.PLAIN, 14)); - medicineTable.setRowHeight(35); - medicineTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - medicineTable.setBackground(Color.WHITE); - medicineTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); - medicineTable.getTableHeader().setBackground(new Color(37, 47, 63)); - medicineTable.getTableHeader().setForeground(Color.WHITE); - } - - private void setupBatchTable() { - batchTable.setFont(new Font("Arial", Font.PLAIN, 14)); - batchTable.setRowHeight(35); - batchTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - batchTable.setBackground(Color.WHITE); - batchTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); - batchTable.getTableHeader().setBackground(new Color(37, 47, 63)); - batchTable.getTableHeader().setForeground(Color.WHITE); - } - - private void setupHistoryTable() { - historyTable.setFont(new Font("Arial", Font.PLAIN, 12)); - historyTable.setRowHeight(30); - historyTable.setBackground(Color.WHITE); - historyTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 12)); - historyTable.getTableHeader().setBackground(new Color(37, 47, 63)); - historyTable.getTableHeader().setForeground(Color.WHITE); - } - - private void layoutComponents() { - JPanel mainPanel = new JPanel(new BorderLayout(0, 20)); - mainPanel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - mainPanel.setOpaque(false); - - // Title - JLabel titleLabel = new JLabel("KIỂM KÊ KHO THUỐC", SwingConstants.CENTER); - titleLabel.setFont(new Font("Arial", Font.BOLD, 28)); - titleLabel.setForeground(new Color(37, 47, 63)); - mainPanel.add(titleLabel, BorderLayout.NORTH); - - // Main content split pane - JSplitPane mainSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT); - mainSplit.setDividerLocation(400); - mainSplit.setResizeWeight(0.6); - - // Top: Selection and adjustment - JPanel topPanel = createSelectionAndAdjustmentPanel(); - mainSplit.setTopComponent(topPanel); - - // Bottom: History - RoundedPanel historyPanel = createHistoryPanel(); - mainSplit.setBottomComponent(historyPanel); - - mainPanel.add(mainSplit, BorderLayout.CENTER); - add(mainPanel, BorderLayout.CENTER); - } - - private JPanel createSelectionAndAdjustmentPanel() { - JPanel panel = new JPanel(new BorderLayout(15, 0)); - panel.setOpaque(false); - - // Left: Medicine and batch selection - JSplitPane selectionSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); - selectionSplit.setDividerLocation(300); - selectionSplit.setResizeWeight(0.5); - - RoundedPanel medicinePanel = createMedicineSelectionPanel(); - RoundedPanel batchPanel = createBatchSelectionPanel(); - - selectionSplit.setLeftComponent(medicinePanel); - selectionSplit.setRightComponent(batchPanel); - - // Right: Adjustment form - RoundedPanel adjustmentPanel = createAdjustmentFormPanel(); - - panel.add(selectionSplit, BorderLayout.CENTER); - panel.add(adjustmentPanel, BorderLayout.EAST); - - return panel; - } - - private RoundedPanel createMedicineSelectionPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout(0, 10)); - panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - - // Header - JLabel headerLabel = new JLabel("1. Chọn Thuốc"); - headerLabel.setFont(new Font("Arial", Font.BOLD, 16)); - headerLabel.setForeground(new Color(37, 47, 63)); - panel.add(headerLabel, BorderLayout.NORTH); - - // Search - JPanel searchPanel = new JPanel(new BorderLayout(5, 0)); - searchPanel.setOpaque(false); - searchPanel.add(new JLabel("🔍"), BorderLayout.WEST); - searchPanel.add(medicineSearchField, BorderLayout.CENTER); - panel.add(searchPanel, BorderLayout.CENTER); - - // Table - JScrollPane scrollPane = new JScrollPane(medicineTable); - scrollPane.setPreferredSize(new Dimension(0, 200)); - panel.add(scrollPane, BorderLayout.SOUTH); - - return panel; - } - - private RoundedPanel createBatchSelectionPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout(0, 10)); - panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - - // Header - JLabel headerLabel = new JLabel("2. Chọn Lô Cần Kiểm Kê"); - headerLabel.setFont(new Font("Arial", Font.BOLD, 16)); - headerLabel.setForeground(new Color(37, 47, 63)); - panel.add(headerLabel, BorderLayout.NORTH); - - // Table - JScrollPane scrollPane = new JScrollPane(batchTable); - panel.add(scrollPane, BorderLayout.CENTER); - - return panel; - } - - private RoundedPanel createAdjustmentFormPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new GridBagLayout()); - panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - panel.setPreferredSize(new Dimension(350, 0)); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(8, 5, 8, 5); - gbc.anchor = GridBagConstraints.WEST; - - // Header - gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2; - JLabel headerLabel = new JLabel("3. Thực Hiện Kiểm Kê"); - headerLabel.setFont(new Font("Arial", Font.BOLD, 16)); - headerLabel.setForeground(new Color(37, 47, 63)); - panel.add(headerLabel, gbc); - - // Selected medicine - gbc.gridy = 1; gbc.gridwidth = 1; - panel.add(new JLabel("Thuốc:"), gbc); - gbc.gridx = 1; - panel.add(selectedMedicineLabel, gbc); - - // Selected batch - gbc.gridx = 0; gbc.gridy = 2; - panel.add(new JLabel("Lô:"), gbc); - gbc.gridx = 1; - panel.add(selectedBatchLabel, gbc); - - // Current quantity - gbc.gridx = 0; gbc.gridy = 3; - panel.add(new JLabel("Tồn hiện tại:"), gbc); - gbc.gridx = 1; - panel.add(currentQuantityLabel, gbc); - - // Actual quantity - gbc.gridx = 0; gbc.gridy = 4; - panel.add(new JLabel("Số lượng thực tế:"), gbc); - gbc.gridx = 1; - panel.add(actualQuantitySpinner, gbc); - - // Reason - gbc.gridx = 0; gbc.gridy = 5; gbc.anchor = GridBagConstraints.NORTHWEST; - panel.add(new JLabel("Lý do:"), gbc); - gbc.gridx = 1; gbc.fill = GridBagConstraints.BOTH; - JScrollPane reasonScroll = new JScrollPane(reasonTextArea); - reasonScroll.setPreferredSize(new Dimension(200, 80)); - panel.add(reasonScroll, gbc); - - // Buttons - gbc.gridx = 0; gbc.gridy = 6; gbc.gridwidth = 2; gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.anchor = GridBagConstraints.CENTER; - JPanel buttonPanel = new JPanel(new FlowLayout()); - buttonPanel.setOpaque(false); - buttonPanel.add(cancelButton); - buttonPanel.add(adjustButton); - panel.add(buttonPanel, gbc); - - return panel; - } - - private RoundedPanel createHistoryPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout(0, 10)); - panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - - // Header - JLabel headerLabel = new JLabel("Lịch Sử Kiểm Kê"); - headerLabel.setFont(new Font("Arial", Font.BOLD, 16)); - headerLabel.setForeground(new Color(37, 47, 63)); - panel.add(headerLabel, BorderLayout.NORTH); - - // Table - JScrollPane scrollPane = new JScrollPane(historyTable); - panel.add(scrollPane, BorderLayout.CENTER); - - return panel; - } - - private void setupEventListeners() { - // Medicine table selection - medicineTable.getSelectionModel().addListSelectionListener(e -> { - if (!e.getValueIsAdjusting()) { - onMedicineSelected(); - } - }); - - // Batch table selection - batchTable.getSelectionModel().addListSelectionListener(e -> { - if (!e.getValueIsAdjusting()) { - onBatchSelected(); - } - }); - - // Adjust button - adjustButton.addActionListener(e -> performStockAdjustment()); - - // Cancel button - cancelButton.addActionListener(e -> clearSelection()); - } - - private void onMedicineSelected() { - int row = medicineTable.getSelectedRow(); - if (row >= 0) { - String medicineName = (String) medicineTableModel.getValueAt(row, 0); - selectedMedicineLabel.setText(medicineName); - loadBatchesForMedicine(medicineName); - clearBatchSelection(); - } - } - - private void onBatchSelected() { - int row = batchTable.getSelectedRow(); - if (row >= 0) { - String batch = (String) batchTableModel.getValueAt(row, 0); - String currentQty = (String) batchTableModel.getValueAt(row, 2); - - selectedBatchLabel.setText("Lô: " + batch); - currentQuantityLabel.setText(currentQty); - - try { - int qty = Integer.parseInt(currentQty); - actualQuantitySpinner.setValue(qty); - } catch (NumberFormatException e) { - actualQuantitySpinner.setValue(0); - } - - adjustButton.setEnabled(true); - } - } - - private void loadBatchesForMedicine(String medicineName) { - batchTableModel.setRowCount(0); - - // TODO: Load actual batch data from database - // Mock data - Object[][] mockBatches = { - {"PA202408", "15/08/2025", "45"}, - {"PA202411", "30/11/2025", "18"}, - {"PA202501", "15/02/2026", "30"} - }; - - for (Object[] batch : mockBatches) { - batchTableModel.addRow(batch); - } - } - - private void performStockAdjustment() { - String medicineName = selectedMedicineLabel.getText(); - String batch = selectedBatchLabel.getText(); - String currentQtyStr = currentQuantityLabel.getText(); - int actualQty = (Integer) actualQuantitySpinner.getValue(); - String reason = reasonTextArea.getText().trim(); - - if (reason.isEmpty()) { - JOptionPane.showMessageDialog(this, "Vui lòng nhập lý do kiểm kê!", - "Thông báo", JOptionPane.WARNING_MESSAGE); - return; - } - - try { - int currentQty = Integer.parseInt(currentQtyStr); - int difference = actualQty - currentQty; - - // Add to history - historyTableModel.insertRow(0, new Object[]{ - dateFormatter.format(new Date()), - medicineName, - batch, - currentQty, - actualQty, - (difference >= 0 ? "+" : "") + difference, - reason - }); - - // TODO: Update database - JOptionPane.showMessageDialog(this, - "Kiểm kê thành công!\nChênh lệch: " + difference + " đơn vị", - "Thành công", JOptionPane.INFORMATION_MESSAGE); - - // Update the batch table - int selectedRow = batchTable.getSelectedRow(); - if (selectedRow >= 0) { - batchTableModel.setValueAt(String.valueOf(actualQty), selectedRow, 2); - } - - clearSelection(); - - } catch (NumberFormatException e) { - JOptionPane.showMessageDialog(this, "Lỗi dữ liệu số lượng!", - "Lỗi", JOptionPane.ERROR_MESSAGE); - } - } - - private void clearSelection() { - medicineTable.clearSelection(); - batchTable.clearSelection(); - clearBatchSelection(); - } - - private void clearBatchSelection() { - selectedMedicineLabel.setText("Chưa chọn thuốc"); - selectedBatchLabel.setText("Chưa chọn lô"); - currentQuantityLabel.setText("0"); - actualQuantitySpinner.setValue(0); - reasonTextArea.setText(""); - adjustButton.setEnabled(false); - } - - public void refreshData() { - // TODO: Load actual data from database - loadMockMedicineData(); - loadMockHistoryData(); - } - - private void loadMockMedicineData() { - medicineTableModel.setRowCount(0); - Object[][] mockData = { - {"Paracetamol 500mg", "63", "Bình thường"}, - {"Amoxicillin 250mg", "112", "Bình thường"}, - {"Vitamin C 1000mg", "8", "Sắp hết"}, - {"Ibuprofen 400mg", "45", "Bình thường"} - }; - - for (Object[] row : mockData) { - medicineTableModel.addRow(row); - } - } - - private void loadMockHistoryData() { - historyTableModel.setRowCount(0); - Object[][] mockHistory = { - {"20/06/2024 14:30", "Paracetamol 500mg", "PA202408", "50", "45", "-5", "Hỏng do ẩm"}, - {"18/06/2024 09:15", "Vitamin C 1000mg", "VC202405", "15", "12", "-3", "Hết hạn"}, - {"15/06/2024 16:45", "Amoxicillin 250mg", "AM202409", "100", "112", "+12", "Tìm thấy thêm trong kho"} - }; - - for (Object[] row : mockHistory) { - historyTableModel.addRow(row); - } - } -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/InventoryPage/SupplierMasterPanel.java b/src/main/java/BsK/client/ui/component/InventoryPage/SupplierMasterPanel.java deleted file mode 100644 index 8b62927..0000000 --- a/src/main/java/BsK/client/ui/component/InventoryPage/SupplierMasterPanel.java +++ /dev/null @@ -1,537 +0,0 @@ -package BsK.client.ui.component.InventoryPage; - -import BsK.client.ui.component.common.RoundedPanel; -import lombok.extern.slf4j.Slf4j; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.text.SimpleDateFormat; - -@Slf4j -public class SupplierMasterPanel extends JPanel { - private InventoryPage parentPage; - - // Supplier list - private JTextField searchField; - private JTable supplierTable; - private DefaultTableModel supplierTableModel; - - // Supplier form - private JTextField companyNameField; - private JTextField contactPersonField; - private JTextField phoneField; - private JTextField emailField; - private JTextField addressField; - private JTextField taxCodeField; - private JTextArea notesArea; - private JComboBox statusCombo; - - // Actions - private JButton addButton; - private JButton editButton; - private JButton saveButton; - private JButton cancelButton; - private JButton deleteButton; - private JButton viewHistoryButton; - - private boolean isEditing = false; - private int editingRow = -1; - private SimpleDateFormat dateFormatter; - - public SupplierMasterPanel(InventoryPage parentPage) { - this.parentPage = parentPage; - this.dateFormatter = new SimpleDateFormat("dd/MM/yyyy"); - - setLayout(new BorderLayout()); - setBackground(new Color(245, 245, 245)); - - initializeComponents(); - layoutComponents(); - setupEventListeners(); - } - - private void initializeComponents() { - // Search field - searchField = new JTextField(); - searchField.setFont(new Font("Arial", Font.PLAIN, 14)); - searchField.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(8, 12, 8, 12) - )); - - // Supplier table - String[] columns = {"Tên Công Ty", "Người Liên Hệ", "Điện Thoại", "Email", "Trạng Thái"}; - supplierTableModel = new DefaultTableModel(columns, 0) { - @Override - public boolean isCellEditable(int row, int column) { return false; } - }; - supplierTable = new JTable(supplierTableModel); - setupSupplierTable(); - - // Form fields - companyNameField = createFormField(); - contactPersonField = createFormField(); - phoneField = createFormField(); - emailField = createFormField(); - addressField = createFormField(); - taxCodeField = createFormField(); - - notesArea = new JTextArea(3, 30); - notesArea.setFont(new Font("Arial", Font.PLAIN, 14)); - notesArea.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(8, 8, 8, 8) - )); - notesArea.setLineWrap(true); - notesArea.setWrapStyleWord(true); - notesArea.setEnabled(false); - - statusCombo = new JComboBox<>(new String[]{"Hoạt động", "Tạm dừng", "Ngừng hợp tác"}); - statusCombo.setFont(new Font("Arial", Font.PLAIN, 14)); - statusCombo.setEnabled(false); - - // Action buttons - addButton = createActionButton("➕ Thêm NCC", new Color(76, 175, 80)); - editButton = createActionButton("✏️ Sửa", new Color(255, 152, 0)); - saveButton = createActionButton("💾 Lưu", new Color(33, 150, 243)); - cancelButton = createActionButton("❌ Hủy", new Color(158, 158, 158)); - deleteButton = createActionButton("🗑️ Xóa", new Color(244, 67, 54)); - viewHistoryButton = createActionButton("📋 Lịch Sử", new Color(156, 39, 176)); - - saveButton.setEnabled(false); - cancelButton.setEnabled(false); - editButton.setEnabled(false); - deleteButton.setEnabled(false); - viewHistoryButton.setEnabled(false); - } - - private JTextField createFormField() { - JTextField field = new JTextField(); - field.setFont(new Font("Arial", Font.PLAIN, 14)); - field.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(200, 200, 200)), - BorderFactory.createEmptyBorder(5, 10, 5, 10) - )); - field.setEnabled(false); - return field; - } - - private JButton createActionButton(String text, Color color) { - JButton button = new JButton(text); - button.setFont(new Font("Arial", Font.BOLD, 14)); - button.setBackground(color); - button.setForeground(Color.WHITE); - button.setFocusPainted(false); - button.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 15)); - button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - return button; - } - - private void setupSupplierTable() { - supplierTable.setFont(new Font("Arial", Font.PLAIN, 14)); - supplierTable.setRowHeight(35); - supplierTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - supplierTable.setBackground(Color.WHITE); - supplierTable.getTableHeader().setFont(new Font("Arial", Font.BOLD, 14)); - supplierTable.getTableHeader().setBackground(new Color(37, 47, 63)); - supplierTable.getTableHeader().setForeground(Color.WHITE); - - // Status column with color coding - supplierTable.getColumnModel().getColumn(4).setCellRenderer(new DefaultTableCellRenderer() { - @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, - boolean hasFocus, int row, int column) { - Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - - if (!isSelected) { - String status = (String) value; - if ("Hoạt động".equals(status)) { - c.setBackground(new Color(232, 245, 233)); - setForeground(new Color(76, 175, 80)); - } else if ("Tạm dừng".equals(status)) { - c.setBackground(new Color(255, 248, 225)); - setForeground(new Color(255, 152, 0)); - } else { - c.setBackground(new Color(255, 235, 238)); - setForeground(new Color(244, 67, 54)); - } - } - setHorizontalAlignment(SwingConstants.CENTER); - return c; - } - }); - } - - private void layoutComponents() { - JPanel mainPanel = new JPanel(new BorderLayout(0, 20)); - mainPanel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - mainPanel.setOpaque(false); - - // Title - JLabel titleLabel = new JLabel("QUẢN LÝ NHÀ CUNG CẤP", SwingConstants.CENTER); - titleLabel.setFont(new Font("Arial", Font.BOLD, 28)); - titleLabel.setForeground(new Color(37, 47, 63)); - mainPanel.add(titleLabel, BorderLayout.NORTH); - - // Split pane: list + form - JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); - splitPane.setDividerLocation(650); - splitPane.setResizeWeight(0.65); - - // Left: Supplier list - RoundedPanel listPanel = createSupplierListPanel(); - splitPane.setLeftComponent(listPanel); - - // Right: Supplier form - RoundedPanel formPanel = createSupplierFormPanel(); - splitPane.setRightComponent(formPanel); - - mainPanel.add(splitPane, BorderLayout.CENTER); - add(mainPanel, BorderLayout.CENTER); - } - - private RoundedPanel createSupplierListPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout(0, 15)); - panel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - - // Header with search - JPanel headerPanel = new JPanel(new BorderLayout(10, 0)); - headerPanel.setOpaque(false); - - JLabel headerLabel = new JLabel("DANH SÁCH NHÀ CUNG CẤP"); - headerLabel.setFont(new Font("Arial", Font.BOLD, 16)); - headerLabel.setForeground(new Color(37, 47, 63)); - - JPanel searchPanel = new JPanel(new BorderLayout(5, 0)); - searchPanel.setOpaque(false); - searchPanel.add(new JLabel("🔍"), BorderLayout.WEST); - searchPanel.add(searchField, BorderLayout.CENTER); - searchPanel.setPreferredSize(new Dimension(250, 35)); - - headerPanel.add(headerLabel, BorderLayout.WEST); - headerPanel.add(searchPanel, BorderLayout.EAST); - panel.add(headerPanel, BorderLayout.NORTH); - - // Table - JScrollPane scrollPane = new JScrollPane(supplierTable); - scrollPane.setBorder(BorderFactory.createLineBorder(new Color(200, 200, 200))); - panel.add(scrollPane, BorderLayout.CENTER); - - return panel; - } - - private RoundedPanel createSupplierFormPanel() { - RoundedPanel panel = new RoundedPanel(10, Color.WHITE, true); - panel.setLayout(new BorderLayout(0, 15)); - panel.setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25)); - - // Header - JLabel headerLabel = new JLabel("THÔNG TIN NHÀ CUNG CẤP"); - headerLabel.setFont(new Font("Arial", Font.BOLD, 16)); - headerLabel.setForeground(new Color(37, 47, 63)); - panel.add(headerLabel, BorderLayout.NORTH); - - // Form fields - JPanel formPanel = new JPanel(new GridBagLayout()); - formPanel.setOpaque(false); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(8, 5, 8, 10); - gbc.anchor = GridBagConstraints.WEST; - - // Company info - addFormRow(formPanel, gbc, 0, "Tên công ty:", companyNameField); - addFormRow(formPanel, gbc, 1, "Người liên hệ:", contactPersonField); - addFormRow(formPanel, gbc, 2, "Điện thoại:", phoneField); - addFormRow(formPanel, gbc, 3, "Email:", emailField); - addFormRow(formPanel, gbc, 4, "Mã số thuế:", taxCodeField); - addFormRow(formPanel, gbc, 5, "Trạng thái:", statusCombo); - - // Address - gbc.gridx = 0; gbc.gridy = 6; gbc.anchor = GridBagConstraints.NORTHWEST; - formPanel.add(new JLabel("Địa chỉ:"), gbc); - gbc.gridx = 1; gbc.fill = GridBagConstraints.HORIZONTAL; - addressField.setPreferredSize(new Dimension(250, 60)); - formPanel.add(addressField, gbc); - - // Notes - gbc.gridx = 0; gbc.gridy = 7; gbc.anchor = GridBagConstraints.NORTHWEST; - gbc.fill = GridBagConstraints.NONE; - formPanel.add(new JLabel("Ghi chú:"), gbc); - gbc.gridx = 1; gbc.fill = GridBagConstraints.BOTH; - JScrollPane notesScroll = new JScrollPane(notesArea); - notesScroll.setPreferredSize(new Dimension(250, 80)); - formPanel.add(notesScroll, gbc); - - panel.add(formPanel, BorderLayout.CENTER); - - // Action buttons - JPanel buttonPanel = new JPanel(new GridLayout(3, 2, 8, 8)); - buttonPanel.setOpaque(false); - buttonPanel.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0)); - buttonPanel.add(addButton); - buttonPanel.add(editButton); - buttonPanel.add(saveButton); - buttonPanel.add(cancelButton); - buttonPanel.add(deleteButton); - buttonPanel.add(viewHistoryButton); - panel.add(buttonPanel, BorderLayout.SOUTH); - - return panel; - } - - private void addFormRow(JPanel parent, GridBagConstraints gbc, int row, String label, JComponent component) { - gbc.gridx = 0; gbc.gridy = row; gbc.fill = GridBagConstraints.NONE; - parent.add(new JLabel(label), gbc); - gbc.gridx = 1; gbc.fill = GridBagConstraints.HORIZONTAL; - component.setPreferredSize(new Dimension(250, 30)); - parent.add(component, gbc); - } - - private void setupEventListeners() { - // Table selection - supplierTable.getSelectionModel().addListSelectionListener(e -> { - if (!e.getValueIsAdjusting()) { - onSupplierSelected(); - } - }); - - // Action buttons - addButton.addActionListener(e -> startAdd()); - editButton.addActionListener(e -> startEdit()); - saveButton.addActionListener(e -> saveSupplier()); - cancelButton.addActionListener(e -> cancelOperation()); - deleteButton.addActionListener(e -> deleteSupplier()); - viewHistoryButton.addActionListener(e -> viewPurchaseHistory()); - } - - private void onSupplierSelected() { - int row = supplierTable.getSelectedRow(); - boolean hasSelection = row >= 0; - - editButton.setEnabled(hasSelection && !isEditing); - deleteButton.setEnabled(hasSelection && !isEditing); - viewHistoryButton.setEnabled(hasSelection && !isEditing); - - if (hasSelection) { - loadSupplierToForm(row); - } - } - - private void loadSupplierToForm(int row) { - // TODO: Load actual supplier data - companyNameField.setText((String) supplierTableModel.getValueAt(row, 0)); - contactPersonField.setText((String) supplierTableModel.getValueAt(row, 1)); - phoneField.setText((String) supplierTableModel.getValueAt(row, 2)); - emailField.setText((String) supplierTableModel.getValueAt(row, 3)); - statusCombo.setSelectedItem(supplierTableModel.getValueAt(row, 4)); - - // Mock additional data - taxCodeField.setText("0123456789"); - addressField.setText("123 Đường ABC, Phường XYZ, Quận 1, TP.HCM"); - notesArea.setText("Nhà cung cấp uy tín, giao hàng đúng hẹn."); - } - - private void startAdd() { - isEditing = true; - editingRow = -1; - enableFormFields(true); - clearForm(); - - addButton.setEnabled(false); - editButton.setEnabled(false); - deleteButton.setEnabled(false); - viewHistoryButton.setEnabled(false); - saveButton.setEnabled(true); - cancelButton.setEnabled(true); - } - - private void startEdit() { - int row = supplierTable.getSelectedRow(); - if (row < 0) return; - - isEditing = true; - editingRow = row; - enableFormFields(true); - - addButton.setEnabled(false); - editButton.setEnabled(false); - deleteButton.setEnabled(false); - viewHistoryButton.setEnabled(false); - saveButton.setEnabled(true); - cancelButton.setEnabled(true); - } - - private void saveSupplier() { - if (!validateForm()) return; - - Object[] rowData = { - companyNameField.getText(), - contactPersonField.getText(), - phoneField.getText(), - emailField.getText(), - statusCombo.getSelectedItem() - }; - - if (editingRow >= 0) { - // Update existing - for (int i = 0; i < rowData.length; i++) { - supplierTableModel.setValueAt(rowData[i], editingRow, i); - } - JOptionPane.showMessageDialog(this, "Cập nhật nhà cung cấp thành công!", - "Thành công", JOptionPane.INFORMATION_MESSAGE); - } else { - // Add new - supplierTableModel.addRow(rowData); - JOptionPane.showMessageDialog(this, "Thêm nhà cung cấp mới thành công!", - "Thành công", JOptionPane.INFORMATION_MESSAGE); - } - - // TODO: Save to database - cancelOperation(); - } - - private void cancelOperation() { - isEditing = false; - editingRow = -1; - enableFormFields(false); - - addButton.setEnabled(true); - saveButton.setEnabled(false); - cancelButton.setEnabled(false); - onSupplierSelected(); // Refresh button states - } - - private void deleteSupplier() { - int row = supplierTable.getSelectedRow(); - if (row < 0) return; - - String companyName = (String) supplierTableModel.getValueAt(row, 0); - int confirm = JOptionPane.showConfirmDialog(this, - "Bạn có chắc chắn muốn xóa nhà cung cấp '" + companyName + "'?", - "Xác nhận xóa", JOptionPane.YES_NO_OPTION); - - if (confirm == JOptionPane.YES_OPTION) { - supplierTableModel.removeRow(row); - clearForm(); - JOptionPane.showMessageDialog(this, "Xóa nhà cung cấp thành công!", - "Thành công", JOptionPane.INFORMATION_MESSAGE); - // TODO: Delete from database - } - } - - private void viewPurchaseHistory() { - int row = supplierTable.getSelectedRow(); - if (row < 0) return; - - String companyName = (String) supplierTableModel.getValueAt(row, 0); - - // Create history dialog - JDialog historyDialog = new JDialog((Frame) SwingUtilities.getWindowAncestor(this), - "Lịch Sử Mua Hàng - " + companyName, true); - historyDialog.setSize(800, 500); - historyDialog.setLocationRelativeTo(this); - - // History table - String[] historyColumns = {"Ngày", "Số HĐ", "Tổng Tiền", "Trạng Thái", "Ghi Chú"}; - DefaultTableModel historyModel = new DefaultTableModel(historyColumns, 0); - JTable historyTable = new JTable(historyModel); - historyTable.setRowHeight(30); - - // Mock history data - Object[][] historyData = { - {"20/06/2024", "HD001", "15.650.000", "Đã thanh toán", "Giao đúng hẹn"}, - {"15/06/2024", "HD002", "8.900.000", "Đã thanh toán", ""}, - {"10/06/2024", "HD003", "22.300.000", "Chưa thanh toán", "Chờ duyệt"}, - {"05/06/2024", "HD004", "12.800.000", "Đã thanh toán", "Có chiết khấu"} - }; - - for (Object[] row : historyData) { - historyModel.addRow(row); - } - - JScrollPane historyScroll = new JScrollPane(historyTable); - historyDialog.add(historyScroll, BorderLayout.CENTER); - - // Summary panel - JPanel summaryPanel = new JPanel(new FlowLayout()); - summaryPanel.add(new JLabel("Tổng số đơn hàng: 4 | Tổng giá trị: 59.650.000 VNĐ")); - historyDialog.add(summaryPanel, BorderLayout.SOUTH); - - historyDialog.setVisible(true); - } - - private void enableFormFields(boolean enabled) { - companyNameField.setEnabled(enabled); - contactPersonField.setEnabled(enabled); - phoneField.setEnabled(enabled); - emailField.setEnabled(enabled); - addressField.setEnabled(enabled); - taxCodeField.setEnabled(enabled); - notesArea.setEnabled(enabled); - statusCombo.setEnabled(enabled); - } - - private void clearForm() { - companyNameField.setText(""); - contactPersonField.setText(""); - phoneField.setText(""); - emailField.setText(""); - addressField.setText(""); - taxCodeField.setText(""); - notesArea.setText(""); - statusCombo.setSelectedIndex(0); - } - - private boolean validateForm() { - if (companyNameField.getText().trim().isEmpty()) { - JOptionPane.showMessageDialog(this, "Vui lòng nhập tên công ty!", - "Thông báo", JOptionPane.WARNING_MESSAGE); - companyNameField.requestFocus(); - return false; - } - - if (contactPersonField.getText().trim().isEmpty()) { - JOptionPane.showMessageDialog(this, "Vui lòng nhập người liên hệ!", - "Thông báo", JOptionPane.WARNING_MESSAGE); - contactPersonField.requestFocus(); - return false; - } - - if (phoneField.getText().trim().isEmpty()) { - JOptionPane.showMessageDialog(this, "Vui lòng nhập số điện thoại!", - "Thông báo", JOptionPane.WARNING_MESSAGE); - phoneField.requestFocus(); - return false; - } - - return true; - } - - public void refreshData() { - // TODO: Load actual data from database - loadMockSupplierData(); - } - - private void loadMockSupplierData() { - supplierTableModel.setRowCount(0); - Object[][] mockData = { - {"Công ty TNHH Dược phẩm ABC", "Nguyễn Văn A", "0901234567", "contact@abc.com", "Hoạt động"}, - {"Công ty CP Dược Hậu Giang", "Trần Thị B", "0902345678", "info@haugiangpharma.com", "Hoạt động"}, - {"Công ty TNHH Dược phẩm Traphaco", "Lê Văn C", "0903456789", "sales@traphaco.com", "Hoạt động"}, - {"Công ty CP Dược phẩm OPC", "Phạm Thị D", "0904567890", "business@opc.vn", "Tạm dừng"}, - {"Công ty TNHH Imexpharm", "Hoàng Văn E", "0905678901", "order@imexpharm.com", "Hoạt động"} - }; - - for (Object[] row : mockData) { - supplierTableModel.addRow(row); - } - - log.info("Supplier master data loaded - " + mockData.length + " suppliers"); - } -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/InventoryPage/common/InventoryButtonFactory.java b/src/main/java/BsK/client/ui/component/InventoryPage/common/InventoryButtonFactory.java deleted file mode 100644 index 9415a4b..0000000 --- a/src/main/java/BsK/client/ui/component/InventoryPage/common/InventoryButtonFactory.java +++ /dev/null @@ -1,296 +0,0 @@ -package BsK.client.ui.component.InventoryPage.common; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -/** - * Centralized button factory for consistent UI/UX across the Inventory Management module. - * Defines standardized button styles for different action types. - */ -public class InventoryButtonFactory { - - // Brand color palette (softer, professional tones) - public static final Color PRIMARY_COLOR = new Color(63, 81, 181); // Professional blue - public static final Color PRIMARY_HOVER = new Color(48, 63, 159); // Darker blue on hover - public static final Color SECONDARY_COLOR = new Color(158, 158, 158); // Neutral grey - public static final Color SECONDARY_HOVER = new Color(117, 117, 117); // Darker grey on hover - public static final Color DESTRUCTIVE_COLOR = new Color(229, 57, 53); // Softer red - public static final Color DESTRUCTIVE_HOVER = new Color(198, 40, 40); // Darker red on hover - public static final Color SPECIAL_COLOR = new Color(97, 97, 97); // Dark grey - public static final Color SPECIAL_HOVER = new Color(66, 66, 66); // Darker grey on hover - public static final Color SUCCESS_COLOR = new Color(67, 160, 71); // Softer green - public static final Color SUCCESS_HOVER = new Color(56, 142, 60); // Darker green on hover - - // Standard button dimensions - public static final Dimension STANDARD_BUTTON_SIZE = new Dimension(140, 40); - public static final Dimension LARGE_BUTTON_SIZE = new Dimension(180, 45); - public static final Dimension SMALL_BUTTON_SIZE = new Dimension(100, 35); - - // Font configurations - public static final Font BUTTON_FONT = new Font("Arial", Font.BOLD, 14); - public static final Font LARGE_BUTTON_FONT = new Font("Arial", Font.BOLD, 16); - public static final Font SMALL_BUTTON_FONT = new Font("Arial", Font.BOLD, 12); - - /** - * Creates a primary action button (Save, Create, Confirm) - * Solid background with white text - */ - public static JButton createPrimaryButton(String text) { - return createStandardButton(text, PRIMARY_COLOR, PRIMARY_HOVER, Color.WHITE, BUTTON_FONT, STANDARD_BUTTON_SIZE); - } - - public static JButton createPrimaryButton(String text, Dimension size) { - Font font = size.equals(LARGE_BUTTON_SIZE) ? LARGE_BUTTON_FONT : - size.equals(SMALL_BUTTON_SIZE) ? SMALL_BUTTON_FONT : BUTTON_FONT; - return createStandardButton(text, PRIMARY_COLOR, PRIMARY_HOVER, Color.WHITE, font, size); - } - - /** - * Creates a secondary action button (Cancel, Close) - * Grey background or outline with colored text - */ - public static JButton createSecondaryButton(String text) { - return createStandardButton(text, SECONDARY_COLOR, SECONDARY_HOVER, Color.WHITE, BUTTON_FONT, STANDARD_BUTTON_SIZE); - } - - public static JButton createSecondaryButton(String text, Dimension size) { - Font font = size.equals(LARGE_BUTTON_SIZE) ? LARGE_BUTTON_FONT : - size.equals(SMALL_BUTTON_SIZE) ? SMALL_BUTTON_FONT : BUTTON_FONT; - return createStandardButton(text, SECONDARY_COLOR, SECONDARY_HOVER, Color.WHITE, font, size); - } - - /** - * Creates a destructive action button (Delete, Remove) - * Solid red background with white text - */ - public static JButton createDestructiveButton(String text) { - return createStandardButton(text, DESTRUCTIVE_COLOR, DESTRUCTIVE_HOVER, Color.WHITE, BUTTON_FONT, STANDARD_BUTTON_SIZE); - } - - public static JButton createDestructiveButton(String text, Dimension size) { - Font font = size.equals(LARGE_BUTTON_SIZE) ? LARGE_BUTTON_FONT : - size.equals(SMALL_BUTTON_SIZE) ? SMALL_BUTTON_FONT : BUTTON_FONT; - return createStandardButton(text, DESTRUCTIVE_COLOR, DESTRUCTIVE_HOVER, Color.WHITE, font, size); - } - - /** - * Creates a special action button (Print, Export) - * Dark grey background with white text - */ - public static JButton createSpecialButton(String text) { - return createStandardButton(text, SPECIAL_COLOR, SPECIAL_HOVER, Color.WHITE, BUTTON_FONT, STANDARD_BUTTON_SIZE); - } - - public static JButton createSpecialButton(String text, Dimension size) { - Font font = size.equals(LARGE_BUTTON_SIZE) ? LARGE_BUTTON_FONT : - size.equals(SMALL_BUTTON_SIZE) ? SMALL_BUTTON_FONT : BUTTON_FONT; - return createStandardButton(text, SPECIAL_COLOR, SPECIAL_HOVER, Color.WHITE, font, size); - } - - /** - * Creates a success action button (Add, Create New) - * Solid green background with white text - */ - public static JButton createSuccessButton(String text) { - return createStandardButton(text, SUCCESS_COLOR, SUCCESS_HOVER, Color.WHITE, BUTTON_FONT, STANDARD_BUTTON_SIZE); - } - - public static JButton createSuccessButton(String text, Dimension size) { - Font font = size.equals(LARGE_BUTTON_SIZE) ? LARGE_BUTTON_FONT : - size.equals(SMALL_BUTTON_SIZE) ? SMALL_BUTTON_FONT : BUTTON_FONT; - return createStandardButton(text, SUCCESS_COLOR, SUCCESS_HOVER, Color.WHITE, font, size); - } - - /** - * Creates an outline button (secondary style with border) - * Transparent background with colored border and text - */ - public static JButton createOutlineButton(String text, Color borderColor) { - JButton button = new JButton(text); - button.setFont(BUTTON_FONT); - button.setForeground(borderColor); - button.setBackground(Color.WHITE); - button.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(borderColor, 2), - BorderFactory.createEmptyBorder(8, 15, 8, 15) - )); - button.setFocusPainted(false); - button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - button.setPreferredSize(STANDARD_BUTTON_SIZE); - button.setMinimumSize(STANDARD_BUTTON_SIZE); - button.setMaximumSize(STANDARD_BUTTON_SIZE); - - // Hover effect for outline buttons - Color hoverBackground = new Color(borderColor.getRed(), borderColor.getGreen(), borderColor.getBlue(), 20); - button.addMouseListener(new MouseAdapter() { - @Override - public void mouseEntered(MouseEvent e) { - if (button.isEnabled()) { - button.setBackground(hoverBackground); - } - } - - @Override - public void mouseExited(MouseEvent e) { - button.setBackground(Color.WHITE); - } - }); - - return button; - } - - /** - * Creates an icon-only button for use in tables and compact areas - */ - public static JButton createIconButton(String icon, Color backgroundColor, Color hoverColor, String tooltip) { - JButton button = new JButton(icon); - button.setFont(new Font("Arial", Font.PLAIN, 16)); - button.setBackground(backgroundColor); - button.setForeground(Color.WHITE); - button.setBorder(BorderFactory.createEmptyBorder(5, 8, 5, 8)); - button.setFocusPainted(false); - button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - button.setPreferredSize(new Dimension(35, 30)); - button.setMinimumSize(new Dimension(35, 30)); - button.setMaximumSize(new Dimension(35, 30)); - - if (tooltip != null && !tooltip.isEmpty()) { - button.setToolTipText(tooltip); - } - - // Hover effect - button.addMouseListener(new MouseAdapter() { - @Override - public void mouseEntered(MouseEvent e) { - if (button.isEnabled()) { - button.setBackground(hoverColor); - } - } - - @Override - public void mouseExited(MouseEvent e) { - if (button.isEnabled()) { - button.setBackground(backgroundColor); - } - } - }); - - return button; - } - - /** - * Core method for creating standardized buttons - */ - private static JButton createStandardButton(String text, Color backgroundColor, Color hoverColor, - Color textColor, Font font, Dimension size) { - JButton button = new JButton(text); - button.setFont(font); - button.setBackground(backgroundColor); - button.setForeground(textColor); - button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); - button.setFocusPainted(false); - button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - button.setPreferredSize(size); - button.setMinimumSize(size); - button.setMaximumSize(size); - - // Hover effect - button.addMouseListener(new MouseAdapter() { - @Override - public void mouseEntered(MouseEvent e) { - if (button.isEnabled()) { - button.setBackground(hoverColor); - } - } - - @Override - public void mouseExited(MouseEvent e) { - if (button.isEnabled()) { - button.setBackground(backgroundColor); - } else { - button.setBackground(new Color(200, 200, 200)); // Disabled color - } - } - }); - - // Disabled state styling - button.addPropertyChangeListener("enabled", evt -> { - if ((Boolean) evt.getNewValue()) { - button.setBackground(backgroundColor); - button.setForeground(textColor); - } else { - button.setBackground(new Color(200, 200, 200)); - button.setForeground(new Color(150, 150, 150)); - } - }); - - return button; - } - - /** - * Creates a button group panel with standardized spacing - */ - public static JPanel createButtonGroup(JButton... buttons) { - JPanel panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0)); - panel.setOpaque(false); - - for (JButton button : buttons) { - panel.add(button); - } - - return panel; - } - - /** - * Creates a button group panel with custom alignment - */ - public static JPanel createButtonGroup(int alignment, JButton... buttons) { - JPanel panel = new JPanel(new FlowLayout(alignment, 10, 0)); - panel.setOpaque(false); - - for (JButton button : buttons) { - panel.add(button); - } - - return panel; - } - - /** - * Creates a split button group (left and right aligned) - */ - public static JPanel createSplitButtonGroup(JButton[] leftButtons, JButton[] rightButtons) { - JPanel panel = new JPanel(new BorderLayout()); - panel.setOpaque(false); - - if (leftButtons != null && leftButtons.length > 0) { - JPanel leftPanel = createButtonGroup(FlowLayout.LEFT, leftButtons); - panel.add(leftPanel, BorderLayout.WEST); - } - - if (rightButtons != null && rightButtons.length > 0) { - JPanel rightPanel = createButtonGroup(FlowLayout.RIGHT, rightButtons); - panel.add(rightPanel, BorderLayout.EAST); - } - - return panel; - } - - // Standard icon constants for common actions - public static final String ICON_DELETE = "🗑️"; - public static final String ICON_EDIT = "✏️"; - public static final String ICON_VIEW = "👁️"; - public static final String ICON_PRINT = "🖨️"; - public static final String ICON_EXPORT = "📤"; - public static final String ICON_SEARCH = "🔍"; - public static final String ICON_ADD = "➕"; - public static final String ICON_SAVE = "💾"; - public static final String ICON_CANCEL = "❌"; - public static final String ICON_REFRESH = "🔄"; - public static final String ICON_SETTINGS = "⚙️"; - public static final String ICON_REPORT = "📊"; - public static final String ICON_WARNING = "⚠️"; - public static final String ICON_SUCCESS = "✅"; - public static final String ICON_INFO = "ℹ️"; -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/InventoryPage/common/InventoryColorScheme.java b/src/main/java/BsK/client/ui/component/InventoryPage/common/InventoryColorScheme.java deleted file mode 100644 index 9eb09f6..0000000 --- a/src/main/java/BsK/client/ui/component/InventoryPage/common/InventoryColorScheme.java +++ /dev/null @@ -1,187 +0,0 @@ -package BsK.client.ui.component.InventoryPage.common; - -import java.awt.*; - -/** - * Centralized color scheme for the Inventory Management module. - * Uses softer, professional tones for better readability and visual appeal. - */ -public class InventoryColorScheme { - - // Primary brand colors (softer, professional tones) - public static final Color PRIMARY_BLUE = new Color(63, 81, 181); - public static final Color PRIMARY_BLUE_LIGHT = new Color(92, 107, 192); - public static final Color PRIMARY_BLUE_DARK = new Color(48, 63, 159); - - // Status colors (less saturated for easier viewing) - public static final Color SUCCESS_GREEN = new Color(67, 160, 71); - public static final Color SUCCESS_GREEN_LIGHT = new Color(232, 245, 233); - public static final Color WARNING_ORANGE = new Color(255, 167, 38); - public static final Color WARNING_ORANGE_LIGHT = new Color(255, 248, 225); - public static final Color DANGER_RED = new Color(229, 57, 53); - public static final Color DANGER_RED_LIGHT = new Color(255, 235, 238); - public static final Color INFO_BLUE = new Color(33, 150, 243); - public static final Color INFO_BLUE_LIGHT = new Color(227, 242, 253); - - // Neutral colors - public static final Color GREY_50 = new Color(250, 250, 250); - public static final Color GREY_100 = new Color(245, 245, 245); - public static final Color GREY_200 = new Color(238, 238, 238); - public static final Color GREY_300 = new Color(224, 224, 224); - public static final Color GREY_400 = new Color(189, 189, 189); - public static final Color GREY_500 = new Color(158, 158, 158); - public static final Color GREY_600 = new Color(117, 117, 117); - public static final Color GREY_700 = new Color(97, 97, 97); - public static final Color GREY_800 = new Color(66, 66, 66); - public static final Color GREY_900 = new Color(33, 33, 33); - - // Background colors - public static final Color BACKGROUND_PRIMARY = Color.WHITE; - public static final Color BACKGROUND_SECONDARY = GREY_50; - public static final Color BACKGROUND_TERTIARY = GREY_100; - - // Text colors - public static final Color TEXT_PRIMARY = new Color(37, 47, 63); - public static final Color TEXT_SECONDARY = GREY_600; - public static final Color TEXT_DISABLED = GREY_400; - public static final Color TEXT_ON_DARK = Color.WHITE; - - // Border colors - public static final Color BORDER_LIGHT = GREY_200; - public static final Color BORDER_MEDIUM = GREY_300; - public static final Color BORDER_DARK = GREY_400; - - // Dashboard card colors (softer backgrounds) - public static final Color CARD_LOW_STOCK_BG = new Color(255, 242, 242); - public static final Color CARD_LOW_STOCK_BORDER = new Color(229, 115, 115); - public static final Color CARD_EXPIRING_BG = new Color(255, 248, 225); - public static final Color CARD_EXPIRING_BORDER = new Color(255, 193, 7); - public static final Color CARD_VALUE_BG = new Color(232, 245, 233); - public static final Color CARD_VALUE_BORDER = new Color(102, 187, 106); - public static final Color CARD_ITEMS_BG = new Color(232, 234, 246); - public static final Color CARD_ITEMS_BORDER = new Color(92, 107, 192); - - // Table colors - public static final Color TABLE_HEADER_BG = new Color(37, 47, 63); - public static final Color TABLE_HEADER_TEXT = Color.WHITE; - public static final Color TABLE_ROW_EVEN = Color.WHITE; - public static final Color TABLE_ROW_ODD = new Color(248, 249, 250); - public static final Color TABLE_ROW_SELECTED = new Color(232, 234, 246); - public static final Color TABLE_ROW_HOVER = new Color(245, 245, 245); - public static final Color TABLE_BORDER = GREY_200; - - // Status indicator colors in tables - public static final Color STATUS_ACTIVE_BG = SUCCESS_GREEN_LIGHT; - public static final Color STATUS_ACTIVE_TEXT = SUCCESS_GREEN; - public static final Color STATUS_WARNING_BG = WARNING_ORANGE_LIGHT; - public static final Color STATUS_WARNING_TEXT = WARNING_ORANGE; - public static final Color STATUS_DANGER_BG = DANGER_RED_LIGHT; - public static final Color STATUS_DANGER_TEXT = DANGER_RED; - public static final Color STATUS_INFO_BG = INFO_BLUE_LIGHT; - public static final Color STATUS_INFO_TEXT = INFO_BLUE; - - // Form colors - public static final Color FORM_FIELD_BG = Color.WHITE; - public static final Color FORM_FIELD_BORDER = BORDER_LIGHT; - public static final Color FORM_FIELD_BORDER_FOCUS = PRIMARY_BLUE; - public static final Color FORM_FIELD_DISABLED_BG = GREY_100; - public static final Color FORM_FIELD_DISABLED_BORDER = GREY_300; - - // Navigation colors - public static final Color NAV_BACKGROUND = new Color(37, 47, 63); - public static final Color NAV_TEXT = Color.WHITE; - public static final Color NAV_TEXT_INACTIVE = new Color(150, 150, 150); - public static final Color NAV_HOVER = new Color(52, 62, 78); - public static final Color NAV_ACTIVE = PRIMARY_BLUE; - - // Utility methods for creating variations - - /** - * Creates a lighter version of the given color - */ - public static Color lighten(Color color, float factor) { - int red = Math.min(255, (int) (color.getRed() + (255 - color.getRed()) * factor)); - int green = Math.min(255, (int) (color.getGreen() + (255 - color.getGreen()) * factor)); - int blue = Math.min(255, (int) (color.getBlue() + (255 - color.getBlue()) * factor)); - return new Color(red, green, blue, color.getAlpha()); - } - - /** - * Creates a darker version of the given color - */ - public static Color darken(Color color, float factor) { - int red = Math.max(0, (int) (color.getRed() * (1 - factor))); - int green = Math.max(0, (int) (color.getGreen() * (1 - factor))); - int blue = Math.max(0, (int) (color.getBlue() * (1 - factor))); - return new Color(red, green, blue, color.getAlpha()); - } - - /** - * Creates a color with specified opacity - */ - public static Color withOpacity(Color color, int alpha) { - return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); - } - - /** - * Gets status color based on string value - */ - public static Color getStatusColor(String status) { - if (status == null) return TEXT_SECONDARY; - - switch (status.toLowerCase()) { - case "hoạt động": - case "active": - case "bình thường": - case "normal": - return STATUS_ACTIVE_TEXT; - case "sắp hết": - case "low": - case "warning": - case "cảnh báo": - return STATUS_WARNING_TEXT; - case "hết hàng": - case "out": - case "danger": - case "nguy hiểm": - return STATUS_DANGER_TEXT; - case "tạm dừng": - case "paused": - case "info": - return STATUS_INFO_TEXT; - default: - return TEXT_SECONDARY; - } - } - - /** - * Gets status background color based on string value - */ - public static Color getStatusBackgroundColor(String status) { - if (status == null) return BACKGROUND_PRIMARY; - - switch (status.toLowerCase()) { - case "hoạt động": - case "active": - case "bình thường": - case "normal": - return STATUS_ACTIVE_BG; - case "sắp hết": - case "low": - case "warning": - case "cảnh báo": - return STATUS_WARNING_BG; - case "hết hàng": - case "out": - case "danger": - case "nguy hiểm": - return STATUS_DANGER_BG; - case "tạm dừng": - case "paused": - case "info": - return STATUS_INFO_BG; - default: - return BACKGROUND_PRIMARY; - } - } -} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/common/NavBar.java b/src/main/java/BsK/client/ui/component/common/NavBar.java index 00c9f5a..39c6ad0 100644 --- a/src/main/java/BsK/client/ui/component/common/NavBar.java +++ b/src/main/java/BsK/client/ui/component/common/NavBar.java @@ -2,6 +2,7 @@ package BsK.client.ui.component.common; import BsK.client.LocalStorage; import BsK.client.ui.component.MainFrame; +import BsK.client.ui.component.InfoPage.InfoDialog; import BsK.common.packet.req.LogoutRequest; import BsK.common.util.network.NetworkUtil; import BsK.client.network.handler.ClientHandler; @@ -71,16 +72,14 @@ public class NavBar extends JPanel { welcomePanel.add(welcomeLabel); userPanel.add(welcomePanel, new GridBagConstraints()); - String[] navBarItems = {"Thống kê", "Thăm khám", "Dữ liệu", "Kho", "Thanh toán", "Cài đặt", "Thông tin"}; - String[] destination = {"DashboardPage", "CheckUpPage", "PatientDataPage", "InventoryPage", "CheckoutPage", "SettingsPage", "InfoPage"}; - String[] iconFiles = {"dashboard.png", "health-check.png", "database.png", "warehouse.png", "cashier-machine.png", "settings.png", "info.png"}; + String[] navBarItems = {"Thống kê", "Thăm khám", "Dữ liệu", "Cài đặt", "Thông tin"}; + String[] destination = {"DashboardPage", "CheckUpPage", "PatientDataPage", "SettingsPage", "InfoPage"}; + String[] iconFiles = {"dashboard.png", "health-check.png", "database.png","settings.png", "info.png"}; Color[] navItemColors = { new Color(51, 135, 204), // Darker Blue new Color(66, 157, 21), // Darker Green new Color(200, 138, 16), // Darker Orange - new Color(204, 62, 63), // Darker Red new Color(91, 37, 167), // Darker Purple - new Color(15, 155, 155), // Darker Cyan new Color(196, 27, 36) // Darker Red-Orange }; @@ -133,6 +132,12 @@ public class NavBar extends JPanel { itemPanel.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { + if ("InfoPage".equals(dest)) { + InfoDialog infoDialog = new InfoDialog(); + infoDialog.setVisible(true); + return; + } + if (activeNavItem != null && activeNavItem != label) { JPanel prevPanel = (JPanel) activeNavItem.getParent(); prevPanel.setBackground(navItemColors[findNavItemIndex(activeNavItem.getText(), navBarItems)].brighter());