@backstage/plugin-scaffolder 0.12.1-next.1 → 0.12.3

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