(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)); })();