window.InstallWait = window.InstallWait || { step: 1, customer: null, job: null, pickup: [], schedule: { date:'', note:'' }, photos: [] }; /* 슬라이드 이동 (100vw 기준) */ function goInstallWaitStep(step) { const wrapper = document.querySelector('.install-wait-steps-wrapper'); if (!wrapper) return; // DOM 아직 없으면 그냥 나감 wrapper.style.transform = `translateX(-${(step - 1) * 100}%)`; } /* ============================ STEP 1 ============================ */ function initAddInstallWaitModal() { const input = document.getElementById('customerSearchInput'); const result = document.getElementById('customerSearchResult'); const btnNext = document.getElementById('btnStep1Next'); $('#addInstallWaitModal') .one('shown.bs.modal', function () { const input = document.getElementById('customerSearchInput'); if (input) input.focus(); }); btnNext.onclick = () => { if (!InstallWait.customer) return; goInstallWaitStep(2); initInstallWaitStep2(); }; let timer = null; input.addEventListener('input', function () { const keyword = this.value.trim(); clearTimeout(timer); if (keyword.length < 2) { result.innerHTML = ''; btnNext.disabled = true; return; } timer = setTimeout(() => { api('search_customer', { keyword }, rows => { if (!rows || !rows.length) { result.innerHTML = `
No customer found
The customer may already be in the wait list or already assigned.
`; return; } result.innerHTML = rows.map(r => `
${r.c_accountno} - ${r.c_name}
${r.c_address}, ${r.c_city}
`).join(''); bindCustomerRowEvents(); }); }, 300); }); } function bindCustomerRowEvents() { const btnNext = document.getElementById('btnStep1Next'); let selected = null; document.querySelectorAll('.customer-row').forEach(row => { row.onclick = function () { if (selected === this) { this.classList.remove('selected'); selected = null; InstallWait.customer = null; btnNext.disabled = true; btnNext.classList.remove('active-step'); return; } document.querySelectorAll('.customer-row').forEach(r => r.classList.remove('selected')); this.classList.add('selected'); selected = this; InstallWait.customer = { uid: this.dataset.uid, account: this.dataset.account, name: this.dataset.name, address: this.dataset.address, city: this.dataset.city, postal: this.dataset.postal, phone: this.dataset.phone }; btnNext.disabled = false; btnNext.classList.add('active-step'); }; }); } /* ============================ STEP 2 ============================ */ function syncCustomerFromStep2() { const map = { account: 'iwCustomerAccount', name: 'iwCustomerName', address: 'iwCustomerAddress', city: 'iwCustomerCity', postal: 'iwCustomerPostal', phone: 'iwCustomerPhone' }; Object.keys(map).forEach(k => { InstallWait.customer[k] = document.getElementById(map[k]).value.trim(); }); } function syncCustomerToInputs() { const c = InstallWait.customer || {}; iwCustomerAccount.value = c.account || ''; iwCustomerName.value = c.name || ''; iwCustomerAddress.value = c.address || ''; iwCustomerCity.value = c.city || ''; iwCustomerPostal.value = c.postal || ''; iwCustomerPhone.value = c.phone || ''; } function initInstallWaitStep2() { syncCustomerToInputs(); document.getElementById('btnStep2Back').onclick = () => { goInstallWaitStep(1); }; bindJobSelection(); bindStep2Next(); } function bindJobSelection() { const cards = document.querySelectorAll('.iw-job-card'); const btnNext = btnStep2Next; let selected = null; cards.forEach(card => { card.onclick = () => { if (selected === card) { card.classList.remove('active'); selected = null; InstallWait.job = null; btnNext.disabled = true; return; } cards.forEach(c => c.classList.remove('active')); card.classList.add('active'); selected = card; InstallWait.job = { type: card.dataset.job }; btnNext.disabled = false; btnNext.classList.add('active-step'); }; }); } function bindStep2Next() { btnStep2Next.onclick = () => { if (!InstallWait.job) return; syncCustomerFromStep2(); const memberUid = document.getElementById('installWaitContext').dataset.memberUid || ''; api('install_wait_create_draft', { c_uid: InstallWait.customer.uid, job_type: InstallWait.job.type, account: InstallWait.customer.account, name: InstallWait.customer.name, address: InstallWait.customer.address, city: InstallWait.customer.city, postal: InstallWait.customer.postal, phone: InstallWait.customer.phone, member_uid: memberUid }, res => { if (!res || !res.ok || !res.iw_uid) return; InstallWait.iw_uid = res.iw_uid; goInstallWaitStep(3); initInstallWaitStep3(); }); }; } /* ============================ STEP 3 ============================ */ function initInstallWaitStep3() { btnStep3Back.onclick = () => { // id 없으면 그냥 back if (!InstallWait.iw_uid) { goInstallWaitStep(2); return; } const iw_uid = InstallWait.iw_uid; if (!confirm('Delete this install request?')) return; btnStep3Back.disabled = true; api('install_wait_delete', { iw_uid }, (res) => { btnStep3Back.disabled = false; if (!res || !res.ok) { // alert(res?.msg || 'Delete failed'); showPopupMessage(res?.msg || 'Delete failed', 'error', 1500); return; } else { showPopupMessage('Successfully Deleted', 'success', 1500); } const rowEl = btnStep3Back.closest('.install-wait-row'); if (rowEl) rowEl.remove(); // 로컬 상태 초기화 InstallWait.iw_uid = null; InstallWait.pickup = []; InstallWait.schedule = { date:'', note:'' }; InstallWait.photos = []; // Step3 UI 잔상 제거 const area = document.getElementById('iwStep3ContainerArea'); if (area) area.innerHTML = ''; goInstallWaitStep(2); initInstallWaitStep2(); return; }); }; // Complete 버튼 btnStep3Complete.onclick = () => { if (!InstallWait.iw_uid) { // alert('Invalid install wait'); showPopupMessage('Invalid install wait', 'error', 1500); return; } // 컨테이너 선택여부 if (!validateContainersBeforeComplete()) { return; } // set parameter const twoOn = document.getElementById('btnTwoPerson').classList.contains('iw-toggle-on'); const recOn = document.getElementById('btnRecurring').classList.contains('iw-toggle-on'); const payload = { iw_uid: InstallWait.iw_uid, request_at: document.getElementById('iwRequestDate').value || null, due_date: document.getElementById('iwScheduleDate').value || null, lock_date: document.getElementById('iwLockDate').checked ? 1 : 0, request_by: document.getElementById('iwRequestBy').value || null, request_note: document.getElementById('iwRequestNote').value || null, cs_note: document.getElementById('iwCsNote').value || null, work_note: document.getElementById('iwWorkNote').value || null, person_cnt: twoOn ? 2 : null, person_reason: twoOn ? document.getElementById('iwTwoPersonReason').value : null, scheduled: recOn ? 1 : 0, scheduled_cycle: recOn ? document.getElementById('iwRecurringDays').value: 0 }; btnStep3Complete.disabled = true; // 1단계: install_wait complete api('install_wait_complete', payload, (res) => { if (!res || !res.ok) { btnStep3Complete.disabled = false; //alert('Failed to complete install request'); showPopupMessage('Save failed', 'error', 1500); return; } // 2단계: 사진 업로드 있을 때 const files = document.getElementById('iwPhotos').files; if (!files || files.length === 0) { showPopupMessage('Successfully Saved', 'success', 1000); finishInstallWaitComplete(); return; } const memberUid = document.getElementById('installWaitContext').dataset.memberUid || ''; const fd = new FormData(); fd.append('actionStr', 'ADDIMAGE'); fd.append('i_type', 'install_order'); fd.append('i_customeruid', InstallWait.customer.uid); fd.append('i_memberuid', memberUid); fd.append('i_createdby', memberUid); fd.append('i_note', 'Before Install'); fd.append('i_sourceuid', InstallWait.iw_uid); [...files].forEach(file => { fd.append('upload_file[]', file); }); fetch('/lib/user_process.php', { method: 'POST', body: fd }) .then(() => { showPopupMessage('Successfully Saved', 'success', 1000); finishInstallWaitComplete(); }) .catch(() => { //alert('Install completed, but image upload failed'); showPopupMessage('Saved (photo failed)', 'error', 1500); finishInstallWaitComplete(); }); }); }; InstallWait.pickup = InstallWait.pickup || []; setStep3Title(); renderStep3ActionBoxes(); loadCurrentContainersIfNeeded(); bindStep3Toggles(); fillRequestBySelect('Customer'); initInstallWaitDatePickers(); } function setStep3Title() { const jobLabel = InstallWait.job?.type || ''; const acct = InstallWait.customer?.account ? ` of ${InstallWait.customer.account}` : ''; const titleEl = document.getElementById('iwStep3Title'); if (titleEl) titleEl.innerText = `Step 3 of 3 - ${jobLabel}${acct} Detail`; } function renderStep3ActionBoxes() { const area = document.getElementById('iwStep3ContainerArea'); area.innerHTML = ''; const jobType = InstallWait.job?.type; if (!jobType) return; // Row 1 : CURRENT | PICKUP if (['PICKUP','EXCHANGE','INSTALL','RELOCATE','CLEAN'].includes(jobType)) { area.insertAdjacentHTML('beforeend', `
${renderActionBox('CURRENT')}
${renderActionBox('PICKUP')}
`); } // Row 2 : SELECT | INSTALL if (['INSTALL','EXCHANGE'].includes(jobType)) { area.insertAdjacentHTML('beforeend', `
${renderActionBox('SELECT')}
${renderActionBox('INSTALL')}
`); } bindSelectInstallEvents(); } function renderActionBox(action) { const icon = { CURRENT: 'bi-archive', PICKUP: 'bi-dash-circle', SELECT: 'bi-plus-square', INSTALL: 'bi-plus-circle', RELOCATE: 'bi-arrow-left-right' }[action] || 'bi-box'; const colorClass = action === 'PICKUP' ? 'pickup' : action === 'INSTALL' ? 'install' : ''; let content = ''; // CURRENT if (action === 'CURRENT') { content = `
Loading...
`; } // SELECT 전용 UI if (action === 'SELECT') { content = renderSelectInstallUI(); } return `
${action}
${content}
`; } function countTaskChips(action) { // action: 'PICKUP' | 'INSTALL' const wrap = document.querySelector(`.container-chips[data-action="${action}"]`); if (!wrap) return 0; // 프로젝트에서 chip 클래스가 여러 개일 수 있어서 span.chip 기준으로 카운트 return wrap.querySelectorAll('span.chip').length; } function validateContainersBeforeComplete() { const jobType = (InstallWait.job?.type || '').toUpperCase(); const pickupCnt = countTaskChips('PICKUP'); const installCnt = countTaskChips('INSTALL'); // EXCHANGE: 둘 다 있어야 함 if (jobType === 'EXCHANGE') { if (pickupCnt < 1 || installCnt < 1) { //alert('EXCHANGE requires containers in BOTH Pickup and Install. Please select containers.'); showPopupMessage('Please select containers.', 'error', 1500); return false; } return true; } // INSTALL: install에 있어야 함 if (jobType === 'INSTALL') { if (installCnt < 1) { //alert('Please select at least 1 container in Install.'); showPopupMessage('Please select containers.', 'error', 1500); return false; } return true; } // PICKUP: pickup에 있어야 함 if (jobType === 'PICKUP') { if (pickupCnt < 1) { //alert('Please select at least 1 container in Pickup.'); showPopupMessage('Please select containers.', 'error', 1500); return false; } return true; } // 다른 타입(혹시 RELOCATE/CLEAN 등 Step3에서도 쓰면 정책 정해서 추가) return true; } function loadCurrentContainersIfNeeded(onDone) { const jobType = InstallWait.job?.type; if (!['PICKUP','EXCHANGE','INSTALL','RELOCATE','CLEAN'].includes(jobType)) { if (typeof onDone === 'function') onDone(); return; } const wrap = document.querySelector('.container-chips[data-action="CURRENT"]'); if (!wrap) { if (typeof onDone === 'function') onDone(); return; } api('get_customer_containers_active', { c_uid: InstallWait.customer.uid }, rows => { if (!rows || rows.length === 0) { wrap.innerHTML = `
No active containers
`; if (typeof onDone === 'function') onDone(); return; } wrap.innerHTML = rows.map(renderContainerChip).join(''); bindCurrentContainerChipEvents(); if (typeof onDone === 'function') onDone(); } ); } function renderContainerChip(c) { const type = (c.cc_type || '').toUpperCase(); const icon = getContainerIconByType(type); const isRestaurantOwned = c.cc_owner_type === 'O'; const chipClass = isRestaurantOwned ? 'chip chip-restaurant iw-current-chip' : 'chip chip-company iw-current-chip'; const ownerBadge = isRestaurantOwned ? 'O' : ''; const flags = (c.cc_has_lock === 'Y' ? ' ' : '') + (c.cc_has_wheel === 'Y' ? ' ' : '') + (c.cc_has_woodframe === 'Y' ? ' ' : ''); const used = c.cc_is_used === 'Y' ? 'Used ' : ''; return ` ${ownerBadge} ${used}${c.cc_type} ${flags} `; } function bindCurrentContainerChipEvents() { const jobType = InstallWait.job?.type; const disableClick = ['INSTALL','RELOCATE','CLEAN'].includes(jobType); const currentWrap = document.querySelector('.container-chips[data-action="CURRENT"]'); const pickupWrap = document.querySelector('.container-chips[data-action="PICKUP"]'); if (!currentWrap || !pickupWrap) return; currentWrap.querySelectorAll('.iw-current-chip').forEach(chip => { chip.onclick = () => { if (disableClick) { // 클릭은 막고, 시각적 피드백만 줄 수도 있음 chip.classList.add('disabled'); return; } pickupWrap.appendChild(chip); upsertPickupContainers(); bindPickupContainerChipEvents(); }; }); } function bindPickupContainerChipEvents() { const currentWrap = document.querySelector('.container-chips[data-action="CURRENT"]'); const pickupWrap = document.querySelector('.container-chips[data-action="PICKUP"]'); if (!currentWrap || !pickupWrap) return; pickupWrap.querySelectorAll('.iw-current-chip').forEach(chip => { chip.onclick = () => { // UI 이동 currentWrap.appendChild(chip); // PICKUP 기준으로 다시 업설트 upsertPickupContainers(); // 이벤트 재바인딩 bindCurrentContainerChipEvents(); }; }); } function chipKeyFromDataset(ds){ const norm = v => (v ?? '').toString(); return [ norm(ds.type).toUpperCase(), norm(ds.capacity), norm(ds.is_used), norm(ds.owner_type), norm(ds.has_lock), norm(ds.has_wheel), norm(ds.has_frame), ].join('|'); } function chipKeyFromIwcRow(r){ const norm = v => (v ?? '').toString(); return [ norm(r.iwc_type).toUpperCase(), norm(r.iwc_capacity), norm(r.iwc_is_used || 'N'), norm(r.iwc_owner_type || 'C'), norm(r.iwc_has_lock || 'N'), norm(r.iwc_has_wheel || 'N'), norm(r.iwc_has_woodframe || 'N'), ].join('|'); } function applyPickupFromDBToCurrent(pickupRows) { const currentWrap = document.querySelector('.container-chips[data-action="CURRENT"]'); const pickupWrap = document.querySelector('.container-chips[data-action="PICKUP"]'); if (!currentWrap || !pickupWrap) return; // key → remaining count const pickupCountMap = {}; (pickupRows || []).forEach(r => { const key = chipKeyFromIwcRow(r); pickupCountMap[key] = (pickupCountMap[key] || 0) + 1; }); // CURRENT에서 하나씩 매칭 [...currentWrap.querySelectorAll('.iw-current-chip')].forEach(chip => { const key = chipKeyFromDataset(chip.dataset); if (pickupCountMap[key] > 0) { pickupWrap.appendChild(chip); pickupCountMap[key]--; // ⭐ 하나 소비 } }); bindCurrentContainerChipEvents(); bindPickupContainerChipEvents(); } function upsertPickupContainers() { const pickupWrap = document.querySelector('.container-chips[data-action="PICKUP"]'); if (!pickupWrap) return; const items = [...pickupWrap.querySelectorAll('.iw-current-chip')].map(el => ({ action: 'PICKUP', type: el.dataset.type, capacity: el.dataset.capacity || null, is_used: el.dataset.is_used || 'N', owner_type: el.dataset.owner_type || 'C', has_lock: el.dataset.has_lock || 'N', has_wheel: el.dataset.has_wheel || 'N', has_frame: el.dataset.has_frame || 'N' })); api('install_wait_upsert_containers', { iw_uid: InstallWait.iw_uid, action: 'PICKUP', items }); } function renderSelectInstallUI() { const binOptions = Object.entries(window.IW_BINS || {}) .map(([key, label]) => `` ).join(''); return `
`; } function bindSelectInstallEvents() { const usedBtn = document.getElementById('iwSelectUsed'); const typeSel = document.getElementById('iwSelectType'); const capInp = document.getElementById('iwSelectCapacity'); if (!usedBtn || !typeSel) return; usedBtn.onclick = () => { usedBtn.classList.toggle('active'); }; // 타입 선택 시 용량 자동 표시 typeSel.onchange = () => { const val = typeSel.value; // CB 는 직접 입력 if (val === 'CB') { capInp.readOnly = false; capInp.value = ''; capInp.focus(); return; } else if (val === 'D') { capInp.value = '200'; return; } capInp.readOnly = true; const m = val.match(/^(\d+)/); capInp.value = m ? m[1] : ''; }; // 악세사리 토글 document.querySelectorAll('.iw-opt').forEach(btn => { btn.onclick = () => btn.classList.toggle('active'); }); // Add → INSTALL 이동 document.getElementById('iwAddInstall').onclick = addInstallFromSelect; } function addInstallFromSelect() { const type = document.getElementById('iwSelectType').value; if (!type) return showPopupMessage('Select container type.', 'error', 1500); //alert('Select container type'); const capacity = document.getElementById('iwSelectCapacity').value || null; const is_used = document.getElementById('iwSelectUsed').classList.contains('active') ? 'Y' : 'N'; const has_lock = document.querySelector('.iw-opt[data-opt="L"]').classList.contains('active') ? 'Y' : 'N'; const has_wheel = document.querySelector('.iw-opt[data-opt="W"]').classList.contains('active') ? 'Y' : 'N'; const has_frame = document.querySelector('.iw-opt[data-opt="F"]').classList.contains('active') ? 'Y' : 'N'; const installWrap = document.querySelector('.container-chips[data-action="INSTALL"]'); const icon = getContainerIconByType(type); const chip = ` ${is_used === 'Y' ? 'Used ' : ''}${type} ${has_lock === 'Y' ? ' ' : ''} ${has_wheel === 'Y' ? ' ' : ''} ${has_frame === 'Y' ? ' ' : ''} `; installWrap.insertAdjacentHTML('beforeend', chip); bindInstallChipEvents(); upsertInstallContainers(); } function upsertInstallContainers() { const wrap = document.querySelector('.container-chips[data-action="INSTALL"]'); const items = [...wrap.querySelectorAll('.iw-install-chip')].map(el => ({ action: 'INSTALL', type: el.dataset.type, capacity: el.dataset.capacity || null, is_used: el.dataset.is_used || 'N', has_lock: el.dataset.has_lock || 'N', has_wheel: el.dataset.has_wheel || 'N', has_frame: el.dataset.has_frame || 'N', owner_type: 'C' })); api('install_wait_upsert_containers', { iw_uid: InstallWait.iw_uid, action: 'INSTALL', items }); } function bindInstallChipEvents() { const installWrap = document.querySelector('.container-chips[data-action="INSTALL"]'); if (!installWrap) return; installWrap.querySelectorAll('.iw-install-chip').forEach(chip => { chip.onclick = () => { chip.remove(); upsertInstallContainers(); }; }); } function initInstallWaitDatePickers() { if (!$.fn.datepicker) return; $('.date-picker-form').datepicker({ dateFormat: 'yy-mm-dd', changeMonth: true, changeYear: true }); } function fillRequestBySelect(selectedValue = 'Customer') { const sel = document.getElementById('iwRequestBy'); if (!sel || !window.IW_REQUEST_BY_OPTIONS) return; sel.innerHTML = window.IW_REQUEST_BY_OPTIONS; if (!sel.querySelector(`option[value="${selectedValue}"]`)) { selectedValue = 'Customer'; } sel.value = selectedValue; } // 이미지 여부 체크 function checkCustomerHasImages(customerUid) { if (!customerUid) return; const btn = document.getElementById('btnViewPhotos'); if (!btn) return; const params = new URLSearchParams({ customer_uid: customerUid, i_type: 'install_order' }); fetch('/lib/customer_image.php?' + params.toString()) .then(res => res.text()) .then(html => { const temp = document.createElement('div'); temp.innerHTML = html; const hasImage = temp.querySelector('img') !== null; console.log('hasImage:', hasImage); if (hasImage) { btn.classList.remove('d-none'); btn.onclick = () => openCustomerImages(customerUid); } else { btn.classList.add('d-none'); } }) .catch(() => { btn.classList.add('d-none'); }); } function bindStep3Toggles() { const btnTwo = document.getElementById('btnTwoPerson'); const reason = document.getElementById('iwTwoPersonReason'); btnTwo.onclick = () => { const on = btnTwo.classList.toggle('iw-toggle-on'); reason.disabled = !on; if (!on) reason.value = ''; }; const btnRec = document.getElementById('btnRecurring'); const days = document.getElementById('iwRecurringDays'); btnRec.onclick = () => { const on = btnRec.classList.toggle('iw-toggle-on'); days.disabled = !on; if (!on) days.value = ''; }; } function getContainerIconByType(type) { type = (type || '').toUpperCase(); if (type.includes('D')) return 'bi-database-fill'; // Drum if (type.includes('B') && !type.includes('BUCKET')) return 'bi-archive-fill'; // Bin return 'bi-box2'; // Fallback } function renderDbChipCommon(r, cls) { const type = (r.iwc_type || '').toUpperCase(); const icon = getContainerIconByType(type); const isRestaurantOwned = (r.iwc_owner_type || 'C') === 'O'; const chipClass = isRestaurantOwned ? `chip chip-restaurant ${cls}` : `chip chip-company ${cls}`; const ownerBadge = isRestaurantOwned ? 'O' : ''; const flags = (r.iwc_has_lock === 'Y' ? ' ' : '') + (r.iwc_has_wheel === 'Y' ? ' ' : '') + ((r.iwc_has_woodframe || 'N') === 'Y' ? ' ' : ''); const used = r.iwc_is_used === 'Y' ? 'Used ' : ''; return ` ${ownerBadge} ${used}${r.iwc_type} ${flags} `; } function renderPickupChipFromDB(r) { return renderDbChipCommon(r, 'iw-current-chip'); // PICKUP는 current-chip 클래스 재사용 } function renderInstallChipFromDB(r) { return renderDbChipCommon(r, 'iw-install-chip'); } // Complete 액션 종료 후 function finishInstallWaitComplete() { const modalEl = document.getElementById('addInstallWaitModal'); // 모달 완전히 닫힌 뒤 리로드 modalEl.addEventListener('hidden.bs.modal', function onHidden() { modalEl.removeEventListener('hidden.bs.modal', onHidden); // 상태 정리 InstallWait = { step: 1 }; // 이 시점에 리로드 location.reload(); }); // 모달 닫기 (애니메이션 시작) const modal = bootstrap.Modal.getInstance(modalEl); if (modal) modal.hide(); }