1
0
mirror of synced 2025-12-19 10:00:34 -05:00
Files
airbyte/docusaurus/src/components/SourceRequestSchema/SourceConfiguration.tsx
letiescanciano af27d8ce8a WIP
2025-12-18 15:30:12 +01:00

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}
/>
)}
</>
)}
</>
);
};