From 736f16d81872ca61c76f016a5288d09ec8ded157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20Th=C3=A0nh=20=C4=90=E1=BA=A1t?= <91755733+lds217@users.noreply.github.com> Date: Sat, 2 Aug 2025 16:38:45 +0700 Subject: [PATCH] [update] Remove deprecated app_settings.properties file and update config.properties with new clinic and server settings. Refactor LocalStorage to centralize clinic information and server configurations. Enhance CheckUpPage and related components for improved data handling and validation. Streamline database queries in ServerHandler for better performance and clarity. --- config/app_settings.properties | 17 -- config/config.properties | 11 +- src/main/java/BsK/client/LocalStorage.java | 29 +- .../ui/component/CheckUpPage/CheckUpPage.java | 37 ++- .../PrintDialog/MedicineInvoice.java | 13 +- .../PrintDialog/UltrasoundResult.java | 11 +- .../PrintDialog/print_forms/InvoiceItem.java | 15 +- .../print_forms/medserinvoice.jrxml | 11 +- .../ui/component/DataDialog/DataDialog.java | 28 +- .../component/DataDialog/ExcelExporter.java | 48 ++- .../SettingsPage/SettingsDialog.java | 286 ++++++++---------- .../component/common/DateLabelFormatter.java | 122 ++++---- .../common/MedicineDialog/MedicineDialog.java | 10 +- .../server/network/handler/ServerHandler.java | 172 ++++++----- src/main/resources/database/BSK.db | Bin 1339392 -> 1339392 bytes 15 files changed, 433 insertions(+), 377 deletions(-) delete mode 100644 config/app_settings.properties diff --git a/config/app_settings.properties b/config/app_settings.properties deleted file mode 100644 index 65249eb..0000000 --- a/config/app_settings.properties +++ /dev/null @@ -1,17 +0,0 @@ -#BSK Client Application Settings -#Wed Jul 23 13:22:55 ICT 2025 -app.auto_save=false -app.auto_save_interval=5 -app.language=Ti\u1EBFng Vi\u1EC7t -app.theme=T\u1ED1i -backup.auto_enabled=false -backup.frequency=H\u1EB1ng ng\u00E0y -backup.path= -clinic.address=Khu v\u1EF1c 5, th\u1ECB tr\u1EA5n \u0110\u1EE9c H\u00F2a, huy\u1EC7n \u0110\u1EE9c H\u00F2a, t\u1EC9nh Long An -clinic.name=Ph\u00F2ng kh\u00E1m B\u00E1c S\u0129 Kh\u1EAFp -clinic.phone=0912345678 -server.address= -server.auto_connect=false -server.port=1999 -storage.image_path= -storage.pdf_path= diff --git a/config/config.properties b/config/config.properties index 70d52d0..d704b01 100644 --- a/config/config.properties +++ b/config/config.properties @@ -1,12 +1,13 @@ #BSK Clinic Management System Configuration - Updated via Settings Dialog -#Wed Jul 23 13:42:36 ICT 2025 -clinic.address=S\u1ED1 ABC, \u0110\u01B0\u1EDDng XYZ, Qu\u1EADn 1, TP.HCM -clinic.name=Ph\u00F2ng kh\u00E1m BSK +#Sat Aug 02 11:33:13 ICT 2025 +clinic.address=So ABC, Duong XYZ, Quan 1, TP.HCM +clinic.name=Phong kham BSK clinic.phone=0123456789 +clinic.prefix=BSK google.drive.enabled=true google.drive.root.folder=BSK_Clinic_Patient_Files server.address=127.0.0.1 server.port=1999 storage.checkup_media_base_dir=src/main/resources/image/checkup_media -storage.image_db_path=img_db -app.project_path=D:/Github Clones/BSK-All-in-One-Clinic-Management-System \ No newline at end of file +storage.ultrasound_folder_path=ANH SIEU AM +app.auto_change_status_to_finished=false \ No newline at end of file diff --git a/src/main/java/BsK/client/LocalStorage.java b/src/main/java/BsK/client/LocalStorage.java index f0c8420..1030738 100644 --- a/src/main/java/BsK/client/LocalStorage.java +++ b/src/main/java/BsK/client/LocalStorage.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; - import java.util.Date; @AllArgsConstructor @@ -19,32 +18,38 @@ public class LocalStorage { public static String username; public static Role role; public static int userId; - public static String ClinicName = "Phòng khám BSK"; - public static String ClinicAddress = "Số ABC, Đường XYZ, Quận 1, TP.HCM"; - public static String ClinicPhone = "0123456789"; - public static String ClinicPrefix = "BSK"; public static List chatHistory = new ArrayList<>(); public static String pathToProject = ""; public static String dataDialogSearchTerm = ""; - public static Date dataDialogFromDate = new Date(); // Default to today - public static Date dataDialogToDate = new Date(); // Default to today - public static String dataDialogDoctorName = "Tất cả"; // Default to "All" + public static Date dataDialogFromDate = new Date(); + public static Date dataDialogToDate = new Date(); + public static String dataDialogDoctorName = "Tất cả"; public static List doctorsName = new ArrayList<>(); public static String[] provinces = new String[]{}; public static String[] wards = new String[]{}; public static Map provinceToId = new java.util.HashMap<>(); - public static HashMap cachedWards; // cache local areas later will do - public static HashMap medUnitConversion; // hash by med id inside is unit id and unit name and conversion rate and unit price + public static HashMap cachedWards; + public static HashMap medUnitConversion; + + // --- Settings managed in the SettingsDialog --- // these variable can be changed by settings page // need restart public static String checkupMediaBaseDir = "src/main/resources/image/checkup_media"; public static String ULTRASOUND_FOLDER_PATH = "ANH SIEU AM"; - + public static String ClinicName = "Phòng khám BSK"; + public static String ClinicAddress = "Số ABC, Đường XYZ, Quận 1, TP.HCM"; + public static String ClinicPhone = "0123456789"; + public static String ClinicPrefix = "BSK"; + public static String serverAddress = "127.0.0.1"; + public static String serverPort = "1999"; + public static boolean googleDriveEnabled = true; + public static String googleDriveRootFolder = "BSK_Clinic_Patient_Files"; + // does not need restart - public static Boolean autoChangeStatusToFinished = false; // turn this on will change status to finished when checkup is save by hitting print + public static Boolean autoChangeStatusToFinished = false; } \ No newline at end of file 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 1d3bebf..b35244a 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/CheckUpPage.java @@ -2229,7 +2229,7 @@ public class CheckUpPage extends JPanel { log.warn("Doctor '{}' not found in the list for patient selection.", selectedPatient.getDoctorName()); } - DoctorItem ultrasoundDoctorToSelect = findDoctorByName(selectedPatient.getDoctorUltrasoundId()); + DoctorItem ultrasoundDoctorToSelect = findDoctorById(selectedPatient.getDoctorUltrasoundId()); if (ultrasoundDoctorToSelect != null) { ultrasoundDoctorComboBox.setSelectedItem(ultrasoundDoctorToSelect); } else if (ultrasoundDoctorComboBox.getItemCount() > 0) { @@ -4615,7 +4615,19 @@ public class CheckUpPage extends JPanel { return; } + Date checkupDate = (Date) datePicker.getModel().getValue(); + Date dob = (Date) dobPicker.getModel().getValue(); Date recheckupDate = (Date) recheckupDatePicker.getModel().getValue(); + + // Add validation for the required dates + if (checkupDate == null) { + JOptionPane.showMessageDialog(this, "Ngày khám không được để trống.", "Thiếu thông tin", JOptionPane.WARNING_MESSAGE); + return; + } + if (dob == null) { + JOptionPane.showMessageDialog(this, "Ngày sinh không được để trống.", "Thiếu thông tin", JOptionPane.WARNING_MESSAGE); + return; + } if (needRecheckupCheckbox.isSelected() && recheckupDate == null) { JOptionPane.showMessageDialog(this, "Vui lòng chọn ngày tái khám.", "Thiếu thông tin", JOptionPane.WARNING_MESSAGE); return; @@ -4627,7 +4639,7 @@ public class CheckUpPage extends JPanel { Integer.parseInt(customerIdField.getText()), Integer.parseInt(doctorIdStr), ultrasoundDoctorIdStr != null ? Integer.parseInt(ultrasoundDoctorIdStr) : null, - DateUtils.convertToDate(datePicker.getJFormattedTextField().getText()).getTime(), + checkupDate.getTime(), suggestionField.getText(), diagnosisField.getText(), getRtfContentAsString(), @@ -4638,7 +4650,7 @@ public class CheckUpPage extends JPanel { needRecheckupCheckbox.isSelected(), customerFirstNameField.getText(), customerLastNameField.getText(), - DateUtils.convertToDate(dobPicker.getJFormattedTextField().getText()).getTime(), + dob.getTime(), (String) genderComboBox.getSelectedItem(), customerAddressField.getText() + ", " + (wardComboBox.getSelectedItem() != null ? wardComboBox.getSelectedItem().toString() : "") + ", " + (provinceComboBox.getSelectedItem() != null ? provinceComboBox.getSelectedItem().toString() : ""), customerPhoneField.getText(), @@ -4954,6 +4966,25 @@ public class CheckUpPage extends JPanel { return null; } + /** + * Finds a DoctorItem in the LocalStorage list by their ID. + * @param id The ID of the doctor to find. + * @return The matching DoctorItem, or null if not found. + */ + private DoctorItem findDoctorById(String id) { + // Return null if the ID is invalid or the doctor list is not available + if (id == null || id.isEmpty() || id.equals("0") || LocalStorage.doctorsName == null) { + return null; + } + // Loop through the doctors and compare by ID + for (DoctorItem doctor : LocalStorage.doctorsName) { + if (doctor.getId().equals(id)) { + return doctor; // Found a match + } + } + return null; // No match found + } + public void loadPatientByCheckupId(Patient patient) { handleRowSelection(-1, patient); } 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 766c6d7..56daa64 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 @@ -290,7 +290,7 @@ public class MedicineInvoice{ // --- CORRECTED THIS LINE --- // Swapped 'amount' and 'supRoute' to match the method signature in InvoiceItem.java // Call signature: (Tên, Ghi chú, Liều dùng, Số lượng, Đường dùng) - supplementItems.add(InvoiceItem.createSupplement(supName, supNote, dosageInfo, amount, supRoute)); + supplementItems.add(InvoiceItem.createSupplement(supName, supNote, dosageInfo, supRoute, amount)); } catch (Exception e) { System.err.println("Error parsing supplement data row: " + java.util.Arrays.toString(supplement) + " | Error: " + e.getMessage()); @@ -323,11 +323,20 @@ public class MedicineInvoice{ parameters.put("checkupNote", notes != null ? notes : ""); parameters.put("barcodeNumber", id != null ? id : ""); parameters.put("driveURL", driveURL != null ? driveURL : ""); - parameters.put("logoImage", LocalStorage.pathToProject + "/src/main/java/BsK/client/ui/assets/icon/logo.jpg"); parameters.put("hasMedicines", !medicineItems.isEmpty()); parameters.put("hasServices", !serviceItems.isEmpty()); parameters.put("hasSupplements", !supplementItems.isEmpty()); + String logoPath = "/BsK/client/ui/assets/icon/logo.jpg"; + // Load the logo as an InputStream + InputStream logoStream = MedicineInvoice.class.getResourceAsStream(logoPath); + if (logoStream == null) { + System.err.println("ERROR: Logo image not found on classpath: " + logoPath); + parameters.put("logoImage", null); + } else { + // Pass the InputStream to the report parameter + parameters.put("logoImage", logoStream); + } String resourcePath = "/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml"; try (InputStream inputStream = MedicineInvoice.class.getResourceAsStream(resourcePath)) { if (inputStream == null) { 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 d29d4c2..f6fe863 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 @@ -173,9 +173,9 @@ public class UltrasoundResult { public JasperPrint createJasperPrint() throws JRException, IOException { String jrxmlPath; if ("Dọc".equalsIgnoreCase(this.printType)) { - jrxmlPath = LocalStorage.pathToProject + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml"; + jrxmlPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml"; } else { - jrxmlPath =LocalStorage.pathToProject + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml"; + jrxmlPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml"; } log.info("Using JRXML template: {}", jrxmlPath); @@ -209,8 +209,8 @@ public class UltrasoundResult { // Vitals data parameters.put("checkupHeight", this.customerHeight != null ? this.customerHeight : ""); parameters.put("checkupWeight", this.customerWeight != null ? this.customerWeight : ""); - parameters.put("checkupHeartBeat", this.heartRate != null ? this.heartRate : ""); - parameters.put("checkupBloodPressure", this.bloodPressure != null ? this.bloodPressure : ""); + parameters.put("checkupHeartBeat", this.heartRate); + parameters.put("checkupBloodPressure", this.bloodPressure); // Template title parameters.put("templateTitle", this.templateTitle); @@ -227,8 +227,7 @@ public class UltrasoundResult { int numberOfImages = this.selectedImages.size(); parameters.put("numberImage", numberOfImages); - String projectDir = LocalStorage.pathToProject; - parameters.put("logoImage", projectDir + "/src/main/java/BsK/client/ui/assets/icon/logo.jpg"); + parameters.put("logoImage", System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/assets/icon/logo.jpg"); for (int i = 0; i < 6; i++) { if (i < numberOfImages) { diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/InvoiceItem.java b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/InvoiceItem.java index 7779fd8..c1bf161 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/InvoiceItem.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/InvoiceItem.java @@ -12,7 +12,6 @@ public class InvoiceItem { private String medName; private String dosage; private String amount; - private Double unitPrice; private String medRoute; private String medNote; @@ -24,10 +23,14 @@ public class InvoiceItem { // --- Supplement Fields --- private String supName; - private String supNote; private String supDosage; private String supAmount; private String supRoute; + private String supNote; + + + + // Private constructor to be used by factory methods private InvoiceItem() {} @@ -40,7 +43,6 @@ public class InvoiceItem { item.dosage = dosage; item.medRoute = route; item.amount = amount; - item.unitPrice = 0.0; item.medNote = medNote; return item; } @@ -55,14 +57,14 @@ public class InvoiceItem { return item; } - public static InvoiceItem createSupplement(String supName, String supNote, String supDosage, String supAmount, String supRoute) { + public static InvoiceItem createSupplement(String supName, String supNote, String supDosage, String supRoute, String supAmount) { InvoiceItem item = new InvoiceItem(); item.type = "SUP"; item.supName = supName; - item.supNote = supNote; item.supDosage = supDosage; - item.supAmount = supAmount; item.supRoute = supRoute; + item.supAmount = supAmount; + item.supNote = supNote; return item; } @@ -74,7 +76,6 @@ public class InvoiceItem { public String getDosage() { return dosage; } public String getMedRoute() { return medRoute; } public String getAmount() { return amount; } - public Double getUnitPrice() { return unitPrice; } public String getMedNote() { return medNote; } public String getSerName() { return serName; } public String getSerNote() { return serNote; } diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml index 09e4932..b08e504 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml @@ -17,7 +17,6 @@ - @@ -62,7 +61,7 @@ - + @@ -211,16 +210,16 @@ - + - - + + - + diff --git a/src/main/java/BsK/client/ui/component/DataDialog/DataDialog.java b/src/main/java/BsK/client/ui/component/DataDialog/DataDialog.java index ce311ca..c4c7b81 100644 --- a/src/main/java/BsK/client/ui/component/DataDialog/DataDialog.java +++ b/src/main/java/BsK/client/ui/component/DataDialog/DataDialog.java @@ -26,7 +26,6 @@ import javax.swing.filechooser.FileNameExtensionFilter; import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; // Import Arrays for utility if needed, though not strictly required here import java.util.List; public class DataDialog extends JDialog { @@ -49,7 +48,7 @@ public class DataDialog extends JDialog { private JPanel mainPanel; private JPanel paginationPanel; private CheckUpPage checkUpPageInstance; - private String[][] currentCheckupData; // <<< MODIFIED: Changed type from List to String[][] + private String[][] currentCheckupData; // // --- Panels for the other tabs @@ -352,7 +351,8 @@ public class DataDialog extends JDialog { } try { - ExcelExporter.exportToExcel(patientsToExport, this.fileForExport); + // <<< MODIFIED: Pass the doctors list to the exporter + ExcelExporter.exportToExcel(patientsToExport, LocalStorage.doctorsName, this.fileForExport); JOptionPane.showMessageDialog(this, "Xuất file Excel thành công!\n" + this.fileForExport.getAbsolutePath(), "Thành công", JOptionPane.INFORMATION_MESSAGE); } catch (IOException ex) { JOptionPane.showMessageDialog(this, "Lỗi khi ghi file Excel: " + ex.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE); @@ -368,13 +368,11 @@ public class DataDialog extends JDialog { this.totalPages = response.getTotalPages(); this.totalRecords = response.getTotalRecords(); this.recordsPerPage = response.getPageSize(); - // This assignment is now correct as both types are String[][] this.currentCheckupData = response.getCheckupData(); tableModel.setRowCount(0); if (this.currentCheckupData != null) { int stt = (currentPage - 1) * recordsPerPage + 1; - // Enhanced for-loop works perfectly on a 2D array for (String[] rowData : this.currentCheckupData) { try { Patient patient = new Patient(rowData); @@ -518,10 +516,15 @@ public class DataDialog extends JDialog { setLayout(new FlowLayout(FlowLayout.CENTER, 2, 2)); ImageIcon editIcon = new ImageIcon("src/main/java/BsK/client/ui/assets/icon/edit.png"); - editIcon.setImage(editIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH)); + if (editIcon.getImage() != null) { + editIcon.setImage(editIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH)); + } editButton = new JButton(editIcon); + ImageIcon deleteIcon = new ImageIcon("src/main/java/BsK/client/ui/assets/icon/delete.png"); - deleteIcon.setImage(deleteIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH)); + if (deleteIcon.getImage() != null) { + deleteIcon.setImage(deleteIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH)); + } deleteButton = new JButton(deleteIcon); editButton.setPreferredSize(new Dimension(30, 25)); @@ -554,10 +557,15 @@ public class DataDialog extends JDialog { panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 2, 2)); ImageIcon editIcon = new ImageIcon("src/main/java/BsK/client/ui/assets/icon/edit.png"); - editIcon.setImage(editIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH)); + if (editIcon.getImage() != null) { + editIcon.setImage(editIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH)); + } editButton = new JButton(editIcon); + ImageIcon deleteIcon = new ImageIcon("src/main/java/BsK/client/ui/assets/icon/delete.png"); - deleteIcon.setImage(deleteIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH)); + if (deleteIcon.getImage() != null) { + deleteIcon.setImage(deleteIcon.getImage().getScaledInstance(16, 16, Image.SCALE_SMOOTH)); + } deleteButton = new JButton(deleteIcon); editButton.setPreferredSize(new Dimension(30, 25)); @@ -572,9 +580,7 @@ public class DataDialog extends JDialog { } int modelRow = dataTable.convertRowIndexToModel(selectedViewRow); - // <<< MODIFIED: Use .length for array and check index validity if (currentCheckupData != null && modelRow >= 0 && modelRow < currentCheckupData.length) { - // <<< MODIFIED: Use array index access `[modelRow]` instead of `.get()` String[] rowData = currentCheckupData[modelRow]; try { Patient selectedPatient = new Patient(rowData); diff --git a/src/main/java/BsK/client/ui/component/DataDialog/ExcelExporter.java b/src/main/java/BsK/client/ui/component/DataDialog/ExcelExporter.java index 708a7d3..4129050 100644 --- a/src/main/java/BsK/client/ui/component/DataDialog/ExcelExporter.java +++ b/src/main/java/BsK/client/ui/component/DataDialog/ExcelExporter.java @@ -1,5 +1,6 @@ package BsK.client.ui.component.DataDialog; +import BsK.common.entity.DoctorItem; import BsK.common.entity.Patient; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; @@ -13,15 +14,34 @@ import java.util.List; public class ExcelExporter { - public static void exportToExcel(List patientList, File file) throws IOException { + /** + * Finds a doctor's name from a list based on their ID. + * @param id The doctor ID to look for. + * @param doctorList The list of all available doctors. + * @return The doctor's name, or an empty string if not found. + */ + private static String getDoctorNameById(String id, List doctorList) { + if (id == null || id.isEmpty() || id.equals("0") || doctorList == null) { + return ""; + } + for (DoctorItem doctor : doctorList) { + if (doctor.getId().equals(id)) { + return doctor.getName(); + } + } + return ""; // Return empty if no match is found + } + + // <<< MODIFIED: Method signature now accepts the list of doctors + public static void exportToExcel(List patientList, List doctorList, File file) throws IOException { // Create a new Excel workbook try (XSSFWorkbook workbook = new XSSFWorkbook()) { XSSFSheet sheet = workbook.createSheet("DanhSachKhamBenh"); - // Define header titles + // <<< MODIFIED: Added "Bác Sĩ Siêu Âm" column String[] headers = { "Mã Khám", "Mã BN", "Họ", "Tên", "Năm Sinh", "Giới Tính", "Số Điện Thoại", - "Địa Chỉ", "Ngày Khám", "Loại Khám", "Bác Sĩ Khám", "CCCD/DDCN", "Cân Nặng (kg)", + "Địa Chỉ", "Ngày Khám", "Loại Khám", "Bác Sĩ Khám", "Bác Sĩ Siêu Âm", "CCCD/DDCN", "Cân Nặng (kg)", "Chiều Cao (cm)", "Nhịp Tim (l/p)", "Huyết Áp (mmHg)", "Chẩn Đoán", "Kết Luận", "Đề Nghị", "Ghi Chú", "Ngày Tái Khám" }; @@ -48,16 +68,18 @@ public class ExcelExporter { row.createCell(8).setCellValue(patient.getCheckupDate()); row.createCell(9).setCellValue(patient.getCheckupType()); row.createCell(10).setCellValue(patient.getDoctorName()); - row.createCell(11).setCellValue(patient.getCccdDdcn()); - row.createCell(12).setCellValue(patient.getCustomerWeight()); - row.createCell(13).setCellValue(patient.getCustomerHeight()); - row.createCell(14).setCellValue(patient.getHeartBeat()); - row.createCell(15).setCellValue(patient.getBloodPressure()); - row.createCell(16).setCellValue(patient.getDiagnosis()); - row.createCell(17).setCellValue(patient.getConclusion()); - row.createCell(18).setCellValue(patient.getSuggestion()); - row.createCell(19).setCellValue(patient.getNotes()); - row.createCell(20).setCellValue(patient.getReCheckupDate()); + String ultrasoundDoctorName = getDoctorNameById(patient.getDoctorUltrasoundId(), doctorList); + row.createCell(11).setCellValue(ultrasoundDoctorName); + row.createCell(12).setCellValue(patient.getCccdDdcn()); + row.createCell(13).setCellValue(patient.getCustomerWeight()); + row.createCell(14).setCellValue(patient.getCustomerHeight()); + row.createCell(15).setCellValue(patient.getHeartBeat()); + row.createCell(16).setCellValue(patient.getBloodPressure()); + row.createCell(17).setCellValue(patient.getDiagnosis()); + row.createCell(18).setCellValue(patient.getConclusion()); + row.createCell(19).setCellValue(patient.getSuggestion()); + row.createCell(20).setCellValue(patient.getNotes()); + row.createCell(21).setCellValue(patient.getReCheckupDate()); } // Write the output to a file diff --git a/src/main/java/BsK/client/ui/component/SettingsPage/SettingsDialog.java b/src/main/java/BsK/client/ui/component/SettingsPage/SettingsDialog.java index 65ee4a6..653d9ff 100644 --- a/src/main/java/BsK/client/ui/component/SettingsPage/SettingsDialog.java +++ b/src/main/java/BsK/client/ui/component/SettingsPage/SettingsDialog.java @@ -1,6 +1,5 @@ package BsK.client.ui.component.SettingsPage; -import BsK.client.Client; import BsK.client.LocalStorage; import lombok.extern.slf4j.Slf4j; @@ -14,15 +13,27 @@ import java.util.Properties; @Slf4j public class SettingsDialog extends JDialog { + // Existing fields private JTextField mediaDirectoryField; private JTextField ultrasoundFolderField; private JCheckBox autoChangeStatusCheckBox; + + // Fields for additional settings + private JTextField clinicNameField; + private JTextField clinicAddressField; + private JTextField clinicPhoneField; + private JTextField clinicPrefixField; + private JTextField serverAddressField; + private JTextField serverPortField; + + // <<< REMOVED: Google Drive UI fields are gone + private Properties originalProps; private boolean hasChanges = false; public SettingsDialog(JFrame parent) { super(parent, "Cài đặt hệ thống", true); // Modal dialog - setSize(700, 600); + setSize(700, 700); // Adjusted height setLocationRelativeTo(parent); setLayout(new BorderLayout()); @@ -76,10 +87,10 @@ public class SettingsDialog extends JDialog { JOptionPane.QUESTION_MESSAGE); if (choice == JOptionPane.YES_OPTION) { - dispose(); // Close dialog without saving + dispose(); } } else { - dispose(); // No changes, just close + dispose(); } }); @@ -100,28 +111,22 @@ public class SettingsDialog extends JDialog { GridBagConstraints gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.NORTHWEST; gbc.insets = new Insets(5, 5, 5, 5); - - // Auto change status setting gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1.0; - autoChangeStatusCheckBox = new JCheckBox("Tự động chuyển trạng thái thành 'Hoàn thành' khi in"); autoChangeStatusCheckBox.setFont(new Font("Arial", Font.PLAIN, 12)); autoChangeStatusCheckBox.setSelected(LocalStorage.autoChangeStatusToFinished); autoChangeStatusCheckBox.addActionListener(e -> hasChanges = true); parent.add(autoChangeStatusCheckBox, gbc); - gbc.gridy = 1; gbc.weighty = 0; JLabel autoStatusDescription = new JLabel("Khi bật, trạng thái khám bệnh sẽ tự động chuyển thành 'Hoàn thành' sau khi in hóa đơn"); autoStatusDescription.setFont(new Font("Arial", Font.PLAIN, 10)); autoStatusDescription.setForeground(Color.GRAY); parent.add(autoStatusDescription, gbc); - - // Add vertical glue to push content to top gbc.gridy = 2; gbc.weighty = 1.0; gbc.fill = GridBagConstraints.VERTICAL; @@ -132,120 +137,88 @@ public class SettingsDialog extends JDialog { GridBagConstraints gbc = new GridBagConstraints(); gbc.anchor = GridBagConstraints.NORTHWEST; gbc.insets = new Insets(5, 5, 5, 5); - - // Media directory setting - gbc.gridx = 0; - gbc.gridy = 0; - gbc.gridwidth = 1; - gbc.fill = GridBagConstraints.NONE; - gbc.weightx = 0; - - JLabel mediaLabel = new JLabel("Thư mục lưu trữ ảnh khám bệnh:"); - mediaLabel.setFont(new Font("Arial", Font.BOLD, 12)); - parent.add(mediaLabel, gbc); - - gbc.gridx = 0; - gbc.gridy = 1; - gbc.gridwidth = 2; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.weightx = 1.0; - - JPanel mediaInputPanel = new JPanel(new BorderLayout(5, 0)); - mediaDirectoryField = new JTextField(); - mediaDirectoryField.setText(LocalStorage.checkupMediaBaseDir); - mediaDirectoryField.setPreferredSize(new Dimension(300, 25)); - mediaDirectoryField.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() { + int y = 0; + + javax.swing.event.DocumentListener changeListener = new javax.swing.event.DocumentListener() { public void insertUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; } public void removeUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; } public void changedUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; } - }); + }; - JButton mediaBrowseButton = new JButton("Chọn thư mục"); - mediaBrowseButton.setPreferredSize(new Dimension(120, 25)); - mediaBrowseButton.addActionListener(e -> { - JFileChooser chooser = new JFileChooser(); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setCurrentDirectory(new File(mediaDirectoryField.getText())); - - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - mediaDirectoryField.setText(chooser.getSelectedFile().getAbsolutePath()); - hasChanges = true; - } - }); - - mediaInputPanel.add(mediaDirectoryField, BorderLayout.CENTER); - mediaInputPanel.add(mediaBrowseButton, BorderLayout.EAST); - parent.add(mediaInputPanel, gbc); - - gbc.gridy = 2; - gbc.weighty = 0; - JLabel mediaDescription = new JLabel("Thư mục chứa ảnh được chụp trong quá trình khám bệnh"); - mediaDescription.setFont(new Font("Arial", Font.PLAIN, 10)); - mediaDescription.setForeground(Color.GRAY); - parent.add(mediaDescription, gbc); - - // Ultrasound folder setting - gbc.gridx = 0; - gbc.gridy = 3; - gbc.gridwidth = 1; - gbc.fill = GridBagConstraints.NONE; - gbc.weightx = 0; - gbc.insets = new Insets(15, 5, 5, 5); // Add extra top margin - - JLabel ultrasoundLabel = new JLabel("Thư mục theo dõi ảnh siêu âm:"); - ultrasoundLabel.setFont(new Font("Arial", Font.BOLD, 12)); - parent.add(ultrasoundLabel, gbc); - - gbc.gridx = 0; - gbc.gridy = 4; - gbc.gridwidth = 2; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.weightx = 1.0; - gbc.insets = new Insets(5, 5, 5, 5); // Reset insets - - JPanel ultrasoundInputPanel = new JPanel(new BorderLayout(5, 0)); - ultrasoundFolderField = new JTextField(); - ultrasoundFolderField.setText(LocalStorage.ULTRASOUND_FOLDER_PATH); - ultrasoundFolderField.setPreferredSize(new Dimension(300, 25)); - ultrasoundFolderField.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() { - public void insertUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; } - public void removeUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; } - public void changedUpdate(javax.swing.event.DocumentEvent e) { hasChanges = true; } - }); - - JButton ultrasoundBrowseButton = new JButton("Chọn thư mục"); - ultrasoundBrowseButton.setPreferredSize(new Dimension(120, 25)); - ultrasoundBrowseButton.addActionListener(e -> { - JFileChooser chooser = new JFileChooser(); - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - chooser.setCurrentDirectory(new File(ultrasoundFolderField.getText())); - - if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { - ultrasoundFolderField.setText(chooser.getSelectedFile().getAbsolutePath()); - hasChanges = true; - } - }); - - ultrasoundInputPanel.add(ultrasoundFolderField, BorderLayout.CENTER); - ultrasoundInputPanel.add(ultrasoundBrowseButton, BorderLayout.EAST); - parent.add(ultrasoundInputPanel, gbc); - - gbc.gridy = 5; - gbc.weighty = 0; - JLabel ultrasoundDescription = new JLabel("Thư mục được hệ thống theo dõi để tự động nhận ảnh siêu âm mới"); - ultrasoundDescription.setFont(new Font("Arial", Font.PLAIN, 10)); - ultrasoundDescription.setForeground(Color.GRAY); - parent.add(ultrasoundDescription, gbc); - - // Add vertical glue to push content to top - gbc.gridy = 6; + // --- Clinic Info --- + clinicNameField = addTextFieldSetting(parent, gbc, y++, "Tên phòng khám:", LocalStorage.ClinicName, "Tên sẽ hiển thị trên các phiếu in.", changeListener); + clinicAddressField = addTextFieldSetting(parent, gbc, y++, "Địa chỉ phòng khám:", LocalStorage.ClinicAddress, "Địa chỉ sẽ hiển thị trên các phiếu in.", changeListener); + clinicPhoneField = addTextFieldSetting(parent, gbc, y++, "SĐT phòng khám:", LocalStorage.ClinicPhone, "Số điện thoại sẽ hiển thị trên các phiếu in.", changeListener); + clinicPrefixField = addTextFieldSetting(parent, gbc, y++, "Tiền tố mã:", LocalStorage.ClinicPrefix, "Tiền tố cho mã bệnh nhân và các mã khác (ví dụ: BSK).", changeListener); + + // --- Server Connection --- + serverAddressField = addTextFieldSetting(parent, gbc, y++, "Địa chỉ máy chủ:", LocalStorage.serverAddress, "Địa chỉ IP hoặc tên miền của máy chủ.", changeListener); + serverPortField = addTextFieldSetting(parent, gbc, y++, "Cổng máy chủ (Port):", LocalStorage.serverPort, "Cổng kết nối tới máy chủ.", changeListener); + + // --- Storage Settings --- + mediaDirectoryField = addTextFieldSettingWithBrowse(parent, gbc, y++, "Thư mục lưu trữ ảnh khám bệnh:", LocalStorage.checkupMediaBaseDir, "Thư mục chứa ảnh được chụp trong quá trình khám bệnh.", changeListener); + ultrasoundFolderField = addTextFieldSettingWithBrowse(parent, gbc, y++, "Thư mục theo dõi ảnh siêu âm:", LocalStorage.ULTRASOUND_FOLDER_PATH, "Thư mục được hệ thống theo dõi để tự động nhận ảnh siêu âm mới.", changeListener); + + // <<< REMOVED: Google Drive Integration UI is gone. + + // Vertical glue to push all content up + gbc.gridy = y * 3; // Adjust glue position gbc.weighty = 1.0; gbc.fill = GridBagConstraints.VERTICAL; parent.add(Box.createVerticalGlue(), gbc); } + + private JTextField addTextFieldSetting(JPanel parent, GridBagConstraints gbc, int yPos, String labelText, String initialValue, String description, javax.swing.event.DocumentListener listener) { + gbc.gridx = 0; gbc.gridy = yPos * 3; gbc.gridwidth = 2; gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1.0; gbc.insets = (yPos == 0) ? new Insets(5, 5, 5, 5) : new Insets(15, 5, 5, 5); + JLabel label = new JLabel(labelText); + label.setFont(new Font("Arial", Font.BOLD, 12)); + parent.add(label, gbc); + + gbc.gridy = yPos * 3 + 1; gbc.insets = new Insets(5, 5, 5, 5); + JTextField textField = new JTextField(initialValue); + textField.getDocument().addDocumentListener(listener); + parent.add(textField, gbc); + + gbc.gridy = yPos * 3 + 2; + JLabel descLabel = new JLabel("" + description + ""); + descLabel.setFont(new Font("Arial", Font.PLAIN, 10)); + descLabel.setForeground(Color.GRAY); + parent.add(descLabel, gbc); + + return textField; + } + + private JTextField addTextFieldSettingWithBrowse(JPanel parent, GridBagConstraints gbc, int yPos, String labelText, String initialValue, String description, javax.swing.event.DocumentListener listener) { + JTextField textField = addTextFieldSetting(parent, gbc, yPos, labelText, initialValue, description, listener); + parent.remove(textField); + + JPanel inputPanel = new JPanel(new BorderLayout(5, 0)); + inputPanel.add(textField, BorderLayout.CENTER); + JButton browseButton = new JButton("Chọn..."); + browseButton.addActionListener(e -> { + JFileChooser chooser = new JFileChooser(); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + try { + File currentDir = new File(textField.getText()); + if (currentDir.exists()) { + chooser.setCurrentDirectory(currentDir); + } + } catch (Exception ex) { /* Ignore */ } + + if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) { + textField.setText(chooser.getSelectedFile().getAbsolutePath()); + } + }); + inputPanel.add(browseButton, BorderLayout.EAST); + + gbc.gridy = yPos * 3 + 1; + parent.add(inputPanel, gbc); + + return textField; + } private JPanel createSection(String title, Color backgroundColor) { - JPanel panel = new JPanel(new GridBagLayout()); // Changed to GridBagLayout + JPanel panel = new JPanel(new GridBagLayout()); panel.setBackground(backgroundColor); panel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder( @@ -261,7 +234,6 @@ public class SettingsDialog extends JDialog { panel.setAlignmentX(Component.LEFT_ALIGNMENT); return panel; } - private void loadSettings() { try { @@ -275,58 +247,64 @@ public class SettingsDialog extends JDialog { hasChanges = false; } catch (IOException e) { log.error("Error loading config", e); - JOptionPane.showMessageDialog(this, - "Lỗi khi tải cài đặt: " + e.getMessage(), - "Lỗi", - JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this, "Lỗi khi tải cài đặt: " + e.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE); } } private void saveSettings() { - // Check if there are any changes - boolean mediaChanged = !mediaDirectoryField.getText().equals(LocalStorage.checkupMediaBaseDir); - boolean ultrasoundChanged = !ultrasoundFolderField.getText().equals(LocalStorage.ULTRASOUND_FOLDER_PATH); - boolean autoStatusChanged = autoChangeStatusCheckBox.isSelected() != LocalStorage.autoChangeStatusToFinished; - - if (!hasChanges && !mediaChanged && !ultrasoundChanged && !autoStatusChanged) { - JOptionPane.showMessageDialog(this, - "Không có thay đổi nào để lưu.", - "Thông báo", - JOptionPane.INFORMATION_MESSAGE); + // <<< MODIFIED: Removed Google Drive fields from change detection + boolean restartRequiredChanges = + !mediaDirectoryField.getText().trim().equals(LocalStorage.checkupMediaBaseDir) || + !ultrasoundFolderField.getText().trim().equals(LocalStorage.ULTRASOUND_FOLDER_PATH) || + !clinicNameField.getText().trim().equals(LocalStorage.ClinicName) || + !clinicAddressField.getText().trim().equals(LocalStorage.ClinicAddress) || + !clinicPhoneField.getText().trim().equals(LocalStorage.ClinicPhone) || + !clinicPrefixField.getText().trim().equals(LocalStorage.ClinicPrefix) || + !serverAddressField.getText().trim().equals(LocalStorage.serverAddress) || + !serverPortField.getText().trim().equals(LocalStorage.serverPort); + + boolean noRestartChanges = autoChangeStatusCheckBox.isSelected() != LocalStorage.autoChangeStatusToFinished; + + if (!restartRequiredChanges && !noRestartChanges) { + JOptionPane.showMessageDialog(this, "Không có thay đổi nào để lưu.", "Thông báo", JOptionPane.INFORMATION_MESSAGE); return; } try { Properties props = new Properties(); if (originalProps != null) { - props.putAll(originalProps); // Preserve other settings + props.putAll(originalProps); } - // Update settings that require restart - String newMediaDir = mediaDirectoryField.getText().trim(); - String newUltrasoundFolder = ultrasoundFolderField.getText().trim(); - props.setProperty("storage.checkup_media_base_dir", newMediaDir); - props.setProperty("storage.ultrasound_folder_path", newUltrasoundFolder); + // Clinic Info + props.setProperty("clinic.name", clinicNameField.getText().trim()); + props.setProperty("clinic.address", clinicAddressField.getText().trim()); + props.setProperty("clinic.phone", clinicPhoneField.getText().trim()); + props.setProperty("clinic.prefix", clinicPrefixField.getText().trim()); - // Update settings that don't require restart + // Server + props.setProperty("server.address", serverAddressField.getText().trim()); + props.setProperty("server.port", serverPortField.getText().trim()); + + // Storage + props.setProperty("storage.checkup_media_base_dir", mediaDirectoryField.getText().trim()); + props.setProperty("storage.ultrasound_folder_path", ultrasoundFolderField.getText().trim()); + + // <<< REMOVED: Saving of Google Drive properties + + // App settings (no restart) boolean newAutoStatus = autoChangeStatusCheckBox.isSelected(); props.setProperty("app.auto_change_status_to_finished", String.valueOf(newAutoStatus)); - LocalStorage.autoChangeStatusToFinished = newAutoStatus; // Update immediately + LocalStorage.autoChangeStatusToFinished = newAutoStatus; - // Ensure config directory exists + // Save properties to file new File("config").mkdirs(); - - // Save properties File configFile = new File("config/config.properties"); try (var output = Files.newOutputStream(configFile.toPath())) { props.store(output, "BSK Clinic Management System Configuration - Updated via Settings Dialog"); } - // Check if restart is required - boolean restartRequired = mediaChanged || ultrasoundChanged; - - if (restartRequired) { - // Show success message with restart required + if (restartRequiredChanges) { int choice = JOptionPane.showConfirmDialog(this, "Cài đặt đã được lưu. Một số thay đổi cần khởi động lại ứng dụng để có hiệu lực.\n\nBạn có muốn khởi động lại ngay bây giờ không?", "Cần khởi động lại", @@ -334,28 +312,20 @@ public class SettingsDialog extends JDialog { JOptionPane.QUESTION_MESSAGE); if (choice == JOptionPane.YES_OPTION) { - // Exit the application System.exit(0); } else { - dispose(); // Close dialog after saving + dispose(); } } else { - // No restart required - JOptionPane.showMessageDialog(this, - "Cài đặt đã được lưu thành công.", - "Thành công", - JOptionPane.INFORMATION_MESSAGE); + JOptionPane.showMessageDialog(this, "Cài đặt đã được lưu thành công.", "Thành công", JOptionPane.INFORMATION_MESSAGE); dispose(); } hasChanges = false; - + } catch (IOException ex) { log.error("Error saving settings", ex); - JOptionPane.showMessageDialog(this, - "Lỗi khi lưu cài đặt: " + ex.getMessage(), - "Lỗi", - JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(this, "Lỗi khi lưu cài đặt: " + ex.getMessage(), "Lỗi", JOptionPane.ERROR_MESSAGE); } } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/common/DateLabelFormatter.java b/src/main/java/BsK/client/ui/component/common/DateLabelFormatter.java index 78eee0f..0b06117 100644 --- a/src/main/java/BsK/client/ui/component/common/DateLabelFormatter.java +++ b/src/main/java/BsK/client/ui/component/common/DateLabelFormatter.java @@ -1,73 +1,73 @@ -package BsK.client.ui.component.common; + package BsK.client.ui.component.common; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.Locale; + import java.text.ParseException; + import java.text.SimpleDateFormat; + import java.util.Calendar; + import java.util.Locale; -import org.jdatepicker.*; + import org.jdatepicker.*; -import javax.swing.*; + import javax.swing.*; -public class DateLabelFormatter extends JFormattedTextField.AbstractFormatter { + public class DateLabelFormatter extends JFormattedTextField.AbstractFormatter { - private String datePattern = "dd/MM/yyyy"; - private SimpleDateFormat dateFormatter = new SimpleDateFormat(datePattern, Locale.ENGLISH); + private String datePattern = "dd/MM/yyyy"; + private SimpleDateFormat dateFormatter = new SimpleDateFormat(datePattern, Locale.ENGLISH); - public DateLabelFormatter() { - // Enable lenient parsing to be more flexible with user input - dateFormatter.setLenient(false); - } + public DateLabelFormatter() { + // Enable lenient parsing to be more flexible with user input + dateFormatter.setLenient(false); + } - @Override - public Object stringToValue(String text) throws ParseException { - if (text == null || text.trim().isEmpty()) { + @Override + public Object stringToValue(String text) throws ParseException { + if (text == null || text.trim().isEmpty()) { + return null; + } + + // Clean up the input text + text = text.trim(); + java.util.Date parsedDate; + + // Handle different input formats that users might type + if (text.matches("\\d{1,2}/\\d{1,2}/\\d{4}")) { + // Standard format: dd/mm/yyyy or d/m/yyyy + parsedDate = (java.util.Date) dateFormatter.parseObject(text); + } else if (text.matches("\\d{1,2}-\\d{1,2}-\\d{4}")) { + // Handle dash format: dd-mm-yyyy + text = text.replace("-", "/"); + parsedDate = (java.util.Date) dateFormatter.parseObject(text); + } else if (text.matches("\\d{1,2}\\.\\d{1,2}\\.\\d{4}")) { + // Handle dot format: dd.mm.yyyy + text = text.replace(".", "/"); + parsedDate = (java.util.Date) dateFormatter.parseObject(text); + } else if (text.matches("\\d{8}")) { + // Handle compact format: ddmmyyyy + String day = text.substring(0, 2); + String month = text.substring(2, 4); + String year = text.substring(4, 8); + text = day + "/" + month + "/" + year; + parsedDate = (java.util.Date) dateFormatter.parseObject(text); + } else { + // If none of the formats match, try default parsing + parsedDate = (java.util.Date) dateFormatter.parseObject(text); + } + + if (parsedDate != null) { + Calendar cal = Calendar.getInstance(); + cal.setTime(parsedDate); + return cal; + } return null; } - // Clean up the input text - text = text.trim(); - java.util.Date parsedDate; + @Override + public String valueToString(Object value) throws ParseException { + if (value != null) { + Calendar cal = (Calendar) value; + return dateFormatter.format(cal.getTime()); + } - // Handle different input formats that users might type - if (text.matches("\\d{1,2}/\\d{1,2}/\\d{4}")) { - // Standard format: dd/mm/yyyy or d/m/yyyy - parsedDate = (java.util.Date) dateFormatter.parseObject(text); - } else if (text.matches("\\d{1,2}-\\d{1,2}-\\d{4}")) { - // Handle dash format: dd-mm-yyyy - text = text.replace("-", "/"); - parsedDate = (java.util.Date) dateFormatter.parseObject(text); - } else if (text.matches("\\d{1,2}\\.\\d{1,2}\\.\\d{4}")) { - // Handle dot format: dd.mm.yyyy - text = text.replace(".", "/"); - parsedDate = (java.util.Date) dateFormatter.parseObject(text); - } else if (text.matches("\\d{8}")) { - // Handle compact format: ddmmyyyy - String day = text.substring(0, 2); - String month = text.substring(2, 4); - String year = text.substring(4, 8); - text = day + "/" + month + "/" + year; - parsedDate = (java.util.Date) dateFormatter.parseObject(text); - } else { - // If none of the formats match, try default parsing - parsedDate = (java.util.Date) dateFormatter.parseObject(text); + return ""; } - - if (parsedDate != null) { - Calendar cal = Calendar.getInstance(); - cal.setTime(parsedDate); - return cal; - } - return null; - } - - @Override - public String valueToString(Object value) throws ParseException { - if (value != null) { - Calendar cal = (Calendar) value; - return dateFormatter.format(cal.getTime()); - } - - return ""; - } -} \ No newline at end of file + } \ No newline at end of file diff --git a/src/main/java/BsK/client/ui/component/common/MedicineDialog/MedicineDialog.java b/src/main/java/BsK/client/ui/component/common/MedicineDialog/MedicineDialog.java index 002df2d..4f9c9f2 100644 --- a/src/main/java/BsK/client/ui/component/common/MedicineDialog/MedicineDialog.java +++ b/src/main/java/BsK/client/ui/component/common/MedicineDialog/MedicineDialog.java @@ -97,7 +97,7 @@ public class MedicineDialog extends JDialog { private void init(final Frame parent) { setDefaultCloseOperation(DISPOSE_ON_CLOSE); - setSize(1200, 700); + setSize(1200, 800); setLocationRelativeTo(parent); setResizable(true); @@ -249,7 +249,7 @@ public class MedicineDialog extends JDialog { gbc.gridx = 1; gbc.weightx = 1.0; - medicineDescriptionField = new JTextArea(3, 20); + medicineDescriptionField = new JTextArea(3, 15); medicineDescriptionField.setFont(textFont); medicineDescriptionField.setLineWrap(true); medicineDescriptionField.setWrapStyleWord(true); @@ -808,11 +808,7 @@ public class MedicineDialog extends JDialog { double price = priceField.getText().isEmpty() ? 0 : Double.parseDouble(priceField.getText()); double totalValue = quantity * price; - // Create formatter for Vietnamese locale - NumberFormat formatter = NumberFormat.getInstance(new Locale("vi", "VN")); - - // Format the total and set the text - totalField.setText(formatter.format(totalValue)); + totalField.setText(String.format("%.0f", totalValue)); } catch (NumberFormatException e) { totalField.setText("Giá trị không hợp lệ"); diff --git a/src/main/java/BsK/server/network/handler/ServerHandler.java b/src/main/java/BsK/server/network/handler/ServerHandler.java index aca7547..cb64f36 100644 --- a/src/main/java/BsK/server/network/handler/ServerHandler.java +++ b/src/main/java/BsK/server/network/handler/ServerHandler.java @@ -109,15 +109,24 @@ public class ServerHandler extends SimpleChannelInboundHandler= CAST(strftime('%s', date('now', 'start of day')) AS INTEGER) * 1000\n" + - "AND a.checkup_date < CAST(strftime('%s', date('now', 'start of day', '+1 day')) AS INTEGER) * 1000" + """ + SELECT + a.checkup_id, a.checkup_date, c.customer_last_name, c.customer_first_name, + d.doctor_first_name, d.doctor_last_name, a.suggestion, a.diagnosis, a.notes, + a.status, a.customer_id, 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, a.doctor_ultrasound_id, a.queue_number + FROM + checkup AS a + JOIN + customer AS c ON a.customer_id = c.customer_id + JOIN + Doctor D ON a.doctor_id = D.doctor_id + WHERE + a.checkup_date >= CAST(strftime('%s', date('now', 'localtime', 'start of day')) AS INTEGER) * 1000 + AND + a.checkup_date < CAST(strftime('%s', date('now', 'localtime', 'start of day', '+1 day')) AS INTEGER) * 1000""" ); if (!rs.isBeforeFirst()) { @@ -730,56 +739,58 @@ public class ServerHandler extends SimpleChannelInboundHandler 0) { + try (PreparedStatement medicineOrderStmt = conn.prepareStatement(medicineOrderSql, PreparedStatement.RETURN_GENERATED_KEYS)) { + double totalMedicineAmount = 0; + for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) { + if (medicine.length > 8) { + totalMedicineAmount += Double.parseDouble(medicine[8]); + } + } + + medicineOrderStmt.setInt(1, saveCheckupRequest.getCheckupId()); + medicineOrderStmt.setInt(2, saveCheckupRequest.getCustomerId()); + medicineOrderStmt.setDouble(3, totalMedicineAmount); + medicineOrderStmt.setString(4, ""); // payment_method + medicineOrderStmt.setString(5, "Pending"); // status + medicineOrderStmt.setString(6, "Unpaid"); // payment_status + medicineOrderStmt.setString(7, ""); // processed_by + medicineOrderStmt.setString(8, ""); // notes + medicineOrderStmt.executeUpdate(); + + // B. Retrieve the generated key + try (ResultSet generatedKeys = medicineOrderStmt.getGeneratedKeys()) { + if (generatedKeys.next()) { + newPrescriptionId = generatedKeys.getInt(1); + log.info("New prescription_id generated: {}", newPrescriptionId); + } else { + throw new SQLException("Creating medicine order failed, no ID obtained."); + } + } + } - // First, clear old data for this checkup - try (PreparedStatement deleteOrderItemsStmt = conn.prepareStatement("DELETE FROM OrderItem WHERE checkup_id = ?")) { - deleteOrderItemsStmt.setInt(1, saveCheckupRequest.getCheckupId()); - deleteOrderItemsStmt.executeUpdate(); - } - try (PreparedStatement deleteMedicineOrderStmt = conn.prepareStatement("DELETE FROM MedicineOrder WHERE checkup_id = ?")) { - deleteMedicineOrderStmt.setInt(1, saveCheckupRequest.getCheckupId()); - deleteMedicineOrderStmt.executeUpdate(); - } - log.info("Cleared previous medicine prescription for checkup_id: {}", saveCheckupRequest.getCheckupId()); - - // A. Insert a new MedicineOrder record to get the auto-generated ID - String medicineOrderSql = """ - INSERT INTO MedicineOrder ( - checkup_id, customer_id, total_amount, payment_method, - status, payment_status, processed_by, notes - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) - """; - - // Use RETURN_GENERATED_KEYS to get the new ID back - try (PreparedStatement medicineOrderStmt = conn.prepareStatement(medicineOrderSql, PreparedStatement.RETURN_GENERATED_KEYS)) { - // ... (your existing totalMedicineAmount calculation) ... - double totalMedicineAmount = 0; - for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) { - if (medicine.length > 8) { totalMedicineAmount += Double.parseDouble(medicine[8]); } - } - - medicineOrderStmt.setInt(1, saveCheckupRequest.getCheckupId()); - medicineOrderStmt.setInt(2, saveCheckupRequest.getCustomerId()); - medicineOrderStmt.setDouble(3, totalMedicineAmount); - medicineOrderStmt.setString(4, ""); // payment_method - medicineOrderStmt.setString(5, "Pending"); // status - medicineOrderStmt.setString(6, "Unpaid"); // payment_status - medicineOrderStmt.setString(7, ""); // processed_by - medicineOrderStmt.setString(8, ""); // notes - medicineOrderStmt.executeUpdate(); - - // B. Retrieve the generated key - try (ResultSet generatedKeys = medicineOrderStmt.getGeneratedKeys()) { - if (generatedKeys.next()) { - newPrescriptionId = generatedKeys.getInt(1); - log.info("New prescription_id generated: {}", newPrescriptionId); - } else { - throw new SQLException("Creating medicine order failed, no ID obtained."); - } - } - } // C. Insert OrderItems using the new prescription_id String orderItemSql = """ @@ -812,7 +823,7 @@ public class ServerHandler extends SimpleChannelInboundHandler= CAST(strftime('%s', date('now', 'start of day')) AS INTEGER) * 1000\n" + - "AND a.checkup_date < CAST(strftime('%s', date('now', 'start of day', '+1 day')) AS INTEGER) * 1000" + "where a.checkup_date >= CAST(strftime('%s', date('now', 'localtime', 'start of day')) AS INTEGER) * 1000\n" + + "AND a.checkup_date < CAST(strftime('%s', date('now', 'localtime', 'start of day', '+1 day')) AS INTEGER) * 1000" ); String[][] resultArray; diff --git a/src/main/resources/database/BSK.db b/src/main/resources/database/BSK.db index 7f4b4049c39db899bb306965b70f5890d89fdd19..5536d2f5523c803569be5f1d27c0e5b85755499f 100644 GIT binary patch delta 1119 zcmcJOT}%{L6oBuUpPiZA9q!ItL3atuBL0NBxPR7dRZ@$Ju?Ye-ZDMNz3pN z8Lgossdc!GY^ajK_l={W6+n!up`{$HffQy!B>@NENrWe9rgm%u!l|m|j93AHViJmm zqpw$o->42pS!P>$?Bw3S*s8E0NEvcbEecMh;Kdl&0fE=GbtF~!ffRpTMB^H&49{?xwCSSrw|(XhT39`Za728}fi zd%)pw3?V#)hvp*2R|Z|K3Irf5aX*)$Tp@a8IF8Z z{_|Zn5;c3e$T9Q7pVVTr{)}2=hCU+(lZsmY6Y4?1OrBR43VNc$xg!-Zp_};4n=^{n z+|*65x$C03QQkOy_Ri?HTj0nCr%o<9d}F~1^OtT?m*?DhPR?^hc@F0}Po7iGbE@0p zgwx`n<5p3vJ7@Vk>K6dIbzD)s66CF4ed;A}_HNaMh4DD_tu;UKtE6o99scLzRE*nF zsW9tdo%5?(VR6tIpewT{tMzV~jTFfyT6guXrKhSY=!@8KD?l+44~wl6e)WAYu0A5h zg@4ukPs>&7X*rR?{ILBeS?)$M!uZhJbilP#@g~KV^bG5%&S6czODbeZxQ_tUAyj85 z;dNG>P6h-W^m rxBZ`?&bgM>7V$JE;Ky`Fm+@oMqmvSAiB7KnAK7YXGKez+Ir8us=ovc{ delta 5467 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 f6vJuzpzfA;