adobe-to-docusign-migrator/web/static/app.js

222 lines
7.7 KiB
JavaScript

// Adobe Sign → DocuSign Migrator — frontend app
// Vanilla JS, no build step.
const $ = id => document.getElementById(id);
let adobeTemplates = []; // [{id, name, modifiedDate}]
let dsTemplates = []; // [{id, name, lastModified}]
let statusTemplates = []; // [{adobe_id, name, status, docusign_id, ...}]
let authState = { adobe: false, docusign: false };
// ── Init ────────────────────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', async () => {
await refreshAuth();
await refreshTemplates();
await refreshHistory();
$('btn-migrate').addEventListener('click', onMigrate);
$('btn-refresh').addEventListener('click', async () => {
await refreshTemplates();
await refreshHistory();
});
});
// ── Auth ─────────────────────────────────────────────────────────────────────
async function refreshAuth() {
const resp = await fetch('/api/auth/status');
authState = await resp.json();
renderAuthBar();
}
function renderAuthBar() {
renderAuthBadge('badge-adobe', 'Adobe Sign', authState.adobe, '/api/auth/adobe/start', '/api/auth/adobe/disconnect');
renderAuthBadge('badge-docusign', 'DocuSign', authState.docusign, '/api/auth/docusign/start', '/api/auth/docusign/disconnect');
}
function renderAuthBadge(id, label, connected, connectUrl, disconnectUrl) {
const el = $(id);
el.textContent = connected ? `${label}` : `Connect ${label}`;
el.className = 'auth-badge' + (connected ? ' connected' : '');
el.onclick = () => {
if (connected) {
fetch(disconnectUrl).then(() => { authState[id.replace('badge-','')] = false; renderAuthBar(); refreshTemplates(); });
} else {
window.location.href = connectUrl;
}
};
}
// ── Templates ────────────────────────────────────────────────────────────────
async function refreshTemplates() {
renderAdobeList([]);
renderDsList([]);
if (!authState.adobe || !authState.docusign) {
setStatus(authState.adobe || authState.docusign
? 'Connect both platforms to see migration status.'
: 'Connect Adobe Sign and DocuSign to get started.');
$('btn-migrate').disabled = true;
return;
}
setStatus('Loading templates…');
try {
const [statusResp, dsResp] = await Promise.all([
fetch('/api/templates/status'),
fetch('/api/templates/docusign'),
]);
statusTemplates = (await statusResp.json()).templates || [];
dsTemplates = ((await dsResp.json()).templates || []);
renderAdobeList(statusTemplates);
renderDsList(dsTemplates);
setStatus(`${statusTemplates.length} Adobe template(s) loaded.`);
} catch (e) {
setStatus('Error loading templates: ' + e.message);
}
}
function renderAdobeList(items) {
const ul = $('adobe-list');
if (!items.length) {
ul.innerHTML = '<li class="empty-msg">No templates found.</li>';
return;
}
ul.innerHTML = items.map(t => `
<li class="template-item" data-id="${t.adobe_id}">
<input type="checkbox" data-id="${t.adobe_id}" />
<span class="template-name">${escHtml(t.name)}</span>
<span class="badge badge-${t.status}">${statusLabel(t.status)}</span>
<span class="template-spinner" id="spin-${t.adobe_id}"></span>
</li>
`).join('');
ul.querySelectorAll('.template-item').forEach(li => {
li.addEventListener('click', e => {
if (e.target.type === 'checkbox') return;
const cb = li.querySelector('input[type=checkbox]');
cb.checked = !cb.checked;
li.classList.toggle('selected', cb.checked);
updateMigrateButton();
});
li.querySelector('input').addEventListener('change', () => {
li.classList.toggle('selected', li.querySelector('input').checked);
updateMigrateButton();
});
});
}
function renderDsList(items) {
const ul = $('ds-list');
if (!items.length) {
ul.innerHTML = '<li class="empty-msg">No templates found.</li>';
return;
}
ul.innerHTML = items.map(t => `
<li class="template-item">
<span class="template-name">${escHtml(t.name)}</span>
<span style="font-size:11px;color:#999">${(t.lastModified || '').slice(0, 10)}</span>
</li>
`).join('');
}
function updateMigrateButton() {
const checked = document.querySelectorAll('#adobe-list input[type=checkbox]:checked');
$('btn-migrate').disabled = checked.length === 0;
}
// ── Migration ─────────────────────────────────────────────────────────────────
async function onMigrate() {
const checked = [...document.querySelectorAll('#adobe-list input[type=checkbox]:checked')];
const ids = checked.map(cb => cb.dataset.id);
if (!ids.length) return;
$('btn-migrate').disabled = true;
setStatus(`Migrating ${ids.length} template(s)…`);
// Show spinners
ids.forEach(id => {
const spin = $('spin-' + id);
if (spin) spin.textContent = '⏳';
});
try {
const resp = await fetch('/api/migrate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ adobe_template_ids: ids }),
});
const data = await resp.json();
let successCount = 0;
(data.results || []).forEach(r => {
const spin = $('spin-' + r.adobe_template_id);
if (r.status === 'success') {
successCount++;
if (spin) spin.textContent = r.action === 'updated' ? '✏️' : '✅';
} else {
if (spin) spin.textContent = '❌';
}
});
setStatus(`Done: ${successCount}/${ids.length} succeeded.`);
await refreshTemplates();
await refreshHistory();
} catch (e) {
setStatus('Migration error: ' + e.message);
}
}
// ── History ───────────────────────────────────────────────────────────────────
async function refreshHistory() {
try {
const resp = await fetch('/api/migrate/history');
const { history } = await resp.json();
renderHistory(history || []);
} catch {
renderHistory([]);
}
}
function renderHistory(records) {
const tbody = $('history-tbody');
if (!records.length) {
tbody.innerHTML = '<tr><td colspan="5" class="empty-msg">No migrations yet.</td></tr>';
return;
}
tbody.innerHTML = [...records].reverse().slice(0, 50).map(r => `
<tr>
<td>${(r.timestamp || '').replace('T', ' ').slice(0, 19)}</td>
<td>${escHtml(r.adobe_template_name || r.adobe_template_id || '')}</td>
<td>${escHtml(r.docusign_template_id || '—')}</td>
<td>${escHtml(r.action || '—')}</td>
<td>
<span class="badge ${r.status === 'success' ? 'badge-migrated' : 'badge-not_migrated'}">
${r.status}
</span>
</td>
</tr>
`).join('');
}
// ── Utilities ─────────────────────────────────────────────────────────────────
function setStatus(msg) { $('status-msg').textContent = msg; }
function statusLabel(s) {
return { not_migrated: 'Not Migrated', migrated: 'Migrated', needs_update: 'Needs Update' }[s] || s;
}
function escHtml(str) {
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}