183 lines
6.1 KiB
TypeScript
183 lines
6.1 KiB
TypeScript
import {
|
|
Combobox,
|
|
ComboboxButton,
|
|
ComboboxInput,
|
|
ComboboxOption,
|
|
ComboboxOptions,
|
|
} from "@headlessui/react";
|
|
import sourceConfigsData from "@site/src/data/source-configs-dereferenced.json";
|
|
import styles from "./SourceConfiguration.module.css";
|
|
import { useState, useMemo } from "react";
|
|
|
|
/**
|
|
* Component to display configuration fields based on selected source type
|
|
* Uses OpenAPI plugin classnames for perfect visual consistency
|
|
*/
|
|
const ConfigurationFields = ({
|
|
selectedSourceId,
|
|
configSchema,
|
|
}: {
|
|
selectedSourceId: string;
|
|
configSchema: any;
|
|
}) => {
|
|
const selectedConfig = configSchema.oneOf?.find(
|
|
(config) => config.title === selectedSourceId,
|
|
);
|
|
|
|
if (!selectedConfig) {
|
|
return null;
|
|
}
|
|
|
|
const requiredFields = selectedConfig.required || [];
|
|
const properties = selectedConfig.properties || {};
|
|
|
|
// Sort properties: required first, then optional
|
|
const sortedEntries = Object.entries(properties).sort(([nameA], [nameB]) => {
|
|
const aRequired = requiredFields.includes(nameA);
|
|
const bRequired = requiredFields.includes(nameB);
|
|
if (aRequired === bRequired) return 0;
|
|
return aRequired ? -1 : 1;
|
|
});
|
|
|
|
return (
|
|
<ul style={{ marginLeft: "1rem", listStyle: "none", padding: 0 }}>
|
|
<div className="openapi-schema__list-item">
|
|
<div>
|
|
<span className="openapi-schema__container">
|
|
<strong className="openapi-schema__property">properties</strong>
|
|
<span className="openapi-schema__name">object</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{sortedEntries.map(([fieldName, fieldSchema]) => (
|
|
<div key={fieldName} className="openapi-schema__list-item">
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
}}
|
|
>
|
|
<span className="openapi-schema__container">
|
|
<strong className="openapi-schema__property">{fieldName}</strong>
|
|
<span className="openapi-schema__name">
|
|
{fieldSchema.title || fieldSchema.type}
|
|
</span>
|
|
</span>
|
|
{requiredFields.includes(fieldName) && (
|
|
<span className="openapi-schema__required">required</span>
|
|
)}
|
|
</div>
|
|
{fieldSchema.description && <p>{fieldSchema.description}</p>}
|
|
</div>
|
|
))}
|
|
</ul>
|
|
);
|
|
};
|
|
|
|
export const SourceConfiguration = ({ endpointData }) => {
|
|
const [sourceConfigs] = useState(sourceConfigsData);
|
|
const [searchQuery, setSearchQuery] = useState("");
|
|
const [selectedSource, setSelectedSource] = useState(
|
|
sourceConfigsData.length > 0 ? sourceConfigsData[0] : null,
|
|
);
|
|
const [bodyExpanded, setBodyExpanded] = useState(true);
|
|
const [configExpanded, setConfigExpanded] = useState(true);
|
|
const [fieldValues, setFieldValues] = useState({});
|
|
|
|
// Filter sources based on search query
|
|
console.log("Source configs data:", endpointData);
|
|
const filteredSources = useMemo(() => {
|
|
if (!searchQuery.trim()) {
|
|
return sourceConfigs;
|
|
}
|
|
const query = searchQuery.toLowerCase();
|
|
return sourceConfigs.filter(
|
|
(source) =>
|
|
source.displayName.toLowerCase().includes(query) ||
|
|
source.id.toLowerCase().includes(query),
|
|
);
|
|
}, [searchQuery, sourceConfigs]);
|
|
|
|
const handleSourceSelect = (source) => {
|
|
console.log("Selected source:", source);
|
|
setSelectedSource(source);
|
|
};
|
|
return (
|
|
<>
|
|
{endpointData?.configurationSchema && (
|
|
<>
|
|
{/* Combobox for source selection */}
|
|
<Combobox
|
|
immediate
|
|
value={selectedSource}
|
|
onChange={handleSourceSelect}
|
|
onClose={() => setSearchQuery("")}
|
|
>
|
|
<div className={styles.comboboxWrapper}>
|
|
<ComboboxInput
|
|
id="source-combobox"
|
|
className={styles.input}
|
|
placeholder="Search sources..."
|
|
displayValue={(source) => source?.id ?? ""}
|
|
onChange={(event) => setSearchQuery(event.target.value)}
|
|
autoComplete="off"
|
|
/>
|
|
<ComboboxButton className={styles.dropdownButton}>
|
|
<svg
|
|
viewBox="0 0 20 20"
|
|
fill="currentColor"
|
|
data-slot="icon"
|
|
aria-hidden="true"
|
|
className="ugz uig"
|
|
>
|
|
<path
|
|
d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z"
|
|
clipRule="evenodd"
|
|
fillRule="evenodd"
|
|
></path>
|
|
</svg>
|
|
</ComboboxButton>
|
|
</div>
|
|
|
|
<ComboboxOptions className={styles.dropdown} anchor="bottom">
|
|
{filteredSources.length === 0 ? (
|
|
<div className={styles.noResults}>
|
|
{searchQuery
|
|
? `No sources found for "${searchQuery}"`
|
|
: "No sources available"}
|
|
</div>
|
|
) : (
|
|
filteredSources.map((source) => (
|
|
<ComboboxOption
|
|
key={source.id}
|
|
value={source}
|
|
className={({ active, selected }) =>
|
|
`${styles.option} ${active ? styles.active : ""} ${
|
|
selected ? styles.selected : ""
|
|
}`
|
|
}
|
|
>
|
|
<span className={styles.optionId}>{source.id}</span>
|
|
</ComboboxOption>
|
|
))
|
|
)}
|
|
</ComboboxOptions>
|
|
</Combobox>
|
|
|
|
{/* Display configuration schema properties for selected source */}
|
|
|
|
{/* Display required properties for selected source */}
|
|
{selectedSource && endpointData.configurationSchema.oneOf && (
|
|
<ConfigurationFields
|
|
selectedSourceId={selectedSource.id}
|
|
configSchema={endpointData.configurationSchema}
|
|
/>
|
|
)}
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
};
|