@backstage/plugin-scaffolder 1.10.0-next.1 → 1.10.0-next.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,693 +0,0 @@
1
- import React, { useState, useMemo, useCallback, useContext, useEffect } from 'react';
2
- import { Link, useNavigate, Navigate, useOutlet, Routes, Route } from 'react-router-dom';
3
- import { Progress, ItemCardHeader, Link as Link$1, MarkdownContent, UserIcon, Button as Button$1, Content, ItemCardGrid, ContentHeader, Page, Header, SupportButton, StructuredMetadataTable, InfoCard } from '@backstage/core-components';
4
- import { useEntityTypeFilter, FavoriteEntity, getEntityRelations, EntityRefLinks, useEntityList, EntityListProvider, CatalogFilterLayout, EntitySearchBar, EntityKindPicker, UserListPicker, EntityTagPicker } from '@backstage/plugin-catalog-react';
5
- import capitalize from 'lodash/capitalize';
6
- import { Box, Typography, FormControlLabel, Checkbox, TextField, makeStyles, useTheme, Card, CardContent, Grid, Divider, Chip, CardActions, Stepper as Stepper$1, Step, StepLabel, Button as Button$2 } from '@material-ui/core';
7
- import CheckBoxIcon from '@material-ui/icons/CheckBox';
8
- import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
9
- import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
10
- import { Autocomplete } from '@material-ui/lab';
11
- import { useApi, alertApiRef, useRouteRef, useApp, errorApiRef, featureFlagsApiRef, useRouteRefParams, useAnalytics, useApiHolder, AnalyticsContext, useElementFilter } from '@backstage/core-plugin-api';
12
- import Button from '@material-ui/core/Button';
13
- import IconButton from '@material-ui/core/IconButton';
14
- import useMediaQuery from '@material-ui/core/useMediaQuery';
15
- import AddCircleOutline from '@material-ui/icons/AddCircleOutline';
16
- import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common';
17
- import { usePermission } from '@backstage/plugin-permission-react';
18
- import { B as nextSelectedTemplateRouteRef, v as viewTechDocRouteRef, r as registerComponentRouteRef, s as selectedTemplateRouteRef, C as nextRouteRef, d as scaffolderTaskRouteRef, S as SecretsContext, c as scaffolderApiRef, F as FIELD_EXTENSION_WRAPPER_KEY, i as FIELD_EXTENSION_KEY, k as SecretsContextProvider } from './index-7e25101d.esm.js';
19
- import { RELATION_OWNED_BY, parseEntityRef, stringifyEntityRef, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
20
- import LanguageIcon from '@material-ui/icons/Language';
21
- import useAsync from 'react-use/lib/useAsync';
22
- import { withTheme } from '@rjsf/core-v5';
23
- import { Draft07 } from 'json-schema-library';
24
- import { c as createFieldValidation, e as extractSchemaFromStep, D as DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS } from './schema-bfff60e2.esm.js';
25
- import validator from '@rjsf/validator-ajv6';
26
- import qs from 'qs';
27
- import '@backstage/errors';
28
- import 'zen-observable';
29
- import '@material-ui/core/FormControl';
30
- import '@material-ui/lab/Autocomplete';
31
- import 'zod';
32
- import 'zod-to-json-schema';
33
- import 'react-use/lib/useEffectOnce';
34
- import '@backstage/integration-react';
35
- import '@material-ui/core/FormHelperText';
36
- import '@material-ui/core/Input';
37
- import '@material-ui/core/InputLabel';
38
- import 'react-use/lib/useDebounce';
39
- import '@material-ui/core/Grid';
40
- import '@material-ui/core/Step';
41
- import '@material-ui/core/StepLabel';
42
- import '@material-ui/core/Stepper';
43
- import '@material-ui/core/styles';
44
- import '@material-ui/core/Typography';
45
- import '@material-ui/icons/Cancel';
46
- import '@material-ui/icons/Check';
47
- import '@material-ui/icons/FiberManualRecord';
48
- import 'classnames';
49
- import 'luxon';
50
- import 'react-use/lib/useInterval';
51
- import 'use-immer';
52
-
53
- const icon = /* @__PURE__ */ React.createElement(CheckBoxOutlineBlankIcon, { fontSize: "small" });
54
- const checkedIcon = /* @__PURE__ */ React.createElement(CheckBoxIcon, { fontSize: "small" });
55
- const CategoryPicker = () => {
56
- const alertApi = useApi(alertApiRef);
57
- const { error, loading, availableTypes, selectedTypes, setSelectedTypes } = useEntityTypeFilter();
58
- if (loading)
59
- return /* @__PURE__ */ React.createElement(Progress, null);
60
- if (error) {
61
- alertApi.post({
62
- message: `Failed to load entity types with error: ${error}`,
63
- severity: "error"
64
- });
65
- return null;
66
- }
67
- if (!availableTypes)
68
- return null;
69
- return /* @__PURE__ */ React.createElement(Box, { pb: 1, pt: 1 }, /* @__PURE__ */ React.createElement(Typography, { variant: "button" }, "Categories"), /* @__PURE__ */ React.createElement(
70
- Autocomplete,
71
- {
72
- multiple: true,
73
- "aria-label": "Categories",
74
- options: availableTypes,
75
- value: selectedTypes,
76
- onChange: (_, value) => setSelectedTypes(value),
77
- renderOption: (option, { selected }) => /* @__PURE__ */ React.createElement(
78
- FormControlLabel,
79
- {
80
- control: /* @__PURE__ */ React.createElement(
81
- Checkbox,
82
- {
83
- icon,
84
- checkedIcon,
85
- checked: selected
86
- }
87
- ),
88
- label: capitalize(option)
89
- }
90
- ),
91
- size: "small",
92
- popupIcon: /* @__PURE__ */ React.createElement(ExpandMoreIcon, null),
93
- renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, { ...params, variant: "outlined" })
94
- }
95
- ));
96
- };
97
-
98
- const RegisterExistingButton = (props) => {
99
- const { title, to } = props;
100
- const { allowed } = usePermission({
101
- permission: catalogEntityCreatePermission
102
- });
103
- const isXSScreen = useMediaQuery(
104
- (theme) => theme.breakpoints.down("xs")
105
- );
106
- if (!to || !allowed) {
107
- return null;
108
- }
109
- return isXSScreen ? /* @__PURE__ */ React.createElement(
110
- IconButton,
111
- {
112
- component: Link,
113
- color: "primary",
114
- title,
115
- size: "small",
116
- to
117
- },
118
- /* @__PURE__ */ React.createElement(AddCircleOutline, null)
119
- ) : /* @__PURE__ */ React.createElement(Button, { component: Link, variant: "contained", color: "primary", to }, title);
120
- };
121
-
122
- const useStyles$4 = makeStyles(
123
- () => ({
124
- header: {
125
- backgroundImage: ({ cardBackgroundImage }) => cardBackgroundImage
126
- },
127
- subtitleWrapper: {
128
- display: "flex",
129
- justifyContent: "space-between"
130
- }
131
- })
132
- );
133
- const CardHeader = (props) => {
134
- const {
135
- template: {
136
- metadata: { title, name },
137
- spec: { type }
138
- }
139
- } = props;
140
- const { getPageTheme } = useTheme();
141
- const themeForType = getPageTheme({ themeId: type });
142
- const styles = useStyles$4({
143
- cardBackgroundImage: themeForType.backgroundImage
144
- });
145
- const SubtitleComponent = /* @__PURE__ */ React.createElement("div", { className: styles.subtitleWrapper }, /* @__PURE__ */ React.createElement("div", null, type), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(FavoriteEntity, { entity: props.template, style: { padding: 0 } })));
146
- return /* @__PURE__ */ React.createElement(
147
- ItemCardHeader,
148
- {
149
- title: title != null ? title : name,
150
- subtitle: SubtitleComponent,
151
- classes: { root: styles.header }
152
- }
153
- );
154
- };
155
-
156
- const useStyles$3 = makeStyles(() => ({
157
- linkText: {
158
- display: "inline-flex",
159
- alignItems: "center"
160
- }
161
- }));
162
- const CardLink = ({ icon: Icon, text, url }) => {
163
- const styles = useStyles$3();
164
- return /* @__PURE__ */ React.createElement("div", { className: styles.linkText }, /* @__PURE__ */ React.createElement(Icon, { fontSize: "small" }), /* @__PURE__ */ React.createElement(Link$1, { style: { marginLeft: "8px" }, to: url }, text || url));
165
- };
166
-
167
- const useStyles$2 = makeStyles((theme) => ({
168
- box: {
169
- overflow: "hidden",
170
- textOverflow: "ellipsis",
171
- display: "-webkit-box",
172
- "-webkit-line-clamp": 10,
173
- "-webkit-box-orient": "vertical"
174
- },
175
- markdown: {
176
- "& :first-child": {
177
- margin: 0
178
- }
179
- },
180
- label: {
181
- color: theme.palette.text.secondary,
182
- textTransform: "uppercase",
183
- fontWeight: "bold",
184
- letterSpacing: 0.5,
185
- lineHeight: 1,
186
- fontSize: "0.75rem"
187
- },
188
- footer: {
189
- display: "flex",
190
- justifyContent: "space-between",
191
- flex: 1,
192
- alignItems: "center"
193
- },
194
- ownedBy: {
195
- display: "flex",
196
- alignItems: "center",
197
- flex: 1,
198
- color: theme.palette.link
199
- }
200
- }));
201
- const TemplateCard = (props) => {
202
- var _a, _b, _c, _d, _e, _f, _g;
203
- const { template } = props;
204
- const styles = useStyles$2();
205
- const ownedByRelations = getEntityRelations(template, RELATION_OWNED_BY);
206
- const templateRoute = useRouteRef(nextSelectedTemplateRouteRef);
207
- const { name, namespace } = parseEntityRef(
208
- stringifyEntityRef(props.template)
209
- );
210
- const href = templateRoute({
211
- templateName: name,
212
- namespace
213
- });
214
- const app = useApp();
215
- const iconResolver = (key) => {
216
- var _a2;
217
- return key ? (_a2 = app.getSystemIcon(key)) != null ? _a2 : LanguageIcon : LanguageIcon;
218
- };
219
- const viewTechDoc = useRouteRef(viewTechDocRouteRef);
220
- const viewTechDocsAnnotation = (_a = template.metadata.annotations) == null ? void 0 : _a["backstage.io/techdocs-ref"];
221
- const viewTechDocsLink = !!viewTechDocsAnnotation && !!viewTechDoc && viewTechDoc({
222
- namespace: template.metadata.namespace || DEFAULT_NAMESPACE,
223
- kind: template.kind,
224
- name: template.metadata.name
225
- });
226
- return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardHeader, { template }), /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Box, { className: styles.box }, /* @__PURE__ */ React.createElement(
227
- MarkdownContent,
228
- {
229
- className: styles.markdown,
230
- content: (_b = template.metadata.description) != null ? _b : "No description"
231
- }
232
- ))), ((_d = (_c = template.metadata.tags) == null ? void 0 : _c.length) != null ? _d : 0) > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Divider, null)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, (_e = template.metadata.tags) == null ? void 0 : _e.map((tag) => /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(
233
- Chip,
234
- {
235
- style: { margin: 0 },
236
- size: "small",
237
- label: tag,
238
- key: tag
239
- }
240
- )))))), (!!viewTechDocsLink || ((_f = template.metadata.links) == null ? void 0 : _f.length)) && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Divider, null)), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, viewTechDocsLink && /* @__PURE__ */ React.createElement(Grid, { className: styles.linkText, item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
241
- CardLink,
242
- {
243
- icon: iconResolver("docs"),
244
- text: "View TechDocs",
245
- url: viewTechDocsLink
246
- }
247
- )), (_g = template.metadata.links) == null ? void 0 : _g.map(({ url, icon, title }) => /* @__PURE__ */ React.createElement(Grid, { className: styles.linkText, item: true, xs: 6 }, /* @__PURE__ */ React.createElement(
248
- CardLink,
249
- {
250
- icon: iconResolver(icon),
251
- text: title || url,
252
- url
253
- }
254
- )))))))), /* @__PURE__ */ React.createElement(CardActions, { style: { padding: "16px" } }, /* @__PURE__ */ React.createElement("div", { className: styles.footer }, /* @__PURE__ */ React.createElement("div", { className: styles.ownedBy }, ownedByRelations.length > 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(UserIcon, { fontSize: "small" }), /* @__PURE__ */ React.createElement(
255
- EntityRefLinks,
256
- {
257
- style: { marginLeft: "8px" },
258
- entityRefs: ownedByRelations,
259
- defaultKind: "Group"
260
- }
261
- ))), /* @__PURE__ */ React.createElement(Button$1, { size: "small", variant: "outlined", color: "primary", to: href }, "Choose"))));
262
- };
263
-
264
- const TemplateGroup = (props) => {
265
- const { templates, title, components: { CardComponent } = {} } = props;
266
- const titleComponent = typeof title === "string" ? /* @__PURE__ */ React.createElement(ContentHeader, { title }) : title;
267
- if (templates.length === 0) {
268
- return /* @__PURE__ */ React.createElement(Content, null, titleComponent, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link$1, { to: "https://backstage.io/docs/features/software-templates/adding-templates" }, "adding templates"), "."));
269
- }
270
- const Card = CardComponent || TemplateCard;
271
- return /* @__PURE__ */ React.createElement(Content, null, titleComponent, /* @__PURE__ */ React.createElement(ItemCardGrid, null, templates.map((template) => /* @__PURE__ */ React.createElement(Card, { key: stringifyEntityRef(template), template }))));
272
- };
273
-
274
- const TemplateGroups = (props) => {
275
- const { loading, error, entities } = useEntityList();
276
- const { groups, TemplateCardComponent } = props;
277
- const errorApi = useApi(errorApiRef);
278
- if (loading) {
279
- return /* @__PURE__ */ React.createElement(Progress, null);
280
- }
281
- if (error) {
282
- errorApi.post(error);
283
- return null;
284
- }
285
- if (!entities || !entities.length) {
286
- return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link$1, { to: "https://backstage.io/docs/features/software-templates/adding-templates" }, "adding templates"), ".");
287
- }
288
- return /* @__PURE__ */ React.createElement(React.Fragment, null, groups.map(({ title, filter }, index) => /* @__PURE__ */ React.createElement(
289
- TemplateGroup,
290
- {
291
- key: index,
292
- templates: entities.filter(
293
- (e) => filter(e)
294
- ),
295
- title,
296
- components: { CardComponent: TemplateCardComponent }
297
- }
298
- )));
299
- };
300
-
301
- const defaultGroup = {
302
- title: "All Templates",
303
- filter: () => true
304
- };
305
- const TemplateListPage = (props) => {
306
- const registerComponentLink = useRouteRef(registerComponentRouteRef);
307
- const { TemplateCardComponent, groups = [] } = props;
308
- return /* @__PURE__ */ React.createElement(EntityListProvider, null, /* @__PURE__ */ React.createElement(Page, { themeId: "website" }, /* @__PURE__ */ React.createElement(
309
- Header,
310
- {
311
- pageTitleOverride: "Create a new component",
312
- title: "Create a new component",
313
- subtitle: "Create new software components using standard templates in your organization"
314
- }
315
- ), /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "Available Templates" }, /* @__PURE__ */ React.createElement(
316
- RegisterExistingButton,
317
- {
318
- title: "Register Existing Component",
319
- to: registerComponentLink && registerComponentLink()
320
- }
321
- ), /* @__PURE__ */ React.createElement(SupportButton, null, "Create new software components using standard templates. Different templates create different kinds of components (services, websites, documentation, ...).")), /* @__PURE__ */ React.createElement(CatalogFilterLayout, null, /* @__PURE__ */ React.createElement(CatalogFilterLayout.Filters, null, /* @__PURE__ */ React.createElement(EntitySearchBar, null), /* @__PURE__ */ React.createElement(EntityKindPicker, { initialFilter: "template", hidden: true }), /* @__PURE__ */ React.createElement(
322
- UserListPicker,
323
- {
324
- initialFilter: "all",
325
- availableFilters: ["all", "starred"]
326
- }
327
- ), /* @__PURE__ */ React.createElement(CategoryPicker, null), /* @__PURE__ */ React.createElement(EntityTagPicker, null)), /* @__PURE__ */ React.createElement(CatalogFilterLayout.Content, null, /* @__PURE__ */ React.createElement(
328
- TemplateGroups,
329
- {
330
- groups: [...groups, defaultGroup],
331
- TemplateCardComponent
332
- }
333
- ))))));
334
- };
335
-
336
- const createAsyncValidators = (rootSchema, validators, context) => {
337
- async function validate(formData, pathPrefix = "#") {
338
- const parsedSchema = new Draft07(rootSchema);
339
- const formValidation = {};
340
- for (const [key, value] of Object.entries(formData)) {
341
- const definitionInSchema = parsedSchema.getSchema(
342
- `${pathPrefix}/${key}`,
343
- formData
344
- );
345
- if (definitionInSchema && "ui:field" in definitionInSchema) {
346
- const validator = validators[definitionInSchema["ui:field"]];
347
- if (validator) {
348
- const fieldValidation = createFieldValidation();
349
- try {
350
- await validator(value, fieldValidation, { ...context, formData });
351
- } catch (ex) {
352
- fieldValidation.addError(ex.message);
353
- }
354
- formValidation[key] = fieldValidation;
355
- }
356
- }
357
- }
358
- return formValidation;
359
- }
360
- return async (formData) => {
361
- return await validate(formData);
362
- };
363
- };
364
-
365
- const useTemplateSchema = (manifest) => {
366
- const featureFlags = useApi(featureFlagsApiRef);
367
- const steps = manifest.steps.map(({ title, description, schema }) => ({
368
- title,
369
- description,
370
- mergedSchema: schema,
371
- ...extractSchemaFromStep(schema)
372
- }));
373
- const returningSteps = steps.filter((step) => {
374
- var _a;
375
- const stepFeatureFlag = (_a = step.uiSchema["ui:backstage"]) == null ? void 0 : _a.featureFlag;
376
- return stepFeatureFlag ? featureFlags.isActive(stepFeatureFlag) : true;
377
- }).map((step) => ({
378
- ...step,
379
- schema: {
380
- ...step.schema,
381
- title: void 0,
382
- properties: Object.fromEntries(
383
- Object.entries(step.schema.properties).filter(
384
- ([key]) => {
385
- var _a, _b;
386
- const stepFeatureFlag = (_b = (_a = step.uiSchema[key]) == null ? void 0 : _a["ui:backstage"]) == null ? void 0 : _b.featureFlag;
387
- return stepFeatureFlag ? featureFlags.isActive(stepFeatureFlag) : true;
388
- }
389
- )
390
- )
391
- }
392
- }));
393
- return {
394
- steps: returningSteps
395
- };
396
- };
397
-
398
- const ReviewState = (props) => {
399
- const reviewData = Object.fromEntries(
400
- Object.entries(props.formState).map(([key, value]) => {
401
- var _a;
402
- for (const step of props.schemas) {
403
- const parsedSchema = new Draft07(step.mergedSchema);
404
- const definitionInSchema = parsedSchema.getSchema(
405
- `#/${key}`,
406
- props.formState
407
- );
408
- if (definitionInSchema) {
409
- const backstageReviewOptions = (_a = definitionInSchema["ui:backstage"]) == null ? void 0 : _a.review;
410
- if (backstageReviewOptions) {
411
- if (backstageReviewOptions.mask) {
412
- return [key, backstageReviewOptions.mask];
413
- }
414
- if (backstageReviewOptions.show === false) {
415
- return [];
416
- }
417
- }
418
- if (definitionInSchema["ui:widget"] === "password") {
419
- return [key, "******"];
420
- }
421
- }
422
- }
423
- return [key, value];
424
- })
425
- );
426
- return /* @__PURE__ */ React.createElement(StructuredMetadataTable, { metadata: reviewData });
427
- };
428
-
429
- const useFormData = () => {
430
- return useState(() => {
431
- const query = qs.parse(window.location.search, {
432
- ignoreQueryPrefix: true
433
- });
434
- try {
435
- return JSON.parse(query.formData);
436
- } catch (e) {
437
- return {};
438
- }
439
- });
440
- };
441
-
442
- const useStyles$1 = makeStyles((theme) => ({
443
- backButton: {
444
- marginRight: theme.spacing(1)
445
- },
446
- footer: {
447
- display: "flex",
448
- flexDirection: "row",
449
- justifyContent: "right"
450
- },
451
- formWrapper: {
452
- padding: theme.spacing(2)
453
- }
454
- }));
455
- const Form = withTheme(require("@rjsf/material-ui-v5").Theme);
456
- const Stepper = (props) => {
457
- var _a;
458
- const { templateName } = useRouteRefParams(selectedTemplateRouteRef);
459
- const analytics = useAnalytics();
460
- const { steps } = useTemplateSchema(props.manifest);
461
- const apiHolder = useApiHolder();
462
- const [activeStep, setActiveStep] = useState(0);
463
- const [formState, setFormState] = useFormData();
464
- const [errors, setErrors] = useState();
465
- const styles = useStyles$1();
466
- const extensions = useMemo(() => {
467
- return Object.fromEntries(
468
- props.extensions.map(({ name, component }) => [name, component])
469
- );
470
- }, [props.extensions]);
471
- const validators = useMemo(() => {
472
- return Object.fromEntries(
473
- props.extensions.map(({ name, validation: validation2 }) => [name, validation2])
474
- );
475
- }, [props.extensions]);
476
- const validation = useMemo(() => {
477
- var _a2;
478
- return createAsyncValidators((_a2 = steps[activeStep]) == null ? void 0 : _a2.mergedSchema, validators, {
479
- apiHolder
480
- });
481
- }, [steps, activeStep, validators, apiHolder]);
482
- const handleBack = () => {
483
- setActiveStep((prevActiveStep) => prevActiveStep - 1);
484
- };
485
- const handleChange = useCallback(
486
- (e) => setFormState((current) => ({ ...current, ...e.formData })),
487
- [setFormState]
488
- );
489
- const handleNext = async ({
490
- formData
491
- }) => {
492
- setErrors(void 0);
493
- const returnedValidation = await validation(formData);
494
- const hasErrors = Object.values(returnedValidation).some(
495
- (i) => {
496
- var _a2;
497
- return (_a2 = i.__errors) == null ? void 0 : _a2.length;
498
- }
499
- );
500
- if (hasErrors) {
501
- setErrors(returnedValidation);
502
- } else {
503
- setErrors(void 0);
504
- setActiveStep((prevActiveStep) => {
505
- const stepNum = prevActiveStep + 1;
506
- analytics.captureEvent("click", `Next Step (${stepNum})`);
507
- return stepNum;
508
- });
509
- }
510
- setFormState((current) => ({ ...current, ...formData }));
511
- };
512
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Stepper$1, { activeStep, alternativeLabel: true, variant: "elevation" }, steps.map((step, index) => /* @__PURE__ */ React.createElement(Step, { key: index }, /* @__PURE__ */ React.createElement(StepLabel, null, step.title))), /* @__PURE__ */ React.createElement(Step, null, /* @__PURE__ */ React.createElement(StepLabel, null, "Review"))), /* @__PURE__ */ React.createElement("div", { className: styles.formWrapper }, activeStep < steps.length ? /* @__PURE__ */ React.createElement(
513
- Form,
514
- {
515
- validator,
516
- extraErrors: errors,
517
- formData: formState,
518
- formContext: { formData: formState },
519
- schema: steps[activeStep].schema,
520
- uiSchema: steps[activeStep].uiSchema,
521
- onSubmit: handleNext,
522
- fields: extensions,
523
- showErrorList: false,
524
- onChange: handleChange,
525
- ...(_a = props.FormProps) != null ? _a : {}
526
- },
527
- /* @__PURE__ */ React.createElement("div", { className: styles.footer }, /* @__PURE__ */ React.createElement(
528
- Button$2,
529
- {
530
- onClick: handleBack,
531
- className: styles.backButton,
532
- disabled: activeStep < 1
533
- },
534
- "Back"
535
- ), /* @__PURE__ */ React.createElement(Button$2, { variant: "contained", color: "primary", type: "submit" }, activeStep === steps.length - 1 ? "Review" : "Next"))
536
- ) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ReviewState, { formState, schemas: steps }), /* @__PURE__ */ React.createElement("div", { className: styles.footer }, /* @__PURE__ */ React.createElement(
537
- Button$2,
538
- {
539
- onClick: handleBack,
540
- className: styles.backButton,
541
- disabled: activeStep < 1
542
- },
543
- "Back"
544
- ), /* @__PURE__ */ React.createElement(
545
- Button$2,
546
- {
547
- variant: "contained",
548
- onClick: () => {
549
- props.onComplete(formState);
550
- const name = typeof formState.name === "string" ? formState.name : void 0;
551
- analytics.captureEvent(
552
- "create",
553
- name || `new ${templateName}`
554
- );
555
- }
556
- },
557
- "Create"
558
- )))));
559
- };
560
-
561
- const useStyles = makeStyles(() => ({
562
- markdown: {
563
- "& :first-child": {
564
- marginTop: 0
565
- },
566
- "& :last-child": {
567
- marginBottom: 0
568
- }
569
- }
570
- }));
571
- const useTemplateParameterSchema = (templateRef) => {
572
- const scaffolderApi = useApi(scaffolderApiRef);
573
- const { value, loading, error } = useAsync(
574
- () => scaffolderApi.getTemplateParameterSchema(templateRef),
575
- [scaffolderApi, templateRef]
576
- );
577
- return { manifest: value, loading, error };
578
- };
579
- const TemplateWizardPage = (props) => {
580
- var _a, _b;
581
- const styles = useStyles();
582
- const rootRef = useRouteRef(nextRouteRef);
583
- const taskRoute = useRouteRef(scaffolderTaskRouteRef);
584
- const { secrets } = (_a = useContext(SecretsContext)) != null ? _a : {};
585
- const scaffolderApi = useApi(scaffolderApiRef);
586
- const navigate = useNavigate();
587
- const { templateName, namespace } = useRouteRefParams(
588
- selectedTemplateRouteRef
589
- );
590
- const templateRef = stringifyEntityRef({
591
- kind: "Template",
592
- namespace,
593
- name: templateName
594
- });
595
- const errorApi = useApi(errorApiRef);
596
- const { loading, manifest, error } = useTemplateParameterSchema(templateRef);
597
- const onComplete = async (values) => {
598
- const { taskId } = await scaffolderApi.scaffold({
599
- templateRef,
600
- values,
601
- secrets
602
- });
603
- navigate(taskRoute({ taskId }));
604
- };
605
- useEffect(() => {
606
- if (error) {
607
- errorApi.post(new Error(`Failed to load template, ${error}`));
608
- }
609
- }, [error, errorApi]);
610
- if (error) {
611
- return /* @__PURE__ */ React.createElement(Navigate, { to: rootRef() });
612
- }
613
- return /* @__PURE__ */ React.createElement(AnalyticsContext, { attributes: { entityRef: templateRef } }, /* @__PURE__ */ React.createElement(Page, { themeId: "website" }, /* @__PURE__ */ React.createElement(
614
- Header,
615
- {
616
- pageTitleOverride: "Create a new component",
617
- title: "Create a new component",
618
- subtitle: "Create new software components using standard templates in your organization"
619
- }
620
- ), /* @__PURE__ */ React.createElement(Content, null, loading && /* @__PURE__ */ React.createElement(Progress, null), manifest && /* @__PURE__ */ React.createElement(
621
- InfoCard,
622
- {
623
- title: manifest.title,
624
- subheader: /* @__PURE__ */ React.createElement(
625
- MarkdownContent,
626
- {
627
- className: styles.markdown,
628
- content: (_b = manifest.description) != null ? _b : "No description"
629
- }
630
- ),
631
- noPadding: true,
632
- titleTypographyProps: { component: "h2" }
633
- },
634
- /* @__PURE__ */ React.createElement(
635
- Stepper,
636
- {
637
- manifest,
638
- extensions: props.customFieldExtensions,
639
- onComplete,
640
- FormProps: props.FormProps
641
- }
642
- )
643
- ))));
644
- };
645
-
646
- const Router = (props) => {
647
- const { components: { TemplateCardComponent } = {} } = props;
648
- const outlet = useOutlet() || props.children;
649
- const customFieldExtensions = useElementFilter(
650
- outlet,
651
- (elements) => elements.selectByComponentData({
652
- key: FIELD_EXTENSION_WRAPPER_KEY
653
- }).findComponentData({
654
- key: FIELD_EXTENSION_KEY
655
- })
656
- );
657
- const fieldExtensions = [
658
- ...customFieldExtensions,
659
- ...DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS.filter(
660
- ({ name }) => !customFieldExtensions.some(
661
- (customFieldExtension) => customFieldExtension.name === name
662
- )
663
- )
664
- ];
665
- return /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(
666
- Route,
667
- {
668
- path: "/",
669
- element: /* @__PURE__ */ React.createElement(
670
- TemplateListPage,
671
- {
672
- TemplateCardComponent,
673
- groups: props.groups
674
- }
675
- )
676
- }
677
- ), /* @__PURE__ */ React.createElement(
678
- Route,
679
- {
680
- path: nextSelectedTemplateRouteRef.path,
681
- element: /* @__PURE__ */ React.createElement(SecretsContextProvider, null, /* @__PURE__ */ React.createElement(
682
- TemplateWizardPage,
683
- {
684
- customFieldExtensions: fieldExtensions,
685
- FormProps: props.FormProps
686
- }
687
- ))
688
- }
689
- ));
690
- };
691
-
692
- export { Router };
693
- //# sourceMappingURL=index-78c07979.esm.js.map