@backstage/plugin-scaffolder 0.12.2 → 0.14.0

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,14 +1,14 @@
1
- import { createApiRef, useApi, attachComponentData, createExternalRouteRef, createRouteRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createRoutableExtension, useRouteRef, alertApiRef, useApp } from '@backstage/core-plugin-api';
1
+ import { parseEntityRef, KubernetesValidatorFunctions, makeValidator, RELATION_OWNED_BY, stringifyEntityRef } from '@backstage/catalog-model';
2
+ import { createApiRef, useApi, attachComponentData, createExternalRouteRef, createRouteRef, createSubRouteRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createRoutableExtension, useRouteRef, alertApiRef, useApp } from '@backstage/core-plugin-api';
2
3
  import { ResponseError } from '@backstage/errors';
3
4
  import qs from 'qs';
4
5
  import ObservableImpl from 'zen-observable';
5
- import { catalogApiRef, formatEntityRefTitle, useOwnedEntities, useStarredEntity, getEntityRelations, getEntitySourceLocation, EntityRefLinks, useEntityListProvider, useEntityTypeFilter, entityRouteRef } from '@backstage/plugin-catalog-react';
6
- import { TextField, FormControl as FormControl$1, withStyles, makeStyles, IconButton, Tooltip, useTheme, Card, CardMedia, CardContent, Box, Typography, Chip, CardActions, Link, FormControlLabel, Checkbox, Grid, StepButton, Paper, Button as Button$1, CircularProgress } from '@material-ui/core';
6
+ import { catalogApiRef, humanizeEntityRef, useOwnedEntities, getEntityRelations, getEntitySourceLocation, FavoriteEntity, EntityRefLinks, useEntityList, useEntityTypeFilter, entityRouteRef } from '@backstage/plugin-catalog-react';
7
+ import { TextField, FormControl as FormControl$1, makeStyles, useTheme, Card, CardMedia, CardContent, Box, Typography, Chip, CardActions, IconButton, Tooltip, Link, FormControlLabel, Checkbox, Grid, StepButton, Paper, Button as Button$1, CircularProgress } from '@material-ui/core';
7
8
  import FormControl from '@material-ui/core/FormControl';
8
9
  import Autocomplete from '@material-ui/lab/Autocomplete';
9
10
  import React, { useCallback, useEffect, useState, createContext, useContext, useMemo, memo } from 'react';
10
11
  import useAsync from 'react-use/lib/useAsync';
11
- import { KubernetesValidatorFunctions, makeValidator, RELATION_OWNED_BY, stringifyEntityRef, parseEntityName } from '@backstage/catalog-model';
12
12
  import useEffectOnce from 'react-use/lib/useEffectOnce';
13
13
  import { Autocomplete as Autocomplete$1 } from '@material-ui/lab';
14
14
  import { scmIntegrationsApiRef, scmAuthApiRef, ScmIntegrationIcon } from '@backstage/integration-react';
@@ -17,10 +17,7 @@ import Input from '@material-ui/core/Input';
17
17
  import InputLabel from '@material-ui/core/InputLabel';
18
18
  import { Select, Progress, ItemCardHeader, Button, ContentHeader, WarningPanel, Link as Link$1, Content, ItemCardGrid, Page, Header, Lifecycle, ErrorPage, LogViewer } from '@backstage/core-components';
19
19
  import useDebounce from 'react-use/lib/useDebounce';
20
- import Star from '@material-ui/icons/Star';
21
- import StarBorder from '@material-ui/icons/StarBorder';
22
20
  import WarningIcon from '@material-ui/icons/Warning';
23
- import { generatePath, useNavigate, useParams } from 'react-router';
24
21
  import capitalize from 'lodash/capitalize';
25
22
  import CheckBoxIcon from '@material-ui/icons/CheckBox';
26
23
  import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
@@ -36,6 +33,7 @@ import Check from '@material-ui/icons/Check';
36
33
  import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
37
34
  import classNames from 'classnames';
38
35
  import { DateTime, Interval } from 'luxon';
36
+ import { useNavigate, useParams } from 'react-router';
39
37
  import useInterval from 'react-use/lib/useInterval';
40
38
  import { useImmerReducer } from 'use-immer';
41
39
  import LanguageIcon from '@material-ui/icons/Language';
@@ -52,15 +50,20 @@ class ScaffolderClient {
52
50
  this.useLongPollingLogs = (_b = options.useLongPollingLogs) != null ? _b : false;
53
51
  }
54
52
  async getIntegrationsList(options) {
55
- return [
53
+ const integrations = [
56
54
  ...this.scmIntegrationsApi.azure.list(),
57
55
  ...this.scmIntegrationsApi.bitbucket.list(),
58
56
  ...this.scmIntegrationsApi.github.list(),
59
57
  ...this.scmIntegrationsApi.gitlab.list()
60
58
  ].map((c) => ({ type: c.type, title: c.title, host: c.config.host })).filter((c) => options.allowedHosts.includes(c.host));
59
+ return {
60
+ integrations
61
+ };
61
62
  }
62
- async getTemplateParameterSchema(templateName) {
63
- const { namespace, kind, name } = templateName;
63
+ async getTemplateParameterSchema(templateRef) {
64
+ const { namespace, kind, name } = parseEntityRef(templateRef, {
65
+ defaultKind: "template"
66
+ });
64
67
  const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
65
68
  const templatePath = [namespace, kind, name].map((s) => encodeURIComponent(s)).join("/");
66
69
  const url = `${baseUrl}/v2/templates/${templatePath}/parameter-schema`;
@@ -71,7 +74,8 @@ class ScaffolderClient {
71
74
  const schema = await response.json();
72
75
  return schema;
73
76
  }
74
- async scaffold(templateName, values, secrets = {}) {
77
+ async scaffold(options) {
78
+ const { templateRef, values, secrets = {} } = options;
75
79
  const url = `${await this.discoveryApi.getBaseUrl("scaffolder")}/v2/tasks`;
76
80
  const response = await this.fetchApi.fetch(url, {
77
81
  method: "POST",
@@ -79,7 +83,7 @@ class ScaffolderClient {
79
83
  "Content-Type": "application/json"
80
84
  },
81
85
  body: JSON.stringify({
82
- templateName,
86
+ templateRef,
83
87
  values: { ...values },
84
88
  secrets
85
89
  })
@@ -90,7 +94,7 @@ class ScaffolderClient {
90
94
  throw new Error(`Backend request failed, ${status} ${body.trim()}`);
91
95
  }
92
96
  const { id } = await response.json();
93
- return id;
97
+ return { taskId: id };
94
98
  }
95
99
  async getTask(taskId) {
96
100
  const baseUrl = await this.discoveryApi.getBaseUrl("scaffolder");
@@ -184,25 +188,22 @@ class ScaffolderClient {
184
188
  }
185
189
  }
186
190
 
187
- const allowArbitraryValues = (uiSchema) => {
188
- var _a, _b;
189
- return (_b = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowArbitraryValues) != null ? _b : true;
190
- };
191
- const EntityPicker = ({
192
- onChange,
193
- schema: { title = "Entity", description = "An entity from the catalog" },
194
- required,
195
- uiSchema,
196
- rawErrors,
197
- formData,
198
- idSchema
199
- }) => {
200
- var _a, _b;
191
+ const EntityPicker = (props) => {
192
+ var _a, _b, _c, _d;
193
+ const {
194
+ onChange,
195
+ schema: { title = "Entity", description = "An entity from the catalog" },
196
+ required,
197
+ uiSchema,
198
+ rawErrors,
199
+ formData,
200
+ idSchema
201
+ } = props;
201
202
  const allowedKinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowedKinds;
202
203
  const defaultKind = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.defaultKind;
203
204
  const catalogApi = useApi(catalogApiRef);
204
205
  const { value: entities, loading } = useAsync(() => catalogApi.getEntities(allowedKinds ? { filter: { kind: allowedKinds } } : void 0));
205
- const entityRefs = entities == null ? void 0 : entities.items.map((e) => formatEntityRefTitle(e, { defaultKind }));
206
+ const entityRefs = entities == null ? void 0 : entities.items.map((e) => humanizeEntityRef(e, { defaultKind }));
206
207
  const onSelect = useCallback((_, value) => {
207
208
  onChange(value || "");
208
209
  }, [onChange]);
@@ -223,7 +224,7 @@ const EntityPicker = ({
223
224
  onChange: onSelect,
224
225
  options: entityRefs || [],
225
226
  autoSelect: true,
226
- freeSolo: allowArbitraryValues(uiSchema),
227
+ freeSolo: (_d = (_c = uiSchema["ui:options"]) == null ? void 0 : _c.allowArbitraryValues) != null ? _d : true,
227
228
  renderInput: (params) => /* @__PURE__ */ React.createElement(TextField, {
228
229
  ...params,
229
230
  label: title,
@@ -236,35 +237,30 @@ const EntityPicker = ({
236
237
  }));
237
238
  };
238
239
 
239
- const TextValuePicker = ({
240
- onChange,
241
- required,
242
- schema: { title, description },
243
- rawErrors,
244
- formData,
245
- uiSchema: { "ui:autofocus": autoFocus },
246
- idSchema,
247
- placeholder
248
- }) => /* @__PURE__ */ React.createElement(TextField, {
249
- id: idSchema == null ? void 0 : idSchema.$id,
250
- label: title,
251
- placeholder,
252
- helperText: description,
253
- required,
254
- value: formData != null ? formData : "",
255
- onChange: ({ target: { value } }) => onChange(value),
256
- margin: "normal",
257
- error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData,
258
- inputProps: { autoFocus }
259
- });
260
-
261
- const EntityNamePicker = ({
262
- schema: { title = "Name", description = "Unique name of the component" },
263
- ...props
264
- }) => /* @__PURE__ */ React.createElement(TextValuePicker, {
265
- schema: { title, description },
266
- ...props
267
- });
240
+ const EntityNamePicker = (props) => {
241
+ const {
242
+ onChange,
243
+ required,
244
+ schema: { title = "Name", description = "Unique name of the component" },
245
+ rawErrors,
246
+ formData,
247
+ uiSchema: { "ui:autofocus": autoFocus },
248
+ idSchema,
249
+ placeholder
250
+ } = props;
251
+ return /* @__PURE__ */ React.createElement(TextField, {
252
+ id: idSchema == null ? void 0 : idSchema.$id,
253
+ label: title,
254
+ placeholder,
255
+ helperText: description,
256
+ required,
257
+ value: formData != null ? formData : "",
258
+ onChange: ({ target: { value } }) => onChange(value),
259
+ margin: "normal",
260
+ error: (rawErrors == null ? void 0 : rawErrors.length) > 0 && !formData,
261
+ inputProps: { autoFocus }
262
+ });
263
+ };
268
264
 
269
265
  const entityNamePickerValidation = (value, validation) => {
270
266
  if (!KubernetesValidatorFunctions.isValidObjectName(value)) {
@@ -272,12 +268,9 @@ const entityNamePickerValidation = (value, validation) => {
272
268
  }
273
269
  };
274
270
 
275
- const EntityTagsPicker = ({
276
- formData,
277
- onChange,
278
- uiSchema
279
- }) => {
271
+ const EntityTagsPicker = (props) => {
280
272
  var _a;
273
+ const { formData, onChange, uiSchema } = props;
281
274
  const catalogApi = useApi(catalogApiRef);
282
275
  const [inputValue, setInputValue] = useState("");
283
276
  const [inputError, setInputError] = useState(false);
@@ -334,12 +327,13 @@ const EntityTagsPicker = ({
334
327
  }));
335
328
  };
336
329
 
337
- const OwnerPicker = ({
338
- schema: { title = "Owner", description = "The owner of the component" },
339
- uiSchema,
340
- ...props
341
- }) => {
330
+ const OwnerPicker = (props) => {
342
331
  var _a;
332
+ const {
333
+ schema: { title = "Owner", description = "The owner of the component" },
334
+ uiSchema,
335
+ ...restProps
336
+ } = props;
343
337
  const ownerUiSchema = {
344
338
  ...uiSchema,
345
339
  "ui:options": {
@@ -351,7 +345,7 @@ const OwnerPicker = ({
351
345
  }
352
346
  };
353
347
  return /* @__PURE__ */ React.createElement(EntityPicker, {
354
- ...props,
348
+ ...restProps,
355
349
  schema: { title, description },
356
350
  uiSchema: ownerUiSchema
357
351
  });
@@ -502,7 +496,7 @@ const BitbucketRepoPicker = (props) => {
502
496
  const RepoUrlPickerHost = (props) => {
503
497
  const { host, hosts, onChange, rawErrors } = props;
504
498
  const scaffolderApi = useApi(scaffolderApiRef);
505
- const { value: integrations, loading } = useAsync(async () => {
499
+ const { value: { integrations } = { integrations: [] }, loading } = useAsync(async () => {
506
500
  return await scaffolderApi.getIntegrationsList({
507
501
  allowedHosts: hosts != null ? hosts : []
508
502
  });
@@ -591,11 +585,11 @@ const useTemplateSecrets = () => {
591
585
  if (!value) {
592
586
  throw new Error("useTemplateSecrets must be used within a SecretsContextProvider");
593
587
  }
594
- const { setSecrets } = value;
595
- const setSecret = useCallback((input) => {
596
- setSecrets((currentSecrets) => ({ ...currentSecrets, ...input }));
597
- }, [setSecrets]);
598
- return { setSecret };
588
+ const { setSecrets: updateSecrets } = value;
589
+ const setSecrets = useCallback((input) => {
590
+ updateSecrets((currentSecrets) => ({ ...currentSecrets, ...input }));
591
+ }, [updateSecrets]);
592
+ return { setSecret: setSecrets, setSecrets };
599
593
  };
600
594
 
601
595
  const RepoUrlPicker = (props) => {
@@ -604,7 +598,7 @@ const RepoUrlPicker = (props) => {
604
598
  const [state, setState] = useState(parseRepoPickerUrl(formData));
605
599
  const integrationApi = useApi(scmIntegrationsApiRef);
606
600
  const scmAuthApi = useApi(scmAuthApiRef);
607
- const { setSecret } = useTemplateSecrets();
601
+ const { setSecrets } = useTemplateSecrets();
608
602
  const allowedHosts = useMemo(() => {
609
603
  var _a2, _b2;
610
604
  return (_b2 = (_a2 = uiSchema == null ? void 0 : uiSchema["ui:options"]) == null ? void 0 : _a2.allowedHosts) != null ? _b2 : [];
@@ -642,7 +636,7 @@ const RepoUrlPicker = (props) => {
642
636
  customScopes: requestUserCredentials.additionalScopes
643
637
  }
644
638
  });
645
- setSecret({ [requestUserCredentials.secretsKey]: token });
639
+ setSecrets({ [requestUserCredentials.secretsKey]: token });
646
640
  }, 500, [state, uiSchema]);
647
641
  const hostType = (_b = state.host && ((_a = integrationApi.byHost(state.host)) == null ? void 0 : _a.type)) != null ? _b : null;
648
642
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(RepoUrlPickerHost, {
@@ -700,20 +694,21 @@ const repoPickerValidation = (value, validation, context) => {
700
694
  }
701
695
  };
702
696
 
703
- const OwnedEntityPicker = ({
704
- onChange,
705
- schema: { title = "Entity", description = "An entity from the catalog" },
706
- required,
707
- uiSchema,
708
- rawErrors,
709
- formData,
710
- idSchema
711
- }) => {
697
+ const OwnedEntityPicker = (props) => {
712
698
  var _a, _b;
699
+ const {
700
+ onChange,
701
+ schema: { title = "Entity", description = "An entity from the catalog" },
702
+ required,
703
+ uiSchema,
704
+ rawErrors,
705
+ formData,
706
+ idSchema
707
+ } = props;
713
708
  const allowedKinds = (_a = uiSchema["ui:options"]) == null ? void 0 : _a.allowedKinds;
714
709
  const defaultKind = (_b = uiSchema["ui:options"]) == null ? void 0 : _b.defaultKind;
715
710
  const { ownedEntities, loading } = useOwnedEntities(allowedKinds);
716
- const entityRefs = ownedEntities == null ? void 0 : ownedEntities.items.map((e) => formatEntityRefTitle(e, { defaultKind })).filter((n) => n);
711
+ const entityRefs = ownedEntities == null ? void 0 : ownedEntities.items.map((e) => humanizeEntityRef(e, { defaultKind })).filter((n) => n);
717
712
  const onSelect = (_, value) => {
718
713
  onChange(value || "");
719
714
  };
@@ -762,6 +757,21 @@ const registerComponentRouteRef = createExternalRouteRef({
762
757
  const rootRouteRef = createRouteRef({
763
758
  id: "scaffolder"
764
759
  });
760
+ const selectedTemplateRouteRef = createSubRouteRef({
761
+ id: "scaffolder/selected-template",
762
+ parent: rootRouteRef,
763
+ path: "/templates/:templateName"
764
+ });
765
+ const scaffolderTaskRouteRef = createSubRouteRef({
766
+ id: "scaffolder/task",
767
+ parent: rootRouteRef,
768
+ path: "/tasks/:taskId"
769
+ });
770
+ createSubRouteRef({
771
+ id: "scaffolder/actions",
772
+ parent: rootRouteRef,
773
+ path: "/actions"
774
+ });
765
775
 
766
776
  const scaffolderPlugin = createPlugin({
767
777
  id: "scaffolder",
@@ -807,7 +817,7 @@ const OwnerPickerFieldExtension = scaffolderPlugin.provide(createScaffolderField
807
817
  }));
808
818
  const ScaffolderPage = scaffolderPlugin.provide(createRoutableExtension({
809
819
  name: "ScaffolderPage",
810
- component: () => import('./Router-5df57206.esm.js').then((m) => m.Router),
820
+ component: () => import('./Router-ba25f61c.esm.js').then((m) => m.Router),
811
821
  mountPoint: rootRouteRef
812
822
  }));
813
823
  const OwnedEntityPickerFieldExtension = scaffolderPlugin.provide(createScaffolderFieldExtension({
@@ -819,39 +829,6 @@ const EntityTagsPickerFieldExtension = scaffolderPlugin.provide(createScaffolder
819
829
  name: "EntityTagsPicker"
820
830
  }));
821
831
 
822
- const YellowStar = withStyles({
823
- root: {
824
- color: "#f3ba37"
825
- }
826
- })(Star);
827
- const WhiteBorderStar = withStyles({
828
- root: {
829
- color: "#ffffff"
830
- }
831
- })(StarBorder);
832
- const useStyles$3 = makeStyles((theme) => ({
833
- starButton: {
834
- position: "absolute",
835
- top: theme.spacing(0.5),
836
- right: theme.spacing(0.5),
837
- padding: "0.25rem"
838
- }
839
- }));
840
- const favouriteTemplateTooltip = (isStarred) => isStarred ? "Remove from favorites" : "Add to favorites";
841
- const favouriteTemplateIcon = (isStarred) => isStarred ? /* @__PURE__ */ React.createElement(YellowStar, null) : /* @__PURE__ */ React.createElement(WhiteBorderStar, null);
842
- const FavouriteTemplate = (props) => {
843
- const classes = useStyles$3();
844
- const { toggleStarredEntity, isStarredEntity } = useStarredEntity(props.entity);
845
- return /* @__PURE__ */ React.createElement(IconButton, {
846
- color: "inherit",
847
- className: classes.starButton,
848
- ...props,
849
- onClick: () => toggleStarredEntity()
850
- }, /* @__PURE__ */ React.createElement(Tooltip, {
851
- title: favouriteTemplateTooltip(isStarredEntity)
852
- }, favouriteTemplateIcon(isStarredEntity)));
853
- };
854
-
855
832
  const useStyles$2 = makeStyles((theme) => ({
856
833
  cardHeader: {
857
834
  position: "relative"
@@ -878,6 +855,12 @@ const useStyles$2 = makeStyles((theme) => ({
878
855
  },
879
856
  leftButton: {
880
857
  marginRight: "auto"
858
+ },
859
+ starButton: {
860
+ position: "absolute",
861
+ top: theme.spacing(0.5),
862
+ right: theme.spacing(0.5),
863
+ padding: "0.25rem"
881
864
  }
882
865
  }));
883
866
  const useDeprecationStyles = makeStyles((theme) => ({
@@ -906,33 +889,32 @@ const DeprecationWarning = () => {
906
889
  const styles = useDeprecationStyles();
907
890
  const Title = /* @__PURE__ */ React.createElement(Typography, {
908
891
  style: { padding: 10, maxWidth: 300 }
909
- }, "This template syntax is deprecated. Click for more info.");
892
+ }, "This template uses a syntax that has been deprecated, and should be migrated to a newer syntax. Click for more info.");
910
893
  return /* @__PURE__ */ React.createElement("div", {
911
894
  className: styles.deprecationIcon
912
895
  }, /* @__PURE__ */ React.createElement(Tooltip, {
913
896
  title: Title
914
897
  }, /* @__PURE__ */ React.createElement(Link, {
915
- href: "https://backstage.io/docs/features/software-templates/migrating-from-v1alpha1-to-v1beta2",
898
+ href: "https://backstage.io/docs/features/software-templates/migrating-from-v1beta2-to-v1beta3",
916
899
  className: styles.link
917
900
  }, /* @__PURE__ */ React.createElement(WarningIcon, null))));
918
901
  };
919
902
  const TemplateCard = ({ template, deprecated }) => {
920
903
  var _a;
921
904
  const backstageTheme = useTheme();
922
- const rootLink = useRouteRef(rootRouteRef);
905
+ const templateRoute = useRouteRef(selectedTemplateRouteRef);
923
906
  const templateProps = getTemplateCardProps(template);
924
907
  const ownedByRelations = getEntityRelations(template, RELATION_OWNED_BY);
925
908
  const themeId = backstageTheme.getPageTheme({ themeId: templateProps.type }) ? templateProps.type : "other";
926
909
  const theme = backstageTheme.getPageTheme({ themeId });
927
910
  const classes = useStyles$2({ backgroundImage: theme.backgroundImage });
928
- const href = generatePath(`${rootLink()}/templates/:templateName`, {
929
- templateName: templateProps.name
930
- });
911
+ const href = templateRoute({ templateName: templateProps.name });
931
912
  const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
932
913
  const sourceLocation = getEntitySourceLocation(template, scmIntegrationsApi);
933
914
  return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardMedia, {
934
915
  className: classes.cardHeader
935
- }, /* @__PURE__ */ React.createElement(FavouriteTemplate, {
916
+ }, /* @__PURE__ */ React.createElement(FavoriteEntity, {
917
+ className: classes.starButton,
936
918
  entity: template
937
919
  }), deprecated && /* @__PURE__ */ React.createElement(DeprecationWarning, null), /* @__PURE__ */ React.createElement(ItemCardHeader, {
938
920
  title: templateProps.title,
@@ -976,14 +958,26 @@ const TemplateList = ({
976
958
  TemplateCardComponent,
977
959
  group
978
960
  }) => {
979
- const { loading, error, entities } = useEntityListProvider();
961
+ const { loading, error, entities } = useEntityList();
980
962
  const Card = TemplateCardComponent || TemplateCard;
981
963
  const maybeFilteredEntities = group ? entities.filter((e) => group.filter(e)) : entities;
982
- const title = group ? group.titleComponent || /* @__PURE__ */ React.createElement(ContentHeader, {
983
- title: group.title
984
- }) : /* @__PURE__ */ React.createElement(ContentHeader, {
985
- title: "Other Templates"
986
- });
964
+ const titleComponent = (() => {
965
+ if (group == null ? void 0 : group.titleComponent) {
966
+ console.warn("DEPRECATED: group.titleComponent is now deprecated. Use group.title instead, it can be a string or a react component");
967
+ return group == null ? void 0 : group.titleComponent;
968
+ }
969
+ if (group && group.title) {
970
+ if (typeof group.title === "string") {
971
+ return /* @__PURE__ */ React.createElement(ContentHeader, {
972
+ title: group.title
973
+ });
974
+ }
975
+ return group.title;
976
+ }
977
+ return /* @__PURE__ */ React.createElement(ContentHeader, {
978
+ title: "Other Templates"
979
+ });
980
+ })();
987
981
  if (group && maybeFilteredEntities.length === 0) {
988
982
  return null;
989
983
  }
@@ -993,9 +987,10 @@ const TemplateList = ({
993
987
  variant: "body2"
994
988
  }, "No templates found that match your filter. Learn more about", " ", /* @__PURE__ */ React.createElement(Link$1, {
995
989
  to: "https://backstage.io/docs/features/software-templates/adding-templates"
996
- }, "adding templates"), "."), /* @__PURE__ */ React.createElement(Content, null, title, /* @__PURE__ */ React.createElement(ItemCardGrid, null, maybeFilteredEntities && (maybeFilteredEntities == null ? void 0 : maybeFilteredEntities.length) > 0 && maybeFilteredEntities.map((template) => /* @__PURE__ */ React.createElement(Card, {
990
+ }, "adding templates"), "."), /* @__PURE__ */ React.createElement(Content, null, titleComponent, /* @__PURE__ */ React.createElement(ItemCardGrid, null, maybeFilteredEntities && (maybeFilteredEntities == null ? void 0 : maybeFilteredEntities.length) > 0 && maybeFilteredEntities.map((template) => /* @__PURE__ */ React.createElement(Card, {
997
991
  key: stringifyEntityRef(template),
998
- template
992
+ template,
993
+ deprecated: template.apiVersion === "backstage.io/v1beta2"
999
994
  })))));
1000
995
  };
1001
996
 
@@ -1223,7 +1218,7 @@ const TaskPageLinks = ({ output }) => {
1223
1218
  pb: 3
1224
1219
  }, links.filter(({ url, entityRef }) => url || entityRef).map(({ url, entityRef, title, icon }) => {
1225
1220
  if (entityRef) {
1226
- const entityName = parseEntityName(entityRef);
1221
+ const entityName = parseEntityRef(entityRef);
1227
1222
  const target = entityRoute(entityName);
1228
1223
  return { title, icon, url: target };
1229
1224
  }
@@ -1357,11 +1352,16 @@ const TaskStatusStepper = memo(({
1357
1352
  })))));
1358
1353
  })));
1359
1354
  });
1360
- const hasLinks = ({ entityRef, remoteUrl, links = [] }) => !!(entityRef || remoteUrl || links.length > 0);
1355
+ const hasLinks = ({
1356
+ entityRef,
1357
+ remoteUrl,
1358
+ links = []
1359
+ }) => !!(entityRef || remoteUrl || links.length > 0);
1361
1360
  const TaskPage = ({ loadingText }) => {
1362
1361
  const classes = useStyles();
1363
1362
  const navigate = useNavigate();
1364
- const rootLink = useRouteRef(rootRouteRef);
1363
+ const rootPath = useRouteRef(rootRouteRef);
1364
+ const templateRoute = useRouteRef(selectedTemplateRouteRef);
1365
1365
  const [userSelectedStepId, setUserSelectedStepId] = useState(void 0);
1366
1366
  const [lastActiveStepId, setLastActiveStepId] = useState(void 0);
1367
1367
  const { taskId } = useParams();
@@ -1400,16 +1400,16 @@ const TaskPage = ({ loadingText }) => {
1400
1400
  const taskNotFound = taskStream.completed === true && taskStream.loading === false && !taskStream.task;
1401
1401
  const { output } = taskStream;
1402
1402
  const handleStartOver = () => {
1403
- var _a, _b;
1404
- if (!taskStream.task || !((_b = (_a = taskStream.task) == null ? void 0 : _a.spec.metadata) == null ? void 0 : _b.name)) {
1405
- navigate(generatePath(rootLink()));
1403
+ var _a, _b, _c;
1404
+ if (!taskStream.task || !((_b = (_a = taskStream.task) == null ? void 0 : _a.spec.templateInfo) == null ? void 0 : _b.entityRef)) {
1405
+ navigate(rootPath());
1406
+ return;
1406
1407
  }
1407
1408
  const formData = taskStream.task.spec.apiVersion === "backstage.io/v1beta2" ? taskStream.task.spec.values : taskStream.task.spec.parameters;
1408
- navigate(generatePath(`${rootLink()}/templates/:templateName?${qs.stringify({
1409
+ const { name } = parseEntityRef((_c = taskStream.task.spec.templateInfo) == null ? void 0 : _c.entityRef);
1410
+ navigate(`${templateRoute({ templateName: name })}?${qs.stringify({
1409
1411
  formData: JSON.stringify(formData)
1410
- })}`, {
1411
- templateName: taskStream.task.spec.metadata.name
1412
- }));
1412
+ })}`);
1413
1413
  };
1414
1414
  return /* @__PURE__ */ React.createElement(Page, {
1415
1415
  themeId: "home"
@@ -1451,5 +1451,5 @@ const TaskPage = ({ loadingText }) => {
1451
1451
  })))))));
1452
1452
  };
1453
1453
 
1454
- export { EntityPicker as E, FIELD_EXTENSION_WRAPPER_KEY as F, OwnerPicker as O, RepoUrlPicker as R, SecretsContext as S, TemplateTypePicker as T, EntityNamePicker as a, EntityTagsPicker as b, OwnedEntityPicker as c, registerComponentRouteRef as d, entityNamePickerValidation as e, TemplateList as f, rootRouteRef as g, FIELD_EXTENSION_KEY as h, SecretsContextProvider as i, TaskPage as j, ScaffolderClient as k, createScaffolderFieldExtension as l, ScaffolderFieldExtensions as m, EntityPickerFieldExtension as n, EntityNamePickerFieldExtension as o, EntityTagsPickerFieldExtension as p, OwnerPickerFieldExtension as q, repoPickerValidation as r, scaffolderApiRef as s, OwnedEntityPickerFieldExtension as t, RepoUrlPickerFieldExtension as u, ScaffolderPage as v, scaffolderPlugin as w, TextValuePicker as x, FavouriteTemplate as y, useTemplateSecrets as z };
1455
- //# sourceMappingURL=index-71578268.esm.js.map
1454
+ export { EntityPicker as E, FIELD_EXTENSION_WRAPPER_KEY as F, OwnerPicker as O, RepoUrlPicker as R, SecretsContext as S, TemplateTypePicker as T, EntityNamePicker as a, EntityTagsPicker as b, OwnedEntityPicker as c, registerComponentRouteRef as d, entityNamePickerValidation as e, TemplateList as f, scaffolderTaskRouteRef as g, rootRouteRef as h, FIELD_EXTENSION_KEY as i, SecretsContextProvider as j, TaskPage as k, ScaffolderClient as l, createScaffolderFieldExtension as m, ScaffolderFieldExtensions as n, EntityPickerFieldExtension as o, EntityNamePickerFieldExtension as p, EntityTagsPickerFieldExtension as q, repoPickerValidation as r, scaffolderApiRef as s, OwnerPickerFieldExtension as t, OwnedEntityPickerFieldExtension as u, RepoUrlPickerFieldExtension as v, ScaffolderPage as w, scaffolderPlugin as x, useTemplateSecrets as y };
1455
+ //# sourceMappingURL=index-da137e20.esm.js.map