From 7eeb8fed74704f68425102244cae7640eca53613 Mon Sep 17 00:00:00 2001 From: jasonbrownrmi <122411427+jasonbrownrmi@users.noreply.github.com> Date: Fri, 22 May 2026 10:41:25 -0700 Subject: [PATCH 1/4] build(ui): update node packages (vite) --- deployments/stitch-frontend/package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deployments/stitch-frontend/package-lock.json b/deployments/stitch-frontend/package-lock.json index 12b8e8b3..242bd135 100644 --- a/deployments/stitch-frontend/package-lock.json +++ b/deployments/stitch-frontend/package-lock.json @@ -32,7 +32,7 @@ "globals": "^16.5.0", "jsdom": "^27.0.1", "prettier": "^3.7.4", - "vite": "^7.2.4", + "vite": "^7.3.3", "vitest": "^3.2.4" }, "engines": { @@ -5495,9 +5495,9 @@ } }, "node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", + "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", "dev": true, "license": "MIT", "engines": { From 51ad556b1f2f4254572ebdcd57b777113958e409 Mon Sep 17 00:00:00 2001 From: jasonbrownrmi <122411427+jasonbrownrmi@users.noreply.github.com> Date: Fri, 22 May 2026 10:48:18 -0700 Subject: [PATCH 2/4] feat(ui): consolidate source sections into compact Sources list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace the separate "Source Details" and "Source data" sections on the resource detail page with a single "Sources" section. - Each source is a compact row showing source label, "Imported by {producer} · {observed_date}", and a View action. - Clicking View reveals basic source fields, with record_id, run_id, and raw payload JSON tucked behind a nested "Technical import record" disclosure so raw JSON is never visible by default. - Eager-fetches per-source detail on mount (deduped by TanStack Query cache key) so producer and observed_at populate the row before any interaction. --- .../src/pages/ResourceDetailPage.jsx | 227 +++++++++++------- 1 file changed, 144 insertions(+), 83 deletions(-) diff --git a/deployments/stitch-frontend/src/pages/ResourceDetailPage.jsx b/deployments/stitch-frontend/src/pages/ResourceDetailPage.jsx index d2af39cd..847dcc42 100644 --- a/deployments/stitch-frontend/src/pages/ResourceDetailPage.jsx +++ b/deployments/stitch-frontend/src/pages/ResourceDetailPage.jsx @@ -13,7 +13,6 @@ import SourceMixBar from "../components/SourceMixBar"; import SectionHeader from "../components/SectionHeader"; import { FieldCard, FieldGrid } from "../components/FieldCard"; import { SOURCE_LABELS } from "../constants/sourceMeta"; -import StructuredDataView from "../components/StructuredDataView"; import Button from "../components/Button"; import { AI_SUGGESTION_FIELDS, @@ -24,6 +23,19 @@ import { const LLM_AUDIT_PRODUCER = "stitch-frontend"; +const OBSERVED_AT_FORMATTER = new Intl.DateTimeFormat(undefined, { + year: "numeric", + month: "short", + day: "numeric", +}); + +function formatObservedAt(value) { + if (!value) return "—"; + const date = new Date(value); + if (Number.isNaN(date.getTime())) return "—"; + return OBSERVED_AT_FORMATTER.format(date); +} + function createPersistIntentId() { if (globalThis.crypto?.randomUUID) { return globalThis.crypto.randomUUID(); @@ -359,7 +371,48 @@ function OrganizationsSection({ data }) { ); } -function SourceDetailCard({ source }) { +function TechnicalImportRecord({ sourceRecord }) { + const [isOpen, setIsOpen] = useState(false); + const panelId = useId(); + + return ( +
+ + {isOpen && ( +
+ + + + +
+

+ Raw payload +

+
+              {JSON.stringify(sourceRecord.payload, null, 2)}
+            
+
+
+ )} +
+ ); +} + +function SourceRow({ source }) { const [isOpen, setIsOpen] = useState(false); const panelId = useId(); const hasId = Number.isFinite(source.id); @@ -368,107 +421,117 @@ function SourceDetailCard({ source }) { isLoading, isError, error, - } = useSourceDetail("oil-gas-field-sources", source.id, isOpen && hasId); + } = useSourceDetail("oil-gas-field-sources", source.id, hasId); + const sourceLabel = SOURCE_LABELS[source.source] ?? source.source; + const sourceRecord = sourceDetail?.source_record ?? null; + + let metaLine; + if (sourceRecord) { + const producer = sourceRecord.producer ?? "—"; + const observed = formatObservedAt(sourceRecord.observed_at); + metaLine = ( + + Imported by {producer} · {observed} + + ); + } else if (isLoading) { + metaLine = ( + Loading source details… + ); + } else if (isError) { + metaLine = ( + Unable to load source details + ); + } else { + metaLine = Imported by — · —; + } return ( -
-
-
-

+

+
+
+

{sourceLabel}

-

+

{metaLine}

+

{source.name ?? "Unnamed source"}

-

- Source row ID: {source.id ?? "Unavailable"} -

- + {isOpen ? "Hide" : "View"} +
- + )}
); } -function SourceDetailsSection({ sources }) { - if (!Array.isArray(sources) || sources.length === 0) return null; +function SourcesSection({ sources }) { + const hasSources = Array.isArray(sources) && sources.length > 0; return (
- -
- {sources.map((source) => ( - - ))} -
-
- ); -} - -function SourceDataSection({ sourceData }) { - return ( -
- -
- - Source records - - -
+ + {hasSources ? ( +
+ {sources.map((source, idx) => ( + + ))} +
+ ) : ( +

+ No sources attached to this resource. +

+ )}
); } @@ -556,9 +619,7 @@ export default function ResourceDetailPage() { - - - +
)}
From 45e9aa72569353f01f4dbe51fdceac2b580f167a Mon Sep 17 00:00:00 2001 From: jasonbrownrmi <122411427+jasonbrownrmi@users.noreply.github.com> Date: Fri, 22 May 2026 10:49:42 -0700 Subject: [PATCH 3/4] test(ui): update source-section tests for the new compact Sources layout --- .../src/pages/ResourceDetailPage.test.jsx | 67 +++++++++++++++---- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/deployments/stitch-frontend/src/pages/ResourceDetailPage.test.jsx b/deployments/stitch-frontend/src/pages/ResourceDetailPage.test.jsx index 9a12528b..475f69b6 100644 --- a/deployments/stitch-frontend/src/pages/ResourceDetailPage.test.jsx +++ b/deployments/stitch-frontend/src/pages/ResourceDetailPage.test.jsx @@ -236,7 +236,7 @@ describe("ResourceDetailPage", () => { ).toBeInTheDocument(); }); - it("shows source detail controls for each attached source", () => { + it("renders a Sources section with a row per attached source", () => { vi.mocked(useResourceDetail).mockReturnValue({ ...defaultHookReturn, data: mockDetailView, @@ -245,15 +245,15 @@ describe("ResourceDetailPage", () => { renderWithQueryClient(); expect( - screen.getByRole("heading", { name: /source details/i }), + screen.getByRole("heading", { name: /^sources$/i, level: 2 }), ).toBeInTheDocument(); expect( - screen.getByRole("button", { name: /show details/i }), + screen.getByRole("button", { name: /^view$/i }), ).toBeInTheDocument(); expect(screen.getAllByText("Burgan Source").length).toBeGreaterThan(0); }); - it("exposes disclosure accessibility attributes on the source detail toggle", async () => { + it("exposes disclosure accessibility attributes on the source row toggle", async () => { vi.mocked(useResourceDetail).mockReturnValue({ ...defaultHookReturn, data: mockDetailView, @@ -262,18 +262,20 @@ describe("ResourceDetailPage", () => { renderWithQueryClient(); - const toggle = screen.getByRole("button", { name: /show details/i }); + const toggle = screen.getByRole("button", { name: /^view$/i }); expect(toggle).toHaveAttribute("aria-expanded", "false"); const panelId = toggle.getAttribute("aria-controls"); expect(panelId).toBeTruthy(); await user.click(toggle); - expect(toggle).toHaveAttribute("aria-expanded", "true"); + expect( + screen.getByRole("button", { name: /^hide$/i }), + ).toHaveAttribute("aria-expanded", "true"); expect(document.getElementById(panelId)).toBeTruthy(); }); - it("renders raw source record details when a source detail panel is opened", async () => { + it("shows formatted producer and observed-at in the compact row once the source detail loads", () => { vi.mocked(useResourceDetail).mockReturnValue({ ...defaultHookReturn, data: mockDetailView, @@ -284,22 +286,61 @@ describe("ResourceDetailPage", () => { id: 11, source: "gem", name: "Burgan Source", - country: "Kuwait", source_record: { producer: "stitch-seed@0.1.0", + observed_at: "2026-05-13T12:00:00Z", + record_id: "abc", + run_id: "run-1", payload: { name: "Burgan Source" }, }, }, }); - const user = userEvent.setup(); renderWithQueryClient(); - await user.click(screen.getByRole("button", { name: /show details/i })); - expect(screen.getByText("stitch-seed@0.1.0")).toBeInTheDocument(); expect( - screen.getAllByText(/"name": "Burgan Source"/).length, - ).toBeGreaterThan(0); + screen.getByText(/imported by stitch-seed@0\.1\.0/i), + ).toBeInTheDocument(); + expect(screen.getByText(/may 13, 2026/i)).toBeInTheDocument(); + expect(screen.queryByText(/"name": "Burgan Source"/)).toBeNull(); + }); + + it("reveals the raw payload only after the Technical import record disclosure is opened", async () => { + vi.mocked(useResourceDetail).mockReturnValue({ + ...defaultHookReturn, + data: mockDetailView, + }); + vi.mocked(useSourceDetail).mockReturnValue({ + ...defaultSourceDetailHookReturn, + data: { + id: 11, + source: "gem", + name: "Burgan Source", + source_record: { + producer: "stitch-seed@0.1.0", + observed_at: "2026-05-13T12:00:00Z", + record_id: "abc", + run_id: "run-1", + payload: { name: "Burgan Source" }, + }, + }, + }); + const user = userEvent.setup(); + + renderWithQueryClient(); + await user.click(screen.getByRole("button", { name: /^view$/i })); + + const techToggle = screen.getByRole("button", { + name: /technical import record/i, + }); + expect(techToggle).toBeInTheDocument(); + expect(screen.queryByText(/"name": "Burgan Source"/)).toBeNull(); + + await user.click(techToggle); + + expect(screen.getByText(/"name": "Burgan Source"/)).toBeInTheDocument(); + expect(screen.getByText("abc")).toBeInTheDocument(); + expect(screen.getByText("run-1")).toBeInTheDocument(); }); it("generates and renders an AI suggestion preview", async () => { From a849f8b3f54363263e1bf2d71f83a3497e25c32f Mon Sep 17 00:00:00 2001 From: jasonbrownrmi <122411427+jasonbrownrmi@users.noreply.github.com> Date: Fri, 22 May 2026 10:54:48 -0700 Subject: [PATCH 4/4] chore: format changed files --- .../src/pages/ResourceDetailPage.jsx | 14 +++----------- .../src/pages/ResourceDetailPage.test.jsx | 11 +++++------ 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/deployments/stitch-frontend/src/pages/ResourceDetailPage.jsx b/deployments/stitch-frontend/src/pages/ResourceDetailPage.jsx index 847dcc42..6354c680 100644 --- a/deployments/stitch-frontend/src/pages/ResourceDetailPage.jsx +++ b/deployments/stitch-frontend/src/pages/ResourceDetailPage.jsx @@ -390,10 +390,7 @@ function TechnicalImportRecord({ sourceRecord }) { {isOpen && ( -
+
@@ -436,9 +433,7 @@ function SourceRow({ source }) { ); } else if (isLoading) { - metaLine = ( - Loading source details… - ); + metaLine = Loading source details…; } else if (isError) { metaLine = ( Unable to load source details @@ -471,10 +466,7 @@ function SourceRow({ source }) {
{isOpen && ( -
+
{isLoading && (

Loading source details…

)} diff --git a/deployments/stitch-frontend/src/pages/ResourceDetailPage.test.jsx b/deployments/stitch-frontend/src/pages/ResourceDetailPage.test.jsx index 475f69b6..5a0a841f 100644 --- a/deployments/stitch-frontend/src/pages/ResourceDetailPage.test.jsx +++ b/deployments/stitch-frontend/src/pages/ResourceDetailPage.test.jsx @@ -247,9 +247,7 @@ describe("ResourceDetailPage", () => { expect( screen.getByRole("heading", { name: /^sources$/i, level: 2 }), ).toBeInTheDocument(); - expect( - screen.getByRole("button", { name: /^view$/i }), - ).toBeInTheDocument(); + expect(screen.getByRole("button", { name: /^view$/i })).toBeInTheDocument(); expect(screen.getAllByText("Burgan Source").length).toBeGreaterThan(0); }); @@ -269,9 +267,10 @@ describe("ResourceDetailPage", () => { await user.click(toggle); - expect( - screen.getByRole("button", { name: /^hide$/i }), - ).toHaveAttribute("aria-expanded", "true"); + expect(screen.getByRole("button", { name: /^hide$/i })).toHaveAttribute( + "aria-expanded", + "true", + ); expect(document.getElementById(panelId)).toBeTruthy(); });