From f5425ba304e0a8df70c93ef5461a6835f92019d5 Mon Sep 17 00:00:00 2001 From: pjs Date: Sun, 18 Jan 2026 16:52:35 +0900 Subject: [PATCH] =?UTF-8?q?kiosk=20=EC=A7=84=ED=96=89=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/madeu/crm/TestClass.java | 12 + .../madeu/crm/kiosk/ctrl/KioskController.java | 11 + .../madeu/crm/kiosk/dto/ConsentFormDTO.java | 43 ++ .../com/madeu/crm/kiosk/map/KioskMAP.java | 45 ++ .../madeu/crm/kiosk/service/KioskService.java | 287 +++++++- .../kiosk/service/impl/KioskServiceImpl.java | 510 ------------- .../crm/kiosk/{KioskSql.xml => KioskMAP.xml} | 20 +- .../static/css/kiosk/new-patient.css | 385 ++++++---- .../resources/static/js/kiosk/new-patient.js | 670 +++++------------- .../resources/static/js/kiosk/new-patient2.js | 548 ++++++++++++++ .../templates/kiosk/new-patient.html | 184 +++-- 11 files changed, 1459 insertions(+), 1256 deletions(-) create mode 100644 src/main/java/com/madeu/crm/TestClass.java create mode 100644 src/main/java/com/madeu/crm/kiosk/dto/ConsentFormDTO.java create mode 100644 src/main/java/com/madeu/crm/kiosk/map/KioskMAP.java delete mode 100644 src/main/java/com/madeu/crm/kiosk/service/impl/KioskServiceImpl.java rename src/main/resources/mappers/crm/kiosk/{KioskSql.xml => KioskMAP.xml} (94%) create mode 100644 src/main/resources/static/js/kiosk/new-patient2.js diff --git a/src/main/java/com/madeu/crm/TestClass.java b/src/main/java/com/madeu/crm/TestClass.java new file mode 100644 index 0000000..67dc613 --- /dev/null +++ b/src/main/java/com/madeu/crm/TestClass.java @@ -0,0 +1,12 @@ +package com.madeu.crm; + +import com.madeu.util.SecurityUtil; + +public class TestClass { + + public static void main(String[] args) { + // TODO Auto-generated method stub + System.out.println(SecurityUtil.encryptSHA256("apdlemdb12#$")); + } + +} diff --git a/src/main/java/com/madeu/crm/kiosk/ctrl/KioskController.java b/src/main/java/com/madeu/crm/kiosk/ctrl/KioskController.java index 712f5e0..2f99a2b 100644 --- a/src/main/java/com/madeu/crm/kiosk/ctrl/KioskController.java +++ b/src/main/java/com/madeu/crm/kiosk/ctrl/KioskController.java @@ -5,11 +5,13 @@ import java.util.HashMap; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; import com.madeu.constants.Constants; +import com.madeu.crm.kiosk.dto.ConsentFormDTO; import com.madeu.crm.kiosk.service.KioskService; import com.madeu.init.ManagerDraftAction; import com.madeu.service.web.webloghistory.WebLogHistoryService; @@ -41,6 +43,15 @@ public class KioskController extends ManagerDraftAction { log.debug("KioskController selectMainIntro END"); return new ModelAndView("/kiosk/consultation"); } + @PostMapping("/kiosk/getConsentForm.do") + public ConsentFormDTO getConsentForm(@RequestBody ConsentFormDTO dto) { + log.debug("KioskController selectMainIntro START"); + + dto = kioskService.getConsentForm(dto); + + log.debug("KioskController selectMainIntro END"); + return dto; + } /** * 키오스크 신규 환자 화면으로 이동. diff --git a/src/main/java/com/madeu/crm/kiosk/dto/ConsentFormDTO.java b/src/main/java/com/madeu/crm/kiosk/dto/ConsentFormDTO.java new file mode 100644 index 0000000..8c68951 --- /dev/null +++ b/src/main/java/com/madeu/crm/kiosk/dto/ConsentFormDTO.java @@ -0,0 +1,43 @@ +package com.madeu.crm.kiosk.dto; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * 동의서 양식 DTO + */ +@Getter +@Setter +@ToString +public class ConsentFormDTO { + + /** 동의서 종류 코드 (PK) */ + private String consentFormTypeCd; + + /** 국가 코드 (PK) */ + private String nationalCd; + + /** 동의서 버전 (PK) */ + private Integer consentFormVersion; + + /** 동의서 내용 (longtext 대응) */ + private String consentFormContent; + + /** 사용 여부 (Y/N) */ + private String useYn; + + /** 등록자 ID */ + private String rgstrId; + + /** 등록 일시 */ + private LocalDateTime regDt; + + /** 수정자 ID */ + private String mdfrId; + + /** 수정 일시 */ + private LocalDateTime modDt; +} \ No newline at end of file diff --git a/src/main/java/com/madeu/crm/kiosk/map/KioskMAP.java b/src/main/java/com/madeu/crm/kiosk/map/KioskMAP.java new file mode 100644 index 0000000..e13002b --- /dev/null +++ b/src/main/java/com/madeu/crm/kiosk/map/KioskMAP.java @@ -0,0 +1,45 @@ +package com.madeu.crm.kiosk.map; + +import java.util.List; +import java.util.Map; +import org.apache.ibatis.annotations.Mapper; +import com.madeu.crm.kiosk.dto.ConsentFormDTO; + +@Mapper +public interface KioskMAP { + // 동의서 조회 + ConsentFormDTO selectConsentForm(ConsentFormDTO dto); + + // 진료 리스트 조회 + List> getTreatmentOptionList(Map paramMap); + + // 카테고리 조회 (List & Item) + List> getCategoryList(Map paramMap); + List> getCategoryItem(Map paramMap); + + // 고객 리스트 및 카운트 + int getUserOptionListCnt(Map paramMap); + List> getUserOptionList(Map paramMap); + + // 고객 등록 및 단일 조회 + int putUser(Map paramMap); + Map getUser(Map paramMap); + + // 병원 및 근무시간 정보 + List> getHospital(Map paramMap); + List> getHospitalHolidayList(Map paramMap); + + // 진료시술 리스트 및 카운트 + int getTotalTreatmentProcedureOptionCount(Map paramMap); + List> getTreatmentProcedureOptionList(Map paramMap); + + // 상담 의사 리스트 + List> getMemberDoctorConsultingOptionList(Map paramMap); + + // 직원 리스트 및 카운트 + List> getMemberListCnt(Map paramMap); + List> getMemberList(Map paramMap); + + // 예약 고객 옵션 리스트 + List> getReserveUserOptionList(Map paramMap); +} \ No newline at end of file diff --git a/src/main/java/com/madeu/crm/kiosk/service/KioskService.java b/src/main/java/com/madeu/crm/kiosk/service/KioskService.java index 4976e70..faeb174 100644 --- a/src/main/java/com/madeu/crm/kiosk/service/KioskService.java +++ b/src/main/java/com/madeu/crm/kiosk/service/KioskService.java @@ -1,17 +1,278 @@ package com.madeu.crm.kiosk.service; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; import java.util.HashMap; +import java.util.List; +import java.util.Map; -public interface KioskService { - public HashMap getTreatmentOptionList(HashMap paramMap) throws Exception; - public HashMap getCategoryList(HashMap paramMap) throws Exception; - public HashMap getCategoryItem(HashMap paramMap) throws Exception; - public HashMap getUserOptionList(HashMap paramMap) throws Exception; - public HashMap putUser(HashMap paramMap) throws Exception; - public HashMap getUser(HashMap paramMap) throws Exception; - public HashMap getWorkTime(HashMap paramMap) throws Exception; - public HashMap getTreatmentProcedureOptionList(HashMap paramMap) throws Exception; - public HashMap getMemberDoctorConsultingOptionList(HashMap paramMap) throws Exception; - public HashMap getMemberList(HashMap paramMap) throws Exception; - public HashMap getReserveUserOptionList(HashMap paramMap) throws Exception; -} +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.madeu.constants.Constants; +import com.madeu.crm.kiosk.dto.ConsentFormDTO; +import com.madeu.crm.kiosk.map.KioskMAP; +import com.madeu.util.ValidationCheckUtil; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@Service +public class KioskService { + + @Autowired + private KioskMAP kioskMAP; // 인터페이스 기반 Mapper 주입 + + /** + * 진료 리스트 조회 (option) + */ + public HashMap getTreatmentOptionList(HashMap paramMap) throws Exception { + log.debug("KioskServiceImpl getTreatmentOptionList START"); + HashMap map = new HashMap(); + + try { + List> listMap = kioskMAP.getTreatmentOptionList(paramMap); + map.put("msgCode", Constants.OK); + map.put("success","true"); + map.put("rows",listMap); + } catch(Exception e) { + log.error("getTreatmentOptionList Error: ", e); + throw e; + } + return map; + } + + /** + * 병원 기타설정 카테고리 조회 (Option) + */ + public HashMap getCategoryList(HashMap paramMap) throws Exception { + HashMap map = new HashMap(); + List> categoryListMap = kioskMAP.getCategoryList(paramMap); + map.put("msgCode", Constants.OK); + map.put("success","true"); + map.put("rows",categoryListMap); + return map; + } + + /** + * 카테고리 아이템 조회 (Option) + */ + public HashMap getCategoryItem(HashMap paramMap) throws Exception { + HashMap map = new HashMap(); + List> categoryListMap = kioskMAP.getCategoryItem(paramMap); + map.put("msgCode", Constants.OK); + map.put("success","true"); + map.put("rows",categoryListMap); + return map; + } + + /** + * 병원 고객정보 리스트 조회 + */ + public HashMap getUserOptionList(HashMap paramMap) throws Exception { + HashMap map = new HashMap(); + List> listMap = new ArrayList<>(); + + if(null != paramMap.get("userDir") && !("").equals(paramMap.get("userDir"))){ + String dir = String.valueOf(paramMap.get("userDir")); + paramMap.put("userDir", ("B").equals(dir) ? "ASC" : "DESC"); + } + + paramMap.put("useYn", "Y"); + int totalCount = kioskMAP.getUserOptionListCnt(paramMap); + if(0 < totalCount){ + listMap = kioskMAP.getUserOptionList(paramMap); + } + + map.put("msgCode", Constants.OK); + map.put("success","true"); + map.put("totalCount", totalCount); + map.put("rows",listMap); + return map; + } + + /** + * 고객 등록 + */ + public HashMap putUser(HashMap paramMap) throws Exception { + HashMap map = new HashMap(); + + try { + boolean check = true; + String tId = String.valueOf(System.currentTimeMillis()); + + // 유효성 검사 로직 + if(!ValidationCheckUtil.emptyCheck(String.valueOf(paramMap.get("nationality")))) { + check = false; map.put("msgCode", Constants.FAIL); map.put("msgDesc","국적 정보가 없습니다."); + } else if(!ValidationCheckUtil.emptyCheck(String.valueOf(paramMap.get("userName")))) { + check = false; map.put("msgCode", Constants.FAIL); map.put("msgDesc","고객 성함 정보가 없습니다."); + } else if(!ValidationCheckUtil.nickNameCheck(String.valueOf(paramMap.get("userName")))) { + check = false; map.put("msgCode", Constants.FAIL); map.put("msgDesc","유효하지 않은 이름 형식입니다."); + } else if(!ValidationCheckUtil.phoneNumberCheck(String.valueOf(paramMap.get("phoneNumber")))) { + check = false; map.put("msgCode", Constants.FAIL); map.put("msgDesc","유효하지 않은 전화번호 형식입니다."); + } + + if(check) { + String muUserId = ("U").concat(String.valueOf(System.currentTimeMillis())); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + paramMap.put("muUserId", muUserId); + paramMap.put("tId", tId); + paramMap.put("tDate", sdf.format(Calendar.getInstance().getTime())); + + kioskMAP.putUser(paramMap); + + map.put("msgCode", Constants.OK); + map.put("msgDesc", "등록되었습니다."); + } + } catch (Exception e) { + log.error("putUser Error: ", e); + throw e; + } + return map; + } + + /** + * 고객정보 조회 + */ + public HashMap getUser(HashMap paramMap) throws Exception { + HashMap map = new HashMap(); + Map usermap = new HashMap(); + if (paramMap.containsKey("muUserId")) { + usermap = kioskMAP.getUser(paramMap); + } + map.put("msgCode", Constants.OK); + map.put("success","true"); + map.put("rows",usermap); + return map; + } + + /** + * 근무시간 조회 + */ + public HashMap getWorkTime(HashMap paramMap) throws Exception { + HashMap map = new HashMap(); + List> hospitalListMap = kioskMAP.getHospital(paramMap); + + if(hospitalListMap.size() == 1){ + paramMap.put("muHospitalId", hospitalListMap.get(0).get("muHospitalId")); + List> holidayList = kioskMAP.getHospitalHolidayList(paramMap); + hospitalListMap.get(0).put("holidayRows", holidayList); + + map.put("msgCode", Constants.OK); + map.put("success","true"); + map.put("rows",hospitalListMap); + } else { + map.put("msgCode", Constants.FAIL); + map.put("msgDesc", "병원 정보가 없습니다."); + } + return map; + } + + /** + * 진료시술 조회 + */ + public HashMap getTreatmentProcedureOptionList(HashMap paramMap) throws Exception { + HashMap map = new HashMap(); + List> listMap = new ArrayList<>(); + + if(null != paramMap.get("treatmentProcedureDir") && !("").equals(paramMap.get("treatmentProcedureDir"))){ + String dir = String.valueOf(paramMap.get("treatmentProcedureDir")); + paramMap.put("treatmentProcedureDir", ("B").equals(dir) ? "ASC" : "DESC"); + } + + paramMap.put("useYn", "Y"); + int totalCount = kioskMAP.getTotalTreatmentProcedureOptionCount(paramMap); + if(0 < totalCount){ + listMap = kioskMAP.getTreatmentProcedureOptionList(paramMap); + } + + map.put("msgCode", Constants.OK); + map.put("success","true"); + map.put("totalCount", totalCount); + map.put("rows",listMap); + return map; + } + + /** + * 상담 의사 리스트 조회 + */ + public HashMap getMemberDoctorConsultingOptionList(HashMap paramMap) throws Exception { + HashMap map = new HashMap(); + String muTreatmentProcedureId = String.valueOf(paramMap.get("muTreatmentProcedureId")); + + if(!ValidationCheckUtil.emptyCheck(muTreatmentProcedureId)){ + map.put("msgCode", Constants.FAIL); + map.put("msgDesc","시술 정보가 없습니다."); + }else{ + String [] arr = muTreatmentProcedureId.split(","); + StringBuilder sb = new StringBuilder(); + for(String id : arr){ + sb.append("'").append(id).append("',"); + } + String list = sb.substring(0, sb.lastIndexOf(",")); + paramMap.put("muTreatmentProcedureIdList", list); + + List> listMap = kioskMAP.getMemberDoctorConsultingOptionList(paramMap); + map.put("msgCode", Constants.OK); + map.put("success","true"); + map.put("rows",listMap); + } + return map; + } + + /** + * 직원 리스트 조회 + */ + public HashMap getMemberList(HashMap paramMap) throws Exception { + HashMap map = new HashMap(); + List> listMap = new ArrayList<>(); + + if(null != paramMap.get("memberDir") && !("").equals(paramMap.get("memberDir"))){ + String dir = String.valueOf(paramMap.get("memberDir")); + paramMap.put("memberDir", ("B").equals(dir) ? "ASC" : "DESC"); + } + + paramMap.put("useYn", "Y"); + List> totalCountListMap = kioskMAP.getMemberListCnt(paramMap); + int totalCount = Integer.parseInt(String.valueOf(totalCountListMap.get(0).get("totalcount"))); + + if(0 < totalCount) { + listMap = kioskMAP.getMemberList(paramMap); + } + + map.put("msgCode", Constants.OK); + map.put("success","true"); + map.put("totalCount", totalCount); + map.put("rows",listMap); + return map; + } + + /** + * 예약 고객 옵션 리스트 조회 + */ + public HashMap getReserveUserOptionList(HashMap paramMap) throws Exception { + HashMap map = new HashMap(); + List> listMap = new ArrayList<>(); + + List> totalCountListMap = kioskMAP.getMemberListCnt(paramMap); + int totalCount = Integer.parseInt(String.valueOf(totalCountListMap.get(0).get("totalcount"))); + + if(0 < totalCount) { + listMap = kioskMAP.getReserveUserOptionList(paramMap); + } + + map.put("msgCode", Constants.OK); + map.put("success","true"); + map.put("totalCount", totalCount); + map.put("rows",listMap); + return map; + } + + /** + * 동의서 조회 + */ + public ConsentFormDTO getConsentForm(ConsentFormDTO dto) { + return kioskMAP.selectConsentForm(dto); + } +} \ No newline at end of file diff --git a/src/main/java/com/madeu/crm/kiosk/service/impl/KioskServiceImpl.java b/src/main/java/com/madeu/crm/kiosk/service/impl/KioskServiceImpl.java deleted file mode 100644 index a1f8f6a..0000000 --- a/src/main/java/com/madeu/crm/kiosk/service/impl/KioskServiceImpl.java +++ /dev/null @@ -1,510 +0,0 @@ -package com.madeu.crm.kiosk.service.impl; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import com.madeu.common.dao.CommonDao; -import com.madeu.constants.Constants; -import com.madeu.crm.kiosk.service.KioskService; -import com.madeu.util.ValidationCheckUtil; - -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@Service -public class KioskServiceImpl implements KioskService { - - @Autowired - private CommonDao cmmnDAO; - - /** - * 진료 리스트 조회 (option) - * - * - * @param paramMap - * @return - * @throws Exception - */ - @Override - public HashMap getTreatmentOptionList(HashMap paramMap) throws Exception { - log.debug("KioskServiceImpl getTreatmentOptionList START"); - - HashMap map = new HashMap(); - - log.debug("loginMemberId :"+paramMap.get("loginMemberId")); - - log.debug("menuClass :"+paramMap.get("menuClass")); - - try{ - boolean check = true; - - if(true == check){ - List> listMap = cmmnDAO.selectList("KioskSql.getTreatmentOptionList", paramMap); - - map.put("msgCode", Constants.OK); - map.put("success","true"); - map.put("rows",listMap); - } - } - catch(Exception e){ - e.printStackTrace(); - throw e; - } - log.debug("KioskServiceImpl getTreatmentOptionList END"); - return map; - } - - /** - * 병원 기타설정 카테고리 조회 (Option) - * - * - * @param paramMap - * @return - * @throws Exception - */ - @Override - public HashMap getCategoryList(HashMap paramMap) throws Exception { - log.debug("KioskServiceImpl getCategoryList START"); - - HashMap map = new HashMap(); - - List> categoryListMap = cmmnDAO.selectList("KioskSql.getCategoryList", paramMap); - - map.put("msgCode", Constants.OK); - map.put("success","true"); - map.put("rows",categoryListMap); - - log.debug("KioskServiceImpl getCategoryList END"); - return map; - } - - /** - * 병원 기타설정 카테고리 조회 (Option) - * - * - * @param paramMap - * @return - * @throws Exception - */ - @Override - public HashMap getCategoryItem(HashMap paramMap) throws Exception { - log.debug("KioskServiceImpl getCategoryItem START"); - - HashMap map = new HashMap(); - - List> categoryListMap = cmmnDAO.selectList("KioskSql.getCategoryItem", paramMap); - - map.put("msgCode", Constants.OK); - map.put("success","true"); - map.put("rows",categoryListMap); - - log.debug("KioskServiceImpl getCategoryItem END"); - return map; - } - - /** - * 병원 고객정보 리스트 조회 - * - * - * @param paramMap - * @return - * @throws Exception - */ - @Override - public HashMap getUserOptionList(HashMap paramMap) throws Exception { - log.debug("KioskServiceImpl getUserOptionList START"); - - HashMap map = new HashMap(); - List> listMap = new ArrayList<>(); - // 정렬 - if(null==paramMap.get("userDir")||("").equals(paramMap.get("userDir"))){ - - } - else{ - String dir = String.valueOf(paramMap.get("userDir")); - if(("A").equals(dir)){ - paramMap.put("userDir", "DESC"); - } - else if(("B").equals(dir)){ - paramMap.put("userDir", "ASC"); - } - else { - paramMap.put("userDir", "DESC"); - } - } - - paramMap.put("useYn", "Y"); - - int totalCount = cmmnDAO.selectCount("KioskSql.getUserOptionListCnt", paramMap); - if(0 putUser(HashMap paramMap) throws Exception { - log.debug("KioskServiceImpl putUser START"); - - HashMap map = new HashMap(); - - log.debug("nationality : " + paramMap.get("nationality")); - log.debug("nationalityCode : " + paramMap.get("nationalityCode")); - log.debug("localYn : " + paramMap.get("localYn")); - log.debug("userName : " + paramMap.get("userName")); - log.debug("userRrn1 : " + paramMap.get("userRrn1")); - log.debug("userRrn2 : " + paramMap.get("userRrn2")); - log.debug("userPno : " + paramMap.get("userPno")); - log.debug("userArc1 : " + paramMap.get("userArc1")); - log.debug("userArc2 : " + paramMap.get("userArc2")); - log.debug("phoneNumber : " + paramMap.get("phoneNumber")); - log.debug("phoneNumber2 : " + paramMap.get("phoneNumber2")); - log.debug("gender : " + paramMap.get("gender")); - log.debug("channel : " + paramMap.get("channel")); - log.debug("channelCode : " + paramMap.get("channelCode")); - log.debug("muGroupId : " + paramMap.get("muGroupId")); - log.debug("memo : " + paramMap.get("memo")); - log.debug("etc : " + paramMap.get("etc")); - log.debug("birthday : " + paramMap.get("birthday")); - log.debug("userTypeCode : " + paramMap.get("userTypeCode")); - log.debug("userType : " + paramMap.get("userType")); - log.debug("refusePhotoYn : " + paramMap.get("refusePhotoYn")); - log.debug("smsYn : " + paramMap.get("smsYn")); - log.debug("identification : " + paramMap.get("identification")); - log.debug("introUserId : " + paramMap.get("introUserId")); - log.debug("muMemberId : " + paramMap.get("muMemberId")); - log.debug("modId : " + paramMap.get("modId")); - log.debug("regId : " + paramMap.get("regId")); - - try{ - boolean check = true; - String tId = String.valueOf(System.currentTimeMillis()); - - - String nationality = String.valueOf(paramMap.get("nationality")); - String userName = String.valueOf(paramMap.get("userName")); - String birthday = String.valueOf(paramMap.get("birthday")); - String phoneNumber = String.valueOf(paramMap.get("phoneNumber")); - String phoneNumber2 = String.valueOf(paramMap.get("phoneNumber2")); - - if(true != ValidationCheckUtil.emptyCheck(nationality)){ - check = false; - map.put("msgCode", Constants.FAIL); - map.put("msgDesc","국적 정보가 없습니다."); - } - if(true != ValidationCheckUtil.emptyCheck(userName)){ - check = false; - map.put("msgCode", Constants.FAIL); - map.put("msgDesc","고객 성함 정보가 없습니다."); - } - if(true != ValidationCheckUtil.emptyCheck(birthday)){ - paramMap.put("birthday", null); - } - if (!ValidationCheckUtil.emptyCheck(userName) || !ValidationCheckUtil.nickNameCheck(userName)) { - check = false; - map.put("msgCode", Constants.FAIL); - map.put("msgDesc","유효하지 않은 이름 형식입니다."); - } - - // 전화번호 형식 검증 - if (!ValidationCheckUtil.emptyCheck(phoneNumber) || !ValidationCheckUtil.phoneNumberCheck(phoneNumber)) { - check = false; - map.put("msgCode", Constants.FAIL); - map.put("msgDesc","유효하지 않은 전화번호 형식입니다."); - } - - // 보조 전화번호 형식 검증 (있을 경우만 검증) - if (ValidationCheckUtil.emptyCheck(phoneNumber2) && !ValidationCheckUtil.phoneNumberCheck(phoneNumber2)) { - check = false; - map.put("msgCode", Constants.FAIL); - map.put("msgDesc","유효하지 않은 보조 전화번호 형식입니다."); - } - - - if(check == true){ - String muUserId = ("U").concat(String.valueOf(System.currentTimeMillis())); - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - Calendar c1 = Calendar.getInstance(); - String tDate = sdf.format(c1.getTime()); - paramMap.put("muUserId", muUserId); - paramMap.put("tId", tId); - paramMap.put("tDate", tDate); - cmmnDAO.insert("KioskSql.putUser", paramMap); - - map.put("msgCode", Constants.OK); - map.put("msgDesc", "등록되었습니다."); - } - }catch (Exception e) { - e.printStackTrace(); - throw e; - } - - log.debug("KioskServiceImpl putUser END"); - return map; - } - - /** - * 고객정보 조회 - * - * - * @param paramMap - * @return - * @throws Exception - */ - @Override - public HashMap getUser(HashMap paramMap) throws Exception { - log.debug("KioskServiceImpl getUser START"); - - HashMap map = new HashMap(); - Map usermap = new HashMap(); - if (paramMap.containsKey("muUserId")) { - usermap = cmmnDAO.select("KioskSql.getUser", paramMap); - } - map.put("msgCode", Constants.OK); - map.put("success","true"); - map.put("rows",usermap); - - log.debug("KioskServiceImpl getUser END"); - return map; - } - - /** - * 근무시간 조회 - * - * - * @param paramMap - * @return - * @throws Exception - */ - @Override - public HashMap getWorkTime(HashMap paramMap) throws Exception { - log.debug("KioskServiceImpl getWorkTime START"); - - HashMap map = new HashMap(); - List> hospitalListMap = cmmnDAO.selectList("KioskSql.getHospital", paramMap); - int hospitalListMapSize = hospitalListMap.size(); - - if(1 == hospitalListMapSize){ - // 병원 휴일 정보 조회 - paramMap.put("muHospitalId", hospitalListMap.get(0).get("muHospitalId")); - List> hospitalHolidayListMap = cmmnDAO.selectList("KioskSql.getHospitalHolidayList", paramMap); - - hospitalListMap.get(0).put("holidayRows", hospitalHolidayListMap); - - map.put("msgCode", Constants.OK); - map.put("success","true"); - map.put("rows",hospitalListMap); - } - else { - map.put("msgCode", Constants.FAIL); - map.put("msgDesc", "병원 정보가 없습니다."); - } - - log.debug("KioskServiceImpl getWorkTime END"); - return map; - } - - /** - * 진료시술 조회 - * - * - * @param paramMap - * @return - * @throws Exception - */ - @Override - public HashMap getTreatmentProcedureOptionList(HashMap paramMap) throws Exception { - log.debug("KioskServiceImpl getTreatmentProcedureOptionList START"); - HashMap map = new HashMap(); - List> listMap = new ArrayList<>(); - - // 정렬 - if(null==paramMap.get("treatmentProcedureDir")||("").equals(paramMap.get("treatmentProcedureDir"))){ - - } - else{ - String dir = String.valueOf(paramMap.get("treatmentProcedureDir")); - if(("A").equals(dir)){ - paramMap.put("treatmentProcedureDir", "DESC"); - } - else if(("B").equals(dir)){ - paramMap.put("treatmentProcedureDir", "ASC"); - } - else { - paramMap.put("treatmentProcedureDir", "DESC"); - } - } - - paramMap.put("useYn", "Y"); - System.out.println(paramMap); - int totalCount = cmmnDAO.selectCount("KioskSql.getTotalTreatmentProcedureOptionCount", paramMap); - if(0 < totalCount){ - listMap = cmmnDAO.selectList("KioskSql.getTreatmentProcedureOptionList", paramMap); - } - - map.put("msgCode", Constants.OK); - map.put("success","true"); - map.put("totalCount", totalCount); - map.put("rows",listMap); - log.debug("KioskServiceImpl getTreatmentProcedureOptionList END"); - return map; - } - - - /** - * 진료시술 조회 - * - * - * @param paramMap - * @return - * @throws Exception - */ - @Override - public HashMap getMemberDoctorConsultingOptionList(HashMap paramMap) throws Exception { - log.debug("KioskServiceImpl getMemberDoctorConsultingOptionList START"); - - // 선택 - log.debug("searchDate :"+paramMap.get("searchDate")); - log.debug("searchTime :"+paramMap.get("searchTime")); - - // 필수 - log.debug("muTreatmentProcedureId :"+paramMap.get("muTreatmentProcedureId")); - HashMap map = new HashMap(); - List> listMap = new ArrayList<>(); - - - String muTreatmentProcedureId = String.valueOf(paramMap.get("muTreatmentProcedureId")); - String muTreatmentProcedureIdList = ""; - - if(true != ValidationCheckUtil.emptyCheck(muTreatmentProcedureId)){ - map.put("msgCode", Constants.FAIL); - map.put("msgDesc","시술 정보가 없습니다."); - }else{ - String [] muTreatmentProcedureIdArr = muTreatmentProcedureId.split(","); - for(int i=0; i getMemberList(HashMap paramMap) throws Exception { - log.debug("KioskServiceImpl getMemberList START"); - HashMap map = new HashMap(); - List> listMap = new ArrayList>(); - - - // 정렬 - if(null==paramMap.get("memberDir")||("").equals(paramMap.get("memberDir"))){ - - } - else{ - String dir = String.valueOf(paramMap.get("memberDir")); - if(("A").equals(dir)){ - paramMap.put("memberDir", "DESC"); - } - else if(("B").equals(dir)){ - paramMap.put("memberDir", "ASC"); - } - else { - paramMap.put("memberDir", "DESC"); - } - } - - paramMap.put("useYn", "Y"); - - // 총 갯수 조회 - List> totalCountListMap = cmmnDAO.selectList("KioskSql.getMemberListCnt", paramMap); - int totalCount = Integer.parseInt(String.valueOf(totalCountListMap.get(0).get("totalcount"))); - - if(0 < totalCount) { - // 리스트 조회 - listMap = cmmnDAO.selectList("KioskSql.getMemberList", paramMap); - } - - map.put("msgCode", Constants.OK); - map.put("success","true"); - map.put("totalCount", totalCount); - map.put("rows",listMap); - - - log.debug("KioskServiceImpl getMemberList END"); - return map; - } - - /** - * 고객 - 1년간 진료내역 조회 (수납완료건) (List, option) - * - * - * @param paramMap - * @return - * @throws Exception - */ - @Override - public HashMap getReserveUserOptionList(HashMap paramMap) throws Exception { - log.debug("KioskServiceImpl getReserveUserOptionList START"); - HashMap map = new HashMap(); - List> listMap = new ArrayList>(); - - - // 총 갯수 조회 - List> totalCountListMap = cmmnDAO.selectList("KioskSql.getMemberListCnt", paramMap); - int totalCount = Integer.parseInt(String.valueOf(totalCountListMap.get(0).get("totalcount"))); - - if(0 < totalCount) { - // 리스트 조회 - listMap = cmmnDAO.selectList("KioskSql.getReserveUserOptionList", paramMap); - } - - map.put("msgCode", Constants.OK); - map.put("success","true"); - map.put("totalCount", totalCount); - map.put("rows",listMap); - - - log.debug("KioskServiceImpl getReserveUserOptionList END"); - return map; - } -} diff --git a/src/main/resources/mappers/crm/kiosk/KioskSql.xml b/src/main/resources/mappers/crm/kiosk/KioskMAP.xml similarity index 94% rename from src/main/resources/mappers/crm/kiosk/KioskSql.xml rename to src/main/resources/mappers/crm/kiosk/KioskMAP.xml index 29bcbb4..681cd83 100644 --- a/src/main/resources/mappers/crm/kiosk/KioskSql.xml +++ b/src/main/resources/mappers/crm/kiosk/KioskMAP.xml @@ -1,6 +1,6 @@ - + + + \ No newline at end of file diff --git a/src/main/resources/static/css/kiosk/new-patient.css b/src/main/resources/static/css/kiosk/new-patient.css index f581b1d..668f065 100644 --- a/src/main/resources/static/css/kiosk/new-patient.css +++ b/src/main/resources/static/css/kiosk/new-patient.css @@ -1,21 +1,20 @@ -/* - New Patient - Project Design System Applied - Font: Pretendard - Colors: Standard Project Blue (#1B66C9) & Gray (#494E53) - Layout: Compact 1080p Fit +/* +New Patient - Project Design System Applied + +Font: Pretendard + +Colors: Standard Project Blue (#1B66C9) & Gray (#494E53) +Layout: Compact 1080p Fit */ /* Font is loaded globally via head.html -> font.css, so just reference it */ :root { - --primary-color: #1B66C9; - /* Project Standard Blue */ + --primary-color: #1B66C9; /* Project Standard Blue */ --primary-hover: #1652a3; - --bg-color: #F8FBFF; - /* Project Standard Background */ + --bg-color: #F8FBFF; /* Project Standard Background */ --card-bg: #ffffff; - --text-main: #494E53; - /* Project Standard Text */ + --text-main: #494E53; /* Project Standard Text */ --text-sub: #777d85; --border-color: #DDE2E8; --input-bg: #FFFFFF; @@ -38,8 +37,7 @@ body { height: 100%; font-family: 'Pretendard', 'Noto Sans KR', sans-serif !important; background-color: var(--bg-color); - overflow-y: auto; - /* Prevent clip */ + overflow-y: auto; /* Prevent clip */ color: var(--text-main); } @@ -89,8 +87,7 @@ textarea { /* Grid Layout */ .form-grid-layout { display: grid; - grid-template-columns: repeat(3, 1fr); - /* Fixed 3 columns */ + grid-template-columns: repeat(3, 1fr); /* Fixed 3 columns */ gap: 20px; width: 100%; max-width: 1800px; @@ -99,8 +96,7 @@ textarea { /* Cards */ .form-section-card { background-color: var(--card-bg); - border-radius: 8px; - /* Border radius matching standard inputs often */ + border-radius: 8px; /* Border radius matching standard inputs often */ padding: 24px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03); border: 1px solid #EEF2F7; @@ -120,13 +116,59 @@ textarea { padding-bottom: 8px; } -/* Form Rows */ +/* Form Rows - 기본 세로 레이아웃 유지 */ .form-row { display: flex; flex-direction: column; gap: 6px; } +/* 체크박스 라인 - radio-group 스타일 적용 */ +.checkbox-row .checkbox-line { + display: flex; + align-items: center; + gap: 12px; +} + +.checkbox-line { + display: flex; + align-items: center; + gap: 12px; + background-color: var(--input-bg); + border-radius: 4px; + padding: 8px 12px; + border: 1px solid var(--border-color); + width: 100%; +} + +.checkbox-line input[type="checkbox"] { + width: 18px; + height: 18px; + margin: 0; + flex-shrink: 0; + accent-color: var(--primary-color); + cursor: pointer; +} + +.checkbox-line p { + margin: 0; + font-size: 14px; + color: var(--text-main); + cursor: pointer; + flex: 1; + text-decoration: underline; + padding-top: 1px; +} + +.checkbox-line p:hover { + color: var(--primary-color); +} + +.checkbox-line:focus-within { + border-color: var(--primary-color); + box-shadow: 0 0 0 2px rgba(27, 102, 201, 0.1); +} + .form-row label { font-size: 13px; font-weight: 600; @@ -149,7 +191,6 @@ input[type="password"], border: 1px solid var(--border-color); background-color: var(--input-bg); border-radius: 4px; - /* Standard radius */ padding: 0 12px; font-size: 14px; color: var(--text-main); @@ -171,8 +212,8 @@ input::placeholder { } /* =============================================== - Choices.js Custom Design - =============================================== */ +Choices.js Custom Design +=============================================== */ .choices { margin-bottom: 0; font-family: 'Pretendard', sans-serif; @@ -289,7 +330,6 @@ input::placeholder { align-items: center; background-color: var(--input-bg); border-radius: 4px; - /* Standard radius */ padding: 0 12px; border: 1px solid var(--border-color); } @@ -347,126 +387,6 @@ textarea:focus { text-align: center; } -/* Footer */ -.page-footer { - height: var(--footer-height); - background-color: var(--card-bg); - border-top: 1px solid var(--border-color); - display: flex; - justify-content: flex-end; - align-items: center; - padding: 0 40px; - gap: 12px; -} - -/* Buttons - Matching Global Design */ -button { - height: 44px; - padding: 0 24px; - border-radius: 4px; - font-size: 14px; - font-weight: 500; - cursor: pointer; - border: none; - font-family: 'Pretendard', sans-serif; - transition: background-color 0.2s; -} - -.cancel_btn { - background-color: #F0F2F5; - /* Light Gray */ - color: #494E53; -} - -.cancel_btn:hover { - background-color: #E6ECF4 !important; - /* From common.css */ -} - -.registration_bth { - background-color: var(--primary-color); - color: #fff; -} - -.registration_bth:hover { - background-color: #1652a3 !important; -} - -/* =============================================== - Flatpickr Custom Design - =============================================== */ -.flatpickr-calendar { - font-family: 'Pretendard', sans-serif !important; - border-radius: 12px !important; - box-shadow: 0 10px 40px rgba(0, 0, 0, 0.12) !important; - border: 1px solid #E5E7EB !important; - width: 320px !important; -} - -.flatpickr-day { - border-radius: 8px !important; -} - -.flatpickr-day.selected, -.flatpickr-day.startRange, -.flatpickr-day.endRange, -.flatpickr-day.selected.inRange, -.flatpickr-day.startRange.inRange, -.flatpickr-day.endRange.inRange, -.flatpickr-day.selected:focus, -.flatpickr-day.startRange:focus, -.flatpickr-day.endRange:focus, -.flatpickr-day.selected:hover, -.flatpickr-day.startRange:hover, -.flatpickr-day.endRange:hover, -.flatpickr-day.selected.prevMonthDay, -.flatpickr-day.startRange.prevMonthDay, -.flatpickr-day.endRange.prevMonthDay, -.flatpickr-day.selected.nextMonthDay, -.flatpickr-day.startRange.nextMonthDay, -.flatpickr-day.endRange.nextMonthDay { - background: var(--primary-color) !important; - border-color: var(--primary-color) !important; -} - -.flatpickr-months .flatpickr-month { - background: transparent !important; - color: var(--text-main) !important; - fill: var(--text-main) !important; - height: 40px; - padding-top: 10px; -} - -.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month { - background-color: #fff !important; - color: var(--text-main) !important; -} - -.flatpickr-weekdays { - background: transparent !important; -} - -.flatpickr-weekday { - color: #9CA3AF !important; - font-weight: 500 !important; - font-size: 13px !important; -} - -.flatpickr-current-month { - font-size: 110% !important; - padding-top: 10px; -} - -.flatpickr-day.today { - border-color: var(--border-color) !important; -} - -.flatpickr-day.today:hover { - background: #EBF5FF !important; - border-color: #EBF5FF !important; - color: var(--text-main) !important; -} - /* Helper classes */ .date-input-wrapper { position: relative; @@ -477,7 +397,6 @@ button { .date-input-wrapper input { flex: 1; padding-right: 70px; - /* Space for age text and icon */ } .date-input-wrapper img { @@ -515,7 +434,6 @@ button { .dropdown-menu li:hover { background-color: #ebf3fd; - /* Project hover color */ } .dropdown-menu li.selected { @@ -524,6 +442,49 @@ button { font-weight: 600; } +/* Footer */ +.page-footer { + height: var(--footer-height); + background-color: var(--card-bg); + border-top: 1px solid var(--border-color); + display: flex; + justify-content: flex-end; + align-items: center; + padding: 0 40px; + gap: 12px; +} + +/* Buttons - Matching Global Design */ +button { + height: 44px; + padding: 0 24px; + border-radius: 4px; + font-size: 14px; + font-weight: 500; + cursor: pointer; + border: none; + font-family: 'Pretendard', sans-serif; + transition: background-color 0.2s; +} + +.cancel_btn { + background-color: #F0F2F5; + color: #494E53; +} + +.cancel_btn:hover { + background-color: #E6ECF4 !important; +} + +.registration_bth { + background-color: var(--primary-color); + color: #fff; +} + +.registration_bth:hover { + background-color: #1652a3 !important; +} + /* Responsive Mobile */ @media (max-width: 1199px) { .form-grid-layout { @@ -533,5 +494,141 @@ button { .page-body { height: auto; + padding: 16px; } +} + + + +/* 동의서 링크 스타일 */ +.consent-link { + color: #1a73e8; + text-decoration: none; + margin-left: 8px; +} + +.consent-link:hover { + text-decoration: underline; + color: #1557b0; +} + +/* 체크박스 라인 레이아웃 */ +.checkbox-line { + display: flex; + align-items: center; + gap: 8px; +} + +/* 로딩 스타일 */ +.loading { + padding: 50px; + text-align: center; + font-size: 16px; + color: #666; +} + +/* 동의서 전체 박스 스타일링 */ +.consent_box { + font-family: 'Pretendard', 'Malgun Gothic', sans-serif; + color: #333; + line-height: 1.6; + padding: 10px; + max-width: 100%; +} + +/* 제목 스타일 */ +.consent_title { + font-size: 24px; + font-weight: 700; + text-align: center; + margin-bottom: 25px; + color: #000; + border-bottom: 2px solid #333; + padding-bottom: 15px; +} + +/* 중앙 강조 문구 */ +.txt_center { + text-align: center; + background: #f8f9fa; + padding: 15px; + border-radius: 8px; + margin-bottom: 25px; + font-size: 0.95em; + word-break: keep-all; +} + +/* 항목별 제목 (1, 2, 3...) */ +.article { + font-size: 1.1em; + font-weight: 600; + margin-top: 20px; + margin-bottom: 8px; + color: #d32f2f; /* 포인트 컬러 */ +} + +/* 본문 텍스트 */ +.consent_box p { + margin-bottom: 10px; + font-size: 15px; + text-align: justify; +} + +/* 서명 영역 박스 */ +.sign_box { + margin-top: 40px; + padding: 30px; + border: 1px solid #eee; + background-color: #fdfdfd; + text-align: center; +} + +.sign_box .date { + font-size: 18px; + margin: 0 10px; +} + +.sign_box .last { + margin-top: 25px; + font-size: 18px; + display: flex; + justify-content: center; + align-items: center; + gap: 10px; +} + +/* 서명 패드 스타일 */ +.signPad { + width: 200px; + height: 80px; + border-bottom: 2px solid #333; + display: inline-block; + position: relative; + background: #fff; +} + +.signPad canvas { + width: 100%; + height: 100%; +} + +/* 로고 영역 */ +.logo_box { + margin-top: 30px; + text-align: center; + opacity: 0.7; +} + +.logo_box img { + height: 24px; + vertical-align: middle; + margin-right: 5px; +} + +.logo_box p { + font-size: 18px; + font-weight: 600; + display: flex; + justify-content: center; + align-items: center; } \ No newline at end of file diff --git a/src/main/resources/static/js/kiosk/new-patient.js b/src/main/resources/static/js/kiosk/new-patient.js index 2a96a6e..db40abe 100644 --- a/src/main/resources/static/js/kiosk/new-patient.js +++ b/src/main/resources/static/js/kiosk/new-patient.js @@ -6,543 +6,225 @@ this.choicesInstances = new Map(); this.data = { nationality: '', - nationalityCode: '', - userType: '', - visitPath: '', - treatment: '' + nationalityCode: '', // 선택된 국적 코드 저장용 + visitPath: '' }; - this.datepickerInstance = null; this.isInitialized = false; - this.userModalRetryCount = 0; + this.KOREA_CODE = 'C202404180007'; + this.allowSystemCheck = false; } init() { if (this.isInitialized) return; - - if (!this.validateDependencies()) { + if (!this.validateDependencies() || !this.validateDOM()) { this.scheduleRetry(); return; } - - if (!this.validateDOM()) { - this.scheduleRetry(); - return; - } - - this.initializeComponents(); - this.isInitialized = true; - console.log('✅ NewPatientPage 초기화 완료'); - } - - validateDependencies() { - return !!($ && window.Choices); - } - - validateDOM() { - const requiredElements = [ - '#selectNationality', '#selectUserType', '#selectTreatment', - '#selectChannel', '#selectIdentification', '#newPatientForm' - ]; - return requiredElements.every(selector => document.querySelector(selector)); - } - - scheduleRetry() { - setTimeout(() => this.init(), 150); - } - - initializeComponents() { this.initChoices(); - this.initUserIntroModal(); // 내장 모달 초기화 this.loadCommonCategories(); - this.initDatepicker(); this.bindEvents(); + this.isInitialized = true; } - initChoices() { - const choicesConfig = { - searchEnabled: true, - searchPlaceholderValue: '검색...', - noResultsText: '결과 없음', - noChoicesText: '선택 가능한 항목이 없습니다', - itemSelectText: '선택', - removeItemButton: false, - shouldSort: false, - placeholderValue: '선택하세요' - }; + validateDependencies() { return !!($ && window.Choices); } + validateDOM() { return !!(document.querySelector('#selectNationality')); } + scheduleRetry() { setTimeout(() => this.init(), 150); } - const selectors = { - nationality: { id: '#selectNationality', key: 'nationality', placeholder: '국적 선택' }, - userType: { id: '#selectUserType', key: 'userType', placeholder: '고객구분 선택' }, - treatment: { id: '#selectTreatment', key: 'treatment', placeholder: '관심진료 선택' }, - channel: { id: '#selectChannel', key: 'channel', placeholder: '방문경로 선택' }, - identification: { - id: '#selectIdentification', - key: 'identification', - config: { searchEnabled: false, placeholder: false } - } - }; + bindEvents() { + const $rrn1 = $('input[name="modalUserRrn1"]'); + const $rrn2 = $('input[name="modalUserRrn2"]'); - Object.entries(selectors).forEach(([key, config]) => { - try { - const instance = new Choices(config.id, { - ...choicesConfig, - placeholder: true, - placeholderValue: config.placeholder, - ...(config.config || {}) - }); - this.choicesInstances.set(config.key, instance); - } catch (e) { - console.error(`Choices 초기화 실패 (${key}):`, e); + $rrn2.on('keyup input', (e) => { + const rrn1Val = $rrn1.val(); + const rrn2Val = $(e.target).val(); + if (rrn1Val.length === 6 && rrn2Val.length >= 1) { + this.handleRrnInput(rrn1Val, rrn2Val.charAt(0)); } }); - this.toggleForeignerBox(false); - this.bindChoicesEvents(); - } - - // 🔥 내장형 userIntroSelectModal - initUserIntroModal() { - window.userIntroSelectModal = { - callback: null, - dataList: null, - - popup(callback) { - this.callback = callback; - this.initModal(); - setTimeout(() => $('#userIntroSelectModal').modal('show'), 200); - }, - - initModal() { - if ($('#userIntroSelectModal').length) { - $('#userIntroSelectModal').remove(); - } - $('body').append(this.htmlTemplate); - this.bindModalEvents(); - }, - - bindModalEvents() { - $('#userIntroSelectModal .btnCancle').off().on('click', () => this.close()); - $('#searchIntroUserBtn').off().on('click', () => this.search()); - $('#introUserSearchKeyword').off('keypress').on('keypress', (e) => { - if (e.key === 'Enter') this.search(); - }); - }, - - search() { - const keyword = $('#introUserSearchKeyword').val()?.trim(); - if (keyword.length < 2) { - this.showAlert('검색어는 2자 이상 입력하세요.'); - return; - } - - $.ajax({ - url: '/kiosk/getUserOptionList.do', - method: 'POST', - data: { userSearchKeywordParam: keyword }, - dataType: 'json', - success: (data) => { - if (data.msgCode === '0') { - this.dataList = data.rows || []; - this.renderResults(this.dataList); - } else { - this.showAlert('검색 결과가 없습니다.'); - } - }, - error: () => this.showAlert('검색 중 오류가 발생했습니다.') - }); - }, - - renderResults(users) { - const $tbody = $('#userIntroSelectModal tbody'); - $tbody.empty(); - - if (users.length === 0) { - $tbody.html('검색결과가 없습니다.'); - return; - } - - users.forEach((user, index) => { - const $row = $(` - - ${user.username || ''} - ${this.formatDate(user.birthday)} - ${user.gender || ''} - ${this.formatPhone(user.phonenumber)} - ${user.lastvisitdate || ''} - - `); - $tbody.append($row); - }); - - $('.user-row').off('click').on('click', (e) => { - const index = $(e.currentTarget).data('index'); - this.selectUser(index); - }); - }, - - selectUser(index) { - if (this.dataList?.[index]) { - this.callback(this.dataList[index]); - } - this.close(); - }, - - formatDate(dateStr) { - if (!dateStr) return ''; - try { - const date = new Date(dateStr.match(/\d{4}-\d{2}-\d{2}/)?.[0]); - if (isNaN(date)) return ''; - return date.toISOString().slice(0, 10).replace(/-/g, '.'); - } catch { - return ''; - } - }, - - formatPhone(phone) { - if (!phone) return ''; - return phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3'); - }, - - showAlert(message) { - if (window.modalEvent) { - window.modalEvent.warning('', message); - } else { - alert(message); - } - }, - - close() { - $('#userIntroSelectModal').modal('hide'); - setTimeout(() => $('#userIntroSelectModal').remove(), 500); - }, - - htmlTemplate: ` - ` - }; - } - - bindChoicesEvents() { - const idSelect = document.getElementById('selectIdentification'); - if (idSelect) { - idSelect.addEventListener('change', (e) => { - const isPassport = e.target.value === 'pno'; - $('.passport_number_box').toggle(isPassport); - $('.foreigner_number_box').toggle(!isPassport); + const nationalityInstance = this.choicesInstances.get('nationality'); + if (nationalityInstance) { + const element = nationalityInstance.passedElement.element; + element.addEventListener('change', (event) => { + this.data.nationalityCode = event.detail.value; // 국적 코드 업데이트 + this.updateUIByNationality(event.detail.value); }); } + + $('#modalAddress, #btnSearchAddress').on('click', () => { + this.openAddrSearch(); + }); - const eventMap = { - 'selectNationality': (e) => this.handleNationalityChange(e), - 'selectUserType': (e) => this.data.userType = e.target.value, - 'selectTreatment': (e) => this.data.treatment = e.target.value, - 'selectChannel': (e) => this.handleChannelChange(e) - }; - - Object.entries(eventMap).forEach(([id, handler]) => { - const element = document.getElementById(id); - if (element) element.addEventListener('change', handler); + // 체크박스 직접 클릭 방지 및 안내 문구 출력 + const consentIds = ['#agreePrivacy', '#agreeProcedure', '#agreeTerms', '#refusePhoto']; + $(consentIds.join(', ')).on('click', (e) => { + if (!this.allowSystemCheck) { + e.preventDefault(); + alert('동의서 보기 버튼을 클릭하여 내용을 확인하신 후 [동의함] 버튼을 눌러주세요.'); + } }); } - handleNationalityChange(event) { - this.data.nationalityCode = event.target.value; - const selectedText = event.target.options[event.target.selectedIndex]?.text || ''; - const isKorean = selectedText.includes('대한민국') || event.target.value.includes('KR') || event.target.value === 'Local'; - this.data.nationality = isKorean ? 'Local' : 'Foreigner'; - this.toggleForeignerBox(!isKorean); - } - - handleChannelChange(event) { - this.data.visitPath = event.target.value; - const selectedText = event.target.options[event.target.selectedIndex]?.text || ''; - if (selectedText.includes('소개')) { - $('.searchIntroUser').show(); + updateUIByNationality(selectedCode) { + const $rrnLabel = $('label[for="modalUserRrn1"], .form-row:has(input[name="modalUserRrn1"]) label').first(); + const $passportBox = $('.foreigner_box'); + if (selectedCode === this.KOREA_CODE) { + $rrnLabel.text('주민등록번호'); + $passportBox.hide(); } else { - $('input[name="modalRecommendId"]').val('').removeAttr('data-userid'); + $rrnLabel.text('외국인등록번호'); + $passportBox.show(); } } - toggleForeignerBox(show) { - $('.foreigner_box').toggle(show); - $('.local_box').toggle(!show); - } + handleRrnInput(rrn1, genderDigitStr) { + const genderDigit = parseInt(genderDigitStr, 10); + const isMale = [1, 3, 5, 7].includes(genderDigit); + if (isMale) $('#genderM').prop('checked', true); + else $('#genderF').prop('checked', true); - loadCommonCategories() { - const categories = [ - { code: 'C202404110001', key: 'nationality' }, - { code: 'C202404110002', key: 'userType' }, - { code: 'C202404110003', key: 'channel' } - ]; - categories.forEach(({ code, key }) => this.fetchCategory(code, key)); - this.fetchTreatmentList(); - } - - fetchCategory(code, choicesKey) { - $.ajax({ - url: '/kiosk/getCategoryItem.do', - type: 'POST', - data: { categoryCode: code }, - success: (data) => { - if (data.rows?.length > 0) { - const choices = data.rows.map(item => ({ - value: item.categoryitemcode || item.commonCode || '', - label: item.categoryitemname || item.codeName || '' - })); - const instance = this.choicesInstances.get(choicesKey); - if (instance) { - instance.setChoices(choices, 'value', 'label', true); - if (choicesKey === 'nationality') { - const defaultItem = choices.find(c => c.label.includes('대한민국')); - if (defaultItem) { - instance.setChoiceByValue(defaultItem.value); - this.data.nationalityCode = defaultItem.value; - this.data.nationality = 'Local'; - } - } - } - } - }, - error: (xhr) => console.error(`카테고리 조회 실패 (${code}):`, xhr.responseText) - }); - } - - fetchTreatmentList() { - $.ajax({ - url: '/kiosk/getTreatmentOptionList.do', - type: 'POST', - success: (data) => { - if (data.rows?.length > 0) { - const choices = data.rows.map(item => ({ - value: item.mutreatmentid || '', - label: item.treatmentname || '' - })); - const instance = this.choicesInstances.get('treatment'); - if (instance) instance.setChoices(choices, 'value', 'label', true); - } - } - }); - } - - initDatepicker() { - const birthdayInput = document.querySelector('input[name="modalBirthday"]'); - if (!birthdayInput) return; - - this.datepickerInstance = flatpickr(birthdayInput, { - locale: "ko", - dateFormat: "Y-m-d", - maxDate: "today", - disableMobile: true, - onChange: (selectedDates) => { - if (selectedDates.length > 0) { - this.calculateAgeAndRrn(selectedDates[0]); - } - } - }); - - $('.date-input-wrapper').off('click.datepicker').on('click.datepicker', (e) => { - e.stopPropagation(); - this.datepickerInstance?.open(); - }); - } - - calculateAgeAndRrn(birthDate) { + let yearPrefix = [1, 2, 5, 6].includes(genderDigit) ? '19' : '20'; + const year = yearPrefix + rrn1.substring(0, 2); + const month = rrn1.substring(2, 4); + const day = rrn1.substring(4, 6); + $('#modalBirthday').val(`${year}${month}${day}`); + const today = new Date(); - let age = today.getFullYear() - birthDate.getFullYear(); + let age = today.getFullYear() - parseInt(year); + const birthDate = new Date(year, month - 1, day); const m = today.getMonth() - birthDate.getMonth(); if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) age--; $('.txtAge').text(`만 ${age}세`); - - const yy = String(birthDate.getFullYear()).substring(2); - const mm = String(birthDate.getMonth() + 1).padStart(2, '0'); - const dd = String(birthDate.getDate()).padStart(2, '0'); - $('input[name="modalUserRrn1"]').val(`${yy}${mm}${dd}`); } - bindEvents() { - $('.cancel_btn').off('click.newPatient').on('click.newPatient', () => history.back()); - $('.registration_bth').off('click.newPatient').on('click.newPatient', () => this.save()); - - $('input[name="modalPhoneNumber"], input[name="modalPhoneNumber2"]') - .off('input.phoneFormat').on('input.phoneFormat', function() { - this.value = this.value.replace(/[^0-9]/g, '') - .replace(/(\d{3})(\d{3,4})(\d{4})/, '$1-$2-$3').substring(0, 13); - }); - - $('.searchIntroUser').off('click.newPatient').on('click.newPatient', () => { - window.userIntroSelectModal?.popup((obj) => { - $('input[name="modalRecommendId"]').val(obj.username || '').attr('data-userid', obj.muuserid || ''); - }); + initChoices() { + const choicesConfig = { searchEnabled: true, itemSelectText: '', shouldSort: false, placeholderValue: '선택하세요' }; + ['nationality', 'channel'].forEach(key => { + const selector = key === 'nationality' ? '#selectNationality' : '#selectChannel'; + this.choicesInstances.set(key, new Choices(selector, choicesConfig)); }); } - async save() { - const formData = this.collectFormData(); - if (!this.validateForm(formData)) return; - - try { - const response = await this.submitForm(formData); - this.handleSaveResponse(response); - } catch (error) { - this.showError('통신 중 오류가 발생했습니다.'); - } + loadCommonCategories() { + const categories = [{ code: 'C202404110001', key: 'nationality' }, { code: 'C202404110003', key: 'channel' }]; + categories.forEach(({ code, key }) => this.fetchCategory(code, key)); } - collectFormData() { - return { - userName: $('input[name="modalUserName"]').val()?.trim() || '', - phone: $('input[name="modalPhoneNumber"]').val().replace(/-/g, '') || '', - phone2: $('input[name="modalPhoneNumber2"]').val().replace(/-/g, '') || '', - birthday: $('input[name="modalBirthday"]').val() || '', - gender: $('input[name="modalGender"]:checked').val() || '', - smsYn: $('input[name="modalSmsYn"]:checked').val() || '', - refusePhoto: $('input[name="modalRefusePhotoYn"]:checked').val() || '', - rrn1: $('input[name="modalUserRrn1"]').val() || '', - rrn2: $('input[name="modalUserRrn2"]').val() || '', - pno: $('input[name="modalUserPno"]').val() || '', - arc1: $('input[name="modalUserArc1"]').val() || '', - arc2: $('input[name="modalUserArc2"]').val() || '', - memo: $('textarea[name="modalMemo"]').val() || '', - etc: $('textarea[name="modalEtc"]').val() || '', - introUserId: $('input[name="modalRecommendId"]').attr('data-userid') || '', - ...this.data - }; - } - - validateForm(data) { - const errors = { - userName: !data.userName && '고객명을 입력해주세요.', - phone: (!data.phone || data.phone.length < 10) && '올바른 연락처를 입력해주세요.', - privacy: !$('#agree_privacy').is(':checked') && '개인정보 수집 이용에 동의해야 합니다.' - }; - - const errorMsg = Object.values(errors).find(Boolean); - if (errorMsg && window.modalEvent) { - window.modalEvent.warning('알림', errorMsg); - return false; - } - return true; - } - - submitForm(data) { - return new Promise((resolve, reject) => { - $.ajax({ - url: '/kiosk/putUser.do', - type: 'POST', - data: { - nationality: data.nationality, - nationalityCode: data.nationalityCode, - userName: data.userName, - phoneNumber: data.phone, - phoneNumber2: data.phone2, - birthday: data.birthday, - gender: data.gender, - userRrn1: data.rrn1, - userRrn2: data.rrn2, - userPno: data.pno, - userArc1: data.arc1, - userArc2: data.arc2, - userTypeCode: data.userType, - channelCode: data.visitPath, - muGroupId: data.treatment, - introUserId: data.introUserId, - memo: data.memo, - etc: data.etc, - smsYn: data.smsYn, - refusePhotoYn: data.refusePhoto - }, - success: resolve, - error: reject - }); - }); - } - - handleSaveResponse(data) { - if (data.msgCode === '0') { - if (window.modalEvent) { - window.modalEvent.success('등록 성공', '신규 고객 등록이 완료되었습니다.', () => { - location.href = '/kiosk/kiosk_main'; - }); - } else { - location.href = '/kiosk/kiosk_main'; + fetchCategory(code, choicesKey) { + $.post('/kiosk/getCategoryItem.do', { categoryCode: code }, (data) => { + if (data.rows?.length > 0) { + const choices = data.rows.map(item => ({ value: item.categoryItemCode || item.commonCode, label: item.categoryItemName || item.codeName })); + const instance = this.choicesInstances.get(choicesKey); + if (instance) { + instance.setChoices(choices, 'value', 'label', true); + if (choicesKey === 'nationality') { + instance.setChoiceByValue(this.KOREA_CODE); + this.data.nationalityCode = this.KOREA_CODE; // 초기 국적 설정 + this.updateUIByNationality(this.KOREA_CODE); + } + } } - } else if (window.modalEvent) { - window.modalEvent.danger('오류', data.msgDesc || '등록에 실패했습니다.'); - } - } - - showError(message) { - if (window.modalEvent) { - window.modalEvent.danger('오류', message); - } else { - alert(message); - } - } - - destroy() { - this.choicesInstances.forEach(instance => instance?.destroy()); - this.choicesInstances.clear(); - this.datepickerInstance?.destroy(); - $('#userIntroSelectModal')?.remove(); - this.isInitialized = false; + }); } + + openAddrSearch() { + new daum.Postcode({ + oncomplete: (data) => { + let addr = data.userSelectedType === 'R' ? data.roadAddress : data.jibunAddress; + document.getElementById("modalAddress").value = addr; + document.getElementById("modalAddressDetail").focus(); + } + }).open(); + } } - // 싱글톤 인스턴스 관리 - let instance = null; - window.newPatientPage = { - init() { - if (instance) return; - instance = new NewPatientPage(); - instance.init(); - }, - destroy() { - instance?.destroy(); - instance = null; - }, - getInstance() { - return instance; - } - }; + $(document).ready(() => { + const pageApp = new NewPatientPage(); + window.newPatientPage = pageApp; + pageApp.init(); + + $('.consent-link').on('click', function(e) { + e.preventDefault(); + openConsentDialog($(this).data('type')); + }); - // 자동 초기화 - const init = () => window.newPatientPage.init(); - - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', init); - } else { - setTimeout(init, 50); - } - window.addEventListener('load', () => setTimeout(init, 100)); -})(); + function openConsentDialog(type) { + if ($('#dynamicConsentDialog').length) $('#dynamicConsentDialog').dialog('destroy').remove(); + + const $dialog = $('
'); + let isAgreed = false; + + $dialog.dialog({ + modal: true, width: 800, height: 700, + title: getDialogTitle(type), + resizable: false, draggable: true, + buttons: [ + { + text: "동의함", class: "registration_bth", + click: function() { + isAgreed = true; + pageApp.allowSystemCheck = true; + $('#' + type).prop('checked', true); + pageApp.allowSystemCheck = false; + $(this).dialog('close'); + } + }, + { text: "닫기", class: "cancel_btn", click: function() { $(this).dialog('close'); } } + ], + close: function() { + if (!isAgreed) { + pageApp.allowSystemCheck = true; + $('#' + type).prop('checked', false); + pageApp.allowSystemCheck = false; + } + $(this).dialog('destroy').remove(); + } + }); + + $dialog.html('
내용을 불러오는 중입니다...
'); + + $.ajax({ + url: '/kiosk/getConsentForm.do', + type: 'POST', + contentType: 'application/json', + data: JSON.stringify({ + consentFormTypeCd: getConsentFormTypeCd(type), // 코드로 변환하여 전송 + nationalCd: pageApp.data.nationalityCode // 현재 국적 코드 전송 + }), + success: function(res) { + if(res && res.consentFormContent) { + $dialog.html(res.consentFormContent); + } else { + $dialog.html('
등록된 동의서 내용이 없습니다.
'); + } + }, + error: function() { + $dialog.html('
서버 통신 중 오류가 발생했습니다.
'); + } + }); + } + + function getDialogTitle(type) { + const titles = { + 'agreePrivacy': '개인정보 수집 및 이용안내', + 'agreeProcedure': '시술동의서', + 'agreeTerms': '이용약관', + 'refusePhoto': '사진촬영거부동의서' + }; + return titles[type] || '동의서'; + } + + /** + * type 명칭을 DB consentFormTypeCd 코드로 변환 + */ + function getConsentFormTypeCd(type) { + const typeMapping = { + 'agreePrivacy': 'C202601180001', + 'agreeProcedure': 'C202601180002', + 'agreeTerms': 'C202601180003', + 'refusePhoto': 'C202601180004' + }; + return typeMapping[type] || ''; + } + }); +})(); \ No newline at end of file diff --git a/src/main/resources/static/js/kiosk/new-patient2.js b/src/main/resources/static/js/kiosk/new-patient2.js new file mode 100644 index 0000000..2a96a6e --- /dev/null +++ b/src/main/resources/static/js/kiosk/new-patient2.js @@ -0,0 +1,548 @@ +(function() { + 'use strict'; + + class NewPatientPage { + constructor() { + this.choicesInstances = new Map(); + this.data = { + nationality: '', + nationalityCode: '', + userType: '', + visitPath: '', + treatment: '' + }; + this.datepickerInstance = null; + this.isInitialized = false; + this.userModalRetryCount = 0; + } + + init() { + if (this.isInitialized) return; + + if (!this.validateDependencies()) { + this.scheduleRetry(); + return; + } + + if (!this.validateDOM()) { + this.scheduleRetry(); + return; + } + + this.initializeComponents(); + this.isInitialized = true; + console.log('✅ NewPatientPage 초기화 완료'); + } + + validateDependencies() { + return !!($ && window.Choices); + } + + validateDOM() { + const requiredElements = [ + '#selectNationality', '#selectUserType', '#selectTreatment', + '#selectChannel', '#selectIdentification', '#newPatientForm' + ]; + return requiredElements.every(selector => document.querySelector(selector)); + } + + scheduleRetry() { + setTimeout(() => this.init(), 150); + } + + initializeComponents() { + this.initChoices(); + this.initUserIntroModal(); // 내장 모달 초기화 + this.loadCommonCategories(); + this.initDatepicker(); + this.bindEvents(); + } + + initChoices() { + const choicesConfig = { + searchEnabled: true, + searchPlaceholderValue: '검색...', + noResultsText: '결과 없음', + noChoicesText: '선택 가능한 항목이 없습니다', + itemSelectText: '선택', + removeItemButton: false, + shouldSort: false, + placeholderValue: '선택하세요' + }; + + const selectors = { + nationality: { id: '#selectNationality', key: 'nationality', placeholder: '국적 선택' }, + userType: { id: '#selectUserType', key: 'userType', placeholder: '고객구분 선택' }, + treatment: { id: '#selectTreatment', key: 'treatment', placeholder: '관심진료 선택' }, + channel: { id: '#selectChannel', key: 'channel', placeholder: '방문경로 선택' }, + identification: { + id: '#selectIdentification', + key: 'identification', + config: { searchEnabled: false, placeholder: false } + } + }; + + Object.entries(selectors).forEach(([key, config]) => { + try { + const instance = new Choices(config.id, { + ...choicesConfig, + placeholder: true, + placeholderValue: config.placeholder, + ...(config.config || {}) + }); + this.choicesInstances.set(config.key, instance); + } catch (e) { + console.error(`Choices 초기화 실패 (${key}):`, e); + } + }); + + this.toggleForeignerBox(false); + this.bindChoicesEvents(); + } + + // 🔥 내장형 userIntroSelectModal + initUserIntroModal() { + window.userIntroSelectModal = { + callback: null, + dataList: null, + + popup(callback) { + this.callback = callback; + this.initModal(); + setTimeout(() => $('#userIntroSelectModal').modal('show'), 200); + }, + + initModal() { + if ($('#userIntroSelectModal').length) { + $('#userIntroSelectModal').remove(); + } + $('body').append(this.htmlTemplate); + this.bindModalEvents(); + }, + + bindModalEvents() { + $('#userIntroSelectModal .btnCancle').off().on('click', () => this.close()); + $('#searchIntroUserBtn').off().on('click', () => this.search()); + $('#introUserSearchKeyword').off('keypress').on('keypress', (e) => { + if (e.key === 'Enter') this.search(); + }); + }, + + search() { + const keyword = $('#introUserSearchKeyword').val()?.trim(); + if (keyword.length < 2) { + this.showAlert('검색어는 2자 이상 입력하세요.'); + return; + } + + $.ajax({ + url: '/kiosk/getUserOptionList.do', + method: 'POST', + data: { userSearchKeywordParam: keyword }, + dataType: 'json', + success: (data) => { + if (data.msgCode === '0') { + this.dataList = data.rows || []; + this.renderResults(this.dataList); + } else { + this.showAlert('검색 결과가 없습니다.'); + } + }, + error: () => this.showAlert('검색 중 오류가 발생했습니다.') + }); + }, + + renderResults(users) { + const $tbody = $('#userIntroSelectModal tbody'); + $tbody.empty(); + + if (users.length === 0) { + $tbody.html('검색결과가 없습니다.'); + return; + } + + users.forEach((user, index) => { + const $row = $(` + + ${user.username || ''} + ${this.formatDate(user.birthday)} + ${user.gender || ''} + ${this.formatPhone(user.phonenumber)} + ${user.lastvisitdate || ''} + + `); + $tbody.append($row); + }); + + $('.user-row').off('click').on('click', (e) => { + const index = $(e.currentTarget).data('index'); + this.selectUser(index); + }); + }, + + selectUser(index) { + if (this.dataList?.[index]) { + this.callback(this.dataList[index]); + } + this.close(); + }, + + formatDate(dateStr) { + if (!dateStr) return ''; + try { + const date = new Date(dateStr.match(/\d{4}-\d{2}-\d{2}/)?.[0]); + if (isNaN(date)) return ''; + return date.toISOString().slice(0, 10).replace(/-/g, '.'); + } catch { + return ''; + } + }, + + formatPhone(phone) { + if (!phone) return ''; + return phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3'); + }, + + showAlert(message) { + if (window.modalEvent) { + window.modalEvent.warning('', message); + } else { + alert(message); + } + }, + + close() { + $('#userIntroSelectModal').modal('hide'); + setTimeout(() => $('#userIntroSelectModal').remove(), 500); + }, + + htmlTemplate: ` + ` + }; + } + + bindChoicesEvents() { + const idSelect = document.getElementById('selectIdentification'); + if (idSelect) { + idSelect.addEventListener('change', (e) => { + const isPassport = e.target.value === 'pno'; + $('.passport_number_box').toggle(isPassport); + $('.foreigner_number_box').toggle(!isPassport); + }); + } + + const eventMap = { + 'selectNationality': (e) => this.handleNationalityChange(e), + 'selectUserType': (e) => this.data.userType = e.target.value, + 'selectTreatment': (e) => this.data.treatment = e.target.value, + 'selectChannel': (e) => this.handleChannelChange(e) + }; + + Object.entries(eventMap).forEach(([id, handler]) => { + const element = document.getElementById(id); + if (element) element.addEventListener('change', handler); + }); + } + + handleNationalityChange(event) { + this.data.nationalityCode = event.target.value; + const selectedText = event.target.options[event.target.selectedIndex]?.text || ''; + const isKorean = selectedText.includes('대한민국') || event.target.value.includes('KR') || event.target.value === 'Local'; + this.data.nationality = isKorean ? 'Local' : 'Foreigner'; + this.toggleForeignerBox(!isKorean); + } + + handleChannelChange(event) { + this.data.visitPath = event.target.value; + const selectedText = event.target.options[event.target.selectedIndex]?.text || ''; + if (selectedText.includes('소개')) { + $('.searchIntroUser').show(); + } else { + $('input[name="modalRecommendId"]').val('').removeAttr('data-userid'); + } + } + + toggleForeignerBox(show) { + $('.foreigner_box').toggle(show); + $('.local_box').toggle(!show); + } + + loadCommonCategories() { + const categories = [ + { code: 'C202404110001', key: 'nationality' }, + { code: 'C202404110002', key: 'userType' }, + { code: 'C202404110003', key: 'channel' } + ]; + categories.forEach(({ code, key }) => this.fetchCategory(code, key)); + this.fetchTreatmentList(); + } + + fetchCategory(code, choicesKey) { + $.ajax({ + url: '/kiosk/getCategoryItem.do', + type: 'POST', + data: { categoryCode: code }, + success: (data) => { + if (data.rows?.length > 0) { + const choices = data.rows.map(item => ({ + value: item.categoryitemcode || item.commonCode || '', + label: item.categoryitemname || item.codeName || '' + })); + const instance = this.choicesInstances.get(choicesKey); + if (instance) { + instance.setChoices(choices, 'value', 'label', true); + if (choicesKey === 'nationality') { + const defaultItem = choices.find(c => c.label.includes('대한민국')); + if (defaultItem) { + instance.setChoiceByValue(defaultItem.value); + this.data.nationalityCode = defaultItem.value; + this.data.nationality = 'Local'; + } + } + } + } + }, + error: (xhr) => console.error(`카테고리 조회 실패 (${code}):`, xhr.responseText) + }); + } + + fetchTreatmentList() { + $.ajax({ + url: '/kiosk/getTreatmentOptionList.do', + type: 'POST', + success: (data) => { + if (data.rows?.length > 0) { + const choices = data.rows.map(item => ({ + value: item.mutreatmentid || '', + label: item.treatmentname || '' + })); + const instance = this.choicesInstances.get('treatment'); + if (instance) instance.setChoices(choices, 'value', 'label', true); + } + } + }); + } + + initDatepicker() { + const birthdayInput = document.querySelector('input[name="modalBirthday"]'); + if (!birthdayInput) return; + + this.datepickerInstance = flatpickr(birthdayInput, { + locale: "ko", + dateFormat: "Y-m-d", + maxDate: "today", + disableMobile: true, + onChange: (selectedDates) => { + if (selectedDates.length > 0) { + this.calculateAgeAndRrn(selectedDates[0]); + } + } + }); + + $('.date-input-wrapper').off('click.datepicker').on('click.datepicker', (e) => { + e.stopPropagation(); + this.datepickerInstance?.open(); + }); + } + + calculateAgeAndRrn(birthDate) { + const today = new Date(); + let age = today.getFullYear() - birthDate.getFullYear(); + const m = today.getMonth() - birthDate.getMonth(); + if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) age--; + $('.txtAge').text(`만 ${age}세`); + + const yy = String(birthDate.getFullYear()).substring(2); + const mm = String(birthDate.getMonth() + 1).padStart(2, '0'); + const dd = String(birthDate.getDate()).padStart(2, '0'); + $('input[name="modalUserRrn1"]').val(`${yy}${mm}${dd}`); + } + + bindEvents() { + $('.cancel_btn').off('click.newPatient').on('click.newPatient', () => history.back()); + $('.registration_bth').off('click.newPatient').on('click.newPatient', () => this.save()); + + $('input[name="modalPhoneNumber"], input[name="modalPhoneNumber2"]') + .off('input.phoneFormat').on('input.phoneFormat', function() { + this.value = this.value.replace(/[^0-9]/g, '') + .replace(/(\d{3})(\d{3,4})(\d{4})/, '$1-$2-$3').substring(0, 13); + }); + + $('.searchIntroUser').off('click.newPatient').on('click.newPatient', () => { + window.userIntroSelectModal?.popup((obj) => { + $('input[name="modalRecommendId"]').val(obj.username || '').attr('data-userid', obj.muuserid || ''); + }); + }); + } + + async save() { + const formData = this.collectFormData(); + if (!this.validateForm(formData)) return; + + try { + const response = await this.submitForm(formData); + this.handleSaveResponse(response); + } catch (error) { + this.showError('통신 중 오류가 발생했습니다.'); + } + } + + collectFormData() { + return { + userName: $('input[name="modalUserName"]').val()?.trim() || '', + phone: $('input[name="modalPhoneNumber"]').val().replace(/-/g, '') || '', + phone2: $('input[name="modalPhoneNumber2"]').val().replace(/-/g, '') || '', + birthday: $('input[name="modalBirthday"]').val() || '', + gender: $('input[name="modalGender"]:checked').val() || '', + smsYn: $('input[name="modalSmsYn"]:checked').val() || '', + refusePhoto: $('input[name="modalRefusePhotoYn"]:checked').val() || '', + rrn1: $('input[name="modalUserRrn1"]').val() || '', + rrn2: $('input[name="modalUserRrn2"]').val() || '', + pno: $('input[name="modalUserPno"]').val() || '', + arc1: $('input[name="modalUserArc1"]').val() || '', + arc2: $('input[name="modalUserArc2"]').val() || '', + memo: $('textarea[name="modalMemo"]').val() || '', + etc: $('textarea[name="modalEtc"]').val() || '', + introUserId: $('input[name="modalRecommendId"]').attr('data-userid') || '', + ...this.data + }; + } + + validateForm(data) { + const errors = { + userName: !data.userName && '고객명을 입력해주세요.', + phone: (!data.phone || data.phone.length < 10) && '올바른 연락처를 입력해주세요.', + privacy: !$('#agree_privacy').is(':checked') && '개인정보 수집 이용에 동의해야 합니다.' + }; + + const errorMsg = Object.values(errors).find(Boolean); + if (errorMsg && window.modalEvent) { + window.modalEvent.warning('알림', errorMsg); + return false; + } + return true; + } + + submitForm(data) { + return new Promise((resolve, reject) => { + $.ajax({ + url: '/kiosk/putUser.do', + type: 'POST', + data: { + nationality: data.nationality, + nationalityCode: data.nationalityCode, + userName: data.userName, + phoneNumber: data.phone, + phoneNumber2: data.phone2, + birthday: data.birthday, + gender: data.gender, + userRrn1: data.rrn1, + userRrn2: data.rrn2, + userPno: data.pno, + userArc1: data.arc1, + userArc2: data.arc2, + userTypeCode: data.userType, + channelCode: data.visitPath, + muGroupId: data.treatment, + introUserId: data.introUserId, + memo: data.memo, + etc: data.etc, + smsYn: data.smsYn, + refusePhotoYn: data.refusePhoto + }, + success: resolve, + error: reject + }); + }); + } + + handleSaveResponse(data) { + if (data.msgCode === '0') { + if (window.modalEvent) { + window.modalEvent.success('등록 성공', '신규 고객 등록이 완료되었습니다.', () => { + location.href = '/kiosk/kiosk_main'; + }); + } else { + location.href = '/kiosk/kiosk_main'; + } + } else if (window.modalEvent) { + window.modalEvent.danger('오류', data.msgDesc || '등록에 실패했습니다.'); + } + } + + showError(message) { + if (window.modalEvent) { + window.modalEvent.danger('오류', message); + } else { + alert(message); + } + } + + destroy() { + this.choicesInstances.forEach(instance => instance?.destroy()); + this.choicesInstances.clear(); + this.datepickerInstance?.destroy(); + $('#userIntroSelectModal')?.remove(); + this.isInitialized = false; + } + } + + // 싱글톤 인스턴스 관리 + let instance = null; + window.newPatientPage = { + init() { + if (instance) return; + instance = new NewPatientPage(); + instance.init(); + }, + destroy() { + instance?.destroy(); + instance = null; + }, + getInstance() { + return instance; + } + }; + + // 자동 초기화 + const init = () => window.newPatientPage.init(); + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + setTimeout(init, 50); + } + window.addEventListener('load', () => setTimeout(init, 100)); +})(); diff --git a/src/main/resources/templates/kiosk/new-patient.html b/src/main/resources/templates/kiosk/new-patient.html index cd162da..50893d7 100644 --- a/src/main/resources/templates/kiosk/new-patient.html +++ b/src/main/resources/templates/kiosk/new-patient.html @@ -9,24 +9,25 @@ - + + + - - - - - + + + + @@ -45,37 +46,55 @@
기본 정보
- - + +
+ + +
+
- - -
- - -
- - -
- -
- - - calendar + +
+ +
+ + - +
+ +
+ +
+ +
+
+ +
+ +
+ + 만 0세 +
+
- +
@@ -84,72 +103,62 @@
- - -
- - -
- -
- + +
+ +
- -
- -
- - - - -
+ +
+ +
+ + +
+ +
+ +
+ +
-
연락처 및 동의
+
개인정보 수집 및 이용동의
+ +
+ + +
- -
- - -
+
+ + +
- -
- - -
+
+ + +
- -
- - -
- - -
- -
-
- - -
-
- - -
-
-
+
+ + +
@@ -171,14 +180,6 @@
기타 정보
- -
- - -
-
@@ -204,11 +205,6 @@
- -
- - -
@@ -223,7 +219,7 @@ - +