mirror of
https://github.com/tiennm99/BSK-All-in-One-Clinic-Management-System.git
synced 2026-10-15 00:16:48 +00:00
[update] Revise CheckUpPage layout to enhance user experience with a new queue management window, improved prescription display, and updated patient information fields. Adjust MedicineDialog and ServiceDialog to initialize prescriptions as empty arrays for better data handling.
This commit is contained in:
1 parent
6661507fd2
commit
7096f33770
9 files changed
+1318
-340
No files matched your search
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
File diff suppressed because it is too large.
Load diff
+6
-2
@@ -76,12 +76,16 @@ public class MedicineDialog extends JDialog {
|
||||
}
|
||||
|
||||
public MedicineDialog(Frame parent) {
|
||||
super(parent, "Thêm thuốc vào đơn", true);
|
||||
super(parent, "Thêm thuốc", true);
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
setSize(1200, 750); // Adjusted size slightly for new panel
|
||||
setSize(1100, 600);
|
||||
setLocationRelativeTo(parent);
|
||||
setResizable(true);
|
||||
|
||||
// Initialize medicinePrescription as empty array
|
||||
medicinePrescription = new String[0][0];
|
||||
|
||||
// Add response listener and send request for medicine data
|
||||
ClientHandler.addResponseListener(GetMedInfoResponse.class, getMedInfoResponseListener);
|
||||
sendGetMedInfoRequest();
|
||||
|
||||
|
||||
@@ -67,6 +67,10 @@ public class ServiceDialog extends JDialog {
|
||||
setLocationRelativeTo(parent);
|
||||
setResizable(true);
|
||||
|
||||
// Initialize servicePrescription as empty array
|
||||
servicePrescription = new String[0][0];
|
||||
|
||||
// Add response listener and send request for service data
|
||||
ClientHandler.addResponseListener(GetSerInfoResponse.class, getSerInfoResponseListener);
|
||||
sendGetSerInfoRequest();
|
||||
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
package BsK.server;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.text.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Vector;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.lang.management.OperatingSystemMXBean;
|
||||
import BsK.server.network.entity.ClientConnection;
|
||||
import BsK.server.network.manager.SessionManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class ServerDashboard extends JFrame {
|
||||
private JTextPane logArea;
|
||||
private JLabel statusLabel;
|
||||
private JLabel portLabel;
|
||||
private JLabel clientsLabel;
|
||||
private JLabel memoryLabel;
|
||||
private JLabel cpuLabel;
|
||||
private JToggleButton autoScrollToggle;
|
||||
private JTextField searchField;
|
||||
private static int connectedClients = 0;
|
||||
private static ServerDashboard instance;
|
||||
private final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
|
||||
private Timer statsTimer;
|
||||
private Timer networkStatsTimer;
|
||||
private boolean isAutoScrollEnabled = true;
|
||||
private JTable networkTable;
|
||||
private DefaultTableModel networkTableModel;
|
||||
|
||||
public static ServerDashboard getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new ServerDashboard();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private ServerDashboard() {
|
||||
setTitle("BSK Server Dashboard");
|
||||
setSize(1200, 800);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setLocationRelativeTo(null);
|
||||
|
||||
// Create main panel with border layout
|
||||
JPanel mainPanel = new JPanel(new BorderLayout());
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
||||
// Status Panel (North)
|
||||
JPanel topPanel = new JPanel(new GridLayout(2, 1, 0, 5));
|
||||
|
||||
// Server Status Panel
|
||||
JPanel statusPanel = new JPanel(new GridLayout(1, 3, 10, 0));
|
||||
statusPanel.setBorder(BorderFactory.createTitledBorder("Server Status"));
|
||||
|
||||
statusLabel = new JLabel("Status: Starting...", SwingConstants.CENTER);
|
||||
portLabel = new JLabel("Port: --", SwingConstants.CENTER);
|
||||
clientsLabel = new JLabel("Connected Clients: 0", SwingConstants.CENTER);
|
||||
|
||||
statusPanel.add(statusLabel);
|
||||
statusPanel.add(portLabel);
|
||||
statusPanel.add(clientsLabel);
|
||||
|
||||
// System Info Panel
|
||||
JPanel systemPanel = new JPanel(new GridLayout(1, 3, 10, 0));
|
||||
systemPanel.setBorder(BorderFactory.createTitledBorder("System Information"));
|
||||
|
||||
memoryLabel = new JLabel("Memory Usage: --", SwingConstants.CENTER);
|
||||
cpuLabel = new JLabel("CPU Usage: --", SwingConstants.CENTER);
|
||||
JLabel osLabel = new JLabel("OS: " + System.getProperty("os.name"), SwingConstants.CENTER);
|
||||
|
||||
systemPanel.add(memoryLabel);
|
||||
systemPanel.add(cpuLabel);
|
||||
systemPanel.add(osLabel);
|
||||
|
||||
topPanel.add(statusPanel);
|
||||
topPanel.add(systemPanel);
|
||||
|
||||
// Create split pane for logs and network info
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
||||
splitPane.setResizeWeight(0.6); // Give more space to the top component
|
||||
|
||||
// Network Information Panel
|
||||
JPanel networkPanel = new JPanel(new BorderLayout());
|
||||
networkPanel.setBorder(BorderFactory.createTitledBorder("Network Information"));
|
||||
|
||||
// Create table model with columns
|
||||
String[] columnNames = {"Session ID", "IP Address", "Port", "Role", "Connected Time", "Last Activity", "Bytes Sent", "Bytes Received"};
|
||||
networkTableModel = new DefaultTableModel(columnNames, 0) {
|
||||
@Override
|
||||
public boolean isCellEditable(int row, int column) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
networkTable = new JTable(networkTableModel);
|
||||
networkTable.setFillsViewportHeight(true);
|
||||
JScrollPane networkScrollPane = new JScrollPane(networkTable);
|
||||
networkPanel.add(networkScrollPane, BorderLayout.CENTER);
|
||||
|
||||
// Log Controls Panel
|
||||
JPanel logControlsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
|
||||
// Search field
|
||||
searchField = new JTextField(20);
|
||||
JButton searchButton = new JButton("Search");
|
||||
searchButton.addActionListener(e -> searchInLogs());
|
||||
|
||||
// Clear logs button
|
||||
JButton clearButton = new JButton("Clear Logs");
|
||||
clearButton.addActionListener(e -> clearLogs());
|
||||
|
||||
// Auto-scroll toggle
|
||||
autoScrollToggle = new JToggleButton("Auto-scroll", true);
|
||||
autoScrollToggle.addActionListener(e -> isAutoScrollEnabled = autoScrollToggle.isSelected());
|
||||
|
||||
logControlsPanel.add(new JLabel("Search: "));
|
||||
logControlsPanel.add(searchField);
|
||||
logControlsPanel.add(searchButton);
|
||||
logControlsPanel.add(Box.createHorizontalStrut(20));
|
||||
logControlsPanel.add(clearButton);
|
||||
logControlsPanel.add(Box.createHorizontalStrut(20));
|
||||
logControlsPanel.add(autoScrollToggle);
|
||||
|
||||
// Log Area Panel
|
||||
JPanel logPanel = new JPanel(new BorderLayout());
|
||||
logPanel.setBorder(BorderFactory.createTitledBorder("Server Logs"));
|
||||
|
||||
logArea = new JTextPane();
|
||||
logArea.setEditable(false);
|
||||
logArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
|
||||
JScrollPane logScrollPane = new JScrollPane(logArea);
|
||||
|
||||
logPanel.add(logControlsPanel, BorderLayout.NORTH);
|
||||
logPanel.add(logScrollPane, BorderLayout.CENTER);
|
||||
|
||||
// Add components to split pane
|
||||
splitPane.setTopComponent(logPanel);
|
||||
splitPane.setBottomComponent(networkPanel);
|
||||
|
||||
// Add components to main panel
|
||||
mainPanel.add(topPanel, BorderLayout.NORTH);
|
||||
mainPanel.add(splitPane, BorderLayout.CENTER);
|
||||
|
||||
add(mainPanel);
|
||||
|
||||
// Start system stats update timer
|
||||
startSystemStatsTimer();
|
||||
// Start network stats update timer
|
||||
startNetworkStatsTimer();
|
||||
}
|
||||
|
||||
private void startSystemStatsTimer() {
|
||||
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
|
||||
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
|
||||
|
||||
statsTimer = new Timer(2000, e -> {
|
||||
// Update memory usage
|
||||
long usedMemory = memoryBean.getHeapMemoryUsage().getUsed() / (1024 * 1024);
|
||||
long maxMemory = memoryBean.getHeapMemoryUsage().getMax() / (1024 * 1024);
|
||||
SwingUtilities.invokeLater(() ->
|
||||
memoryLabel.setText(String.format("Memory Usage: %d MB / %d MB", usedMemory, maxMemory))
|
||||
);
|
||||
|
||||
// Update CPU usage if available
|
||||
if (osBean instanceof com.sun.management.OperatingSystemMXBean) {
|
||||
double cpuLoad = ((com.sun.management.OperatingSystemMXBean) osBean).getSystemCpuLoad() * 100;
|
||||
SwingUtilities.invokeLater(() ->
|
||||
cpuLabel.setText(String.format("CPU Usage: %.1f%%", cpuLoad))
|
||||
);
|
||||
}
|
||||
});
|
||||
statsTimer.start();
|
||||
}
|
||||
|
||||
private void startNetworkStatsTimer() {
|
||||
networkStatsTimer = new Timer(1000, e -> refreshNetworkTable());
|
||||
networkStatsTimer.start();
|
||||
}
|
||||
|
||||
public void refreshNetworkTable() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
// Clear existing rows
|
||||
networkTableModel.setRowCount(0);
|
||||
|
||||
// Add current connections
|
||||
for (ClientConnection conn : SessionManager.getAllConnections()) {
|
||||
Vector<Object> row = new Vector<>();
|
||||
row.add(conn.getSessionId());
|
||||
row.add(conn.getIpAddress());
|
||||
row.add(conn.getPort());
|
||||
row.add(conn.getUserRole());
|
||||
row.add(conn.getConnectionDuration());
|
||||
row.add(conn.getLastActivityDuration());
|
||||
row.add(formatBytes(conn.getBytesSent()));
|
||||
row.add(formatBytes(conn.getBytesReceived()));
|
||||
networkTableModel.addRow(row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String formatBytes(long bytes) {
|
||||
if (bytes < 1024) return bytes + " B";
|
||||
if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
|
||||
if (bytes < 1024 * 1024 * 1024) return String.format("%.1f MB", bytes / (1024.0 * 1024));
|
||||
return String.format("%.1f GB", bytes / (1024.0 * 1024 * 1024));
|
||||
}
|
||||
|
||||
private void searchInLogs() {
|
||||
String searchText = searchField.getText().toLowerCase();
|
||||
if (searchText.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String content = logArea.getDocument().getText(0, logArea.getDocument().getLength());
|
||||
logArea.getHighlighter().removeAllHighlights();
|
||||
|
||||
int index = content.toLowerCase().indexOf(searchText);
|
||||
while (index >= 0) {
|
||||
logArea.getHighlighter().addHighlight(
|
||||
index,
|
||||
index + searchText.length(),
|
||||
new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW)
|
||||
);
|
||||
index = content.toLowerCase().indexOf(searchText, index + 1);
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
log.error("Error searching logs", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearLogs() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
logArea.getDocument().remove(0, logArea.getDocument().getLength());
|
||||
addLog("Logs cleared");
|
||||
} catch (BadLocationException e) {
|
||||
log.error("Error clearing logs", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void updateStatus(String status, Color color) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusLabel.setText("Status: " + status);
|
||||
statusLabel.setForeground(color);
|
||||
});
|
||||
}
|
||||
|
||||
public void updatePort(int port) {
|
||||
SwingUtilities.invokeLater(() ->
|
||||
portLabel.setText("Port: " + port)
|
||||
);
|
||||
}
|
||||
|
||||
public static void incrementClients() {
|
||||
connectedClients++;
|
||||
updateClientCount();
|
||||
}
|
||||
|
||||
public static void decrementClients() {
|
||||
connectedClients--;
|
||||
updateClientCount();
|
||||
}
|
||||
|
||||
private static void updateClientCount() {
|
||||
if (instance != null) {
|
||||
SwingUtilities.invokeLater(() ->
|
||||
instance.clientsLabel.setText("Connected Clients: " + connectedClients)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void addLog(String message) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
Document doc = logArea.getDocument();
|
||||
String timestamp = timeFormat.format(new Date());
|
||||
String formattedMessage = String.format("[%s] %s%n", timestamp, message);
|
||||
|
||||
// Create style for new text
|
||||
Style style = logArea.addStyle("Log Style", null);
|
||||
StyleConstants.setFontFamily(style, "Monospace");
|
||||
StyleConstants.setFontSize(style, 12);
|
||||
|
||||
// Insert the text at the end
|
||||
doc.insertString(doc.getLength(), formattedMessage, style);
|
||||
|
||||
// Scroll to bottom if auto-scroll is enabled
|
||||
if (isAutoScrollEnabled) {
|
||||
logArea.setCaretPosition(doc.getLength());
|
||||
}
|
||||
} catch (BadLocationException e) {
|
||||
log.error("Error adding log message", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (statsTimer != null) {
|
||||
statsTimer.stop();
|
||||
}
|
||||
if (networkStatsTimer != null) {
|
||||
networkStatsTimer.stop();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package BsK.server.network.entity;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.Data;
|
||||
import java.time.Instant;
|
||||
import java.time.Duration;
|
||||
|
||||
@Data
|
||||
public class ClientConnection {
|
||||
private final String ipAddress;
|
||||
private final int port;
|
||||
private final Instant connectTime;
|
||||
private Instant lastActivityTime;
|
||||
private long bytesSent;
|
||||
private long bytesReceived;
|
||||
private final int sessionId;
|
||||
private String userRole;
|
||||
private int userId;
|
||||
|
||||
public ClientConnection(Channel channel, int sessionId) {
|
||||
String remoteAddress = channel.remoteAddress().toString();
|
||||
// Remove the leading '/' if present and split by ':'
|
||||
String[] parts = remoteAddress.replaceFirst("^/", "").split(":");
|
||||
this.ipAddress = parts[0];
|
||||
this.port = Integer.parseInt(parts[1]);
|
||||
this.connectTime = Instant.now();
|
||||
this.lastActivityTime = Instant.now();
|
||||
this.sessionId = sessionId;
|
||||
this.userRole = "GUEST";
|
||||
this.bytesSent = 0;
|
||||
this.bytesReceived = 0;
|
||||
}
|
||||
|
||||
public void updateLastActivity() {
|
||||
this.lastActivityTime = Instant.now();
|
||||
}
|
||||
|
||||
public void addBytesSent(long bytes) {
|
||||
this.bytesSent += bytes;
|
||||
}
|
||||
|
||||
public void addBytesReceived(long bytes) {
|
||||
this.bytesReceived += bytes;
|
||||
}
|
||||
|
||||
public String getConnectionDuration() {
|
||||
Duration duration = Duration.between(connectTime, Instant.now());
|
||||
long hours = duration.toHours();
|
||||
long minutes = duration.toMinutesPart();
|
||||
long seconds = duration.toSecondsPart();
|
||||
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
|
||||
}
|
||||
|
||||
public String getLastActivityDuration() {
|
||||
Duration duration = Duration.between(lastActivityTime, Instant.now());
|
||||
long minutes = duration.toMinutes();
|
||||
if (minutes < 1) {
|
||||
return "Just now";
|
||||
} else if (minutes == 1) {
|
||||
return "1 minute ago";
|
||||
} else {
|
||||
return minutes + " minutes ago";
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 393 KiB |
Reference in new issue
Block a user