diff --git a/logs/bsk_server-2.log.gz b/logs/bsk_server-2.log.gz new file mode 100644 index 0000000..49937ce Binary files /dev/null and b/logs/bsk_server-2.log.gz 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 8506449..37b27bc 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java @@ -41,6 +41,11 @@ import java.text.SimpleDateFormat; import java.text.ParseException; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; +import javax.imageio.ImageIO; +import javax.imageio.ImageWriter; +import javax.imageio.ImageWriteParam; +import javax.imageio.IIOImage; +import javax.imageio.stream.ImageOutputStream; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -119,6 +124,7 @@ import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; import java.io.OutputStream; import java.io.InputStream; +import java.io.StringReader; // --- JasperReports Imports --- import net.sf.jasperreports.engine.JasperPrint; @@ -169,8 +175,11 @@ public class CheckUpPage extends JPanel { private JTextArea suggestionField, diagnosisField, conclusionField; // Changed symptomsField to suggestionField private JTextPane notesField; private JComboBox doctorComboBox, statusComboBox, genderComboBox, provinceComboBox, districtComboBox, wardComboBox, checkupTypeComboBox, templateComboBox, orientationComboBox; // Added orientationComboBox + private JComboBox ultrasoundDoctorComboBox; + private JCheckBox needRecheckupCheckbox; // Checkbox to indicate if re-checkup is needed private JSpinner customerWeightSpinner, customerHeightSpinner, patientHeartRateSpinner, bloodPressureSystolicSpinner, bloodPressureDiastolicSpinner; private JDatePickerImpl datePicker, dobPicker, recheckupDatePicker; + private JButton recheckupDatePickerButton; private String[][] medicinePrescription = new String[0][0]; // Initialize to empty private String[][] servicePrescription = new String[0][0]; // Initialize to empty private String[] doctorOptions; @@ -861,7 +870,7 @@ public class CheckUpPage extends JPanel { // Top Row Panel (Doctor, Status, Type) JPanel topRowPanel = new JPanel(new GridBagLayout()); GridBagConstraints gbcTop = new GridBagConstraints(); - gbcTop.insets = new Insets(3, 5, 3, 5); // Reduced vertical insets from 5 to 3 + gbcTop.insets = new Insets(1, 5, 1, 5); // Reduced vertical insets for density gbcTop.fill = GridBagConstraints.HORIZONTAL; // Initialize datePicker @@ -878,43 +887,59 @@ public class CheckUpPage extends JPanel { setupDateFieldForDirectInput(datePicker); gbcTop.gridx = 0; gbcTop.gridy = 0; - JLabel doctorLabel = new JLabel("Bác Sĩ"); - doctorLabel.setFont(labelFont); - topRowPanel.add(doctorLabel, gbcTop); - - gbcTop.gridx = 1; gbcTop.weightx = 0.4; - doctorComboBox = new JComboBox<>(LocalStorage.doctorsName != null ? LocalStorage.doctorsName : new String[]{"Đang tải bác sĩ..."}); - doctorComboBox.setFont(fieldFont); - topRowPanel.add(doctorComboBox, gbcTop); - - gbcTop.gridx = 2; gbcTop.weightx = 0; JLabel dateLabel = new JLabel("Đơn Ngày:"); dateLabel.setFont(labelFont); topRowPanel.add(dateLabel, gbcTop); - gbcTop.gridx = 3; gbcTop.weightx = 0.3; + gbcTop.gridx = 1; gbcTop.weightx = 0.3; topRowPanel.add(datePicker, gbcTop); - gbcTop.gridx = 4; gbcTop.weightx = 0; + gbcTop.gridx = 2; gbcTop.weightx = 0; JLabel statusLabel = new JLabel("Trạng thái"); statusLabel.setFont(labelFont); topRowPanel.add(statusLabel, gbcTop); - gbcTop.gridx = 5; gbcTop.weightx = 0.3; + gbcTop.gridx = 3; gbcTop.weightx = 0.3; statusComboBox = new JComboBox<>(new String[]{"ĐANG KHÁM", "CHỜ KHÁM", "ĐÃ KHÁM"}); statusComboBox.setFont(fieldFont); topRowPanel.add(statusComboBox, gbcTop); - gbcTop.gridx = 6; gbcTop.weightx = 0; + gbcTop.gridx = 4; gbcTop.weightx = 0; JLabel typeLabel = new JLabel("Loại khám"); typeLabel.setFont(labelFont); topRowPanel.add(typeLabel, gbcTop); - gbcTop.gridx = 7; gbcTop.weightx = 0.3; + gbcTop.gridx = 5; gbcTop.weightx = 0.3; checkupTypeComboBox = new JComboBox<>(new String[]{"BỆNH", "THAI", "KHÁC"}); checkupTypeComboBox.setFont(fieldFont); topRowPanel.add(checkupTypeComboBox, gbcTop); + // Doctor Selection Row + JPanel doctorPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbcDoctor = new GridBagConstraints(); + gbcDoctor.insets = new Insets(1, 5, 1, 5); // Reduced vertical insets for density + gbcDoctor.fill = GridBagConstraints.HORIZONTAL; + + gbcDoctor.gridx = 0; gbcDoctor.gridy = 0; + JLabel doctorLabel = new JLabel("Bác sĩ chỉ định"); + doctorLabel.setFont(labelFont); + doctorPanel.add(doctorLabel, gbcDoctor); + + gbcDoctor.gridx = 1; gbcDoctor.weightx = 0.4; + doctorComboBox = new JComboBox<>(LocalStorage.doctorsName != null ? LocalStorage.doctorsName : new String[]{"Đang tải bác sĩ..."}); + doctorComboBox.setFont(fieldFont); + doctorPanel.add(doctorComboBox, gbcDoctor); + + gbcDoctor.gridx = 2; gbcDoctor.weightx = 0; + JLabel ultrasoundDoctorLabel = new JLabel("Bác sĩ siêu âm"); + ultrasoundDoctorLabel.setFont(labelFont); + doctorPanel.add(ultrasoundDoctorLabel, gbcDoctor); + + gbcDoctor.gridx = 3; gbcDoctor.weightx = 0.4; + ultrasoundDoctorComboBox = new JComboBox<>(LocalStorage.doctorsName != null ? LocalStorage.doctorsName : new String[]{"Đang tải bác sĩ..."}); + ultrasoundDoctorComboBox.setFont(fieldFont); + doctorPanel.add(ultrasoundDoctorComboBox, gbcDoctor); + // Template Selection Row JPanel templatePanel = new JPanel(new GridBagLayout()); GridBagConstraints gbcTemplate = new GridBagConstraints(); @@ -972,7 +997,7 @@ public class CheckUpPage extends JPanel { templatePanel.add(orientationLabel, gbcTemplate); gbcTemplate.gridx = 7; - orientationComboBox = new JComboBox<>(new String[]{"ngang", "dọc"}); + orientationComboBox = new JComboBox<>(new String[]{"Ngang", "Dọc"}); orientationComboBox.setFont(fieldFont); orientationComboBox.setPreferredSize(new Dimension(80, 25)); orientationComboBox.addActionListener(e -> { @@ -1171,32 +1196,56 @@ public class CheckUpPage extends JPanel { rightPanel.setMinimumSize(new Dimension(200, 0)); // Set minimum size to prevent it from shrinking too much rightPanel.setMaximumSize(new Dimension(280, Integer.MAX_VALUE)); // Set maximum width to limit expansion - // Suggestion Panel - JPanel suggestionPanel = new JPanel(new BorderLayout(5, 5)); + // Suggestion Panel - more compact + JPanel suggestionPanel = new JPanel(new BorderLayout(3, 3)); // Reduced spacing suggestionPanel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), "Đề nghị", TitledBorder.LEADING, TitledBorder.TOP, - new Font("Arial", Font.BOLD, 14), + new Font("Arial", Font.BOLD, 12), // Smaller font new Color(50, 50, 50) )); - suggestionField = new JTextArea(5, 20); // Adjusted rows for better layout + suggestionField = new JTextArea(2, 10); // Further reduced to 2 rows suggestionField.setFont(fieldFont); suggestionField.setLineWrap(true); suggestionField.setWrapStyleWord(true); addSelectAllOnFocus(suggestionField); JScrollPane suggestionScrollPane = new JScrollPane(suggestionField); - suggestionScrollPane.setPreferredSize(new Dimension(0, 200)); // Set fixed height + suggestionScrollPane.setPreferredSize(new Dimension(0, 50)); // Reduced height suggestionPanel.add(suggestionScrollPane, BorderLayout.CENTER); - suggestionPanel.setMinimumSize(new Dimension(200, 200)); - suggestionPanel.setPreferredSize(new Dimension(250, 200)); + suggestionPanel.setMinimumSize(new Dimension(200, 90)); // Reduced height + suggestionPanel.setPreferredSize(new Dimension(280, 90)); // Reduced height - // Add Re-checkup date picker - JPanel recheckupPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 2)); + // Add Re-checkup date picker - even more compact layout + JPanel recheckupPanel = new JPanel(); + recheckupPanel.setLayout(new BoxLayout(recheckupPanel, BoxLayout.X_AXIS)); // More compact layout + recheckupPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); // No border recheckupPanel.setBackground(Color.WHITE); JLabel recheckupLabel = new JLabel("Tái khám:"); - recheckupLabel.setFont(labelFont); + recheckupLabel.setFont(new Font("Arial", Font.BOLD, 12)); // Even smaller font recheckupPanel.add(recheckupLabel); + recheckupPanel.add(Box.createHorizontalStrut(2)); // Minimal spacing + + // Add checkbox for re-checkup needed - ultra compact + needRecheckupCheckbox = new JCheckBox(); + needRecheckupCheckbox.setFont(new Font("Arial", Font.PLAIN, 11)); // Even smaller font + needRecheckupCheckbox.setBackground(Color.WHITE); + needRecheckupCheckbox.setSelected(false); + needRecheckupCheckbox.setMargin(new Insets(0, 0, 0, 0)); // Remove margin + needRecheckupCheckbox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); // No border + needRecheckupCheckbox.addActionListener(e -> { + boolean selected = needRecheckupCheckbox.isSelected(); + recheckupDatePicker.setEnabled(selected); + recheckupDatePicker.getJFormattedTextField().setEditable(selected); + if(recheckupDatePickerButton != null) { + recheckupDatePickerButton.setEnabled(selected); + } + if (!selected) { + // Clear the date if checkbox is unchecked + recheckupDatePicker.getModel().setValue(null); + } + }); + recheckupPanel.add(needRecheckupCheckbox); UtilDateModel recheckupModel = new UtilDateModel(); Properties recheckupProps = new Properties(); @@ -1205,52 +1254,70 @@ public class CheckUpPage extends JPanel { // Remove "text.today" since these dates shouldn't default to today JDatePanelImpl recheckupDatePanel = new JDatePanelImpl(recheckupModel, recheckupProps); recheckupDatePicker = new JDatePickerImpl(recheckupDatePanel, new DateLabelFormatter()); - recheckupDatePicker.getJFormattedTextField().setFont(fieldFont); + // Find and store the button component + for (Component comp : recheckupDatePicker.getComponents()) { + if (comp instanceof JButton) { + recheckupDatePickerButton = (JButton) comp; + break; + } + } + recheckupDatePicker.getJFormattedTextField().setFont(new Font("Arial", Font.PLAIN, 12)); // Smaller font recheckupDatePicker.getJFormattedTextField().setToolTipText("Nhập ngày theo định dạng dd/mm/yyyy"); setupDateFieldForDirectInput(recheckupDatePicker); - recheckupDatePicker.setPreferredSize(new Dimension(140, 30)); // Make it a bit smaller + recheckupDatePicker.setPreferredSize(new Dimension(120, 25)); // Even smaller dimensions + recheckupDatePicker.setEnabled(false); // Initially disabled until checkbox is checked + recheckupDatePicker.getJFormattedTextField().setEditable(false); + if (recheckupDatePickerButton != null) { + recheckupDatePickerButton.setEnabled(false); + } recheckupPanel.add(recheckupDatePicker); + + // Adjust the layout to make room for the new checkbox + suggestionPanel.setPreferredSize(new Dimension(280, 200)); // Increased width from 250 to 280 + rightPanel.setPreferredSize(new Dimension(280, 0)); // Increased width from 250 to 280 + rightPanel.setMinimumSize(new Dimension(230, 0)); // Increased minimum width from 200 to 230 + suggestionPanel.add(recheckupPanel, BorderLayout.SOUTH); suggestionPanel.setMinimumSize(new Dimension(200, 200)); suggestionPanel.setPreferredSize(new Dimension(250, 200)); - // Diagnosis Panel - JPanel diagnosisPanel = new JPanel(new BorderLayout(5, 5)); + // Diagnosis Panel - more compact + JPanel diagnosisPanel = new JPanel(new BorderLayout(3, 3)); // Reduced spacing diagnosisPanel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), "Chẩn đoán", TitledBorder.LEADING, TitledBorder.TOP, - new Font("Arial", Font.BOLD, 14), + new Font("Arial", Font.BOLD, 12), // Smaller font new Color(50, 50, 50) )); - diagnosisField = new JTextArea(4, 20); - diagnosisField.setFont(fieldFont); + diagnosisField = new JTextArea(3, 20); // Reduced rows + diagnosisField.setFont(new Font("Arial", Font.PLAIN, 14)); // Slightly smaller font diagnosisField.setLineWrap(true); diagnosisField.setWrapStyleWord(true); addSelectAllOnFocus(diagnosisField); diagnosisPanel.add(new JScrollPane(diagnosisField), BorderLayout.CENTER); - // Conclusion Panel - JPanel conclusionPanel = new JPanel(new BorderLayout(5, 5)); + // Conclusion Panel - more compact + JPanel conclusionPanel = new JPanel(new BorderLayout(3, 3)); // Reduced spacing conclusionPanel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), "Kết luận", TitledBorder.LEADING, TitledBorder.TOP, - new Font("Arial", Font.BOLD, 14), + new Font("Arial", Font.BOLD, 12), // Smaller font new Color(50, 50, 50) )); - conclusionField = new JTextArea(4, 20); - conclusionField.setFont(fieldFont); + conclusionField = new JTextArea(3, 20); // Reduced rows + conclusionField.setFont(new Font("Arial", Font.PLAIN, 14)); // Slightly smaller font conclusionField.setLineWrap(true); conclusionField.setWrapStyleWord(true); addSelectAllOnFocus(conclusionField); conclusionPanel.add(new JScrollPane(conclusionField), BorderLayout.CENTER); rightPanel.add(suggestionPanel); - rightPanel.add(Box.createRigidArea(new Dimension(0, 10))); + rightPanel.add(Box.createRigidArea(new Dimension(0, 5))); // Reduced from 10 to 5 rightPanel.add(diagnosisPanel); - rightPanel.add(Box.createRigidArea(new Dimension(0, 10))); + rightPanel.add(Box.createRigidArea(new Dimension(0, 5))); // Reduced from 10 to 5 rightPanel.add(conclusionPanel); // Create split pane for left and right panels @@ -1264,8 +1331,14 @@ public class CheckUpPage extends JPanel { mainContentPanel.add(templatePanel, BorderLayout.NORTH); mainContentPanel.add(contentSplitPane, BorderLayout.CENTER); + // Create a container panel to hold the top rows with vertical layout + JPanel topContainer = new JPanel(); + topContainer.setLayout(new BoxLayout(topContainer, BoxLayout.Y_AXIS)); + topContainer.add(topRowPanel); + topContainer.add(doctorPanel); + // Assemble checkup info panel - checkupInfoPanel.add(topRowPanel, BorderLayout.NORTH); + checkupInfoPanel.add(topContainer, BorderLayout.NORTH); checkupInfoPanel.add(mainContentPanel, BorderLayout.CENTER); // Create tabbed pane for patient info and checkup info @@ -1465,6 +1538,65 @@ public class CheckUpPage extends JPanel { // In the constructor, after creating notesField: setupNotesPasteHandler(); setupShortcuts(); + + // Listen for CallPatientResponse to update the TV queue display + ClientHandler.addResponseListener(CallPatientResponse.class, response -> { + log.info("Received CallPatientResponse: PatientId={}, RoomId={}, Status={}, QueueNumber={}", + response.getPatientId(), response.getRoomId(), response.getStatus(), response.getQueueNumber()); + + if (tvQueueFrame != null && tvQueueFrame.isShowing()) { + // Find the full patient name and birth year from patient queue + String patientName = ""; + String birthYear = ""; + + // Look up the patient in our patient queue by checkup ID + for (Patient patient : patientQueue) { + if (patient.getCheckupId().equals(String.valueOf(response.getPatientId()))) { + patientName = patient.getCustomerLastName() + " " + patient.getCustomerFirstName(); + + // Extract year from DOB + if (patient.getCustomerDob() != null && !patient.getCustomerDob().isEmpty()) { + int year = DateUtils.extractYearFromTimestamp(patient.getCustomerDob()); + if (year != -1) { + birthYear = String.valueOf(year); + } + } + break; + } + } + + String fullPatientInfo = patientName + " (" + birthYear + ")"; + + // Pass the QUEUE NUMBER to be displayed in the small room status box + tvQueueFrame.updateSpecificRoomStatus( + response.getRoomId(), + String.valueOf(response.getPatientId()), + response.getQueueNumber(), // Use the queue number here + fullPatientInfo, // Full info for the central display + response.getStatus() + ); + } + }); + + // Listen for queue updates + ClientHandler.addResponseListener(GetCheckUpQueueUpdateResponse.class, response -> { + SwingUtilities.invokeLater(() -> { + log.info("Received checkup update queue"); + this.rawQueueForTv = response.getQueue(); + this.patientQueue.clear(); + if (this.rawQueueForTv != null) { + for (String[] patientData : this.rawQueueForTv) { + this.patientQueue.add(new Patient(patientData)); + } + } + if (queueManagementPage != null) { + queueManagementPage.updateQueueTable(); + } + if (tvQueueFrame != null && tvQueueFrame.isVisible()) { + tvQueueFrame.updateQueueData(this.rawQueueForTv); + } + }); + }); } private void addSelectAllOnFocus(JTextComponent textComponent) { @@ -1707,6 +1839,7 @@ public class CheckUpPage extends JPanel { (String) genderComboBox.getSelectedItem(), customerAddressField.getText() + ", " + (wardComboBox.getSelectedItem() != null ? wardComboBox.getSelectedItem().toString() : "") + ", " + (districtComboBox.getSelectedItem() != null ? districtComboBox.getSelectedItem().toString() : "") + ", " + (provinceComboBox.getSelectedItem() != null ? provinceComboBox.getSelectedItem().toString() : ""), (String) doctorComboBox.getSelectedItem(), + (String) ultrasoundDoctorComboBox.getSelectedItem(), datePicker.getJFormattedTextField().getText(), TextUtils.scaleRtfFontSize(getRtfContentAsString()), conclusionField.getText(), @@ -1781,6 +1914,7 @@ public class CheckUpPage extends JPanel { (String) genderComboBox.getSelectedItem(), customerAddressField.getText() + ", " + (wardComboBox.getSelectedItem() != null ? wardComboBox.getSelectedItem().toString() : "") + ", " + (districtComboBox.getSelectedItem() != null ? districtComboBox.getSelectedItem().toString() : "") + ", " + (provinceComboBox.getSelectedItem() != null ? provinceComboBox.getSelectedItem().toString() : ""), (String) doctorComboBox.getSelectedItem(), + (String) ultrasoundDoctorComboBox.getSelectedItem(), datePicker.getJFormattedTextField().getText(), TextUtils.scaleRtfFontSize(getRtfContentAsString()), conclusionField.getText(), @@ -1988,6 +2122,37 @@ public class CheckUpPage extends JPanel { log.error("Invalid date format for checkup date: {}", selectedPatient.getCheckupDate()); JOptionPane.showMessageDialog(null, "Định dạng ngày hoặc dấu thời gian không hợp lệ: " + selectedPatient.getCheckupDate(), "Lỗi định dạng ngày", JOptionPane.ERROR_MESSAGE); } + + // Check if re-checkup date is present and valid + if (selectedPatient.getReCheckupDate() != null && !selectedPatient.getReCheckupDate().isEmpty() && + !selectedPatient.getReCheckupDate().equals("0") && !selectedPatient.getReCheckupDate().equals("null")) { + needRecheckupCheckbox.setSelected(true); + recheckupDatePicker.setEnabled(true); + recheckupDatePicker.getJFormattedTextField().setEditable(true); + if (recheckupDatePickerButton != null) { + recheckupDatePickerButton.setEnabled(true); + } + // Parse and set the re-checkup date + Date recheckupDate = DateUtils.convertToDate(selectedPatient.getReCheckupDate()); + if (recheckupDate != null) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(recheckupDate); + recheckupDatePicker.getModel().setDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH)); + recheckupDatePicker.getModel().setSelected(true); + } else { + // If parsing fails, log it and keep the field clear + log.warn("Could not parse re-checkup date: {}", selectedPatient.getReCheckupDate()); + recheckupDatePicker.getModel().setValue(null); + } + } else { + needRecheckupCheckbox.setSelected(false); + recheckupDatePicker.setEnabled(false); + recheckupDatePicker.getJFormattedTextField().setEditable(false); + if (recheckupDatePickerButton != null) { + recheckupDatePickerButton.setEnabled(false); + } + recheckupDatePicker.getModel().setValue(null); + } // Set DOB using utility function Date dobDate = DateUtils.convertToDate(selectedPatient.getCustomerDob()); @@ -2004,6 +2169,7 @@ public class CheckUpPage extends JPanel { customerLastNameField.setText(selectedPatient.getCustomerLastName()); customerFirstNameField.setText(selectedPatient.getCustomerFirstName()); doctorComboBox.setSelectedItem(selectedPatient.getDoctorName()); + ultrasoundDoctorComboBox.setSelectedItem(selectedPatient.getDoctorUltrasoundId()); suggestionField.setText(selectedPatient.getSuggestion()); // De nghi diagnosisField.setText(selectedPatient.getDiagnosis()); // Chuan doan conclusionField.setText(selectedPatient.getConclusion()); // Ket luan @@ -2285,11 +2451,13 @@ public class CheckUpPage extends JPanel { private void handleCallPatientResponse(CallPatientResponse response) { SwingUtilities.invokeLater(() -> { - log.info("Received call patient response: Room {}, Patient ID {}, Status {}", response.getRoomId(), response.getPatientId(), response.getStatus()); + log.info("Received call patient response: Room {}, Patient ID {}, Queue Number {}, Status {}", + response.getRoomId(), response.getPatientId(), response.getQueueNumber(), response.getStatus()); if (tvQueueFrame != null && tvQueueFrame.isVisible()) { if (response.getStatus() == Status.PROCESSING) { String patientIdToFind = String.valueOf(response.getPatientId()); - String patientDisplayInfo = patientIdToFind; + String queueNumber = response.getQueueNumber(); + String patientDisplayInfo = queueNumber; // Use queue number instead of patient ID if (this.patientQueue != null) { for (Patient patient : this.patientQueue) { @@ -2316,7 +2484,7 @@ public class CheckUpPage extends JPanel { } } } - tvQueueFrame.updateSpecificRoomStatus(response.getRoomId(), String.valueOf(response.getPatientId()), patientDisplayInfo, response.getStatus()); + tvQueueFrame.updateSpecificRoomStatus(response.getRoomId(), String.valueOf(response.getPatientId()), queueNumber, patientDisplayInfo, response.getStatus()); } else if (response.getStatus() == Status.EMPTY) { tvQueueFrame.markRoomAsFree(response.getRoomId()); } @@ -2342,39 +2510,44 @@ public class CheckUpPage extends JPanel { callingStatusLabel.setBackground(new Color(230, 255, 230)); JOptionPane.showMessageDialog(this, "Phòng " + selectedRoomName + " đã được đánh dấu là trống.", "Cập nhật trạng thái phòng", JOptionPane.INFORMATION_MESSAGE); log.info("Room {} marked as free", selectedRoomName); - NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new CallPatientRequest(roomId, -1, Status.EMPTY)); + NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new CallPatientRequest(roomId, -1, "", Status.EMPTY)); } private void handleCallPatient() { - // Check if a patient is selected on the main CheckUpPage - if (selectedCheckupId == null || selectedCheckupId.isEmpty()) { - JOptionPane.showMessageDialog(this, "Vui lòng chọn một bệnh nhân từ danh sách để gọi.", "Chưa chọn bệnh nhân", JOptionPane.WARNING_MESSAGE); + if (selectedCheckupId == null) { + JOptionPane.showMessageDialog(this, "Vui lòng chọn một bệnh nhân từ danh sách chờ.", "Chưa chọn bệnh nhân", JOptionPane.WARNING_MESSAGE); return; } - // Find the selected patient by checkup ID Patient selectedPatient = null; for (Patient patient : patientQueue) { - if (patient.getCheckupId().equals(selectedCheckupId)) { + if (selectedCheckupId.equals(patient.getCheckupId())) { selectedPatient = patient; break; } } if (selectedPatient == null) { - JOptionPane.showMessageDialog(this, "Không tìm thấy bệnh nhân đã chọn trong hàng đợi.", "Lỗi", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this, "Không tìm thấy thông tin chi tiết cho bệnh nhân đã chọn.", "Lỗi", JOptionPane.ERROR_MESSAGE); return; } - String maKhamBenh = selectedPatient.getCheckupId(); - String patientName = selectedPatient.getCustomerLastName() + " " + selectedPatient.getCustomerFirstName(); - int selectedRoom = callRoomComboBox.getSelectedIndex() + 1; - JOptionPane.showMessageDialog(this, "Đang gọi bệnh nhân " + patientName + " (Mã KB: " + maKhamBenh + ") đến " + callRoomComboBox.getSelectedItem().toString(), "Gọi bệnh nhân", JOptionPane.INFORMATION_MESSAGE); - NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new CallPatientRequest(selectedRoom, Integer.parseInt(maKhamBenh), Status.PROCESSING)); - log.info("Called patient {} (ID: {}) to room {}", patientName, maKhamBenh, selectedRoom); - String callingText = "Đang gọi: BN " + patientName + " (Mã KB: " + maKhamBenh + ") đến " + callRoomComboBox.getSelectedItem().toString() + ""; - callingStatusLabel.setText(callingText); - callingStatusLabel.setForeground(Color.WHITE); - callingStatusLabel.setBackground(new Color(217, 83, 79)); // Red background for calling + + int roomId = callRoomComboBox.getSelectedIndex() + 1; + int patientId = Integer.parseInt(selectedCheckupId); + + // Get queue number from patient DTO and format it to 2 digits with leading zero + String queueNumber = selectedPatient.getQueueNumber(); + + // Set status to PROCESSING when calling + statusComboBox.setSelectedItem("ĐANG KHÁM"); + Status status = Status.PROCESSING; + + callingStatusLabel.setText("Đang gọi bệnh nhân " + selectedPatient.getCustomerFirstName() + " (STT: " + queueNumber + ") vào phòng " + roomId); + callingStatusLabel.setForeground(Color.BLUE); + callingStatusLabel.setBackground(new Color(220, 230, 255)); + + CallPatientRequest request = new CallPatientRequest(roomId, patientId, queueNumber, status); + NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request); } private void handleGetOrderInfoByCheckupResponse(GetOrderInfoByCheckupRes response) { @@ -3276,9 +3449,26 @@ public class CheckUpPage extends JPanel { private void uploadImageInBackground(String fileName, BufferedImage image) { imageUploadExecutor.submit(() -> { try { - // Compress the image to JPEG for smaller size + // Compress the image to JPEG with high quality (0.9) for better preservation ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ImageIO.write(image, "jpg", baos); + + // Get JPEG writer and set high quality compression + ImageWriter jpegWriter = ImageIO.getImageWritersByFormatName("jpg").next(); + ImageOutputStream ios = ImageIO.createImageOutputStream(baos); + jpegWriter.setOutput(ios); + + // Set compression quality to 0.9 (90% quality) + ImageWriteParam writeParam = jpegWriter.getDefaultWriteParam(); + writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); + writeParam.setCompressionQuality(0.9f); // High quality compression + + // Write the image with high quality + jpegWriter.write(null, new IIOImage(image, null, null), writeParam); + + // Clean up + jpegWriter.dispose(); + ios.close(); + byte[] compressedImageData = baos.toByteArray(); // Create and send the request @@ -4112,13 +4302,14 @@ public class CheckUpPage extends JPanel { } private int findRowByCheckupId(String checkupId) { + // Find the patient in the patientQueue if (checkupId == null) return -1; for (int i = 0; i < queueTableModel.getRowCount(); i++) { if (checkupId.equals(queueTableModel.getValueAt(i, 0))) { return i; } } - return -1; + return -1; // Not found } /** @@ -4330,6 +4521,7 @@ public class CheckUpPage extends JPanel { String customerWeight = customerWeightSpinner.getValue().toString(); String customerHeight = customerHeightSpinner.getValue().toString(); String doctorId = String.valueOf(doctorComboBox.getSelectedIndex() + 1); + String ultrasoundDoctorId = String.valueOf(ultrasoundDoctorComboBox.getSelectedIndex() + 1); String suggestions = suggestionField.getText(); String diagnosis = diagnosisField.getText(); String conclusion = conclusionField.getText(); @@ -4352,6 +4544,7 @@ public class CheckUpPage extends JPanel { log.info("customerWeight: {}", customerWeight); log.info("customerHeight: {}", customerHeight); log.info("doctorId: {}", doctorId); + log.info("ultrasoundDoctorId: {}", ultrasoundDoctorId); log.info("suggestions: {}", suggestions); log.info("diagnosis: {}", diagnosis); log.info("conclusion: {}", conclusion); @@ -4371,10 +4564,18 @@ public class CheckUpPage extends JPanel { Long reCheckupDateLong = DateUtils.convertToDate(reCheckupDate).getTime(); Long checkupDateLong = DateUtils.convertToDate(checkupDate).getTime(); Integer heartBeat = (Integer) patientHeartRateSpinner.getValue(); + Boolean needsRecheckup = needRecheckupCheckbox.isSelected(); + + // If re-checkup is not needed, clear the re-checkup date + if (!needsRecheckup) { + reCheckupDateLong = 0L; // Use 0 to indicate no re-checkup date + } + SaveCheckupRequest request = new SaveCheckupRequest( Integer.parseInt(checkupId), Integer.parseInt(customerId), Integer.parseInt(doctorId), + Integer.parseInt(ultrasoundDoctorId), checkupDateLong, suggestions, diagnosis, @@ -4383,6 +4584,7 @@ public class CheckUpPage extends JPanel { checkupType, conclusion, reCheckupDateLong, + needsRecheckup, customerFirstName, customerLastName, customerDobLong, @@ -4421,30 +4623,47 @@ public class CheckUpPage extends JPanel { } private void applyTemplate(Template template) { - diagnosisField.setText(template.getDiagnosis()); - conclusionField.setText(template.getConclusion()); - suggestionField.setText(template.getSuggestion()); + if (template == null) return; - String rtfContent = template.getContent(); - if (rtfContent != null && !rtfContent.isEmpty()) { - setRtfContentFromString(rtfContent); - } else { - setRtfContentFromString(""); // Clear if content is empty + // Set the print orientation from the template + if (template.getPrintType() != null) { + orientationComboBox.setSelectedItem(template.getPrintType()); } - // Set template print setting - photoNum = Integer.parseInt(template.getPhotoNum()); - printType = template.getPrintType(); - orientationComboBox.setSelectedItem(template.getPrintType()); // Update orientation dropdown - templateName = template.getTemplateName(); + templateTitle = template.getTemplateTitle(); - templateGender = template.getTemplateGender(); - templateContent = template.getContent(); - templateConclusion = template.getConclusion(); - templateSuggestion = template.getSuggestion(); + photoNum = Integer.parseInt(template.getPhotoNum()); - // Update template info labels - imageCountValueLabel.setText(template.getPhotoNum()); - genderValueLabel.setText(template.getTemplateGender()); + // Use a try-catch for the RTF content to prevent application crashes + try { + RTFEditorKit rtfEditorKit = (RTFEditorKit) notesField.getEditorKit(); + // First clear the document to ensure we're not appending content + notesField.getDocument().remove(0, notesField.getDocument().getLength()); + + if (template.getContent() != null && !template.getContent().isEmpty()) { + rtfEditorKit.read(new StringReader(template.getContent()), notesField.getDocument(), 0); + } + } catch (Exception e) { + log.error("Failed to apply RTF content from template '{}'", template.getTemplateName(), e); + // Fallback to plain text or show an error + notesField.setText("Lỗi khi tải nội dung mẫu."); + } + + // Only set fields if they have content in the template + if (template.getDiagnosis() != null && !template.getDiagnosis().isEmpty()) { + diagnosisField.setText(template.getDiagnosis()); + } + + if (template.getConclusion() != null && !template.getConclusion().isEmpty()) { + conclusionField.setText(template.getConclusion()); + } + + if (template.getSuggestion() != null && !template.getSuggestion().isEmpty()) { + suggestionField.setText(template.getSuggestion()); + } + + callingStatusLabel.setText("Đã áp dụng mẫu '" + template.getTemplateName() + "'."); + callingStatusLabel.setBackground(new Color(220, 230, 255)); + callingStatusLabel.setForeground(new Color(0, 0, 139)); } private void handleGetAllTemplatesResponse(GetAllTemplatesRes response) { @@ -4537,6 +4756,12 @@ public class CheckUpPage extends JPanel { datePicker.getModel().setValue(null); dobPicker.getModel().setValue(null); recheckupDatePicker.getModel().setValue(null); + needRecheckupCheckbox.setSelected(false); + recheckupDatePicker.setEnabled(false); + recheckupDatePicker.getJFormattedTextField().setEditable(false); + if (recheckupDatePickerButton != null) { + recheckupDatePickerButton.setEnabled(false); + } medicinePrescription = new String[0][0]; servicePrescription = new String[0][0]; @@ -4662,6 +4887,16 @@ public class CheckUpPage extends JPanel { } }); } + + private int findRowByCheckupId(String checkupId) { + DefaultTableModel model = (DefaultTableModel) table.getModel(); + for (int i = 0; i < model.getRowCount(); i++) { + if (model.getValueAt(i, 0).equals(checkupId)) { + return i; + } + } + return -1; // Not found + } } diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java index 2db9300..59a263d 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/UltrasoundResult.java @@ -70,6 +70,7 @@ public class UltrasoundResult { private final String patientGender; private final String patientAddress; private final String doctorName; + private final String ultrasoundDoctorName; private final String checkupDate; private final String rtfContent; private final String conclusion; @@ -89,32 +90,59 @@ public class UltrasoundResult { private static final String PDF_PATH = "ultrasound_result.pdf"; public UltrasoundResult(String checkupId, String patientName, String patientDOB, String patientGender, - String patientAddress, String doctorName, String checkupDate, + String patientAddress, String doctorName, String ultrasoundDoctorName, String checkupDate, String rtfContent, String conclusion, String suggestion, String recheckupDate, List selectedImages, String printType, String templateTitle, String customerHeight, String customerWeight, String heartRate, String bloodPressure, String driveUrl) { // Defensive null checks to prevent JasperReports from crashing on null parameters - this.checkupId = checkupId != null ? checkupId : ""; - this.patientName = patientName != null ? patientName : ""; - this.patientDOB = patientDOB != null ? patientDOB : ""; - this.patientGender = patientGender != null ? patientGender : ""; - this.patientAddress = patientAddress != null ? patientAddress : ""; - this.doctorName = doctorName != null ? doctorName : ""; - this.checkupDate = checkupDate != null ? checkupDate : ""; - this.rtfContent = rtfContent != null ? rtfContent : ""; - this.conclusion = conclusion != null ? conclusion : ""; - this.suggestion = suggestion != null ? suggestion : ""; - this.recheckupDate = recheckupDate != null ? recheckupDate : ""; + // Also handle "null" string values by converting them to empty strings + this.checkupId = convertNullToEmpty(checkupId); + this.patientName = convertNullToEmpty(patientName); + this.patientDOB = convertNullToEmpty(patientDOB); + this.patientGender = convertNullToEmpty(patientGender); + this.patientAddress = convertNullToEmpty(patientAddress); + this.doctorName = convertNullToEmpty(doctorName); + this.ultrasoundDoctorName = convertNullToEmpty(ultrasoundDoctorName); + this.checkupDate = convertNullToEmpty(checkupDate); + this.rtfContent = convertNullToEmpty(rtfContent); + this.conclusion = convertNullToEmpty(conclusion); + this.suggestion = convertNullToEmpty(suggestion); + this.recheckupDate = convertNullToEmpty(recheckupDate); this.selectedImages = selectedImages != null ? selectedImages : new ArrayList<>(); - this.printType = printType != null ? printType : ""; - this.templateTitle = templateTitle != null ? templateTitle : ""; - this.customerHeight = customerHeight != null ? customerHeight : ""; - this.customerWeight = customerWeight != null ? customerWeight : ""; - this.heartRate = heartRate != null ? heartRate : ""; - this.bloodPressure = bloodPressure != null ? bloodPressure : ""; - this.driveUrl = driveUrl != null ? driveUrl : ""; // Google Drive URL + this.printType = convertNullToEmpty(printType); + this.templateTitle = convertNullToEmpty(templateTitle); + this.customerHeight = convertNullToEmpty(customerHeight); + this.customerWeight = convertNullToEmpty(customerWeight); + this.heartRate = convertNullToEmpty(heartRate); + this.bloodPressure = convertNullToEmpty(bloodPressure); + this.driveUrl = convertNullToEmpty(driveUrl); // Google Drive URL + } + + /** + * Helper method to convert null or "null" string values to empty strings + * @param value The string to check + * @return Empty string if value is null or equals "null", otherwise the original value + */ + private String convertNullToEmpty(String value) { + if (value == null || "null".equals(value)) { + return ""; + } + return value; + } + + /** + * Helper method to convert null or "null" string values to a default value + * @param value The string to check + * @param defaultValue The default value to use if value is null or "null" + * @return defaultValue if value is null or equals "null", otherwise the original value + */ + private String convertNullToEmpty(String value, String defaultValue) { + if (value == null || "null".equals(value)) { + return defaultValue; + } + return value; } public void showDirectJasperViewer() { @@ -143,7 +171,15 @@ public class UltrasoundResult { } public JasperPrint createJasperPrint() throws JRException, IOException { - String jrxmlPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml"; + String jrxmlPath; + if ("Dọc".equalsIgnoreCase(this.printType)) { + jrxmlPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml"; + } else { + jrxmlPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml"; + } + + log.info("Using JRXML template: {}", jrxmlPath); + try (InputStream inputStream = new FileInputStream(new File(jrxmlPath))) { Map parameters = new HashMap<>(); @@ -151,14 +187,15 @@ public class UltrasoundResult { parameters.put(JRParameter.REPORT_LOCALE, new Locale("vi", "VN")); // Populate all the string parameters - parameters.put("clinicName", LocalStorage.ClinicName != null ? LocalStorage.ClinicName : "Phòng khám BSK"); - parameters.put("clinicAddress", LocalStorage.ClinicAddress != null ? LocalStorage.ClinicAddress : "Địa chỉ phòng khám"); - parameters.put("clinicPhone", LocalStorage.ClinicPhone != null ? LocalStorage.ClinicPhone : "SĐT phòng khám"); + parameters.put("clinicName", convertNullToEmpty(LocalStorage.ClinicName, "Phòng khám BSK")); + parameters.put("clinicAddress", convertNullToEmpty(LocalStorage.ClinicAddress, "Địa chỉ phòng khám")); + parameters.put("clinicPhone", convertNullToEmpty(LocalStorage.ClinicPhone, "SĐT phòng khám")); parameters.put("patientName", this.patientName); parameters.put("patientDOB", this.patientDOB); parameters.put("patientGender", this.patientGender); parameters.put("patientAddress", this.patientAddress); parameters.put("doctorName", this.doctorName); + parameters.put("ultrasoundDoctorName", this.ultrasoundDoctorName); parameters.put("checkupDate", this.checkupDate); parameters.put("barcodeNumber", this.checkupId); @@ -176,6 +213,14 @@ public class UltrasoundResult { // Template title parameters.put("templateTitle", this.templateTitle); + + // Extra safety: Ensure no parameter is null or "null" string + for (String key : parameters.keySet()) { + Object value = parameters.get(key); + if (value instanceof String && ("null".equals(value) || value == null)) { + parameters.put(key, ""); + } + } // Handle images int numberOfImages = this.selectedImages.size(); diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml index ad273e7..b581bb4 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml @@ -37,6 +37,7 @@ + @@ -45,17 +46,17 @@ <detail> <band height="540" splitType="Stretch"> <element kind="frame" uuid="2b7ec588-953c-4145-87b7-1d861c272fe0" x="0" y="0" width="400" height="539"> - <element kind="image" uuid="fd80ab38-4cae-409b-84b9-356220d2b1e5" x="0" y="-5" width="70" height="64"> + <element kind="image" uuid="fd80ab38-4cae-409b-84b9-356220d2b1e5" x="0" y="1" width="60" height="60"> <expression><![CDATA[$P{logoImage}]]></expression> </element> - <element kind="textField" uuid="eb56d4c8-2a0d-45fa-89a2-ad6528f9a99b" x="70" y="0" width="233" height="20" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true"> + <element kind="textField" uuid="eb56d4c8-2a0d-45fa-89a2-ad6528f9a99b" x="60" y="0" width="290" height="20" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true"> <expression><![CDATA[$P{clinicName}]]></expression> </element> - <element kind="textField" uuid="796d9ffd-6d2e-4ae3-bdfb-b08183d7d9c0" x="70" y="20" width="217" height="22" fontName="Times New Roman" fontSize="10.0"> + <element kind="textField" uuid="796d9ffd-6d2e-4ae3-bdfb-b08183d7d9c0" x="60" y="20" width="235" height="25" fontName="Times New Roman" fontSize="10.0"> <expression><![CDATA[$P{clinicAddress}]]></expression> </element> - <element kind="textField" uuid="260bb33d-1d11-4178-aceb-3fbf8fcb4b45" x="70" y="42" width="162" height="15" fontName="Times New Roman" fontSize="8.0"> - <expression><![CDATA[$P{clinicPhone}]]></expression> + <element kind="textField" uuid="260bb33d-1d11-4178-aceb-3fbf8fcb4b45" x="60" y="45" width="162" height="12" fontName="Times New Roman" fontSize="8.0"> + <expression><![CDATA["SDT: " + $P{clinicPhone}]]></expression> </element> <element kind="staticText" uuid="6f89a93f-2d10-4506-baa5-226bdd047b17" x="0" y="65" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true"> <text><![CDATA[I.Thông tin]]></text> @@ -132,7 +133,7 @@ <element kind="textField" uuid="6b0b21be-2ca3-4594-bbe9-34868e13eb1e" x="335" y="150" width="63" height="16" fontName="Times New Roman" fontSize="10.0"> <expression><![CDATA[$P{checkupBloodPressure} +" mmHg"]]></expression> </element> - <element kind="component" uuid="e8842007-3343-4399-a101-76a302d0755d" x="352" y="19" width="40" height="37"> + <element kind="component" uuid="e8842007-3343-4399-a101-76a302d0755d" x="362" y="19" width="38" height="35"> <component kind="barcode4j:QRCode"> <codeExpression><![CDATA[$P{driveURL}]]></codeExpression> </component> @@ -149,11 +150,11 @@ <element kind="staticText" uuid="c53ef0ab-a702-47fc-9b7c-24307a495b30" x="0" y="430" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="15.0" bold="true"> <text><![CDATA[Đề nghị:]]></text> </element> - <element kind="staticText" uuid="a38b1187-ddb8-4410-9b52-dc041f894b7b" x="247" y="442" width="96" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Center"> + <element kind="staticText" uuid="a38b1187-ddb8-4410-9b52-dc041f894b7b" x="250" y="440" width="96" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Center"> <text><![CDATA[Bác sĩ siêu âm]]></text> </element> <element kind="textField" uuid="b548d99d-bedf-4e90-b13b-bdbc763aa974" x="200" y="513" width="200" height="21" fontName="Times New Roman" fontSize="14.0" bold="true" hTextAlign="Center"> - <expression><![CDATA[$P{doctorName}]]></expression> + <expression><![CDATA[$P{ultrasoundDoctorName}]]></expression> </element> <element kind="textField" uuid="8bca58ab-0958-40fe-8e51-2a39a9d379a5" x="200" y="521" width="200" height="19" fontName="Times New Roman" pattern="MMMMM dd, yyyy" hTextAlign="Center" vTextAlign="Bottom"> <expression><![CDATA[new java.text.SimpleDateFormat("'Đức Hoà, ngày ' dd ' tháng ' MM ' năm ' yyyy", new java.util.Locale("vi", "VN")).format(new java.util.Date())]]></expression> @@ -164,7 +165,7 @@ <element kind="textField" uuid="e90ad772-4425-482d-9e75-f2bc66b9084d" x="0" y="358" width="400" height="70" fontName="Times New Roman" fontSize="12.0"> <expression><![CDATA[$P{checkupConclusion}]]></expression> </element> - <element kind="component" uuid="cb28d811-1328-45dc-9461-cfbb88b7eeae" x="-113" y="19" width="64" height="40"> + <element kind="component" uuid="cb28d811-1328-45dc-9461-cfbb88b7eeae" x="-105" y="19" width="64" height="40"> <component kind="barcode4j:Code128"> <codeExpression><![CDATA[$P{barcodeNumber}]]></codeExpression> </component> @@ -259,7 +260,7 @@ <expression><![CDATA[$P{image1}]]></expression> </element> </element> - <element kind="staticText" uuid="8f35aa72-22a4-45f0-a35e-8e35e832404e" x="-55" y="54" width="55" height="14" fontName="Times New Roman" fontSize="8.0" bold="false" hTextAlign="Center"> + <element kind="staticText" uuid="8f35aa72-22a4-45f0-a35e-8e35e832404e" x="-55" y="53" width="55" height="14" fontName="Times New Roman" fontSize="8.0" bold="false" hTextAlign="Center"> <printWhenExpression><![CDATA[$P{driveURL} != null && !$P{driveURL}.trim().isEmpty()]]></printWhenExpression> <text><![CDATA[Xem KQ online]]></text> </element> diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml new file mode 100644 index 0000000..f6082c0 --- /dev/null +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml @@ -0,0 +1,198 @@ +<!-- Created with Jaspersoft Studio version 7.0.3.final using JasperReports Library version 7.0.3-41034ca841d452f3305ba55b9042260aaa1ab5dd --> +<jasperReport name="Blank_A4_Potrait" language="java" columnCount="1" pageWidth="595" pageHeight="842" orientation="Landscape" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="e7f9ca7e-f0db-48b0-bef9-93be21b3b0a3"> + <property name="com.jaspersoft.studio.data.defaultdataadapter" value="One Empty Record"/> + <parameter name="checkupNote" class="java.lang.String"/> + <parameter name="checkupDate" class="java.lang.String"/> + <parameter name="patientDiagnos" class="java.lang.String"/> + <parameter name="doctorName" class="java.lang.String"/> + <parameter name="medicineDS" class="net.sf.jasperreports.engine.data.JRBeanCollectionDataSource"/> + <parameter name="serviceDS" class="net.sf.jasperreports.engine.data.JRBeanCollectionDataSource"/> + <parameter name="supplementDS" class="net.sf.jasperreports.engine.data.JRBeanCollectionDataSource"/> + <parameter name="name" class="java.lang.String"/> + <parameter name="clinicName" class="java.lang.String"/> + <parameter name="patientAddress" class="java.lang.String"/> + <parameter name="clinicPhone" class="java.lang.String"/> + <parameter name="patientName" class="java.lang.String"/> + <parameter name="patientDOB" class="java.lang.String"/> + <parameter name="clinicAddress" class="java.lang.String"/> + <parameter name="logoImage" class="java.lang.String"/> + <parameter name="barcodeNumber" class="java.lang.String"/> + <parameter name="patientGender" class="java.lang.String"/> + <parameter name="hasMedicines" class="java.lang.Boolean"/> + <parameter name="hasServices" class="java.lang.Boolean"/> + <parameter name="hasSupplements" class="java.lang.Boolean"/> + <parameter name="checkupConclusion" class="java.lang.String"/> + <parameter name="checkupSuggestion" class="java.lang.String"/> + <parameter name="reCheckupDate" class="java.lang.String"/> + <parameter name="image1" class="java.lang.String"/> + <parameter name="image2" class="java.lang.String"/> + <parameter name="image3" class="java.lang.String"/> + <parameter name="checkupHeight" class="java.lang.String"/> + <parameter name="image4" class="java.lang.String"/> + <parameter name="image5" class="java.lang.String"/> + <parameter name="numberImage" class="java.lang.Integer"/> + <parameter name="image6" class="java.lang.String"/> + <parameter name="checkupBloodPressure" class="java.lang.String"/> + <parameter name="checkupHeartBeat" class="java.lang.String"/> + <parameter name="checkupWeight" class="java.lang.String"/> + <parameter name="templateTitle" class="java.lang.String"/> + <parameter name="driveURL" class="java.lang.String"/> + <parameter name="ultrasoundDoctorName" class="java.lang.String"/> + <query language="sql"><![CDATA[]]></query> + <background splitType="Stretch"/> + <title splitType="Stretch"/> + <pageHeader splitType="Stretch"/> + <columnHeader splitType="Stretch"/> + <detail> + <band height="598" splitType="Stretch"> + <element kind="frame" uuid="2b7ec588-953c-4145-87b7-1d861c272fe0" x="0" y="0" width="551" height="598"> + <element kind="image" uuid="fd80ab38-4cae-409b-84b9-356220d2b1e5" x="0" y="-5" width="70" height="64"> + <expression><![CDATA[$P{logoImage}]]></expression> + </element> + <element kind="textField" uuid="eb56d4c8-2a0d-45fa-89a2-ad6528f9a99b" x="70" y="0" width="323" height="20" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true"> + <expression><![CDATA[$P{clinicName}]]></expression> + </element> + <element kind="textField" uuid="796d9ffd-6d2e-4ae3-bdfb-b08183d7d9c0" x="70" y="20" width="323" height="22" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{clinicAddress}]]></expression> + </element> + <element kind="textField" uuid="260bb33d-1d11-4178-aceb-3fbf8fcb4b45" x="70" y="42" width="276" height="15" fontName="Times New Roman" fontSize="8.0"> + <expression><![CDATA[$P{clinicPhone}]]></expression> + </element> + <element kind="staticText" uuid="6f89a93f-2d10-4506-baa5-226bdd047b17" x="0" y="65" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true"> + <text><![CDATA[I.Thông tin]]></text> + </element> + <element kind="staticText" uuid="b8dfca43-cf97-49e5-b58d-4667143dba62" x="0" y="79" width="50" height="15" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left"> + <text><![CDATA[Họ và tên:]]></text> + </element> + <element kind="staticText" uuid="0a9ca0c8-f2bd-4ade-a025-a6d79c13613f" x="1" y="94" width="80" height="15" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left"> + <text><![CDATA[Ngày sinh:]]></text> + </element> + <element kind="staticText" uuid="21917ab8-ae07-44c8-ba4e-bc6c4c54b440" x="140" y="94" width="80" height="15" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left"> + <text><![CDATA[Giới tính:]]></text> + </element> + <element kind="staticText" uuid="bcbb614e-7a4d-48a3-83c2-198e4fb62c5f" x="1" y="109" width="49" height="15" fontName="Times New Roman" bold="true" hTextAlign="Left"> + <text><![CDATA[Địa chỉ:]]></text> + </element> + <element kind="staticText" uuid="45fe4be1-9e21-459b-aeee-2efc8354f372" x="221" y="80" width="68" height="15" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left"> + <text><![CDATA[Bác sĩ chỉ định:]]></text> + </element> + <element kind="staticText" uuid="f2ff4b17-63cc-4e8c-a3b0-2523f534f21a" x="221" y="94" width="60" height="15" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left"> + <text><![CDATA[Ngày khám:]]></text> + </element> + <element kind="textField" uuid="1511f53a-b282-41b1-b233-612b6106c07a" x="48" y="79" width="172" height="15" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{patientName}]]></expression> + </element> + <element kind="textField" uuid="5bdac267-523d-4931-926b-76233ce9b84d" x="50" y="94" width="80" height="15" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{patientDOB}]]></expression> + </element> + <element kind="textField" uuid="f4df20b4-920c-491e-882b-6e0733cb4866" x="184" y="94" width="36" height="15" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{patientGender}]]></expression> + </element> + <element kind="textField" uuid="3b8ac40e-85aa-4ab8-8445-64bcddb8b267" x="39" y="109" width="361" height="15" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{patientAddress}]]></expression> + </element> + <element kind="textField" uuid="db40cdb0-2850-4d66-8430-addc54f90321" x="290" y="80" width="110" height="15" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{doctorName}]]></expression> + </element> + <element kind="textField" uuid="eb9cf54a-3b50-43c5-8696-cfc1e21bddfe" x="276" y="94" width="124" height="15" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{checkupDate}]]></expression> + </element> + <element kind="staticText" uuid="9f7de81c-4c38-41a2-9fea-a165eff19431" x="1" y="124" width="59" height="16" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left"> + <text><![CDATA[Chuẩn đoán:]]></text> + </element> + <element kind="textField" uuid="5ae67e13-7a79-437f-bfb8-c720d896720d" x="60" y="124" width="340" height="26" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{patientDiagnos}]]></expression> + </element> + <element kind="staticText" uuid="b1fa845a-48da-4e45-82b8-8b9d972650f6" x="0" y="162" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true"> + <text><![CDATA[II.Nội dung]]></text> + </element> + <element kind="textField" uuid="6d652a25-b701-45bd-91ba-750842650004" x="0" y="177" width="400" height="421" markup="rtf" fontName="Times New Roman" fontSize="16.0"> + <expression><![CDATA[$P{checkupNote}]]></expression> + </element> + <element kind="staticText" uuid="8f720f6a-0257-4556-b3bc-3b81ad4f8099" x="1" y="150" width="59" height="16" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left"> + <text><![CDATA[Chiều cao:]]></text> + </element> + <element kind="staticText" uuid="a57e7a9d-4ac7-4219-b268-cd08ce943327" x="100" y="150" width="59" height="16" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left"> + <text><![CDATA[Cân nặng:]]></text> + </element> + <element kind="staticText" uuid="2b9cfd9d-ec8f-4036-9c96-039fd7855e24" x="200" y="150" width="59" height="16" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left"> + <text><![CDATA[Nhịp tim:]]></text> + </element> + <element kind="staticText" uuid="c1356370-11ce-4b68-97d6-ce6e18e306c5" x="290" y="150" width="59" height="16" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left"> + <text><![CDATA[Huyết áp:]]></text> + </element> + <element kind="textField" uuid="3e1069a6-daea-45c2-b21f-935b49baba45" x="50" y="150" width="50" height="16" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{checkupWeight} +" cm"]]></expression> + </element> + <element kind="textField" uuid="fb080012-3196-4535-bcd2-e87274c66fa5" x="147" y="150" width="53" height="16" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{checkupWeight} + " kg"]]></expression> + </element> + <element kind="textField" uuid="e4185ffe-8712-41e3-a9ff-2be6569dff15" x="243" y="150" width="47" height="16" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{checkupHeartBeat} + " l/p"]]></expression> + </element> + <element kind="textField" uuid="6b0b21be-2ca3-4594-bbe9-34868e13eb1e" x="335" y="150" width="65" height="16" fontName="Times New Roman" fontSize="10.0"> + <expression><![CDATA[$P{checkupBloodPressure} +" mmHg"]]></expression> + </element> + <element kind="component" uuid="e8842007-3343-4399-a101-76a302d0755d" x="486" y="16" width="53" height="49"> + <component kind="barcode4j:QRCode"> + <codeExpression><![CDATA[$P{driveURL}]]></codeExpression> + </component> + </element> + <element kind="textField" uuid="9763a634-05c2-4f64-a903-c914455b7ee6" x="70" y="57" width="282" height="20" forecolor="#FF0905" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Center"> + <printWhenExpression><![CDATA[$P{templateTitle} != null && !$P{templateTitle}.trim().isEmpty()]]></printWhenExpression> + <expression><![CDATA["PHIẾU " + $P{templateTitle}]]></expression> + </element> + <element kind="staticText" uuid="8f35aa72-22a4-45f0-a35e-8e35e832404e" x="484" y="65" width="55" height="14" fontName="Times New Roman" fontSize="8.0" bold="false" hTextAlign="Center"> + <printWhenExpression><![CDATA[$P{driveURL} != null && !$P{driveURL}.trim().isEmpty()]]></printWhenExpression> + <text><![CDATA[Xem KQ online]]></text> + </element> + <element kind="component" uuid="73f562ce-2891-4577-aec2-7284203c8430" x="394" y="21" width="70" height="49"> + <component kind="barcode4j:Code128"> + <codeExpression><![CDATA[$P{barcodeNumber}]]></codeExpression> + </component> + </element> + <element kind="image" uuid="692eae9f-a304-4cda-856d-7a3a95fac3df" stretchType="NoStretch" x="400" y="91" width="151" height="124"> + <expression><![CDATA[$P{image1}]]></expression> + </element> + <element kind="image" uuid="9a15475c-d08c-4c8f-bbdf-70cf98f97e90" x="400" y="216" width="151" height="124"> + <expression><![CDATA[$P{image2}]]></expression> + </element> + <element kind="image" uuid="2902cf3c-31dd-4b96-be87-fa8a13ba88ae" x="400" y="342" width="151" height="124"> + <expression><![CDATA[$P{image3}]]></expression> + </element> + <element kind="image" uuid="129e6c61-d572-48a9-925e-1ab2e4e573e2" x="400" y="468" width="151" height="129"> + <expression><![CDATA[$P{image4}]]></expression> + </element> + </element> + </band> + </detail> + <summary height="179"> + <element kind="staticText" uuid="b6ee4329-aeb1-4ddf-99e8-41ebebaff12b" x="0" y="0" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="15.0" bold="true"> + <text><![CDATA[III.Kết luận]]></text> + </element> + <element kind="staticText" uuid="f4c12e4d-9aac-428d-bd79-59c3d15f0d87" x="0" y="60" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="15.0" bold="true"> + <text><![CDATA[Đề nghị:]]></text> + </element> + <element kind="staticText" uuid="d1d33b8f-b79a-45a1-b33b-1eecb656f49a" x="400" y="70" width="96" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Center"> + <text><![CDATA[Bác sĩ siêu âm]]></text> + </element> + <element kind="textField" uuid="de0d077c-f1d6-4d0c-8bbd-fb08c19b07c6" x="347" y="140" width="200" height="21" fontName="Times New Roman" fontSize="14.0" bold="true" hTextAlign="Center"> + <expression><![CDATA[$P{ultrasoundDoctorName}]]></expression> + </element> + <element kind="textField" uuid="0b5be2b8-8837-41ee-a805-81f562f5f961" x="347" y="157" width="200" height="19" fontName="Times New Roman" pattern="MMMMM dd, yyyy" hTextAlign="Center" vTextAlign="Bottom"> + <expression><![CDATA[new java.text.SimpleDateFormat("'Đức Hoà, ngày ' dd ' tháng ' MM ' năm ' yyyy", new java.util.Locale("vi", "VN")).format(new java.util.Date())]]></expression> + </element> + <element kind="staticText" uuid="0b4ca7da-4831-4ca6-8272-94af19fd3089" x="0" y="155" width="110" height="14" forecolor="#FF0905" fontName="Times New Roman" fontSize="10.0" bold="true"> + <text><![CDATA[Tái khám:]]></text> + </element> + <element kind="textField" uuid="e4fb5d0b-bbc3-4e42-a431-1bd20bcfb25d" x="0" y="20" width="546" height="38" fontName="Times New Roman" fontSize="12.0"> + <expression><![CDATA[$P{checkupConclusion}]]></expression> + </element> + <element kind="textField" uuid="16b6dfab-1434-4de5-a1c4-619470f3d867" x="0" y="80" width="347" height="66" fontName="Times New Roman" fontSize="12.0"> + <expression><![CDATA[$P{checkupSuggestion}]]></expression> + </element> + <element kind="textField" uuid="4d8aeb33-ab41-4a5b-a5d1-52bab4043246" x="48" y="155" width="150" height="15" fontName="Times New Roman" fontSize="12.0"> + <expression><![CDATA[$P{reCheckupDate}]]></expression> + </element> + </summary> +</jasperReport> diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/TemplateDialog/TemplateDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/TemplateDialog/TemplateDialog.java index 4837c9e..b92c098 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/TemplateDialog/TemplateDialog.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/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", "5", "6"}); + imageCountComboBox = new JComboBox<>(new String[]{"1", "2", "3", "4"}); printTypeComboBox = new JComboBox<>(new String[]{"Ngang", "Dọc"}); sttField = new JTextField(10); diff --git a/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java b/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java index ec0b180..b564c42 100644 --- a/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java +++ b/src/main/java/BsK/client/ui/component/QueueViewPage/QueueViewPage.java @@ -18,7 +18,7 @@ import java.util.List; public class QueueViewPage extends JFrame { private DefaultTableModel tvQueueTableModel; private JTable tvQueueTable; - private final String[] tvQueueColumns = {"Mã khám bệnh", "Họ và Tên", "Năm sinh"}; + private final String[] tvQueueColumns = {"STT", "Họ và Tên", "Năm sinh"}; private JLabel clinicNameLabel; private JLabel clinicAddressLabel; @@ -211,7 +211,7 @@ public class QueueViewPage extends JFrame { } } - public void updateSpecificRoomStatus(int roomId, String patientIdForRoomBox, String fullPatientInfoForCentralDisplay, BsK.common.entity.Status status) { + public void updateSpecificRoomStatus(int roomId, String patientIdForRoomBox, String queueNumberForRoomBox, String fullPatientInfoForCentralDisplay, BsK.common.entity.Status status) { JLabel targetLabel; JPanel targetPanel; String roomName; @@ -230,7 +230,7 @@ public class QueueViewPage extends JFrame { } if (status == BsK.common.entity.Status.PROCESSING) { - styleRoomLabel(targetLabel, targetPanel, Color.RED.darker(), "Mã BN: " + patientIdForRoomBox); + styleRoomLabel(targetLabel, targetPanel, Color.RED.darker(), "STT: " + queueNumberForRoomBox); if (roomId == 1) { room1PatientInfo = fullPatientInfoForCentralDisplay; } else { @@ -289,8 +289,8 @@ public class QueueViewPage extends JFrame { SimpleDateFormat inputDateFormat = new SimpleDateFormat("dd/MM/yyyy"); for (String[] row : fullQueueData) { - if (row.length > 15) { // Changed from > 3 to > 15 to ensure we have DOB data - String maKhamBenh = row[0]; + if (row.length > 24) { // Ensure we have all data including queue number + String queueNumber = row[24]; // Use the new queue number from index 24 String ho = row[2]; String ten = row[3]; String hoVaTen = ho + " " + ten; @@ -316,7 +316,7 @@ public class QueueViewPage extends JFrame { } } } - tvDataList.add(new Object[]{maKhamBenh, hoVaTen, namSinh}); + tvDataList.add(new Object[]{queueNumber, hoVaTen, namSinh}); } } tvQueueTableModel.setDataVector(tvDataList.toArray(new Object[0][0]), tvQueueColumns); diff --git a/src/main/java/BsK/common/entity/Patient.java b/src/main/java/BsK/common/entity/Patient.java index 18ef7c3..2e25c59 100644 --- a/src/main/java/BsK/common/entity/Patient.java +++ b/src/main/java/BsK/common/entity/Patient.java @@ -30,6 +30,8 @@ public class Patient { private String heartBeat; private String bloodPressure; private String driveUrl; + private String doctorUltrasoundId; + private String queueNumber; public Patient(String[][] array) { this.checkupId = array[0][0]; @@ -55,12 +57,14 @@ public class Patient { this.heartBeat = array[0][20]; this.bloodPressure = array[0][21]; this.driveUrl = array[0][22]; + this.doctorUltrasoundId = array[0][23]; + this.queueNumber = array[0][24]; } // New constructor for a single patient data row public Patient(String[] data) { - if (data.length < 23) { - throw new IllegalArgumentException("Patient data array must contain at least 23 elements to include a conclusion."); + if (data.length < 25) { + throw new IllegalArgumentException("Patient data array must contain at least 25 elements to include a conclusion."); } this.checkupId = data[0]; this.checkupDate = data[1]; @@ -85,5 +89,7 @@ public class Patient { this.heartBeat = data[20]; this.bloodPressure = data[21]; this.driveUrl = data[22]; + this.doctorUltrasoundId = data[23]; + this.queueNumber = data[24]; } } diff --git a/src/main/java/BsK/common/packet/req/CallPatientRequest.java b/src/main/java/BsK/common/packet/req/CallPatientRequest.java index 275a7c3..0b922f1 100644 --- a/src/main/java/BsK/common/packet/req/CallPatientRequest.java +++ b/src/main/java/BsK/common/packet/req/CallPatientRequest.java @@ -10,5 +10,6 @@ import lombok.Data; public class CallPatientRequest implements Packet { int roomId; int patientId; + String queueNumber; Status status; } diff --git a/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java b/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java index 1429100..7f5a408 100644 --- a/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java +++ b/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java @@ -11,6 +11,7 @@ public class SaveCheckupRequest implements Packet { Integer checkupId; Integer customerId; Integer doctorId; + Integer doctorUltrasoundId; Long checkupDate; // Checkup Details @@ -21,6 +22,7 @@ public class SaveCheckupRequest implements Packet { String checkupType; String conclusion; Long reCheckupDate; + Boolean needsRecheckup; // Flag to indicate if re-checkup is needed // Patient Details String customerFirstName; diff --git a/src/main/java/BsK/common/packet/res/CallPatientResponse.java b/src/main/java/BsK/common/packet/res/CallPatientResponse.java index d88ee57..d3ecdbf 100644 --- a/src/main/java/BsK/common/packet/res/CallPatientResponse.java +++ b/src/main/java/BsK/common/packet/res/CallPatientResponse.java @@ -10,5 +10,6 @@ import lombok.Data; public class CallPatientResponse implements Packet { int patientId; int roomId; + String queueNumber; Status status; } diff --git a/src/main/java/BsK/server/network/handler/ServerHandler.java b/src/main/java/BsK/server/network/handler/ServerHandler.java index e08fd3d..30b30b6 100644 --- a/src/main/java/BsK/server/network/handler/ServerHandler.java +++ b/src/main/java/BsK/server/network/handler/ServerHandler.java @@ -109,7 +109,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram "select a.checkup_id, a.checkup_date, c.customer_last_name, c.customer_first_name,\n" + "d.doctor_first_name, d.doctor_last_name, a.suggestion, a.diagnosis, a.notes, a.status, a.customer_id, \n" + "c.customer_number, c.customer_address, a.customer_weight, a.customer_height, c.customer_gender, c.customer_dob,\n" + - "a.checkup_type, a.conclusion, a.reCheckupDate, c.cccd_ddcn, a.heart_beat, a.blood_pressure, c.drive_url\n" + + "a.checkup_type, a.conclusion, a.reCheckupDate, c.cccd_ddcn, a.heart_beat, a.blood_pressure, c.drive_url, a.doctor_ultrasound_id, a.queue_number\n" + "from checkup as a\n" + "join customer as c on a.customer_id = c.customer_id\n" + "join Doctor D on a.doctor_id = D.doctor_id\n" + @@ -151,6 +151,8 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram String heartBeat = rs.getString("heart_beat"); String bloodPressure = rs.getString("blood_pressure"); String driveUrl = rs.getString("drive_url"); + String doctorUltrasoundId = rs.getString("doctor_ultrasound_id"); + String queueNumber = String.format("%02d", rs.getInt("queue_number")); if (driveUrl == null) { driveUrl = ""; } @@ -159,7 +161,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram doctorLastName + " " + doctorFirstName, suggestion, diagnosis, notes, status, customerId, customerNumber, customerAddress, customerWeight, customerHeight, customerGender, customerDob, checkupType, conclusion, reCheckupDate, cccdDdcn, heartBeat, bloodPressure, - driveUrl + driveUrl, doctorUltrasoundId, queueNumber ); resultList.add(result); @@ -184,7 +186,8 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram ResultSet rs = statement.executeQuery( "select a.checkup_id, a.checkup_date, c.customer_last_name, c.customer_first_name,\n" + "d.doctor_first_name, d.doctor_last_name, a.suggestion, a.diagnosis, a.notes, a.status, a.customer_id, \n" + - "c.customer_number, c.customer_address, a.customer_weight, a.customer_height, c.customer_gender, c.customer_dob, a.checkup_type, a.conclusion, a.reCheckupDate, c.cccd_ddcn, a.heart_beat, a.blood_pressure, c.drive_url\n" + + "c.customer_number, c.customer_address, a.customer_weight, a.customer_height, c.customer_gender, c.customer_dob, \n" + + "a.checkup_type, a.conclusion, a.reCheckupDate, c.cccd_ddcn, a.heart_beat, a.blood_pressure, c.drive_url, a.doctor_ultrasound_id, a.queue_number\n" + "from checkup as a\n" + "join customer as c on a.customer_id = c.customer_id\n" + "join Doctor D on a.doctor_id = D.doctor_id\n" + @@ -230,6 +233,8 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram String heartBeat = rs.getString("heart_beat"); String bloodPressure = rs.getString("blood_pressure"); String driveUrl = rs.getString("drive_url"); + String doctorUltrasoundId = rs.getString("doctor_ultrasound_id"); + String queueNumber = String.format("%02d", rs.getInt("queue_number")); if (driveUrl == null) { driveUrl = ""; } @@ -238,7 +243,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram doctorLastName + " " + doctorFirstName, suggestion, diagnosis, notes, status, customerId, customerNumber, customerAddress, customerWeight, customerHeight, customerGender, customerDob, checkupType, conclusion, reCheckupDate, cccdDdcn, heartBeat, bloodPressure, - driveUrl + driveUrl, doctorUltrasoundId, queueNumber ); resultList.add(result); @@ -676,13 +681,44 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram // Disable auto-commit Server.connection.setAutoCommit(false); + int queueNumber = 0; + // Use a transaction to ensure atomic operations + try { + // First, try to update an existing counter for today + try (PreparedStatement updateStmt = Server.connection.prepareStatement( + "UPDATE DailyQueueCounter SET current_count = current_count + 1 " + + "WHERE date = date('now', 'localtime') RETURNING current_count")) { + ResultSet rs = updateStmt.executeQuery(); + if (rs.next()) { + // Update successful, get the new value + queueNumber = rs.getInt(1); + } else { + // No row for today exists yet, insert one + try (PreparedStatement insertStmt = Server.connection.prepareStatement( + "INSERT INTO DailyQueueCounter (date, current_count) VALUES (date('now', 'localtime'), 1) " + + "RETURNING current_count")) { + ResultSet insertRs = insertStmt.executeQuery(); + if (insertRs.next()) { + queueNumber = insertRs.getInt(1); // Should be 1 + } else { + throw new SQLException("Failed to insert new queue counter"); + } + } + } + } + } catch (SQLException e) { + log.error("Error updating queue counter", e); + throw new RuntimeException(e); + } + // First query: Insert Checkup PreparedStatement checkupStmt = Server.connection.prepareStatement( - "INSERT INTO Checkup (customer_id, doctor_id, checkup_type, status) VALUES (?, ?, ?, ?)"); + "INSERT INTO Checkup (customer_id, doctor_id, checkup_type, status, queue_number) VALUES (?, ?, ?, ?, ?)"); checkupStmt.setInt(1, addCheckupRequest.getCustomerId()); checkupStmt.setInt(2, addCheckupRequest.getDoctorId()); checkupStmt.setString(3, addCheckupRequest.getCheckupType()); checkupStmt.setString(4, addCheckupRequest.getStatus()); + checkupStmt.setInt(5, queueNumber); checkupStmt.executeUpdate(); // Second query: Insert MedicineOrder @@ -742,14 +778,15 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram } if (packet instanceof CallPatientRequest callPatientRequest) { - log.debug("Received CallPatientRequest to call patient{}", callPatientRequest.getPatientId()); - int patientId = callPatientRequest.getPatientId(); + log.debug("Received CallPatientRequest to call patient checkup_id: {}", callPatientRequest.getPatientId()); + int checkupId = callPatientRequest.getPatientId(); // This is the checkup_id int roomId = callPatientRequest.getRoomId(); Status status = callPatientRequest.getStatus(); - // send to all clients + String queueNumber = callPatientRequest.getQueueNumber(); + // send to all clients with the queue number included int maxCurId = SessionManager.getMaxSessionId(); for (int sessionId = 1; sessionId <= maxCurId; sessionId++) { - UserUtil.sendPacket(sessionId, new CallPatientResponse(patientId, roomId, status)); + UserUtil.sendPacket(sessionId, new CallPatientResponse(checkupId, roomId, queueNumber, status)); } } @@ -809,8 +846,9 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram String checkupSql = """ INSERT INTO Checkup ( checkup_id, customer_id, doctor_id, checkup_date, - suggestion, diagnosis, prescription_id, notes, status, checkup_type, conclusion, reCheckupDate, customer_weight, customer_height, heart_beat, blood_pressure - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + suggestion, diagnosis, prescription_id, notes, status, checkup_type, conclusion, reCheckupDate, customer_weight, customer_height, + heart_beat, blood_pressure, doctor_ultrasound_id + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(checkup_id) DO UPDATE SET suggestion = excluded.suggestion, diagnosis = excluded.diagnosis, @@ -823,7 +861,9 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram customer_weight = excluded.customer_weight, customer_height = excluded.customer_height, heart_beat = excluded.heart_beat, - blood_pressure = excluded.blood_pressure + blood_pressure = excluded.blood_pressure, + doctor_ultrasound_id = excluded.doctor_ultrasound_id, + doctor_id = excluded.doctor_id """; PreparedStatement checkupStmt = conn.prepareStatement(checkupSql); @@ -843,6 +883,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram checkupStmt.setDouble(14, saveCheckupRequest.getCustomerHeight()); checkupStmt.setInt(15, saveCheckupRequest.getHeartBeat()); checkupStmt.setString(16, saveCheckupRequest.getBloodPressure()); + checkupStmt.setInt(17, saveCheckupRequest.getDoctorUltrasoundId()); checkupStmt.executeUpdate(); log.info("Prescription ID generated or edited: {}", prescriptionId); log.info("Checkup saved successfully"); diff --git a/src/main/resources/database/BSK.db b/src/main/resources/database/BSK.db index cd13455..3cac793 100644 Binary files a/src/main/resources/database/BSK.db and b/src/main/resources/database/BSK.db differ