diff --git a/.vscode/settings.json b/.vscode/settings.json index 9bd06c2..0164833 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "java.configuration.updateBuildConfiguration": "automatic", - "java.debug.settings.onBuildFailureProceed": true + "java.debug.settings.onBuildFailureProceed": true, + "java.compile.nullAnalysis.mode": "automatic" } \ No newline at end of file diff --git a/database/BSK.db-shm b/database/BSK.db-shm index 4d7a7fc..e729ed4 100644 Binary files a/database/BSK.db-shm and b/database/BSK.db-shm differ diff --git a/database/BSK.db-wal b/database/BSK.db-wal index dcc7d2d..1d3f5c6 100644 Binary files a/database/BSK.db-wal and b/database/BSK.db-wal differ diff --git a/src/main/java/BsK/client/ui/component/DataDialog/DataDialog.java b/src/main/java/BsK/client/ui/component/DataDialog/DataDialog.java index cb29204..3ae2cdd 100644 --- a/src/main/java/BsK/client/ui/component/DataDialog/DataDialog.java +++ b/src/main/java/BsK/client/ui/component/DataDialog/DataDialog.java @@ -12,6 +12,7 @@ import BsK.common.packet.res.DeleteCheckupResponse; import BsK.common.packet.res.GetCheckupDataResponse; import BsK.common.util.network.NetworkUtil; import BsK.client.ui.component.CheckUpPage.CheckUpPage; +import BsK.common.entity.CheckupData; import javax.swing.*; import javax.swing.border.EmptyBorder; @@ -28,7 +29,9 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; +import lombok.extern.slf4j.Slf4j; +@Slf4j public class DataDialog extends JDialog { // @@ -57,6 +60,7 @@ public class DataDialog extends JDialog { // --- Panels for the other tabs private MedicineManagementPanel medicinePanel; private ServiceManagementPanel servicePanel; + private DoctorManagementPanel doctorPanel; private UserManagementPanel userPanel; public DataDialog(JFrame parent, CheckUpPage checkUpPage) { @@ -100,6 +104,10 @@ public class DataDialog extends JDialog { userPanel = new UserManagementPanel(); tabbedPane.addTab("Quản Lý Người Dùng", userPanel); + // --- Tab 5: Doctor Management --- + doctorPanel = new DoctorManagementPanel(); + tabbedPane.addTab("Quản Lý Bác Sĩ", doctorPanel); + add(tabbedPane, BorderLayout.CENTER); loadFiltersFromLocalStorage(); @@ -124,6 +132,9 @@ public class DataDialog extends JDialog { if (userPanel != null) { userPanel.cleanup(); } + if (doctorPanel != null) { + doctorPanel.cleanup(); + } super.windowClosing(e); } }); @@ -285,7 +296,7 @@ public class DataDialog extends JDialog { }); getAllButton.addActionListener(e -> { - GetCheckupDataRequest request = new GetCheckupDataRequest(null, null,null, null, null, 1, recordsPerPage); + GetCheckupDataRequest request = new GetCheckupDataRequest(null, null,null, null, null, 1, recordsPerPage, LocalStorage.currentShift); NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request); resultCountLabel.setText("Đang tải tất cả dữ liệu..."); }); @@ -380,7 +391,7 @@ public class DataDialog extends JDialog { } } - GetCheckupDataRequest request = new GetCheckupDataRequest(searchTerm, checkupIdSearch, fromTimestamp, toTimestamp, doctorId, page, recordsPerPage); + GetCheckupDataRequest request = new GetCheckupDataRequest(searchTerm, checkupIdSearch, fromTimestamp, toTimestamp, doctorId, page, recordsPerPage, LocalStorage.currentShift); NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request); resultCountLabel.setText("Đang tải dữ liệu cho trang " + page + "..."); @@ -679,7 +690,7 @@ public class DataDialog extends JDialog { if (confirmation == JOptionPane.YES_OPTION) { try { long checkupId = Long.parseLong(checkupIdStr); - DeleteCheckupRequest deleteRequest = new DeleteCheckupRequest(checkupId); + DeleteCheckupRequest deleteRequest = new DeleteCheckupRequest(checkupId, LocalStorage.currentShift); NetworkUtil.sendPacket(ClientHandler.ctx.channel(), deleteRequest); } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(parentDialog, "Mã khám không hợp lệ: " + checkupIdStr, "Lỗi", JOptionPane.ERROR_MESSAGE); diff --git a/src/main/java/BsK/client/ui/component/DataDialog/DoctorManagementPanel.java b/src/main/java/BsK/client/ui/component/DataDialog/DoctorManagementPanel.java new file mode 100644 index 0000000..4c6d1b2 --- /dev/null +++ b/src/main/java/BsK/client/ui/component/DataDialog/DoctorManagementPanel.java @@ -0,0 +1,338 @@ +package BsK.client.ui.component.DataDialog; + +import BsK.client.LocalStorage; +import BsK.client.network.handler.ClientHandler; +import BsK.client.network.handler.ResponseListener; +import BsK.common.entity.Doctor; +import BsK.common.entity.DoctorItem; +import BsK.common.packet.req.AddDoctorRequest; +import BsK.common.packet.req.EditDoctorRequest; +import BsK.common.packet.req.GetDoctorInfoRequest; +import BsK.common.packet.res.GetDoctorInfoResponse; +import BsK.common.util.network.NetworkUtil; +import BsK.common.util.text.TextUtils; +import lombok.extern.slf4j.Slf4j; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.table.DefaultTableModel; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + + +@Slf4j +public class DoctorManagementPanel extends JPanel { + + private JTable doctorTable; + private DefaultTableModel doctorTableModel; + private JTextField doctorSearchField; + + private JTextField hoField; + private JTextField tenField; + private JCheckBox chkIsDeleted; + + private JButton btnAdd, btnEdit, btnClear; + + private List allDoctors = new ArrayList<>(); + private String selectedDoctorId = null; + + // --- Networking --- + private final ResponseListener getDoctorInfoResponseListener = this::handleGetDoctorInfoResponse; + + public DoctorManagementPanel() { + super(new BorderLayout(10, 10)); + initComponents(); + setupNetworking(); + } + + private void setupNetworking() { + ClientHandler.addResponseListener(GetDoctorInfoResponse.class, getDoctorInfoResponseListener); + NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetDoctorInfoRequest()); + } + + private void initComponents() { + this.setBorder(new EmptyBorder(15, 15, 15, 15)); + this.add(createDoctorInputPanel(), BorderLayout.WEST); + this.add(createDoctorListPanel(), BorderLayout.CENTER); + } + + private void handleGetDoctorInfoResponse(GetDoctorInfoResponse response) { + SwingUtilities.invokeLater(() -> { + if (response != null && response.getDoctors() != null) { + allDoctors = response.getDoctors(); + populateDoctorTable(); + + // Update LocalStorage.doctorsName with only non-deleted doctors + LocalStorage.doctorsName.clear(); + for (Doctor doctor : allDoctors) { + if ("0".equals(doctor.getDeleted())) { + LocalStorage.doctorsName.add(new DoctorItem( + doctor.getId(), + doctor.getFullName() + )); + } + } + log.info("Updated LocalStorage.doctorsName with {} active doctors", LocalStorage.doctorsName.size()); + } + }); + } + + private JPanel createDoctorInputPanel() { + JPanel mainInputPanel = new JPanel(new GridBagLayout()); + mainInputPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + GridBagConstraints mainGbc = new GridBagConstraints(); + mainGbc.fill = GridBagConstraints.HORIZONTAL; + mainGbc.anchor = GridBagConstraints.NORTHWEST; + mainGbc.insets = new Insets(5, 5, 5, 5); + mainGbc.weightx = 1.0; + + Font titleFont = new Font("Arial", Font.BOLD, 16); + Font labelFont = new Font("Arial", Font.BOLD, 15); + Font textFont = new Font("Arial", Font.PLAIN, 15); + Dimension textFieldSize = new Dimension(100, 30); + + JPanel doctorInfoPanel = new JPanel(new GridBagLayout()); + doctorInfoPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Thông tin bác sĩ", + TitledBorder.LEADING, TitledBorder.TOP, + titleFont, new Color(50, 50, 50) + )); + mainGbc.gridx = 0; + mainGbc.gridy = 0; + mainInputPanel.add(doctorInfoPanel, mainGbc); + + mainGbc.gridy = 1; + mainInputPanel.add(createDoctorButtonPanel(), mainGbc); + + mainGbc.gridy = 2; + mainGbc.weighty = 1.0; + mainInputPanel.add(new JPanel(), mainGbc); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(5, 5, 5, 5); + gbc.weightx = 0.0; + gbc.weighty = 0.0; + + gbc.gridy = 0; + gbc.gridx = 0; + JLabel hoLabel = new JLabel("Họ:"); + hoLabel.setFont(labelFont); + doctorInfoPanel.add(hoLabel, gbc); + + gbc.gridx = 1; + gbc.weightx = 1.0; + hoField = new JTextField(20); + hoField.setFont(textFont); + hoField.setPreferredSize(textFieldSize); + doctorInfoPanel.add(hoField, gbc); + + gbc.gridy++; + gbc.gridx = 0; + gbc.weightx = 0.0; + JLabel tenLabel = new JLabel("Tên:"); + tenLabel.setFont(labelFont); + doctorInfoPanel.add(tenLabel, gbc); + + gbc.gridx = 1; + gbc.weightx = 1.0; + tenField = new JTextField(10); + tenField.setFont(textFont); + tenField.setPreferredSize(textFieldSize); + doctorInfoPanel.add(tenField, gbc); + + return mainInputPanel; + } + + private JPanel createDoctorButtonPanel() { + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 5)); + btnAdd = new JButton("Thêm mới"); + btnEdit = new JButton("Chỉnh sửa"); + btnClear = new JButton("Làm mới"); + chkIsDeleted = new JCheckBox("Ẩn (Xoá)"); + chkIsDeleted.setFont(new Font("Arial", Font.BOLD, 13)); + + Dimension btnSize = new Dimension(100, 35); + btnAdd.setPreferredSize(btnSize); + btnEdit.setPreferredSize(btnSize); + btnClear.setPreferredSize(btnSize); + + buttonPanel.add(chkIsDeleted); + buttonPanel.add(btnAdd); + buttonPanel.add(btnEdit); + buttonPanel.add(btnClear); + + btnClear.addActionListener(e -> clearDoctorFields()); + + // --- ADD DOCTOR ACTION --- + btnAdd.addActionListener(e -> { + // 1. Validate required fields + String ho = hoField.getText().trim(); + String ten = tenField.getText().trim(); + + if (ho.isEmpty() || ten.isEmpty()) { + JOptionPane.showMessageDialog(this, "Họ và tên không được để trống.", "Lỗi", JOptionPane.ERROR_MESSAGE); + if (ho.isEmpty()) { + hoField.requestFocusInWindow(); + } else { + tenField.requestFocusInWindow(); + } + return; + } + + // 2. Create the request packet and send it to the server + AddDoctorRequest request = new AddDoctorRequest(ho, ten, false); + NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request); + + // 3. Provide feedback and reset the form + JOptionPane.showMessageDialog(this, "Yêu cầu thêm bác sĩ '" + ho + " " + ten + "' đã được gửi.", "Thành công", JOptionPane.INFORMATION_MESSAGE); + clearDoctorFields(); + }); + + // --- EDIT DOCTOR ACTION --- + btnEdit.addActionListener(e -> { + // 1. Check if a doctor has been selected + if (selectedDoctorId == null) { + JOptionPane.showMessageDialog(this, "Vui lòng chọn một bác sĩ để chỉnh sửa.", "Chưa chọn bác sĩ", JOptionPane.WARNING_MESSAGE); + return; + } + + // 2. Validate required fields + String ho = hoField.getText().trim(); + String ten = tenField.getText().trim(); + + if (ho.isEmpty() || ten.isEmpty()) { + JOptionPane.showMessageDialog(this, "Họ và tên không được để trống.", "Lỗi", JOptionPane.ERROR_MESSAGE); + if (ho.isEmpty()) { + hoField.requestFocusInWindow(); + } else { + tenField.requestFocusInWindow(); + } + return; + } + + // 3. Gather data from all fields + String id = selectedDoctorId; + Boolean isDeleted = chkIsDeleted.isSelected(); + + // 4. Create the request packet and send it + EditDoctorRequest request = new EditDoctorRequest(id, ho, ten, isDeleted); + NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request); + + // 5. Provide user feedback and clear the form + JOptionPane.showMessageDialog(this, "Yêu cầu chỉnh sửa bác sĩ '" + ho + " " + ten + "' đã được gửi.", "Thành công", JOptionPane.INFORMATION_MESSAGE); + clearDoctorFields(); + }); + + btnEdit.setEnabled(false); + chkIsDeleted.setEnabled(false); + + return buttonPanel; + } + + private JPanel createDoctorListPanel() { + JPanel listPanel = new JPanel(new BorderLayout(10, 10)); + listPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Danh Sách Bác Sĩ", + TitledBorder.LEFT, TitledBorder.TOP, new Font("Arial", Font.BOLD, 14))); + + String[] doctorColumns = {"ID", "Họ", "Tên", "Trạng thái"}; + doctorTableModel = new DefaultTableModel(doctorColumns, 0) { + @Override + public boolean isCellEditable(int row, int column) { return false; } + }; + doctorTable = new JTable(doctorTableModel); + doctorTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + doctorTable.setFont(new Font("Arial", Font.PLAIN, 12)); + doctorTable.setRowHeight(28); + + doctorTable.getSelectionModel().addListSelectionListener(e -> { + if (!e.getValueIsAdjusting() && doctorTable.getSelectedRow() != -1) { + int viewRow = doctorTable.getSelectedRow(); + String doctorId = doctorTableModel.getValueAt(viewRow, 0).toString(); + + allDoctors.stream() + .filter(doctor -> doctor.getId().equals(doctorId)) + .findFirst() + .ifPresent(this::populateDoctorFields); + } + }); + + JPanel searchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + searchPanel.add(new JLabel("Tìm kiếm bác sĩ:")); + doctorSearchField = new JTextField(25); + searchPanel.add(doctorSearchField); + + doctorSearchField.getDocument().addDocumentListener(new DocumentListener() { + @Override public void insertUpdate(DocumentEvent e) { filterDoctorTable(); } + @Override public void removeUpdate(DocumentEvent e) { filterDoctorTable(); } + @Override public void changedUpdate(DocumentEvent e) { filterDoctorTable(); } + }); + + listPanel.add(searchPanel, BorderLayout.NORTH); + listPanel.add(new JScrollPane(doctorTable), BorderLayout.CENTER); + + return listPanel; + } + + private void populateDoctorTable() { + filterDoctorTable(); + } + + private void populateDoctorFields(Doctor doctor) { + if (doctor == null) return; + selectedDoctorId = doctor.getId(); + + hoField.setText(doctor.getLastName()); + tenField.setText(doctor.getFirstName()); + chkIsDeleted.setSelected("1".equals(doctor.getDeleted())); + + btnAdd.setEnabled(false); + btnEdit.setEnabled(true); + chkIsDeleted.setEnabled(true); + } + + private void clearDoctorFields() { + selectedDoctorId = null; + hoField.setText(""); + tenField.setText(""); + chkIsDeleted.setSelected(false); + doctorTable.clearSelection(); + hoField.requestFocusInWindow(); + + btnAdd.setEnabled(true); + btnEdit.setEnabled(false); + chkIsDeleted.setEnabled(false); + } + + private void filterDoctorTable() { + String filterText = doctorSearchField.getText().trim(); + String lowerCaseFilterText = TextUtils.removeAccents(filterText.toLowerCase()); + + doctorTableModel.setRowCount(0); + + for (Doctor doctor : allDoctors) { + String fullName = doctor.getFullName(); + if (filterText.isEmpty() || TextUtils.removeAccents(fullName.toLowerCase()).contains(lowerCaseFilterText)) { + String status = "0".equals(doctor.getDeleted()) ? "Hoạt động" : "Đã ẩn"; + + doctorTableModel.addRow(new Object[]{ + doctor.getId(), + doctor.getLastName(), + doctor.getFirstName(), + status + }); + } + } + } + + public void cleanup() { + ClientHandler.deleteListener(GetDoctorInfoResponse.class, getDoctorInfoResponseListener); + } +} + diff --git a/src/main/java/BsK/client/ui/component/common/NavBar.java b/src/main/java/BsK/client/ui/component/common/NavBar.java index a8fd8fa..f1b2678 100644 --- a/src/main/java/BsK/client/ui/component/common/NavBar.java +++ b/src/main/java/BsK/client/ui/component/common/NavBar.java @@ -32,6 +32,11 @@ import java.awt.event.ActionListener; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Calendar; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Slf4j public class NavBar extends JPanel { @@ -281,6 +286,15 @@ public class NavBar extends JPanel { // --- END MODIFIED SEPARATOR --- } + String version = getClientVersion(); + if (!"N/A".equals(version)) { + JLabel versionLabel = new JLabel("Phiên bản: " + version); + versionLabel.setFont(new Font("Arial", Font.PLAIN, 10)); + versionLabel.setForeground(new Color(120, 120, 120)); + versionLabel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 0)); + navItemsPanel.add(versionLabel); + } + this.add(navItemsPanel); this.add(Box.createHorizontalGlue()); this.add(titlePanel); @@ -752,4 +766,18 @@ public class NavBar extends JPanel { ClientHandler.deleteListener(SetCounterResponse.class, setCounterResponseListener); // NEW: Clean up counter listener log.info("All NavBar listeners removed."); } + + private String getClientVersion() { + try { + String content = new String(Files.readAllBytes(Paths.get("version_client.json"))); + Pattern pattern = Pattern.compile("\"currentVersion\":\\s*\"([^\"]*)\""); + Matcher matcher = pattern.matcher(content); + if (matcher.find()) { + return matcher.group(1); + } + } catch (IOException e) { + log.error("Error reading client version file", e); + } + return "N/A"; + } } \ No newline at end of file diff --git a/src/main/java/BsK/common/entity/Doctor.java b/src/main/java/BsK/common/entity/Doctor.java new file mode 100644 index 0000000..ba7d85d --- /dev/null +++ b/src/main/java/BsK/common/entity/Doctor.java @@ -0,0 +1,49 @@ +package BsK.common.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Entity class for Doctor + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Doctor { + private String id; + private String lastName; + private String firstName; + private String deleted; // 0: active, 1: deleted + + /** + * Constructor to create Doctor from array data + * @param data Array of doctor data from backend [id, lastName, firstName, deleted] + */ + public Doctor(String[] data) { + if (data.length < 4) { + throw new IllegalArgumentException("Doctor data array must contain at least 4 elements"); + } + this.id = data[0]; + this.lastName = data[1]; + this.firstName = data[2]; + this.deleted = data[3]; + } + + /** + * Get the full name of the doctor + * @return Full name (lastName + firstName) + */ + public String getFullName() { + return (lastName + " " + firstName).trim(); + } + + /** + * Convert the Doctor entity to a String array + * @return String array representation of Doctor + */ + public String[] toStringArray() { + return new String[]{id, lastName, firstName, deleted}; + } +} + diff --git a/src/main/java/BsK/common/entity/DoctorItem.java b/src/main/java/BsK/common/entity/DoctorItem.java index 2d05ec3..9dcdb09 100644 --- a/src/main/java/BsK/common/entity/DoctorItem.java +++ b/src/main/java/BsK/common/entity/DoctorItem.java @@ -8,7 +8,7 @@ import lombok.Getter; public class DoctorItem { private String id; private String name; - + /** * This is the crucial part. JComboBox will use this method to display the * item in the dropdown. diff --git a/src/main/java/BsK/common/packet/req/AddDoctorRequest.java b/src/main/java/BsK/common/packet/req/AddDoctorRequest.java new file mode 100644 index 0000000..d96d160 --- /dev/null +++ b/src/main/java/BsK/common/packet/req/AddDoctorRequest.java @@ -0,0 +1,13 @@ +package BsK.common.packet.req; + +import BsK.common.packet.Packet; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class AddDoctorRequest implements Packet { + private String lastName; + private String firstName; + private Boolean deleted; +} diff --git a/src/main/java/BsK/common/packet/req/EditDoctorRequest.java b/src/main/java/BsK/common/packet/req/EditDoctorRequest.java new file mode 100644 index 0000000..bcf2cbc --- /dev/null +++ b/src/main/java/BsK/common/packet/req/EditDoctorRequest.java @@ -0,0 +1,14 @@ +package BsK.common.packet.req; + +import BsK.common.packet.Packet; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class EditDoctorRequest implements Packet { + private String id; + private String lastName; + private String firstName; + private Boolean deleted; +} diff --git a/src/main/java/BsK/common/packet/req/GetCheckupDataRequest.java b/src/main/java/BsK/common/packet/req/GetCheckupDataRequest.java index a78e6bc..f0aa474 100644 --- a/src/main/java/BsK/common/packet/req/GetCheckupDataRequest.java +++ b/src/main/java/BsK/common/packet/req/GetCheckupDataRequest.java @@ -16,4 +16,5 @@ public class GetCheckupDataRequest implements Packet { private Integer doctorId; private int page = 1; private int pageSize = 20; + private int shift; } \ No newline at end of file diff --git a/src/main/java/BsK/common/packet/req/GetDoctorInfoRequest.java b/src/main/java/BsK/common/packet/req/GetDoctorInfoRequest.java new file mode 100644 index 0000000..3b71cfb --- /dev/null +++ b/src/main/java/BsK/common/packet/req/GetDoctorInfoRequest.java @@ -0,0 +1,11 @@ +package BsK.common.packet.req; + +import BsK.common.packet.Packet; +import lombok.AllArgsConstructor; +import lombok.Data; + +@AllArgsConstructor +@Data +public class GetDoctorInfoRequest implements Packet { +} + diff --git a/src/main/java/BsK/common/packet/req/LoginRequest.java b/src/main/java/BsK/common/packet/req/LoginRequest.java index 58c35a2..4f20fd3 100644 --- a/src/main/java/BsK/common/packet/req/LoginRequest.java +++ b/src/main/java/BsK/common/packet/req/LoginRequest.java @@ -12,4 +12,5 @@ import java.time.LocalDateTime; public class LoginRequest implements Packet { String username; String password; + int shift; } \ No newline at end of file diff --git a/src/main/java/BsK/common/packet/res/GetDoctorInfoResponse.java b/src/main/java/BsK/common/packet/res/GetDoctorInfoResponse.java new file mode 100644 index 0000000..45df09f --- /dev/null +++ b/src/main/java/BsK/common/packet/res/GetDoctorInfoResponse.java @@ -0,0 +1,50 @@ +package BsK.common.packet.res; + +import BsK.common.entity.Doctor; +import BsK.common.packet.Packet; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +@AllArgsConstructor +public class GetDoctorInfoResponse implements Packet { + private String[][] doctorInfo; + private transient List doctors; + + public GetDoctorInfoResponse(String[][] doctorInfo) { + this.doctorInfo = doctorInfo; + this.doctors = convertToDoctorList(doctorInfo); + } + + /** + * Converts the raw string array data to a list of Doctor objects + * @param doctorInfo Raw doctor data as string arrays + * @return List of Doctor objects + */ + private List convertToDoctorList(String[][] doctorInfo) { + List result = new ArrayList<>(); + if (doctorInfo != null) { + for (String[] data : doctorInfo) { + if (data.length >= 4) { + result.add(new Doctor(data)); + } + } + } + return result; + } + + /** + * Returns the doctor list, converting from raw data if needed + * @return List of Doctor objects + */ + public List getDoctors() { + if (doctors == null && doctorInfo != null) { + doctors = convertToDoctorList(doctorInfo); + } + return doctors; + } +} + diff --git a/src/main/java/BsK/server/ServerDashboard.java b/src/main/java/BsK/server/ServerDashboard.java index 67f2b73..d127837 100644 --- a/src/main/java/BsK/server/ServerDashboard.java +++ b/src/main/java/BsK/server/ServerDashboard.java @@ -7,12 +7,17 @@ import javax.swing.text.*; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; +import java.io.IOException; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.OperatingSystemMXBean; +import java.nio.file.Files; +import java.nio.file.Paths; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import BsK.server.network.entity.ClientConnection; import BsK.server.network.manager.SessionManager; import lombok.extern.slf4j.Slf4j; @@ -55,7 +60,12 @@ public class ServerDashboard extends JFrame { } private ServerDashboard() { - setTitle("Bảng Điều Khiển Máy Chủ BSK"); + String version = getServerVersion(); + String title = "Bảng Điều Khiển Máy Chủ BSK"; + if (!"N/A".equals(version)) { + title += " - Phiên bản " + version; + } + setTitle(title); setSize(1400, 900); setLocationRelativeTo(null); @@ -650,5 +660,19 @@ public class ServerDashboard extends JFrame { } // If the user chooses "No", the dialog closes and nothing else happens. } + + private String getServerVersion() { + try { + String content = new String(Files.readAllBytes(Paths.get("version_server.json"))); + Pattern pattern = Pattern.compile("\"currentVersion\":\\s*\"([^\"]*)\""); + Matcher matcher = pattern.matcher(content); + if (matcher.find()) { + return matcher.group(1); + } + } catch (IOException e) { + log.error("Error reading server version file", e); + } + return "N/A"; + } // --- MODIFICATION END --- } \ No newline at end of file diff --git a/src/main/java/BsK/server/network/handler/ServerHandler.java b/src/main/java/BsK/server/network/handler/ServerHandler.java index f5ce101..ca6f4ea 100644 --- a/src/main/java/BsK/server/network/handler/ServerHandler.java +++ b/src/main/java/BsK/server/network/handler/ServerHandler.java @@ -1868,7 +1868,7 @@ public class ServerHandler extends SimpleChannelInboundHandler 0) { + log.info("Updated doctor successfully: {} {}", editDoctorRequest.getLastName(), editDoctorRequest.getFirstName()); + getDoctorInfo(currentUser.getSessionId()); + } else { + log.warn("No doctor found with ID {} to update.", editDoctorRequest.getId()); + } + + } catch (SQLException e) { + log.error("Error updating doctor", e); + UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION)); + } + } + if (packet instanceof GetAllUserInfoRequest) { if (!currentUser.getRole().equals(Role.ADMIN)) { UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.ACCESS_DENIED)); @@ -2159,6 +2219,55 @@ public class ServerHandler extends SimpleChannelInboundHandler resultList = new ArrayList<>(); + while (rs.next()) { + String doctorId = rs.getString("doctor_id"); + String doctorLastName = rs.getString("doctor_last_name"); + String doctorFirstName = rs.getString("doctor_first_name"); + + // Lấy giá trị boolean một cách an toàn và chuyển thành String "1" hoặc "0" + String deleted = rs.getBoolean("deleted") ? "1" : "0"; + + String result = String.join("|", + doctorId, + doctorLastName != null ? doctorLastName : "", + doctorFirstName != null ? doctorFirstName : "", + deleted + ); + resultList.add(result); + } + + String[] resultString = resultList.toArray(new String[0]); + String[][] resultArray = new String[resultString.length][]; + for (int i = 0; i < resultString.length; i++) { + resultArray[i] = resultString[i].split("\\|", -1); + } + + UserUtil.sendPacket(sessionId, new GetDoctorInfoResponse(resultArray)); + + } catch (SQLException e) { + log.error("Error fetching doctors", e); + UserUtil.sendPacket(sessionId, new ErrorResponse(Error.SQL_EXCEPTION)); + } + } + /** * Lấy danh sách bệnh nhân cần tái khám trong vòng 7 ngày tới và gửi cho một session cụ thể. * diff --git a/src/main/resources/print_forms/ultrasoundresult.jrxml b/src/main/resources/print_forms/ultrasoundresult.jrxml index ca92b09..f289b14 100644 --- a/src/main/resources/print_forms/ultrasoundresult.jrxml +++ b/src/main/resources/print_forms/ultrasoundresult.jrxml @@ -66,13 +66,13 @@ - - + + - + @@ -102,9 +102,6 @@ - - - @@ -114,9 +111,6 @@ - - - @@ -258,7 +252,7 @@ - + diff --git a/src/main/resources/print_forms/ultrasoundresult_potrait.jrxml b/src/main/resources/print_forms/ultrasoundresult_potrait.jrxml index 2b075a9..30d18cf 100644 --- a/src/main/resources/print_forms/ultrasoundresult_potrait.jrxml +++ b/src/main/resources/print_forms/ultrasoundresult_potrait.jrxml @@ -71,13 +71,13 @@ - + - + @@ -89,7 +89,7 @@ - + @@ -107,9 +107,6 @@ - - - @@ -119,9 +116,6 @@ - - - @@ -191,7 +185,7 @@ - +