@backstage/plugin-scaffolder-react 1.1.1-next.0 → 1.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @backstage/plugin-scaffolder-react
2
2
 
3
+ ## 1.2.0-next.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 65454876fb2: Minor API report tweaks
8
+ - 3c96e77b513: Make scaffolder adhere to page themes by using page `fontColor` consistently. If your theme overwrites template list or card headers, review those styles.
9
+ - d9893263ba9: scaffolder/next: Fix for steps without properties
10
+ - Updated dependencies
11
+ - @backstage/core-components@0.12.5-next.2
12
+ - @backstage/plugin-catalog-react@1.4.0-next.2
13
+ - @backstage/core-plugin-api@1.5.0-next.2
14
+
15
+ ## 1.2.0-next.1
16
+
17
+ ### Minor Changes
18
+
19
+ - 8f4d13f21cf: Move `useTaskStream`, `TaskBorder`, `TaskLogStream` and `TaskSteps` into `scaffolder-react`.
20
+
21
+ ### Patch Changes
22
+
23
+ - 44941fc97eb: scaffolder/next: Move the `uiSchema` to its own property in the validation `context` to align with component development and access of `ui:options`
24
+ - Updated dependencies
25
+ - @backstage/core-components@0.12.5-next.1
26
+ - @backstage/errors@1.1.5-next.0
27
+ - @backstage/catalog-client@1.4.0-next.1
28
+ - @backstage/core-plugin-api@1.4.1-next.1
29
+ - @backstage/theme@0.2.18-next.0
30
+ - @backstage/plugin-catalog-react@1.4.0-next.1
31
+ - @backstage/catalog-model@1.2.1-next.1
32
+ - @backstage/types@1.0.2
33
+ - @backstage/version-bridge@1.0.3
34
+ - @backstage/plugin-scaffolder-common@1.2.6-next.1
35
+
3
36
  ## 1.1.1-next.0
4
37
 
5
38
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-scaffolder-react",
3
- "version": "1.1.1-next.0",
3
+ "version": "1.2.0-next.2",
4
4
  "main": "../dist/alpha.esm.js",
5
5
  "module": "../dist/alpha.esm.js",
6
6
  "types": "../dist/alpha.d.ts"
package/dist/alpha.d.ts CHANGED
@@ -5,10 +5,10 @@ import react__default, { PropsWithChildren, ReactNode } from 'react';
5
5
  import { ApiHolder, Extension, IconComponent } from '@backstage/core-plugin-api';
6
6
  import * as _rjsf_utils from '@rjsf/utils';
7
7
  import { FieldProps, UiSchema, UIOptionsType, FieldValidation } from '@rjsf/utils';
8
- import { CustomFieldExtensionSchema, FieldExtensionComponent, TemplateParameterSchema, LayoutOptions, ScaffolderTaskOutput } from '@backstage/plugin-scaffolder-react';
8
+ import { CustomFieldExtensionSchema, FieldExtensionComponent, TemplateParameterSchema, LayoutOptions, ScaffolderTaskOutput, ScaffolderStep } from '@backstage/plugin-scaffolder-react';
9
9
  import * as _rjsf_core_v5 from '@rjsf/core-v5';
10
10
  import { FormProps as FormProps$1 } from '@rjsf/core-v5';
11
- import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
11
+ import { TemplateEntityV1beta3, TaskStep } from '@backstage/plugin-scaffolder-common';
12
12
 
13
13
  /**
14
14
  * Type for Field Extension Props for RJSF v5
@@ -16,19 +16,26 @@ import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common';
16
16
  * @alpha
17
17
  */
18
18
  interface NextFieldExtensionComponentProps<TFieldReturnValue, TUiOptions = {}> extends PropsWithChildren<FieldProps<TFieldReturnValue>> {
19
- uiSchema?: UiSchema<TFieldReturnValue> & {
20
- 'ui:options'?: TUiOptions & UIOptionsType;
21
- };
19
+ uiSchema?: NextFieldExtensionUiSchema<TFieldReturnValue, TUiOptions>;
20
+ }
21
+ /**
22
+ * Type for Field Extension UiSchema
23
+ *
24
+ * @alpha
25
+ */
26
+ interface NextFieldExtensionUiSchema<TFieldReturnValue, TUiOptions> extends UiSchema<TFieldReturnValue> {
27
+ 'ui:options'?: TUiOptions & UIOptionsType;
22
28
  }
23
29
  /**
24
30
  * Field validation type for Custom Field Extensions.
25
31
  *
26
32
  * @alpha
27
33
  */
28
- declare type NextCustomFieldValidator<TFieldReturnValue> = (data: TFieldReturnValue, field: FieldValidation, context: {
34
+ declare type NextCustomFieldValidator<TFieldReturnValue, TUiOptions = unknown> = (data: TFieldReturnValue, field: FieldValidation, context: {
29
35
  apiHolder: ApiHolder;
30
36
  formData: JsonObject;
31
37
  schema: JsonObject;
38
+ uiSchema?: NextFieldExtensionUiSchema<TFieldReturnValue, TUiOptions>;
32
39
  }) => void | Promise<void>;
33
40
  /**
34
41
  * Type for the Custom Field Extension with the
@@ -36,10 +43,10 @@ declare type NextCustomFieldValidator<TFieldReturnValue> = (data: TFieldReturnVa
36
43
  *
37
44
  * @alpha
38
45
  */
39
- declare type NextFieldExtensionOptions<TFieldReturnValue = unknown, TInputProps = unknown> = {
46
+ declare type NextFieldExtensionOptions<TFieldReturnValue = unknown, TUiOptions = unknown> = {
40
47
  name: string;
41
- component: (props: NextFieldExtensionComponentProps<TFieldReturnValue, TInputProps>) => JSX.Element | null;
42
- validation?: NextCustomFieldValidator<TFieldReturnValue>;
48
+ component: (props: NextFieldExtensionComponentProps<TFieldReturnValue, TUiOptions>) => JSX.Element | null;
49
+ validation?: NextCustomFieldValidator<TFieldReturnValue, TUiOptions>;
43
50
  schema?: CustomFieldExtensionSchema;
44
51
  };
45
52
 
@@ -191,6 +198,35 @@ declare const DefaultTemplateOutputs: (props: {
191
198
  /** @alpha */
192
199
  declare const Form: react.ComponentType<_rjsf_core_v5.FormProps<any, _rjsf_utils.RJSFSchema, any>>;
193
200
 
201
+ /**
202
+ * Props for the TaskSteps component
203
+ *
204
+ * @alpha
205
+ */
206
+ interface TaskStepsProps {
207
+ steps: (TaskStep & ScaffolderStep)[];
208
+ activeStep?: number;
209
+ isComplete?: boolean;
210
+ isError?: boolean;
211
+ }
212
+ /**
213
+ * The visual stepper of the task event stream
214
+ *
215
+ * @alpha
216
+ */
217
+ declare const TaskSteps: (props: TaskStepsProps) => JSX.Element;
218
+
219
+ /**
220
+ * The text of the event stream
221
+ *
222
+ * @alpha
223
+ */
224
+ declare const TaskLogStream: (props: {
225
+ logs: {
226
+ [k: string]: string[];
227
+ };
228
+ }) => JSX.Element;
229
+
194
230
  /**
195
231
  * Takes a step from a Backstage Template Manifest and converts it to a JSON Schema and UI Schema for rjsf
196
232
  * @alpha
@@ -220,4 +256,4 @@ declare const useTemplateParameterSchema: (templateRef: string) => {
220
256
  error: Error | undefined;
221
257
  };
222
258
 
223
- export { DefaultTemplateOutputs, EmbeddableWorkflow, Form, FormProps, NextCustomFieldValidator, NextFieldExtensionComponentProps, NextFieldExtensionOptions, ParsedTemplateSchema, ReviewState, ReviewStateProps, Stepper, StepperProps, TemplateCard, TemplateCardProps, TemplateGroup, TemplateGroupProps, Workflow, WorkflowProps, createFieldValidation, createNextScaffolderFieldExtension, extractSchemaFromStep, useFormDataFromQuery, useTemplateParameterSchema, useTemplateSchema };
259
+ export { DefaultTemplateOutputs, EmbeddableWorkflow, Form, FormProps, NextCustomFieldValidator, NextFieldExtensionComponentProps, NextFieldExtensionOptions, NextFieldExtensionUiSchema, ParsedTemplateSchema, ReviewState, ReviewStateProps, Stepper, StepperProps, TaskLogStream, TaskSteps, TaskStepsProps, TemplateCard, TemplateCardProps, TemplateGroup, TemplateGroupProps, Workflow, WorkflowProps, createFieldValidation, createNextScaffolderFieldExtension, extractSchemaFromStep, useFormDataFromQuery, useTemplateParameterSchema, useTemplateSchema };
package/dist/alpha.esm.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { useApi, featureFlagsApiRef, createApiRef, useAnalytics, useApiHolder, useApp, errorApiRef, useRouteRef, attachComponentData } from '@backstage/core-plugin-api';
2
- import { makeStyles, Stepper as Stepper$1, Step, StepLabel, Button, useTheme, Card, CardContent, Grid, Box, Divider, Chip, CardActions, Typography, Paper } from '@material-ui/core';
2
+ import { makeStyles, Stepper as Stepper$1, Step, StepLabel, Button, useTheme, Card, CardContent, Grid, Box, Divider, Chip, CardActions, Typography, Paper, CircularProgress, LinearProgress, StepButton } from '@material-ui/core';
3
3
  import React, { useState, useMemo, useCallback, useEffect } from 'react';
4
4
  import { Draft07 } from 'json-schema-library';
5
- import { StructuredMetadataTable, MarkdownContent, ItemCardHeader, Link, UserIcon, Content, ItemCardGrid, ContentHeader, Progress, InfoCard } from '@backstage/core-components';
5
+ import { StructuredMetadataTable, MarkdownContent, ItemCardHeader, Link, UserIcon, Content, ItemCardGrid, ContentHeader, Progress, InfoCard, LogViewer } from '@backstage/core-components';
6
6
  import validator from '@rjsf/validator-ajv8';
7
7
  import qs from 'qs';
8
8
  import useAsync from 'react-use/lib/useAsync';
@@ -12,6 +12,16 @@ import { RELATION_OWNED_BY, stringifyEntityRef, parseEntityRef } from '@backstag
12
12
  import { FavoriteEntity, getEntityRelations, EntityRefLinks, entityRouteRef } from '@backstage/plugin-catalog-react';
13
13
  import LanguageIcon from '@material-ui/icons/Language';
14
14
  import WebIcon from '@material-ui/icons/Web';
15
+ import RemoveCircleOutline from '@material-ui/icons/RemoveCircleOutline';
16
+ import PanoramaFishEyeIcon from '@material-ui/icons/PanoramaFishEye';
17
+ import classNames from 'classnames';
18
+ import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline';
19
+ import ErrorOutline from '@material-ui/icons/ErrorOutline';
20
+ import useInterval from 'react-use/lib/useInterval';
21
+ import { DateTime, Interval } from 'luxon';
22
+ import humanizeDuration from 'humanize-duration';
23
+ import { useMountEffect } from '@react-hookz/web';
24
+ import { makeStyles as makeStyles$1 } from '@material-ui/core/styles';
15
25
 
16
26
  function isObject$1(value) {
17
27
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -127,9 +137,10 @@ function isObject(value) {
127
137
 
128
138
  const createAsyncValidators = (rootSchema, validators, context) => {
129
139
  async function validate(formData, pathPrefix = "#", current = formData) {
140
+ var _a, _b;
130
141
  const parsedSchema = new Draft07(rootSchema);
131
142
  const formValidation = {};
132
- const validateForm = async (validatorName, key, value, schema) => {
143
+ const validateForm = async (validatorName, key, value, schema, uiSchema) => {
133
144
  const validator = validators[validatorName];
134
145
  if (validator) {
135
146
  const fieldValidation = createFieldValidation();
@@ -137,7 +148,8 @@ const createAsyncValidators = (rootSchema, validators, context) => {
137
148
  await validator(value, fieldValidation, {
138
149
  ...context,
139
150
  formData,
140
- schema
151
+ schema,
152
+ uiSchema
141
153
  });
142
154
  } catch (ex) {
143
155
  fieldValidation.addError(ex.message);
@@ -148,23 +160,31 @@ const createAsyncValidators = (rootSchema, validators, context) => {
148
160
  for (const [key, value] of Object.entries(current)) {
149
161
  const path = `${pathPrefix}/${key}`;
150
162
  const definitionInSchema = parsedSchema.getSchema(path, formData);
151
- if (definitionInSchema && "ui:field" in definitionInSchema) {
152
- if ("ui:field" in definitionInSchema) {
153
- await validateForm(
154
- definitionInSchema["ui:field"],
155
- key,
156
- value,
157
- definitionInSchema
158
- );
163
+ const { schema, uiSchema } = extractSchemaFromStep(definitionInSchema);
164
+ const hasItems = definitionInSchema && definitionInSchema.items;
165
+ const doValidateItem = async (propValue, itemSchema, itemUiSchema) => {
166
+ await validateForm(
167
+ propValue["ui:field"],
168
+ key,
169
+ value,
170
+ itemSchema,
171
+ itemUiSchema
172
+ );
173
+ };
174
+ const doValidate = async (propValue) => {
175
+ if ("ui:field" in propValue) {
176
+ const { schema: itemsSchema, uiSchema: itemsUiSchema } = extractSchemaFromStep(definitionInSchema.items);
177
+ await doValidateItem(propValue, itemsSchema, itemsUiSchema);
159
178
  }
160
- } else if (definitionInSchema && definitionInSchema.items && "ui:field" in definitionInSchema.items) {
161
- if ("ui:field" in definitionInSchema.items) {
162
- await validateForm(
163
- definitionInSchema.items["ui:field"],
164
- key,
165
- value,
166
- definitionInSchema.items
167
- );
179
+ };
180
+ if (definitionInSchema && "ui:field" in definitionInSchema) {
181
+ await doValidateItem(definitionInSchema, schema, uiSchema);
182
+ } else if (hasItems && "ui:field" in definitionInSchema.items) {
183
+ await doValidate(definitionInSchema.items);
184
+ } else if (hasItems && definitionInSchema.items.type === "object") {
185
+ const properties = (_b = (_a = definitionInSchema.items) == null ? void 0 : _a.properties) != null ? _b : [];
186
+ for (const [, propValue] of Object.entries(properties)) {
187
+ await doValidate(propValue);
168
188
  }
169
189
  } else if (isObject(value)) {
170
190
  formValidation[key] = await validate(formData, path, value);
@@ -220,23 +240,26 @@ const useTemplateSchema = (manifest) => {
220
240
  var _a;
221
241
  const stepFeatureFlag = (_a = step.uiSchema["ui:backstage"]) == null ? void 0 : _a.featureFlag;
222
242
  return stepFeatureFlag ? featureFlags.isActive(stepFeatureFlag) : true;
223
- }).map((step) => ({
224
- ...step,
225
- schema: {
226
- ...step.schema,
227
- // Title is rendered at the top of the page, so let's ignore this from jsonschemaform
228
- title: void 0,
229
- properties: Object.fromEntries(
230
- Object.entries(step.schema.properties).filter(
231
- ([key]) => {
232
- var _a, _b;
233
- const stepFeatureFlag = (_b = (_a = step.uiSchema[key]) == null ? void 0 : _a["ui:backstage"]) == null ? void 0 : _b.featureFlag;
234
- return stepFeatureFlag ? featureFlags.isActive(stepFeatureFlag) : true;
235
- }
243
+ }).map((step) => {
244
+ var _a, _b;
245
+ return {
246
+ ...step,
247
+ schema: {
248
+ ...step.schema,
249
+ // Title is rendered at the top of the page, so let's ignore this from jsonschemaform
250
+ title: void 0,
251
+ properties: Object.fromEntries(
252
+ Object.entries((_b = (_a = step.schema) == null ? void 0 : _a.properties) != null ? _b : []).filter(
253
+ ([key]) => {
254
+ var _a2, _b2;
255
+ const stepFeatureFlag = (_b2 = (_a2 = step.uiSchema[key]) == null ? void 0 : _a2["ui:backstage"]) == null ? void 0 : _b2.featureFlag;
256
+ return stepFeatureFlag ? featureFlags.isActive(stepFeatureFlag) : true;
257
+ }
258
+ )
236
259
  )
237
- )
238
- }
239
- }));
260
+ }
261
+ };
262
+ });
240
263
  return {
241
264
  steps: returningSteps
242
265
  };
@@ -305,7 +328,7 @@ var FieldOverrides = /*#__PURE__*/Object.freeze({
305
328
 
306
329
  const Form = withTheme(require("@rjsf/material-ui-v5").Theme);
307
330
 
308
- const useStyles$5 = makeStyles((theme) => ({
331
+ const useStyles$7 = makeStyles((theme) => ({
309
332
  backButton: {
310
333
  marginRight: theme.spacing(1)
311
334
  },
@@ -332,7 +355,7 @@ const Stepper = (stepperProps) => {
332
355
  const [activeStep, setActiveStep] = useState(0);
333
356
  const [formState, setFormState] = useFormDataFromQuery(props.initialState);
334
357
  const [errors, setErrors] = useState();
335
- const styles = useStyles$5();
358
+ const styles = useStyles$7();
336
359
  const extensions = useMemo(() => {
337
360
  return Object.fromEntries(
338
361
  props.extensions.map(({ name, component }) => [name, component])
@@ -424,17 +447,16 @@ const Stepper = (stepperProps) => {
424
447
  )))));
425
448
  };
426
449
 
427
- const useStyles$4 = makeStyles(
428
- () => ({
429
- header: {
430
- backgroundImage: ({ cardBackgroundImage }) => cardBackgroundImage
431
- },
432
- subtitleWrapper: {
433
- display: "flex",
434
- justifyContent: "space-between"
435
- }
436
- })
437
- );
450
+ const useStyles$6 = makeStyles(() => ({
451
+ header: {
452
+ backgroundImage: ({ cardBackgroundImage }) => cardBackgroundImage,
453
+ color: ({ cardFontColor }) => cardFontColor
454
+ },
455
+ subtitleWrapper: {
456
+ display: "flex",
457
+ justifyContent: "space-between"
458
+ }
459
+ }));
438
460
  const CardHeader = (props) => {
439
461
  const {
440
462
  template: {
@@ -444,7 +466,8 @@ const CardHeader = (props) => {
444
466
  } = props;
445
467
  const { getPageTheme } = useTheme();
446
468
  const themeForType = getPageTheme({ themeId: type });
447
- const styles = useStyles$4({
469
+ const styles = useStyles$6({
470
+ cardFontColor: themeForType.fontColor,
448
471
  cardBackgroundImage: themeForType.backgroundImage
449
472
  });
450
473
  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 } })));
@@ -458,18 +481,18 @@ const CardHeader = (props) => {
458
481
  );
459
482
  };
460
483
 
461
- const useStyles$3 = makeStyles(() => ({
484
+ const useStyles$5 = makeStyles(() => ({
462
485
  linkText: {
463
486
  display: "inline-flex",
464
487
  alignItems: "center"
465
488
  }
466
489
  }));
467
490
  const CardLink = ({ icon: Icon, text, url }) => {
468
- const styles = useStyles$3();
491
+ const styles = useStyles$5();
469
492
  return /* @__PURE__ */ React.createElement("div", { className: styles.linkText }, /* @__PURE__ */ React.createElement(Icon, { fontSize: "small" }), /* @__PURE__ */ React.createElement(Link, { style: { marginLeft: "8px" }, to: url }, text || url));
470
493
  };
471
494
 
472
- const useStyles$2 = makeStyles((theme) => ({
495
+ const useStyles$4 = makeStyles((theme) => ({
473
496
  box: {
474
497
  overflow: "hidden",
475
498
  textOverflow: "ellipsis",
@@ -507,7 +530,7 @@ const useStyles$2 = makeStyles((theme) => ({
507
530
  const TemplateCard = (props) => {
508
531
  var _a, _b, _c, _d, _e, _f, _g;
509
532
  const { template } = props;
510
- const styles = useStyles$2();
533
+ const styles = useStyles$4();
511
534
  const ownedByRelations = getEntityRelations(template, RELATION_OWNED_BY);
512
535
  const app = useApp();
513
536
  const iconResolver = (key) => {
@@ -581,18 +604,18 @@ const TemplateGroup = (props) => {
581
604
  };
582
605
 
583
606
  const SecretsContext = createVersionedContext("secrets-context");
584
- const SecretsContextProvider = ({ children }) => {
607
+ const SecretsContextProvider = (props) => {
585
608
  const [secrets, setSecrets] = useState({});
586
609
  return /* @__PURE__ */ React.createElement(
587
610
  SecretsContext.Provider,
588
611
  {
589
612
  value: createVersionedValueMap({ 1: { secrets, setSecrets } })
590
613
  },
591
- children
614
+ props.children
592
615
  );
593
616
  };
594
617
 
595
- const useStyles$1 = makeStyles(() => ({
618
+ const useStyles$3 = makeStyles(() => ({
596
619
  markdown: {
597
620
  /** to make the styles for React Markdown not leak into the description */
598
621
  "& :first-child": {
@@ -606,7 +629,7 @@ const useStyles$1 = makeStyles(() => ({
606
629
  const Workflow = (workflowProps) => {
607
630
  var _a;
608
631
  const { title, description, namespace, templateName, ...props } = workflowProps;
609
- const styles = useStyles$1();
632
+ const styles = useStyles$3();
610
633
  const templateRef = stringifyEntityRef({
611
634
  kind: "Template",
612
635
  namespace,
@@ -641,7 +664,7 @@ const Workflow = (workflowProps) => {
641
664
  };
642
665
  const EmbeddableWorkflow = (props) => /* @__PURE__ */ React.createElement(SecretsContextProvider, null, /* @__PURE__ */ React.createElement(Workflow, { ...props }));
643
666
 
644
- const useStyles = makeStyles({
667
+ const useStyles$2 = makeStyles({
645
668
  root: {
646
669
  "&:hover": {
647
670
  textDecoration: "none"
@@ -650,7 +673,7 @@ const useStyles = makeStyles({
650
673
  });
651
674
  const LinkOutputs = (props) => {
652
675
  const { links = [] } = props.output;
653
- const classes = useStyles();
676
+ const classes = useStyles$2();
654
677
  const app = useApp();
655
678
  const entityRoute = useRouteRef(entityRouteRef);
656
679
  const iconResolver = (key) => {
@@ -678,6 +701,144 @@ const DefaultTemplateOutputs = (props) => {
678
701
  return /* @__PURE__ */ React.createElement(Box, { paddingBottom: 2 }, /* @__PURE__ */ React.createElement(Paper, null, /* @__PURE__ */ React.createElement(Box, { padding: 2, justifyContent: "center", display: "flex", gridGap: 16 }, /* @__PURE__ */ React.createElement(LinkOutputs, { output: props.output }))));
679
702
  };
680
703
 
704
+ const useStepIconStyles = makeStyles((theme) => ({
705
+ root: {
706
+ color: theme.palette.text.disabled
707
+ },
708
+ completed: {
709
+ color: theme.palette.status.ok
710
+ },
711
+ error: {
712
+ color: theme.palette.status.error
713
+ }
714
+ }));
715
+ const StepIcon = (props) => {
716
+ const classes = useStepIconStyles();
717
+ const { active, completed, error, skipped } = props;
718
+ const getMiddle = () => {
719
+ if (active) {
720
+ return /* @__PURE__ */ React.createElement(CircularProgress, { size: "20px" });
721
+ }
722
+ if (completed) {
723
+ return /* @__PURE__ */ React.createElement(CheckCircleOutline, null);
724
+ }
725
+ if (error) {
726
+ return /* @__PURE__ */ React.createElement(ErrorOutline, null);
727
+ }
728
+ if (skipped) {
729
+ return /* @__PURE__ */ React.createElement(RemoveCircleOutline, null);
730
+ }
731
+ return /* @__PURE__ */ React.createElement(PanoramaFishEyeIcon, null);
732
+ };
733
+ return /* @__PURE__ */ React.createElement(
734
+ "div",
735
+ {
736
+ className: classNames(classes.root, {
737
+ [classes.completed]: completed,
738
+ [classes.error]: error
739
+ })
740
+ },
741
+ getMiddle()
742
+ );
743
+ };
744
+
745
+ const StepTime = (props) => {
746
+ const [time, setTime] = useState("");
747
+ const { step } = props;
748
+ const calculate = useCallback(() => {
749
+ if (!step.startedAt) {
750
+ setTime("");
751
+ return;
752
+ }
753
+ const end = step.endedAt ? DateTime.fromISO(step.endedAt) : DateTime.local();
754
+ const startedAt = DateTime.fromISO(step.startedAt);
755
+ const formatted = Interval.fromDateTimes(startedAt, end).toDuration().valueOf();
756
+ setTime(humanizeDuration(formatted, { round: true }));
757
+ }, [step.endedAt, step.startedAt]);
758
+ useMountEffect(() => calculate());
759
+ useInterval(() => !step.endedAt && calculate(), 1e3);
760
+ return /* @__PURE__ */ React.createElement(Typography, { variant: "caption" }, time);
761
+ };
762
+
763
+ const useStyles$1 = makeStyles((theme) => ({
764
+ failed: {
765
+ backgroundColor: theme.palette.error.main
766
+ },
767
+ success: {
768
+ backgroundColor: theme.palette.success.main
769
+ }
770
+ }));
771
+ const TaskBorder = (props) => {
772
+ const styles = useStyles$1();
773
+ if (!props.isComplete) {
774
+ return /* @__PURE__ */ React.createElement(LinearProgress, { variant: "indeterminate" });
775
+ }
776
+ return /* @__PURE__ */ React.createElement(
777
+ LinearProgress,
778
+ {
779
+ variant: "determinate",
780
+ classes: { bar: props.isError ? styles.failed : styles.success },
781
+ value: 100
782
+ }
783
+ );
784
+ };
785
+
786
+ const TaskSteps = (props) => {
787
+ var _a, _b;
788
+ return /* @__PURE__ */ React.createElement(Paper, { style: { position: "relative", overflow: "hidden" } }, /* @__PURE__ */ React.createElement(
789
+ TaskBorder,
790
+ {
791
+ isComplete: (_a = props.isComplete) != null ? _a : false,
792
+ isError: (_b = props.isError) != null ? _b : false
793
+ }
794
+ ), /* @__PURE__ */ React.createElement(Box, { padding: 2 }, /* @__PURE__ */ React.createElement(
795
+ Stepper$1,
796
+ {
797
+ activeStep: props.activeStep,
798
+ alternativeLabel: true,
799
+ variant: "elevation"
800
+ },
801
+ props.steps.map((step, index) => {
802
+ const isCompleted = step.status === "completed";
803
+ const isFailed = step.status === "failed";
804
+ const isActive = step.status === "processing";
805
+ const isSkipped = step.status === "skipped";
806
+ const stepIconProps = {
807
+ completed: isCompleted,
808
+ error: isFailed,
809
+ active: isActive,
810
+ skipped: isSkipped
811
+ };
812
+ return /* @__PURE__ */ React.createElement(Step, { key: index }, /* @__PURE__ */ React.createElement(StepButton, null, /* @__PURE__ */ React.createElement(
813
+ StepLabel,
814
+ {
815
+ StepIconProps: stepIconProps,
816
+ StepIconComponent: StepIcon
817
+ },
818
+ /* @__PURE__ */ React.createElement(Box, null, step.name),
819
+ /* @__PURE__ */ React.createElement(StepTime, { step })
820
+ )));
821
+ })
822
+ )));
823
+ };
824
+
825
+ const useStyles = makeStyles$1({
826
+ root: {
827
+ width: "100%",
828
+ height: "100%",
829
+ position: "relative"
830
+ }
831
+ });
832
+ const TaskLogStream = (props) => {
833
+ const styles = useStyles();
834
+ return /* @__PURE__ */ React.createElement("div", { className: styles.root }, /* @__PURE__ */ React.createElement(
835
+ LogViewer,
836
+ {
837
+ text: Object.values(props.logs).map((l) => l.join("\n")).filter(Boolean).join("\n")
838
+ }
839
+ ));
840
+ };
841
+
681
842
  const FIELD_EXTENSION_KEY = "scaffolder.extensions.field.v1";
682
843
 
683
844
  function createNextScaffolderFieldExtension(options) {
@@ -694,5 +855,5 @@ function createNextScaffolderFieldExtension(options) {
694
855
  };
695
856
  }
696
857
 
697
- export { DefaultTemplateOutputs, EmbeddableWorkflow, Form, ReviewState, Stepper, TemplateCard, TemplateGroup, Workflow, createFieldValidation, createNextScaffolderFieldExtension, extractSchemaFromStep, useFormDataFromQuery, useTemplateParameterSchema, useTemplateSchema };
858
+ export { DefaultTemplateOutputs, EmbeddableWorkflow, Form, ReviewState, Stepper, TaskLogStream, TaskSteps, TemplateCard, TemplateGroup, Workflow, createFieldValidation, createNextScaffolderFieldExtension, extractSchemaFromStep, useFormDataFromQuery, useTemplateParameterSchema, useTemplateSchema };
698
859
  //# sourceMappingURL=alpha.esm.js.map