diff --git a/.vscode/settings.json b/.vscode/settings.json
index a760fd6..9bd06c2 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,4 @@
{
- "java.configuration.updateBuildConfiguration": "interactive",
+ "java.configuration.updateBuildConfiguration": "automatic",
"java.debug.settings.onBuildFailureProceed": true
}
\ No newline at end of file
diff --git a/config/config.properties b/config/config.properties
index 3712df6..70d52d0 100644
--- a/config/config.properties
+++ b/config/config.properties
@@ -9,3 +9,4 @@ 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
diff --git a/medical_invoice.pdf b/medical_invoice.pdf
index 95baf0a..027b59e 100644
Binary files a/medical_invoice.pdf and b/medical_invoice.pdf differ
diff --git a/pom.xml b/pom.xml
index b61a1f8..374ffc8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -316,5 +316,10 @@
poi-ooxml
5.2.5
+
+ com.zaxxer
+ HikariCP
+ 5.1.0
+
diff --git a/src/main/java/BsK/client/Client.java b/src/main/java/BsK/client/Client.java
index 6413cad..e213a50 100644
--- a/src/main/java/BsK/client/Client.java
+++ b/src/main/java/BsK/client/Client.java
@@ -136,6 +136,15 @@ public class Client {
log.info("Using default auto change status: {}", LocalStorage.autoChangeStatusToFinished);
}
+ //load projectPath
+ String projectPath = config.getProperty("app.project_path");
+ if (projectPath != null && !projectPath.trim().isEmpty()) {
+ LocalStorage.pathToProject = projectPath.trim();
+ log.info("Using configured project path: {}", projectPath);
+ } else {
+ log.info("Using default project path: {}", LocalStorage.pathToProject);
+ }
+
// Use defaults if not configured
if (portStr != null) {
int port = Integer.parseInt(portStr);
diff --git a/src/main/java/BsK/client/LocalStorage.java b/src/main/java/BsK/client/LocalStorage.java
index 014eb84..f0c8420 100644
--- a/src/main/java/BsK/client/LocalStorage.java
+++ b/src/main/java/BsK/client/LocalStorage.java
@@ -22,8 +22,11 @@ public class LocalStorage {
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
diff --git a/src/main/java/BsK/client/network/handler/ClientHandler.java b/src/main/java/BsK/client/network/handler/ClientHandler.java
index dcce267..d6c9c3b 100644
--- a/src/main/java/BsK/client/network/handler/ClientHandler.java
+++ b/src/main/java/BsK/client/network/handler/ClientHandler.java
@@ -66,6 +66,7 @@ public class ClientHandler extends SimpleChannelInboundHandler generatePdfBytesAsync() {
return CompletableFuture.supplyAsync(() -> {
@@ -62,105 +62,9 @@ public class MedicineInvoice{
});
}
- public static void main(String[] args) {
- SwingUtilities.invokeLater(() -> {
- JFrame frame = new JFrame("Medicine Invoice Test");
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- frame.setSize(600, 400);
- frame.setLayout(new GridLayout(3, 2, 10, 10));
-
- // Test Case 1: Only Medicines
- JButton medicineOnlyButton = new JButton("Only Medicines");
- medicineOnlyButton.addActionListener(e -> new MedicineInvoice("1001", "Nguyễn Văn A", "01/01/1990", "0123456789",
- "Nam", "123 Đường ABC, Quận XYZ, TP HCM", "Bác sĩ XYZ", "Sốt cao",
- "Nghỉ ngơi nhiều, uống nhiều nước", "https://example.com/drive1",
- new String[][]{
- {"1", "Paracetamol", "2 viên", "viên", "1", "2", "1", "5000", "10000", "Uống sau ăn"}
- },
- new String[][]{}, // No services
- new String[][]{} // No supplements
- ).createDialog(frame));
-
- // Test Case 2: Only Services
- JButton serviceOnlyButton = new JButton("Only Services");
- serviceOnlyButton.addActionListener(e -> new MedicineInvoice("1002", "Trần Thị B", "15/05/1985", "0987654321",
- "Nữ", "456 Đường DEF, Quận ABC, TP HCM", "Bác sĩ ABC", "Khám tổng quát",
- "Theo dõi định kỳ", "https://example.com/drive2",
- new String[][]{}, // No medicines
- new String[][] {
- {"1", "Khám tổng quát", "1", "150000", "150000", "Kiểm tra sức khỏe tổng quát"},
- {"2", "Xét nghiệm máu", "1", "80000", "80000", "Xét nghiệm công thức máu"}
- },
- new String[][]{} // No supplements
- ).createDialog(frame));
-
- // Test Case 3: Only Supplements
- JButton supplementOnlyButton = new JButton("Only Supplements");
- supplementOnlyButton.addActionListener(e -> new MedicineInvoice("1003", "Lê Văn C", "20/12/1992", "0456789123",
- "Nam", "789 Đường GHI, Quận DEF, TP HCM", "Bác sĩ DEF", "Tư vấn dinh dưỡng",
- "Bổ sung vitamin và khoáng chất", "https://example.com/drive3",
- new String[][]{}, // No medicines
- new String[][]{}, // No services
- new String[][] {
- {"1", "Vitamin D3", "1", "viên", "1 viên/ngày sau ăn sáng", "30 viên", "2000", "60000", "Bổ sung vitamin D"},
- {"2", "Omega 3", "2", "viên", "2 viên/ngày sau ăn", "60 viên", "1500", "90000", "Bổ sung dầu cá"}
- }
- ).createDialog(frame));
-
- // Test Case 4: Medicines + Services (No Supplements)
- JButton medServiceButton = new JButton("Medicines + Services");
- medServiceButton.addActionListener(e -> new MedicineInvoice("1004", "Phạm Thị D", "10/03/1988", "0789123456",
- "Nữ", "321 Đường JKL, Quận GHI, TP HCM", "Bác sĩ GHI", "Viêm họng",
- "Điều trị kháng sinh và theo dõi", "https://example.com/drive4",
- new String[][]{
- {"1", "Amoxicillin", "1 vỉ", "viên", "1", "1", "0", "20000", "20000", "Uống trước ăn 30 phút"}
- },
- new String[][] {
- {"1", "Khám tai mũi họng", "1", "100000", "100000", "Khám chuyên khoa"}
- },
- new String[][]{} // No supplements
- ).createDialog(frame));
-
- // Test Case 5: All Three Categories
- JButton allCategoriesButton = new JButton("All Categories");
- allCategoriesButton.addActionListener(e -> new MedicineInvoice("1005", "Hoàng Văn E", "25/07/1990", "0654321987",
- "Nam", "654 Đường MNO, Quận JKL, TP HCM", "Bác sĩ JKL", "Kiểm tra sức khỏe định kỳ",
- "Tổng quát + bổ sung dinh dưỡng", "https://example.com/drive5",
- new String[][]{
- {"1", "Vitamin C", "1 hộp", "viên", "1", "0", "0", "10000", "10000", "Uống buổi sáng"}
- },
- new String[][] {
- {"1", "Khám tổng quát", "1", "150000", "150000", "Kiểm tra sức khỏe tổng quát"}
- },
- new String[][] {
- {"1", "Calcium 500mg", "1 lọ", "viên", "1 viên/ngày sau ăn tối", "30 viên", "2500", "75000", "Bổ sung canxi"}
- }
- ).createDialog(frame));
-
- // Test Case 6: Empty Invoice (No data)
- JButton emptyButton = new JButton("Empty Invoice");
- emptyButton.addActionListener(e -> new MedicineInvoice("1006", "Võ Thị F", "05/11/1995", "0321654987",
- "Nữ", "987 Đường PQR, Quận MNO, TP HCM", "Bác sĩ MNO", "Tư vấn y tế",
- "Chỉ tư vấn, không kê đơn", null,
- new String[][]{}, // No medicines
- new String[][]{}, // No services
- new String[][]{} // No supplements
- ).createDialog(frame));
-
- frame.add(medicineOnlyButton);
- frame.add(serviceOnlyButton);
- frame.add(supplementOnlyButton);
- frame.add(medServiceButton);
- frame.add(allCategoriesButton);
- frame.add(emptyButton);
-
- frame.setVisible(true);
- });
- }
-
public MedicineInvoice(String id, String patientName, String patientDOB, String patientPhone,
- String patientGender, String patientAddress, String doctorName, String diagnosis,
- String notes, String driveURL, String[][] med, String[][] services, String[][] supplements) {
+ String patientGender, String patientAddress, String doctorName, String diagnosis,
+ String notes, String driveURL, String[][] med, String[][] services, String[][] supplements) {
this.id = id;
this.patientName = patientName;
this.patientDOB = patientDOB;
@@ -185,13 +89,15 @@ public class MedicineInvoice{
}
+ private JasperPrint jasperPrint; // Store JasperPrint for reuse
+
/**
* Shows the invoice directly in JasperViewer without creating a custom dialog
*/
public void showDirectJasperViewer() {
try {
- String pdfPath = "medical_invoice.pdf";
- generatePdf(pdfPath);
+ // This method now only fills the report and shows the viewer
+ fillJasperPrint();
// Use the constructor to prevent the application from closing
JasperViewer viewer = new JasperViewer(jasperPrint, false);
viewer.setVisible(true);
@@ -241,7 +147,7 @@ public class MedicineInvoice{
dialog.add(scrollPane, BorderLayout.CENTER);
dialog.add(buttonPanel, BorderLayout.SOUTH);
- String pdfPath = "medical_invoice.pdf";
+ String pdfPath = "temp_medical_invoice.pdf"; // Use a temporary file name
try {
generatePdf(pdfPath);
displayPdfInLabel(pdfPath, pdfViewer);
@@ -255,21 +161,20 @@ public class MedicineInvoice{
try {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("Save PDF As");
- fileChooser.setSelectedFile(new File("medical_invoice.pdf"));
+ // Suggest a file name based on patient name and date
+ fileChooser.setSelectedFile(new File(patientName.replace(" ", "_") + "_invoice_" + date.replace("/", "-") + ".pdf"));
int userSelection = fileChooser.showSaveDialog(dialog);
if (userSelection == JFileChooser.APPROVE_OPTION) {
File fileToSave = fileChooser.getSelectedFile();
- File generatedFile = new File(pdfPath);
- try (InputStream in = new FileInputStream(generatedFile);
- OutputStream out = new FileOutputStream(fileToSave)) {
- byte[] buffer = new byte[1024];
- int bytesRead;
- while ((bytesRead = in.read(buffer)) != -1) {
- out.write(buffer, 0, bytesRead);
- }
- JOptionPane.showMessageDialog(dialog, "PDF saved: " + fileToSave.getAbsolutePath());
+ // Ensure the file has a .pdf extension
+ if (!fileToSave.getName().toLowerCase().endsWith(".pdf")) {
+ fileToSave = new File(fileToSave.getAbsolutePath() + ".pdf");
}
+
+ // Export the already generated JasperPrint object to the chosen file
+ JasperExportManager.exportReportToPdfFile(jasperPrint, fileToSave.getAbsolutePath());
+ JOptionPane.showMessageDialog(dialog, "PDF saved: " + fileToSave.getAbsolutePath());
}
} catch (Exception ex) {
ex.printStackTrace();
@@ -300,8 +205,6 @@ public class MedicineInvoice{
dialog.setVisible(true);
}
- private JasperPrint jasperPrint; // Store JasperPrint for reuse
-
private byte[] generatePdfBytes() throws Exception {
try {
// This method is almost identical to generatePdf, but exports to a byte array
@@ -329,181 +232,129 @@ public class MedicineInvoice{
* Common method to fill the JasperPrint object with data and parameters.
* This avoids code duplication between generating a file and a byte array.
*/
- private void fillJasperPrint() throws JRException, FileNotFoundException {
- // Only fill if it hasn't been filled already
+ private void fillJasperPrint() throws JRException, IOException {
if (jasperPrint != null) {
return;
}
- // 1. Create data sources for medicines, services, and supplements
- java.util.List medicines = new ArrayList<>();
- java.util.List serviceItems = new ArrayList<>();
- java.util.List supplementItems = new ArrayList<>();
- // Convert medicine data to InvoiceItem objects
+ java.util.List medicineItems = new ArrayList<>();
+ java.util.List serviceItems = new ArrayList<>();
+ java.util.List supplementItems = new ArrayList<>();
+
if (med != null) {
for (String[] medicine : med) {
- if (medicine != null && medicine.length >= 10) {
- try {
- String dosageInfo = String.format("S: %s, T: %s, C: %s",
- medicine[4] != null ? medicine[4] : "0",
- medicine[5] != null ? medicine[5] : "0",
- medicine[6] != null ? medicine[6] : "0");
- String amount = medicine[2] + " " + medicine[3];
-
- medicines.add(InvoiceItem.createMedicine(
- medicine[1] != null ? medicine[1] : "", // medName
- dosageInfo, // dosage
- amount // amount
- ));
- } catch (NumberFormatException e) {
- System.err.println("Error parsing medicine data: " + e.getMessage());
- }
+ if (medicine != null && medicine.length >= 12) {
+ try {
+ String medName = medicine[1];
+ String amount = medicine[2] + " " + medicine[3];
+ String dosageInfo = String.format("S: %s, T: %s, C: %s", medicine[4], medicine[5], medicine[6]);
+ String note = medicine[9];
+ String route = medicine[11];
+
+ // Call signature: (Tên, Ghi chú, Liều dùng, Đường dùng, Số lượng)
+ medicineItems.add(InvoiceItem.createMedicine(medName, note, dosageInfo, route, amount));
+ } catch (Exception e) {
+ System.err.println("Error parsing medicine data row: " + java.util.Arrays.toString(medicine) + " | Error: " + e.getMessage());
}
}
}
+ }
- // Convert service data to InvoiceItem objects
- if (services != null) {
+ if (services != null) {
for (String[] service : services) {
- if (service != null && service.length >= 5) {
- try {
- serviceItems.add(InvoiceItem.createService(
- service[1] != null ? service[1] : "", // serName
- service.length > 5 && service[5] != null ? service[5] : "", // serNote
- service[2] != null ? Integer.parseInt(service[2]) : 0, // serAmount
- service[3] != null ? Double.parseDouble(service[3]) : 0.0 // serUnitPrice
- ));
- } catch (NumberFormatException e) {
- System.err.println("Error parsing service data: " + e.getMessage());
- }
+ if (service != null && service.length >= 5) {
+ try {
+ serviceItems.add(InvoiceItem.createService(
+ service[1],
+ service.length > 5 ? service[5] : "",
+ Integer.parseInt(service[2]),
+ Double.parseDouble(service[3])
+ ));
+ } catch (NumberFormatException e) {
+ System.err.println("Error parsing service data: " + e.getMessage());
}
}
}
+ }
- // Convert supplement data to InvoiceItem objects
- if (supplements != null) {
- for (String[] supplement : supplements) {
- if (supplement != null && supplement.length >= 10) {
- try {
- String dosageInfo = String.format("S: %s, T: %s, C: %s",
- supplement[4] != null ? supplement[4] : "0",
- supplement[5] != null ? supplement[5] : "0",
- supplement[6] != null ? supplement[6] : "0");
-
- String amount = supplement[2] + " " + supplement[3]; // Quantity + Unit
-
- supplementItems.add(InvoiceItem.createSupplement(
- supplement[1] != null ? supplement[1] : "", // supName
- supplement[9] != null ? supplement[9] : "", // supNote
- dosageInfo, // supDosage
- amount, // supAmount
- 0.0 // supUnitPrice, since it's not displayed for supplements
- ));
- } catch (Exception e) {
- System.err.println("Error parsing supplement data: " + e.getMessage());
- }
+ if (supplements != null) {
+ for (String[] supplement : supplements) {
+ if (supplement != null && supplement.length >= 12) {
+ try {
+ String supName = supplement[1];
+ String amount = supplement[2] + " " + supplement[3];
+ String dosageInfo = String.format("S: %s, T: %s, C: %s", supplement[4], supplement[5], supplement[6]);
+ String supNote = supplement[9];
+ String supRoute = supplement[11];
+
+ // --- 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));
+
+ } catch (Exception e) {
+ System.err.println("Error parsing supplement data row: " + java.util.Arrays.toString(supplement) + " | Error: " + e.getMessage());
}
}
}
+ }
- // Create data sources
- JRBeanCollectionDataSource medicineDS = new JRBeanCollectionDataSource(medicines);
- JRBeanCollectionDataSource serviceDS = new JRBeanCollectionDataSource(serviceItems);
- JRBeanCollectionDataSource supplementDS = new JRBeanCollectionDataSource(supplementItems);
+ JRBeanCollectionDataSource medicineDS = new JRBeanCollectionDataSource(medicineItems);
+ JRBeanCollectionDataSource serviceDS = new JRBeanCollectionDataSource(serviceItems);
+ JRBeanCollectionDataSource supplementDS = new JRBeanCollectionDataSource(supplementItems);
- // 2. Create parameters map
- java.util.Map parameters = new java.util.HashMap<>();
-
- // Data sources
- parameters.put("medicineDS", medicineDS);
- parameters.put("serviceDS", serviceDS);
- parameters.put("supplementDS", supplementDS);
+ java.util.Map parameters = new java.util.HashMap<>();
+
+ parameters.put("medicineDS", medicineDS);
+ parameters.put("serviceDS", serviceDS);
+ parameters.put("supplementDS", supplementDS);
+ parameters.put(JRParameter.REPORT_LOCALE, new java.util.Locale("vi", "VN"));
+ parameters.put("patientName", patientName != null ? patientName : "");
+ parameters.put("patientDOB", patientDOB != null ? patientDOB : "");
+ parameters.put("patientGender", patientGender != null ? patientGender : "");
+ parameters.put("patientAddress", patientAddress != null ? patientAddress : "");
+ parameters.put("clinicPrefix", LocalStorage.ClinicPrefix != null ? LocalStorage.ClinicPrefix : "");
+ parameters.put("clinicName", LocalStorage.ClinicName != null ? LocalStorage.ClinicName : "");
+ parameters.put("clinicPhone", LocalStorage.ClinicPhone != null ? LocalStorage.ClinicPhone : "");
+ parameters.put("clinicAddress", LocalStorage.ClinicAddress != null ? LocalStorage.ClinicAddress : "");
+ parameters.put("doctorName", doctorName != null ? doctorName : "");
+ parameters.put("checkupDate", date != null ? date : "");
+ parameters.put("patientDiagnos", diagnosis != null ? diagnosis : "");
+ 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());
- // Set locale for Vietnamese formatting
- parameters.put(JRParameter.REPORT_LOCALE, new java.util.Locale("vi", "VN"));
-
- // Patient information
- parameters.put("patientName", patientName != null ? patientName : "");
- parameters.put("patientDOB", patientDOB != null ? patientDOB : "");
- parameters.put("patientGender", patientGender != null ? patientGender : "");
- parameters.put("patientAddress", patientAddress != null ? patientAddress : "");
-
- // Clinic information
- parameters.put("clinicName", LocalStorage.ClinicName != null ? LocalStorage.ClinicName : "");
- parameters.put("clinicPhone", LocalStorage.ClinicPhone != null ? LocalStorage.ClinicPhone : "");
- parameters.put("clinicAddress", LocalStorage.ClinicAddress != null ? LocalStorage.ClinicAddress : "");
-
- // Doctor and medical information
- parameters.put("doctorName", doctorName != null ? doctorName : "");
- parameters.put("checkupDate", date != null ? date : "");
- parameters.put("patientDiagnos", diagnosis != null ? diagnosis : "");
- parameters.put("checkupNote", notes != null ? notes : "");
-
- // Barcode
- parameters.put("barcodeNumber", id != null ? id : "");
-
- // Drive URL for QR Code
- parameters.put("driveURL", driveURL != null ? driveURL : "");
-
- // Logo image path
- String logoPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/assets/icon/logo.jpg";
- parameters.put("logoImage", logoPath);
-
- // Add conditional parameters to check if data sources have data
- parameters.put("hasMedicines", medicines != null && !medicines.isEmpty());
- parameters.put("hasServices", serviceItems != null && !serviceItems.isEmpty());
- parameters.put("hasSupplements", supplementItems != null && !supplementItems.isEmpty());
-
- // 3. Load and compile the JRXML template
- String jrxmlPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml";
- java.io.InputStream inputStream = new java.io.FileInputStream(new java.io.File(jrxmlPath));
+ String resourcePath = "/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/medserinvoice.jrxml";
+ try (InputStream inputStream = MedicineInvoice.class.getResourceAsStream(resourcePath)) {
+ if (inputStream == null) {
+ throw new FileNotFoundException("Không thể tìm thấy tệp mẫu Jasper report trong classpath: " + resourcePath);
+ }
JasperDesign jasperDesign = JRXmlLoader.load(inputStream);
JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
-
- // 4. Fill the report with data
jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, new JREmptyDataSource());
+ }
}
private void displayPdfInLabel(String pdfPath, JPanel pdfPanel) throws Exception {
PDDocument document = PDDocument.load(new File(pdfPath));
PDFRenderer pdfRenderer = new PDFRenderer(document);
- // Create a panel to hold all pages
- pdfPanel.setLayout(new BoxLayout(pdfPanel, BoxLayout.Y_AXIS)); // Stack pages vertically
+ pdfPanel.setLayout(new BoxLayout(pdfPanel, BoxLayout.Y_AXIS));
- // Render each page and add it to the pdfPanel
for (int i = 0; i < document.getNumberOfPages(); i++) {
- BufferedImage image = pdfRenderer.renderImageWithDPI(i, 150); // Adjust DPI as needed
-
- // Scale the image to a smaller size (e.g., 50% of the original size)
- int scaledWidth = image.getWidth() / 2; // Make it half of the original width
- int scaledHeight = image.getHeight() / 2; // Make it half of the original height
-
- // Scale the image using getScaledInstance
- java.awt.Image scaledImage = image.getScaledInstance(scaledWidth, scaledHeight, java.awt.Image.SCALE_SMOOTH);
-
- // Create an ImageIcon from the scaled image
- ImageIcon icon = new ImageIcon(scaledImage);
-
- // Create a label for the image and add it to the panel
+ BufferedImage image = pdfRenderer.renderImageWithDPI(i, 150);
+ ImageIcon icon = new ImageIcon(image);
JLabel pageLabel = new JLabel(icon);
pdfPanel.add(pageLabel);
}
document.close();
- // Put the panel inside a JScrollPane to make it scrollable
- JScrollPane scrollPane = new JScrollPane(pdfPanel);
- scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
- scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); // Added for horizontal scroll if needed
-
- // Increase scroll sensitivity
- JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar();
- verticalScrollBar.setUnitIncrement(20); // Increase for finer control, adjust as needed
- verticalScrollBar.setBlockIncrement(100); // Increase for page-like scrolling, adjust as needed
-
- // Assuming `dialog` is the parent component
- dialog.add(scrollPane, BorderLayout.CENTER);
- dialog.validate();
+
+ dialog.revalidate();
dialog.repaint();
}
@@ -514,39 +365,31 @@ public class MedicineInvoice{
printerJob.setJobName("Print Medicine Invoice");
if (printerJob.printDialog()) {
- printerJob.setPrintable(new Printable() {
- @Override
- public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
- if (pageIndex >= document.getNumberOfPages()) {
- return NO_SUCH_PAGE;
- }
-
- Graphics2D g2 = (Graphics2D) graphics;
- g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
-
- try {
- PDFRenderer pdfRenderer = new PDFRenderer(document);
- BufferedImage pageImage = pdfRenderer.renderImageWithDPI(pageIndex, 300); // Render at 300 DPI
-
- double pageWidth = pageImage.getWidth();
- double pageHeight = pageImage.getHeight();
-
- double scaleX = pageFormat.getImageableWidth() / pageWidth;
- double scaleY = pageFormat.getImageableHeight() / pageHeight;
- double scale = Math.min(scaleX, scaleY);
-
- g2.scale(scale, scale);
- g2.drawImage(pageImage, 0, 0, null);
-
- } catch (IOException e) {
- e.printStackTrace();
- return NO_SUCH_PAGE;
- }
-
- return PAGE_EXISTS;
+ printerJob.setPrintable((graphics, pageFormat, pageIndex) -> {
+ if (pageIndex >= document.getNumberOfPages()) {
+ return Printable.NO_SUCH_PAGE;
}
- });
+ Graphics2D g2 = (Graphics2D) graphics;
+ g2.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
+
+ try {
+ PDFRenderer pdfRenderer = new PDFRenderer(document);
+ BufferedImage pageImage = pdfRenderer.renderImageWithDPI(pageIndex, 300);
+
+ double scale = Math.min(pageFormat.getImageableWidth() / pageImage.getWidth(),
+ pageFormat.getImageableHeight() / pageImage.getHeight());
+
+ g2.scale(scale, scale);
+ g2.drawImage(pageImage, 0, 0, null);
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ return Printable.NO_SUCH_PAGE;
+ }
+
+ return Printable.PAGE_EXISTS;
+ });
printerJob.print();
System.out.println("Printing completed.");
@@ -559,4 +402,4 @@ public class MedicineInvoice{
}
}
-}
+}
\ No newline at end of file
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 59a263d..d29d4c2 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 = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml";
+ jrxmlPath = LocalStorage.pathToProject + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml";
} else {
- jrxmlPath = System.getProperty("user.dir") + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml";
+ jrxmlPath =LocalStorage.pathToProject + "/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml";
}
log.info("Using JRXML template: {}", jrxmlPath);
@@ -187,6 +187,7 @@ public class UltrasoundResult {
parameters.put(JRParameter.REPORT_LOCALE, new Locale("vi", "VN"));
// Populate all the string parameters
+ parameters.put("clinicPrefix", LocalStorage.ClinicPrefix != null ? LocalStorage.ClinicPrefix : "");
parameters.put("clinicName", convertNullToEmpty(LocalStorage.ClinicName, "Phòng khám BSK"));
parameters.put("clinicAddress", convertNullToEmpty(LocalStorage.ClinicAddress, "Địa chỉ phòng khám"));
parameters.put("clinicPhone", convertNullToEmpty(LocalStorage.ClinicPhone, "SĐT phòng khám"));
@@ -200,16 +201,16 @@ public class UltrasoundResult {
parameters.put("barcodeNumber", this.checkupId);
// RTF and plain text content
- parameters.put("checkupNote", this.rtfContent);
- parameters.put("checkupConclusion", this.conclusion);
- parameters.put("checkupSuggestion", this.suggestion);
- parameters.put("reCheckupDate", this.recheckupDate);
+ parameters.put("checkupNote", this.rtfContent != null ? this.rtfContent : "");
+ parameters.put("checkupConclusion", this.conclusion != null ? this.conclusion : "");
+ parameters.put("checkupSuggestion", this.suggestion != null ? this.suggestion : "");
+ parameters.put("reCheckupDate", this.recheckupDate != null ? this.recheckupDate : "");
// Vitals data
- parameters.put("checkupHeight", this.customerHeight);
- parameters.put("checkupWeight", this.customerWeight);
- parameters.put("checkupHeartBeat", this.heartRate);
- parameters.put("checkupBloodPressure", this.bloodPressure);
+ 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 : "");
// Template title
parameters.put("templateTitle", this.templateTitle);
@@ -226,7 +227,7 @@ public class UltrasoundResult {
int numberOfImages = this.selectedImages.size();
parameters.put("numberImage", numberOfImages);
- String projectDir = System.getProperty("user.dir");
+ String projectDir = LocalStorage.pathToProject;
parameters.put("logoImage", projectDir + "/src/main/java/BsK/client/ui/assets/icon/logo.jpg");
for (int i = 0; i < 6; i++) {
@@ -362,7 +363,7 @@ public class UltrasoundResult {
}
Paragraph clinicInfo = new Paragraph()
- .add(new Text(LocalStorage.ClinicName + "\n").setFont(boldFont).setFontSize(10))
+ .add(new Text(LocalStorage.ClinicPrefix + " " + LocalStorage.ClinicName + "\n").setFont(boldFont).setFontSize(10))
.add(new Text("Địa chỉ: " + LocalStorage.ClinicAddress + "\n").setFontSize(8))
.add(new Text("Điện thoại: " + LocalStorage.ClinicPhone).setFontSize(8))
.setFont(font);
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 bdc973b..7779fd8 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
@@ -13,6 +13,7 @@ public class InvoiceItem {
private String dosage;
private String amount;
private Double unitPrice;
+ private String medRoute;
private String medNote;
// --- Service Fields ---
@@ -26,21 +27,21 @@ public class InvoiceItem {
private String supNote;
private String supDosage;
private String supAmount;
- private Double supUnitPrice;
-
+ private String supRoute;
// Private constructor to be used by factory methods
private InvoiceItem() {}
// --- Factory Methods ---
- public static InvoiceItem createMedicine(String medName, String dosage, String amount) {
+ public static InvoiceItem createMedicine(String medName, String medNote, String dosage, String route, String amount) {
InvoiceItem item = new InvoiceItem();
item.type = "MED";
item.medName = medName;
item.dosage = dosage;
+ item.medRoute = route;
item.amount = amount;
item.unitPrice = 0.0;
- item.medNote = "";
+ item.medNote = medNote;
return item;
}
@@ -54,14 +55,14 @@ public class InvoiceItem {
return item;
}
- public static InvoiceItem createSupplement(String supName, String supNote, String supDosage, String supAmount, Double supUnitPrice) {
+ public static InvoiceItem createSupplement(String supName, String supNote, String supDosage, String supAmount, String supRoute) {
InvoiceItem item = new InvoiceItem();
item.type = "SUP";
item.supName = supName;
item.supNote = supNote;
item.supDosage = supDosage;
item.supAmount = supAmount;
- item.supUnitPrice = supUnitPrice;
+ item.supRoute = supRoute;
return item;
}
@@ -71,6 +72,7 @@ public class InvoiceItem {
public String getType() { return type; }
public String getMedName() { return medName; }
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; }
@@ -82,5 +84,5 @@ public class InvoiceItem {
public String getSupNote() { return supNote; }
public String getSupDosage() { return supDosage; }
public String getSupAmount() { return supAmount; }
- public Double getSupUnitPrice() { return supUnitPrice; }
+ public String getSupRoute() { return supRoute; }
}
\ No newline at end of file
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 48a416b..09e4932 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
@@ -27,7 +27,8 @@
-
+
+
@@ -68,6 +69,7 @@
+
@@ -86,12 +88,12 @@
-
+
-
+
-
+
@@ -110,7 +112,7 @@
-
+
@@ -119,7 +121,7 @@
-
+
@@ -138,6 +140,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -173,7 +195,7 @@
-
+
@@ -188,17 +210,17 @@
-
-
+
+
-
-
-
+
+
+
-
+
-
-
+
+
@@ -216,7 +238,7 @@
-
+
@@ -225,7 +247,7 @@
-
+
@@ -244,9 +266,29 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -263,7 +305,7 @@
-
+
@@ -279,7 +321,7 @@
-
+
@@ -292,13 +334,13 @@
-
+
-
+
-
+
@@ -329,8 +371,8 @@
-
-
+
+
@@ -339,7 +381,7 @@
-
+
@@ -383,8 +425,8 @@
-
-
+
+
@@ -434,8 +476,8 @@
-
-
+
+
@@ -473,7 +515,7 @@
-
+
@@ -489,8 +531,8 @@
-
-
+
+
@@ -521,84 +563,88 @@
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
+
-
+
-
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml
index b581bb4..2cc8225 100644
--- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml
+++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult.jrxml
@@ -38,24 +38,25 @@
+
-
-
+
+
-
-
+
+
-
+
-
+
@@ -88,28 +89,28 @@
-
+
-
+
-
+
-
+
-
+
-
+
@@ -118,10 +119,10 @@
-
+
-
+
@@ -130,20 +131,23 @@
-
+
-
+
-
+
+
+
+
-
+
@@ -153,19 +157,19 @@
-
+
-
+
-
+
-
+
-
+
@@ -173,7 +177,7 @@
-
+
@@ -260,11 +264,17 @@
-
+
+
+
+
+
+
+
diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml
index f6082c0..b4f531b 100644
--- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml
+++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/print_forms/ultrasoundresult_potrait.jrxml
@@ -38,6 +38,7 @@
+
@@ -49,58 +50,58 @@
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -109,44 +110,44 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -160,12 +161,21 @@
-
+
+
+
+
+
+
+
+
+
+
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 24df594..d3c473c 100644
--- a/src/main/java/BsK/client/ui/component/DataDialog/DataDialog.java
+++ b/src/main/java/BsK/client/ui/component/DataDialog/DataDialog.java
@@ -530,9 +530,9 @@ public class DataDialog extends JDialog {
public ActionButtonRenderer() {
setLayout(new FlowLayout(FlowLayout.CENTER, 2, 2));
- ImageIcon editIcon = createIcon("/BsK/client/ui/assets/icon/edit.png", 16, 16);
+ ImageIcon editIcon = createIcon("src/main/java/BsK/client/ui/assets/icon/edit.png", 16, 16);
editButton = new JButton(editIcon);
- ImageIcon deleteIcon = createIcon("/BsK/client/ui/assets/icon/delete.png", 16, 16);
+ ImageIcon deleteIcon = createIcon("src/main/java/BsK/client/ui/assets/icon/delete.png", 16, 16);
deleteButton = new JButton(deleteIcon);
editButton.setPreferredSize(new Dimension(30, 25));
@@ -564,9 +564,9 @@ public class DataDialog extends JDialog {
super(new JCheckBox());
panel = new JPanel(new FlowLayout(FlowLayout.CENTER, 2, 2));
- ImageIcon editIcon = createIcon("/BsK/client/ui/assets/icon/edit.png", 16, 16);
+ ImageIcon editIcon = createIcon("src/main/java/BsK/client/ui/assets/icon/edit.png", 16, 16);
editButton = new JButton(editIcon);
- ImageIcon deleteIcon = createIcon("/BsK/client/ui/assets/icon/delete.png", 16, 16);
+ ImageIcon deleteIcon = createIcon("src/main/java/BsK/client/ui/assets/icon/delete.png", 16, 16);
deleteButton = new JButton(deleteIcon);
editButton.setPreferredSize(new Dimension(30, 25));
diff --git a/src/main/java/BsK/client/ui/component/DataDialog/MedicineManagementPanel.java b/src/main/java/BsK/client/ui/component/DataDialog/MedicineManagementPanel.java
index 42dbcfd..9f4ee49 100644
--- a/src/main/java/BsK/client/ui/component/DataDialog/MedicineManagementPanel.java
+++ b/src/main/java/BsK/client/ui/component/DataDialog/MedicineManagementPanel.java
@@ -37,12 +37,13 @@ public class MedicineManagementPanel extends JPanel {
private JTextField medicineNameField;
private JTextField medicineCompanyField;
private JTextArea medicineDescriptionField;
+ private JTextField routeField; // --- ADDED --- Field for Route of Administration
private JTextField medicineUnitField;
private JTextField medicinePriceField;
private JSpinner morningSpinner, noonSpinner, eveningSpinner;
private JTextArea noteField;
- private JCheckBox chkIsDeleted; // Checkbox for soft delete
- private JCheckBox chkIsSupplement; // Checkbox for supplement status
+ private JCheckBox chkIsDeleted; // Checkbox for soft delete
+ private JCheckBox chkIsSupplement; // Checkbox for supplement status
// --- Action Buttons ---
private JButton btnAdd, btnEdit, btnClear;
@@ -151,6 +152,22 @@ public class MedicineManagementPanel extends JPanel {
medicineInfoPanel.add(descriptionScrollPane, gbc);
gbc.weightx = 0.0;
+ // --- ADDED: Route of Administration Field ---
+ gbc.gridy++;
+ gbc.gridx = 0;
+ JLabel routeLabel = new JLabel("Đường dùng:");
+ routeLabel.setFont(labelFont);
+ medicineInfoPanel.add(routeLabel, gbc);
+
+ gbc.gridx = 1;
+ gbc.weightx = 1.0;
+ routeField = new JTextField(20);
+ routeField.setFont(textFont);
+ routeField.setPreferredSize(textFieldSize);
+ medicineInfoPanel.add(routeField, gbc);
+ gbc.weightx = 0.0;
+ // --- END ADDED ---
+
mainGbc.gridx = 0; mainGbc.gridy = 0;
mainInputPanel.add(medicineInfoPanel, mainGbc);
@@ -331,6 +348,7 @@ public class MedicineManagementPanel extends JPanel {
// 2. Gather data from all other fields
String company = medicineCompanyField.getText().trim();
String description = medicineDescriptionField.getText().trim();
+ String route = routeField.getText().trim(); // --- ADDED ---
String unit = medicineUnitField.getText().trim();
Boolean isSupplement = chkIsSupplement.isSelected();
Boolean isDeleted = false; // New medicines are active by default
@@ -352,13 +370,13 @@ public class MedicineManagementPanel extends JPanel {
// 4. Construct the special 'preferredNote' string
String preferredNote = morningSpinner.getValue().toString() + ","
- + noonSpinner.getValue().toString() + ","
- + eveningSpinner.getValue().toString() + ","
- + noteField.getText().trim();
+ + noonSpinner.getValue().toString() + ","
+ + eveningSpinner.getValue().toString() + ","
+ + noteField.getText().trim();
// 5. Create the request packet and send it to the server
- // IMPORTANT: Assumes your AddMedicineRequest packet is updated to include 'supplement'
- AddMedicineRequest request = new AddMedicineRequest(name, company, description, unit, price, preferredNote, isSupplement, isDeleted);
+ // --- UPDATED ---: Added 'route' to the request constructor
+ AddMedicineRequest request = new AddMedicineRequest(name, company, description, unit, price, preferredNote, isSupplement, isDeleted, route);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request);
// 6. Provide feedback and reset the form
@@ -393,6 +411,7 @@ public class MedicineManagementPanel extends JPanel {
String id = selectedMedicineId;
String company = medicineCompanyField.getText().trim();
String description = medicineDescriptionField.getText().trim();
+ String route = routeField.getText().trim(); // --- ADDED ---
String unit = medicineUnitField.getText().trim();
Boolean isSupplement = chkIsSupplement.isSelected();
Boolean isDeleted = chkIsDeleted.isSelected();
@@ -414,13 +433,13 @@ public class MedicineManagementPanel extends JPanel {
// 5. Construct the 'preferredNote' string
String preferredNote = morningSpinner.getValue().toString() + ","
- + noonSpinner.getValue().toString() + ","
- + eveningSpinner.getValue().toString() + ","
- + noteField.getText().trim();
+ + noonSpinner.getValue().toString() + ","
+ + eveningSpinner.getValue().toString() + ","
+ + noteField.getText().trim();
// 6. Create the request packet and send it
- // IMPORTANT: Assumes your EditMedicineRequest packet is updated to include 'supplement'
- EditMedicineRequest request = new EditMedicineRequest(id, name, company, description, unit, price, preferredNote, isSupplement, isDeleted);
+ // --- UPDATED ---: Added 'route' to the request constructor
+ EditMedicineRequest request = new EditMedicineRequest(id, name, company, description, unit, price, preferredNote, isSupplement, isDeleted, route);
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request);
// 7. Provide user feedback and clear the form
@@ -507,6 +526,7 @@ public class MedicineManagementPanel extends JPanel {
medicineNameField.setText(med.getName());
medicineCompanyField.setText(med.getCompany());
medicineDescriptionField.setText(med.getDescription());
+ routeField.setText(med.getRoute()); // --- ADDED ---
medicineUnitField.setText(med.getUnit());
medicinePriceField.setText(med.getSellingPrice());
@@ -552,6 +572,7 @@ public class MedicineManagementPanel extends JPanel {
medicineNameField.setText("");
medicineCompanyField.setText("");
medicineDescriptionField.setText("");
+ routeField.setText(""); // --- ADDED ---
medicineUnitField.setText("");
medicinePriceField.setText("");
diff --git a/src/main/java/BsK/client/ui/component/DataDialog/UserManagementPanel.java b/src/main/java/BsK/client/ui/component/DataDialog/UserManagementPanel.java
index dfc5b5a..7fde327 100644
--- a/src/main/java/BsK/client/ui/component/DataDialog/UserManagementPanel.java
+++ b/src/main/java/BsK/client/ui/component/DataDialog/UserManagementPanel.java
@@ -149,7 +149,7 @@ public class UserManagementPanel extends JPanel {
panel.add(new JLabel("Vai trò:"), gbc);
gbc.gridx = 1;
gbc.weightx = 1;
- roleComboBox = new JComboBox<>(new String[]{"ADMIN", "DOCTOR", "STAFF"});
+ roleComboBox = new JComboBox<>(new String[]{"ADMIN", "USER"});
panel.add(roleComboBox, gbc);
}
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 ee61d8b..002df2d 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
@@ -18,17 +18,18 @@ import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.*;
-import java.text.NumberFormat; // --- ADD THIS IMPORT ---
+import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.Locale; // --- ADD THIS IMPORT ---
+import java.util.Locale;
@Slf4j
public class MedicineDialog extends JDialog {
private JTextField medicineNameField;
private JTextField medicineCompanyField;
private JTextArea medicineDescriptionField;
+ private JTextField routeField; // --- ADDED ---
private JCheckBox chkIsSupplement; // The new checkbox
private JSpinner quantitySpinner;
private JComboBox UnitComboBox;
@@ -258,6 +259,23 @@ public class MedicineDialog extends JDialog {
medicineInfoPanel.add(descriptionScrollPane, gbc);
gbc.weightx = 0.0;
+ // --- ADDED: Route of Administration Field ---
+ gbc.gridy++;
+ gbc.gridx = 0;
+ JLabel routeLabel = new JLabel("Đường dùng:");
+ routeLabel.setFont(labelFont);
+ medicineInfoPanel.add(routeLabel, gbc);
+
+ gbc.gridx = 1;
+ gbc.weightx = 1.0;
+ routeField = new JTextField(20);
+ routeField.setFont(textFont);
+ routeField.setPreferredSize(textFieldSize);
+ routeField.setEditable(false);
+ medicineInfoPanel.add(routeField, gbc);
+ gbc.weightx = 0.0;
+ // --- END ADDED ---
+
gbc.gridy++;
gbc.gridx = 0;
gbc.gridwidth = 2;
@@ -408,10 +426,12 @@ public class MedicineDialog extends JDialog {
suggestionPopup.add(suggestionScrollPane, BorderLayout.CENTER);
suggestionPopup.setFocusable(false);
- String[] selectedColumnNames = {"ID", "Tên thuốc", "SL", "ĐVT", "Sáng", "Trưa", "Chiều", "Đơn giá", "Thành tiền", "Ghi chú", "Supplement"};
+ // --- UPDATED: Added "Đường dùng" column ---
+ String[] selectedColumnNames = {"ID", "Tên thuốc", "SL", "ĐVT", "Sáng", "Trưa", "Chiều", "Đơn giá", "Thành tiền", "Ghi chú", "Supplement", "Đường dùng"};
selectedTableModel = new DefaultTableModel(new String[][]{}, selectedColumnNames) {
@Override
public boolean isCellEditable(int row, int column) {
+ // Allow editing Quantity, Dosage, and Note
return column == 2 || column == 4 || column == 5 || column == 6 || column == 9;
}
};
@@ -603,8 +623,9 @@ public class MedicineDialog extends JDialog {
logger.info("Prescription saved.");
if (medicinePrescription != null) {
for (String[] row : medicinePrescription) {
- logger.info("Med: ID={}, Name={}, Qty={}, Unit={}, M={}, N={}, E={}, Price={}, Total={}, Note={}",
- row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9]);
+ // --- UPDATED: Logged the new 'route' field ---
+ logger.info("Med: ID={}, Name={}, Qty={}, Unit={}, M={}, N={}, E={}, Price={}, Total={}, Note={}, Supp={}, Route={}",
+ row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11]);
}
}
dispose();
@@ -724,6 +745,7 @@ public class MedicineDialog extends JDialog {
medicineCompanyField.setText(medicine.getCompany());
medicineDescriptionField.setText(medicine.getDescription());
+ routeField.setText(medicine.getRoute()); // --- ADDED ---
UnitComboBox.setSelectedItem(medicine.getUnit());
priceField.setText(medicine.getSellingPrice());
@@ -764,6 +786,7 @@ public class MedicineDialog extends JDialog {
// Don't clear the name field as the user is typing in it
medicineCompanyField.setText("");
medicineDescriptionField.setText("");
+ routeField.setText(""); // --- ADDED ---
UnitComboBox.setSelectedIndex(0);
priceField.setText("");
totalField.setText("");
@@ -800,7 +823,7 @@ public class MedicineDialog extends JDialog {
private void addSelectedMedicine() {
int selectedRowInSuggestionTable = suggestionTable.getSelectedRow();
- String medicineId, name, unit, currentPrice, supplement;
+ String medicineId, name, unit, currentPrice, supplement, route; // --- UPDATED ---
String searchName = medicineNameField.getText().trim();
if (searchName.isEmpty()) {
@@ -835,6 +858,7 @@ public class MedicineDialog extends JDialog {
unit = selectedMedicine.getUnit();
currentPrice = selectedMedicine.getSellingPrice();
supplement = selectedMedicine.getSupplement();
+ route = selectedMedicine.getRoute(); // --- ADDED ---
int quantity = (Integer) quantitySpinner.getValue();
int morningVal = (Integer) morningSpinner.getValue();
@@ -848,8 +872,8 @@ public class MedicineDialog extends JDialog {
return;
}
- // Include supplement information
- selectedTableModel.addRow(new Object[]{medicineId, name, quantity, unit, morningVal, noonVal, eveningVal, currentPrice, totalAmount, note, supplement});
+ // --- UPDATED: Add 'route' to the table row data ---
+ selectedTableModel.addRow(new Object[]{medicineId, name, quantity, unit, morningVal, noonVal, eveningVal, currentPrice, totalAmount, note, supplement, route});
// Clear fields for next entry
isProgrammaticallySettingMedicineNameField = true;
@@ -874,22 +898,27 @@ public class MedicineDialog extends JDialog {
medicinePrescription = new String[0][0];
return;
}
- medicinePrescription = new String[rowCount][11];
+ // --- UPDATED: Array size changed from 11 to 12 ---
+ medicinePrescription = new String[rowCount][12];
for (int i = 0; i < rowCount; i++) {
- medicinePrescription[i][0] = selectedTableModel.getValueAt(i, 0).toString();
- medicinePrescription[i][1] = selectedTableModel.getValueAt(i, 1).toString();
- medicinePrescription[i][2] = selectedTableModel.getValueAt(i, 2).toString();
- medicinePrescription[i][3] = selectedTableModel.getValueAt(i, 3).toString();
- medicinePrescription[i][4] = selectedTableModel.getValueAt(i, 4).toString();
- medicinePrescription[i][5] = selectedTableModel.getValueAt(i, 5).toString();
- medicinePrescription[i][6] = selectedTableModel.getValueAt(i, 6).toString();
- medicinePrescription[i][7] = selectedTableModel.getValueAt(i, 7).toString();
- medicinePrescription[i][8] = selectedTableModel.getValueAt(i, 8).toString();
- medicinePrescription[i][9] = selectedTableModel.getValueAt(i, 9).toString();
+ medicinePrescription[i][0] = selectedTableModel.getValueAt(i, 0).toString(); // ID
+ medicinePrescription[i][1] = selectedTableModel.getValueAt(i, 1).toString(); // Tên
+ medicinePrescription[i][2] = selectedTableModel.getValueAt(i, 2).toString(); // SL
+ medicinePrescription[i][3] = selectedTableModel.getValueAt(i, 3).toString(); // ĐVT
+ medicinePrescription[i][4] = selectedTableModel.getValueAt(i, 4).toString(); // Sáng
+ medicinePrescription[i][5] = selectedTableModel.getValueAt(i, 5).toString(); // Trưa
+ medicinePrescription[i][6] = selectedTableModel.getValueAt(i, 6).toString(); // Chiều
+ medicinePrescription[i][7] = selectedTableModel.getValueAt(i, 7).toString(); // Đơn giá
+ medicinePrescription[i][8] = selectedTableModel.getValueAt(i, 8).toString(); // Thành tiền
+ medicinePrescription[i][9] = selectedTableModel.getValueAt(i, 9).toString(); // Ghi chú
// Get supplement value (0/1) or default to "0" if null
Object supplementObj = selectedTableModel.getValueAt(i, 10);
- medicinePrescription[i][10] = (supplementObj != null) ? supplementObj.toString() : "0";
+ medicinePrescription[i][10] = (supplementObj != null) ? supplementObj.toString() : "0"; // Supplement
+
+ // --- ADDED: Get route value ---
+ Object routeObj = selectedTableModel.getValueAt(i, 11);
+ medicinePrescription[i][11] = (routeObj != null) ? routeObj.toString() : ""; // Đường dùng
}
}
@@ -958,7 +987,8 @@ public class MedicineDialog extends JDialog {
if (prescription != null && prescription.length > 0) {
System.out.println("Prescription obtained:");
for (String[] item : prescription) {
- System.out.println(String.format("ID:%s, Name:%s, Qty:%s, Unit:%s, M:%s, N:%s, E:%s, Price:%s, Total:%s, Note:%s",
+ // --- UPDATED: Test printout format ---
+ System.out.println(String.format("ID:%s, Name:%s, Qty:%s, Unit:%s, M:%s, N:%s, E:%s, Price:%s, Total:%s, Note:%s, Supp:%s, Route:%s",
(Object[]) item));
}
} else {
diff --git a/src/main/java/BsK/common/entity/Medicine.java b/src/main/java/BsK/common/entity/Medicine.java
index 6c9d678..b86d1af 100644
--- a/src/main/java/BsK/common/entity/Medicine.java
+++ b/src/main/java/BsK/common/entity/Medicine.java
@@ -20,6 +20,7 @@ public class Medicine {
private String preferredNote;
private String supplement; // 0: no, 1: yes
private String deleted; // 0: no, 1: yes
+ private String route;
/**
* Constructor to create Medicine from array data
@@ -38,6 +39,7 @@ public class Medicine {
this.preferredNote = data[6];
this.supplement = data[7];
this.deleted = data[8];
+ this.route = data[9];
}
/**
@@ -45,6 +47,6 @@ public class Medicine {
* @return String array representation of Medicine
*/
public String[] toStringArray() {
- return new String[]{id, name, company, description, unit, sellingPrice, preferredNote, supplement, deleted};
+ return new String[]{id, name, company, description, unit, sellingPrice, preferredNote, supplement, deleted, route};
}
}
\ No newline at end of file
diff --git a/src/main/java/BsK/common/packet/req/AddMedicineRequest.java b/src/main/java/BsK/common/packet/req/AddMedicineRequest.java
index 72057f4..ec08947 100644
--- a/src/main/java/BsK/common/packet/req/AddMedicineRequest.java
+++ b/src/main/java/BsK/common/packet/req/AddMedicineRequest.java
@@ -15,4 +15,5 @@ public class AddMedicineRequest implements Packet{
private String preferredNote;
private Boolean supplement;
private Boolean deleted;
+ private String route;
}
diff --git a/src/main/java/BsK/common/packet/req/EditMedicineRequest.java b/src/main/java/BsK/common/packet/req/EditMedicineRequest.java
index e99592e..428d8e8 100644
--- a/src/main/java/BsK/common/packet/req/EditMedicineRequest.java
+++ b/src/main/java/BsK/common/packet/req/EditMedicineRequest.java
@@ -16,4 +16,5 @@ public class EditMedicineRequest implements Packet {
private String preferredNote;
private Boolean supplement;
private Boolean deleted;
+ private String route;
}
diff --git a/src/main/java/BsK/common/packet/res/ClinicInfoResponse.java b/src/main/java/BsK/common/packet/res/ClinicInfoResponse.java
index ed225f0..921608f 100644
--- a/src/main/java/BsK/common/packet/res/ClinicInfoResponse.java
+++ b/src/main/java/BsK/common/packet/res/ClinicInfoResponse.java
@@ -12,4 +12,5 @@ public class ClinicInfoResponse implements Packet {
private String clinicName;
private String clinicAddress;
private String clinicPhone;
+ private String clinicPrefix;
}
diff --git a/src/main/java/BsK/server/Server.java b/src/main/java/BsK/server/Server.java
index 32154d9..aab5115 100644
--- a/src/main/java/BsK/server/Server.java
+++ b/src/main/java/BsK/server/Server.java
@@ -2,6 +2,8 @@ package BsK.server;
import BsK.server.network.handler.ServerHandler;
import BsK.server.service.GoogleDriveServiceOAuth;
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
@@ -23,32 +25,32 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
import java.sql.Connection;
-import java.sql.DriverManager;
import java.sql.SQLException;
-import java.sql.Statement;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Server {
- private static int PORT;
- public static Connection connection;
- public static GoogleDriveServiceOAuth googleDriveService;
- private static boolean googleDriveEnabled = true; // Can be configured
- private static String googleDriveRootFolderName = "BSK_Clinic_Patient_Files"; // Default
- public static String imageDbPath = "img_db"; // Default
- public static String checkupMediaBaseDir = "src/main/resources/image/checkup_media"; // Default
+ private static int PORT;
+
+ // --- REFACTORED DATABASE CONNECTION ---
+ // The single, static connection pool (DataSource). This is thread-safe.
+ private static HikariDataSource dataSource;
+
+ // These static fields are still okay as they are configured once at startup.
+ public static GoogleDriveServiceOAuth googleDriveService;
+ private static boolean googleDriveEnabled = true;
+ private static String googleDriveRootFolderName = "BSK_Clinic_Patient_Files";
+ public static String imageDbPath = "img_db";
+ public static String checkupMediaBaseDir = "src/main/resources/image/checkup_media";
+ // Static initializer block to set up the server configuration and connection pool
static {
try {
- // Load configuration
+ // --- Load configuration ---
Properties props = new Properties();
-
- // First try to load from external config file
File externalConfig = new File("config/config.properties");
if (externalConfig.exists()) {
try (var input = Files.newInputStream(externalConfig.toPath())) {
@@ -56,7 +58,6 @@ public class Server {
log.info("Loaded configuration from external file: {}", externalConfig.getAbsolutePath());
}
} else {
- // Fall back to internal config
try (InputStream input = Server.class.getClassLoader().getResourceAsStream("config.properties")) {
if (input == null) {
log.error("Unable to find config.properties");
@@ -66,67 +67,108 @@ public class Server {
log.info("Loaded configuration from internal resources");
}
}
-
- // Get port configuration
- String portStr = props.getProperty("server.port");
- String addressStr = props.getProperty("server.address");
-
- // Log address configuration
- if (addressStr != null) {
- log.info("Server address configured as: {}", addressStr);
- } else {
- log.info("No server address configured, clients will need to use the correct address to connect");
- }
-
- // Get port configuration
- if (portStr != null) {
- PORT = Integer.parseInt(portStr);
- log.info("Using configured port: {}", PORT);
- } else {
- PORT = 1999;
- log.info("Using default port: {}", PORT);
- }
- // Get Google Drive configuration
- String driveEnabledStr = props.getProperty("google.drive.enabled");
- if (driveEnabledStr != null) {
- googleDriveEnabled = Boolean.parseBoolean(driveEnabledStr);
- }
-
- String driveRootFolderStr = props.getProperty("google.drive.root.folder");
- if (driveRootFolderStr != null && !driveRootFolderStr.trim().isEmpty()) {
- googleDriveRootFolderName = driveRootFolderStr.trim();
- }
-
- log.info("Google Drive integration enabled: {}", googleDriveEnabled);
+ PORT = Integer.parseInt(props.getProperty("server.port", "1999"));
+ log.info("Using port: {}", PORT);
+
+ // Load other properties
+ googleDriveEnabled = Boolean.parseBoolean(props.getProperty("google.drive.enabled", "true"));
+ googleDriveRootFolderName = props.getProperty("google.drive.root.folder", "BSK_Clinic_Patient_Files").trim();
+ imageDbPath = props.getProperty("storage.image_db_path", "img_db").trim();
+ checkupMediaBaseDir = props.getProperty("storage.checkup_media_base_dir", "src/main/resources/image/checkup_media").trim();
+ log.info("Google Drive enabled: {}", googleDriveEnabled);
log.info("Google Drive root folder: {}", googleDriveRootFolderName);
-
- // Get Storage paths configuration
- String imageDbPathStr = props.getProperty("storage.image_db_path");
- if (imageDbPathStr != null && !imageDbPathStr.trim().isEmpty()) {
- imageDbPath = imageDbPathStr.trim();
- }
-
- String checkupMediaBaseDirStr = props.getProperty("storage.checkup_media_base_dir");
- if (checkupMediaBaseDirStr != null && !checkupMediaBaseDirStr.trim().isEmpty()) {
- checkupMediaBaseDir = checkupMediaBaseDirStr.trim();
- }
-
log.info("Image DB path: {}", imageDbPath);
log.info("Checkup media base dir: {}", checkupMediaBaseDir);
- // Directly use the provided database path
+
+ // --- DATABASE POOL INITIALIZATION ---
String dbPath = "src/main/resources/database/BSK.db";
- connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
+
+ // 1. Configure HikariCP
+ HikariConfig config = new HikariConfig();
+ config.setJdbcUrl("jdbc:sqlite:" + dbPath);
+ config.setConnectionTestQuery("SELECT 1");
+ config.setMaximumPoolSize(10); // Max 10 concurrent connections, perfect for your use case
+ config.setPoolName("BsK-DB-Pool");
+ config.addDataSourceProperty("cachePrepStmts", "true");
+ config.addDataSourceProperty("prepStmtCacheSize", "250");
+ config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
+
+ // 2. Create the DataSource (the pool itself)
+ dataSource = new HikariDataSource(config);
+ log.info("✅ Database connection pool initialized successfully for {}", dbPath);
// Initialize Google Drive service if enabled
initializeGoogleDriveService();
} catch (Exception e) {
+ log.error("❌ Failed to initialize server", e);
throw new RuntimeException("Failed to initialize server", e);
}
}
+ /**
+ * This is the new, safe way for handlers to get a database connection.
+ * @return A database connection from the pool.
+ * @throws SQLException if a connection cannot be obtained.
+ */
+ public static Connection getConnection() throws SQLException {
+ return dataSource.getConnection();
+ }
+
+ public static void main(String[] args) throws InterruptedException {
+ log.info("\n____ _____ _ __\n| _ \\ / ____| |/ \n| |_) | (___ | ' / \n| _ < \\___ \\| <\n| |_) |____) | . \\\n|____/|_____/|_|\\_\n");
+ ServerDashboard dashboard = ServerDashboard.getInstance();
+ dashboard.setVisible(true);
+ dashboard.updateStatus("Starting...", Color.ORANGE);
+ dashboard.updatePort(PORT);
+ dashboard.updateGoogleDriveStatus(isGoogleDriveConnected(), isGoogleDriveConnected() ? "Connected" : "Disconnected");
+
+ EventLoopGroup parentGroup = Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
+ EventLoopGroup childGroup = Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
+ try {
+ ServerBootstrap bootstrap = new ServerBootstrap()
+ .group(parentGroup, childGroup)
+ .channel(Epoll.isAvailable() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
+ .localAddress(new InetSocketAddress(PORT))
+ .childHandler(new ChannelInitializer() {
+ @Override
+ protected void initChannel(SocketChannel ch) throws Exception {
+ ch.pipeline()
+ .addLast(new IdleStateHandler(60 * 30, 0, 0, TimeUnit.SECONDS))
+ .addLast(new HttpServerCodec())
+ .addLast(new ChunkedWriteHandler())
+ .addLast(new HttpObjectAggregator(50 * 1024 * 1024))
+ .addLast(new WebSocketServerProtocolHandler("/", null, true, 50 * 1024 * 1024))
+ .addLast(new ServerHandler()); // A new handler for each client
+ }
+ });
+
+ ChannelFuture f = bootstrap.bind().sync();
+ log.info("Server started on port {}", PORT);
+ dashboard.updateStatus("Running", Color.GREEN);
+ dashboard.addLog("Server started successfully on port " + PORT);
+
+ f.channel().closeFuture().sync();
+ } catch (Exception e) {
+ dashboard.updateStatus("Error", Color.RED);
+ dashboard.addLog("Server error: " + e.getMessage());
+ log.error("Server crashed", e);
+ } finally {
+ dashboard.updateStatus("Shutting down", Color.ORANGE);
+ dashboard.addLog("Server shutting down...");
+ parentGroup.shutdownGracefully();
+ childGroup.shutdownGracefully();
+ if (dataSource != null) {
+ dataSource.close(); // Close the connection pool on shutdown
+ log.info("Database connection pool closed.");
+ }
+ }
+ }
+
+ // --- Google Drive and other helper methods ---
+
private static void initializeGoogleDriveService() {
if (!googleDriveEnabled) {
log.info("Google Drive integration is disabled");
@@ -139,7 +181,6 @@ public class Server {
googleDriveService = new GoogleDriveServiceOAuth(googleDriveRootFolderName);
log.info("✅ Google Drive OAuth service initialized successfully");
- // Update dashboard if it exists
if (ServerDashboard.getInstance() != null) {
ServerDashboard.getInstance().updateGoogleDriveStatus(true, "Connected");
}
@@ -147,48 +188,31 @@ public class Server {
} catch (Exception e) {
log.error("❌ Failed to initialize Google Drive OAuth service: {}", e.getMessage());
log.info("💡 Server will continue without Google Drive integration");
- log.info("🔧 To fix this, ensure google-oauth-credentials.json is properly configured");
googleDriveService = null;
- // Update dashboard if it exists
if (ServerDashboard.getInstance() != null) {
ServerDashboard.getInstance().updateGoogleDriveStatus(false, "Failed: " + e.getMessage());
}
}
}
- /**
- * Manually retry Google Drive connection
- */
public static void retryGoogleDriveConnection() {
log.info("🔄 Retrying Google Drive connection...");
initializeGoogleDriveService();
}
- /**
- * Check if Google Drive is connected
- */
public static boolean isGoogleDriveConnected() {
return googleDriveService != null;
}
- /**
- * Get Google Drive service instance
- */
public static GoogleDriveServiceOAuth getGoogleDriveService() {
return googleDriveService;
}
- /**
- * Get the current Google Drive root folder name
- */
public static String getGoogleDriveRootFolderName() {
return googleDriveRootFolderName;
}
- /**
- * Update Google Drive root folder name and reinitialize service
- */
public static void updateGoogleDriveRootFolder(String newRootFolderName) throws Exception {
if (newRootFolderName == null || newRootFolderName.trim().isEmpty()) {
throw new IllegalArgumentException("Root folder name cannot be empty");
@@ -197,23 +221,17 @@ public class Server {
googleDriveRootFolderName = newRootFolderName.trim();
log.info("🔄 Updating Google Drive root folder to: {}", googleDriveRootFolderName);
- // Save to configuration file
saveGoogleDriveRootFolderToConfig(googleDriveRootFolderName);
- // Reinitialize Google Drive service with new root folder
if (googleDriveEnabled) {
initializeGoogleDriveService();
}
}
- /**
- * Save Google Drive root folder name to config file
- */
private static void saveGoogleDriveRootFolderToConfig(String rootFolderName) {
try {
Properties props = new Properties();
- // Load existing properties
File externalConfig = new File("config/config.properties");
if (externalConfig.exists()) {
try (var input = Files.newInputStream(externalConfig.toPath())) {
@@ -227,16 +245,13 @@ public class Server {
}
}
- // Update the root folder property
props.setProperty("google.drive.root.folder", rootFolderName);
- // Create config directory if it doesn't exist
File configDir = new File("config");
if (!configDir.exists()) {
configDir.mkdirs();
}
- // Save to external config file
try (var output = Files.newOutputStream(externalConfig.toPath())) {
props.store(output, "BSK Server Configuration - Updated by Dashboard");
log.info("💾 Saved Google Drive root folder setting to config file");
@@ -247,96 +262,6 @@ public class Server {
}
}
- private static String extractDatabaseFile() throws IOException {
- throw new UnsupportedOperationException("extractDatabaseFile is not used anymore. Database is read directly from the specified path.");
- }
-
- public static Statement statement;
-
- static {
- try {
- statement = connection.createStatement();
- } catch (SQLException e) {
- throw new RuntimeException(e);
- }
- }
-
- public Server() throws SQLException {
- }
-
- public static void main(String[] args) throws InterruptedException, SQLException {
- log.info("\n____ _____ _ __\n| _ \\ / ____| |/\n| |_) | (___ | ' /\n| _ < \\___ \\| <\n| |_) |____) | . \\\n|____/|_____/|_|\\_\n");
- // Initialize and show server dashboard
- ServerDashboard dashboard = ServerDashboard.getInstance();
- dashboard.setVisible(true);
- dashboard.updateStatus("Starting...", Color.ORANGE);
- dashboard.updatePort(PORT);
-
- // Update Google Drive status on dashboard
- dashboard.updateGoogleDriveStatus(isGoogleDriveConnected(),
- isGoogleDriveConnected() ? "Connected" : "Disconnected");
-
- EventLoopGroup parentGroup =
- Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
- EventLoopGroup childGroup =
- Epoll.isAvailable() ? new EpollEventLoopGroup() : new NioEventLoopGroup();
- try {
- ServerBootstrap bootstrap =
- new ServerBootstrap()
- .group(parentGroup, childGroup)
- .channel(
- Epoll.isAvailable()
- ? EpollServerSocketChannel.class
- : NioServerSocketChannel.class)
- .localAddress(new InetSocketAddress(PORT))
- .childHandler(
- new ChannelInitializer() {
- @Override
- protected void initChannel(SocketChannel ch) throws Exception {
- ch.pipeline()
- .addLast(new IdleStateHandler(60 * 30, 0, 0, TimeUnit.SECONDS))
- .addLast(new HttpServerCodec())
- .addLast(new ChunkedWriteHandler())
- .addLast(new HttpObjectAggregator(50 * 1024 * 1024))
- .addLast(new WebSocketServerProtocolHandler("/", null, true, 50 * 1024 * 1024))
- .addLast(new ServerHandler());
- }
- });
-
- ChannelFuture f = bootstrap.bind().sync();
-
- log.info("Server started on port {}", PORT);
- log.info("To connect, clients should use:");
- log.info(" - If on same machine: ws://localhost:{} or ws://127.0.0.1:{}", PORT, PORT);
- log.info(" - If on network: ws://:{}", PORT);
-
- // Update dashboard status to running
- dashboard.updateStatus("Running", Color.GREEN);
- dashboard.addLog("Server started successfully on port " + PORT);
- if (isGoogleDriveConnected()) {
- dashboard.addLog("Google Drive integration: Connected ✅");
- } else {
- dashboard.addLog("Google Drive integration: Disconnected ❌");
- }
-
- f.channel().closeFuture().sync();
- } catch (Exception e) {
- dashboard.updateStatus("Error", Color.RED);
- dashboard.addLog("Server error: " + e.getMessage());
- throw e;
- } finally {
- dashboard.updateStatus("Shutting down", Color.ORANGE);
- dashboard.addLog("Server shutting down...");
- parentGroup.shutdownGracefully();
- childGroup.shutdownGracefully();
- }
- }
-
- /**
- * Initiates the backup of the main database file to Google Drive.
- * This is intended to be called from the ServerDashboard.
- * @throws IOException if the file cannot be found or the upload fails.
- */
public static void backupDatabaseToDrive() throws IOException {
ServerDashboard dashboard = ServerDashboard.getInstance();
@@ -345,7 +270,6 @@ public class Server {
throw new IOException("Google Drive service is not available.");
}
- // Define the path to the database
String dbPath = "src/main/resources/database/BSK.db";
java.io.File dbFile = new java.io.File(dbPath);
@@ -354,7 +278,6 @@ public class Server {
throw new IOException("Database file not found: " + dbPath);
}
- // Call the service to perform the backup
googleDriveService.backupDatabaseFile(dbFile);
- }
+ }
}
diff --git a/src/main/java/BsK/server/network/handler/ServerHandler.java b/src/main/java/BsK/server/network/handler/ServerHandler.java
index a716914..604466b 100644
--- a/src/main/java/BsK/server/network/handler/ServerHandler.java
+++ b/src/main/java/BsK/server/network/handler/ServerHandler.java
@@ -116,7 +116,8 @@ 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"
);
if (!rs.isBeforeFirst()) {
@@ -310,7 +311,7 @@ public class ServerHandler extends SimpleChannelInboundHandler medList = new ArrayList<>();
while(medRs.next()) {
- String[] med = new String[11];
+ String[] med = new String[12];
med[0] = medRs.getString("med_id");
med[1] = medRs.getString("med_name");
med[2] = medRs.getString("quantity_ordered");
@@ -933,6 +942,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"
);
String[][] resultArray;
diff --git a/src/main/resources/assets/logo.jpg b/src/main/resources/assets/logo.jpg
deleted file mode 100644
index c98dcc4..0000000
Binary files a/src/main/resources/assets/logo.jpg and /dev/null differ
diff --git a/src/main/resources/database/BSK.db b/src/main/resources/database/BSK.db
index 05f1b4c..7f4b404 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/reports/medserinvoice.jrxml b/src/main/resources/reports/medserinvoice.jrxml
deleted file mode 100644
index 861a71b..0000000
Binary files a/src/main/resources/reports/medserinvoice.jrxml and /dev/null differ
diff --git a/src/main/resources/reports/ultrasoundresult.jrxml b/src/main/resources/reports/ultrasoundresult.jrxml
deleted file mode 100644
index b581bb4..0000000
--- a/src/main/resources/reports/ultrasoundresult.jrxml
+++ /dev/null
@@ -1,270 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/resources/reports/ultrasoundresult_potrait.jrxml b/src/main/resources/reports/ultrasoundresult_potrait.jrxml
deleted file mode 100644
index f6082c0..0000000
--- a/src/main/resources/reports/ultrasoundresult_potrait.jrxml
+++ /dev/null
@@ -1,198 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-