diff --git a/README b/README new file mode 100644 index 0000000000000000000000000000000000000000..a4ae1350ecea644bad730240b9df247dfaf493fa --- /dev/null +++ b/README @@ -0,0 +1,15 @@ +database name:healthcare +username:root +password:comsc +port:3306 + +The database can be created automatically when the project is started. If no, manually run schema.sql and data.sql in the resource directory. +SuperAdmin: + name:2649783657@qq.com + password:admin +hospital: + name:hospital2@qq.com + password:hospital2 +commonAdmin: + name:123cmy@qq.com + password:123 \ No newline at end of file diff --git a/src/main/java/com/cardiff/client_project/controller/admin/CommonAdminHospitalController.java b/src/main/java/com/cardiff/client_project/controller/admin/CommonAdminHospitalController.java index fa508744974e4506fd77f441badc79d5c4aba29c..2ecb253364419d949e37d3b221ed1ecf72c3d045 100644 --- a/src/main/java/com/cardiff/client_project/controller/admin/CommonAdminHospitalController.java +++ b/src/main/java/com/cardiff/client_project/controller/admin/CommonAdminHospitalController.java @@ -54,6 +54,7 @@ public class CommonAdminHospitalController { public ResponseEntity<String> deleteHospital(@PathVariable int id) { try { commonAdminHospitalService.deleteHospital(id); + commonAdminHospitalService.resetHospitalIds(); // Reset IDs after deletion return ResponseEntity.ok("Hospital deleted successfully!"); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) @@ -61,6 +62,7 @@ public class CommonAdminHospitalController { } } + @GetMapping("/nurses") public List<NurseVO> getNurses(@RequestParam(value = "name", required = false) String name, @RequestParam(value = "hospitalId", required = false) Integer hospitalId) { @@ -70,4 +72,16 @@ public class CommonAdminHospitalController { return commonAdminHospitalService.searchNurses(name, hospitalId); } + @GetMapping("/{id}") + public ResponseEntity<HospitalDTO> getHospitalById(@PathVariable int id) { + HospitalDTO hospital = commonAdminHospitalService.getHospitalById(id); + if (hospital != null) { + return ResponseEntity.ok(hospital); + } else { + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body(null); + } + } + + } diff --git a/src/main/java/com/cardiff/client_project/controller/nursinghome/NursingHomeController.java b/src/main/java/com/cardiff/client_project/controller/nursinghome/NursingHomeController.java index e4bada752481ab1b2afe44ff1d745d0bb65427e0..59d82833cf7a33ea190edba4285072452340d158 100644 --- a/src/main/java/com/cardiff/client_project/controller/nursinghome/NursingHomeController.java +++ b/src/main/java/com/cardiff/client_project/controller/nursinghome/NursingHomeController.java @@ -3,18 +3,24 @@ package com.cardiff.client_project.controller.nursinghome; import com.cardiff.client_project.pojo.dto.HospitalDTO; import com.cardiff.client_project.service.NursingHomeService; import com.cardiff.client_project.utils.Result; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; +@Slf4j @RestController @RequestMapping("/api/hospitals") @CrossOrigin(origins = "*") public class NursingHomeController { + private final NursingHomeService nursingHomeService; + @Autowired - private NursingHomeService nursingHomeService; + public NursingHomeController(NursingHomeService nursingHomeService) { + this.nursingHomeService = nursingHomeService; + } // READ - Get available beds @GetMapping("/available") @@ -58,4 +64,41 @@ public class NursingHomeController { return Result.error("Error deleting hospital: " + e.getMessage()); } } + + @GetMapping("/search") + public Result<List<HospitalDTO>> searchHospitals(@RequestParam(required = false) String name) { + try { + if (name == null || name.trim().isEmpty()) { + return nursingHomeService.getAvailableBeds(); + } + return nursingHomeService.searchHospitals(name.trim()); + } catch (Exception e) { + log.error("Search error: ", e); + return Result.error("Search failed: " + e.getMessage()); + } + } + + @PutMapping("/{id}/approval") + public Result<String> updateApprovalStatus( + @PathVariable Integer id, + @RequestParam String status) { + try { + log.info("Updating approval status for hospital {}: {}", id, status); + return nursingHomeService.updateApprovalStatus(id, status); + } catch (Exception e) { + log.error("Error updating approval status: ", e); + return Result.error("Failed to update approval status"); + } + } + + @GetMapping("/pending") + public Result<List<HospitalDTO>> getPendingApprovals() { + try { + log.info("Fetching pending approvals"); + return nursingHomeService.getPendingApprovals(); + } catch (Exception e) { + log.error("Error fetching pending approvals: ", e); + return Result.error("Failed to fetch pending approvals"); + } + } } diff --git a/src/main/java/com/cardiff/client_project/mapper/CommonAdminHospitalMapper.java b/src/main/java/com/cardiff/client_project/mapper/CommonAdminHospitalMapper.java index eecf39fc96f45a64b2c82731548776e9b199308f..11b40123cee8a995bed7897f7fb0edc528bc0cd1 100644 --- a/src/main/java/com/cardiff/client_project/mapper/CommonAdminHospitalMapper.java +++ b/src/main/java/com/cardiff/client_project/mapper/CommonAdminHospitalMapper.java @@ -15,8 +15,7 @@ public class CommonAdminHospitalMapper { @Autowired JdbcTemplate jdbcTemplate; - - // æ›´æ–°åŒ»é™¢ä¿¡æ¯ + public int update(HospitalDTO hospital) { String sql = "UPDATE hospital SET name=?, address=?, phone=?, totalBeds=?, availableBeds=?, occupancyRate=? WHERE id=?"; return jdbcTemplate.update(sql, hospital.getName(), hospital.getAddress(), hospital.getPhone(), diff --git a/src/main/java/com/cardiff/client_project/mapper/NursingHomeMapper.java b/src/main/java/com/cardiff/client_project/mapper/NursingHomeMapper.java index 5303ec126a1456d87b69305b6951586a1dfdf31d..2be6c149b53006c258e2d5f941f06d5ea42da7c0 100644 --- a/src/main/java/com/cardiff/client_project/mapper/NursingHomeMapper.java +++ b/src/main/java/com/cardiff/client_project/mapper/NursingHomeMapper.java @@ -5,6 +5,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.List; @Repository @@ -13,33 +15,64 @@ public class NursingHomeMapper { @Autowired private JdbcTemplate jdbcTemplate; + public List<HospitalDTO> searchHospitals(String name) { + String sql = """ + SELECT + id, name, address, phone, maxAmount, + amountPatient, status, approval_status + FROM hospital + WHERE status = 1 + AND name LIKE CONCAT('%', ?, '%') + ORDER BY name ASC + """; + + return jdbcTemplate.query(sql, + ps -> ps.setString(1, name), + this::mapToDTO); + } + public List<HospitalDTO> findAllNursingHomes() { String sql = """ SELECT - id, - name, - address, - phone, - maxAmount, - amountPatient, - status + id, name, address, phone, maxAmount, + amountPatient, status, approval_status FROM hospital WHERE status = 1 """; - return jdbcTemplate.query(sql, (rs, rowNum) -> { - HospitalDTO dto = new HospitalDTO(); - dto.setId(rs.getInt("id")); - dto.setName(rs.getString("name")); - dto.setLocation(rs.getString("address")); - dto.setPhone(rs.getString("phone")); - dto.setTotalBeds(rs.getInt("maxAmount")); - int maxAmount = rs.getInt("maxAmount"); - int amountPatient = rs.getInt("amountPatient"); - dto.setAvailableBeds(maxAmount - amountPatient); - dto.setOccupancyRate(calculateOccupancy(maxAmount, amountPatient)); - return dto; - }); + return jdbcTemplate.query(sql, this::mapToDTO); + } + + public List<HospitalDTO> getPendingApprovals() { + String sql = """ + SELECT + id, name, address, phone, maxAmount, + amountPatient, status, approval_status + FROM hospital + WHERE approval_status = 'PENDING' + """; + + return jdbcTemplate.query(sql, this::mapToDTO); + } + + public int updateApprovalStatus(int id, String status) { + String sql = "UPDATE hospital SET approval_status = ? WHERE id = ?"; + return jdbcTemplate.update(sql, status, id); + } + + private HospitalDTO mapToDTO(ResultSet rs, int rowNum) throws SQLException { + HospitalDTO dto = new HospitalDTO(); + dto.setId(rs.getInt("id")); + dto.setName(rs.getString("name")); + dto.setLocation(rs.getString("address")); + dto.setPhone(rs.getString("phone")); + int maxAmount = rs.getInt("maxAmount"); + int amountPatient = rs.getInt("amountPatient"); + dto.setTotalBeds(maxAmount); + dto.setAvailableBeds(maxAmount - amountPatient); + dto.setOccupancyRate(calculateOccupancy(maxAmount, amountPatient)); + dto.setApprovalStatus(rs.getString("approval_status")); + return dto; } private double calculateOccupancy(int maxAmount, int amountPatient) { @@ -49,15 +82,10 @@ public class NursingHomeMapper { public int insertHospital(HospitalDTO dto) { String sql = """ INSERT INTO hospital ( - name, - address, - phone, - maxAmount, - amountPatient, - status, - type, - roleId - ) VALUES (?, ?, ?, ?, ?, 1, 'hospital', 3) + name, address, phone, maxAmount, + amountPatient, status, type, roleId, + approval_status + ) VALUES (?, ?, ?, ?, ?, 1, 'hospital', 3, 'PENDING') """; return jdbcTemplate.update(sql, diff --git a/src/main/java/com/cardiff/client_project/pojo/dto/HospitalDTO.java b/src/main/java/com/cardiff/client_project/pojo/dto/HospitalDTO.java index 0bc6896473690451937cacd5b0b4fd12b2147005..9bb0df21c749649cfa0b64a4ff9029b36f93d257 100644 --- a/src/main/java/com/cardiff/client_project/pojo/dto/HospitalDTO.java +++ b/src/main/java/com/cardiff/client_project/pojo/dto/HospitalDTO.java @@ -12,4 +12,5 @@ public class HospitalDTO { private int totalBeds; private int availableBeds; private double occupancyRate; + private String approvalStatus; } \ No newline at end of file diff --git a/src/main/java/com/cardiff/client_project/pojo/vo/HospitalVO.java b/src/main/java/com/cardiff/client_project/pojo/vo/HospitalVO.java index 3e673b8b8a28227dd626cf424453391420edeb9d..a12f13c974e19c257bbf296a4dba4a7c15787528 100644 --- a/src/main/java/com/cardiff/client_project/pojo/vo/HospitalVO.java +++ b/src/main/java/com/cardiff/client_project/pojo/vo/HospitalVO.java @@ -15,4 +15,6 @@ public class HospitalVO { private String status_str; private int maxAmount; private int amountPatient; + private String approvalStatus; + private String approvalStatus_str; } diff --git a/src/main/java/com/cardiff/client_project/service/CommonAdminHospitalService.java b/src/main/java/com/cardiff/client_project/service/CommonAdminHospitalService.java index 9bce53158e34312afcd7e585641a29a02cd5affc..1be393024defafc26c690141c86f493cb4ab554c 100644 --- a/src/main/java/com/cardiff/client_project/service/CommonAdminHospitalService.java +++ b/src/main/java/com/cardiff/client_project/service/CommonAdminHospitalService.java @@ -18,8 +18,15 @@ public interface CommonAdminHospitalService { void updateHospital(HospitalDTO hospital); + void editHospital(HospitalDTO hospital); + void deleteHospital(int id); + HospitalDTO getHospitalById(int id); + + + void resetHospitalIds(); + List<NurseVO> getAllNurses(); List<NurseVO> searchNurses(String name, Integer hospitalId); diff --git a/src/main/java/com/cardiff/client_project/service/NursingHomeService.java b/src/main/java/com/cardiff/client_project/service/NursingHomeService.java index c0e0026a9c7de0f5a59ab84e562175c620ef4ccf..f31c3ba660315d3d190f80f5e5d2fde364a84e9c 100644 --- a/src/main/java/com/cardiff/client_project/service/NursingHomeService.java +++ b/src/main/java/com/cardiff/client_project/service/NursingHomeService.java @@ -11,4 +11,7 @@ public interface NursingHomeService { Result<String> deletePatientById(List<Integer> ids); Result<List<HospitalDTO>> getAllPatients(); Result<String> updateBedCount(int hospitalId, int currentPatients); + Result<List<HospitalDTO>> searchHospitals(String name); + Result<String> updateApprovalStatus(Integer hospitalId, String status); + Result<List<HospitalDTO>> getPendingApprovals(); } \ No newline at end of file diff --git a/src/main/java/com/cardiff/client_project/service/imp/CommonAdminHospitalServiceImp.java b/src/main/java/com/cardiff/client_project/service/imp/CommonAdminHospitalServiceImp.java index e91fbeb29cdc0baa550a7eff82ef5fc15a233ad2..938c24802bb2eb66699f19b9a5c5acf2a5eda4f9 100644 --- a/src/main/java/com/cardiff/client_project/service/imp/CommonAdminHospitalServiceImp.java +++ b/src/main/java/com/cardiff/client_project/service/imp/CommonAdminHospitalServiceImp.java @@ -52,8 +52,13 @@ public class CommonAdminHospitalServiceImp implements CommonAdminHospitalService double occupancyRate = 1.0 - (double) hospital.getAvailableBeds() / hospital.getTotalBeds(); String sql = "INSERT INTO hospital (name, address, phone, maxAmount, amountPatient) VALUES (?, ?, ?, ?, ?)"; - jdbcTemplate.update(sql, hospital.getName(), hospital.getAddress(), hospital.getPhone(), - hospital.getTotalBeds(), hospital.getAvailableBeds()); + jdbcTemplate.update(sql, + hospital.getName(), + hospital.getAddress(), + hospital.getPhone(), + hospital.getTotalBeds(), + hospital.getAvailableBeds() + ); } @Override @@ -63,26 +68,59 @@ public class CommonAdminHospitalServiceImp implements CommonAdminHospitalService @Override public void updateHospital(HospitalDTO hospital) { + + } + + @Override + public void editHospital(HospitalDTO hospital) { if (hospital.getId() <= 0 || hospital.getName() == null || hospital.getAddress() == null || hospital.getPhone() == null || hospital.getTotalBeds() <= 0 || hospital.getAvailableBeds() < 0) { throw new IllegalArgumentException("Invalid hospital data: Missing or incorrect fields."); } - String sql = "UPDATE hospital SET name = ?, address = ?, phone = ?, maxAmount = ?, amountPatient = ? WHERE id = ?"; + // æ›´æ–°å 用率计算(å¯é€‰ï¼Œå…·ä½“ä¾é¡¹ç›®éœ€æ±‚) double occupancyRate = 1.0 - (double) hospital.getAvailableBeds() / hospital.getTotalBeds(); - int rowsUpdated = jdbcTemplate.update(sql, hospital.getName(), hospital.getAddress(), hospital.getPhone(), - hospital.getTotalBeds(), hospital.getAvailableBeds(), occupancyRate, hospital.getId()); - - if (rowsUpdated == 0) { - throw new IllegalArgumentException("No hospital found with ID " + hospital.getId()); - } + // SQL è¯å¥æ›´æ–°åŒ»é™¢æ•°æ® + String sql = "UPDATE hospital SET name = ?, address = ?, phone = ?, maxAmount = ?, amountPatient = ? WHERE id = ?"; + jdbcTemplate.update(sql, hospital.getName(), hospital.getAddress(), hospital.getPhone(), + hospital.getTotalBeds(), hospital.getAvailableBeds(), hospital.getId()); } @Override public void deleteHospital(int id) { - String sql = "DELETE FROM hospital WHERE id = ?"; - jdbcTemplate.update(sql, id); + String deleteSql = "DELETE FROM hospital WHERE id = ?"; + jdbcTemplate.update(deleteSql, id); + } + + @Override + public HospitalDTO getHospitalById(int id) { + String sql = "SELECT * FROM hospital WHERE id = ?"; + List<HospitalDTO> hospitals = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(HospitalDTO.class), id); + return hospitals.isEmpty() ? null : hospitals.get(0); + } + + + @Override + public void resetHospitalIds() { + try { + // Step 1: 创建一个å˜é‡ç”¨äºŽæ›´æ–°è¡Œå· + String setRowNumber = "SET @row_number = 0;"; + jdbcTemplate.execute(setRowNumber); + + // Step 2: 釿–°æ›´æ–°è¡¨çš„ ID é¡ºåº + String updateIds = "UPDATE hospital SET id = (@row_number := @row_number + 1);"; + jdbcTemplate.execute(updateIds); + + // Step 3: é‡ç½® AUTO_INCREMENT + String resetAutoIncrement = "ALTER TABLE hospital AUTO_INCREMENT = 1;"; + jdbcTemplate.execute(resetAutoIncrement); + + System.out.println("Hospital IDs reset successfully."); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Failed to reset hospital IDs: " + e.getMessage()); + } } diff --git a/src/main/java/com/cardiff/client_project/service/imp/NursingHomeServiceImp.java b/src/main/java/com/cardiff/client_project/service/imp/NursingHomeServiceImp.java index 8a3a75e785a585e9343c2138014df48eada6ea0a..6b71aae30bd9d9430a475e3ce4ad8a048a20d3f3 100644 --- a/src/main/java/com/cardiff/client_project/service/imp/NursingHomeServiceImp.java +++ b/src/main/java/com/cardiff/client_project/service/imp/NursingHomeServiceImp.java @@ -7,45 +7,66 @@ import com.cardiff.client_project.utils.Result; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.dao.DataAccessException; +import org.springframework.transaction.annotation.Transactional; +import lombok.extern.slf4j.Slf4j; import java.util.List; +import java.util.stream.Collectors; @Service +@Slf4j +@Transactional public class NursingHomeServiceImp implements NursingHomeService { + private static final String STATUS_PENDING = "PENDING"; + private static final String STATUS_APPROVED = "APPROVED"; + private static final String STATUS_REJECTED = "REJECTED"; + + private final NursingHomeMapper nursingHomeMapper; + @Autowired - private NursingHomeMapper nursingHomeMapper; + public NursingHomeServiceImp(NursingHomeMapper nursingHomeMapper) { + this.nursingHomeMapper = nursingHomeMapper; + } @Override + @Transactional(readOnly = true) public Result<List<HospitalDTO>> getAvailableBeds() { try { List<HospitalDTO> hospitals = nursingHomeMapper.findAllNursingHomes(); - if (hospitals == null || hospitals.isEmpty()) { - return Result.error("No hospitals available"); + hospitals = hospitals.stream() + .filter(h -> STATUS_APPROVED.equals(h.getApprovalStatus())) + .collect(Collectors.toList()); + + if (hospitals.isEmpty()) { + return Result.error("No approved hospitals available"); } return Result.success(hospitals); } catch (DataAccessException e) { return Result.error("Database error: " + e.getMessage()); } catch (Exception e) { - return Result.error("Internal server error"); + log.error("Error fetching available beds: {}", e.getMessage(), e); + return Result.error("Failed to fetch available beds"); } } @Override + @Transactional public Result<HospitalDTO> insertPatientInfo(HospitalDTO hospitalDTO) { try { if (!validateHospitalData(hospitalDTO)) { return Result.error("Invalid hospital data"); } + hospitalDTO.setApprovalStatus(STATUS_PENDING); int result = nursingHomeMapper.insertHospital(hospitalDTO); if (result > 0) { + log.info("Successfully inserted hospital: {}", hospitalDTO.getName()); return Result.success(hospitalDTO); } return Result.error("Failed to insert hospital"); } catch (DataAccessException e) { - return Result.error("Database error: " + e.getMessage()); - } catch (Exception e) { - return Result.error("Internal server error"); + log.error("Database error while inserting hospital: {}", e.getMessage(), e); + return Result.error("Database error occurred"); } } @@ -96,6 +117,80 @@ public class NursingHomeServiceImp implements NursingHomeService { } } + @Override + @Transactional(readOnly = true) + public Result<List<HospitalDTO>> searchHospitals(String name) { + try { + if (name == null || name.trim().isEmpty()) { + return Result.error("Search term cannot be empty"); + } + List<HospitalDTO> hospitals = nursingHomeMapper.searchHospitals(name); + return hospitals.isEmpty() ? + Result.error("No hospitals found") : + Result.success(hospitals); + } catch (Exception e) { + log.error("Search error: ", e); + return Result.error("Search failed"); + } + } + + @Override + @Transactional + public Result<String> updateApprovalStatus(Integer hospitalId, String status) { + try { + log.info("Updating approval status for hospital {}: {}", hospitalId, status); + + if (hospitalId == null || hospitalId <= 0) { + log.warn("Invalid hospital ID: {}", hospitalId); + return Result.error("Invalid hospital ID"); + } + + if (!isValidApprovalStatus(status)) { + log.warn("Invalid approval status: {}", status); + return Result.error("Invalid approval status"); + } + + int result = nursingHomeMapper.updateApprovalStatus(hospitalId, status.toUpperCase()); + if (result > 0) { + log.info("Successfully updated approval status for hospital {}", hospitalId); + return Result.success("Approval status updated successfully"); + } + + log.warn("Hospital not found with ID: {}", hospitalId); + return Result.error("Hospital not found"); + + } catch (DataAccessException e) { + log.error("Database error while updating approval status: {}", e.getMessage(), e); + return Result.error("Database error occurred"); + } catch (Exception e) { + log.error("Unexpected error while updating approval status: {}", e.getMessage(), e); + return Result.error("Failed to update approval status"); + } + } + + @Override + @Transactional(readOnly = true) + public Result<List<HospitalDTO>> getPendingApprovals() { + try { + log.info("Fetching pending approval hospitals"); + List<HospitalDTO> pendingHospitals = nursingHomeMapper.getPendingApprovals(); + + if (pendingHospitals.isEmpty()) { + log.info("No pending approvals found"); + return Result.error("No pending approvals"); + } + + log.info("Found {} pending approvals", pendingHospitals.size()); + return Result.success(pendingHospitals); + } catch (DataAccessException e) { + log.error("Database error while fetching pending approvals: {}", e.getMessage(), e); + return Result.error("Database error occurred"); + } catch (Exception e) { + log.error("Unexpected error while fetching pending approvals: {}", e.getMessage(), e); + return Result.error("Failed to fetch pending approvals"); + } + } + private boolean validateHospitalData(HospitalDTO hospitalDTO) { if (hospitalDTO == null) return false; return hospitalDTO.getName() != null && @@ -108,4 +203,17 @@ public class NursingHomeServiceImp implements NursingHomeService { hospitalDTO.getAvailableBeds() >= 0 && hospitalDTO.getAvailableBeds() <= hospitalDTO.getTotalBeds(); } + + private boolean isValidStatus(String status) { + return status != null && + (status.equals("APPROVED") || + status.equals("REJECTED") || + status.equals("PENDING")); + } + + private boolean isValidApprovalStatus(String status) { + if (status == null) return false; + return List.of(STATUS_PENDING, STATUS_APPROVED, STATUS_REJECTED) + .contains(status.toUpperCase()); + } } \ No newline at end of file diff --git a/src/main/resources/healthcare.sql b/src/main/resources/healthcare.sql new file mode 100644 index 0000000000000000000000000000000000000000..382870c98e718f1d77cdff8cc502e46e04604ae0 --- /dev/null +++ b/src/main/resources/healthcare.sql @@ -0,0 +1,227 @@ +CREATE DATABASE IF NOT EXISTS healthcare; + +/* 切æ¢åˆ° healthcare æ•°æ®åº“*/ +USE healthcare; + +/* + Navicat Premium Data Transfer + + Source Server : mariaDB + Source Server Type : MariaDB + Source Server Version : 101110 + Source Host : localhost:3307 + Source Schema : healthcare + + Target Server Type : MariaDB + Target Server Version : 101110 + File Encoding : 65001 + + Date: 16/02/2025 22:16:39 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for care_homes +-- ---------------------------- +DROP TABLE IF EXISTS `care_homes`; +CREATE TABLE `care_homes` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `address` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `status` enum('approved','rejected','pending') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'pending', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of care_homes +-- ---------------------------- + +-- ---------------------------- +-- Table structure for common_admin +-- ---------------------------- +DROP TABLE IF EXISTS `common_admin`; +CREATE TABLE `common_admin` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `email` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `password` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `type` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `roleId` int(11) NULL DEFAULT NULL, + `phone` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `status` int(11) NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of common_admin +-- ---------------------------- +INSERT INTO `common_admin` VALUES (1, '26497@qq.com', '$2a$10$5UzuKUxgR8H0RzLYrP.BUu8.76yKwMD4XkvJIu/xxB7AAHWfphFQm', 'commonAdmin', 1, '111', 'justnoww', 1); +INSERT INTO `common_admin` VALUES (2, '1234@qq.com', '$2a$10$iW/zeL9TLq55SHFaOfyfr.xFMvB4aD3CL2layeAlGC7CqZ9kcscA.', 'commonAdmin', 1, '123', 'BAa', 1); +INSERT INTO `common_admin` VALUES (3, '123cmy@qq.com', '$2a$10$u2KxNBaHuTVwX7KudUgyNelp8j2lymarXKkkUXfWBMRAgbPNryTuW', 'commonAdmin', 1, '123', 'cmy', 1); + +-- ---------------------------- +-- Table structure for device +-- ---------------------------- +DROP TABLE IF EXISTS `device`; +CREATE TABLE `device` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `hospitalId` int(11) NULL DEFAULT NULL, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `type` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `freeTime` datetime NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of device +-- ---------------------------- +INSERT INTO `device` VALUES (1, 0, 'bed1', 'bed', '2024-12-02 09:51:12'); +INSERT INTO `device` VALUES (2, 2, 'bed2', 'bed', '2024-12-03 09:30:00'); +INSERT INTO `device` VALUES (3, 2, 'bed3', 'bed', '2025-12-05 10:30:35'); +INSERT INTO `device` VALUES (4, 2, 'instrument1', 'instrument', '2024-12-05 07:15:59'); +INSERT INTO `device` VALUES (5, 2, 'instrument5', 'instrument', '2024-12-05 10:30:00'); + +-- ---------------------------- +-- Table structure for hospital +-- ---------------------------- +DROP TABLE IF EXISTS `hospital`; +CREATE TABLE `hospital` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `amountPatient` int(11) NULL DEFAULT NULL, + `maxAmount` int(11) NULL DEFAULT NULL, + `type` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `address` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `phone` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `status` int(11) NULL DEFAULT NULL, + `roleId` int(11) NULL DEFAULT NULL, + `email` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `password` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `patientId` int(11) NULL DEFAULT NULL, + `deviceId` int(11) NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of hospital +-- ---------------------------- +INSERT INTO `hospital` VALUES (1, 'hospital1', 5, 200, 'hospital', 'beijing', '6371', 1, 3, 'hospital1@qq.com', '$2a$10$wvAZoj4V51MH/MLhVIrnG.NrY07/.Gn9Ar6JsyzRAubWtqbWzgKie', 0, 0); +INSERT INTO `hospital` VALUES (2, 'hospital2', 10, 50, 'hospital', 'cardiff', '123', 1, 3, 'hospital2@qq.com', '$2a$10$wvAZoj4V51MH/MLhVIrnG.NrY07/.Gn9Ar6JsyzRAubWtqbWzgKie', 0, 0); +INSERT INTO `hospital` VALUES (3, 'hospital3', 50, 100, NULL, 'cardiff', '123', NULL, NULL, NULL, NULL, NULL, NULL); + +-- ---------------------------- +-- Table structure for hospital_device +-- ---------------------------- +DROP TABLE IF EXISTS `hospital_device`; +CREATE TABLE `hospital_device` ( + `hospitalId` int(11) NULL DEFAULT NULL, + `deviceId` int(11) NULL DEFAULT NULL +) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of hospital_device +-- ---------------------------- + +-- ---------------------------- +-- Table structure for nurse +-- ---------------------------- +DROP TABLE IF EXISTS `nurse`; +CREATE TABLE `nurse` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `status` int(11) NULL DEFAULT NULL, + `hospitalId` int(11) NULL DEFAULT NULL, + `type` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `age` int(11) NULL DEFAULT NULL, + `phone` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `email` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `address` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of nurse +-- ---------------------------- +INSERT INTO `nurse` VALUES (1, 'nn', 0, 2, 'nurse', 23, '111', '23453', 'Cardiff'); +INSERT INTO `nurse` VALUES (2, 'Li Xiaoming', 1, 2, 'nurse', 25, '222', '4399', 'Cardiff'); +INSERT INTO `nurse` VALUES (3, 'Sun Hao', 1, 2, 'nurse', 24, '123', '23576', 'Bute'); +INSERT INTO `nurse` VALUES (4, 'Wang Xiaoming', 0, 2, 'nurse', 26, '333', '34578', 'Cardiff'); +INSERT INTO `nurse` VALUES (5, 'Tian Hao', 0, 2, 'nurse', 30, '321', '24353', 'Cardiff'); + +-- ---------------------------- +-- Table structure for patient +-- ---------------------------- +DROP TABLE IF EXISTS `patient`; +CREATE TABLE `patient` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `password` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `roleId` int(11) NULL DEFAULT NULL, + `status` int(11) NULL DEFAULT NULL, + `hospitalId` int(11) NULL DEFAULT NULL, + `type` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `age` int(11) NULL DEFAULT NULL, + `phone` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `email` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of patient +-- ---------------------------- +INSERT INTO `patient` VALUES (1, 'Li Ming', '$2a$10$obgmyiqAuhIkBjuw3bROr.LkCh.Nb70e4dL8lwKjSsPxS8gSpQxSC', 2, 1, 0, 'patient', 20, '135', 'wwgwg@outlook.com'); +INSERT INTO `patient` VALUES (2, 'Li Hua', '$2a$10$obgmyiqAuhIkBjuw3bROr.LkCh.Nb70e4dL8lwKjSsPxS8gSpQxSC', 2, 0, 0, 'patient', 21, '166', 'ss@qq.com'); +INSERT INTO `patient` VALUES (3, 'Huang Li', '$2a$10$obgmyiqAuhIkBjuw3bROr.LkCh.Nb70e4dL8lwKjSsPxS8gSpQxSC', 2, 0, 0, 'patient', 13, '111', '22@qq.com'); +INSERT INTO `patient` VALUES (4, 'Xia Ming', '$2a$10$obgmyiqAuhIkBjuw3bROr.LkCh.Nb70e4dL8lwKjSsPxS8gSpQxSC', 2, 1, 2, 'patient', 61, '15', '131@qq.com'); +INSERT INTO `patient` VALUES (5, 'Xia Yu', '$2a$10$obgmyiqAuhIkBjuw3bROr.LkCh.Nb70e4dL8lwKjSsPxS8gSpQxSC', 2, 1, 2, 'patient', 33, '14315', 'xx@qq.com'); + +-- ---------------------------- +-- Table structure for patient_hospital +-- ---------------------------- +DROP TABLE IF EXISTS `patient_hospital`; +CREATE TABLE `patient_hospital` ( + `hosptialId` int(11) NULL DEFAULT NULL, + `patientId` int(11) NULL DEFAULT NULL +) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of patient_hospital +-- ---------------------------- + +-- ---------------------------- +-- Table structure for role +-- ---------------------------- +DROP TABLE IF EXISTS `role`; +CREATE TABLE `role` ( + `roleId` int(11) NULL DEFAULT NULL, + `type` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL +) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of role +-- ---------------------------- +INSERT INTO `role` VALUES (0, 'SUPER'); +INSERT INTO `role` VALUES (1, 'ADMIN'); +INSERT INTO `role` VALUES (2, 'PATIENT'); +INSERT INTO `role` VALUES (3, 'HOSPITAL'); + +-- ---------------------------- +-- Table structure for super_admin +-- ---------------------------- +DROP TABLE IF EXISTS `super_admin`; +CREATE TABLE `super_admin` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, + `password` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, + `roleId` int(11) NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of super_admin +-- ---------------------------- +INSERT INTO `super_admin` VALUES (1, '2649783657@qq.com', '$2a$10$5UzuKUxgR8H0RzLYrP.BUu8.76yKwMD4XkvJIu/xxB7AAHWfphFQm', 0); + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/src/main/resources/static/css/admin.css b/src/main/resources/static/css/admin.css index b5acb2260ee53ca8d95a1e508b3eb82b2539ff2e..c24a4db3d2df05c6c52a38ca4f21d1efc9c0c039 100644 --- a/src/main/resources/static/css/admin.css +++ b/src/main/resources/static/css/admin.css @@ -4,39 +4,111 @@ body { padding: 0; background-color: #f4f4f4; } +/* Sidebar Styling */ .sidebar { position: fixed; top: 0; left: 0; - width: 250px; height: 100vh; - background-color: #1f2937; + width: 250px; + background: #1f2937; + padding: 1.5rem 1rem; color: white; - padding: 1rem; display: flex; flex-direction: column; - gap: 0.5rem; /* ä¸ºæŒ‰é’®ä¹‹é—´å¢žåŠ é—´è· */ + box-shadow: 4px 0 10px rgba(0, 0, 0, 0.1); + z-index: 1000; } -.sidebar .text-lg { + +/* Logo/Brand section */ +.sidebar-brand { + color: white; font-size: 1.25rem; - font-weight: bold; - margin-bottom: 1rem; + font-weight: 600; + padding: 1rem; + margin-bottom: 2rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.1); } -.sidebar a { - padding: 0.75rem 1rem; - margin-bottom: 0.5rem; - color: white; + + +.sidebar .nav-item { + display: flex; + align-items: center; + padding: 0.875rem 1rem; + color: rgba(255, 255, 255, 0.8); text-decoration: none; border-radius: 0.5rem; - transition: background-color 0.3s ease; + margin: 0.25rem 0; + transition: all 0.3s ease; + cursor: pointer; } -.sidebar a:hover { - background-color: #4b5563; + +.sidebar .nav-item:before { + font-family: "Font Awesome 5 Free"; + font-weight: 900; + margin-right: 0.75rem; +} + +.sidebar a[data-content="hospital"]:before { + content: "\f0f8"; /* hospital icon */ +} + +.sidebar a[data-content="nursing"]:before { + content: "\f0f0"; /* user-nurse icon */ +} + +.sidebar .sign-out:before { + content: "\f2f5"; /* sign-out icon */ } + +.sidebar .nav-item:hover { + background: rgba(255, 255, 255, 0.1); + color: white; + transform: translateX(5px); +} + +.sidebar .sign-out { + margin-top: auto; + color: #ef4444; +} + +.sidebar .sign-out:hover { + background: #ef4444; + color: white; +} + .content { margin-left: 250px; padding: 2rem; + min-height: 100vh; +} + +/* Active state for sidebar items */ +.sidebar .nav-item.active { + background: #3b82f6; + color: white; +} + +.sidebar div[onclick="chooseOut()"] { + margin-top: auto; + color: #ef4444; + border-top: 1px solid rgba(255, 255, 255, 0.1); + padding-top: 1rem; +} + +.sidebar div[onclick="chooseOut()"]:hover { + background-color: #ef4444; + color: white; +} + +/* Content area adjustment */ +.content { + margin-left: 250px; + padding: 2rem; + min-height: 100vh; + background-color: #f9fafb; } + .table-container { margin-top: 2rem; padding: 1rem; diff --git a/src/main/resources/static/css/beds.css b/src/main/resources/static/css/beds.css index 18f829e7a573ab424a9572096675a4128b8f594f..e49622e277744558f744d5e8b4e83fa5abd30601 100644 --- a/src/main/resources/static/css/beds.css +++ b/src/main/resources/static/css/beds.css @@ -335,10 +335,74 @@ footer { } } -/* beds.css */ +.status-badge.pending { + background-color: #f59e0b; + color: white; + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-size: 0.875rem; +} + +.btn-success { + background-color: #10b981; + color: white; + margin-right: 0.5rem; +} + +.btn-danger { + background-color: #ef4444; + color: white; +} + +.messages { + position: fixed; + top: 1rem; + right: 1rem; + z-index: 1000; + min-width: 300px; +} + +.alert { + padding: 1rem; + margin-bottom: 1rem; + border-radius: 6px; + animation: fadeIn 0.3s ease; +} + +.alert-success { + background-color: #10b981; + color: white; +} + +.alert-error { + background-color: #ef4444; + color: white; +} + +.alert-warning { + background-color: #f59e0b; + color: white; +} .error { color: #ef4444; text-align: center; padding: 1rem; } +.approval-pending { + background-color: var(--warning-color); + color: white; + padding: 0.25rem 0.5rem; + border-radius: 4px; +} + +.approval-rejected { + background-color: var(--danger-color); + color: white; +} + +.approval-approved { + background-color: var(--success-color); + color: white; +} + diff --git a/src/main/resources/static/css/shared-nav.css b/src/main/resources/static/css/shared-nav.css new file mode 100644 index 0000000000000000000000000000000000000000..5b0e2f9cfdd522e933909c37606ea83f60ee8455 --- /dev/null +++ b/src/main/resources/static/css/shared-nav.css @@ -0,0 +1,73 @@ +.top-nav { + background: #1f2937; + padding: 1rem 2rem; + display: flex; + justify-content: space-between; + align-items: center; + box-shadow: 0 2px 8px rgba(0,0,0,0.15); + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; +} + +.nav-brand { + color: white; + font-size: 1.5rem; + font-weight: 600; + text-decoration: none; +} + +.nav-items { + display: flex; + gap: 1.5rem; + align-items: center; +} + +.nav-link { + color: white; + text-decoration: none; + padding: 0.75rem 1.25rem; + border-radius: 6px; + transition: all 0.3s ease; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.nav-link:hover, .nav-link.active { + background: rgba(255,255,255,0.1); + transform: translateY(-1px); +} + +.user-info { + display: flex; + flex-direction: column; + align-items: flex-end; + color: white; + gap: 0.25rem; +} + +.content-wrapper { + margin-top: 80px; + padding: 2rem; + max-width: 1200px; + margin-left: auto; + margin-right: auto; +} + +.sign-out { + background: #ef4444; + border: none; + cursor: pointer; + padding: 0.75rem 1.25rem; + border-radius: 6px; + color: white; + transition: all 0.3s ease; +} + +.sign-out:hover { + background: #dc2626; + transform: translateY(-1px); +} \ No newline at end of file diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css deleted file mode 100644 index f558ba59ee96cc12539796db46127d73e1334fbb..0000000000000000000000000000000000000000 --- a/src/main/resources/static/css/style.css +++ /dev/null @@ -1,18 +0,0 @@ -/* basic */ -.nav-item { - background-color: #93a5e7; - color: white; - padding: 15px; - text-align: center; - margin-bottom: 10px; - border-radius: 5px; - font-size: 18px; - font-weight: bold; - transition: background-color 0.3s ease, transform 0.2s ease; -} - -/* hover */ -.nav-item:hover { - background-color: #555; - transform: scale(1.05); /* Magnification effect */ -} diff --git a/src/main/resources/static/html/CHApprovalSystem.html b/src/main/resources/static/html/CHApprovalSystem.html new file mode 100644 index 0000000000000000000000000000000000000000..8c3f9385d078b16017fd1ea1250a8d28c739d0c4 --- /dev/null +++ b/src/main/resources/static/html/CHApprovalSystem.html @@ -0,0 +1,113 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Hospital Approval System</title> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> + <link rel="stylesheet" href="/css/common.css"> + <link rel="stylesheet" href="/css/beds.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> + <link rel="stylesheet" href="../css/shared-nav.css"> + <style> + .approval-dashboard { + background-color: #ffffff; + border-radius: 10px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + padding: 20px; + margin-top: 20px; + } + + .status-badge { + padding: 5px 10px; + border-radius: 15px; + font-size: 0.9em; + font-weight: 500; + } + + .status-pending { + background-color: #ffc107; + color: #000; + } + + .status-approved { + background-color: #28a745; + color: #fff; + } + + .status-rejected { + background-color: #dc3545; + color: #fff; + } + + .action-buttons button { + margin: 0 5px; + } + </style> + <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> + <script src="../js/shared-nav.js"></script> +</head> + +<body> + <div id="container"> + <!-- Reuse your existing top navigation --> + <div id="top"> + <span class="brand">Digital Insight for Health</span> + <span id="showSpan"></span> + </div> + + <div class="main-content"> + <div class="approval-dashboard"> + <h2>Pending Hospital Approvals</h2> + <div class="table-responsive"> + <table class="table"> + <thead> + <tr> + <th>Hospital Name</th> + <th>Location</th> + <th>Phone</th> + <th>Total Beds</th> + <th>Available Beds</th> + <th>Status</th> + <th>Actions</th> + </tr> + </thead> + <tbody id="pendingTableBody"> + <!-- Dynamically populated --> + </tbody> + </table> + </div> + </div> + </div> + </div> + + <!-- Approval Modal --> + <div class="modal fade" id="approvalModal" tabindex="-1"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title">Confirm Action</h5> + <button type="button" class="btn-close" data-bs-dismiss="modal"></button> + </div> + <div class="modal-body"> + <p id="modalMessage"></p> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button> + <button type="button" class="btn btn-primary" id="confirmAction">Confirm</button> + </div> + </div> + </div> + </div> + + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script> + <script src="/js/approvalSystem.js"></script> + <script> + document.addEventListener('DOMContentLoaded', function() { + setupNavigation('currentPage'); // Replace currentPage with: dashboard, beds, or approvals + }); + </script> +</body> + +</html> diff --git a/src/main/resources/static/html/admin.html b/src/main/resources/static/html/admin.html index 10ec49f919d25b1c79c652f9fd0b46aff99ec539..a7cd1e46a6678b67421c798421802f81689eb65d 100644 --- a/src/main/resources/static/html/admin.html +++ b/src/main/resources/static/html/admin.html @@ -4,14 +4,28 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Hospital Management</title> - <link rel="stylesheet" type="text/css" href="../css/admin.css"> + <link rel="stylesheet" href="../css/beds.css"> + <link rel="stylesheet" href="../css/admin.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> + <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> + <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap" rel="stylesheet"> + <link rel="stylesheet" href="../css/shared-nav.css"> + <script src="../js/shared-nav.js"></script> </head> <body> +<!-- Update sidebar HTML structure --> <nav class="sidebar"> - <div class="text-lg font-semibold">System</div> - <a href="#" data-content="hospital">Hospital</a> - <a href="#" data-content="nursing">Nursing Staff</a> - <a><div onclick="chooseOut()">Sign Out</div></a> + <div class="text-lg">System</div> + <a href="#" data-content="hospital" class="nav-item"> + Hospital + </a> + <a href="#" data-content="nursing" class="nav-item"> + Nursing Staff + </a> + <div onclick="chooseOut()" class="nav-item sign-out"> + Sign Out + </div> +</nav> </nav> <div class="content"> <div id="user-info" style="text-align: right; padding: 10px; font-weight: bold;"></div> @@ -180,6 +194,71 @@ }); } + function openEditHospitalModal(hospitalId) { + fetch(`/commonAdmin/hospital/${hospitalId}`) + .then(response => { + if (!response.ok) throw new Error("Failed to fetch hospital data."); + return response.json(); + }) + .then(hospital => { + document.getElementById('modal-title').innerText = "Edit Hospital"; + document.getElementById('modal-name').value = hospital.name; + document.getElementById('modal-address').value = hospital.address; + document.getElementById('modal-phone').value = hospital.phone; + document.getElementById('modal-totalBeds').value = hospital.totalBeds; + document.getElementById('modal-availableBeds').value = hospital.availableBeds; + document.getElementById('hospital-modal').style.display = 'flex'; + + const saveButton = document.getElementById('save-hospital-btn'); + saveButton.replaceWith(saveButton.cloneNode(true)); + document.getElementById('save-hospital-btn').addEventListener('click', () => updateHospital(hospitalId)); + }) + .catch(error => { + console.error("Error loading hospital data:", error); + alert("Failed to load hospital data."); + }); + } + + function updateHospital(hospitalId) { + const hospitalData = { + id: hospitalId, + name: document.getElementById('modal-name').value.trim(), + location: document.getElementById('modal-location') ? document.getElementById('modal-location').value.trim() : "", // 如果 location 是å¯é€‰çš„ + address: document.getElementById('modal-address').value.trim(), + phone: document.getElementById('modal-phone').value.trim(), + totalBeds: parseInt(document.getElementById('modal-totalBeds').value.trim()), + availableBeds: parseInt(document.getElementById('modal-availableBeds').value.trim()) + }; + + console.log("Updating hospital with data:", hospitalData); + + if (!hospitalData.name || !hospitalData.address || !hospitalData.phone || + isNaN(hospitalData.totalBeds) || isNaN(hospitalData.availableBeds)) { + alert("All fields are required!"); + return; + } + + fetch(`/commonAdmin/hospital/update/${hospitalId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(hospitalData), + }) + .then(response => { + if (response.ok) { + alert("Hospital updated successfully!"); + closeHospitalModal(); + loadHospitals(); // Reload hospital list + } else { + return response.text().then(text => { throw new Error(text); }); + } + }) + .catch(error => { + console.error("Error updating hospital:", error); + alert("Failed to update hospital: " + error.message); + }); + } + + function loadHospitals(filters = {}) { const query = new URLSearchParams(filters).toString(); fetch(`/commonAdmin/hospital/all?${query}`) @@ -202,6 +281,35 @@ `; tableBody.appendChild(row); }); + + document.querySelectorAll(".editButton").forEach(button => { + button.addEventListener("click", () => { + const hospitalId = button.getAttribute("data-id"); + openEditHospitalModal(hospitalId); + }); + }); + + document.querySelectorAll(".deleteButton").forEach(button => { + button.addEventListener("click", () => { + const hospitalId = button.getAttribute("data-id"); + if (confirm("Are you sure you want to delete this hospital?")) { + fetch(`/commonAdmin/hospital/delete/${hospitalId}`, { + method: "DELETE", + }) + .then(response => { + if (!response.ok) { + return response.text().then(text => { throw new Error(text); }); + } + alert("Hospital deleted successfully!"); + loadHospitals(); + }) + .catch(error => { + console.error("Error deleting hospital:", error); + alert(`Failed to delete hospital: ${error.message}`); + }); + } + }); + }); }) .catch(error => console.error("Error loading hospitals:", error)); } @@ -251,7 +359,6 @@ loadHospitals(); }); - // Add close button event listener document.getElementById('close-hospital-modal').addEventListener('click', closeHospitalModal); } @@ -284,6 +391,10 @@ } }); }); + + document.addEventListener('DOMContentLoaded', function() { + setupNavigation('currentPage'); // Replace currentPage with: dashboard, beds, or approvals + }); </script> </body> </html> diff --git a/src/main/resources/static/html/beds.html b/src/main/resources/static/html/beds.html index d1a008e1384a899270fb2ce15230056cd25edc15..baa1411e8fc590d07e55a93bcc8c0639e822b114 100644 --- a/src/main/resources/static/html/beds.html +++ b/src/main/resources/static/html/beds.html @@ -8,6 +8,8 @@ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap" rel="stylesheet"> + <link rel="stylesheet" href="../css/shared-nav.css"> + <script src="../js/shared-nav.js"></script> </head> <body> <div class="main-container"> @@ -87,5 +89,10 @@ </div> <script src="../js/beds.js"></script> + <script> + document.addEventListener('DOMContentLoaded', function() { + setupNavigation('currentPage'); // Replace currentPage with: dashboard, beds, or approvals + }); + </script> </body> </html> diff --git a/src/main/resources/static/html/hospitalView.html b/src/main/resources/static/html/hospitalView.html index 9e7f296ea1a822039f1b94159f9447d5d9e62c9a..fec912c20bf3a142849bebbe7bbeefaea4932a46 100644 --- a/src/main/resources/static/html/hospitalView.html +++ b/src/main/resources/static/html/hospitalView.html @@ -4,6 +4,8 @@ <meta charset="UTF-8"> <title>Title</title> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> + <link rel="stylesheet" href="../css/shared-nav.css"> + <script src="../js/shared-nav.js"></script> <style> body{ background-image:url("/img/backpicture.jpg"); @@ -107,5 +109,9 @@ } }) + + document.addEventListener('DOMContentLoaded', function() { + setupNavigation('currentPage'); // Replace currentPage with: dashboard, beds, or approvals + }); </script> </html> \ No newline at end of file diff --git a/src/main/resources/static/html/superAdminView.html b/src/main/resources/static/html/superAdminView.html index 328844d8ca5077bec65b9b126897ab39a84eeac5..dc2b8cb7a72b9d5538d3167da03c9ad19f686fe8 100644 --- a/src/main/resources/static/html/superAdminView.html +++ b/src/main/resources/static/html/superAdminView.html @@ -4,6 +4,9 @@ <meta charset="UTF-8"> <title>Title</title> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> + <link rel="stylesheet" href="../css/admin.css"> + <link rel="stylesheet" href="../css/shared-nav.css"> + <script src="../js/shared-nav.js"></script> <style> body{ background-image:url("/img/backpicture.jpg"); @@ -76,5 +79,9 @@ } }) + + document.addEventListener('DOMContentLoaded', function() { + setupNavigation('currentPage'); // Replace currentPage with: dashboard, beds, or approvals + }); </script> </html> \ No newline at end of file diff --git a/src/main/resources/static/js/approvalSystem.js b/src/main/resources/static/js/approvalSystem.js new file mode 100644 index 0000000000000000000000000000000000000000..7d0bf8b93b9cb8c7c8a12e9f048d3083ce879d4e --- /dev/null +++ b/src/main/resources/static/js/approvalSystem.js @@ -0,0 +1,132 @@ +$(document).ready(function() { + const API_BASE_URL = '/api/hospitals'; + + // Initialize + loadPendingHospitals(); + setupNavigation(); + + function setupNavigation() { + const nav = ` + <nav class="top-nav"> + <div class="nav-brand">Digital Insight for Health</div> + <div class="nav-items"> + <a href="/superAdminView.html?type=hospital" class="nav-link"> + <i class="fas fa-home"></i> Dashboard + </a> + <a href="/beds.html" class="nav-link"> + <i class="fas fa-bed"></i> Beds + </a> + <a href="/approvalSystem.html" class="nav-link active"> + <i class="fas fa-check-circle"></i> Approvals + </a> + <button class="nav-link sign-out" onclick="handleSignOut()"> + <i class="fas fa-sign-out-alt"></i> Sign Out + </button> + </div> + <div class="user-info"> + <span class="user-name">${localStorage.getItem('name') || 'User'}</span> + <span class="user-role">${localStorage.getItem('type') || 'Guest'}</span> + </div> + </nav> + `; + $('.main-container').prepend(nav); + } + + function loadPendingHospitals() { + $.ajax({ + url: `${API_BASE_URL}/pending`, + method: 'GET', + success: function(response) { + if (response.code === 1) { + displayPendingHospitals(response.data); + } else { + showMessage('warning', 'No pending hospitals found'); + } + }, + error: handleError + }); + } + + function displayPendingHospitals(hospitals) { + const tbody = $('#pendingTableBody'); + tbody.empty(); + + if (!hospitals || hospitals.length === 0) { + tbody.append(` + <tr> + <td colspan="7" class="text-center">No pending hospitals found</td> + </tr> + `); + return; + } + + hospitals.forEach(hospital => { + const tr = ` + <tr> + <td>${hospital.name}</td> + <td>${hospital.location}</td> + <td>${hospital.phone}</td> + <td>${hospital.totalBeds}</td> + <td>${hospital.availableBeds}</td> + <td> + <span class="status-badge pending">PENDING</span> + </td> + <td> + <button class="btn btn-success approve-btn" onclick="handleApproval(${hospital.id}, 'APPROVED')"> + <i class="fas fa-check"></i> Approve + </button> + <button class="btn btn-danger reject-btn" onclick="handleApproval(${hospital.id}, 'REJECTED')"> + <i class="fas fa-times"></i> Reject + </button> + </td> + </tr> + `; + tbody.append(tr); + }); + } + + window.handleApproval = function(hospitalId, status) { + if (!confirm(`Are you sure you want to ${status.toLowerCase()} this hospital?`)) { + return; + } + + $.ajax({ + url: `${API_BASE_URL}/${hospitalId}/approval`, + method: 'PUT', + data: { status: status }, + success: function(response) { + if (response.code === 1) { + showMessage('success', `Hospital ${status.toLowerCase()} successfully`); + loadPendingHospitals(); + } else { + showMessage('error', response.msg || 'Operation failed'); + } + }, + error: handleError + }); + } + + function showMessage(type, message) { + const alertDiv = ` + <div class="alert alert-${type} alert-dismissible fade show"> + ${message} + <button type="button" class="btn-close" data-bs-dismiss="alert"></button> + </div> + `; + + $('.messages').html(alertDiv); + setTimeout(() => { + $('.alert').fadeOut().remove(); + }, 3000); + } + + function handleError(xhr, status, error) { + console.error('API Error:', error); + showMessage('error', 'An error occurred. Please try again.'); + } +}); + +function handleSignOut() { + localStorage.clear(); + window.location.href = '/login.html'; +} \ No newline at end of file diff --git a/src/main/resources/static/js/beds.js b/src/main/resources/static/js/beds.js index d251cb4af6eb3bbff23d1b2955a4b58d04a6a5b9..f9b37375513d88a8722ad7811ef0ce451d228020 100644 --- a/src/main/resources/static/js/beds.js +++ b/src/main/resources/static/js/beds.js @@ -52,35 +52,46 @@ $(document).ready(function () { function updateTable(hospitals) { const tbody = $('#hospitalTable tbody'); tbody.empty(); - + + if (!hospitals || hospitals.length === 0) { + showNoDataMessage(); + return; + } + hospitals.forEach(function(hospital) { - const tr = ` - <tr> - <td>${hospital.id}</td> - <td>${hospital.name || ''}</td> - <td>${hospital.location || ''}</td> - <td>${hospital.phone || ''}</td> - <td>${hospital.totalBeds || 0}</td> - <td>${hospital.availableBeds || 0}</td> - <td>${Math.round(hospital.occupancyRate)}%</td> - <td> - <button class="btn edit-btn" data-id="${hospital.id}"> - <i class="fas fa-edit"></i> - </button> - <button class="btn delete-btn" data-id="${hospital.id}"> - <i class="fas fa-trash"></i> - </button> - </td> - </tr> - `; - tbody.append(tr); + // Only show approved hospitals + if (hospital.approvalStatus === 'APPROVED') { + const tr = $('<tr>').append( + $('<td>').text(hospital.name), + $('<td>').text(hospital.location), + $('<td>').text(hospital.phone), + $('<td>').text(hospital.totalBeds), + $('<td>').text(hospital.availableBeds), + $('<td>').text(hospital.occupancyRate + '%'), + $('<td>').append( + $('<span>') + .addClass('status-badge') + .addClass('approval-' + hospital.approvalStatus.toLowerCase()) + .text(hospital.approvalStatus) + ) + ); + tbody.append(tr); + } }); + + if (tbody.children().length === 0) { + tbody.append( + $('<tr>').append( + $('<td>') + .attr('colspan', '7') + .addClass('no-data') + .text('No approved hospitals found') + ) + ); + } } function initializeEventHandlers() { - // Search Handler - $("#searchButton").click(handleSearch); - // Add Hospital Handler $("#addHospitalBtn").click(function() { $("#hospitalModal").fadeIn(); @@ -159,17 +170,23 @@ $(document).ready(function () { }); } }); + + // Update event handler binding + $("#searchButton").click(handleSearch); } function handleSearch(event) { event.preventDefault(); const searchTerm = $("#searchName").val().trim(); + if (!searchTerm) { + loadHospitals(); + return; + } + $.ajax({ - url: `${API_BASE_URL}/search`, - method: 'POST', - contentType: 'application/json', - data: JSON.stringify({ name: searchTerm }), + url: `${API_BASE_URL}/search?name=${encodeURIComponent(searchTerm)}`, + method: 'GET', success: function(response) { if(response && response.code === 1) { updateTable(response.data || []); @@ -177,7 +194,10 @@ $(document).ready(function () { showError('No hospitals found'); } }, - error: handleAjaxError + error: function(xhr) { + console.error('Search Error:', xhr); + showError('Search failed'); + } }); } @@ -326,11 +346,26 @@ $(document).ready(function () { $('<style>').text(styles).appendTo('head'); - // Initialize tooltips and other UI enhancements - $('[data-toggle="tooltip"]').tooltip(); - // Add fade effects for smoother transitions $('.table-container').hide().fadeIn(); + + // Add new function for admin approval handling + function handleApproval(hospitalId, status) { + $.ajax({ + url: `${API_BASE_URL}/approval/${hospitalId}`, + method: 'PUT', + data: { status: status }, + success: function(response) { + if(response.code === 1) { + showSuccess(`Hospital ${status.toLowerCase()} successfully`); + loadHospitals(); + } else { + showError(response.msg || DEFAULT_ERROR); + } + }, + error: handleAjaxError + }); + } }); function handleSignOut() { diff --git a/src/main/resources/static/js/shared-nav.js b/src/main/resources/static/js/shared-nav.js new file mode 100644 index 0000000000000000000000000000000000000000..2f3a698573212a169f31cc56af16ed65b252ffcc --- /dev/null +++ b/src/main/resources/static/js/shared-nav.js @@ -0,0 +1,35 @@ +function setupNavigation(currentPage) { + const nav = ` + <nav class="top-nav"> + <a href="/superAdminView.html" class="nav-brand">Digital Insight for Health</a> + <div class="nav-items"> + <a href="/superAdminView.html" class="nav-link ${currentPage === 'dashboard' ? 'active' : ''}"> + <i class="fas fa-home"></i> Dashboard + </a> + <a href="/beds.html" class="nav-link ${currentPage === 'beds' ? 'active' : ''}"> + <i class="fas fa-bed"></i> Beds + </a> + <a href="/CHApprovalSystem.html" class="nav-link ${currentPage === 'approvals' ? 'active' : ''}"> + <i class="fas fa-check-circle"></i> Approvals + </a> + <div class="user-info"> + <span class="user-name">${localStorage.getItem('name') || 'User'}</span> + <span class="user-role">${localStorage.getItem('type') || 'Guest'}</span> + </div> + <button class="sign-out" onclick="handleSignOut()"> + <i class="fas fa-sign-out-alt"></i> Sign Out + </button> + </div> + </nav> + <div class="content-wrapper"> + <!-- Page content will go here --> + </div> + `; + + document.body.insertAdjacentHTML('afterbegin', nav); +} + +function handleSignOut() { + localStorage.clear(); + window.location.href = '/login.html'; +} \ No newline at end of file