diff --git a/ANH SIEU AM/New folder (2)/eed78d50-3b49-4dd9-ab8e-12212e7481fd.jpg b/ANH SIEU AM/New folder (2)/eed78d50-3b49-4dd9-ab8e-12212e7481fd.jpg deleted file mode 100644 index 79253f8..0000000 Binary files a/ANH SIEU AM/New folder (2)/eed78d50-3b49-4dd9-ab8e-12212e7481fd.jpg and /dev/null differ diff --git a/ANH SIEU AM/New folder - Copy (2)/desktop.ini b/ANH SIEU AM/New folder - Copy (2)/desktop.ini new file mode 100644 index 0000000..0a32a59 --- /dev/null +++ b/ANH SIEU AM/New folder - Copy (2)/desktop.ini @@ -0,0 +1,2 @@ +[LocalizedFileNames] +faker.jpg=@faker,0 diff --git a/ANH SIEU AM/New folder - Copy/033569a5-03d2-4aeb-8ac0-cfae61c46df2.jpg b/ANH SIEU AM/New folder - Copy/033569a5-03d2-4aeb-8ac0-cfae61c46df2.jpg deleted file mode 100644 index fcbdcee..0000000 Binary files a/ANH SIEU AM/New folder - Copy/033569a5-03d2-4aeb-8ac0-cfae61c46df2.jpg and /dev/null differ diff --git a/ANH SIEU AM/desktop.ini b/ANH SIEU AM/desktop.ini new file mode 100644 index 0000000..0a32a59 --- /dev/null +++ b/ANH SIEU AM/desktop.ini @@ -0,0 +1,2 @@ +[LocalizedFileNames] +faker.jpg=@faker,0 diff --git a/database/BSK.db-shm b/database/BSK.db-shm index e2152e3..6c77a27 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 25e98eb..1146c84 100644 Binary files a/database/BSK.db-wal and b/database/BSK.db-wal differ diff --git a/image/checkup_media/35/ultrasound_35_20250808_140245_316.jpg b/image/checkup_media/35/ultrasound_35_20250808_140245_316.jpg new file mode 100644 index 0000000..a97e3e7 Binary files /dev/null and b/image/checkup_media/35/ultrasound_35_20250808_140245_316.jpg differ diff --git a/image/checkup_media/35/ultrasound_35_20250808_140250_016.jpg b/image/checkup_media/35/ultrasound_35_20250808_140250_016.jpg new file mode 100644 index 0000000..a97e3e7 Binary files /dev/null and b/image/checkup_media/35/ultrasound_35_20250808_140250_016.jpg differ diff --git a/image/checkup_media/35/ultrasound_35_20250808_140255_728.jpg b/image/checkup_media/35/ultrasound_35_20250808_140255_728.jpg new file mode 100644 index 0000000..44ebb1f Binary files /dev/null and b/image/checkup_media/35/ultrasound_35_20250808_140255_728.jpg differ diff --git a/image/checkup_media/42/ultrasound_42_20250809_085730_260.jpg b/image/checkup_media/42/ultrasound_42_20250809_085730_260.jpg new file mode 100644 index 0000000..fdf906b Binary files /dev/null and b/image/checkup_media/42/ultrasound_42_20250809_085730_260.jpg differ diff --git a/image/checkup_media/42/ultrasound_42_20250809_085733_924.jpg b/image/checkup_media/42/ultrasound_42_20250809_085733_924.jpg new file mode 100644 index 0000000..fdf906b Binary files /dev/null and b/image/checkup_media/42/ultrasound_42_20250809_085733_924.jpg differ diff --git a/image/checkup_media/42/ultrasound_42_20250809_085741_135.jpg b/image/checkup_media/42/ultrasound_42_20250809_085741_135.jpg new file mode 100644 index 0000000..fdf906b Binary files /dev/null and b/image/checkup_media/42/ultrasound_42_20250809_085741_135.jpg differ diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java index 3ac4e5a..c91c8fa 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java @@ -147,7 +147,7 @@ public class CheckUpPage extends JPanel { private ScheduledExecutorService folderScanExecutor; private volatile boolean isScanning = false; private static final int SCAN_INTERVAL_MILLISECONDS = 500; // Check every 1 - private static final int MAX_FOLDERS_TO_SCAN = 4; + private static final int MAX_FOLDERS_TO_SCAN = 12; private final Set processingFiles = ConcurrentHashMap.newKeySet(); private final Map lastSeenFiles = new ConcurrentHashMap<>(); @@ -398,7 +398,14 @@ public class CheckUpPage extends JPanel { openQueueButton.setBorder(BorderFactory.createEmptyBorder(8, 15, 8, 15)); // Reduced vertical padding from 10 to 8 openQueueButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); openQueueButton.addActionListener(e -> { + // Always fetch the latest queue to avoid cross-client mismatch updateQueue(); + // Reset filters to "Tất cả" to avoid index/filter/sort mismatch on open + if (queueManagementPage != null) { + queueManagementPage.checkupTypeFilter.setSelectedItem("Tất cả"); + queueManagementPage.patientNameFilter.setText(""); + queueManagementPage.applyFilters(); + } queueManagementPage.setVisible(true); queueManagementPage.toFront(); }); @@ -1914,13 +1921,7 @@ public class CheckUpPage extends JPanel { break; case "ultrasound": // This case now handles "Lưu & In" - // Step 1: Validate conditions first. - if (selectedImagesForPrint.isEmpty()) { - JOptionPane.showMessageDialog(this, - "Vui lòng chọn ít nhất một hình ảnh để in kết quả.", - "Chưa chọn ảnh", JOptionPane.WARNING_MESSAGE); - return; - } + if (photoNum != 0 && selectedImagesForPrint.size() > photoNum) { JOptionPane.showMessageDialog(this, "Số lượng ảnh đã chọn (" + selectedImagesForPrint.size() + ") nhiều hơn số lượng cho phép trong mẫu (" + photoNum + ").", @@ -1995,13 +1996,7 @@ public class CheckUpPage extends JPanel { afterSaveActions(statusToSavePrint, "Đã lưu. Đang gửi lệnh in..."); break; case "loupe": // This case now handles "Lưu & Xem" - // Step 1: Validate conditions first. - if (selectedImagesForPrint.isEmpty()) { - JOptionPane.showMessageDialog(this, - "Vui lòng chọn ít nhất một hình ảnh để xem kết quả.", - "Chưa chọn ảnh", JOptionPane.WARNING_MESSAGE); - return; - } + if (photoNum != 0 && selectedImagesForPrint.size() > photoNum) { JOptionPane.showMessageDialog(this, "Số lượng ảnh đã chọn (" + selectedImagesForPrint.size() + ") nhiều hơn số lượng cho phép trong mẫu (" + photoNum + ").", @@ -2729,10 +2724,8 @@ public class CheckUpPage extends JPanel { private JPanel createImageDisplayPanel() { JPanel panel = new JPanel(new BorderLayout(5, 5)); - panel.setBorder(BorderFactory.createTitledBorder( - BorderFactory.createEtchedBorder(), "Thư viện Hình ảnh", - TitledBorder.CENTER, TitledBorder.TOP, - new Font("Arial", Font.ITALIC, 14), new Color(50, 50, 50))); + // Remove inner title to avoid duplicate "Thư viện Hình ảnh" header and save vertical space + panel.setBorder(BorderFactory.createEmptyBorder()); imageGalleryPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 10)); // Added more spacing imageGalleryPanel.setBackground(Color.WHITE); // Set a background for the gallery @@ -3192,14 +3185,14 @@ public class CheckUpPage extends JPanel { return Collections.emptyList(); } - // Sort by creation time (newest first) + // Sort by last modified time (newest first) folders.sort((path1, path2) -> { try { - FileTime time1 = Files.readAttributes(path1, BasicFileAttributes.class).creationTime(); - FileTime time2 = Files.readAttributes(path2, BasicFileAttributes.class).creationTime(); - return time2.compareTo(time1); // Newest first + FileTime time1 = Files.getLastModifiedTime(path1); + FileTime time2 = Files.getLastModifiedTime(path2); + return time2.compareTo(time1); } catch (Exception e) { - log.warn("Error comparing creation times for {} and {}: {}", path1, path2, e.getMessage()); + log.warn("Error comparing last modified times for {} and {}: {}", path1, path2, e.getMessage()); return 0; } }); @@ -4303,8 +4296,13 @@ public class CheckUpPage extends JPanel { return; } } - // Call the parent class's method to handle the selection with the model row index - CheckUpPage.this.handleRowSelection(modelRow, null); + // Resolve the selected patient by Checkup ID to avoid index mismatch after filtering/sorting + Patient selected = patientQueue.stream() + .filter(p -> newlySelectedCheckupId.equals(p.getCheckupId())) + .findFirst().orElse(null); + if (selected != null) { + CheckUpPage.this.handleRowSelection(-1, selected); + } // Reset patient name filter when closing dialog after selection patientNameFilter.setText(""); applyFilters(); @@ -4353,8 +4351,13 @@ public class CheckUpPage extends JPanel { return; } } - // Call the parent class's method to handle the selection with the model row index - CheckUpPage.this.handleRowSelection(modelRow, null); + // Resolve patient by Checkup ID to avoid index mismatch + Patient selected = patientQueue.stream() + .filter(p -> newlySelectedCheckupId.equals(p.getCheckupId())) + .findFirst().orElse(null); + if (selected != null) { + CheckUpPage.this.handleRowSelection(-1, selected); + } // Reset patient name filter when closing dialog after selection patientNameFilter.setText(""); applyFilters(); @@ -4444,9 +4447,12 @@ public class CheckUpPage extends JPanel { // 1. Filter your master list into a temporary list filteredPatients = patientQueue.stream() .filter(patient -> { - // ... (your existing filter logic remains the same) ... + // Checkup type filter with normalized comparison (accent-insensitive, case-insensitive) if (!"Tất cả".equals(selectedCheckupType)) { - if (!selectedCheckupType.equals(patient.getCheckupType())) { + String normalizedSelected = TextUtils.removeAccents(selectedCheckupType).trim().toUpperCase(); + String patientType = patient.getCheckupType(); + String normalizedPatientType = TextUtils.removeAccents(patientType == null ? "" : patientType).trim().toUpperCase(); + if (!normalizedSelected.equals(normalizedPatientType)) { return false; } } @@ -4522,17 +4528,45 @@ public class CheckUpPage extends JPanel { queueTable.scrollRectToVisible(queueTable.getCellRect(viewRow, 0, true)); } } else { - JOptionPane.showMessageDialog( - mainFrame, - "Trạng thái của bệnh nhân bạn đang chọn đã thay đổi và không còn trong hàng chờ.\n" + - "Giao diện của bạn sẽ được làm mới.", - "Cập Nhật Trạng Thái Bệnh Nhân", - JOptionPane.INFORMATION_MESSAGE - ); - - clearPatientDetails(); // Now clear the details - if (queueTable.getRowCount() > 0) { - queueTable.setRowSelectionInterval(0, 0); + // Not visible under current filters. Check if the patient still exists in the full queue. + Patient target = patientQueue.stream() + .filter(p -> previouslySelectedId.equals(p.getCheckupId())) + .findFirst().orElse(null); + if (target != null) { + // Adjust filters to include the target patient's type and re-apply + if (checkupTypeFilter != null) { + String type = target.getCheckupType(); + checkupTypeFilter.setSelectedItem(type == null || type.isEmpty() ? "Tất cả" : type); + } + if (patientNameFilter != null) { + patientNameFilter.setText(""); + } + applyFilters(); + int rowAgain = findRowByCheckupId(previouslySelectedId); + if (rowAgain != -1) { + int viewRow = queueTable.convertRowIndexToView(rowAgain); + if (viewRow != -1) { + queueTable.setRowSelectionInterval(viewRow, viewRow); + queueTable.scrollRectToVisible(queueTable.getCellRect(viewRow, 0, true)); + } + } else if (queueTable.getRowCount() > 0) { + // Fall back to first row without showing a misleading status-change message + queueTable.setRowSelectionInterval(0, 0); + } + } else { + // Patient truly no longer in queue; show message and clear + JOptionPane.showMessageDialog( + mainFrame, + "Trạng thái của bệnh nhân bạn đang chọn đã thay đổi và không còn trong hàng chờ.\n" + + "Giao diện của bạn sẽ được làm mới.", + "Cập Nhật Trạng Thái Bệnh Nhân", + JOptionPane.INFORMATION_MESSAGE + ); + + clearPatientDetails(); // Now clear the details + if (queueTable.getRowCount() > 0) { + queueTable.setRowSelectionInterval(0, 0); + } } } } else { diff --git a/src/main/java/BsK/client/ui/component/common/AddDialog/AddDialog.java b/src/main/java/BsK/client/ui/component/common/AddDialog/AddDialog.java index 3c348aa..1988e49 100644 --- a/src/main/java/BsK/client/ui/component/common/AddDialog/AddDialog.java +++ b/src/main/java/BsK/client/ui/component/common/AddDialog/AddDialog.java @@ -248,12 +248,12 @@ public class AddDialog extends JDialog { setLocationRelativeTo(parent); setResizable(true); - // UI Fonts and Dimensions - Font labelFont = new Font("Arial", Font.BOLD, 15); - Font textFont = new Font("Arial", Font.PLAIN, 15); - Font titleFont = new Font("Arial", Font.BOLD, 16); - Dimension textFieldSize = new Dimension(100, 30); - Dimension comboBoxSize = new Dimension(100, 30); + // UI Fonts and Dimensions (labels bigger, fields smaller) + Font labelFont = new Font("Arial", Font.BOLD, 18); + Font textFont = new Font("Arial", Font.PLAIN, 14); + Font titleFont = new Font("Arial", Font.BOLD, 18); + Dimension textFieldSize = new Dimension(100, 26); + Dimension comboBoxSize = new Dimension(100, 26); // Ensure modal behavior and proper parent relationship setModalityType(Dialog.ModalityType.APPLICATION_MODAL); @@ -436,7 +436,7 @@ public class AddDialog extends JDialog { dobPicker.getJFormattedTextField().setFont(textFont); dobPicker.getJFormattedTextField().setToolTipText("Nhập ngày theo định dạng dd/mm/yyyy"); setupDateFieldForDirectInput(dobPicker); - dobPicker.setPreferredSize(new Dimension(150, 30)); + dobPicker.setPreferredSize(new Dimension(140, 26)); patientInfoPanel.add(dobPicker, gbc); gbc.gridx = 2; @@ -571,15 +571,15 @@ public class AddDialog extends JDialog { gbc.anchor = GridBagConstraints.EAST; gbc.gridy++; // Move to a new row - gbc.gridx = 0; + gbc.gridx = 2; gbc.gridwidth = 2; // Span 2 columns specifyQueueNumberCheckBox = new JCheckBox("Chỉ định STT"); specifyQueueNumberCheckBox.setFont(labelFont); checkupInfoPanel.add(specifyQueueNumberCheckBox, gbc); gbc.anchor = GridBagConstraints.WEST; - gbc.gridx = 2; - gbc.gridwidth = 2; // Span the other 2 columns + gbc.gridx = 1; + gbc.gridwidth = 1; // Span the other 2 columns SpinnerNumberModel queueSpinnerModel = new SpinnerNumberModel(1, 1, 999, 1); // (defaultValue, min, max, step) queueNumberSpinner = new JSpinner(queueSpinnerModel); queueNumberSpinner.setFont(textFont); @@ -636,9 +636,13 @@ public class AddDialog extends JDialog { } log.info("Sending AddCheckupRequest with queue number: {}", queueNumber); NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new AddCheckupRequest(patientId, doctorId, LocalStorage.userId, selectedCheckupType, "CHỜ KHÁM", queueNumber)); + clearButton.doClick(); }); checkupInfoPanel.add(saveButton, gbc); + // Initially disable checkup controls until a patient is selected + setCheckupControlsEnabled(false); + // Reset insets and weights gbc.insets = new Insets(5, 5, 5, 5); gbc.weightx = 0.0; @@ -717,7 +721,8 @@ public class AddDialog extends JDialog { } }); - inputPanel.setMinimumSize(new Dimension(400, 0)); + // Make the left panel narrower + inputPanel.setMinimumSize(new Dimension(300, 0)); // Wrap inputPanel in a JScrollPane JScrollPane inputScrollPane = new JScrollPane(inputPanel); @@ -752,7 +757,9 @@ public class AddDialog extends JDialog { rightPanel.add(paginationPanel, BorderLayout.SOUTH); JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, inputScrollPane, rightPanel); - splitPane.setResizeWeight(0.5); // Keep or adjust as needed + // Favor the right panel; set an initial divider position and resize weight + splitPane.setDividerLocation(0.25); + splitPane.setResizeWeight(0.2); add(splitPane, BorderLayout.CENTER); @@ -873,6 +880,7 @@ public class AddDialog extends JDialog { // Update button states saveButton.setEnabled(false); addPatientButton.setEnabled(true); + setCheckupControlsEnabled(false); setDefaultLocation(); // Reset table to show all patients sendGetRecentPatientRequest(); @@ -1189,5 +1197,28 @@ private void setupDebounceSearch() { // Enable the save button only if the patient ID field is not empty. boolean isPatientSelected = !patientIdField.getText().trim().isEmpty(); saveButton.setEnabled(isPatientSelected); + setCheckupControlsEnabled(isPatientSelected); + } + + private void setCheckupControlsEnabled(boolean enabled) { + // Guard against initialization order + if (doctorComboBox != null) { + doctorComboBox.setEnabled(enabled); + } + if (checkupTypeComboBox != null) { + checkupTypeComboBox.setEnabled(enabled); + } + if (specifyQueueNumberCheckBox != null) { + specifyQueueNumberCheckBox.setEnabled(enabled); + } + if (queueNumberSpinner != null) { + // Spinner enabled only when both the section is enabled and the checkbox is selected + boolean spinnerEnabled = enabled && specifyQueueNumberCheckBox != null && specifyQueueNumberCheckBox.isSelected(); + queueNumberSpinner.setEnabled(spinnerEnabled); + } + if (saveButton != null) { + // Keep saveButton consistent with the enabled state as well + saveButton.setEnabled(enabled); + } } } diff --git a/src/main/java/BsK/client/ui/component/common/TemplateDialog/TemplateDialog.java b/src/main/java/BsK/client/ui/component/common/TemplateDialog/TemplateDialog.java index 2384e1a..e530f08 100644 --- a/src/main/java/BsK/client/ui/component/common/TemplateDialog/TemplateDialog.java +++ b/src/main/java/BsK/client/ui/component/common/TemplateDialog/TemplateDialog.java @@ -158,7 +158,7 @@ public class TemplateDialog extends JDialog { diagnosisArea.setLineWrap(true); diagnosisArea.setWrapStyleWord(true); - imageCountComboBox = new JComboBox<>(new String[]{"1", "2", "3", "4"}); + imageCountComboBox = new JComboBox<>(new String[]{"0", "1", "2", "3", "4"}); printTypeComboBox = new JComboBox<>(new String[]{"Ngang", "Dọc"}); sttField = new JTextField(10); diff --git a/src/main/java/BsK/server/network/handler/ServerHandler.java b/src/main/java/BsK/server/network/handler/ServerHandler.java index b8f136c..1a8cf72 100644 --- a/src/main/java/BsK/server/network/handler/ServerHandler.java +++ b/src/main/java/BsK/server/network/handler/ServerHandler.java @@ -618,7 +618,7 @@ public class ServerHandler extends SimpleChannelInboundHandler