mirror of
https://github.com/tiennm99/BSK-All-in-One-Clinic-Management-System.git
synced 2026-10-11 12:11:43 +00:00
[update] Introduce new medical unit conversion mapping in LocalStorage. Enhance AddDialog with improved UI for province and ward selection, including additional labels for better user guidance. Remove CheckoutPage and InventoryPage components to streamline the application structure.
This commit is contained in:
1 parent
a08a7de3c5
commit
45a0a2d792
16 files changed
+599
-5201
No files matched your search
@@ -20,4 +20,5 @@ public class LocalStorage {
|
||||
public static String[] wards;
|
||||
public static HashMap<String, String> provinceToId;
|
||||
public static HashMap<String, String[]> cachedWards; // cache local areas later will do
|
||||
public static HashMap<String, String[][]> medUnitConversion; // hash by med id inside is unit id and unit name and conversion rate and unit price
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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 = "<html>"
|
||||
+ "<body style='font-family: Segoe UI, Arial, sans-serif; line-height: 1.6; color: #333; background-color: #ffffff;'>"
|
||||
+ "<div style='text-align: center; margin-bottom: 25px;'>"
|
||||
+ "<h1 style='color: #2c5282; font-size: 24px; margin-bottom: 10px; font-weight: bold;'>BSK CLINIC MANAGEMENT SYSTEM</h1>"
|
||||
+ "<hr style='border: 2px solid #4299e1; width: 60%; margin: 20px auto;'>"
|
||||
+ "</div>"
|
||||
|
||||
+ "<div style='margin-bottom: 10px;'>"
|
||||
+ "<h2 style='color: #2d3748; font-size: 18px; margin-bottom: 15px; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px;'>Giới thiệu</h2>"
|
||||
+ "<p style='font-size: 14px; text-align: justify; margin-bottom: 15px;'>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 <strong style='color: #2c5282;'>Lê Thành Đạt </strong> "
|
||||
+ "(<a href='https://github.com/lds217' style='color: #3182ce; text-decoration: underline;'>lds</a>) "
|
||||
+ "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ừ <strong style='color: #2c5282;'>Nguyễn Minh Tiến </strong> "
|
||||
+ "(<a href='https://github.com/tiennm99' style='color: #3182ce; text-decoration: underline;'>tiennm99</a>).</p>"
|
||||
+ "<p style='font-size: 14px; text-align: justify;'>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.</p>"
|
||||
+ "</div>"
|
||||
|
||||
+ "<div style='margin-bottom: 25px;'>"
|
||||
+ "<h2 style='color: #2d3748; font-size: 18px; margin-bottom: 15px; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px;'>Tính năng nổi bật</h2>"
|
||||
+ "<div style='background-color: #f7fafc; padding: 15px; border-radius: 8px; border-left: 4px solid #4299e1;'>"
|
||||
+ "<ul style='font-size: 14px; margin: 0; padding-left: 20px;'>"
|
||||
+ "<li style='margin-bottom: 12px;'><strong style='color: #2c5282;'>Kết nối mạng nội bộ:</strong> 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.</li>"
|
||||
+ "<li style='margin-bottom: 12px;'><strong style='color: #2c5282;'>Tự động phân phối ảnh siêu âm:</strong> 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.</li>"
|
||||
+ "<li style='margin-bottom: 12px;'><strong style='color: #2c5282;'>Giao diện hiện đại:</strong> 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.</li>"
|
||||
+ "<li style='margin-bottom: 12px;'><strong style='color: #2c5282;'>Tích hợp Google Drive:</strong> 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.</li>"
|
||||
+ "<li style='margin-bottom: 0;'><strong style='color: #2c5282;'>Thông báo tái khám thông minh:</strong> 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.</li>"
|
||||
+ "</ul>"
|
||||
+ "</div>"
|
||||
+ "</div>"
|
||||
|
||||
+ "<div style='margin-bottom: 25px;'>"
|
||||
+ "<h2 style='color: #2d3748; font-size: 18px; margin-bottom: 15px; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px;'>Sứ mệnh</h2>"
|
||||
+ "<div style='background-color: #f0fff4; padding: 15px; border-radius: 8px; border-left: 4px solid #48bb78;'>"
|
||||
+ "<p style='font-size: 14px; text-align: justify; margin: 0;'>Đâ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.</p>"
|
||||
+ "</div>"
|
||||
+ "</div>"
|
||||
|
||||
+ "<div style='margin-bottom: 25px;'>"
|
||||
+ "<h2 style='color: #2d3748; font-size: 18px; margin-bottom: 15px; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px;'>Bản quyền và Liên hệ</h2>"
|
||||
+ "<div style='background-color: #edf2f7; padding: 15px; border-radius: 8px;'>"
|
||||
+ "<p style='font-size: 14px; margin-bottom: 15px;'>Dự án được phát triển và sở hữu bởi <strong style='color: #2c5282;'>Lê Thành Đạt (lds)</strong>. "
|
||||
+ "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:</p>"
|
||||
+ "<ul style='font-size: 14px; margin: 0; padding-left: 20px;'>"
|
||||
+ "<li style='margin-bottom: 8px;'><strong>GitHub:</strong> <a href='https://github.com/lds217' style='color: #3182ce; text-decoration: underline;'>lds217</a></li>"
|
||||
+ "<li style='margin-bottom: 0;'><strong>LinkedIn:</strong> <a href='https://www.linkedin.com/in/ldss21/' style='color: #3182ce; text-decoration: underline;'>Lê Thành Đạt</a></li>"
|
||||
+ "</ul>"
|
||||
+ "</div>"
|
||||
+ "</div>"
|
||||
|
||||
+ "<div style='margin-bottom: 10px;'>"
|
||||
+ "<h2 style='color: #2d3748; font-size: 18px; margin-bottom: 15px; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px;'>Cách đóng góp</h2>"
|
||||
+ "<div style='background-color: #fffaf0; padding: 15px; border-radius: 8px; border-left: 4px solid #ed8936;'>"
|
||||
+ "<p style='font-size: 14px; margin-bottom: 15px;'>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:</p>"
|
||||
+ "<ul style='font-size: 14px; margin: 0; padding-left: 20px;'>"
|
||||
+ "<li style='margin-bottom: 10px;'>Đề xuất tính năng mới hoặc báo cáo lỗi thông qua GitHub Issues.</li>"
|
||||
+ "<li style='margin-bottom: 10px;'>Đóng góp code thông qua Pull Requests.</li>"
|
||||
+ "<li style='margin-bottom: 0;'>Ủng hộ dự án một ly cà phê qua <strong style='color: #d69e2e;'>MB Bank: 0908308878</strong></li>"
|
||||
+ "</ul>"
|
||||
+ "</div>"
|
||||
+ "</div>"
|
||||
+ "</body></html>";
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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 "🗑️";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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"); }
|
||||
}
|
||||
@@ -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<String> 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");
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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");
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<String> 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");
|
||||
}
|
||||
}
|
||||
-296
@@ -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 = "ℹ️";
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
Reference in new issue
Block a user