Loading…+
diff --git a/frontend-dapp/public/omnl/compliance/index.html b/frontend-dapp/public/omnl/compliance/index.html new file mode 100644 index 0000000..37c848d --- /dev/null +++ b/frontend-dapp/public/omnl/compliance/index.html @@ -0,0 +1,84 @@ + + +
+ + + +HYBX · IPSAS / IFRS / US GAAP · Web3 notary & reserve attestation · Chain 138
+→ OMNL Compliance Console (posture, pending actions, Safe tx download)
+Reads /api/v1/omnl/ipsas/registry and /api/v1/omnl/ipsas/fineract-compare (503 if Fineract env missing; 401 if OMNL_API_KEY is set and no access_token in this page URL).
OpenAPI 3 JSON · OMNL API catalog · integration status · registry JSON · matrix JSON · Fineract health
+Loading…+
Loading…+ + + + diff --git a/frontend-dapp/public/omnl/static/omnl-api-base.js b/frontend-dapp/public/omnl/static/omnl-api-base.js new file mode 100644 index 0000000..2f79ad0 --- /dev/null +++ b/frontend-dapp/public/omnl/static/omnl-api-base.js @@ -0,0 +1,8 @@ +(function () { + /** auto → same-origin /api/v1 (vite proxy, portal-serve, or token-aggregation). */ + window.resolveOmnlApiBase = function () { + const meta = document.querySelector('meta[name="omnl-api-base"]')?.getAttribute('content')?.trim(); + if (meta && meta !== 'auto') return meta.replace(/\/$/, ''); + return '/api/v1'; + }; +})(); diff --git a/frontend-dapp/public/omnl/static/omnl-compliance-console.css b/frontend-dapp/public/omnl/static/omnl-compliance-console.css new file mode 100644 index 0000000..41ed3ec --- /dev/null +++ b/frontend-dapp/public/omnl/static/omnl-compliance-console.css @@ -0,0 +1,219 @@ +:root { + --bg: #0c0f14; + --panel: #141a22; + --border: #243044; + --text: #e8edf4; + --muted: #8b9cb3; + --ok: #3dd68c; + --warn: #f5c542; + --bad: #f07178; + --accent: #4ade80; + --mono: ui-monospace, 'Cascadia Code', 'SF Mono', monospace; +} + +* { box-sizing: border-box; } + +body { + margin: 0; + font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.45; +} + +.wrap { + max-width: 72rem; + margin: 0 auto; + padding: 1.25rem 1rem 3rem; +} + +header { + display: flex; + flex-wrap: wrap; + gap: 0.75rem 1.5rem; + align-items: baseline; + justify-content: space-between; + margin-bottom: 1.25rem; +} + +h1 { + font-size: 1.35rem; + font-weight: 650; + margin: 0; +} + +.sub { + color: var(--muted); + font-size: 0.9rem; + margin: 0.25rem 0 0; +} + +.toolbar { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: center; +} + +button, .btn { + appearance: none; + border: 1px solid var(--border); + background: var(--panel); + color: var(--text); + border-radius: 8px; + padding: 0.45rem 0.75rem; + font-size: 0.85rem; + cursor: pointer; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 0.35rem; +} + +button:hover, .btn:hover { border-color: var(--accent); color: var(--accent); } +button.primary { background: #14532d; border-color: #22c55e; } +button.primary:hover { background: #166534; } + +.grid { + display: grid; + gap: 1rem; + grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); + margin-bottom: 1rem; +} + +.card { + background: var(--panel); + border: 1px solid var(--border); + border-radius: 12px; + padding: 1rem; +} + +.card h2 { + font-size: 0.78rem; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--muted); + margin: 0 0 0.75rem; +} + +.section { margin-bottom: 1.25rem; } + +.section > h2 { + font-size: 1rem; + margin: 0 0 0.65rem; +} + +.badge { + display: inline-block; + padding: 0.2rem 0.55rem; + border-radius: 999px; + font-size: 0.78rem; + font-weight: 600; + border: 1px solid transparent; +} + +.badge.ok { background: #143528; color: var(--ok); border-color: #2a6b4a; } +.badge.warn { background: #3a3014; color: var(--warn); border-color: #6b5620; } +.badge.bad { background: #3a1c20; color: var(--bad); border-color: #6b3038; } +.badge.neutral { background: #1c2430; color: var(--muted); border-color: var(--border); } + +.metric { + font-size: 1.1rem; + font-weight: 650; +} + +.metric small { + display: block; + font-size: 0.78rem; + font-weight: 400; + color: var(--muted); + margin-top: 0.15rem; +} + +.actions-list { list-style: none; padding: 0; margin: 0; } + +.actions-list li { + border: 1px solid var(--border); + border-radius: 10px; + padding: 0.85rem 1rem; + margin-bottom: 0.65rem; + background: #10151c; +} + +.actions-list h3 { + margin: 0 0 0.35rem; + font-size: 0.95rem; +} + +.actions-list p { + margin: 0 0 0.5rem; + color: var(--muted); + font-size: 0.88rem; +} + +.link-row { display: flex; flex-wrap: wrap; gap: 0.5rem; } + +.link-row a { + color: var(--accent); + font-size: 0.82rem; + text-decoration: none; +} + +.link-row a:hover { text-decoration: underline; } + +.kv { + display: grid; + grid-template-columns: minmax(8rem, 11rem) 1fr; + gap: 0.35rem 0.75rem; + font-size: 0.86rem; +} + +.kv dt { color: var(--muted); margin: 0; } +.kv dd { margin: 0; font-family: var(--mono); font-size: 0.8rem; word-break: break-all; } + +pre.raw { + background: #090c10; + border: 1px solid var(--border); + border-radius: 10px; + padding: 0.85rem; + overflow: auto; + font-size: 0.72rem; + line-height: 1.35; + max-height: 24rem; +} + +.status-bar { + padding: 0.65rem 0.85rem; + border-radius: 8px; + margin-bottom: 1rem; + font-size: 0.88rem; +} + +.status-bar.loading { background: #1a2430; color: var(--muted); } +.status-bar.error { background: #3a1c20; color: var(--bad); } +.status-bar.ok { background: #143528; color: var(--ok); } + +.hidden { display: none !important; } + +footer { + margin-top: 2rem; + color: var(--muted); + font-size: 0.8rem; +} + +footer a { color: var(--accent); } + +@media (max-width: 640px) { + .kv { grid-template-columns: 1fr; } + .wrap { padding-left: 0.75rem; padding-right: 0.75rem; } + header { flex-direction: column; align-items: stretch; } + .toolbar { width: 100%; } + .toolbar button, + .toolbar .btn { + min-height: 44px; + flex: 1 1 calc(50% - 0.25rem); + justify-content: center; + } + h1 { font-size: 1.15rem; } + pre, table { font-size: 12px; } +} diff --git a/frontend-dapp/public/omnl/static/omnl-settlement-terminal.css b/frontend-dapp/public/omnl/static/omnl-settlement-terminal.css new file mode 100644 index 0000000..74a8da0 --- /dev/null +++ b/frontend-dapp/public/omnl/static/omnl-settlement-terminal.css @@ -0,0 +1,190 @@ +:root { + --font-mono: "Consolas", "Lucida Console", "Courier New", monospace; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-height: 100vh; + font-family: system-ui, sans-serif; +} + +body.theme-black { + background: #0a0a0a; + color: #33ff66; +} + +body.theme-color { + background: #d8d8d8; + color: #111; +} + +.layout { + max-width: 1200px; + margin: 0 auto; + padding: 1rem 1.25rem 2rem; +} + +.topbar { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 1rem; + align-items: flex-start; + margin-bottom: 1rem; +} + +h1 { + font-size: 1.15rem; + margin: 0 0 0.25rem; +} + +.sub { + margin: 0; + opacity: 0.85; + font-size: 0.85rem; +} + +.toolbar { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +button, +.btn { + font: inherit; + cursor: pointer; + border: 1px solid currentColor; + background: transparent; + color: inherit; + padding: 0.35rem 0.65rem; + border-radius: 4px; + text-decoration: none; + font-size: 0.85rem; +} + +button.active { + font-weight: 700; +} + +.card { + border: 1px solid currentColor; + border-radius: 6px; + padding: 0.75rem; + margin-bottom: 1rem; +} + +.params { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); + gap: 0.75rem; +} + +.params label { + display: flex; + flex-direction: column; + gap: 0.25rem; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.params input { + font: inherit; + padding: 0.35rem 0.5rem; + border: 1px solid currentColor; + background: transparent; + color: inherit; + border-radius: 4px; +} + +.main { + display: grid; + grid-template-columns: 1fr min(22rem, 100%); + gap: 1rem; +} + +@media (max-width: 900px) { + .main { + grid-template-columns: 1fr; + } +} + +.terminal { + margin: 0; + min-height: 28rem; + max-height: 70vh; + overflow: auto; + font-family: var(--font-mono); + font-size: 0.78rem; + line-height: 1.35; + white-space: pre-wrap; + word-break: break-word; +} + +body.theme-black .terminal { + background: #000; + color: #33ff66; + padding: 1rem; +} + +body.theme-color .terminal { + background: #ececec; + color: #111; + padding: 1rem; +} + +.side h2 { + font-size: 0.85rem; + margin: 0 0 0.5rem; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.cmd-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.35rem; + margin-bottom: 0.75rem; +} + +.alchemy-row { + display: flex; + flex-direction: column; + gap: 0.25rem; + font-size: 0.75rem; + margin-bottom: 0.75rem; +} + +.alchemy-row input { + font: inherit; + padding: 0.35rem; + border: 1px solid currentColor; + background: transparent; + color: inherit; + border-radius: 4px; +} + +.json-out { + margin: 0; + max-height: 16rem; + overflow: auto; + font-family: var(--font-mono); + font-size: 0.7rem; + white-space: pre-wrap; + word-break: break-word; + opacity: 0.9; +} + +footer { + font-size: 0.75rem; + opacity: 0.8; +} + +footer code { + font-family: var(--font-mono); +} diff --git a/frontend-dapp/public/omnl/static/omnl-settlement-terminal.js b/frontend-dapp/public/omnl/static/omnl-settlement-terminal.js new file mode 100644 index 0000000..09e8174 --- /dev/null +++ b/frontend-dapp/public/omnl/static/omnl-settlement-terminal.js @@ -0,0 +1,219 @@ +(function () { + const API_BASE = window.resolveOmnlApiBase + ? window.resolveOmnlApiBase() + : document.querySelector('meta[name="omnl-api-base"]')?.getAttribute('content') || '/api/v1'; + const screenEl = document.getElementById('terminal-screen'); + const jsonEl = document.getElementById('json-out'); + const inpRef = document.getElementById('inp-settlement-ref'); + const inpOffice = document.getElementById('inp-office-id'); + const inpDate = document.getElementById('inp-value-date'); + const inpAmount = document.getElementById('inp-amount'); + const inpToken = document.getElementById('inp-token'); + const inpAlchemyAddr = document.getElementById('inp-alchemy-addr'); + + let screenMode = 'black'; + + const inpConnection = document.getElementById('inp-connection'); + const DEFAULT_CONNECTION = 'mk-albert-gate-limited'; + const DEFAULT_WALLET = '0xb3B416BdE671c256aAbFB56358baD91D46BB9c39'; + + (function initFromQuery() { + const p = new URLSearchParams(window.location.search); + const conn = p.get('connectionId') || DEFAULT_CONNECTION; + if (inpConnection) inpConnection.value = conn; + const ref = p.get('settlementRef'); + if (ref) inpRef.value = ref; + })(); + + function qs() { + const p = new URLSearchParams(window.location.search); + const token = inpToken.value.trim() || p.get('access_token') || ''; + const q = new URLSearchParams({ + settlementRef: inpRef.value.trim(), + officeId: String(inpOffice.value || '1'), + valueDate: inpDate.value, + amountUsd: inpAmount.value.trim(), + }); + const conn = inpConnection && inpConnection.value ? inpConnection.value.trim() : ''; + if (conn) q.set('connectionId', conn); + if (token) q.set('access_token', token); + return q; + } + + function authHeaders() { + const token = inpToken.value.trim() || new URLSearchParams(window.location.search).get('access_token') || ''; + return token ? { Authorization: 'Bearer ' + token } : {}; + } + + async function apiGet(path, extra) { + const q = qs(); + if (extra) { + Object.entries(extra).forEach(function (kv) { + q.set(kv[0], kv[1]); + }); + } + const res = await fetch(API_BASE + path + '?' + q.toString(), { headers: authHeaders() }); + const text = await res.text(); + let data; + try { + data = JSON.parse(text); + } catch { + data = { raw: text, status: res.status }; + } + if (!res.ok) throw new Error(data.error || data.raw || res.statusText); + return data; + } + + async function apiPost(path, body) { + const q = qs(); + const res = await fetch(API_BASE + path + '?' + q.toString(), { + method: 'POST', + headers: { 'Content-Type': 'application/json', ...authHeaders() }, + body: JSON.stringify(body), + }); + const data = await res.json().catch(function () { + return {}; + }); + if (!res.ok) throw new Error(data.error || res.statusText); + return data; + } + + function showJson(data) { + jsonEl.textContent = JSON.stringify(data, null, 2); + } + + function showScreen(text) { + screenEl.textContent = text; + } + + function setTheme(mode) { + screenMode = mode === 'color' ? 'color' : 'black'; + document.body.classList.toggle('theme-black', screenMode === 'black'); + document.body.classList.toggle('theme-color', screenMode === 'color'); + document.getElementById('btn-mode-black').classList.toggle('active', screenMode === 'black'); + document.getElementById('btn-mode-color').classList.toggle('active', screenMode === 'color'); + } + + async function loadScreen(artifact) { + const data = await apiGet('/omnl/terminal/screen', { + mode: screenMode, + artifact: artifact || 'package', + format: 'json', + }); + showScreen(data.text || ''); + showJson(data); + } + + async function runCmd(cmd) { + try { + if (cmd === 'help') { + showScreen( + [ + 'OMNL / HYBX SETTLEMENT TERMINAL — COMMANDS', + '', + ' status — integration status', + ' package — full JSON package', + ' pof — proof of funds', + ' debit-note — debit note', + ' remittance — remittance advice', + ' token-san — tokenization sanitized copy', + ' swift-copy — conversion SWIFT copy', + ' screen — terminal screen (current theme)', + ' alchemy balance — eth_getBalance via Alchemy', + '', + 'Toggle Black/Green vs Gray/Black with toolbar buttons.', + ].join('\n') + ); + return; + } + if (cmd === 'status') { + const q = qs(); + const token = q.get('access_token'); + const url = API_BASE + '/omnl/terminal/status' + (token ? '?access_token=' + encodeURIComponent(token) : ''); + const data = await fetch(url, { headers: authHeaders() }).then(function (r) { + return r.json(); + }); + showJson(data); + showScreen(JSON.stringify(data, null, 2)); + return; + } + if (cmd === 'package') { + const data = await apiGet('/omnl/terminal/package'); + showJson(data); + if (data.artifacts && data.artifacts.screens) { + showScreen(data.artifacts.screens[screenMode] || ''); + } + return; + } + if (cmd === 'pof') { + const data = await apiGet('/omnl/terminal/proof-of-funds'); + showJson(data); + await loadScreen('proof-of-funds'); + return; + } + if (cmd === 'debit') { + const data = await apiGet('/omnl/terminal/debit-note'); + showJson(data); + await loadScreen('debit-note'); + return; + } + if (cmd === 'remittance') { + const data = await apiGet('/omnl/terminal/remittance-advice'); + showJson(data); + await loadScreen('remittance-advice'); + return; + } + if (cmd === 'token') { + const data = await apiGet('/omnl/terminal/tokenization-sanitized'); + showJson(data); + await loadScreen('tokenization-sanitized'); + return; + } + if (cmd === 'swift') { + const data = await apiGet('/omnl/terminal/conversion-swift-copy'); + showJson(data); + await loadScreen('conversion-swift-copy'); + return; + } + if (cmd === 'screen') { + await loadScreen('package'); + return; + } + if (cmd === 'alchemy-balance') { + const addr = (inpAlchemyAddr.value || '').trim(); + if (!addr) throw new Error('Set Alchemy address first'); + const data = await apiPost('/omnl/terminal/alchemy/rpc', { + network: 'eth-mainnet', + method: 'eth_getBalance', + params: [addr, 'latest'], + connectionId: (inpConnection && inpConnection.value) || DEFAULT_CONNECTION, + }); + showJson(data); + showScreen('ALCHEMY eth_getBalance\nADDRESS: ' + addr + '\nRESULT: ' + data.result); + return; + } + } catch (e) { + showScreen('ERROR: ' + (e && e.message ? e.message : String(e))); + } + } + + document.getElementById('btn-mode-black').addEventListener('click', function () { + setTheme('black'); + runCmd('screen'); + }); + document.getElementById('btn-mode-color').addEventListener('click', function () { + setTheme('color'); + runCmd('screen'); + }); + document.getElementById('btn-refresh').addEventListener('click', function () { + runCmd('screen'); + }); + document.querySelectorAll('[data-cmd]').forEach(function (btn) { + btn.addEventListener('click', function () { + runCmd(btn.getAttribute('data-cmd')); + }); + }); + + setTheme('black'); + runCmd('screen'); +})(); diff --git a/frontend-dapp/public/omnl/terminal/index.html b/frontend-dapp/public/omnl/terminal/index.html new file mode 100644 index 0000000..17803b4 --- /dev/null +++ b/frontend-dapp/public/omnl/terminal/index.html @@ -0,0 +1,76 @@ + + + + + + +
Fineract SoR · Alchemy on-chain · institutional document API
+Loading…+