mirror of
https://github.com/tiennm99/BSK-All-in-One-Clinic-Management-System.git
synced 2026-10-11 12:11:43 +00:00
614 lines
28 KiB
Java
614 lines
28 KiB
Java
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.client.ui.component.SettingsPage.SettingsDialog;
|
|
import BsK.client.ui.component.DataDialog.DataDialog;
|
|
import BsK.client.ui.component.CheckUpPage.CheckUpPage;
|
|
import BsK.common.Error;
|
|
import BsK.common.packet.req.EmergencyRequest;
|
|
import BsK.common.packet.req.LogoutRequest;
|
|
import BsK.common.util.network.NetworkUtil;
|
|
import BsK.client.network.handler.ClientHandler;
|
|
import BsK.client.network.handler.ResponseListener;
|
|
import BsK.common.packet.res.EmergencyResponse;
|
|
import BsK.common.packet.res.ErrorResponse;
|
|
import BsK.common.packet.res.SimpleMessageResponse;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import javax.swing.Icon;
|
|
|
|
import javax.swing.*;
|
|
import java.awt.*;
|
|
import java.awt.event.MouseAdapter;
|
|
import java.awt.event.MouseEvent;
|
|
import java.awt.event.ActionListener;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.Date;
|
|
|
|
@Slf4j
|
|
public class NavBar extends JPanel {
|
|
|
|
private JLabel activeNavItem = null;
|
|
private final MainFrame mainFrame;
|
|
private JLabel unreadCountLabel; // NEW: Label to display unread count
|
|
private RoundedPanel messageButtonPanel; // NEW: Reference to message button panel for repainting
|
|
|
|
// Listener for incoming emergency alerts
|
|
private final ResponseListener<EmergencyResponse> emergencyResponseListener = this::handleEmergencyResponse;
|
|
private final ResponseListener<ErrorResponse> errorResponseListener = this::handleErrorResponse;
|
|
private final ResponseListener<SimpleMessageResponse> navBarMessageListener = this::showNotificationForMessage;
|
|
|
|
public NavBar(MainFrame mainFrame, String activePageTitle) {
|
|
this.mainFrame = mainFrame;
|
|
|
|
// Register the response listener for emergency alerts.
|
|
ClientHandler.addResponseListener(EmergencyResponse.class, emergencyResponseListener);
|
|
// add error response listener
|
|
ClientHandler.addResponseListener(ErrorResponse.class, errorResponseListener);
|
|
ClientHandler.addResponseListener(SimpleMessageResponse.class, navBarMessageListener);
|
|
this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
|
this.setBackground(new Color(240, 240, 240));
|
|
this.setBorder(BorderFactory.createEmptyBorder(5, 25, 5, 25));
|
|
|
|
// Left section with navigation items
|
|
JPanel navItemsPanel = new JPanel();
|
|
navItemsPanel.setBackground(this.getBackground());
|
|
navItemsPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 8, 0));
|
|
navItemsPanel.setAlignmentY(Component.CENTER_ALIGNMENT);
|
|
|
|
// Center section with current page title
|
|
JPanel titlePanel = new JPanel(new GridBagLayout());
|
|
titlePanel.setBackground(this.getBackground());
|
|
titlePanel.setAlignmentY(Component.CENTER_ALIGNMENT);
|
|
|
|
JLabel titleLabel = new JLabel(activePageTitle);
|
|
titleLabel.setFont(new Font("Arial", Font.BOLD, 24));
|
|
titleLabel.setForeground(new Color(60, 60, 60));
|
|
titlePanel.add(titleLabel, new GridBagConstraints());
|
|
|
|
// Right section with user info and emergency button
|
|
JPanel userPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 10, 0));
|
|
userPanel.setBackground(this.getBackground());
|
|
userPanel.setAlignmentY(Component.CENTER_ALIGNMENT);
|
|
|
|
Component messageButton = createMessageButton();
|
|
userPanel.add(messageButton);
|
|
// --- END: NEW MESSAGE BUTTON ---
|
|
|
|
// Add the Emergency button
|
|
Component emergencyButton = createEmergencyButton();
|
|
userPanel.add(emergencyButton);
|
|
|
|
// Add welcome label with user info
|
|
JPanel welcomePanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
|
welcomePanel.setBackground(this.getBackground());
|
|
JLabel welcomeLabel = new JLabel("Chào, " + LocalStorage.username);
|
|
welcomeLabel.setFont(new Font("Arial", Font.BOLD, 14));
|
|
welcomeLabel.setForeground(new Color(60, 60, 60));
|
|
welcomeLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
|
welcomeLabel.addMouseListener(new MouseAdapter() {
|
|
@Override
|
|
public void mouseClicked(MouseEvent e) {
|
|
showUserMenu(welcomeLabel);
|
|
}
|
|
@Override
|
|
public void mouseEntered(MouseEvent e) {
|
|
welcomeLabel.setForeground(new Color(30, 30, 30));
|
|
}
|
|
@Override
|
|
public void mouseExited(MouseEvent e) {
|
|
welcomeLabel.setForeground(new Color(60, 60, 60));
|
|
}
|
|
});
|
|
welcomePanel.add(welcomeLabel);
|
|
userPanel.add(welcomePanel);
|
|
|
|
String[] navBarItems = {"Thống kê", "Thăm khám", "Dữ liệu", "Cài đặt", "Thông tin"};
|
|
String[] destination = {"DashboardPage", "CheckUpPage", "CheckUpPage", "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(91, 37, 167), // Darker Purple
|
|
new Color(196, 27, 36) // Darker Red-Orange
|
|
};
|
|
|
|
final Color defaultTextColor = new Color(50, 50, 50);
|
|
final Color activeTextColor = Color.WHITE;
|
|
|
|
for (int i = 0; i < navBarItems.length; i++) {
|
|
final String itemText = navBarItems[i];
|
|
final String dest = destination[i];
|
|
String iconFileName = iconFiles[i];
|
|
final Color itemColor = navItemColors[i];
|
|
|
|
RoundedPanel itemPanel = new RoundedPanel(15, itemColor.brighter(), true);
|
|
itemPanel.setLayout(new BorderLayout(2, 2));
|
|
itemPanel.setBorder(BorderFactory.createCompoundBorder(
|
|
BorderFactory.createEmptyBorder(2, 2, 2, 2),
|
|
BorderFactory.createEmptyBorder(5, 10, 5, 10)
|
|
));
|
|
itemPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
|
itemPanel.setPreferredSize(new Dimension(100, 55));
|
|
|
|
final JLabel label = new JLabel(itemText);
|
|
label.setForeground(defaultTextColor);
|
|
label.setHorizontalAlignment(SwingConstants.CENTER);
|
|
label.setFont(new Font("Arial", Font.BOLD, 12));
|
|
label.setOpaque(false);
|
|
|
|
try {
|
|
String iconPath = "src/main/java/BsK/client/ui/assets/icon/" + iconFileName;
|
|
ImageIcon originalIcon = new ImageIcon(iconPath);
|
|
Image scaledImage = originalIcon.getImage().getScaledInstance(20, 20, Image.SCALE_SMOOTH);
|
|
ImageIcon scaledIcon = new ImageIcon(scaledImage);
|
|
label.setIcon(scaledIcon);
|
|
} catch (Exception e) {
|
|
System.err.println("Error loading icon: " + iconFileName + " for nav item: " + itemText);
|
|
e.printStackTrace();
|
|
}
|
|
|
|
label.setVerticalTextPosition(SwingConstants.BOTTOM);
|
|
label.setHorizontalTextPosition(SwingConstants.CENTER);
|
|
itemPanel.add(label, BorderLayout.CENTER);
|
|
|
|
if (itemText.equals(activePageTitle)) {
|
|
itemPanel.setBackground(itemColor);
|
|
label.setForeground(activeTextColor);
|
|
activeNavItem = label;
|
|
}
|
|
|
|
itemPanel.addMouseListener(new MouseAdapter() {
|
|
@Override
|
|
public void mouseClicked(MouseEvent e) {
|
|
if ("InfoPage".equals(dest)) {
|
|
InfoDialog infoDialog = new InfoDialog();
|
|
infoDialog.setVisible(true);
|
|
return;
|
|
}
|
|
|
|
if ("SettingsPage".equals(dest)) {
|
|
SettingsDialog settingsDialog = new SettingsDialog(mainFrame);
|
|
settingsDialog.setVisible(true);
|
|
return;
|
|
}
|
|
|
|
if ("Dữ liệu".equals(itemText)) {
|
|
if (!(mainFrame.getCurrentPage() instanceof CheckUpPage)) {
|
|
mainFrame.showPage("CheckUpPage");
|
|
if (activeNavItem != null) {
|
|
JPanel prevPanel = (JPanel) activeNavItem.getParent();
|
|
prevPanel.setBackground(navItemColors[findNavItemIndex(activeNavItem.getText(), navBarItems)].brighter());
|
|
activeNavItem.setForeground(defaultTextColor);
|
|
}
|
|
for (int j = 0; j < navBarItems.length; j++) {
|
|
if ("Thăm khám".equals(navBarItems[j])) {
|
|
Component[] components = navItemsPanel.getComponents();
|
|
for(Component comp : components) {
|
|
if (comp instanceof JPanel && ((JPanel) comp).getComponentCount() > 0 && ((JPanel) comp).getComponent(0) instanceof JLabel) {
|
|
JLabel potentialLabel = (JLabel) ((JPanel) comp).getComponent(0);
|
|
if ("Thăm khám".equals(potentialLabel.getText())) {
|
|
activeNavItem = potentialLabel;
|
|
((JPanel) comp).setBackground(navItemColors[j]);
|
|
potentialLabel.setForeground(activeTextColor);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
SwingUtilities.invokeLater(() -> {
|
|
// Get the current CheckUpPage instance from the main frame
|
|
CheckUpPage checkUpPage = (CheckUpPage) mainFrame.getCurrentPage();
|
|
|
|
// Pass this instance to the DataDialog
|
|
DataDialog dataDialog = new DataDialog(mainFrame, checkUpPage); // <-- MODIFIED CONSTRUCTOR
|
|
dataDialog.setVisible(true);
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (activeNavItem != null && activeNavItem != label) {
|
|
JPanel prevPanel = (JPanel) activeNavItem.getParent();
|
|
prevPanel.setBackground(navItemColors[findNavItemIndex(activeNavItem.getText(), navBarItems)].brighter());
|
|
activeNavItem.setForeground(defaultTextColor);
|
|
}
|
|
activeNavItem = label;
|
|
itemPanel.setBackground(itemColor);
|
|
label.setForeground(activeTextColor);
|
|
mainFrame.showPage(dest);
|
|
}
|
|
|
|
@Override
|
|
public void mouseEntered(MouseEvent e) {
|
|
if (label != activeNavItem) {
|
|
itemPanel.setBackground(itemColor);
|
|
label.setForeground(activeTextColor);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void mouseExited(MouseEvent e) {
|
|
if (label != activeNavItem) {
|
|
itemPanel.setBackground(itemColor.brighter());
|
|
label.setForeground(defaultTextColor);
|
|
}
|
|
}
|
|
});
|
|
navItemsPanel.add(itemPanel);
|
|
|
|
// --- MODIFIED SEPARATOR ---
|
|
if (i == 1) {
|
|
// Create the separator with a darker color
|
|
JSeparator separator = new JSeparator(SwingConstants.VERTICAL);
|
|
separator.setForeground(new Color(150, 150, 150));
|
|
|
|
// Set a fixed size to make it thicker and taller
|
|
separator.setPreferredSize(new Dimension(2, 45));
|
|
|
|
// Create a panel to hold the separator and add some horizontal padding for spacing
|
|
JPanel separatorPanel = new JPanel();
|
|
separatorPanel.setBackground(this.getBackground()); // Match the navbar background
|
|
separatorPanel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); // Add 5px padding left and right
|
|
separatorPanel.add(separator);
|
|
|
|
navItemsPanel.add(separatorPanel);
|
|
}
|
|
// --- END MODIFIED SEPARATOR ---
|
|
}
|
|
|
|
this.add(navItemsPanel);
|
|
this.add(Box.createHorizontalGlue());
|
|
this.add(titlePanel);
|
|
this.add(Box.createHorizontalGlue());
|
|
this.add(userPanel);
|
|
}
|
|
|
|
private int findNavItemIndex(String text, String[] items) {
|
|
for (int i = 0; i < items.length; i++) {
|
|
if (items[i].equals(text)) return i;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private void showNotificationForMessage(SimpleMessageResponse response) {
|
|
SwingUtilities.invokeLater(() -> {
|
|
// Increment unread message count
|
|
LocalStorage.unreadMessageCnt++;
|
|
updateUnreadCountDisplay();
|
|
|
|
// Create a borderless window for the notification
|
|
JWindow notificationWindow = new JWindow(mainFrame);
|
|
|
|
// Create a styled panel for the content
|
|
JPanel panel = new JPanel(new BorderLayout(10, 5));
|
|
panel.setBackground(new Color(45, 45, 45));
|
|
panel.setBorder(BorderFactory.createCompoundBorder(
|
|
BorderFactory.createLineBorder(new Color(0, 132, 255), 2),
|
|
BorderFactory.createEmptyBorder(10, 30, 10, 30)
|
|
));
|
|
|
|
String timestamp = new SimpleDateFormat("HH:mm").format(new Date());
|
|
String formattedMessage = String.format("[%s] %s: %s\n",
|
|
timestamp,
|
|
response.getSenderName(),
|
|
response.getMessage()
|
|
);
|
|
// add message to local storage
|
|
LocalStorage.chatHistory.add(formattedMessage);
|
|
|
|
// Create the message content
|
|
JLabel titleLabel = new JLabel("Tin nhắn mới từ: " + response.getSenderName());
|
|
titleLabel.setFont(new Font("Arial", Font.BOLD, 14));
|
|
titleLabel.setForeground(Color.WHITE);
|
|
|
|
JLabel messageLabel = new JLabel(response.getMessage());
|
|
messageLabel.setFont(new Font("Arial", Font.PLAIN, 12));
|
|
messageLabel.setForeground(Color.LIGHT_GRAY);
|
|
|
|
panel.add(titleLabel, BorderLayout.NORTH);
|
|
panel.add(messageLabel, BorderLayout.CENTER);
|
|
|
|
notificationWindow.add(panel);
|
|
notificationWindow.pack();
|
|
|
|
// Position the notification at the bottom-right of the screen
|
|
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
|
Rectangle screenRect = ge.getMaximumWindowBounds();
|
|
int x = screenRect.width - notificationWindow.getWidth() - 30;
|
|
int y = screenRect.height - notificationWindow.getHeight() - 30;
|
|
notificationWindow.setLocation(x, y);
|
|
|
|
notificationWindow.setVisible(true);
|
|
|
|
// Use a Timer to automatically close the notification after 5 seconds
|
|
Timer closeTimer = new Timer(5000, e -> notificationWindow.dispose());
|
|
closeTimer.setRepeats(false); // Only run once
|
|
closeTimer.start();
|
|
});
|
|
}
|
|
|
|
public void disableMessageListener() {
|
|
ClientHandler.deleteListener(SimpleMessageResponse.class, navBarMessageListener);
|
|
log.info("NavBar message listener disabled.");
|
|
}
|
|
|
|
public void enableMessageListener() {
|
|
ClientHandler.addResponseListener(SimpleMessageResponse.class, navBarMessageListener);
|
|
log.info("NavBar message listener re-enabled.");
|
|
}
|
|
|
|
// NEW: Method to reset unread count when chat dialog opens
|
|
public void resetUnreadMessageCount() {
|
|
LocalStorage.unreadMessageCnt = 0;
|
|
updateUnreadCountDisplay();
|
|
log.info("Unread message count reset to 0.");
|
|
}
|
|
|
|
// NEW: Method to update the unread count display
|
|
private void updateUnreadCountDisplay() {
|
|
SwingUtilities.invokeLater(() -> {
|
|
if (LocalStorage.unreadMessageCnt > 0) {
|
|
unreadCountLabel.setText(String.valueOf(LocalStorage.unreadMessageCnt));
|
|
unreadCountLabel.setVisible(true);
|
|
} else {
|
|
unreadCountLabel.setVisible(false);
|
|
}
|
|
messageButtonPanel.repaint();
|
|
});
|
|
}
|
|
|
|
private Component createEmergencyButton() {
|
|
Color emergencyColor = new Color(220, 38, 38);
|
|
RoundedPanel buttonPanel = new RoundedPanel(15, emergencyColor, true);
|
|
buttonPanel.setLayout(new BorderLayout(2, 2));
|
|
buttonPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
|
|
|
buttonPanel.setPreferredSize(new Dimension(100, 55));
|
|
buttonPanel.setBorder(BorderFactory.createCompoundBorder(
|
|
BorderFactory.createEmptyBorder(2, 2, 2, 2),
|
|
BorderFactory.createEmptyBorder(5, 10, 5, 10)
|
|
));
|
|
|
|
JLabel buttonLabel = new JLabel("Khẩn cấp");
|
|
buttonLabel.setForeground(Color.WHITE);
|
|
buttonLabel.setFont(new Font("Arial", Font.BOLD, 12));
|
|
buttonLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
|
buttonLabel.setOpaque(false);
|
|
|
|
try {
|
|
String iconPath = "src/main/java/BsK/client/ui/assets/icon/emergency.png";
|
|
ImageIcon originalIcon = new ImageIcon(iconPath);
|
|
Image scaledImage = originalIcon.getImage().getScaledInstance(20, 20, Image.SCALE_SMOOTH);
|
|
buttonLabel.setIcon(new ImageIcon(scaledImage));
|
|
} catch (Exception e) {
|
|
System.err.println("Error loading icon: emergency.png. Using text fallback.");
|
|
buttonLabel.setText("Khẩn cấp");
|
|
}
|
|
|
|
buttonLabel.setVerticalTextPosition(SwingConstants.BOTTOM);
|
|
buttonLabel.setHorizontalTextPosition(SwingConstants.CENTER);
|
|
buttonPanel.add(buttonLabel, BorderLayout.CENTER);
|
|
|
|
buttonPanel.addMouseListener(new MouseAdapter() {
|
|
@Override
|
|
public void mouseClicked(MouseEvent e) {
|
|
int response = JOptionPane.showConfirmDialog(
|
|
mainFrame,
|
|
"Bạn có chắc chắn muốn kích hoạt chế độ khẩn cấp không?",
|
|
"XÁC NHẬN KHẨN CẤP",
|
|
JOptionPane.YES_NO_OPTION,
|
|
JOptionPane.WARNING_MESSAGE
|
|
);
|
|
if (response == JOptionPane.YES_OPTION) {
|
|
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new EmergencyRequest());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void mouseEntered(MouseEvent e) {
|
|
buttonPanel.setBackground(emergencyColor.darker());
|
|
}
|
|
|
|
@Override
|
|
public void mouseExited(MouseEvent e) {
|
|
buttonPanel.setBackground(emergencyColor);
|
|
}
|
|
});
|
|
|
|
return buttonPanel;
|
|
}
|
|
|
|
private void showUserMenu(JLabel welcomeLabel) {
|
|
JPopupMenu popupMenu = new JPopupMenu();
|
|
popupMenu.setBorder(BorderFactory.createEmptyBorder());
|
|
|
|
JMenuItem profileItem = createMenuItem("Hồ sơ cá nhân", e ->
|
|
JOptionPane.showMessageDialog(mainFrame, "Tính năng Hồ sơ cá nhân sắp ra mắt!", "Thông báo", JOptionPane.INFORMATION_MESSAGE));
|
|
|
|
JMenuItem settingsItem = createMenuItem("Cài đặt", e -> {
|
|
SettingsDialog settingsDialog = new SettingsDialog(mainFrame);
|
|
settingsDialog.setVisible(true);
|
|
});
|
|
|
|
JMenuItem logoutItem = createMenuItem("Đăng xuất", e -> {
|
|
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new LogoutRequest());
|
|
LocalStorage.username = null;
|
|
LocalStorage.userId = -1;
|
|
mainFrame.showPage("LandingPage");
|
|
});
|
|
|
|
popupMenu.add(profileItem);
|
|
popupMenu.add(settingsItem);
|
|
popupMenu.addSeparator();
|
|
popupMenu.add(logoutItem);
|
|
|
|
popupMenu.setPreferredSize(new Dimension(180, popupMenu.getPreferredSize().height));
|
|
popupMenu.show(welcomeLabel, 0, welcomeLabel.getHeight());
|
|
}
|
|
|
|
private JMenuItem createMenuItem(String text, ActionListener action) {
|
|
JMenuItem item = new JMenuItem(text);
|
|
item.setFont(new Font("Arial", Font.PLAIN, 12));
|
|
item.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
|
|
item.addActionListener(action);
|
|
return item;
|
|
}
|
|
|
|
private Component createMessageButton() {
|
|
Color messageColor = new Color(150, 150, 150); // Muted gray color
|
|
messageButtonPanel = new RoundedPanel(15, messageColor, true); // Store reference
|
|
messageButtonPanel.setLayout(null); // Use null layout for absolute positioning
|
|
messageButtonPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
|
messageButtonPanel.setToolTipText("Tin nhắn"); // Add a tooltip
|
|
|
|
// Make the button smaller and square-like
|
|
messageButtonPanel.setPreferredSize(new Dimension(55, 55));
|
|
messageButtonPanel.setBorder(BorderFactory.createCompoundBorder(
|
|
BorderFactory.createEmptyBorder(2, 2, 2, 2),
|
|
BorderFactory.createEmptyBorder(5, 10, 5, 10)
|
|
));
|
|
|
|
// Create the main icon label
|
|
JLabel buttonLabel = new JLabel();
|
|
buttonLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
|
buttonLabel.setOpaque(false);
|
|
buttonLabel.setBounds(0, 0, 55, 55); // Position the main icon
|
|
|
|
try {
|
|
String iconPath = "src/main/java/BsK/client/ui/assets/icon/chat.png";
|
|
ImageIcon originalIcon = new ImageIcon(iconPath);
|
|
// Use a slightly smaller icon
|
|
Image scaledImage = originalIcon.getImage().getScaledInstance(24, 24, Image.SCALE_SMOOTH);
|
|
buttonLabel.setIcon(new ImageIcon(scaledImage));
|
|
} catch (Exception e) {
|
|
log.error("Error loading icon: chat.png. Using text fallback.");
|
|
buttonLabel.setText("Msg"); // Fallback text if icon fails
|
|
}
|
|
|
|
// Create the unread count badge
|
|
unreadCountLabel = new JLabel();
|
|
unreadCountLabel.setOpaque(true);
|
|
unreadCountLabel.setBackground(Color.RED);
|
|
unreadCountLabel.setForeground(Color.WHITE);
|
|
unreadCountLabel.setFont(new Font("Arial", Font.BOLD, 10));
|
|
unreadCountLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
|
unreadCountLabel.setBounds(35, 5, 18, 18); // Position in top-right corner
|
|
unreadCountLabel.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
|
|
|
// Make the badge circular
|
|
unreadCountLabel.setPreferredSize(new Dimension(18, 18));
|
|
unreadCountLabel.setMinimumSize(new Dimension(18, 18));
|
|
unreadCountLabel.setMaximumSize(new Dimension(18, 18));
|
|
|
|
// Initially hide the badge
|
|
unreadCountLabel.setVisible(false);
|
|
|
|
messageButtonPanel.add(buttonLabel);
|
|
messageButtonPanel.add(unreadCountLabel);
|
|
|
|
messageButtonPanel.addMouseListener(new MouseAdapter() {
|
|
@Override
|
|
public void mouseClicked(MouseEvent e) {
|
|
// Reset unread count when opening chat
|
|
resetUnreadMessageCount();
|
|
|
|
// Open the SimpleChatDialog
|
|
SimpleChatDialog chatDialog = new SimpleChatDialog(mainFrame, NavBar.this);
|
|
chatDialog.setVisible(true);
|
|
}
|
|
|
|
@Override
|
|
public void mouseEntered(MouseEvent e) {
|
|
messageButtonPanel.setBackground(messageColor.darker());
|
|
}
|
|
|
|
@Override
|
|
public void mouseExited(MouseEvent e) {
|
|
messageButtonPanel.setBackground(messageColor);
|
|
}
|
|
});
|
|
|
|
return messageButtonPanel;
|
|
}
|
|
|
|
/**
|
|
* Handles the incoming EmergencyResponse. This version is silent and uses a manual
|
|
* dialog creation to ensure the custom close button works correctly.
|
|
* @param response The emergency response packet from the server.
|
|
*/
|
|
private void handleEmergencyResponse(EmergencyResponse response) {
|
|
SwingUtilities.invokeLater(() -> {
|
|
log.warn("Received EMERGENCY alert from: {}", response.getSenderName());
|
|
|
|
// Create enhanced visual components for the dialog
|
|
String htmlMessage = "<html><body style='width: 350px; text-align: center;'>"
|
|
+ "<h1><font color='#B22222'>CẢNH BÁO KHẨN CẤP!</font></h1>"
|
|
+ "<p style='font-size: 14px;'>Nhận được tín hiệu khẩn cấp từ người dùng:</p>"
|
|
+ "<h2 style='font-size: 24px; color: #8B0000;'>" + response.getSenderName() + "</h2>"
|
|
+ "<p style='font-size: 14px;'>Vui lòng kiểm tra và hỗ trợ ngay lập tức!</p>"
|
|
+ "</body></html>";
|
|
JLabel messageLabel = new JLabel(htmlMessage);
|
|
|
|
JCheckBox acknowledgementCheck = new JCheckBox("Tôi xác nhận đã đọc và hiểu rõ cảnh báo này.");
|
|
acknowledgementCheck.setFont(new Font("Arial", Font.ITALIC, 12));
|
|
acknowledgementCheck.setOpaque(false);
|
|
|
|
JPanel contentPanel = new JPanel(new BorderLayout(0, 15));
|
|
contentPanel.setOpaque(false);
|
|
contentPanel.add(messageLabel, BorderLayout.CENTER);
|
|
contentPanel.add(acknowledgementCheck, BorderLayout.SOUTH);
|
|
|
|
JPanel dialogPanel = new JPanel(new BorderLayout());
|
|
dialogPanel.setBackground(new Color(255, 220, 220));
|
|
dialogPanel.setBorder(BorderFactory.createLineBorder(Color.RED, 3));
|
|
dialogPanel.add(contentPanel, BorderLayout.CENTER);
|
|
|
|
JButton confirmButton = new JButton("ĐÃ XỬ LÝ");
|
|
confirmButton.setFont(new Font("Arial", Font.BOLD, 14));
|
|
confirmButton.setEnabled(false);
|
|
|
|
acknowledgementCheck.addActionListener(e -> confirmButton.setEnabled(acknowledgementCheck.isSelected()));
|
|
|
|
// --- CORRECTED DIALOG CREATION ---
|
|
// 1. Create a JOptionPane instance with our custom panel and button.
|
|
JOptionPane optionPane = new JOptionPane(
|
|
dialogPanel,
|
|
JOptionPane.ERROR_MESSAGE,
|
|
JOptionPane.DEFAULT_OPTION,
|
|
null,
|
|
new Object[]{confirmButton}, // Array of options
|
|
confirmButton);
|
|
|
|
// 2. Create a JDialog from the JOptionPane.
|
|
final JDialog dialog = optionPane.createDialog(mainFrame, "🚨🚨 TÍN HIỆU KHẨN CẤP 🚨🚨");
|
|
|
|
// 3. Add an ActionListener to our custom button to close the dialog.
|
|
confirmButton.addActionListener(e -> dialog.dispose());
|
|
|
|
// 4. Show the dialog.
|
|
dialog.setVisible(true);
|
|
});
|
|
}
|
|
|
|
private void handleErrorResponse(ErrorResponse response) {
|
|
log.error("Error response: {}", response.getError());
|
|
SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(mainFrame,
|
|
response.getError().toString(), "Lỗi", JOptionPane.ERROR_MESSAGE));
|
|
}
|
|
|
|
/**
|
|
* Cleans up resources, specifically unregistering network listeners.
|
|
* This should be called when the NavBar is no longer needed (e.g., on logout).
|
|
*/
|
|
public void cleanup() {
|
|
ClientHandler.deleteListener(EmergencyResponse.class, emergencyResponseListener);
|
|
ClientHandler.deleteListener(ErrorResponse.class, errorResponseListener);
|
|
ClientHandler.deleteListener(SimpleMessageResponse.class, navBarMessageListener);
|
|
log.info("All NavBar listeners removed.");
|
|
}
|
|
} |