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());