From 387f59ef5c6af25fc6c8ed6bdd5f575f1e4c69c9 Mon Sep 17 00:00:00 2001 From: coodos Date: Wed, 22 Apr 2026 16:49:04 +0530 Subject: [PATCH] feat: blabsy and pictqiue filter --- .../dev-sandbox/src/routes/+page.svelte | 232 ++++++++++++++++++ .../client/src/components/chat/chat-list.tsx | 7 +- .../routes/(protected)/messages/+page.svelte | 6 +- 3 files changed, 241 insertions(+), 4 deletions(-) diff --git a/infrastructure/dev-sandbox/src/routes/+page.svelte b/infrastructure/dev-sandbox/src/routes/+page.svelte index de6147f80..45f65d5a3 100644 --- a/infrastructure/dev-sandbox/src/routes/+page.svelte +++ b/infrastructure/dev-sandbox/src/routes/+page.svelte @@ -36,6 +36,10 @@ const config = $derived({ let ontologies: { id: string; title: string }[] = $state([]); let selectedOntologyId: string | null = $state(null); let inspectorEName: string = $state(""); +const ontologyTitleMap = $derived( + new Map(ontologies.map((o) => [o.id, o.title || o.id])), +); + let schemasLoading = $state(false); let schemasError: string | null = $state(null); @@ -258,6 +262,88 @@ let beforeCursor: string | null = $state(null); let pageOffset: number = $state(0); let currentTab = $state("sandbox"); +// eVault Logs state +interface EvaultLog { + id: string; + eName: string; + metaEnvelopeId: string; + envelopeHash: string; + operation: string; + platform: string; + timestamp: string; + ontology: string; +} +let logsEName: string = $state(""); +let evaultLogs: EvaultLog[] = $state([]); +let logsLoading = $state(false); +let logsError: string | null = $state(null); +let logsNextCursor: string | null = $state(null); +let logsHasMore = $state(false); +let logsPageSize = 20; + +async function loadEvaultLogs(cursor?: string | null): Promise { + const ename = logsEName.trim() || selectedIdentity?.w3id; + if (!ename) { + logsError = "Enter an eName or provision an identity first."; + return; + } + + logsLoading = true; + logsError = null; + if (ontologies.length === 0) await loadOntologies(); + try { + const token = await getPlatformToken(); + + // Resolve eVault URI + let baseUrl: string; + const lookupEName = logsEName.trim(); + if (!lookupEName && selectedIdentity) { + baseUrl = selectedIdentity.uri.replace(/\/+$/, ""); + } else { + const resolveRes = await fetch( + new URL(`resolve?w3id=${encodeURIComponent(ename)}`, registryUrl).toString(), + ); + if (!resolveRes.ok) throw new Error(`Registry resolve failed: ${resolveRes.status}`); + const resolveData = await resolveRes.json(); + if (!resolveData.uri) throw new Error("Registry returned no URI for that eName."); + baseUrl = resolveData.uri.replace(/\/+$/, ""); + } + + const params = new URLSearchParams({ limit: String(logsPageSize) }); + if (cursor) params.set("cursor", cursor); + + const headers: Record = { "X-ENAME": ename }; + if (token) headers["Authorization"] = `Bearer ${token}`; + + const res = await fetch(`${baseUrl}/logs?${params.toString()}`, { headers }); + if (!res.ok) throw new Error(`Logs request failed: ${res.status}`); + + const data = await res.json(); + if (cursor) { + evaultLogs = [...evaultLogs, ...data.logs]; + } else { + evaultLogs = data.logs; + } + logsNextCursor = data.nextCursor ?? null; + logsHasMore = data.hasMore ?? false; + } catch (e) { + logsError = e instanceof Error ? e.message : String(e); + } finally { + logsLoading = false; + } +} + +function loadLogsFirstPage() { + evaultLogs = []; + logsNextCursor = null; + logsHasMore = false; + loadEvaultLogs(); +} + +function loadLogsNextPage() { + if (logsNextCursor) loadEvaultLogs(logsNextCursor); +} + // Expanded envelope IDs set let expandedIds = $state(new Set()); function toggleExpand(id: string) { @@ -786,6 +872,13 @@ async function doSign() { > eVault Inspector + + + + + {#if logsError} +

{logsError}

+ {/if} + + {#if logsLoading && evaultLogs.length === 0} +
Loading logs...
+ {:else if evaultLogs.length === 0 && !logsError && currentTab === 'logs'} +
No logs loaded. Enter an eName and click Load Logs.
+ {:else if evaultLogs.length > 0} +
+ + + + + + + + + + + + + {#each evaultLogs as log (log.id)} + + + + + + + + + {/each} + +
TimestampOperationOntologyMetaEnvelope IDPlatformHash
{log.timestamp.slice(0, 19).replace('T', ' ')}{log.operation}{ontologyTitleMap.get(log.ontology) ?? "Unknown"}{log.metaEnvelopeId}{log.platform}{log.envelopeHash}
+
+ + {#if logsHasMore} + + {/if} + {/if} + +

Sandbox Config

@@ -1707,4 +1866,77 @@ async function doSign() { color: var(--muted, #64748b); margin-right: 0.35rem; } + + /* eVault Logs — table */ + .logs-table-wrap { + overflow-x: auto; + border: 1px solid var(--border, #e2e8f0); + border-radius: 10px; + background: var(--bg-card, #fff); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06); + } + .logs-table { + width: 100%; + border-collapse: collapse; + font-size: 0.82rem; + } + .logs-table thead th { + text-align: left; + padding: 0.6rem 0.75rem; + font-weight: 600; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.03em; + color: var(--muted, #64748b); + background: var(--bg-page, #f0f2f5); + border-bottom: 2px solid var(--border, #e2e8f0); + white-space: nowrap; + } + .logs-table tbody tr { + border-bottom: 1px solid var(--border, #e2e8f0); + } + .logs-table tbody tr:last-child { + border-bottom: none; + } + .logs-table tbody tr:hover { + background: var(--bg-page, #f8fafc); + } + .logs-table td { + padding: 0.55rem 0.75rem; + vertical-align: top; + } + .logs-td-ts { + white-space: nowrap; + color: var(--muted, #64748b); + font-size: 0.78rem; + } + .logs-td-mono { + font-family: ui-monospace, "Cascadia Code", "SF Mono", monospace; + font-size: 0.75rem; + word-break: break-all; + } + .logs-td-hash { + color: var(--muted, #64748b); + max-width: 18ch; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .logs-td-ontology { + font-weight: 500; + } + .logs-op-badge { + display: inline-block; + font-size: 0.72rem; + font-weight: 600; + text-transform: uppercase; + padding: 0.15em 0.45em; + border-radius: 4px; + background: #475569; + color: #fff; + white-space: nowrap; + } + .logs-op-badge.op-create { background: #15803d; } + .logs-op-badge.op-update { background: #b45309; } + .logs-op-badge.op-delete { background: #b91c1c; } diff --git a/platforms/blabsy/client/src/components/chat/chat-list.tsx b/platforms/blabsy/client/src/components/chat/chat-list.tsx index ab2ceb6b0..11839d777 100644 --- a/platforms/blabsy/client/src/components/chat/chat-list.tsx +++ b/platforms/blabsy/client/src/components/chat/chat-list.tsx @@ -66,7 +66,10 @@ export function ChatList(): JSX.Element { return ; } - if (!chats?.length) { + const visibleChats = + chats?.filter((chat) => chat.participants.length >= 2) ?? []; + + if (!visibleChats.length) { console.log('ChatList: No chats found'); return (
@@ -94,7 +97,7 @@ export function ChatList(): JSX.Element { return (
- {chats.map((chat) => { + {visibleChats.map((chat) => { const otherParticipant = chat.participants.find( (p) => p !== user?.id ); diff --git a/platforms/pictique/client/src/routes/(protected)/messages/+page.svelte b/platforms/pictique/client/src/routes/(protected)/messages/+page.svelte index 8d3efa9dd..19c60bb19 100644 --- a/platforms/pictique/client/src/routes/(protected)/messages/+page.svelte +++ b/platforms/pictique/client/src/routes/(protected)/messages/+page.svelte @@ -49,8 +49,10 @@ totalChats = data.total; hasMorePages = data.page < data.totalPages; - // Transform chats to messages - const newMessages = data.chats.map((c) => { + // Transform chats to messages, hiding chats that only contain a single user + const newMessages = data.chats + .filter((c) => c.participants.length >= 2) + .map((c) => { const members = c.participants.filter((u) => u.id !== userData.id); const memberNames = members.map((m) => m.name ?? m.handle ?? m.ename); const isGroup = members.length > 1;