// PendingSubmissions.jsx — app-wide pending submission drawer // Groups queue items by Requires attention / Sending / Waiting / Recently synced. (function injectPendingSubmissionStyles() { if (document.getElementById('plc-pending-styles')) return; const css = ` .plc-pending-backdrop { position: fixed; inset: 0; background: rgba(11,31,58,0.42); z-index: 1100; display: flex; justify-content: flex-end; } .plc-pending-drawer { width: min(520px, 100vw); height: 100%; background: #fff; box-shadow: -18px 0 45px rgba(11,31,58,0.24); display: flex; flex-direction: column; animation: plc-pending-in 180ms var(--ease-out); } .plc-pending-head { padding: 20px; border-bottom: 1px solid var(--border-default); display: flex; align-items: flex-start; gap: 14px; } .plc-pending-body { padding: 18px; overflow-y: auto; display: grid; gap: 18px; } .plc-pending-section { display: grid; gap: 10px; } .plc-pending-section__title { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; font-weight: 900; color: var(--color-grey-500); display: flex; align-items: center; justify-content: space-between; } .plc-pending-empty { border: 1px dashed var(--border-default); border-radius: 12px; padding: 28px 18px; text-align: center; color: var(--color-grey-600); background: var(--color-grey-50); } .plc-pending-history { font-size: 12px; color: var(--color-grey-600); display: grid; gap: 8px; } .plc-pending-history__row { display: flex; justify-content: space-between; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--color-grey-100); } @keyframes plc-pending-in { from { transform: translateX(24px); opacity: .6; } to { transform: translateX(0); opacity: 1; } } @media (prefers-reduced-motion: reduce) { .plc-pending-drawer { animation: none; } } `; const tag = document.createElement('style'); tag.id = 'plc-pending-styles'; tag.textContent = css; document.head.appendChild(tag); })(); function PendingSubmissionsDrawer() { const s = window.useSync ? window.useSync() : { drawerOpen: false, items: [], history: [] }; const closeBtnRef = React.useRef(null); const drawerRef = React.useRef(null); React.useEffect(() => { if (!s.drawerOpen) return; setTimeout(() => closeBtnRef.current && closeBtnRef.current.focus(), 0); const onKey = (e) => { if (e.key === 'Escape') window.PLCSync.setDrawerOpen(false); if (e.key === 'Tab' && drawerRef.current) { const nodes = Array.from(drawerRef.current.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')).filter(el => !el.disabled); if (!nodes.length) return; const first = nodes[0], last = nodes[nodes.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } } }; document.addEventListener('keydown', onKey); return () => document.removeEventListener('keydown', onKey); }, [s.drawerOpen]); if (!s.drawerOpen) return null; const cfg = window.STATUS_CONFIG || {}; const group = (name) => (s.items || []).filter(i => (cfg[i.current_status] || {}).group === name); const attention = group('attention'); const sending = (s.items || []).filter(i => i.current_status === 'sending'); const waiting = (s.items || []).filter(i => ['queued', 'retry_scheduled', 'failed_temporary'].includes(i.current_status)); const history = s.history || []; const Section = ({ title, rows, children }) => (
{title}{rows ? rows.length : null}
{children || (rows && rows.length ? rows.map(item => ) : null)}
); return (
{ if (e.target === e.currentTarget) window.PLCSync.setDrawerOpen(false); }}>
); } window.PendingSubmissionsDrawer = PendingSubmissionsDrawer;