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 (
+
-
-
-
+
+
+
+
{sourceLabel}
-
+
{metaLine}
+
{source.name ?? "Unnamed source"}
-
- Source row ID: {source.id ?? "Unavailable"}
-
-
+ {isOpen ? "Hide" : "View"}
+
-
- {isLoading && (
-
Loading source details…
- )}
+ {isOpen && (
+
+ {isLoading && (
+
Loading source details…
+ )}
+
+ {isError && (
+
+ Failed to load source details
+ {error?.message ? `: ${error.message}` : "."}
+
+ )}
- {isError && (
-
- Failed to load source details
- {error?.message ? `: ${error.message}` : "."}
-
- )}
+ {sourceDetail && !sourceRecord && (
+
+ No import record available.
+
+ )}
- {sourceDetail && (
- <>
-
-
-
-
-
- Source Record
-
-
- {JSON.stringify(sourceDetail.source_record, null, 2)}
-
-
- >
- )}
-
+ {sourceRecord && (
+ <>
+
+
+
+
+
+
+
+ >
+ )}
+
+ )}
);
}
-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();
});