import './ground-popup.css'; const API='/__pierzainer/ground-link'; let importGround: ((text: string, name: string) => Promise) | undefined; export function registerGroundImporter(importer: (text: string, name: string) => Promise) { importGround = importer; } let pending:{file:File;folder?:string}|undefined,staged:Promise=Promise.resolve(undefined),version=0; const request=async(action:string,body:object)=>{const r=await fetch(`${API}/${action}`,{method:'POST',headers:{'Content-Type':'application/json','X-Pierzainer-Action':'ground-link'},body:JSON.stringify(body)});const value=await r.json();if(!r.ok)throw new Error(value.error??'지반 프로그램 연동 실패');return value;}; const base64=(file:File)=>new Promise((resolve,reject)=>{const reader=new FileReader();reader.onerror=()=>reject(new Error('Excel 파일을 읽지 못했습니다.'));reader.onload=()=>resolve(String(reader.result).split(',')[1]);reader.readAsDataURL(file);}); document.addEventListener('change',event=>{const input=event.target as HTMLInputElement;if(input.id==='file'&&input.files?.[0])pending={file:input.files[0]};if(input.id==='project-file'){pending=undefined;version++;staged=Promise.resolve(undefined);}},true); window.addEventListener('pierzainer:ground-source',(event:Event)=>{const d=(event as CustomEvent).detail;pending={file:new File([d.bytes],d.name,{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}),folder:d.folder};}); document.addEventListener('click',event=>{if(!(event.target as Element).closest('#dlg-apply')||!pending)return;const source=pending;pending=undefined;version++;staged=(async()=>{const data=await request('stage',{name:source.file.name,base64:await base64(source.file),folder:source.folder});return data.id as string;})();staged.catch(()=>{});},true); let opening=false,running=false,launchSerial=0; let popup:HTMLDialogElement; window.addEventListener('pierzainer:project-changed',()=>{ version++;launchSerial++;running=false;opening=false; if(popup){popup.close();popup.querySelector('[data-status]')!.textContent='프로젝트가 변경되어 이전 작업의 결과를 연결하지 않습니다.';} }); function getPopup(){ if(popup)return popup; popup=document.createElement('dialog');popup.className='ground-popup';popup.setAttribute('aria-labelledby','ground-popup-title'); popup.innerHTML=`
GROUND MODEL

지반 모델 생성

종평면도와 교각 Excel로 지반면·위성사진·2D 지도 JSON을 생성합니다.

기존 생성 엔진을 사용합니다. 지도 다운로드에는 인터넷 연결이 필요합니다.

`; popup.querySelectorAll('[data-pick]').forEach(button=>button.addEventListener('click',()=>popup.querySelector(`[data-${button.dataset.pick}]`)!.click())); popup.querySelectorAll('[data-close]').forEach(button=>button.addEventListener('click',()=>popup.close()));document.body.append(popup);return popup; } window.addEventListener('pierzainer:open-ground',async()=>{ if(opening&&popup?.open)return;if(running&&popup){popup.showModal();opening=true;return;}opening=true; const dialog=getPopup(),ticket=++launchSerial,snapshot=version; const status=dialog.querySelector('[data-status]')!,logs=dialog.querySelector('[data-logs]')!,run=dialog.querySelector('[data-run]')!; const drawingInput=dialog.querySelector('[data-drawing]')!,excelInput=dialog.querySelector('[data-excel]')!; drawingInput.value='';excelInput.value='';drawingInput.disabled=excelInput.disabled=false;dialog.querySelector('[data-drawing-name]')!.textContent='파일 확인 중…';dialog.querySelector('[data-excel-name]')!.textContent='파일 확인 중…';run.disabled=true;status.textContent='입력 파일 확인 중…';logs.textContent='';dialog.showModal(); let stageId:string|undefined,drawing=''; try{stageId=await staged;const prepared=await request('prepare',{stageId});if(snapshot!==version||ticket!==launchSerial)return;drawing=prepared.drawing; const display=(kind:string,name:string,connected:boolean)=>{dialog.querySelector(`[data-${kind}-name]`)!.textContent=name;dialog.querySelector(`[data-pick="${kind}"]`)!.textContent=connected?'파일 변경':kind==='drawing'?'종평면도 선택':'Excel 선택';}; const drawingName=drawing.split(/[\\/]/).pop()||'종평면도를 선택하세요.';const excelName=prepared.excelName||'교각 Excel을 선택하세요.'; display('drawing',drawingName,!!drawing);display('excel',excelName,prepared.excelSelected); drawingInput.onchange=()=>display('drawing',drawingInput.files?.[0]?.name||drawingName,!!drawingInput.files?.[0]||!!drawing); excelInput.onchange=()=>display('excel',excelInput.files?.[0]?.name||excelName,!!excelInput.files?.[0]||prepared.excelSelected);status.textContent='입력을 확인하고 지반 생성을 누르세요.';run.disabled=false; }catch(error){if(snapshot!==version||ticket!==launchSerial)return;status.textContent=(error as Error).message;opening=false;return;} run.onclick=async()=>{ if(snapshot!==version){status.textContent='프로젝트가 변경되었습니다. 닫고 다시 열어 주세요.';return;} run.disabled=true;drawingInput.disabled=excelInput.disabled=true; try{ const upload=async(file:File)=>(await request('upload',{name:file.name,base64:await base64(file)})).file as string; status.textContent='입력 파일 전달 중…';const chosen=drawingInput.files?.[0],excel=excelInput.files?.[0]; if(chosen)drawing=await upload(chosen);if(!drawing)throw new Error('종평면도를 선택하세요.'); const excelPath=excel?await upload(excel):undefined; if(snapshot!==version||ticket!==launchSerial)return; if(!excelPath&&!stageId)throw new Error('교각 Excel을 선택하세요.'); const job=await request('launch',{stageId,drawing,excel:excelPath});if(snapshot!==version||ticket!==launchSerial)return;running=true;status.textContent='지반·지도 생성 중… 닫아도 생성은 계속됩니다.'; const deadline=Date.now()+30*60*1000; const poll=async()=>{ if(snapshot!==version||ticket!==launchSerial)return; try{ const result=await request('result',{id:job.id});logs.textContent=result.logs??'';logs.scrollTop=logs.scrollHeight; if(snapshot!==version||ticket!==launchSerial)return; if(result.state==='error')throw new Error('지반 생성 실패. 진행 로그를 확인하세요.'); if(result.ready){ if(!importGround)throw new Error('프로젝트 지반모델 불러오기를 사용할 수 없습니다.'); await importGround(result.text,result.filename); status.textContent='지반 생성 완료. 프로젝트 공통 모델로 불러왔습니다. 관리창에서 비교 후 적용하세요.'; running=false;run.disabled=false;drawingInput.disabled=excelInput.disabled=false;return; } if(Date.now()>deadline)throw new Error('응답 대기 시간이 지났습니다. 결과 폴더를 확인하세요.');setTimeout(poll,2000); }catch(error){if(snapshot!==version||ticket!==launchSerial)return;status.textContent=(error as Error).message;running=false;run.disabled=false;drawingInput.disabled=excelInput.disabled=false;} };setTimeout(poll,1000); }catch(error){if(snapshot!==version||ticket!==launchSerial)return;status.textContent=(error as Error).message;running=false;run.disabled=false;drawingInput.disabled=excelInput.disabled=false;} }; }); // Closing hides the panel; the current job and its polling continue. document.addEventListener('close',event=>{if(event.target===popup)opening=false;},true);