Skip to content

Commit b94dcbb

Browse files
committed
feat(dataset-detail): add “Copy URL” next to Preview for previewable data; refs #98
1 parent 8d4c775 commit b94dcbb

File tree

2 files changed

+139
-36
lines changed

2 files changed

+139
-36
lines changed

src/components/DatasetDetailPage/MetaDataPanel.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ const MetaDataPanel: React.FC<Props> = ({
106106
Modalities
107107
</Typography>
108108

109-
{/* {(() => {
109+
{(() => {
110110
const mods = Array.isArray(dbViewInfo?.rows?.[0]?.value?.modality)
111111
? [...new Set(dbViewInfo.rows[0].value.modality as string[])]
112112
: [];
@@ -149,10 +149,10 @@ const MetaDataPanel: React.FC<Props> = ({
149149
))}
150150
</Box>
151151
);
152-
})()} */}
153-
<Typography sx={{ color: "text.secondary" }}>
152+
})()}
153+
{/* <Typography sx={{ color: "text.secondary" }}>
154154
{dbViewInfo?.rows?.[0]?.value?.modality?.join(", ") ?? "N/A"}
155-
</Typography>
155+
</Typography> */}
156156
</Box>
157157
<Box>
158158
<Typography sx={{ color: Colors.darkPurple, fontWeight: "600" }}>

src/pages/UpdatedDatasetDetailPage.tsx

Lines changed: 135 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import PreviewModal from "../components/PreviewModal";
22
import CloudDownloadIcon from "@mui/icons-material/CloudDownload";
3+
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
34
import DescriptionIcon from "@mui/icons-material/Description";
45
import ExpandLess from "@mui/icons-material/ExpandLess";
56
import ExpandMore from "@mui/icons-material/ExpandMore";
@@ -12,6 +13,7 @@ import {
1213
Alert,
1314
Button,
1415
Collapse,
16+
Snackbar,
1517
} from "@mui/material";
1618
import FileTree from "components/DatasetDetailPage/FileTree/FileTree";
1719
import {
@@ -93,20 +95,16 @@ const UpdatedDatasetDetailPage: React.FC = () => {
9395
const [isExternalExpanded, setIsExternalExpanded] = useState(true);
9496
const [jsonSize, setJsonSize] = useState<number>(0);
9597
const [previewIndex, setPreviewIndex] = useState<number>(0);
98+
const [copiedToast, setCopiedToast] = useState<{
99+
open: boolean;
100+
text: string;
101+
}>({
102+
open: false,
103+
text: "",
104+
});
96105
const aiSummary = datasetDocument?.[".datainfo"]?.AISummary ?? "";
97106

98-
// useEffect(() => {
99-
// if (!datasetDocument) {
100-
// setJsonSize(0);
101-
// return;
102-
// }
103-
// const bytes = new TextEncoder().encode(
104-
// JSON.stringify(datasetDocument)
105-
// ).length;
106-
// setJsonSize(bytes);
107-
// }, [datasetDocument]);
108-
109-
const linkMap = useMemo(() => makeLinkMap(externalLinks), [externalLinks]);
107+
const linkMap = useMemo(() => makeLinkMap(externalLinks), [externalLinks]); // => external Link Map
110108

111109
const treeData = useMemo(
112110
() => buildTreeFromDoc(datasetDocument || {}, linkMap, ""),
@@ -262,6 +260,32 @@ const UpdatedDatasetDetailPage: React.FC = () => {
262260

263261
return internalLinks;
264262
};
263+
// Build a shareable preview URL for a JSON path in this dataset
264+
const buildPreviewUrl = (path: string) => {
265+
const origin = window.location.origin;
266+
const revPart = rev ? `rev=${encodeURIComponent(rev)}&` : "";
267+
return `${origin}/db/${dbName}/${docId}?${revPart}preview=${encodeURIComponent(
268+
path
269+
)}`;
270+
};
271+
272+
// Copy helper
273+
const copyPreviewUrl = async (path: string) => {
274+
const url = buildPreviewUrl(path);
275+
try {
276+
await navigator.clipboard.writeText(url);
277+
setCopiedToast({ open: true, text: "Preview link copied" });
278+
} catch {
279+
// fallback
280+
const ta = document.createElement("textarea");
281+
ta.value = url;
282+
document.body.appendChild(ta);
283+
ta.select();
284+
document.execCommand("copy");
285+
document.body.removeChild(ta);
286+
setCopiedToast({ open: true, text: "Preview link copied" });
287+
}
288+
};
265289

266290
// useEffect(() => {
267291
// const fetchData = async () => {
@@ -386,6 +410,13 @@ const UpdatedDatasetDetailPage: React.FC = () => {
386410
}
387411
}, [datasetDocument, docId]);
388412

413+
// const externalMap = React.useMemo(() => {
414+
// const m = new Map<string, { url: string; index: number }>();
415+
// for (const it of externalLinks)
416+
// m.set(it.path, { url: it.url, index: it.index });
417+
// return m;
418+
// }, [externalLinks]);
419+
389420
const [previewOpen, setPreviewOpen] = useState(false);
390421
const [previewDataKey, setPreviewDataKey] = useState<any>(null);
391422

@@ -563,6 +594,34 @@ const UpdatedDatasetDetailPage: React.FC = () => {
563594
[datasetDocument]
564595
);
565596

597+
useEffect(() => {
598+
const p = searchParams.get("preview");
599+
if (!p || !datasetDocument) return;
600+
601+
const previewPath = decodeURIComponent(p);
602+
603+
// Try internal data first
604+
const internal = internalMap.get(previewPath);
605+
if (internal) {
606+
handlePreview(internal.data, internal.index, true);
607+
return;
608+
}
609+
610+
// Then try external data by JSON path
611+
const external = linkMap.get(previewPath);
612+
if (external) {
613+
handlePreview(external.url, external.index, false);
614+
}
615+
}, [
616+
datasetDocument,
617+
internalLinks,
618+
externalLinks,
619+
searchParams,
620+
internalMap,
621+
// externalMap,
622+
linkMap,
623+
]);
624+
566625
const handleClosePreview = () => {
567626
setPreviewOpen(false);
568627
setPreviewDataKey(null);
@@ -976,26 +1035,48 @@ const UpdatedDatasetDetailPage: React.FC = () => {
9761035
{link.name}{" "}
9771036
{link.arraySize ? `[${link.arraySize.join("x")}]` : ""}
9781037
</Typography>
979-
<Button
980-
variant="contained"
981-
size="small"
982-
sx={{
983-
backgroundColor: Colors.purple,
984-
flexShrink: 0,
985-
minWidth: "70px",
986-
fontSize: "0.7rem",
987-
padding: "2px 6px",
988-
lineHeight: 1,
989-
"&:hover": {
990-
backgroundColor: Colors.secondaryPurple,
991-
},
992-
}}
993-
onClick={() =>
994-
handlePreview(link.data, link.index, true)
995-
}
996-
>
997-
Preview
998-
</Button>
1038+
<Box sx={{ display: "flex", flexShrink: 0, gap: 1 }}>
1039+
<Button
1040+
variant="contained"
1041+
size="small"
1042+
sx={{
1043+
backgroundColor: Colors.purple,
1044+
flexShrink: 0,
1045+
minWidth: "70px",
1046+
fontSize: "0.7rem",
1047+
padding: "2px 6px",
1048+
lineHeight: 1,
1049+
"&:hover": {
1050+
backgroundColor: Colors.secondaryPurple,
1051+
},
1052+
}}
1053+
onClick={() =>
1054+
handlePreview(link.data, link.index, true)
1055+
}
1056+
>
1057+
Preview
1058+
</Button>
1059+
{/* <Button
1060+
variant="outlined"
1061+
size="small"
1062+
sx={{
1063+
color: Colors.purple,
1064+
borderColor: Colors.purple,
1065+
minWidth: "90px",
1066+
fontSize: "0.7rem",
1067+
padding: "2px 6px",
1068+
lineHeight: 1,
1069+
"&:hover": {
1070+
color: Colors.secondaryPurple,
1071+
borderColor: Colors.secondaryPurple,
1072+
},
1073+
}}
1074+
onClick={() => copyPreviewUrl(link.path)}
1075+
>
1076+
<ContentCopyIcon sx={{ fontSize: 16, mr: 0.5 }} />
1077+
Copy URL
1078+
</Button> */}
1079+
</Box>
9991080
</Box>
10001081
))
10011082
) : (
@@ -1129,6 +1210,28 @@ const UpdatedDatasetDetailPage: React.FC = () => {
11291210
Preview
11301211
</Button>
11311212
)}
1213+
{/* {isPreviewable && (
1214+
<Button
1215+
variant="outlined"
1216+
size="small"
1217+
sx={{
1218+
color: Colors.purple,
1219+
borderColor: Colors.purple,
1220+
minWidth: "90px",
1221+
fontSize: "0.7rem",
1222+
padding: "2px 6px",
1223+
lineHeight: 1,
1224+
"&:hover": {
1225+
color: Colors.secondaryPurple,
1226+
borderColor: Colors.secondaryPurple,
1227+
},
1228+
}}
1229+
onClick={() => copyPreviewUrl(link.path)} // <-- use the JSON path
1230+
>
1231+
<ContentCopyIcon sx={{ fontSize: 16, mr: 0.5 }} />
1232+
Copy URL
1233+
</Button>
1234+
)} */}
11321235
</Box>
11331236
</Box>
11341237
);

0 commit comments

Comments
 (0)