Skip to content

Commit f878a99

Browse files
V48 (impl-only): Align Packs master-detail with Deposit/Read
Compact header metrics only, drill-in table → AssetPack detail with Back, rich main/aside detail grid, and a short portfolio strip on the list view.
1 parent 9469a09 commit f878a99

8 files changed

Lines changed: 669 additions & 551 deletions

File tree

uapi/components/packs/PacksActivityDetail/PacksActivityDetail.tsx

Lines changed: 135 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
'use client';
1+
"use client";
22

33
/**
4-
* Packs detail aside shell: overview, measurements, then section units
5-
* (states, accounting, governance, proof roots) for the selected row.
4+
* Packs detail shell for drill-in master-detail.
5+
* - layout="main": overview, measurements, states, accounting (primary column)
6+
* - layout="aside": governance + proof roots (route-state twin)
7+
* - layout="full": legacy single-column stack (tests / compact embeds)
68
*/
79

810
import React from "react";
@@ -21,12 +23,98 @@ import { PacksActivityDetailProofRoots } from "@/components/packs/PacksActivityD
2123

2224
export type PacksActivityDetailProps = {
2325
detail: PackActivityDetailProjection | null;
26+
/** Column role in the deposit/read-style detail grid. */
27+
layout?: "main" | "aside" | "full";
2428
};
2529

26-
export function PacksActivityDetail({ detail }: PacksActivityDetailProps) {
30+
function DetailHeader({ detail }: { detail: PackActivityDetailProjection }) {
31+
return (
32+
<div>
33+
<p className="flex items-center gap-2 text-[0.68rem] uppercase tracking-[0.22em] text-emerald-200/80">
34+
<ShieldCheck className="h-4 w-4" aria-hidden="true" />
35+
Source-safe detail
36+
</p>
37+
<h2 className="mt-3 text-2xl font-semibold tracking-tight text-white">
38+
{detail.title}
39+
</h2>
40+
<p className="mt-2 text-sm leading-6 text-neutral-300">
41+
{detail.description}
42+
</p>
43+
</div>
44+
);
45+
}
46+
47+
function OverviewAndMeasurements({
48+
detail,
49+
}: {
50+
detail: PackActivityDetailProjection;
51+
}) {
52+
return (
53+
<>
54+
<PacksDetailSection title="Overview">
55+
<dl className="grid gap-3 text-sm tablet:grid-cols-2">
56+
<div>
57+
<dt className="text-neutral-500">Type</dt>
58+
<dd className="mt-1 text-neutral-100">{formatType(detail.type)}</dd>
59+
</div>
60+
<div>
61+
<dt className="text-neutral-500">State</dt>
62+
<dd className="mt-1 text-neutral-100">
63+
{detail.overview.state || "not recorded"}
64+
</dd>
65+
</div>
66+
<div>
67+
<dt className="text-neutral-500">Repository</dt>
68+
<dd className="mt-1 text-neutral-100">
69+
{detail.overview.repository || "not recorded"}
70+
</dd>
71+
</div>
72+
<div>
73+
<dt className="text-neutral-500">Time</dt>
74+
<dd className="mt-1 text-neutral-100">
75+
{formatTimestamp(detail.timestamp)}
76+
</dd>
77+
</div>
78+
</dl>
79+
</PacksDetailSection>
80+
81+
<PacksDetailSection title="Measurements">
82+
<div className="grid gap-2">
83+
{detail.measurements.length ? (
84+
detail.measurements.map((measurement) => (
85+
<div
86+
key={`${measurement.id}:${measurement.value}`}
87+
className="flex items-center justify-between gap-3 border border-white/10 bg-black/18 px-3 py-2 text-sm"
88+
>
89+
<span className="text-neutral-400">{measurement.label}</span>
90+
<span className="font-mono text-neutral-100">
91+
{measurement.value} {measurement.unit || ""}
92+
</span>
93+
</div>
94+
))
95+
) : (
96+
<p className="text-sm text-neutral-500">
97+
No source-safe measurements recorded.
98+
</p>
99+
)}
100+
</div>
101+
</PacksDetailSection>
102+
</>
103+
);
104+
}
105+
106+
export function PacksActivityDetail({
107+
detail,
108+
layout = "full",
109+
}: PacksActivityDetailProps) {
27110
if (!detail) {
28111
return (
29-
<aside className="min-w-0 border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.055),rgba(255,255,255,0.025))] p-5">
112+
<aside
113+
className="min-w-0 border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.055),rgba(255,255,255,0.025))] p-5"
114+
data-testid={
115+
layout === "aside" ? "packs-detail-aside" : "packs-detail-main"
116+
}
117+
>
30118
<div className="py-12">
31119
<ProductRouteStatePanel
32120
variant="empty"
@@ -38,83 +126,58 @@ export function PacksActivityDetail({ detail }: PacksActivityDetailProps) {
38126
);
39127
}
40128

41-
return (
42-
<aside className="min-w-0 border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.055),rgba(255,255,255,0.025))] p-5">
43-
<div className="grid gap-5">
44-
<div>
45-
<p className="flex items-center gap-2 text-[0.68rem] uppercase tracking-[0.22em] text-emerald-200/80">
46-
<ShieldCheck className="h-4 w-4" aria-hidden="true" />
47-
Source-safe detail
48-
</p>
49-
<h2 className="mt-3 text-2xl font-semibold tracking-tight text-white">
50-
{detail.title}
51-
</h2>
52-
<p className="mt-2 text-sm leading-6 text-neutral-300">
53-
{detail.description}
54-
</p>
129+
if (layout === "aside") {
130+
return (
131+
<aside
132+
className="grid h-fit items-start gap-5"
133+
aria-label="Pack route state"
134+
data-testid="packs-detail-aside"
135+
>
136+
<div className="min-w-0 border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.055),rgba(255,255,255,0.025))] p-5">
137+
<div className="grid gap-5">
138+
{detail.governance ? (
139+
<PacksActivityDetailGovernance governance={detail.governance} />
140+
) : null}
141+
<PacksActivityDetailProofRoots detail={detail} />
142+
</div>
55143
</div>
144+
</aside>
145+
);
146+
}
56147

57-
<PacksDetailSection title="Overview">
58-
<dl className="grid gap-3 text-sm tablet:grid-cols-2">
59-
<div>
60-
<dt className="text-neutral-500">Type</dt>
61-
<dd className="mt-1 text-neutral-100">
62-
{formatType(detail.type)}
63-
</dd>
64-
</div>
65-
<div>
66-
<dt className="text-neutral-500">State</dt>
67-
<dd className="mt-1 text-neutral-100">
68-
{detail.overview.state || "not recorded"}
69-
</dd>
70-
</div>
71-
<div>
72-
<dt className="text-neutral-500">Repository</dt>
73-
<dd className="mt-1 text-neutral-100">
74-
{detail.overview.repository || "not recorded"}
75-
</dd>
76-
</div>
77-
<div>
78-
<dt className="text-neutral-500">Time</dt>
79-
<dd className="mt-1 text-neutral-100">
80-
{formatTimestamp(detail.timestamp)}
81-
</dd>
82-
</div>
83-
</dl>
84-
</PacksDetailSection>
85-
86-
<PacksDetailSection title="Measurements">
87-
<div className="grid gap-2">
88-
{detail.measurements.length ? (
89-
detail.measurements.map((measurement) => (
90-
<div
91-
key={`${measurement.id}:${measurement.value}`}
92-
className="flex items-center justify-between gap-3 border border-white/10 bg-black/18 px-3 py-2 text-sm"
93-
>
94-
<span className="text-neutral-400">{measurement.label}</span>
95-
<span className="font-mono text-neutral-100">
96-
{measurement.value} {measurement.unit || ""}
97-
</span>
98-
</div>
99-
))
100-
) : (
101-
<p className="text-sm text-neutral-500">
102-
No source-safe measurements recorded.
103-
</p>
104-
)}
105-
</div>
106-
</PacksDetailSection>
148+
if (layout === "main") {
149+
return (
150+
<div
151+
className="min-w-0 border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.055),rgba(255,255,255,0.025))] p-5"
152+
data-testid="packs-detail-main"
153+
>
154+
<div className="grid gap-5">
155+
<DetailHeader detail={detail} />
156+
<OverviewAndMeasurements detail={detail} />
157+
<PacksActivityDetailStates detail={detail} />
158+
{detail.accounting ? (
159+
<PacksActivityDetailAccounting accounting={detail.accounting} />
160+
) : null}
161+
</div>
162+
</div>
163+
);
164+
}
107165

166+
return (
167+
<aside
168+
className="min-w-0 border border-white/10 bg-[linear-gradient(180deg,rgba(255,255,255,0.055),rgba(255,255,255,0.025))] p-5"
169+
data-testid="packs-detail-full"
170+
>
171+
<div className="grid gap-5">
172+
<DetailHeader detail={detail} />
173+
<OverviewAndMeasurements detail={detail} />
108174
<PacksActivityDetailStates detail={detail} />
109-
110175
{detail.accounting ? (
111176
<PacksActivityDetailAccounting accounting={detail.accounting} />
112177
) : null}
113-
114178
{detail.governance ? (
115179
<PacksActivityDetailGovernance governance={detail.governance} />
116180
) : null}
117-
118181
<PacksActivityDetailProofRoots detail={detail} />
119182
</div>
120183
</aside>

uapi/components/packs/PacksActivityFilterBar/PacksActivityFilterBar.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,28 +45,23 @@ export function PacksActivityFilterBar({
4545
}: PacksActivityFilterBarProps) {
4646
return (
4747
<>
48-
<div className="border-b border-white/10 px-4 py-3">
48+
<div className="border-b border-white/10 px-3 py-2">
4949
<ProductRouteKeyboardHint
5050
testId="packs-keyboard-navigation"
5151
tone="emerald"
5252
shortcuts={[
5353
{
5454
keys: "Tab",
55-
label: "Move through filters, rows, and detail controls.",
55+
label: "Move through filters and activity rows.",
5656
},
5757
{
5858
keys: "Enter",
59-
label:
60-
"Select focused position, signal, filter, or activity row.",
61-
},
62-
{
63-
keys: "Space",
64-
label: "Open or close expandable proof detail.",
59+
label: "Open the focused AssetPack into detail.",
6560
},
6661
]}
6762
/>
6863
</div>
69-
<div className="grid gap-3 border-b border-white/10 p-4 laptop:grid-cols-[minmax(220px,1fr)_170px_150px_150px_auto]">
64+
<div className="grid gap-2 border-b border-white/10 p-3 laptop:grid-cols-[minmax(220px,1fr)_170px_150px_150px_auto]">
7065
<label className="relative min-w-0">
7166
<span className="sr-only">Search pack activity</span>
7267
<Search

0 commit comments

Comments
 (0)