import type { SASjsRequest } from '@sasjs/adapter' import { appContext } from '../core/AppContext' import styles from './RequestsModal.css?inline' export class RequestsModal extends HTMLElement { private static styleSheet = new CSSStyleSheet() private shadow: ShadowRoot private dialog: HTMLDialogElement | null = null static { this.styleSheet.replaceSync(styles) } constructor() { super() this.shadow = this.attachShadow({ mode: 'open' }) this.shadow.adoptedStyleSheets = [RequestsModal.styleSheet] } connectedCallback() { this.render() this.attachEventListeners() } render() { this.shadow.innerHTML = ` ` } attachEventListeners() { const dialog = this.shadow.getElementById( 'requests-dialog' ) as HTMLDialogElement const closeBtn = this.shadow.getElementById('close-btn') this.dialog = dialog closeBtn?.addEventListener('click', () => this.closeModal()) dialog?.addEventListener('click', (e) => { if (e.target === dialog) { this.closeModal() } }) } openModal() { if (!this.dialog) return const adapter = appContext.getAdapter() if (!adapter) return const requests = adapter.getSasRequests() const title = this.shadow.getElementById('modal-title') const content = this.shadow.getElementById('modal-content') if (!title || !content) return title.textContent = 'Last 20 requests' if (!requests || requests.length === 0) { content.innerHTML = `
🐛

There are no requests available.

Please run a test and check again.
` } else { content.innerHTML = `
${requests .map((request, index) => this.renderRequest(request, index)) .join('')}
` this.attachTabListeners() } this.dialog.showModal() } closeModal() { this.dialog?.close() } renderRequest(request: SASjsRequest, index: number): string { const timestamp = new Date(request.timestamp) const formattedDate = timestamp.toLocaleString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric', second: 'numeric' }) const timeAgo = this.getTimeAgo(timestamp) return `
${request.serviceLink} ${formattedDate} (${timeAgo})
${this.decodeHtml(request.logFile)}
${this.decodeHtml(request.sourceCode)}
${this.decodeHtml(request.generatedCode)}
` } attachTabListeners() { const tabBtns = this.shadow.querySelectorAll('.tab-btn') tabBtns.forEach((btn) => { btn.addEventListener('click', (e) => { const target = e.currentTarget as HTMLElement const tabId = target.getAttribute('data-tab') if (!tabId) return const container = target.closest('.request-content') if (!container) return container .querySelectorAll('.tab-btn') .forEach((b) => b.classList.remove('active')) container .querySelectorAll('.tab-pane') .forEach((p) => p.classList.remove('active')) target.classList.add('active') const pane = container.querySelector(`#${tabId}`) pane?.classList.add('active') }) }) } decodeHtml(encodedString: string): string { const tempElement = document.createElement('textarea') tempElement.innerHTML = encodedString return tempElement.value } getTimeAgo(date: Date): string { const seconds = Math.floor((new Date().getTime() - date.getTime()) / 1000) if (seconds < 60) return `${seconds} seconds ago` const minutes = Math.floor(seconds / 60) if (minutes < 60) return `${minutes} minute${minutes !== 1 ? 's' : ''} ago` const hours = Math.floor(minutes / 60) if (hours < 24) return `${hours} hour${hours !== 1 ? 's' : ''} ago` const days = Math.floor(hours / 24) return `${days} day${days !== 1 ? 's' : ''} ago` } } customElements.define('requests-modal', RequestsModal)