Last active
May 12, 2026 13:25
-
-
Save Windowsfreak/3945740389f8cbcbd735c64a092d5671 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (async () => { | |
| // 1. Configuration & Selectors | |
| const CONTAINER_SELECTOR = 'div.@container/main.flex.flex-1.flex-col.gap-2.w-full'.replace(/([@/])/g, '\\$1'); | |
| const targetElement = document.querySelector(CONTAINER_SELECTOR); | |
| if (!targetElement) { | |
| console.error("Target container not found:", CONTAINER_SELECTOR); | |
| return; | |
| } | |
| // 2. Load Dependencies (vis-network) | |
| if (typeof vis === 'undefined') { | |
| const script = document.createElement('script'); | |
| script.src = "https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"; | |
| document.head.appendChild(script); | |
| await new Promise(resolve => script.onload = resolve); | |
| } | |
| // 3. Inject Scoped Styles & Prepare Container | |
| targetElement.innerHTML = ` | |
| <style> | |
| #aurum-tree-wrapper { | |
| width: 100%; | |
| height: 800px; | |
| background: radial-gradient(circle at top right, #1e293b 0%, #0f172a 100%); | |
| border-radius: 12px; | |
| position: relative; | |
| overflow: hidden; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| #aurum-tree-network { width: 100%; height: 100%; } | |
| .tree-overlay { | |
| position: absolute; top: 20px; left: 20px; z-index: 5; | |
| background: rgba(30, 41, 59, 0.7); backdrop-filter: blur(8px); | |
| padding: 15px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.1); | |
| pointer-events: none; | |
| } | |
| .tree-error { | |
| padding: 40px; color: #f87171; text-align: center; font-family: sans-serif; | |
| background: rgba(0,0,0,0.2); height: 100%; display: flex; align-items: center; justify-content: center; | |
| } | |
| </style> | |
| <div id="aurum-tree-wrapper"> | |
| <div class="tree-overlay"> | |
| <h3 style="margin:0; color:#38bdf8; font-size:1.1rem;">Network Tree</h3> | |
| <p style="margin:5px 0 0 0; color:#94a3b8; font-size:0.8rem;">Hierarchical Balance Analysis</p> | |
| </div> | |
| <div id="aurum-tree-network"></div> | |
| </div> | |
| `; | |
| // 4. Authentication Check | |
| const rawToken = localStorage.getItem('ib_portal_auth_token'); | |
| if (!rawToken) { | |
| targetElement.innerHTML = `<div class="tree-error">Error: No 'ib_portal_auth_token' found in localStorage. Please log in first.</div>`; | |
| return; | |
| } | |
| const token = rawToken.replace(/^"|"$/g, ''); | |
| const headers = { | |
| 'Accept': 'application/json, text/plain, */*', | |
| 'Authorization': `Bearer ${token}` | |
| }; | |
| // 5. Fetch & Process Data | |
| const API_URL = 'https://api.ibportal.io/api/Backoffice/v1.0/Tree/tag/UnilevelTree?minLevel=1&maxLevel=10&includeSelf=true'; | |
| try { | |
| const response = await fetch(API_URL, { headers }); | |
| if (!response.ok) throw new Error(`HTTP ${response.status}`); | |
| const data = await response.json(); | |
| const nodes = []; | |
| const edges = []; | |
| const dataMap = new Map(); | |
| // Pass 1: Map for lookups | |
| data.forEach(item => dataMap.set(item.distributorId, { ...item, children: [] })); | |
| // Pass 2: Build hierarchy | |
| data.forEach(item => { | |
| const parent = dataMap.get(item.enrollerId); | |
| if (parent) parent.children.push(item.distributorId); | |
| }); | |
| // Pass 3: Compute Net Balances & Build Vis Nodes | |
| data.forEach(item => { | |
| const distributor = dataMap.get(item.distributorId); | |
| const childrenSum = distributor.children.reduce((s, cid) => s + (dataMap.get(cid)?.activeTotalBalance || 0), 0); | |
| const netBalance = item.activeTotalBalance - childrenSum; | |
| const x = Math.max(512, netBalance); | |
| const margin = Math.round((Math.log2(x) - 8) * 4); | |
| const hue = (item.level * 36) % 360; | |
| nodes.push({ | |
| id: item.distributorId, | |
| label: `<b>${item.fullName}</b>\n${netBalance.toFixed(2)}`, | |
| color: { | |
| background: `hsla(${hue}, 70%, 25%, 0.9)`, | |
| border: `hsla(${hue}, 80%, 50%, 1)`, | |
| highlight: { background: `hsla(${hue}, 80%, 40%, 1)`, border: '#fff' } | |
| }, | |
| shape: 'box', | |
| shapeProperties: { | |
| borderDashes: !item.isQualified ? [5, 5] : false | |
| }, | |
| margin: margin, | |
| level: item.level, | |
| font: { color: '#fff', size: 13, multi: 'html', align: 'center' }, | |
| borderWidth: 2, | |
| shadow: { enabled: true, color: 'rgba(0,0,0,0.3)', size: 5 } | |
| }); | |
| if (dataMap.has(item.enrollerId)) { | |
| edges.push({ | |
| from: item.enrollerId, to: item.distributorId, | |
| arrows: 'to', color: { color: 'rgba(255,255,255,0.15)' }, | |
| width: 1, smooth: { type: 'cubicBezier', forceDirection: 'vertical', roundness: 0.4 } | |
| }); | |
| } | |
| }); | |
| // 6. Initialize Network | |
| const container = document.getElementById('aurum-tree-network'); | |
| new vis.Network(container, { nodes: new vis.DataSet(nodes), edges: new vis.DataSet(edges) }, { | |
| layout: { hierarchical: { direction: 'UD', sortMethod: 'directed', levelSeparation: 120, nodeSpacing: 200 } }, | |
| physics: { enabled: false }, | |
| interaction: { hover: true, dragNodes: false } | |
| }); | |
| } catch (e) { | |
| console.error("Tree Rendering Error:", e); | |
| targetElement.innerHTML = `<div class="tree-error">Failed to load tree data: ${e.message}</div>`; | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment