@backstage/plugin-scaffolder 0.12.0 → 0.12.2-next.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  # @backstage/plugin-scaffolder
2
2
 
3
+ ## 0.12.2-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 33e139e652: Adds a loading bar to the scaffolder task page if the task is still loading. This can happen if it takes a while for a task worker to pick up a task.
8
+ - 6458be3307: Encode the `formData` in the `queryString` using `JSON.stringify` to keep the types in the decoded value
9
+ - 319f4b79a2: The ScaffolderPage can be passed an optional `TaskPageComponent` with a `loadingText` string. It will replace the Loading text in the scaffolder task page.
10
+ - Updated dependencies
11
+ - @backstage/core-components@0.8.8-next.0
12
+ - @backstage/plugin-catalog-react@0.6.14-next.0
13
+ - @backstage/integration-react@0.1.21-next.0
14
+
15
+ ## 0.12.1
16
+
17
+ ### Patch Changes
18
+
19
+ - ba59832aed: Permission the Register Existing Component button
20
+ - cee44ad289: Added the ability to collect users `oauth` token from the `RepoUrlPicker` for use in the template manifest
21
+ - a681cb9c2f: Make linkTarget configurable for MarkdownContent component
22
+ - Updated dependencies
23
+ - @backstage/core-components@0.8.7
24
+ - @backstage/plugin-catalog-react@0.6.13
25
+ - @backstage/plugin-catalog-common@0.1.2
26
+ - @backstage/integration-react@0.1.20
27
+
28
+ ## 0.12.1-next.1
29
+
30
+ ### Patch Changes
31
+
32
+ - ba59832aed: Permission the Register Existing Component button
33
+ - Updated dependencies
34
+ - @backstage/core-components@0.8.7-next.1
35
+ - @backstage/plugin-catalog-react@0.6.13-next.1
36
+ - @backstage/plugin-catalog-common@0.1.2-next.0
37
+
38
+ ## 0.12.1-next.0
39
+
40
+ ### Patch Changes
41
+
42
+ - a681cb9c2f: Make linkTarget configurable for MarkdownContent component
43
+ - Updated dependencies
44
+ - @backstage/core-components@0.8.7-next.0
45
+ - @backstage/integration-react@0.1.20-next.0
46
+ - @backstage/plugin-catalog-react@0.6.13-next.0
47
+
3
48
  ## 0.12.0
4
49
 
5
50
  ### Minor Changes
@@ -1,40 +1,30 @@
1
- import React, { useState, useCallback, useEffect, memo, useMemo } from 'react';
2
- import { useNavigate, Navigate, generatePath, useParams as useParams$1, useOutlet, Routes, Route } from 'react-router';
3
- import { Page, Header, Lifecycle, Content, ContentHeader, CreateButton, SupportButton, MarkdownContent, StructuredMetadataTable, InfoCard, Link, ErrorPage, LogViewer, Progress } from '@backstage/core-components';
4
- import { useRouteRef, useApi, errorApiRef, useApiHolder, useApp, useElementFilter } from '@backstage/core-plugin-api';
5
- import { EntityListProvider, EntitySearchBar, EntityKindPicker, UserListPicker, EntityTagPicker, entityRouteRef } from '@backstage/plugin-catalog-react';
6
- import { makeStyles, Stepper, Step, StepLabel, Typography, StepContent, Button, Paper, Box, LinearProgress, Grid, StepButton, CircularProgress, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from '@material-ui/core';
7
- import { E as EntityPicker, a as EntityNamePicker, e as entityNamePickerValidation, b as EntityTagsPicker, R as RepoUrlPicker, r as repoPickerValidation, O as OwnerPicker, c as OwnedEntityPicker, d as registerComponentRouteRef, T as TemplateTypePicker, f as TemplateList, s as scaffolderApiRef, g as rootRouteRef, F as FIELD_EXTENSION_WRAPPER_KEY, h as FIELD_EXTENSION_KEY } from './index-8b5d21c1.esm.js';
1
+ import React, { useState, useContext, useCallback } from 'react';
2
+ import { useNavigate, Navigate, generatePath, useOutlet, Routes, Route } from 'react-router';
3
+ import { Page, Header, Lifecycle, Content, ContentHeader, CreateButton, SupportButton, MarkdownContent, StructuredMetadataTable, InfoCard, Progress, ErrorPage } from '@backstage/core-components';
4
+ import { useRouteRef, useApi, errorApiRef, useApiHolder, useElementFilter } from '@backstage/core-plugin-api';
5
+ import { EntityListProvider, EntitySearchBar, EntityKindPicker, UserListPicker, EntityTagPicker } from '@backstage/plugin-catalog-react';
6
+ import { makeStyles, Stepper, Step, StepLabel, Typography, StepContent, Button, Paper, Box, LinearProgress, TableContainer, Table, TableHead, TableRow, TableCell, TableBody } from '@material-ui/core';
7
+ import { E as EntityPicker, a as EntityNamePicker, e as entityNamePickerValidation, b as EntityTagsPicker, R as RepoUrlPicker, r as repoPickerValidation, O as OwnerPicker, c as OwnedEntityPicker, d as registerComponentRouteRef, T as TemplateTypePicker, f as TemplateList, S as SecretsContext, s as scaffolderApiRef, g as rootRouteRef, F as FIELD_EXTENSION_WRAPPER_KEY, h as FIELD_EXTENSION_KEY, i as SecretsContextProvider, j as TaskPage } from './index-71578268.esm.js';
8
+ import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common';
9
+ import { usePermission } from '@backstage/plugin-permission-react';
8
10
  import qs from 'qs';
9
11
  import { useParams } from 'react-router-dom';
10
12
  import useAsync from 'react-use/lib/useAsync';
11
13
  import { withTheme } from '@rjsf/core';
12
14
  import { Theme } from '@rjsf/material-ui';
13
- import Grid$1 from '@material-ui/core/Grid';
14
- import Step$1 from '@material-ui/core/Step';
15
- import StepLabel$1 from '@material-ui/core/StepLabel';
16
- import Stepper$1 from '@material-ui/core/Stepper';
17
- import { makeStyles as makeStyles$1, createStyles } from '@material-ui/core/styles';
18
- import Typography$1 from '@material-ui/core/Typography';
19
- import Cancel from '@material-ui/icons/Cancel';
20
- import Check from '@material-ui/icons/Check';
21
- import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
22
15
  import classNames from 'classnames';
23
- import { DateTime, Interval } from 'luxon';
24
- import useInterval from 'react-use/lib/useInterval';
25
- import { useImmerReducer } from 'use-immer';
26
- import { parseEntityName } from '@backstage/catalog-model';
27
- import LanguageIcon from '@material-ui/icons/Language';
28
16
  import '@backstage/errors';
29
17
  import 'zen-observable';
30
18
  import '@material-ui/core/FormControl';
31
19
  import '@material-ui/lab/Autocomplete';
20
+ import '@backstage/catalog-model';
32
21
  import 'react-use/lib/useEffectOnce';
33
22
  import '@material-ui/lab';
34
23
  import '@backstage/integration-react';
35
24
  import '@material-ui/core/FormHelperText';
36
25
  import '@material-ui/core/Input';
37
26
  import '@material-ui/core/InputLabel';
27
+ import 'react-use/lib/useDebounce';
38
28
  import '@material-ui/icons/Star';
39
29
  import '@material-ui/icons/StarBorder';
40
30
  import '@material-ui/icons/Warning';
@@ -42,6 +32,19 @@ import 'lodash/capitalize';
42
32
  import '@material-ui/icons/CheckBox';
43
33
  import '@material-ui/icons/CheckBoxOutlineBlank';
44
34
  import '@material-ui/icons/ExpandMore';
35
+ import '@material-ui/core/Grid';
36
+ import '@material-ui/core/Step';
37
+ import '@material-ui/core/StepLabel';
38
+ import '@material-ui/core/Stepper';
39
+ import '@material-ui/core/styles';
40
+ import '@material-ui/core/Typography';
41
+ import '@material-ui/icons/Cancel';
42
+ import '@material-ui/icons/Check';
43
+ import '@material-ui/icons/FiberManualRecord';
44
+ import 'luxon';
45
+ import 'react-use/lib/useInterval';
46
+ import 'use-immer';
47
+ import '@material-ui/icons/Language';
45
48
 
46
49
  const DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS = [
47
50
  {
@@ -72,7 +75,7 @@ const DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS = [
72
75
  }
73
76
  ];
74
77
 
75
- const useStyles$3 = makeStyles((theme) => ({
78
+ const useStyles$1 = makeStyles((theme) => ({
76
79
  contentWrapper: {
77
80
  display: "grid",
78
81
  gridTemplateAreas: "'filters' 'grid'",
@@ -84,7 +87,7 @@ const ScaffolderPageContents = ({
84
87
  TemplateCardComponent,
85
88
  groups
86
89
  }) => {
87
- const styles = useStyles$3();
90
+ const styles = useStyles$1();
88
91
  const registerComponentLink = useRouteRef(registerComponentRouteRef);
89
92
  const otherTemplatesGroup = {
90
93
  title: groups ? "Other Templates" : "Templates",
@@ -93,6 +96,7 @@ const ScaffolderPageContents = ({
93
96
  return !filtered.some((result) => result === true);
94
97
  }
95
98
  };
99
+ const { allowed } = usePermission(catalogEntityCreatePermission);
96
100
  return /* @__PURE__ */ React.createElement(Page, {
97
101
  themeId: "home"
98
102
  }, /* @__PURE__ */ React.createElement(Header, {
@@ -103,7 +107,7 @@ const ScaffolderPageContents = ({
103
107
  subtitle: "Create new software components using standard templates"
104
108
  }), /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, {
105
109
  title: "Available Templates"
106
- }, /* @__PURE__ */ React.createElement(CreateButton, {
110
+ }, allowed && /* @__PURE__ */ React.createElement(CreateButton, {
107
111
  title: "Register Existing Component",
108
112
  to: registerComponentLink && registerComponentLink()
109
113
  }), /* @__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("div", {
@@ -212,7 +216,8 @@ function transformSchemaToProps(inputSchema) {
212
216
  }
213
217
 
214
218
  const DescriptionField = ({ description }) => description && /* @__PURE__ */ React.createElement(MarkdownContent, {
215
- content: description
219
+ content: description,
220
+ linkTarget: "_blank"
216
221
  });
217
222
 
218
223
  var fieldOverrides = /*#__PURE__*/Object.freeze({
@@ -398,6 +403,7 @@ const TemplatePage = ({
398
403
  customFieldExtensions = []
399
404
  }) => {
400
405
  const apiHolder = useApiHolder();
406
+ const secretsContext = useContext(SecretsContext);
401
407
  const errorApi = useApi(errorApiRef);
402
408
  const scaffolderApi = useApi(scaffolderApiRef);
403
409
  const { templateName } = useParams();
@@ -409,13 +415,17 @@ const TemplatePage = ({
409
415
  const query = qs.parse(window.location.search, {
410
416
  ignoreQueryPrefix: true
411
417
  });
412
- return (_a = query.formData) != null ? _a : {};
418
+ try {
419
+ return JSON.parse(query.formData);
420
+ } catch (e) {
421
+ return (_a = query.formData) != null ? _a : {};
422
+ }
413
423
  });
414
424
  const handleFormReset = () => setFormState({});
415
425
  const handleChange = useCallback((e) => setFormState(e.formData), [setFormState]);
416
426
  const handleCreate = async () => {
417
427
  var _a;
418
- const id = await scaffolderApi.scaffold(templateName, formState);
428
+ const id = await scaffolderApi.scaffold(templateName, formState, secretsContext == null ? void 0 : secretsContext.secrets);
419
429
  const formParams = qs.stringify({ formData: formState }, { addQueryPrefix: true });
420
430
  const newUrl = `${window.location.pathname}${formParams}`;
421
431
  (_a = window.history) == null ? void 0 : _a.replaceState(null, document.title, newUrl);
@@ -464,406 +474,6 @@ const TemplatePage = ({
464
474
  }))));
465
475
  };
466
476
 
467
- function reducer(draft, action) {
468
- var _a, _b, _c;
469
- switch (action.type) {
470
- case "INIT": {
471
- draft.steps = action.data.spec.steps.reduce((current, next) => {
472
- current[next.id] = { status: "open", id: next.id };
473
- return current;
474
- }, {});
475
- draft.stepLogs = action.data.spec.steps.reduce((current, next) => {
476
- current[next.id] = [];
477
- return current;
478
- }, {});
479
- draft.loading = false;
480
- draft.error = void 0;
481
- draft.completed = false;
482
- draft.task = action.data;
483
- return;
484
- }
485
- case "LOGS": {
486
- const entries = action.data;
487
- for (const entry of entries) {
488
- const logLine = `${entry.createdAt} ${entry.body.message}`;
489
- if (!entry.body.stepId || !((_a = draft.steps) == null ? void 0 : _a[entry.body.stepId])) {
490
- continue;
491
- }
492
- const currentStepLog = (_b = draft.stepLogs) == null ? void 0 : _b[entry.body.stepId];
493
- const currentStep = (_c = draft.steps) == null ? void 0 : _c[entry.body.stepId];
494
- if (entry.body.status && entry.body.status !== currentStep.status) {
495
- currentStep.status = entry.body.status;
496
- if (currentStep.status === "processing") {
497
- currentStep.startedAt = entry.createdAt;
498
- }
499
- if (["cancelled", "failed", "completed"].includes(currentStep.status)) {
500
- currentStep.endedAt = entry.createdAt;
501
- }
502
- }
503
- currentStepLog == null ? void 0 : currentStepLog.push(logLine);
504
- }
505
- return;
506
- }
507
- case "COMPLETED": {
508
- draft.completed = true;
509
- draft.output = action.data.body.output;
510
- return;
511
- }
512
- case "ERROR": {
513
- draft.error = action.data;
514
- draft.loading = false;
515
- draft.completed = true;
516
- return;
517
- }
518
- default:
519
- return;
520
- }
521
- }
522
- const useTaskEventStream = (taskId) => {
523
- const scaffolderApi = useApi(scaffolderApiRef);
524
- const [state, dispatch] = useImmerReducer(reducer, {
525
- loading: true,
526
- completed: false,
527
- stepLogs: {},
528
- steps: {}
529
- });
530
- useEffect(() => {
531
- let didCancel = false;
532
- let subscription;
533
- let logPusher;
534
- scaffolderApi.getTask(taskId).then((task) => {
535
- if (didCancel) {
536
- return;
537
- }
538
- dispatch({ type: "INIT", data: task });
539
- const observable = scaffolderApi.streamLogs({ taskId });
540
- const collectedLogEvents = new Array();
541
- function emitLogs() {
542
- if (collectedLogEvents.length) {
543
- const logs = collectedLogEvents.splice(0, collectedLogEvents.length);
544
- dispatch({ type: "LOGS", data: logs });
545
- }
546
- }
547
- logPusher = setInterval(emitLogs, 500);
548
- subscription = observable.subscribe({
549
- next: (event) => {
550
- switch (event.type) {
551
- case "log":
552
- return collectedLogEvents.push(event);
553
- case "completion":
554
- emitLogs();
555
- dispatch({ type: "COMPLETED", data: event });
556
- return void 0;
557
- default:
558
- throw new Error(`Unhandled event type ${event.type} in observer`);
559
- }
560
- },
561
- error: (error) => {
562
- emitLogs();
563
- dispatch({ type: "ERROR", data: error });
564
- }
565
- });
566
- }, (error) => {
567
- if (!didCancel) {
568
- dispatch({ type: "ERROR", data: error });
569
- }
570
- });
571
- return () => {
572
- didCancel = true;
573
- if (subscription) {
574
- subscription.unsubscribe();
575
- }
576
- if (logPusher) {
577
- clearInterval(logPusher);
578
- }
579
- };
580
- }, [scaffolderApi, dispatch, taskId]);
581
- return state;
582
- };
583
-
584
- const useStyles$2 = makeStyles({
585
- svgIcon: {
586
- display: "inline-block",
587
- "& svg": {
588
- display: "inline-block",
589
- fontSize: "inherit",
590
- verticalAlign: "baseline"
591
- }
592
- }
593
- });
594
- const IconLink = (props) => {
595
- const { href, text, Icon, ...linkProps } = props;
596
- const classes = useStyles$2();
597
- return /* @__PURE__ */ React.createElement(Grid, {
598
- container: true,
599
- direction: "row",
600
- spacing: 1
601
- }, /* @__PURE__ */ React.createElement(Grid, {
602
- item: true
603
- }, /* @__PURE__ */ React.createElement(Typography, {
604
- component: "div",
605
- className: classes.svgIcon
606
- }, Icon ? /* @__PURE__ */ React.createElement(Icon, null) : /* @__PURE__ */ React.createElement(LanguageIcon, null))), /* @__PURE__ */ React.createElement(Grid, {
607
- item: true
608
- }, /* @__PURE__ */ React.createElement(Link, {
609
- to: href,
610
- ...linkProps
611
- }, text || href)));
612
- };
613
-
614
- const TaskPageLinks = ({ output }) => {
615
- const { entityRef: entityRefOutput, remoteUrl } = output;
616
- let { links = [] } = output;
617
- const app = useApp();
618
- const entityRoute = useRouteRef(entityRouteRef);
619
- const iconResolver = (key) => {
620
- var _a;
621
- return key ? (_a = app.getSystemIcon(key)) != null ? _a : LanguageIcon : LanguageIcon;
622
- };
623
- if (remoteUrl) {
624
- links = [{ url: remoteUrl, title: "Repo" }, ...links];
625
- }
626
- if (entityRefOutput) {
627
- links = [
628
- {
629
- entityRef: entityRefOutput,
630
- title: "Open in catalog",
631
- icon: "catalog"
632
- },
633
- ...links
634
- ];
635
- }
636
- return /* @__PURE__ */ React.createElement(Box, {
637
- px: 3,
638
- pb: 3
639
- }, links.filter(({ url, entityRef }) => url || entityRef).map(({ url, entityRef, title, icon }) => {
640
- if (entityRef) {
641
- const entityName = parseEntityName(entityRef);
642
- const target = entityRoute(entityName);
643
- return { title, icon, url: target };
644
- }
645
- return { title, icon, url };
646
- }).map(({ url, title, icon }, i) => /* @__PURE__ */ React.createElement(IconLink, {
647
- key: `output-link-${i}`,
648
- href: url,
649
- text: title != null ? title : url,
650
- Icon: iconResolver(icon),
651
- target: "_blank"
652
- })));
653
- };
654
-
655
- const humanizeDuration = require("humanize-duration");
656
- const useStyles$1 = makeStyles$1((theme) => createStyles({
657
- root: {
658
- width: "100%"
659
- },
660
- button: {
661
- marginBottom: theme.spacing(2),
662
- marginLeft: theme.spacing(2)
663
- },
664
- actionsContainer: {
665
- marginBottom: theme.spacing(2)
666
- },
667
- resetContainer: {
668
- padding: theme.spacing(3)
669
- },
670
- labelWrapper: {
671
- display: "flex",
672
- flex: 1,
673
- flexDirection: "row",
674
- justifyContent: "space-between"
675
- },
676
- stepWrapper: {
677
- width: "100%"
678
- }
679
- }));
680
- const StepTimeTicker = ({ step }) => {
681
- const [time, setTime] = useState("");
682
- useInterval(() => {
683
- if (!step.startedAt) {
684
- setTime("");
685
- return;
686
- }
687
- const end = step.endedAt ? DateTime.fromISO(step.endedAt) : DateTime.local();
688
- const startedAt = DateTime.fromISO(step.startedAt);
689
- const formatted = Interval.fromDateTimes(startedAt, end).toDuration().valueOf();
690
- setTime(humanizeDuration(formatted, { round: true }));
691
- }, 1e3);
692
- return /* @__PURE__ */ React.createElement(Typography$1, {
693
- variant: "caption"
694
- }, time);
695
- };
696
- const useStepIconStyles = makeStyles$1((theme) => createStyles({
697
- root: {
698
- color: theme.palette.text.disabled,
699
- display: "flex",
700
- height: 22,
701
- alignItems: "center"
702
- },
703
- completed: {
704
- color: theme.palette.status.ok
705
- },
706
- error: {
707
- color: theme.palette.status.error
708
- }
709
- }));
710
- function TaskStepIconComponent(props) {
711
- const classes = useStepIconStyles();
712
- const { active, completed, error } = props;
713
- const getMiddle = () => {
714
- if (active) {
715
- return /* @__PURE__ */ React.createElement(CircularProgress, {
716
- size: "24px"
717
- });
718
- }
719
- if (completed) {
720
- return /* @__PURE__ */ React.createElement(Check, null);
721
- }
722
- if (error) {
723
- return /* @__PURE__ */ React.createElement(Cancel, null);
724
- }
725
- return /* @__PURE__ */ React.createElement(FiberManualRecordIcon, null);
726
- };
727
- return /* @__PURE__ */ React.createElement("div", {
728
- className: classNames(classes.root, {
729
- [classes.completed]: completed,
730
- [classes.error]: error
731
- })
732
- }, getMiddle());
733
- }
734
- const TaskStatusStepper = memo(({
735
- steps,
736
- currentStepId,
737
- onUserStepChange
738
- }) => {
739
- const classes = useStyles$1();
740
- return /* @__PURE__ */ React.createElement("div", {
741
- className: classes.root
742
- }, /* @__PURE__ */ React.createElement(Stepper$1, {
743
- activeStep: steps.findIndex((s) => s.id === currentStepId),
744
- orientation: "vertical",
745
- nonLinear: true
746
- }, steps.map((step, index) => {
747
- const isCompleted = step.status === "completed";
748
- const isFailed = step.status === "failed";
749
- const isActive = step.status === "processing";
750
- const isSkipped = step.status === "skipped";
751
- return /* @__PURE__ */ React.createElement(Step$1, {
752
- key: String(index),
753
- expanded: true
754
- }, /* @__PURE__ */ React.createElement(StepButton, {
755
- onClick: () => onUserStepChange(step.id)
756
- }, /* @__PURE__ */ React.createElement(StepLabel$1, {
757
- StepIconProps: {
758
- completed: isCompleted,
759
- error: isFailed,
760
- active: isActive
761
- },
762
- StepIconComponent: TaskStepIconComponent,
763
- className: classes.stepWrapper
764
- }, /* @__PURE__ */ React.createElement("div", {
765
- className: classes.labelWrapper
766
- }, /* @__PURE__ */ React.createElement(Typography$1, {
767
- variant: "subtitle2"
768
- }, step.name), isSkipped ? /* @__PURE__ */ React.createElement(Typography$1, {
769
- variant: "caption"
770
- }, "Skipped") : /* @__PURE__ */ React.createElement(StepTimeTicker, {
771
- step
772
- })))));
773
- })));
774
- });
775
- const hasLinks = ({ entityRef, remoteUrl, links = [] }) => !!(entityRef || remoteUrl || links.length > 0);
776
- const TaskPage = () => {
777
- const classes = useStyles$1();
778
- const navigate = useNavigate();
779
- const rootLink = useRouteRef(rootRouteRef);
780
- const [userSelectedStepId, setUserSelectedStepId] = useState(void 0);
781
- const [lastActiveStepId, setLastActiveStepId] = useState(void 0);
782
- const { taskId } = useParams$1();
783
- const taskStream = useTaskEventStream(taskId);
784
- const completed = taskStream.completed;
785
- const steps = useMemo(() => {
786
- var _a, _b;
787
- return (_b = (_a = taskStream.task) == null ? void 0 : _a.spec.steps.map((step) => {
788
- var _a2;
789
- return {
790
- ...step,
791
- ...(_a2 = taskStream == null ? void 0 : taskStream.steps) == null ? void 0 : _a2[step.id]
792
- };
793
- })) != null ? _b : [];
794
- }, [taskStream]);
795
- useEffect(() => {
796
- var _a;
797
- const mostRecentFailedOrActiveStep = steps.find((step) => ["failed", "processing"].includes(step.status));
798
- if (completed && !mostRecentFailedOrActiveStep) {
799
- setLastActiveStepId((_a = steps[steps.length - 1]) == null ? void 0 : _a.id);
800
- return;
801
- }
802
- setLastActiveStepId(mostRecentFailedOrActiveStep == null ? void 0 : mostRecentFailedOrActiveStep.id);
803
- }, [steps, completed]);
804
- const currentStepId = userSelectedStepId != null ? userSelectedStepId : lastActiveStepId;
805
- const logAsString = useMemo(() => {
806
- if (!currentStepId) {
807
- return "Loading...";
808
- }
809
- const log = taskStream.stepLogs[currentStepId];
810
- if (!(log == null ? void 0 : log.length)) {
811
- return "Waiting for logs...";
812
- }
813
- return log.join("\n");
814
- }, [taskStream.stepLogs, currentStepId]);
815
- const taskNotFound = taskStream.completed === true && taskStream.loading === false && !taskStream.task;
816
- const { output } = taskStream;
817
- const handleStartOver = () => {
818
- var _a, _b;
819
- if (!taskStream.task || !((_b = (_a = taskStream.task) == null ? void 0 : _a.spec.metadata) == null ? void 0 : _b.name)) {
820
- navigate(generatePath(rootLink()));
821
- }
822
- const formData = taskStream.task.spec.apiVersion === "backstage.io/v1beta2" ? taskStream.task.spec.values : taskStream.task.spec.parameters;
823
- navigate(generatePath(`${rootLink()}/templates/:templateName?${qs.stringify({ formData })}`, {
824
- templateName: taskStream.task.spec.metadata.name
825
- }));
826
- };
827
- return /* @__PURE__ */ React.createElement(Page, {
828
- themeId: "home"
829
- }, /* @__PURE__ */ React.createElement(Header, {
830
- pageTitleOverride: `Task ${taskId}`,
831
- title: /* @__PURE__ */ React.createElement(React.Fragment, null, "Task Activity ", /* @__PURE__ */ React.createElement(Lifecycle, {
832
- alpha: true,
833
- shorthand: true
834
- })),
835
- subtitle: `Activity for task: ${taskId}`
836
- }), /* @__PURE__ */ React.createElement(Content, null, taskNotFound ? /* @__PURE__ */ React.createElement(ErrorPage, {
837
- status: "404",
838
- statusMessage: "Task not found",
839
- additionalInfo: "No task found with this ID"
840
- }) : /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(Grid$1, {
841
- container: true
842
- }, /* @__PURE__ */ React.createElement(Grid$1, {
843
- item: true,
844
- xs: 3
845
- }, /* @__PURE__ */ React.createElement(Paper, null, /* @__PURE__ */ React.createElement(TaskStatusStepper, {
846
- steps,
847
- currentStepId,
848
- onUserStepChange: setUserSelectedStepId
849
- }), output && hasLinks(output) && /* @__PURE__ */ React.createElement(TaskPageLinks, {
850
- output
851
- }), /* @__PURE__ */ React.createElement(Button, {
852
- className: classes.button,
853
- onClick: handleStartOver,
854
- disabled: !completed,
855
- variant: "contained",
856
- color: "primary"
857
- }, "Start Over"))), /* @__PURE__ */ React.createElement(Grid$1, {
858
- item: true,
859
- xs: 9
860
- }, /* @__PURE__ */ React.createElement("div", {
861
- style: { height: "80vh" }
862
- }, /* @__PURE__ */ React.createElement(LogViewer, {
863
- text: logAsString
864
- })))))));
865
- };
866
-
867
477
  const useStyles = makeStyles((theme) => ({
868
478
  code: {
869
479
  fontFamily: "Menlo, monospace",
@@ -972,8 +582,13 @@ const ActionsPage = () => {
972
582
  }), /* @__PURE__ */ React.createElement(Content, null, items));
973
583
  };
974
584
 
975
- const Router = ({ TemplateCardComponent, groups }) => {
585
+ const Router = ({
586
+ TemplateCardComponent,
587
+ TaskPageComponent,
588
+ groups
589
+ }) => {
976
590
  const outlet = useOutlet();
591
+ const TaskPageElement = TaskPageComponent || TaskPage;
977
592
  const customFieldExtensions = useElementFilter(outlet, (elements) => elements.selectByComponentData({
978
593
  key: FIELD_EXTENSION_WRAPPER_KEY
979
594
  }).findComponentData({
@@ -991,12 +606,12 @@ const Router = ({ TemplateCardComponent, groups }) => {
991
606
  })
992
607
  }), /* @__PURE__ */ React.createElement(Route, {
993
608
  path: "/templates/:templateName",
994
- element: /* @__PURE__ */ React.createElement(TemplatePage, {
609
+ element: /* @__PURE__ */ React.createElement(SecretsContextProvider, null, /* @__PURE__ */ React.createElement(TemplatePage, {
995
610
  customFieldExtensions: fieldExtensions
996
- })
611
+ }))
997
612
  }), /* @__PURE__ */ React.createElement(Route, {
998
613
  path: "/tasks/:taskId",
999
- element: /* @__PURE__ */ React.createElement(TaskPage, null)
614
+ element: /* @__PURE__ */ React.createElement(TaskPageElement, null)
1000
615
  }), /* @__PURE__ */ React.createElement(Route, {
1001
616
  path: "/actions",
1002
617
  element: /* @__PURE__ */ React.createElement(ActionsPage, null)
@@ -1004,4 +619,4 @@ const Router = ({ TemplateCardComponent, groups }) => {
1004
619
  };
1005
620
 
1006
621
  export { Router };
1007
- //# sourceMappingURL=Router-5ca674eb.esm.js.map
622
+ //# sourceMappingURL=Router-5df57206.esm.js.map