UNPKG

@rm3l/plugin-scaffolder-frontend-module-devfile-field

Version:

Devfile Field Extensions (Frontend Plugin)

233 lines (229 loc) 8.93 kB
import { useApi, configApiRef, createPlugin } from '@backstage/core-plugin-api'; import { makeFieldSchemaFromZod, scaffolderPlugin } from '@backstage/plugin-scaffolder'; import { createScaffolderFieldExtension } from '@backstage/plugin-scaffolder-react'; import React, { useState } from 'react'; import { FormControl, TextField } from '@material-ui/core'; import { z } from 'zod'; import { useAsync } from 'react-use'; import Autocomplete from '@material-ui/lab/Autocomplete'; import { makeStyles } from '@material-ui/core/styles'; const DevfileSelectorExtensionWithOptionsFieldSchema = makeFieldSchemaFromZod( z.object({ devfile: z.string().describe("Devfile name"), version: z.string().describe("Devfile Stack version"), starter_project: z.string().optional().describe("Devfile Stack starter project") }) ); const DevfileSelectorExtensionWithOptionsSchema = DevfileSelectorExtensionWithOptionsFieldSchema.schema; const useStyles = makeStyles({ option: { fontSize: 15, "& > span": { marginRight: 10, fontSize: 18 } } }); const DevfileSelectorExtension = ({ onChange, rawErrors, required, formData, idSchema, schema: { description } }) => { var _a, _b, _c, _d; const config = useApi(configApiRef); const [loading, setLoading] = useState(true); const [data, setData] = useState([]); const [selectedStack, setSelectedStack] = useState(""); const [versions, setVersions] = useState([]); const [selectedVersion, setSelectedVersion] = useState(""); const [starterprojects, setStarterprojects] = useState([]); const backendUrl = config.getString("backend.baseUrl"); const registryApiEndpoint = `${backendUrl}/api/proxy/devfile-registry/v2index`; useAsync(async () => { const req = await fetch(registryApiEndpoint, { headers: { Accept: "application/json" } }); const resp = await req.json(); resp.sort( (a, b) => { var _a2, _b2; return ((_a2 = a.displayName) != null ? _a2 : "").localeCompare((_b2 = b.displayName) != null ? _b2 : ""); } ); setData(resp); setVersions([]); setStarterprojects([]); setLoading(false); }); const handleDevfileStack = (value) => { var _a2; const filteredStacks = data.filter((stack) => stack.name === (value == null ? void 0 : value.name)); const versionList = filteredStacks.flatMap((stack) => stack.versions); const filteredVersions = versionList.map((v) => v.version); filteredVersions.sort((a, b) => a.localeCompare(b)); let filteredStarterProjects = []; if (versionList.length > 0) { filteredStarterProjects = (_a2 = versionList[0].starterProjects) != null ? _a2 : []; } setSelectedStack(value.name); setSelectedVersion((filteredVersions == null ? void 0 : filteredVersions.length) > 0 ? filteredVersions[0] : ""); setVersions(filteredVersions); setStarterprojects(filteredStarterProjects); onChange({ devfile: value.name, version: versionList.length > 0 ? versionList[0].version : "", starter_project: filteredStarterProjects.length > 0 ? filteredStarterProjects[0] : "" }); }; const handleDevfileStackVersion = (value) => { const filteredResult = data.filter((stack) => stack.name === selectedStack).flatMap((stack) => stack.versions).filter((v) => v.version === value).flatMap((v) => { var _a2; return (_a2 = v.starterProjects) != null ? _a2 : []; }); filteredResult.sort((a, b) => a.localeCompare(b)); setSelectedVersion(value); setStarterprojects(filteredResult); onChange({ devfile: selectedStack, version: value, starter_project: filteredResult.length > 0 ? filteredResult[0] : "" }); }; const handleDevfileStarterProject = (value) => { onChange({ devfile: selectedStack, version: selectedVersion, starter_project: value }); }; const classes = useStyles(); return /* @__PURE__ */ React.createElement( FormControl, { margin: "normal", required, error: (rawErrors == null ? void 0 : rawErrors.length) > 0 }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement( Autocomplete, { id: `devfile-selector-${idSchema == null ? void 0 : idSchema.$id}`, loading, noOptionsText: "No Devfile Stacks available from registry", value: ( // dummy DevfileStack object with the name set, so that getOptionSelected can resolve the right item from data { name: (_a = formData == null ? void 0 : formData.devfile) != null ? _a : selectedStack, icon: "", displayName: "", versions: [] } ), classes: { option: classes.option }, options: data, renderOption: (option) => option.icon ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement( "img", { style: { width: 50, height: 50 }, src: option.icon, alt: `icon for ${option.name}` } )), option.displayName) : /* @__PURE__ */ React.createElement(React.Fragment, null, option.displayName), getOptionLabel: (option) => option.name, renderInput: (params) => /* @__PURE__ */ React.createElement( TextField, { ...params, label: "Devfile Stack", variant: "outlined", required, error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData, inputProps: { ...params.inputProps, autoComplete: "new-password" // disable autocomplete and autofill }, helperText: description } ), onChange: (_, value) => handleDevfileStack(value), getOptionSelected: (option, value) => option.name === value.name, disableClearable: true } )), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement( Autocomplete, { id: `devfile-version-selector-${idSchema == null ? void 0 : idSchema.$id}`, loading, value: (_c = (_b = formData == null ? void 0 : formData.version) != null ? _b : selectedVersion) != null ? _c : versions.length > 0 ? versions[0] : null, noOptionsText: "No version available in Devfile Stack", renderInput: (params) => /* @__PURE__ */ React.createElement( TextField, { ...params, label: "Version", variant: "outlined", required, error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData, helperText: description, inputProps: { ...params.inputProps, autoComplete: "new-password" // disable autocomplete and autofill } } ), options: versions, onChange: (_, value) => handleDevfileStackVersion(value), getOptionSelected: (option, value) => option === value, disableClearable: true } )), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement( Autocomplete, { id: `devfile-starter-project-selector-${idSchema == null ? void 0 : idSchema.$id}`, loading, value: (_d = formData == null ? void 0 : formData.starter_project) != null ? _d : starterprojects.length > 0 ? starterprojects[0] : "", noOptionsText: "No starter project available in Devfile Stack", renderInput: (params) => /* @__PURE__ */ React.createElement( TextField, { ...params, label: "Starter Project", variant: "outlined", required: false, error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData, inputProps: { ...params.inputProps, autoComplete: "new-password" // disable autocomplete and autofill }, helperText: description } ), options: starterprojects, onChange: (_, value) => handleDevfileStarterProject(value), getOptionSelected: (option, value) => option === value, disableClearable: true } )) ); }; const devfileSelectorExtensionPlugin = createPlugin({ id: "devfile-selector-extension" }); const DevfileSelectorFieldExtension = scaffolderPlugin.provide( createScaffolderFieldExtension({ name: "DevfileSelectorExtension", component: DevfileSelectorExtension, schema: DevfileSelectorExtensionWithOptionsSchema }) ); export { DevfileSelectorFieldExtension, devfileSelectorExtensionPlugin }; //# sourceMappingURL=index.esm.js.map