diff --git a/.idea/runConfigurations/BSK_Client.xml b/.idea/runConfigurations/BSK_Client.xml new file mode 100644 index 0000000..70202fb --- /dev/null +++ b/.idea/runConfigurations/BSK_Client.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/.idea/runConfigurations/BSK_Server.xml b/.idea/runConfigurations/BSK_Server.xml new file mode 100644 index 0000000..dcad2e6 --- /dev/null +++ b/.idea/runConfigurations/BSK_Server.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..55e4357 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,72 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Client", + "request": "launch", + "mainClass": "BsK.client.Client", + "projectName": "ldS_BsK" + }, + { + "type": "java", + "name": "Server", + "request": "launch", + "mainClass": "BsK.server.Server", + "projectName": "ldS_BsK" + }, + { + "type": "java", + "name": "BSK Server", + "request": "launch", + "mainClass": "BsK.server.Server", + "projectName": "BSK-All-in-One-Clinic-Management-System", + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "internalConsoleOptions": "openOnSessionStart", + "args": [], + "vmArgs": [], + "env": {}, + "preLaunchTask": "${command:java.clean.workspace}" + }, + { + "type": "java", + "name": "BSK Client", + "request": "launch", + "mainClass": "BsK.client.Client", + "projectName": "BSK-All-in-One-Clinic-Management-System", + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "internalConsoleOptions": "openOnSessionStart", + "args": [], + "vmArgs": [], + "env": {} + }, + { + "type": "java", + "name": "BSK Client (Instance 2)", + "request": "launch", + "mainClass": "BsK.client.Client", + "projectName": "BSK-All-in-One-Clinic-Management-System", + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "internalConsoleOptions": "openOnSessionStart", + "args": [], + "vmArgs": [], + "env": {} + }, + { + "type": "java", + "name": "BSK Client (Instance 3)", + "request": "launch", + "mainClass": "BsK.client.Client", + "projectName": "BSK-All-in-One-Clinic-Management-System", + "cwd": "${workspaceFolder}", + "console": "internalConsole", + "internalConsoleOptions": "openOnSessionStart", + "args": [], + "vmArgs": [], + "env": {} + } + ] +} \ No newline at end of file diff --git a/medical_invoice.pdf b/medical_invoice.pdf index 175b575..f98e23a 100644 Binary files a/medical_invoice.pdf and b/medical_invoice.pdf differ diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java index 30c94c1..282c379 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/AddDialog/AddDialog.java @@ -173,9 +173,30 @@ public class AddDialog extends JDialog { // Set size of the dialog setSize(1000, 400); - // Put in the middle - setLocationRelativeTo(null); + // Put in the middle of parent window + setLocationRelativeTo(parent); setResizable(true); + + // Ensure modal behavior and proper parent relationship + setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this + + // Add window listener to handle minimize/restore events with parent + addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (parent != null) { + parent.setState(Frame.ICONIFIED); + } + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (parent != null && parent.getState() == Frame.ICONIFIED) { + parent.setState(Frame.NORMAL); + } + } + }); // Send request to get the latest 20 patients in the database sendGetRecentPatientRequest(); 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 6505831..ec27195 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java @@ -38,6 +38,8 @@ import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.awt.datatransfer.*; import java.text.DecimalFormat; import java.text.ParseException; @@ -95,6 +97,11 @@ import java.io.ByteArrayInputStream; import java.io.OutputStream; import java.io.InputStream; +// Add new imports +import BsK.common.entity.PatientHistory; +import BsK.common.packet.req.GetPatientHistoryRequest; +import BsK.common.packet.res.GetPatientHistoryResponse; + @Slf4j public class CheckUpPage extends JPanel { private MainFrame mainFrame; @@ -105,7 +112,7 @@ public class CheckUpPage extends JPanel { private JTable historyTable; private final ResponseListener checkUpQueueUpdateListener = this::handleGetCheckUpQueueUpdateResponse; private final ResponseListener checkUpQueueListener = this::handleGetCheckUpQueueResponse; - private final ResponseListener customerHistoryListener = this::handleGetCustomerHistoryResponse; + private final ResponseListener patientHistoryListener = this::handleGetPatientHistoryResponse; private final ResponseListener districtResponseListener = this::handleGetDistrictResponse; private final ResponseListener wardResponseListener = this::handleGetWardResponse; private final ResponseListener callPatientResponseListener = this::handleCallPatientResponse; @@ -264,7 +271,7 @@ public class CheckUpPage extends JPanel { ClientHandler.addResponseListener(GetCheckUpQueueResponse.class, checkUpQueueListener); ClientHandler.addResponseListener(GetCheckUpQueueUpdateResponse.class, checkUpQueueUpdateListener); - ClientHandler.addResponseListener(GetCustomerHistoryResponse.class, customerHistoryListener); + ClientHandler.addResponseListener(GetPatientHistoryResponse.class, patientHistoryListener); ClientHandler.addResponseListener(GetDistrictResponse.class, districtResponseListener); ClientHandler.addResponseListener(GetWardResponse.class, wardResponseListener); ClientHandler.addResponseListener(CallPatientResponse.class, callPatientResponseListener); @@ -1047,7 +1054,8 @@ public class CheckUpPage extends JPanel { public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 1) { int selectedRow = historyTable.getSelectedRow(); - // if (selectedRow != -1) { System.out.println("Clicked on history row: " + selectedRow); } + // send getPatientInfoByCheckupIdReq + } } }); @@ -1436,9 +1444,9 @@ public class CheckUpPage extends JPanel { customerLastNameField.setText(selectedPatient.getCustomerLastName()); customerFirstNameField.setText(selectedPatient.getCustomerFirstName()); doctorComboBox.setSelectedItem(selectedPatient.getDoctorName()); - suggestionField.setText(selectedPatient.getSymptoms()); - diagnosisField.setText(selectedPatient.getDiagnosis()); - conclusionField.setText(""); // Clear conclusion field for now + suggestionField.setText(selectedPatient.getSuggestion()); // De nghi + diagnosisField.setText(selectedPatient.getDiagnosis()); // Chuan doan + conclusionField.setText(selectedPatient.getConclusion()); // Ket luan // Handle RTF notes content String notes = selectedPatient.getNotes(); @@ -1469,7 +1477,7 @@ public class CheckUpPage extends JPanel { genderComboBox.setSelectedItem(selectedPatient.getCustomerGender()); checkupTypeComboBox.setSelectedItem(selectedPatient.getCheckupType()); - NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCustomerHistoryRequest(Integer.parseInt(selectedPatient.getCustomerId()))); + NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetPatientHistoryRequest(Integer.parseInt(selectedPatient.getCustomerId()))); String fullAddress = selectedPatient.getCustomerAddress(); String[] addressParts = fullAddress.split(", "); @@ -1578,9 +1586,13 @@ public class CheckUpPage extends JPanel { } } - private void handleGetCustomerHistoryResponse(GetCustomerHistoryResponse response) { - log.info("Received customer history"); + private void handleGetPatientHistoryResponse(GetPatientHistoryResponse response) { + log.info("Received patient history"); this.history = response.getHistory(); + + // Access PatientHistory objects if needed + List patientHistories = response.getPatientHistoryList(); + historyModel.setDataVector(this.history, new String[]{"Ngày Tháng","Mã khám bệnh", "Triệu chứng", "Chẩn đoán", "Ghi chú"}); } @@ -2400,6 +2412,29 @@ public class CheckUpPage extends JPanel { setIconImage(new ImageIcon("src/main/java/BsK/client/ui/assets/icon/database.png").getImage()); setSize(800, 600); setLayout(new BorderLayout()); + + // Add window listener to handle minimize/restore events with parent + addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (mainFrame != null) { + mainFrame.setState(java.awt.Frame.ICONIFIED); + } + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (mainFrame != null && mainFrame.getState() == java.awt.Frame.ICONIFIED) { + mainFrame.setState(java.awt.Frame.NORMAL); + } + } + + @Override + public void windowClosing(WindowEvent e) { + // Hide instead of closing to maintain state + setVisible(false); + } + }); String[] columns = {"Mã khám bệnh", "Ngày sinh", "Họ", "Tên", "Bác Sĩ", "Loại khám"}; queueTableModel = new DefaultTableModel(preprocessPatientDataForTable(patientQueue), columns) { @@ -2622,7 +2657,7 @@ public class CheckUpPage extends JPanel { String customerNumber = customerPhoneField.getText(); String customerWeight = customerWeightSpinner.getValue().toString(); String customerHeight = customerHeightSpinner.getValue().toString(); - String doctorName = (String) doctorComboBox.getSelectedItem(); + String doctorId = String.valueOf(doctorComboBox.getSelectedIndex() + 1); String suggestions = suggestionField.getText(); String diagnosis = diagnosisField.getText(); String conclusion = conclusionField.getText(); @@ -2642,7 +2677,7 @@ public class CheckUpPage extends JPanel { log.info("customerNumber: {}", customerNumber); log.info("customerWeight: {}", customerWeight); log.info("customerHeight: {}", customerHeight); - log.info("doctorName: {}", doctorName); + log.info("doctorId: {}", doctorId); log.info("suggestions: {}", suggestions); log.info("diagnosis: {}", diagnosis); log.info("conclusion: {}", conclusion); @@ -2653,7 +2688,7 @@ public class CheckUpPage extends JPanel { log.info("servicePrescription: {}", (Object) servicePrescription); log.info("=== End SaveCheckupRequest Data ==="); - // Create and send the save request + SaveCheckupRequest request = new SaveCheckupRequest( checkupId, checkupDate, @@ -2666,12 +2701,13 @@ public class CheckUpPage extends JPanel { customerNumber, customerWeight, customerHeight, - doctorName, - suggestions, - diagnosis, + doctorId, + suggestions, // De nghi + diagnosis, // Chuan doan rtfContent, // Send RTF content as notes status, checkupType, + conclusion, // Ket luan medicinePrescription, servicePrescription ); diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java index d617537..01fcc30 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java @@ -8,6 +8,7 @@ import javax.swing.table.TableColumnModel; import BsK.client.network.handler.ClientHandler; import BsK.client.network.handler.ResponseListener; +import BsK.common.entity.Medicine; import BsK.common.packet.req.GetMedInfoRequest; import BsK.common.packet.res.GetMedInfoResponse; import BsK.common.util.network.NetworkUtil; @@ -21,6 +22,8 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -47,6 +50,7 @@ public class MedicineDialog extends JDialog { private JTable selectedTable; private boolean isProgrammaticallySettingMedicineNameField = false; + private List medicines = new ArrayList<>(); private String[][] medicinePrescription; private static final Logger logger = LoggerFactory.getLogger(MedicineDialog.class); @@ -57,6 +61,11 @@ public class MedicineDialog extends JDialog { void getMedInfoHandler(GetMedInfoResponse response) { logger.info("Received medicine data"); medicineData = response.getMedInfo(); + + // Get Medicine objects from the response + medicines = response.getMedicines(); + + // Set the data vector with the raw string arrays for backward compatibility tableModel.setDataVector(medicineData, medcineColumns); // resize column width @@ -81,6 +90,27 @@ public class MedicineDialog extends JDialog { setSize(1100, 600); setLocationRelativeTo(parent); setResizable(true); + + // Ensure modal behavior and proper parent relationship + setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this + + // Add window listener to handle minimize/restore events with parent + addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (parent != null) { + parent.setState(Frame.ICONIFIED); + } + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (parent != null && parent.getState() == Frame.ICONIFIED) { + parent.setState(Frame.NORMAL); + } + } + }); // Initialize medicinePrescription as empty array medicinePrescription = new String[0][0]; @@ -325,12 +355,29 @@ public class MedicineDialog extends JDialog { JButton removeMedicineButton = new JButton("Xóa thuốc đã chọn"); removeButtonPanel.add(removeMedicineButton); + // Create titled panels for right side sections + JPanel availableMedicinesPanel = new JPanel(new BorderLayout()); + availableMedicinesPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Thuốc trong kho", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(63, 81, 181) + )); + availableMedicinesPanel.add(medicineTableScrollPane, BorderLayout.CENTER); + + JPanel chosenMedicinesPanel = new JPanel(new BorderLayout()); + chosenMedicinesPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Thuốc đã chọn", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(220, 20, 60) + )); + chosenMedicinesPanel.setBackground(new Color(255, 240, 240)); // Light red background + selectedTableScrollPane.setBackground(new Color(255, 240, 240)); // Light red background + chosenMedicinesPanel.add(selectedTableScrollPane, BorderLayout.CENTER); + chosenMedicinesPanel.add(removeButtonPanel, BorderLayout.SOUTH); + JPanel rightPanel = new JPanel(new BorderLayout(0, 5)); - rightPanel.add(medicineTableScrollPane, BorderLayout.CENTER); - JPanel bottomRightsPanel = new JPanel(new BorderLayout(0,5)); - bottomRightsPanel.add(selectedTableScrollPane, BorderLayout.CENTER); - bottomRightsPanel.add(removeButtonPanel, BorderLayout.SOUTH); - rightPanel.add(bottomRightsPanel, BorderLayout.SOUTH); + rightPanel.add(availableMedicinesPanel, BorderLayout.CENTER); + rightPanel.add(chosenMedicinesPanel, BorderLayout.SOUTH); JScrollPane mainInputScrollPane = new JScrollPane(mainInputPanel); mainInputScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); @@ -439,12 +486,16 @@ public class MedicineDialog extends JDialog { medicineTable.clearSelection(); } else { List filteredData = new ArrayList<>(); + List filteredMedicines = new ArrayList<>(); String lowerCaseFilterText = TextUtils.removeAccents(filterText.toLowerCase()); - for (String[] row : medicineData) { - if (row.length > 1 && row[1] != null && TextUtils.removeAccents(row[1].toLowerCase()).contains(lowerCaseFilterText)) { - filteredData.add(row); + + for (Medicine med : medicines) { + if (TextUtils.removeAccents(med.getName().toLowerCase()).contains(lowerCaseFilterText)) { + filteredMedicines.add(med); + filteredData.add(med.toStringArray()); } } + tableModel.setDataVector(filteredData.toArray(new String[0][0]), medcineColumns); resizeMedicineTableColumns(); if (!filteredData.isEmpty()) { @@ -534,18 +585,23 @@ public class MedicineDialog extends JDialog { currentPrice = tableModel.getValueAt(modelRow, 6).toString(); } else { String searchName = medicineNameField.getText().trim(); - String foundId = null, foundUnit = null, foundPrice = null, actualName = null; - for (String[] med : medicineData) { - if (TextUtils.removeAccents(med[1].toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) { - foundId = med[0]; actualName = med[1]; foundUnit = med[5]; foundPrice = med[6]; + Medicine foundMedicine = null; + + for (Medicine med : medicines) { + if (TextUtils.removeAccents(med.getName().toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) { + foundMedicine = med; break; } } - if (foundId == null) { + + if (foundMedicine == null) { JOptionPane.showMessageDialog(this, "Không tìm thấy ID cho thuốc: " + searchName + ". Vui lòng chọn từ bảng.", "Lỗi", JOptionPane.ERROR_MESSAGE); return; } - medicineId = foundId; name = actualName; unit = foundUnit; currentPrice = foundPrice; + medicineId = foundMedicine.getId(); + name = foundMedicine.getName(); + unit = foundMedicine.getUnit(); + currentPrice = foundMedicine.getSellingPrice(); } int quantity = (Integer) quantitySpinner.getValue(); diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java index 4439a53..1af05bf 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java @@ -32,6 +32,8 @@ import java.util.ArrayList; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.text.DecimalFormat; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; public class MedicineInvoice{ @@ -107,6 +109,27 @@ public class MedicineInvoice{ dialog.setResizable(true); dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); dialog.setLayout(new BorderLayout()); + + // Ensure modal behavior and proper parent relationship + dialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + dialog.setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this + + // Add window listener to handle minimize/restore events with parent + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (parent != null) { + parent.setState(Frame.ICONIFIED); + } + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (parent != null && parent.getState() == Frame.ICONIFIED) { + parent.setState(Frame.NORMAL); + } + } + }); JPanel buttonPanel = new JPanel(new GridLayout(1, 2)); JButton saveButton = new JButton("Save PDF"); diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java index f618674..e12bf5a 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java @@ -8,6 +8,7 @@ import javax.swing.table.TableColumnModel; import BsK.client.network.handler.ClientHandler; import BsK.client.network.handler.ResponseListener; +import BsK.common.entity.Service; import BsK.common.packet.req.GetSerInfoRequest; import BsK.common.packet.res.GetSerInfoResponse; import BsK.common.util.network.NetworkUtil; @@ -21,6 +22,8 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -41,6 +44,7 @@ public class ServiceDialog extends JDialog { private JTable selectedTable; private boolean isProgrammaticallySettingNameField = false; + private List services = new ArrayList<>(); private String[][] servicePrescription; private static final Logger logger = LoggerFactory.getLogger(ServiceDialog.class); @@ -51,6 +55,11 @@ public class ServiceDialog extends JDialog { void getSerInfoHandler(GetSerInfoResponse response) { logger.info("Received service data"); serviceData = response.getSerInfo(); + + // Get Service objects from the response + services = response.getServices(); + + // Set the data vector with the raw string arrays for backward compatibility tableModel.setDataVector(serviceData, serviceColumns); resizeServiceTableColumns(); } @@ -66,6 +75,27 @@ public class ServiceDialog extends JDialog { setSize(1100, 600); setLocationRelativeTo(parent); setResizable(true); + + // Ensure modal behavior and proper parent relationship + setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this + + // Add window listener to handle minimize/restore events with parent + addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (parent != null) { + parent.setState(Frame.ICONIFIED); + } + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (parent != null && parent.getState() == Frame.ICONIFIED) { + parent.setState(Frame.NORMAL); + } + } + }); // Initialize servicePrescription as empty array servicePrescription = new String[0][0]; @@ -217,12 +247,29 @@ public class ServiceDialog extends JDialog { JButton removeServiceButton = new JButton("Xóa dịch vụ đã chọn"); removeServiceButtonPanel.add(removeServiceButton); + // Create titled panels for right side sections + JPanel availableServicesPanel = new JPanel(new BorderLayout()); + availableServicesPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Dịch vụ có", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(63, 81, 181) + )); + availableServicesPanel.add(serviceTableScrollPane, BorderLayout.CENTER); + + JPanel chosenServicesPanel = new JPanel(new BorderLayout()); + chosenServicesPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Dịch vụ đã chọn", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(220, 20, 60) + )); + chosenServicesPanel.setBackground(new Color(255, 240, 240)); // Light red background + selectedTableScrollPane.setBackground(new Color(255, 240, 240)); // Light red background + chosenServicesPanel.add(selectedTableScrollPane, BorderLayout.CENTER); + chosenServicesPanel.add(removeServiceButtonPanel, BorderLayout.SOUTH); + JPanel rightPanel = new JPanel(new BorderLayout(0,5)); - rightPanel.add(serviceTableScrollPane, BorderLayout.CENTER); - JPanel bottomRightPanel = new JPanel(new BorderLayout(0,5)); - bottomRightPanel.add(selectedTableScrollPane, BorderLayout.CENTER); - bottomRightPanel.add(removeServiceButtonPanel, BorderLayout.SOUTH); - rightPanel.add(bottomRightPanel, BorderLayout.SOUTH); + rightPanel.add(availableServicesPanel, BorderLayout.CENTER); + rightPanel.add(chosenServicesPanel, BorderLayout.SOUTH); JScrollPane mainInputScrollPane = new JScrollPane(mainInputPanel); mainInputScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); @@ -333,30 +380,30 @@ public class ServiceDialog extends JDialog { private void filterServiceTable() { String filterText = serviceNameField.getText().trim(); if (serviceData == null) return; - if (filterText.isEmpty()) { tableModel.setDataVector(serviceData, serviceColumns); resizeServiceTableColumns(); serviceTable.clearSelection(); } else { List filteredData = new ArrayList<>(); + List filteredServices = new ArrayList<>(); String lowerCaseFilterText = TextUtils.removeAccents(filterText.toLowerCase()); - for (String[] row : serviceData) { - if (row.length > 1 && row[1] != null) { - String cellValue = TextUtils.removeAccents(row[1].toLowerCase()); - if (cellValue.contains(lowerCaseFilterText)) { - filteredData.add(row); - } + + for (Service service : services) { + if (TextUtils.removeAccents(service.getName().toLowerCase()).contains(lowerCaseFilterText)) { + filteredServices.add(service); + filteredData.add(service.toStringArray()); } } + tableModel.setDataVector(filteredData.toArray(new String[0][0]), serviceColumns); resizeServiceTableColumns(); - if (!filteredData.isEmpty()) { + if (!filteredData.isEmpty()) { serviceTable.setRowSelectionInterval(0,0); handleServiceTableRowSelection(0); } else { - clearInputFields(); - } + clearInputFields(); + } } } @@ -416,9 +463,7 @@ public class ServiceDialog extends JDialog { return; } - String serviceId; - String name; - String currentPrice; + String serviceId, name, currentPrice; if (selectedRowInServiceTable != -1) { int modelRow = serviceTable.convertRowIndexToModel(selectedRowInServiceTable); @@ -427,26 +472,22 @@ public class ServiceDialog extends JDialog { currentPrice = tableModel.getValueAt(modelRow, 2).toString(); } else { String searchName = serviceNameField.getText().trim(); - String foundId = null; - String foundPrice = null; - String actualName = null; - - for (String[] serv : serviceData) { - if (TextUtils.removeAccents(serv[1].toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) { - foundId = serv[0]; - actualName = serv[1]; - foundPrice = serv[2]; + Service foundService = null; + + for (Service service : services) { + if (TextUtils.removeAccents(service.getName().toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) { + foundService = service; break; } } - - if (foundId == null) { + + if (foundService == null) { JOptionPane.showMessageDialog(this, "Không tìm thấy ID cho dịch vụ: " + searchName + ". Vui lòng chọn từ bảng.", "Lỗi", JOptionPane.ERROR_MESSAGE); return; } - serviceId = foundId; - name = actualName; - currentPrice = foundPrice; + serviceId = foundService.getId(); + name = foundService.getName(); + currentPrice = foundService.getCost(); } int quantity = (Integer) quantitySpinner.getValue(); 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 871f02b..2d01b35 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 @@ -14,6 +14,8 @@ import javax.swing.text.StyleConstants; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; @Slf4j public class TemplateDialog extends JDialog { @@ -46,6 +48,27 @@ public class TemplateDialog extends JDialog { super(mainFrame, "Quản lý mẫu", true); this.mainFrame = mainFrame; + // Ensure modal behavior and proper parent relationship + setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this + + // Add window listener to handle minimize/restore events with parent + addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (mainFrame != null) { + mainFrame.setState(Frame.ICONIFIED); + } + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (mainFrame != null && mainFrame.getState() == Frame.ICONIFIED) { + mainFrame.setState(Frame.NORMAL); + } + } + }); + initializeComponents(); setupLayout(); setupListeners(); diff --git a/src/main/java/BsK/common/entity/Medicine.java b/src/main/java/BsK/common/entity/Medicine.java new file mode 100644 index 0000000..bd6341f --- /dev/null +++ b/src/main/java/BsK/common/entity/Medicine.java @@ -0,0 +1,46 @@ +package BsK.common.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Entity class for Medicine + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Medicine { + private String id; + private String name; + private String company; + private String description; + private String quantity; + private String unit; + private String sellingPrice; + + /** + * Constructor to create Medicine from array data + * @param data Array of medicine data from backend + */ + public Medicine(String[] data) { + if (data.length < 7) { + throw new IllegalArgumentException("Medicine data array must contain at least 7 elements"); + } + this.id = data[0]; + this.name = data[1]; + this.company = data[2]; + this.description = data[3]; + this.quantity = data[4]; + this.unit = data[5]; + this.sellingPrice = data[6]; + } + + /** + * Convert the Medicine entity to a String array + * @return String array representation of Medicine + */ + public String[] toStringArray() { + return new String[]{id, name, company, description, quantity, unit, sellingPrice}; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/common/entity/Patient.java b/src/main/java/BsK/common/entity/Patient.java index 11f5f90..5152ee0 100644 --- a/src/main/java/BsK/common/entity/Patient.java +++ b/src/main/java/BsK/common/entity/Patient.java @@ -12,7 +12,7 @@ public class Patient { private String customerLastName; private String customerFirstName; private String doctorName; - private String symptoms; + private String suggestion; private String diagnosis; private String notes; private String status; @@ -24,6 +24,7 @@ public class Patient { private String customerGender; private String customerDob; private String checkupType; + private String conclusion; public Patient(String[][] array) { this.checkupId = array[0][0]; @@ -31,7 +32,7 @@ public class Patient { this.customerLastName = array[0][2]; this.customerFirstName = array[0][3]; this.doctorName = array[0][4]; - this.symptoms = array[0][5]; + this.suggestion = array[0][5]; this.diagnosis = array[0][6]; this.notes = array[0][7]; this.status = array[0][8]; @@ -43,6 +44,7 @@ public class Patient { this.customerGender = array[0][14]; this.customerDob = array[0][15]; this.checkupType = array[0][16]; + this.conclusion = array[0][17]; } // New constructor for a single patient data row @@ -55,7 +57,7 @@ public class Patient { this.customerLastName = data[2]; this.customerFirstName = data[3]; this.doctorName = data[4]; - this.symptoms = data[5]; + this.suggestion = data[5]; this.diagnosis = data[6]; this.notes = data[7]; this.status = data[8]; @@ -67,5 +69,6 @@ public class Patient { this.customerGender = data[14]; this.customerDob = data[15]; this.checkupType = data[16]; + this.conclusion = data[17]; } } diff --git a/src/main/java/BsK/common/entity/PatientHistory.java b/src/main/java/BsK/common/entity/PatientHistory.java new file mode 100644 index 0000000..c5e5826 --- /dev/null +++ b/src/main/java/BsK/common/entity/PatientHistory.java @@ -0,0 +1,44 @@ +package BsK.common.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Entity class for Patient History + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PatientHistory { + private String checkupDate; + private String checkupId; + private String suggestion; + private String diagnosis; + private String prescriptionId; + private String notes; + + /** + * Constructor to create PatientHistory from array data + * @param data Array of patient history data from backend + */ + public PatientHistory(String[] data) { + if (data.length < 6) { + throw new IllegalArgumentException("PatientHistory data array must contain at least 6 elements"); + } + this.checkupDate = data[0]; + this.checkupId = data[1]; + this.suggestion = data[2]; + this.diagnosis = data[3]; + this.prescriptionId = data[4]; + this.notes = data[5]; + } + + /** + * Convert the PatientHistory entity to a String array + * @return String array representation of PatientHistory + */ + public String[] toStringArray() { + return new String[]{checkupDate, checkupId, suggestion, diagnosis, prescriptionId, notes}; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/common/entity/Service.java b/src/main/java/BsK/common/entity/Service.java new file mode 100644 index 0000000..9b88414 --- /dev/null +++ b/src/main/java/BsK/common/entity/Service.java @@ -0,0 +1,38 @@ +package BsK.common.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Entity class for Service + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Service { + private String id; + private String name; + private String cost; + + /** + * Constructor to create Service from array data + * @param data Array of service data from backend + */ + public Service(String[] data) { + if (data.length < 3) { + throw new IllegalArgumentException("Service data array must contain at least 3 elements"); + } + this.id = data[0]; + this.name = data[1]; + this.cost = data[2]; + } + + /** + * Convert the Service entity to a String array + * @return String array representation of Service + */ + public String[] toStringArray() { + return new String[]{id, name, cost}; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/common/packet/req/GetPatientHistoryRequest.java b/src/main/java/BsK/common/packet/req/GetPatientHistoryRequest.java new file mode 100644 index 0000000..d1285be --- /dev/null +++ b/src/main/java/BsK/common/packet/req/GetPatientHistoryRequest.java @@ -0,0 +1,14 @@ +package BsK.common.packet.req; + +import BsK.common.packet.Packet; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + + +@Slf4j +@AllArgsConstructor +@Data +public class GetPatientHistoryRequest implements Packet { + int patientId; +} \ No newline at end of file diff --git a/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java b/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java index c3852b0..69b1637 100644 --- a/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java +++ b/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java @@ -20,12 +20,13 @@ public class SaveCheckupRequest implements Packet { private String customerNumber; // Phone number private String customerWeight; private String customerHeight; - private String doctorName; // Or doctorId if preferred for backend processing - private String symptoms; + private String doctorId; // Or doctorId if preferred for backend processing + private String suggestions; private String diagnosis; private String notes; private String status; private String checkupType; + private String conclusion; private String[][] medicinePrescription; // Structure: [med_id, name, quantity, unit, dose_morning, dose_noon, dose_evening, unit_price, total_price, notes] private String[][] servicePrescription; // Structure: [ser_id, name, quantity, unit_price, total_price, notes] } \ No newline at end of file diff --git a/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java b/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java index c879912..effaa6a 100644 --- a/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java +++ b/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java @@ -1,13 +1,51 @@ package BsK.common.packet.res; +import BsK.common.entity.Medicine; import BsK.common.packet.Packet; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import java.util.ArrayList; +import java.util.List; + @Slf4j @Data @AllArgsConstructor public class GetMedInfoResponse implements Packet { private String[][] medInfo; + private transient List medicines; + + public GetMedInfoResponse(String[][] medInfo) { + this.medInfo = medInfo; + this.medicines = convertToMedicineList(medInfo); + } + + /** + * Converts the raw string array data to a list of Medicine objects + * @param medInfo Raw medicine data as string arrays + * @return List of Medicine objects + */ + private List convertToMedicineList(String[][] medInfo) { + List result = new ArrayList<>(); + if (medInfo != null) { + for (String[] data : medInfo) { + if (data.length >= 7) { + result.add(new Medicine(data)); + } + } + } + return result; + } + + /** + * Returns the medicine list, converting from raw data if needed + * @return List of Medicine objects + */ + public List getMedicines() { + if (medicines == null && medInfo != null) { + medicines = convertToMedicineList(medInfo); + } + return medicines; + } } diff --git a/src/main/java/BsK/common/packet/res/GetPatientHistoryResponse.java b/src/main/java/BsK/common/packet/res/GetPatientHistoryResponse.java new file mode 100644 index 0000000..1f5f044 --- /dev/null +++ b/src/main/java/BsK/common/packet/res/GetPatientHistoryResponse.java @@ -0,0 +1,51 @@ +package BsK.common.packet.res; + +import BsK.common.entity.PatientHistory; +import BsK.common.packet.Packet; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.List; + +@Data +@AllArgsConstructor +@Slf4j +public class GetPatientHistoryResponse implements Packet { + private String[][] history; + private transient List patientHistoryList; + + public GetPatientHistoryResponse(String[][] history) { + this.history = history; + this.patientHistoryList = convertToPatientHistoryList(history); + } + + /** + * Converts the raw string array data to a list of PatientHistory objects + * @param history Raw patient history data as string arrays + * @return List of PatientHistory objects + */ + private List convertToPatientHistoryList(String[][] history) { + List result = new ArrayList<>(); + if (history != null) { + for (String[] data : history) { + if (data.length >= 6) { + result.add(new PatientHistory(data)); + } + } + } + return result; + } + + /** + * Returns the patient history list, converting from raw data if needed + * @return List of PatientHistory objects + */ + public List getPatientHistoryList() { + if (patientHistoryList == null && history != null) { + patientHistoryList = convertToPatientHistoryList(history); + } + return patientHistoryList; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java b/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java index 5276232..f834346 100644 --- a/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java +++ b/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java @@ -1,13 +1,51 @@ package BsK.common.packet.res; +import BsK.common.entity.Service; import BsK.common.packet.Packet; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import java.util.ArrayList; +import java.util.List; + @Slf4j @Data @AllArgsConstructor public class GetSerInfoResponse implements Packet { private String[][] serInfo; + private transient List services; + + public GetSerInfoResponse(String[][] serInfo) { + this.serInfo = serInfo; + this.services = convertToServiceList(serInfo); + } + + /** + * Converts the raw string array data to a list of Service objects + * @param serInfo Raw service data as string arrays + * @return List of Service objects + */ + private List convertToServiceList(String[][] serInfo) { + List result = new ArrayList<>(); + if (serInfo != null) { + for (String[] data : serInfo) { + if (data.length >= 3) { + result.add(new Service(data)); + } + } + } + return result; + } + + /** + * Returns the service list, converting from raw data if needed + * @return List of Service objects + */ + public List getServices() { + if (services == null && serInfo != null) { + services = convertToServiceList(serInfo); + } + return services; + } } diff --git a/src/main/java/BsK/server/network/handler/ServerHandler.java b/src/main/java/BsK/server/network/handler/ServerHandler.java index 992d364..8fb40aa 100644 --- a/src/main/java/BsK/server/network/handler/ServerHandler.java +++ b/src/main/java/BsK/server/network/handler/ServerHandler.java @@ -19,6 +19,7 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.Han import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; import java.io.IOException; +import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -30,6 +31,9 @@ import java.util.HashMap; import lombok.extern.slf4j.Slf4j; +import BsK.common.entity.Medicine; +import BsK.common.entity.Service; +import BsK.common.entity.PatientHistory; import static BsK.server.Server.statement; @@ -94,8 +98,8 @@ public class ServerHandler extends SimpleChannelInboundHandler resultList = new ArrayList<>(); while (rs.next()) { @@ -280,11 +284,11 @@ public class ServerHandler extends SimpleChannelInboundHandler 0) { + + // Get next prescription_id + String getNextIdSql = "SELECT COALESCE(MAX(prescription_id), 0) + 1 as next_id FROM MedicineOrder"; + PreparedStatement nextIdStmt = conn.prepareStatement(getNextIdSql); + ResultSet rs = nextIdStmt.executeQuery(); + if (rs.next()) { + prescriptionId = rs.getString("next_id"); + } + rs.close(); + nextIdStmt.close(); + } + + // 3. Insert/Update Checkup + String checkupSql = """ + INSERT INTO Checkup ( + checkup_id, customer_id, doctor_id, checkup_date, + suggestion, diagnosis, prescription_id, notes, status, checkup_type, conclusion + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(checkup_id) DO UPDATE SET + suggestion = excluded.suggestion, + diagnosis = excluded.diagnosis, + prescription_id = excluded.prescription_id, + notes = excluded.notes, + status = excluded.status, + checkup_type = excluded.checkup_type, + conclusion = excluded.conclusion + """; + + PreparedStatement checkupStmt = conn.prepareStatement(checkupSql); + checkupStmt.setString(1, saveCheckupRequest.getCheckupId()); + checkupStmt.setString(2, saveCheckupRequest.getCustomerId()); + checkupStmt.setString(3, saveCheckupRequest.getDoctorId()); + checkupStmt.setString(4, saveCheckupRequest.getCheckupDate()); + checkupStmt.setString(5, saveCheckupRequest.getSuggestions()); + checkupStmt.setString(6, saveCheckupRequest.getDiagnosis()); + checkupStmt.setString(7, prescriptionId); + checkupStmt.setString(8, saveCheckupRequest.getNotes()); + checkupStmt.setString(9, saveCheckupRequest.getStatus()); + checkupStmt.setString(10, saveCheckupRequest.getCheckupType()); + checkupStmt.setString(11, saveCheckupRequest.getConclusion()); + checkupStmt.executeUpdate(); + log.info("Prescription ID generated or edited: {}", prescriptionId); + log.info("Checkup saved successfully"); + // 4. Handle Medicine Prescriptions + if (saveCheckupRequest.getMedicinePrescription() != null && + saveCheckupRequest.getMedicinePrescription().length > 0 && prescriptionId != null) { + + // Calculate total amount for medicine order + double totalMedicineAmount = 0; + for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) { + if (medicine.length > 8) { + try { + totalMedicineAmount += Double.parseDouble(medicine[8]); // total_price + } catch (NumberFormatException e) { + log.warn("Invalid medicine total price: {}", medicine[8]); + } + } + } + + // Insert/Update MedicineOrder + String medicineOrderSql = """ + INSERT INTO MedicineOrder ( + prescription_id, checkup_id, customer_id, + total_amount, payment_method, status, + payment_status, processed_by, notes, datetime + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_DATE) + ON CONFLICT(prescription_id, checkup_id) DO UPDATE SET + customer_id = excluded.customer_id, + total_amount = excluded.total_amount, + payment_method = excluded.payment_method, + status = excluded.status, + payment_status = excluded.payment_status, + processed_by = excluded.processed_by, + notes = excluded.notes, + datetime = CURRENT_DATE + """; + + PreparedStatement medicineOrderStmt = conn.prepareStatement(medicineOrderSql); + medicineOrderStmt.setString(1, prescriptionId); + medicineOrderStmt.setString(2, saveCheckupRequest.getCheckupId()); + medicineOrderStmt.setString(3, saveCheckupRequest.getCustomerId()); + medicineOrderStmt.setDouble(4, totalMedicineAmount); + medicineOrderStmt.setString(5, ""); // payment_method + medicineOrderStmt.setString(6, "Pending"); // status + medicineOrderStmt.setString(7, "Unpaid"); // payment_status + medicineOrderStmt.setString(8, ""); // processed_by + medicineOrderStmt.setString(9, ""); // notes + medicineOrderStmt.executeUpdate(); + log.info("MedicineOrder saved successfully"); + // Insert OrderItems + String orderItemSql = """ + INSERT INTO OrderItem ( + prescription_id, med_id, quantity_ordered, + dosage, duration, price_per_unit, total_price, checkup_id, notes + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(order_item_id) DO UPDATE SET + quantity_ordered = excluded.quantity_ordered, + dosage = excluded.dosage, + duration = excluded.duration, + price_per_unit = excluded.price_per_unit, + total_price = excluded.total_price, + checkup_id = excluded.checkup_id, + notes = excluded.notes + """; + log.info("OrderItem saved successfully"); + PreparedStatement orderItemStmt = conn.prepareStatement(orderItemSql); + + for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) { + if (medicine.length >= 10) { + orderItemStmt.setString(1, prescriptionId); + orderItemStmt.setString(2, medicine[0]); // med_id + orderItemStmt.setString(3, medicine[2]); // quantity + + // Build dosage string from morning, noon, evening + String dosage = String.format("Sáng %s, Trưa %s, Chiều %s", + medicine[4], medicine[5], medicine[6]); + orderItemStmt.setString(4, dosage); + orderItemStmt.setString(5, ""); // duration - could be calculated or added to request + orderItemStmt.setString(6, medicine[7]); // unit_price + orderItemStmt.setString(7, medicine[8]); // total_price + orderItemStmt.setString(8, saveCheckupRequest.getCheckupId()); + orderItemStmt.setString(9, medicine[9]); // notes + orderItemStmt.addBatch(); + } + } + orderItemStmt.executeBatch(); + } + log.info("Medicine prescriptions saved successfully"); + // 5. Handle Service Prescriptions + if (saveCheckupRequest.getServicePrescription() != null && + saveCheckupRequest.getServicePrescription().length > 0) { + + String serviceSql = """ + INSERT INTO CheckupService ( + checkup_id, service_id, quantity, + total_cost, status, checkup_date + ) VALUES (?, ?, ?, ?, ?, CURRENT_DATE) + ON CONFLICT(order_id) DO UPDATE SET + quantity = excluded.quantity, + total_cost = excluded.total_cost, + status = excluded.status + """; + + PreparedStatement serviceStmt = conn.prepareStatement(serviceSql); + + for (String[] service : saveCheckupRequest.getServicePrescription()) { + if (service.length >= 6) { + serviceStmt.setString(1, saveCheckupRequest.getCheckupId()); + serviceStmt.setString(2, service[0]); // service_id + serviceStmt.setString(3, service[2]); // quantity + serviceStmt.setString(4, service[4]); // total_cost + serviceStmt.setString(5, "PENDING"); // status + + serviceStmt.addBatch(); + } + } + serviceStmt.executeBatch(); + } + log.info("Service prescriptions saved successfully"); + // Commit transaction + conn.commit(); + log.info("Successfully saved checkup {} with all related data", saveCheckupRequest.getCheckupId()); + + UserUtil.sendPacket(currentUser.getSessionId(), new AddCheckupResponse(true, "Checkup saved successfully")); + + } catch (Exception e) { + log.error("Error saving checkup transaction", e); + // Rollback transaction on error + if (conn != null) { + try { + conn.rollback(); + log.info("Transaction rolled back due to error"); + } catch (SQLException rollbackEx) { + log.error("Error rolling back transaction", rollbackEx); + } + } + UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION)); + } finally { + // Restore auto-commit + if (conn != null) { + try { + conn.setAutoCommit(true); + } catch (SQLException e) { + log.error("Error restoring auto-commit", e); + } + } + } + } } } diff --git a/src/main/resources/database/BSK.db b/src/main/resources/database/BSK.db index 4a0d49f..7073513 100644 Binary files a/src/main/resources/database/BSK.db and b/src/main/resources/database/BSK.db differ diff --git a/src/main/resources/image/checkup_media/6/1cfa34a2b4125ed44fed8283bd56676b.jpg b/src/main/resources/image/checkup_media/6/1cfa34a2b4125ed44fed8283bd56676b.jpg deleted file mode 100644 index e46e8a2..0000000 Binary files a/src/main/resources/image/checkup_media/6/1cfa34a2b4125ed44fed8283bd56676b.jpg and /dev/null differ