인터페이스 설계서 자동생성DTO
This commit is contained in:
@@ -19,6 +19,10 @@
|
||||
.result { background:#101012; border:1px solid var(--line); border-radius:8px; padding:14px; white-space:pre-wrap; overflow:auto; max-height:520px; font:13px/1.55 ui-monospace,SFMono-Regular,Consolas,monospace; }
|
||||
.case-list { display:grid; gap:8px; margin-top:12px; } .case-row { display:flex; align-items:center; gap:8px; padding:9px; border:1px solid var(--line); border-radius:8px; } .case-row main { padding:0; margin:0; flex:1; min-width:0; } .case-row strong,.case-row small { display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } .case-row small { color:var(--muted); margin-top:3px; }
|
||||
.run-log { margin-top:12px; font:12px/1.55 ui-monospace,monospace; color:var(--muted); white-space:pre-wrap; max-height:220px; overflow:auto; }
|
||||
.modal-backdrop { position:fixed; inset:0; z-index:100; display:flex; align-items:center; justify-content:center; padding:20px; background:rgba(0,0,0,.68); }
|
||||
.modal { width:min(420px,100%); padding:20px; background:var(--surface); border:1px solid var(--line); border-radius:12px; box-shadow:0 24px 70px rgba(0,0,0,.5); }
|
||||
.modal-header { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:18px; } .modal-header h2 { margin:0; font-size:17px; }
|
||||
.modal-close { padding:4px 8px; font-size:18px; line-height:1; } .modal-actions { display:flex; justify-content:flex-end; margin-top:18px; }
|
||||
.footer-note { margin-top:18px; color:#71717a; font-size:12px; } @media (max-width:900px) { .grid { grid-template-columns:1fr; } .header-inner { padding:0 16px; } main { padding:20px 16px; } }
|
||||
</style>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
@@ -49,6 +53,7 @@
|
||||
</nav>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<button id="dtoDownloadButton" class="primary" type="button">DTO 다운로드</button>
|
||||
<span class="badge" id="manifestStatus" style="border:1px solid #3f3f46; border-radius:99px; padding:4px 8px; color:#a1a1aa; font-size:11px; font-family:ui-monospace,monospace;">Manifest loading</span>
|
||||
<span class="text-[10px] uppercase tracking-widest px-2 py-1 rounded font-bold" style="background:rgba(59,130,246,0.1); color:#60a5fa; border:1px solid rgba(59,130,246,0.2);">v0.0.1</span>
|
||||
</div>
|
||||
@@ -68,6 +73,19 @@
|
||||
</div>
|
||||
<p class="footer-note">이 화면은 현재 Tool Pod의 <code>/tool-manifest</code>와 <code>/mcp/{toolName}</code>만 사용합니다. 저장된 케이스는 이 브라우저의 localStorage에만 보관됩니다.</p>
|
||||
</main>
|
||||
<div id="dtoDownloadModal" class="modal-backdrop hidden" role="dialog" aria-modal="true" aria-labelledby="dtoDownloadTitle">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h2 id="dtoDownloadTitle">DTO 다운로드</h2>
|
||||
<button id="dtoModalCloseButton" class="modal-close" type="button" aria-label="닫기">×</button>
|
||||
</div>
|
||||
<label for="dtoSelect">DTO 선택</label>
|
||||
<select id="dtoSelect">
|
||||
<option value="">DTO 목록 불러오는 중...</option>
|
||||
</select>
|
||||
<div class="modal-actions"><button id="dtoConfirmButton" class="primary" type="button">확인</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(() => {
|
||||
const STORAGE_KEY = 'axhub.tool-test-console.cases.v1';
|
||||
@@ -75,6 +93,61 @@
|
||||
const $ = id => document.getElementById(id);
|
||||
const select = $('toolSelect'), args = $('arguments'), result = $('result');
|
||||
|
||||
async function loadDtoOptions() {
|
||||
// 서버가 itrf/mci/**/io 패키지에서 찾은 DTO 목록으로 셀렉트박스를 구성한다.
|
||||
const dtoSelect = $('dtoSelect');
|
||||
try {
|
||||
const response = await fetch('/dto-download/options', { headers: { 'Cache-Control': 'no-cache' } });
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const dtoNames = await response.json();
|
||||
dtoSelect.innerHTML = '';
|
||||
for (const dtoName of dtoNames) {
|
||||
const option = document.createElement('option');
|
||||
option.value = dtoName;
|
||||
option.textContent = `${dtoName}.java`;
|
||||
dtoSelect.appendChild(option);
|
||||
}
|
||||
if (!dtoNames.length) dtoSelect.innerHTML = '<option value="">DTO 파일이 없습니다.</option>';
|
||||
} catch (error) {
|
||||
dtoSelect.innerHTML = '<option value="">DTO 목록을 불러오지 못했습니다.</option>';
|
||||
}
|
||||
}
|
||||
function openDtoModal() { $('dtoDownloadModal').classList.remove('hidden'); loadDtoOptions(); $('dtoSelect').focus(); }
|
||||
function closeDtoModal() { $('dtoDownloadModal').classList.add('hidden'); $('dtoDownloadButton').focus(); }
|
||||
async function downloadSelectedDto() {
|
||||
// 선택한 DTO의 엑셀 생성 API를 호출하고 응답 Blob을 브라우저 다운로드로 저장한다.
|
||||
const dtoName = $('dtoSelect').value;
|
||||
if (!dtoName) return alert('다운로드할 DTO를 선택해주세요.');
|
||||
const confirmButton = $('dtoConfirmButton');
|
||||
confirmButton.disabled = true;
|
||||
try {
|
||||
const response = await fetch(`/dto-download/${encodeURIComponent(dtoName)}`);
|
||||
if (!response.ok) {
|
||||
// 422는 GlowTrgmField 메타데이터가 없어 설계서 양식으로 변환할 수 없는 경우다.
|
||||
const message = (await response.text()).trim();
|
||||
throw new Error(response.status === 422
|
||||
? 'dto의 내용이 엑셀파일 양식에 맞지않습니다 파일을 확인해주세요'
|
||||
: message || (response.status === 404
|
||||
? `${dtoName}.xlsx 파일을 찾을 수 없습니다.`
|
||||
: `다운로드에 실패했습니다. (HTTP ${response.status})`));
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `${dtoName}.xlsx`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
closeDtoModal();
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
confirmButton.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function loadCases() { try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); } catch (_) { return []; } }
|
||||
function persistCases() { localStorage.setItem(STORAGE_KEY, JSON.stringify(state.cases)); renderCases(); }
|
||||
function requestId() { return crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(16).slice(2)}`; }
|
||||
@@ -252,9 +325,14 @@
|
||||
$('executeButton').addEventListener('click', async () => { try { await execute(); } catch (error) { $('httpStatus').textContent = '입력 오류'; $('httpStatus').className = 'badge fail'; result.textContent = error.message; } });
|
||||
$('runAllButton').addEventListener('click', runSavedCases);
|
||||
$('clearCasesButton').addEventListener('click', () => { if (confirm('저장된 테스트 케이스를 모두 삭제할까요?')) { state.cases = []; persistCases(); } });
|
||||
$('dtoDownloadButton').addEventListener('click', openDtoModal);
|
||||
$('dtoModalCloseButton').addEventListener('click', closeDtoModal);
|
||||
$('dtoConfirmButton').addEventListener('click', downloadSelectedDto);
|
||||
$('dtoDownloadModal').addEventListener('click', event => { if (event.target === $('dtoDownloadModal')) closeDtoModal(); });
|
||||
document.addEventListener('keydown', event => { if (event.key === 'Escape' && !$('dtoDownloadModal').classList.contains('hidden')) closeDtoModal(); });
|
||||
$('caseList').addEventListener('click', async event => { const id = event.target.dataset.run || event.target.dataset.delete; if (!id) return; const item = state.cases.find(candidate => candidate.id === id); if (event.target.dataset.delete) { state.cases = state.cases.filter(candidate => candidate.id !== id); persistCases(); return; } const tool = state.tools.find(candidate => candidate.name === item.toolName); try { await execute(tool, item.arguments); } catch (error) { result.textContent = error.message; } });
|
||||
renderCases(); loadManifest();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user