const ENVELOPE_VERSION = '1';
const app = document.getElementById('app');
const ticketSection = document.getElementById('ticket-content');
const loadingEl = document.getElementById('loading');
const refreshBtn = document.getElementById('refresh-btn');
const params = new URLSearchParams(window.location.search);
const extensionId = params.get('extensionId') || '';
const tenantId = params.get('tenant') || '';
const defaultLimit = 10;
const hostOrigin = resolveHostOrigin();
let bootstrapSession = null;
let lastError = null;
function resolveHostOrigin() {
const referrer = document.referrer;
if (referrer) {
try {
return new URL(referrer).origin;
} catch {
// ignore invalid referrer
}
}
try {
if (window.parent && window.parent !== window && window.parent.location) {
return window.parent.location.origin;
}
} catch {
// cross-origin access throws; swallow and fallback
}
return window.location.origin;
}
function buildProxyUrl(id) {
const base = hostOrigin || window.location.origin;
const path = `/api/ext-proxy/${encodeURIComponent(id)}/tickets/list`;
try {
return new URL(path, base).toString();
} catch {
return path;
}
}
function postReady(requestId) {
window.parent?.postMessage(
{
alga: true,
version: ENVELOPE_VERSION,
type: 'ready',
request_id: requestId,
payload: {},
},
'*'
);
}
function renderTickets(tickets) {
ticketSection.innerHTML = '';
if (!tickets || tickets.length === 0) {
const empty = document.createElement('p');
empty.textContent = 'No open tickets 🎉';
ticketSection.appendChild(empty);
return;
}
const table = document.createElement('table');
const thead = document.createElement('thead');
thead.innerHTML = `
| Ticket |
Title |
Status |
Assignee |
`;
table.appendChild(thead);
const tbody = document.createElement('tbody');
tickets.forEach((ticket) => {
const tr = document.createElement('tr');
const status = (ticket.status || '').toLowerCase();
const assignee = ticket.assignee || 'Unassigned';
tr.innerHTML = `
${ticket.id} |
${ticket.title ?? '—'} |
${status || 'unknown'} |
${assignee} |
`;
tbody.appendChild(tr);
});
table.appendChild(tbody);
ticketSection.appendChild(table);
}
function renderError(message) {
ticketSection.innerHTML = '';
const div = document.createElement('div');
div.className = 'error';
div.innerHTML = `
Unable to load tickets.
${message}
`;
ticketSection.appendChild(div);
}
function setLoading(isLoading) {
if (isLoading) {
loadingEl.dataset.state = 'loading';
loadingEl.textContent = 'Loading ticket list…';
if (!ticketSection.contains(loadingEl)) {
ticketSection.appendChild(loadingEl);
}
} else {
loadingEl.dataset.state = 'idle';
if (ticketSection.contains(loadingEl)) {
ticketSection.removeChild(loadingEl);
}
}
}
async function fetchTickets(limit = defaultLimit) {
if (!extensionId) {
renderError('Missing extension context.');
return;
}
setLoading(true);
lastError = null;
try {
const proxyUrl = buildProxyUrl(extensionId);
const resp = await fetch(proxyUrl, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({ limit }),
credentials: 'include',
});
const result = await resp.json();
if (!resp.ok || !result?.ok) {
const msg =
result?.error || `Proxy request failed (${resp.status})`;
lastError = msg;
renderError(msg);
} else {
renderTickets(result.tickets || []);
}
} catch (err) {
lastError = err instanceof Error ? err.message : String(err);
renderError(lastError);
} finally {
setLoading(false);
reportResize();
}
}
function reportResize() {
const height = app.getBoundingClientRect().height;
window.parent?.postMessage(
{
alga: true,
version: ENVELOPE_VERSION,
type: 'resize',
payload: { height },
},
'*'
);
}
window.addEventListener('message', (event) => {
const data = event.data;
if (!data || typeof data !== 'object') return;
if (data.alga !== true || data.version !== ENVELOPE_VERSION) return;
if (data.type === 'bootstrap') {
bootstrapSession = data.payload?.session ?? null;
// Use session token for future requests if required
if (bootstrapSession?.token) {
window.sessionStorage.setItem('alga-ext-session-token', bootstrapSession.token);
}
fetchTickets();
}
});
refreshBtn.addEventListener('click', () => {
fetchTickets();
});
window.addEventListener('load', () => {
postReady();
fetchTickets();
});
// Periodically report size changes (for dynamic content)
const resizeObserver = new ResizeObserver(() => reportResize());
resizeObserver.observe(app);