Skip to content

Commit 6bdb90a

Browse files
committed
feat: load collections
1 parent ba3ee36 commit 6bdb90a

File tree

5 files changed

+166
-12
lines changed

5 files changed

+166
-12
lines changed

src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ function App() {
3838
<Box zIndex={1}>
3939
<Overlay>
4040
<Header href={href} setHref={setHref}></Header>
41-
<Panel href={href}></Panel>
41+
<Panel href={href} setHref={setHref}></Panel>
4242
</Overlay>
4343
</Box>
4444
<Toaster></Toaster>

src/components/panel.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import { Box, FileUpload, Icon, SimpleGrid, Tabs } from "@chakra-ui/react";
2-
import { useEffect, useState } from "react";
2+
import { useEffect, useState, type Dispatch, type SetStateAction } from "react";
33
import { LuInfo, LuUpload } from "react-icons/lu";
44
import Value from "./stac/value";
55

6-
export function Panel({ href }: { href: string }) {
6+
export function Panel({
7+
href,
8+
setHref,
9+
}: {
10+
href: string;
11+
setHref: Dispatch<SetStateAction<string>>;
12+
}) {
713
const [tabValue, setTabValue] = useState("upload");
814

915
useEffect(() => {
@@ -20,6 +26,8 @@ export function Panel({ href }: { href: string }) {
2026
bg={"bg.muted"}
2127
rounded={4}
2228
pointerEvents={"auto"}
29+
overflow={"scroll"}
30+
maxH={{ base: "40vh", md: "90vh" }}
2331
>
2432
<Tabs.List>
2533
<Tabs.Trigger value="value">
@@ -31,7 +39,7 @@ export function Panel({ href }: { href: string }) {
3139
</Tabs.List>
3240
<Box px={4} pb={4}>
3341
<Tabs.Content value="value">
34-
<Value href={href}></Value>
42+
<Value href={href} setHref={setHref}></Value>
3543
</Tabs.Content>
3644
<Tabs.Content value="upload">
3745
<FileUpload.Root alignItems={"stretch"}>

src/components/stac/catalog.tsx

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,48 @@
1-
import { Stack } from "@chakra-ui/react";
1+
import { Heading, Skeleton, Stack, Text } from "@chakra-ui/react";
2+
import { useEffect, useState, type Dispatch, type SetStateAction } from "react";
23
import { LuFolder } from "react-icons/lu";
3-
import type { StacCatalog } from "stac-ts";
4+
import type { StacCatalog, StacCollection, StacLink } from "stac-ts";
5+
import { CollectionCard } from "./collection";
46
import { ValueInfo } from "./shared";
57

6-
export function Catalog({ catalog }: { catalog: StacCatalog }) {
8+
function Collections({
9+
href,
10+
setHref,
11+
}: {
12+
href: string;
13+
setHref: Dispatch<SetStateAction<string>>;
14+
}) {
15+
const { collections, loading, error } = useCollections(href);
16+
17+
if (error) {
18+
<Text colorPalette={"red"}>Error when loading collections: {error}</Text>;
19+
} else if (loading) {
20+
return <Skeleton h={200}></Skeleton>;
21+
} else {
22+
return (
23+
<Stack>
24+
{collections.map((collection) => (
25+
<CollectionCard
26+
collection={collection}
27+
setHref={setHref}
28+
key={collection.id}
29+
></CollectionCard>
30+
))}
31+
</Stack>
32+
);
33+
}
34+
}
35+
36+
export function Catalog({
37+
catalog,
38+
setHref,
39+
}: {
40+
catalog: StacCatalog;
41+
setHref: Dispatch<SetStateAction<string>>;
42+
}) {
43+
const collectionsHref = catalog.links.find(
44+
(link) => link.rel == "data"
45+
)?.href;
746
return (
847
<Stack>
948
<ValueInfo
@@ -14,6 +53,56 @@ export function Catalog({ catalog }: { catalog: StacCatalog }) {
1453
title={catalog.title}
1554
description={catalog.description}
1655
></ValueInfo>
56+
57+
{collectionsHref && (
58+
<>
59+
<Heading size={"md"} mt={8}>
60+
Collections
61+
</Heading>
62+
<Collections href={collectionsHref} setHref={setHref}></Collections>
63+
</>
64+
)}
1765
</Stack>
1866
);
1967
}
68+
69+
function useCollections(href: string) {
70+
const [loading, setLoading] = useState(false);
71+
const [error, setError] = useState<string | undefined>();
72+
const [collections, setCollections] = useState<StacCollection[]>([]);
73+
74+
useEffect(() => {
75+
setLoading(true);
76+
(async () => {
77+
let nextHref = href;
78+
let fetchedCollections: StacCollection[] = [];
79+
while (true) {
80+
const response = await fetch(nextHref);
81+
if (response.ok) {
82+
const data = await response.json();
83+
fetchedCollections = [
84+
...fetchedCollections,
85+
...(data.collections ?? []),
86+
];
87+
const nextLink = (data.links ?? []).find(
88+
(link: StacLink) => link.rel == "next"
89+
);
90+
if (nextLink && nextLink.href != nextHref) {
91+
nextHref = nextLink.href;
92+
} else {
93+
break;
94+
}
95+
} else {
96+
setError(
97+
"Error while fetching " + href + ": " + (await response.text())
98+
);
99+
break;
100+
}
101+
}
102+
setCollections(fetchedCollections);
103+
setLoading(false);
104+
})();
105+
}, [href]);
106+
107+
return { collections, loading, error };
108+
}

src/components/stac/collection.tsx

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
import { Stack } from "@chakra-ui/react";
2-
import { LuFolderPlus } from "react-icons/lu";
1+
import { Card, HStack, IconButton, Stack, Text } from "@chakra-ui/react";
2+
import type { Dispatch, SetStateAction } from "react";
3+
import { LuFolderPlus, LuUpload } from "react-icons/lu";
34
import type { StacCollection } from "stac-ts";
5+
import { RawJsonDialogButton } from "../json";
6+
import { Tooltip } from "../ui/tooltip";
47
import { ValueInfo } from "./shared";
8+
import { getSelfHref } from "./utils";
59

610
export function Collection({ collection }: { collection: StacCollection }) {
711
return (
@@ -17,3 +21,50 @@ export function Collection({ collection }: { collection: StacCollection }) {
1721
</Stack>
1822
);
1923
}
24+
25+
export function CollectionCard({
26+
collection,
27+
setHref,
28+
}: {
29+
collection: StacCollection;
30+
setHref: Dispatch<SetStateAction<string>>;
31+
}) {
32+
const selfHref = getSelfHref(collection);
33+
return (
34+
<Card.Root size={"sm"}>
35+
<Card.Header>
36+
<Text fontSize={"xs"}>{collection.title || collection.id}</Text>
37+
</Card.Header>
38+
<Card.Body>
39+
<Text lineClamp={3}>{collection.description}</Text>
40+
</Card.Body>
41+
<Card.Footer>
42+
<Card.Footer>
43+
<Stack>
44+
<HStack>
45+
{selfHref && (
46+
<Tooltip content="Load collection">
47+
<IconButton
48+
variant={"surface"}
49+
size={"xs"}
50+
onClick={() => setHref(selfHref)}
51+
>
52+
<LuUpload></LuUpload>
53+
</IconButton>
54+
</Tooltip>
55+
)}
56+
<Tooltip content="View raw JSON">
57+
<RawJsonDialogButton
58+
title={collection.id}
59+
value={collection}
60+
variant={"surface"}
61+
size={"xs"}
62+
></RawJsonDialogButton>
63+
</Tooltip>
64+
</HStack>
65+
</Stack>
66+
</Card.Footer>
67+
</Card.Footer>
68+
</Card.Root>
69+
);
70+
}

src/components/stac/value.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import { SkeletonText, Text } from "@chakra-ui/react";
2-
import { useEffect, useState } from "react";
2+
import { useEffect, useState, type Dispatch, type SetStateAction } from "react";
33
import { Catalog } from "./catalog";
44
import { Collection } from "./collection";
55
import { Item } from "./item";
66
import { ItemCollection } from "./item-collection";
77

8-
export default function Value({ href }: { href: string }) {
8+
export default function Value({
9+
href,
10+
setHref,
11+
}: {
12+
href: string;
13+
setHref: Dispatch<SetStateAction<string>>;
14+
}) {
915
const { value, loading, error } = useStacValue(href);
1016
if (loading) {
1117
return <SkeletonText></SkeletonText>;
@@ -14,7 +20,7 @@ export default function Value({ href }: { href: string }) {
1420
} else if (value) {
1521
switch (value.type) {
1622
case "Catalog":
17-
return <Catalog catalog={value}></Catalog>;
23+
return <Catalog catalog={value} setHref={setHref}></Catalog>;
1824
case "Collection":
1925
return <Collection collection={value}></Collection>;
2026
case "Feature":

0 commit comments

Comments
 (0)