Skip to content

Commit 0a04ef0

Browse files
committed
parent infobox, FAQs
1 parent c9f1dad commit 0a04ef0

File tree

7 files changed

+234
-91
lines changed

7 files changed

+234
-91
lines changed

src/components/OptimadeClient/DatabaseSelector.jsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export function DatabaseSelector({
77
providers,
88
onQueryUrlChange,
99
onChildChange,
10+
onProviderChange,
1011
}) {
1112
const [selectedProvider, setSelectedProvider] = useState("");
1213
const [childEntries, setChildEntries] = useState([]);
@@ -78,6 +79,19 @@ export function DatabaseSelector({
7879
}
7980
}, [customUrl, childSelected, selectedProvider]);
8081

82+
useEffect(() => {
83+
// Notify parent of selected provider
84+
if (selectedProvider === "__custom__" || !selectedProvider) {
85+
onProviderChange?.(null);
86+
} else {
87+
// Find the full provider object from the list
88+
const providerObj = providers.find(
89+
(p) => p.attributes?.base_url === selectedProvider
90+
);
91+
onProviderChange?.(providerObj || null);
92+
}
93+
}, [selectedProvider, providers, onProviderChange]);
94+
8195
return (
8296
<div className="flex flex-col items-start space-y-2 max-w-md">
8397
<select
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { containerStyle } from "../../styles/containerStyles";
2+
import { textNormal, textTiny, textHyperlink } from "../../styles/textStyles";
3+
4+
export default function OptimadeChildInfo({ child }) {
5+
if (!child) return;
6+
7+
console.log("childAttr", child);
8+
9+
const { name, description, homepage, base_url, ...otherAttrs } = child;
10+
11+
return (
12+
<div className={`${containerStyle} ${textTiny} h-35 overflow-auto`}>
13+
<h3 className={`${textNormal} pb-1.5`}>{name}</h3>
14+
{description && <p className="pb-1">{description}</p>}
15+
{homepage && (
16+
<p>
17+
Homepage:{" "}
18+
<a
19+
href={homepage}
20+
target="_blank"
21+
rel="noopener noreferrer"
22+
className={textHyperlink}
23+
>
24+
{homepage}
25+
</a>
26+
</p>
27+
)}
28+
29+
{base_url && (
30+
<p>
31+
Base url:{" "}
32+
<a
33+
href={homepage}
34+
target="_blank"
35+
rel="noopener noreferrer"
36+
className={textHyperlink}
37+
>
38+
{base_url}
39+
</a>
40+
</p>
41+
)}
42+
</div>
43+
);
44+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { useState } from "react";
2+
import { containerStyle } from "../../styles/containerStyles";
3+
4+
import { textNormal, textSmall, textHyperlink } from "../../styles/textStyles";
5+
6+
const sectionclassName = "space-y-2";
7+
8+
export default function OptimadeFAQs() {
9+
const [showMore, setShowMore] = useState(false);
10+
11+
return (
12+
<div
13+
className={`${containerStyle} ${textSmall} [&>p]:pl-4 [&>section>p]:pl-1`}
14+
>
15+
<section className={sectionclassName}>
16+
<h4 className={textNormal}>What is the OPTIMADE Client?</h4>
17+
<p>
18+
This is a friendly client to search through databases and other
19+
implementations exposing an OPTIMADE RESTful API. To get more
20+
information about the OPTIMADE API, please see{" "}
21+
<a
22+
href="https://www.optimade.org/"
23+
target="_blank"
24+
className={textHyperlink}
25+
rel="noopener noreferrer"
26+
>
27+
the official web page
28+
</a>
29+
. All providers are retrieved from{" "}
30+
<a
31+
href="https://providers.optimade.org/"
32+
target="_blank"
33+
className={textHyperlink}
34+
rel="noopener noreferrer"
35+
>
36+
the OPTIMADE consortium's list of providers
37+
</a>
38+
.
39+
</p>
40+
41+
<button
42+
className={`flex items-center px-2 mt-2 mb-1 ${textHyperlink} hover:cursor-pointer`}
43+
onClick={() => setShowMore(!showMore)}
44+
>
45+
<span className={textHyperlink}>
46+
{showMore ? "Hide additional Info" : "More info"}
47+
</span>
48+
<svg
49+
className={`w-4 h-4 transition-transform duration-200 ${
50+
showMore ? "rotate-90" : "rotate-0"
51+
}`}
52+
fill="none"
53+
stroke="currentColor"
54+
strokeWidth="2"
55+
viewBox="0 0 24 24"
56+
>
57+
<path
58+
strokeLinecap="round"
59+
strokeLinejoin="round"
60+
d="M9 5l7 7-7 7"
61+
/>
62+
</svg>
63+
</button>
64+
</section>
65+
66+
{showMore && (
67+
<section className={sectionclassName}>
68+
<h4 className={textNormal}>
69+
Why is a given provider not shown in the client?
70+
</h4>
71+
<p>
72+
This webapp has filtered some providers for various reasons; some do
73+
not support open queries and others have no link to a meta-database.
74+
To check the validity of a particular provider visit{" "}
75+
<a
76+
href="https://optimade.org/providers-dashboard/"
77+
target="_blank"
78+
className={textHyperlink}
79+
rel="noopener noreferrer"
80+
>
81+
the providers dashboard
82+
</a>
83+
. Querying of missing providers should still be possible through the
84+
'Custom endpoint' method.
85+
</p>
86+
87+
<h4 className={textNormal}>
88+
Why does the structure visualiser not work for some structures?
89+
</h4>
90+
<p>
91+
The visualiser is fairly primitive and the OPTIMADE specification is
92+
very broad. For complex structure data (assemblies, unknown
93+
positions, or partial occupancies), the JSON response is still
94+
fetched but the visualisation often fails.
95+
</p>
96+
</section>
97+
)}
98+
</div>
99+
);
100+
}

src/components/OptimadeClient/OptimadeMetadata.jsx

Lines changed: 0 additions & 86 deletions
This file was deleted.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { containerStyle } from "../../styles/containerStyles";
2+
import { textNormal, textTiny, textHyperlink } from "../../styles/textStyles";
3+
4+
export default function OptimadeParentInfo({ provider, providers }) {
5+
if (!provider || !providers) return null;
6+
7+
// Find full provider object from providers array by matching base_url
8+
const fullProvider = providers.find(
9+
(p) => p.attributes?.base_url === provider.attributes?.base_url
10+
);
11+
12+
if (!fullProvider) return null;
13+
14+
const { name, description, homepage, base_url, ...otherAttrs } =
15+
fullProvider.attributes ?? {};
16+
17+
return (
18+
<div className={`${containerStyle} h-35 overflow-auto ${textTiny}`}>
19+
<h3 className={`${textNormal} pb-1.5`}>{name}</h3>
20+
{description && <p className="pb-1">{description}</p>}
21+
{homepage && (
22+
<p>
23+
Homepage:{" "}
24+
<a
25+
href={homepage}
26+
target="_blank"
27+
rel="noopener noreferrer"
28+
className={textHyperlink}
29+
>
30+
{homepage}
31+
</a>
32+
</p>
33+
)}
34+
{base_url && (
35+
<p>
36+
Index metadb:{" "}
37+
<a
38+
href={`${base_url}/links`}
39+
target="_blank"
40+
rel="noopener noreferrer"
41+
className={textHyperlink}
42+
>
43+
{`${base_url}/links`}
44+
</a>
45+
</p>
46+
)}
47+
</div>
48+
);
49+
}

src/components/OptimadeClient/index.jsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { getProvidersList, getStructures } from "../../api";
33
import OptimadeHeader from "./OptimadeHeader";
44
import { DatabaseSelector } from "./DatabaseSelector";
55
import OptimadeFilters from "./OptimadeFilters";
6+
import OptimadeFAQs from "./OptimadeFAQs";
67
import { ResultViewer } from "./ResultViewer";
78
import ResultsDropdown from "./ResultsDropdown";
89
import OptimadeProviderInfo from "./OptimadeProviderInfo";
@@ -12,7 +13,8 @@ import { AnimatePresence, motion } from "framer-motion";
1213
import OptimadeNoResults from "./OptimadeNoResults";
1314

1415
import MaterialsCloudHeader from "mc-react-header";
15-
import OptimadeMetadata from "./OptimadeMetadata";
16+
import OptimadeChildInfo from "./OptimadeChildInfo";
17+
import OptimadeParentInfo from "./OptimadeParentInfo";
1618

1719
import { textHyperlink, textNormal, textSmall } from "../../styles/textStyles";
1820
import { containerStyle } from "../../styles/containerStyles";
@@ -21,6 +23,8 @@ export function OptimadeClient({ hideProviderList = ["exmpl", "matcloud"] }) {
2123
const [providers, setProviders] = useState([]);
2224
const [queryUrl, setQueryUrl] = useState("");
2325
const [selectedChild, setSelectedChild] = useState(null);
26+
const [selectedProvider, setSelectedProvider] = useState(null);
27+
2428
const [currentFilter, setCurrentFilter] = useState("");
2529
const [currentPage, setCurrentPage] = useState(1);
2630
const [currentResult, setCurrentResult] = useState(null);
@@ -96,11 +100,16 @@ export function OptimadeClient({ hideProviderList = ["exmpl", "matcloud"] }) {
96100
<div className="min-h-screen max-w-5xl mx-auto bg-white mb-4 shadow-md rounded-xs">
97101
<div className="flex flex-col items-center w-full px-2 py-2">
98102
<OptimadeHeader />
103+
104+
<div className="p-2 w-full">
105+
<OptimadeFAQs />
106+
</div>
99107
{/* Database selector */}
100108
<div className="pt-4 p-2">
101109
<DatabaseSelector
102110
providers={providers}
103111
onChildChange={setSelectedChild}
112+
onProviderChange={setSelectedProvider}
104113
onQueryUrlChange={(url) => {
105114
// Only reset filter if provider actually changes
106115
if (url !== queryUrl) {
@@ -126,8 +135,17 @@ export function OptimadeClient({ hideProviderList = ["exmpl", "matcloud"] }) {
126135
</div>
127136
)}
128137

129-
<div className="p-2 max-w-4xl">
130-
<OptimadeMetadata child={selectedChild} />
138+
<div className="flex flex-col md:flex-row w-full max-w-5xl px-4 py-2 gap-4">
139+
<div className="md:w-1/2 w-full">
140+
<OptimadeParentInfo
141+
provider={selectedProvider}
142+
providers={providers}
143+
/>
144+
</div>
145+
146+
<div className="md:w-1/2 w-full">
147+
<OptimadeChildInfo child={selectedChild} />
148+
</div>
131149
</div>
132150

133151
{/* Filters */}

src/styles/textStyles.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
export const textNormal = " text-sm md:text-base";
22

3+
export const textLarge = " text-base md:text-lg";
4+
35
export const textHyperlink =
46
" text-blue-500 hover:underline hover:text-blue-600";
57

6-
export const textSmall = "text-xs md:text-sm";
8+
export const textSmall = " text-xs md:text-sm";
9+
10+
export const textTiny = " text-[13px] md:text-[13px]";
711

8-
export const textError = "text-center text-red-500";
12+
export const textError = " text-center text-red-500";

0 commit comments

Comments
 (0)