Merge branch 'dev'
CI/CD / deploy (push) Successful in 2m19s
Details
CI/CD / deploy (push) Successful in 2m19s
Details
This commit is contained in:
commit
d0db0ffde7
|
|
@ -195,12 +195,71 @@ class FolkGraphViewer extends HTMLElement {
|
||||||
|
|
||||||
private _delegationsHandler: ((e: Event) => void) | null = null;
|
private _delegationsHandler: ((e: Event) => void) | null = null;
|
||||||
|
|
||||||
|
private _onTabChange = (e: Event) => {
|
||||||
|
const tab = (e as CustomEvent).detail?.tab;
|
||||||
|
if (!tab) return;
|
||||||
|
this.applyTab(tab);
|
||||||
|
};
|
||||||
|
|
||||||
|
private applyTab(tab: string) {
|
||||||
|
const wasTrust = this.trustMode;
|
||||||
|
const wasLayers = this.layersMode;
|
||||||
|
|
||||||
|
switch (tab) {
|
||||||
|
case "members":
|
||||||
|
this.filter = "all";
|
||||||
|
this.trustMode = false;
|
||||||
|
if (wasLayers) this.exitLayersMode();
|
||||||
|
break;
|
||||||
|
case "people":
|
||||||
|
this.filter = "person";
|
||||||
|
this.trustMode = false;
|
||||||
|
if (wasLayers) this.exitLayersMode();
|
||||||
|
break;
|
||||||
|
case "companies":
|
||||||
|
this.filter = "company";
|
||||||
|
this.trustMode = false;
|
||||||
|
if (wasLayers) this.exitLayersMode();
|
||||||
|
break;
|
||||||
|
case "trust":
|
||||||
|
this.filter = "all";
|
||||||
|
this.trustMode = true;
|
||||||
|
if (wasLayers) this.exitLayersMode();
|
||||||
|
if (this.layoutMode !== "rings") {
|
||||||
|
this.layoutMode = "rings";
|
||||||
|
const ringsBtn = this.shadow.getElementById("rings-toggle");
|
||||||
|
if (ringsBtn) ringsBtn.classList.add("active");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "layers":
|
||||||
|
this.filter = "all";
|
||||||
|
this.trustMode = false;
|
||||||
|
if (!wasLayers) this.enterLayersMode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.updateAuthorityBar();
|
||||||
|
// Trust mode change needs full data reload
|
||||||
|
if (this.trustMode !== wasTrust) {
|
||||||
|
this.loadData();
|
||||||
|
} else {
|
||||||
|
this.updateGraphData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.space = this.getAttribute("space") || "demo";
|
this.space = this.getAttribute("space") || "demo";
|
||||||
|
|
||||||
|
// Read initial tab from attribute or URL
|
||||||
|
const attrTab = this.getAttribute("active-tab");
|
||||||
|
if (attrTab) this.applyTab(attrTab);
|
||||||
|
|
||||||
this.renderDOM();
|
this.renderDOM();
|
||||||
this.loadData();
|
this.loadData();
|
||||||
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rnetwork', context: 'Network Graph' }));
|
this._stopPresence = startPresenceHeartbeat(() => ({ module: 'rnetwork', context: 'Network Graph' }));
|
||||||
|
|
||||||
|
// Listen for shell tab bar changes
|
||||||
|
document.addEventListener("rapp-tab-change", this._onTabChange);
|
||||||
|
|
||||||
// Listen for cross-component delegation updates
|
// Listen for cross-component delegation updates
|
||||||
this._delegationsHandler = () => {
|
this._delegationsHandler = () => {
|
||||||
this._textSpriteCache.clear();
|
this._textSpriteCache.clear();
|
||||||
|
|
@ -219,6 +278,7 @@ class FolkGraphViewer extends HTMLElement {
|
||||||
|
|
||||||
disconnectedCallback() {
|
disconnectedCallback() {
|
||||||
this._stopPresence?.();
|
this._stopPresence?.();
|
||||||
|
document.removeEventListener("rapp-tab-change", this._onTabChange);
|
||||||
if (this._keyHandler) {
|
if (this._keyHandler) {
|
||||||
document.removeEventListener("keydown", this._keyHandler);
|
document.removeEventListener("keydown", this._keyHandler);
|
||||||
this._keyHandler = null;
|
this._keyHandler = null;
|
||||||
|
|
@ -869,15 +929,8 @@ class FolkGraphViewer extends HTMLElement {
|
||||||
|
|
||||||
<div class="toolbar">
|
<div class="toolbar">
|
||||||
<input class="search-input" type="text" placeholder="Search nodes..." id="search-input" value="">
|
<input class="search-input" type="text" placeholder="Search nodes..." id="search-input" value="">
|
||||||
<button class="filter-btn active" data-filter="all">All</button>
|
|
||||||
<button class="filter-btn" data-filter="person">People</button>
|
|
||||||
<button class="filter-btn" data-filter="company">Organizations</button>
|
|
||||||
<button class="filter-btn" data-filter="opportunity">Opportunities</button>
|
|
||||||
<button class="filter-btn" data-filter="rspace_user">Members</button>
|
|
||||||
<button class="filter-btn" id="trust-toggle" title="Toggle trust-weighted view">Trust</button>
|
|
||||||
<button class="filter-btn" id="rings-toggle" title="Toggle concentric ring layout">Rings</button>
|
<button class="filter-btn" id="rings-toggle" title="Toggle concentric ring layout">Rings</button>
|
||||||
<button class="filter-btn" id="list-toggle" title="Toggle member list sidebar">List</button>
|
<button class="filter-btn" id="list-toggle" title="Toggle member list sidebar">List</button>
|
||||||
<button class="filter-btn" id="layers-toggle" title="Toggle multi-layer rApp visualization">Layers</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="authority-bar" id="authority-bar">
|
<div class="authority-bar" id="authority-bar">
|
||||||
|
|
@ -932,17 +985,6 @@ class FolkGraphViewer extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private attachListeners() {
|
private attachListeners() {
|
||||||
// Filter buttons
|
|
||||||
this.shadow.querySelectorAll("[data-filter]").forEach(el => {
|
|
||||||
el.addEventListener("click", () => {
|
|
||||||
this.filter = (el as HTMLElement).dataset.filter as any;
|
|
||||||
// Update active state
|
|
||||||
this.shadow.querySelectorAll("[data-filter]").forEach(b => b.classList.remove("active"));
|
|
||||||
el.classList.add("active");
|
|
||||||
this.updateGraphData();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
let searchTimeout: any;
|
let searchTimeout: any;
|
||||||
this.shadow.getElementById("search-input")?.addEventListener("input", (e) => {
|
this.shadow.getElementById("search-input")?.addEventListener("input", (e) => {
|
||||||
|
|
@ -951,21 +993,6 @@ class FolkGraphViewer extends HTMLElement {
|
||||||
searchTimeout = setTimeout(() => this.updateGraphData(), 200);
|
searchTimeout = setTimeout(() => this.updateGraphData(), 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Trust toggle
|
|
||||||
this.shadow.getElementById("trust-toggle")?.addEventListener("click", () => {
|
|
||||||
this.trustMode = !this.trustMode;
|
|
||||||
const btn = this.shadow.getElementById("trust-toggle");
|
|
||||||
if (btn) btn.classList.toggle("active", this.trustMode);
|
|
||||||
// Auto-enable rings when trust mode is turned on
|
|
||||||
if (this.trustMode && this.layoutMode !== "rings") {
|
|
||||||
this.layoutMode = "rings";
|
|
||||||
const ringsBtn = this.shadow.getElementById("rings-toggle");
|
|
||||||
if (ringsBtn) ringsBtn.classList.add("active");
|
|
||||||
}
|
|
||||||
this.updateAuthorityBar();
|
|
||||||
this.loadData();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Rings toggle
|
// Rings toggle
|
||||||
this.shadow.getElementById("rings-toggle")?.addEventListener("click", () => {
|
this.shadow.getElementById("rings-toggle")?.addEventListener("click", () => {
|
||||||
this.layoutMode = this.layoutMode === "rings" ? "force" : "rings";
|
this.layoutMode = this.layoutMode === "rings" ? "force" : "rings";
|
||||||
|
|
@ -1028,17 +1055,6 @@ class FolkGraphViewer extends HTMLElement {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Layers toggle
|
|
||||||
this.shadow.getElementById("layers-toggle")?.addEventListener("click", () => {
|
|
||||||
if (this.layersMode) {
|
|
||||||
this.exitLayersMode();
|
|
||||||
} else {
|
|
||||||
this.enterLayersMode();
|
|
||||||
}
|
|
||||||
const btn = this.shadow.getElementById("layers-toggle");
|
|
||||||
if (btn) btn.classList.toggle("active", this.layersMode);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Keyboard shortcuts
|
// Keyboard shortcuts
|
||||||
if (this._keyHandler) document.removeEventListener("keydown", this._keyHandler);
|
if (this._keyHandler) document.removeEventListener("keydown", this._keyHandler);
|
||||||
this._keyHandler = (e: KeyboardEvent) => {
|
this._keyHandler = (e: KeyboardEvent) => {
|
||||||
|
|
@ -1067,10 +1083,6 @@ class FolkGraphViewer extends HTMLElement {
|
||||||
case "F":
|
case "F":
|
||||||
if (this.graph) this.graph.zoomToFit(300, 20);
|
if (this.graph) this.graph.zoomToFit(300, 20);
|
||||||
break;
|
break;
|
||||||
case "t":
|
|
||||||
case "T":
|
|
||||||
this.shadow.getElementById("trust-toggle")?.click();
|
|
||||||
break;
|
|
||||||
case "l":
|
case "l":
|
||||||
case "L":
|
case "L":
|
||||||
this.shadow.getElementById("list-toggle")?.click();
|
this.shadow.getElementById("list-toggle")?.click();
|
||||||
|
|
|
||||||
|
|
@ -692,6 +692,17 @@ routes.get("/api/opportunities", async (c) => {
|
||||||
return c.json({ opportunities });
|
return c.json({ opportunities });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Graph tabs (main view) ──
|
||||||
|
const GRAPH_TABS = [
|
||||||
|
{ id: "members", label: "Members" },
|
||||||
|
{ id: "people", label: "People" },
|
||||||
|
{ id: "companies", label: "Companies" },
|
||||||
|
{ id: "trust", label: "Trust" },
|
||||||
|
{ id: "layers", label: "Layers" },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const GRAPH_TAB_IDS = new Set(GRAPH_TABS.map(t => t.id));
|
||||||
|
|
||||||
// ── CRM sub-route — API-driven CRM view ──
|
// ── CRM sub-route — API-driven CRM view ──
|
||||||
const CRM_TABS = [
|
const CRM_TABS = [
|
||||||
{ id: "pipeline", label: "Pipeline" },
|
{ id: "pipeline", label: "Pipeline" },
|
||||||
|
|
@ -735,6 +746,31 @@ routes.get("/crm/:tabId", (c) => {
|
||||||
return c.html(renderCrm(space, tabId, c.get("isSubdomain")));
|
return c.html(renderCrm(space, tabId, c.get("isSubdomain")));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Graph sub-tab routes ──
|
||||||
|
function renderGraph(space: string, activeTab: string, isSubdomain: boolean) {
|
||||||
|
return renderShell({
|
||||||
|
title: `${space} — Network | rSpace`,
|
||||||
|
moduleId: "rnetwork",
|
||||||
|
spaceSlug: space,
|
||||||
|
modules: getModuleInfoList(),
|
||||||
|
head: GRAPH3D_HEAD,
|
||||||
|
body: `<folk-graph-viewer space="${space}" active-tab="${activeTab}"></folk-graph-viewer>`,
|
||||||
|
scripts: `<script type="module" src="/modules/rnetwork/folk-graph-viewer.js?v=3"></script>`,
|
||||||
|
styles: `<link rel="stylesheet" href="/modules/rnetwork/network.css">`,
|
||||||
|
tabs: [...GRAPH_TABS],
|
||||||
|
activeTab,
|
||||||
|
tabBasePath: isSubdomain ? `/rnetwork` : `/${space}/rnetwork`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
routes.get("/:tabId", (c, next) => {
|
||||||
|
const tabId = c.req.param("tabId");
|
||||||
|
// Only handle graph tab IDs here; let other routes (crm, api, etc.) pass through
|
||||||
|
if (!GRAPH_TAB_IDS.has(tabId as any)) return next();
|
||||||
|
const space = c.req.param("space") || "demo";
|
||||||
|
return c.html(renderGraph(space, tabId, c.get("isSubdomain")));
|
||||||
|
});
|
||||||
|
|
||||||
// ── Page route ──
|
// ── Page route ──
|
||||||
routes.get("/", (c) => {
|
routes.get("/", (c) => {
|
||||||
const space = c.req.param("space") || "demo";
|
const space = c.req.param("space") || "demo";
|
||||||
|
|
@ -744,16 +780,7 @@ routes.get("/", (c) => {
|
||||||
return c.redirect(c.get("isSubdomain") ? `/rnetwork/crm` : `/${space}/rnetwork/crm`, 301);
|
return c.redirect(c.get("isSubdomain") ? `/rnetwork/crm` : `/${space}/rnetwork/crm`, 301);
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.html(renderShell({
|
return c.html(renderGraph(space, "members", c.get("isSubdomain")));
|
||||||
title: `${space} — Network | rSpace`,
|
|
||||||
moduleId: "rnetwork",
|
|
||||||
spaceSlug: space,
|
|
||||||
modules: getModuleInfoList(),
|
|
||||||
head: GRAPH3D_HEAD,
|
|
||||||
body: `<folk-graph-viewer space="${space}"></folk-graph-viewer>`,
|
|
||||||
scripts: `<script type="module" src="/modules/rnetwork/folk-graph-viewer.js?v=2"></script>`,
|
|
||||||
styles: `<link rel="stylesheet" href="/modules/rnetwork/network.css">`,
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── MI Data Export ──
|
// ── MI Data Export ──
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue