@backstage/plugin-scaffolder 1.14.1-next.1 → 1.14.1

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,29 +1,32 @@
1
- import React, { useState, Fragment, createContext, useRef, useCallback, useMemo, useContext, useEffect, Children } from 'react';
1
+ import React, { useState, Fragment, createContext, useEffect, useContext, useRef, useCallback, useMemo, Children } from 'react';
2
2
  import useAsync from 'react-use/lib/useAsync';
3
3
  import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
4
- import { makeStyles, Box, Typography, Accordion, AccordionSummary, AccordionDetails, Grid, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, Collapse, Card, List, MenuItem, ListItemIcon, ListItemText, Tooltip, IconButton as IconButton$1, Divider as Divider$1 } from '@material-ui/core';
4
+ import { makeStyles, Box, Typography, Accordion, AccordionSummary, AccordionDetails, Grid, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, Collapse, Tooltip as Tooltip$1, IconButton as IconButton$1, Divider as Divider$1, Card as Card$1, List as List$1, MenuItem, ListItemIcon as ListItemIcon$1, ListItemText as ListItemText$1 } from '@material-ui/core';
5
5
  import classNames from 'classnames';
6
6
  import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
7
7
  import ExpandLessIcon from '@material-ui/icons/ExpandLess';
8
- import { useApi } from '@backstage/core-plugin-api';
9
- import { Progress, ErrorPage, MarkdownContent, Page, Header, Content, CodeSnippet, ErrorPanel, LogViewer } from '@backstage/core-components';
8
+ import { useApi, useRouteRef } from '@backstage/core-plugin-api';
9
+ import { Progress, ErrorPage, MarkdownContent, Page, Header, Content, CodeSnippet, ErrorPanel, LogViewer, StatusError, StatusOK, StatusPending, Lifecycle, EmptyState, Table as Table$1, Link } from '@backstage/core-components';
10
10
  import Chip from '@material-ui/core/Chip';
11
- import SettingsIcon from '@material-ui/icons/Settings';
12
- import AllIcon from '@material-ui/icons/FontDownload';
11
+ import Card from '@material-ui/core/Card';
12
+ import CardActionArea from '@material-ui/core/CardActionArea';
13
+ import CardContent from '@material-ui/core/CardContent';
14
+ import Tooltip from '@material-ui/core/Tooltip';
15
+ import Typography$1 from '@material-ui/core/Typography';
16
+ import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined';
17
+ import { makeStyles as makeStyles$1 } from '@material-ui/core/styles';
13
18
  import Accordion$1 from '@material-ui/core/Accordion';
14
19
  import AccordionDetails$1 from '@material-ui/core/AccordionDetails';
15
20
  import AccordionSummary$1 from '@material-ui/core/AccordionSummary';
16
21
  import Divider from '@material-ui/core/Divider';
17
- import { makeStyles as makeStyles$1 } from '@material-ui/core/styles';
18
- import Typography$1 from '@material-ui/core/Typography';
19
22
  import { useAsync as useAsync$1, useRerender, usePrevious, useKeyboardEvent } from '@react-hookz/web';
20
23
  import yaml from 'yaml';
21
24
  import IconButton from '@material-ui/core/IconButton';
22
- import List$1 from '@material-ui/core/List';
25
+ import List from '@material-ui/core/List';
23
26
  import ListItem from '@material-ui/core/ListItem';
24
- import ListItemIcon$1 from '@material-ui/core/ListItemIcon';
27
+ import ListItemIcon from '@material-ui/core/ListItemIcon';
25
28
  import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
26
- import ListItemText$1 from '@material-ui/core/ListItemText';
29
+ import ListItemText from '@material-ui/core/ListItemText';
27
30
  import Cancel from '@material-ui/icons/Cancel';
28
31
  import Check from '@material-ui/icons/Check';
29
32
  import DeleteIcon from '@material-ui/icons/Delete';
@@ -33,7 +36,7 @@ import Box$1 from '@material-ui/core/Box';
33
36
  import Tab from '@material-ui/core/Tab';
34
37
  import Tabs from '@material-ui/core/Tabs';
35
38
  import CodeMirror from '@uiw/react-codemirror';
36
- import { o as TaskStatusStepper, p as TaskPageLinks } from './TaskPage-b9589b1b.esm.js';
39
+ import { a as TaskStatusStepper, b as TaskPageLinks } from './TaskPage-e70ca2f8.esm.js';
37
40
  import CloseIcon from '@material-ui/icons/Close';
38
41
  import RefreshIcon from '@material-ui/icons/Refresh';
39
42
  import SaveIcon from '@material-ui/icons/Save';
@@ -41,11 +44,13 @@ import TreeView from '@material-ui/lab/TreeView';
41
44
  import ChevronRightIcon from '@material-ui/icons/ChevronRight';
42
45
  import TreeItem from '@material-ui/lab/TreeItem';
43
46
  import { showPanel } from '@codemirror/view';
44
- import Card$1 from '@material-ui/core/Card';
45
- import CardActionArea from '@material-ui/core/CardActionArea';
46
- import CardContent from '@material-ui/core/CardContent';
47
- import Tooltip$1 from '@material-ui/core/Tooltip';
48
- import InfoOutlinedIcon from '@material-ui/icons/InfoOutlined';
47
+ import { x as EntityPicker, y as EntityPickerSchema, z as EntityNamePicker, A as entityNamePickerValidation, B as EntityNamePickerSchema, C as EntityTagsPicker, D as EntityTagsPickerSchema, F as RepoUrlPicker, h as repoPickerValidation, G as RepoUrlPickerSchema, H as OwnerPicker, I as OwnerPickerSchema, J as OwnedEntityPicker, K as OwnedEntityPickerSchema, L as MyGroupsPicker, k as MyGroupsPickerSchema, r as rootRouteRef } from './plugin-e86ee6a1.esm.js';
48
+ import { catalogApiRef, EntityRefLink, CatalogFilterLayout } from '@backstage/plugin-catalog-react';
49
+ import SettingsIcon from '@material-ui/icons/Settings';
50
+ import AllIcon from '@material-ui/icons/FontDownload';
51
+ import { DateTime, Interval } from 'luxon';
52
+ import humanizeDuration from 'humanize-duration';
53
+ import { parseEntityRef } from '@backstage/catalog-model';
49
54
 
50
55
  const useStyles$9 = makeStyles((theme) => ({
51
56
  code: {
@@ -191,82 +196,6 @@ const ActionsPage = () => {
191
196
  ), /* @__PURE__ */ React.createElement(Content, null, items));
192
197
  };
193
198
 
194
- const useStyles$8 = makeStyles(
195
- (theme) => ({
196
- root: {
197
- backgroundColor: "rgba(0, 0, 0, .11)",
198
- boxShadow: "none",
199
- margin: theme.spacing(1, 0, 1, 0)
200
- },
201
- title: {
202
- margin: theme.spacing(1, 0, 0, 1),
203
- textTransform: "uppercase",
204
- fontSize: 12,
205
- fontWeight: "bold"
206
- },
207
- listIcon: {
208
- minWidth: 30,
209
- color: theme.palette.text.primary
210
- },
211
- menuItem: {
212
- minHeight: theme.spacing(6)
213
- },
214
- groupWrapper: {
215
- margin: theme.spacing(1, 1, 2, 1)
216
- }
217
- }),
218
- {
219
- name: "ScaffolderReactOwnerListPicker"
220
- }
221
- );
222
- function getFilterGroups() {
223
- return [
224
- {
225
- name: "Task Owner",
226
- items: [
227
- {
228
- id: "owned",
229
- label: "Owned",
230
- icon: SettingsIcon
231
- },
232
- {
233
- id: "all",
234
- label: "All",
235
- icon: AllIcon
236
- }
237
- ]
238
- }
239
- ];
240
- }
241
- const OwnerListPicker = (props) => {
242
- const { filter, onSelectOwner } = props;
243
- const classes = useStyles$8();
244
- const filterGroups = getFilterGroups();
245
- return /* @__PURE__ */ React.createElement(Card, { className: classes.root }, filterGroups.map((group) => /* @__PURE__ */ React.createElement(Fragment, { key: group.name }, /* @__PURE__ */ React.createElement(
246
- Typography,
247
- {
248
- variant: "subtitle2",
249
- component: "span",
250
- className: classes.title
251
- },
252
- group.name
253
- ), /* @__PURE__ */ React.createElement(Card, { className: classes.groupWrapper }, /* @__PURE__ */ React.createElement(List, { disablePadding: true, dense: true, role: "menu" }, group.items.map((item) => /* @__PURE__ */ React.createElement(
254
- MenuItem,
255
- {
256
- key: item.id,
257
- button: true,
258
- divider: true,
259
- ContainerProps: { role: "menuitem" },
260
- onClick: () => onSelectOwner(item.id),
261
- selected: item.id === filter,
262
- className: classes.menuItem,
263
- "data-testid": `owner-picker-${item.id}`
264
- },
265
- item.icon && /* @__PURE__ */ React.createElement(ListItemIcon, { className: classes.listIcon }, /* @__PURE__ */ React.createElement(item.icon, { fontSize: "small" })),
266
- /* @__PURE__ */ React.createElement(ListItemText, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, item.label))
267
- )))))));
268
- };
269
-
270
199
  const showDirectoryPicker = window.showDirectoryPicker;
271
200
  class WebFileAccess {
272
201
  constructor(path, handle) {
@@ -321,109 +250,12 @@ class WebFileSystemAccess {
321
250
  }
322
251
  }
323
252
 
324
- const MAX_CONTENT_SIZE = 64 * 1024;
325
- const CHUNK_SIZE = 32 * 1024;
326
- const DryRunContext = createContext(void 0);
327
- function base64EncodeContent(content) {
328
- if (content.length > MAX_CONTENT_SIZE) {
329
- return window.btoa("<file too large>");
330
- }
331
- try {
332
- return window.btoa(content);
333
- } catch {
334
- const decoder = new TextEncoder();
335
- const buffer = decoder.encode(content);
336
- const chunks = new Array();
337
- for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {
338
- chunks.push(
339
- String.fromCharCode(...buffer.slice(offset, offset + CHUNK_SIZE))
340
- );
341
- }
342
- return window.btoa(chunks.join(""));
343
- }
344
- }
345
- function DryRunProvider(props) {
346
- const scaffolderApi = useApi(scaffolderApiRef);
347
- const [state, setState] = useState({
348
- results: [],
349
- selectedResult: void 0
350
- });
351
- const idRef = useRef(1);
352
- const selectResult = useCallback((id) => {
353
- setState((prevState) => {
354
- const result = prevState.results.find((r) => r.id === id);
355
- if (result === prevState.selectedResult) {
356
- return prevState;
357
- }
358
- return {
359
- results: prevState.results,
360
- selectedResult: result
361
- };
362
- });
363
- }, []);
364
- const deleteResult = useCallback((id) => {
365
- setState((prevState) => {
366
- var _a;
367
- const index = prevState.results.findIndex((r) => r.id === id);
368
- if (index === -1) {
369
- return prevState;
370
- }
371
- const newResults = prevState.results.slice();
372
- const [deleted] = newResults.splice(index, 1);
373
- return {
374
- results: newResults,
375
- selectedResult: ((_a = prevState.selectedResult) == null ? void 0 : _a.id) === deleted.id ? newResults[0] : prevState.selectedResult
376
- };
377
- });
378
- }, []);
379
- const execute = useCallback(
380
- async (options) => {
381
- if (!scaffolderApi.dryRun) {
382
- throw new Error("Scaffolder API does not support dry-run");
383
- }
384
- const parsed = yaml.parse(options.templateContent);
385
- const response = await scaffolderApi.dryRun({
386
- template: parsed,
387
- values: options.values,
388
- secrets: {},
389
- directoryContents: options.files.map((file) => ({
390
- path: file.path,
391
- base64Content: base64EncodeContent(file.content)
392
- }))
393
- });
394
- const result = {
395
- ...response,
396
- id: idRef.current++
397
- };
398
- setState((prevState) => {
399
- var _a;
400
- return {
401
- results: [...prevState.results, result],
402
- selectedResult: (_a = prevState.selectedResult) != null ? _a : result
403
- };
404
- });
405
- },
406
- [scaffolderApi]
407
- );
408
- const dryRun = useMemo(
409
- () => ({
410
- ...state,
411
- selectResult,
412
- deleteResult,
413
- execute
414
- }),
415
- [state, selectResult, deleteResult, execute]
416
- );
417
- return /* @__PURE__ */ React.createElement(DryRunContext.Provider, { value: dryRun }, props.children);
418
- }
419
- function useDryRun() {
420
- const value = useContext(DryRunContext);
421
- if (!value) {
422
- throw new Error("must be used within a DryRunProvider");
423
- }
253
+ var __defProp = Object.defineProperty;
254
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
255
+ var __publicField = (obj, key, value) => {
256
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
424
257
  return value;
425
- }
426
-
258
+ };
427
259
  var __accessCheck = (obj, member, msg) => {
428
260
  if (!member.has(obj))
429
261
  throw TypeError("Cannot " + msg);
@@ -506,14 +338,14 @@ class DirectoryEditorManager {
506
338
  __privateAdd(this, _listeners, /* @__PURE__ */ new Set());
507
339
  __privateAdd(this, _files, []);
508
340
  __privateAdd(this, _selectedFile, void 0);
509
- this.setSelectedFile = (path) => {
341
+ __publicField(this, "setSelectedFile", (path) => {
510
342
  const prev = __privateGet(this, _selectedFile);
511
343
  const next = __privateGet(this, _files).find((file) => file.path === path);
512
344
  if (prev !== next) {
513
345
  __privateSet(this, _selectedFile, next);
514
346
  __privateGet(this, _signalUpdate2).call(this);
515
347
  }
516
- };
348
+ });
517
349
  __privateAdd(this, _signalUpdate2, () => {
518
350
  __privateGet(this, _listeners).forEach((listener) => listener());
519
351
  });
@@ -595,7 +427,191 @@ function DirectoryEditorProvider(props) {
595
427
  } else if (!result) {
596
428
  return /* @__PURE__ */ React.createElement(Progress, null);
597
429
  }
598
- return /* @__PURE__ */ React.createElement(DirectoryEditorContext.Provider, { value: result }, props.children);
430
+ return /* @__PURE__ */ React.createElement(DirectoryEditorContext.Provider, { value: result }, props.children);
431
+ }
432
+
433
+ const MAX_CONTENT_SIZE = 64 * 1024;
434
+ const CHUNK_SIZE = 32 * 1024;
435
+ const DryRunContext = createContext(void 0);
436
+ function base64EncodeContent(content) {
437
+ if (content.length > MAX_CONTENT_SIZE) {
438
+ return window.btoa("<file too large>");
439
+ }
440
+ try {
441
+ return window.btoa(content);
442
+ } catch {
443
+ const decoder = new TextEncoder();
444
+ const buffer = decoder.encode(content);
445
+ const chunks = new Array();
446
+ for (let offset = 0; offset < buffer.length; offset += CHUNK_SIZE) {
447
+ chunks.push(
448
+ String.fromCharCode(...buffer.slice(offset, offset + CHUNK_SIZE))
449
+ );
450
+ }
451
+ return window.btoa(chunks.join(""));
452
+ }
453
+ }
454
+ function DryRunProvider(props) {
455
+ const scaffolderApi = useApi(scaffolderApiRef);
456
+ const [state, setState] = useState({
457
+ results: [],
458
+ selectedResult: void 0
459
+ });
460
+ const idRef = useRef(1);
461
+ const selectResult = useCallback((id) => {
462
+ setState((prevState) => {
463
+ const result = prevState.results.find((r) => r.id === id);
464
+ if (result === prevState.selectedResult) {
465
+ return prevState;
466
+ }
467
+ return {
468
+ results: prevState.results,
469
+ selectedResult: result
470
+ };
471
+ });
472
+ }, []);
473
+ const deleteResult = useCallback((id) => {
474
+ setState((prevState) => {
475
+ var _a;
476
+ const index = prevState.results.findIndex((r) => r.id === id);
477
+ if (index === -1) {
478
+ return prevState;
479
+ }
480
+ const newResults = prevState.results.slice();
481
+ const [deleted] = newResults.splice(index, 1);
482
+ return {
483
+ results: newResults,
484
+ selectedResult: ((_a = prevState.selectedResult) == null ? void 0 : _a.id) === deleted.id ? newResults[0] : prevState.selectedResult
485
+ };
486
+ });
487
+ }, []);
488
+ const execute = useCallback(
489
+ async (options) => {
490
+ if (!scaffolderApi.dryRun) {
491
+ throw new Error("Scaffolder API does not support dry-run");
492
+ }
493
+ const parsed = yaml.parse(options.templateContent);
494
+ const response = await scaffolderApi.dryRun({
495
+ template: parsed,
496
+ values: options.values,
497
+ secrets: {},
498
+ directoryContents: options.files.map((file) => ({
499
+ path: file.path,
500
+ base64Content: base64EncodeContent(file.content)
501
+ }))
502
+ });
503
+ const result = {
504
+ ...response,
505
+ id: idRef.current++
506
+ };
507
+ setState((prevState) => {
508
+ var _a;
509
+ return {
510
+ results: [...prevState.results, result],
511
+ selectedResult: (_a = prevState.selectedResult) != null ? _a : result
512
+ };
513
+ });
514
+ },
515
+ [scaffolderApi]
516
+ );
517
+ const dryRun = useMemo(
518
+ () => ({
519
+ ...state,
520
+ selectResult,
521
+ deleteResult,
522
+ execute
523
+ }),
524
+ [state, selectResult, deleteResult, execute]
525
+ );
526
+ return /* @__PURE__ */ React.createElement(DryRunContext.Provider, { value: dryRun }, props.children);
527
+ }
528
+ function useDryRun() {
529
+ const value = useContext(DryRunContext);
530
+ if (!value) {
531
+ throw new Error("must be used within a DryRunProvider");
532
+ }
533
+ return value;
534
+ }
535
+
536
+ const useStyles$8 = makeStyles$1((theme) => ({
537
+ introText: {
538
+ textAlign: "center",
539
+ marginTop: theme.spacing(2)
540
+ },
541
+ card: {
542
+ position: "relative",
543
+ maxWidth: 340,
544
+ marginTop: theme.spacing(4),
545
+ margin: theme.spacing(0, 2)
546
+ },
547
+ infoIcon: {
548
+ position: "absolute",
549
+ top: theme.spacing(1),
550
+ right: theme.spacing(1)
551
+ }
552
+ }));
553
+ function TemplateEditorIntro(props) {
554
+ const classes = useStyles$8();
555
+ const supportsLoad = WebFileSystemAccess.isSupported();
556
+ const cardLoadLocal = /* @__PURE__ */ React.createElement(Card, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(
557
+ CardActionArea,
558
+ {
559
+ disabled: !supportsLoad,
560
+ onClick: () => {
561
+ var _a;
562
+ return (_a = props.onSelect) == null ? void 0 : _a.call(props, "local");
563
+ }
564
+ },
565
+ /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(
566
+ Typography$1,
567
+ {
568
+ variant: "h4",
569
+ component: "h3",
570
+ gutterBottom: true,
571
+ color: supportsLoad ? void 0 : "textSecondary",
572
+ style: { display: "flex", flexFlow: "row nowrap" }
573
+ },
574
+ "Load Template Directory"
575
+ ), /* @__PURE__ */ React.createElement(
576
+ Typography$1,
577
+ {
578
+ variant: "body1",
579
+ color: supportsLoad ? void 0 : "textSecondary"
580
+ },
581
+ "Load a local template directory, allowing you to both edit and try executing your own template."
582
+ ))
583
+ ), !supportsLoad && /* @__PURE__ */ React.createElement("div", { className: classes.infoIcon }, /* @__PURE__ */ React.createElement(
584
+ Tooltip,
585
+ {
586
+ placement: "top",
587
+ title: "Only supported in some Chromium-based browsers"
588
+ },
589
+ /* @__PURE__ */ React.createElement(InfoOutlinedIcon, null)
590
+ )));
591
+ const cardFormEditor = /* @__PURE__ */ React.createElement(Card, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(CardActionArea, { onClick: () => {
592
+ var _a;
593
+ return (_a = props.onSelect) == null ? void 0 : _a.call(props, "form");
594
+ } }, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h4", component: "h3", gutterBottom: true }, "Edit Template Form"), /* @__PURE__ */ React.createElement(Typography$1, { variant: "body1" }, "Preview and edit a template form, either using a sample template or by loading a template from the catalog."))));
595
+ const cardFieldExplorer = /* @__PURE__ */ React.createElement(Card, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(CardActionArea, { onClick: () => {
596
+ var _a;
597
+ return (_a = props.onSelect) == null ? void 0 : _a.call(props, "field-explorer");
598
+ } }, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h4", component: "h3", gutterBottom: true }, "Custom Field Explorer"), /* @__PURE__ */ React.createElement(Typography$1, { variant: "body1" }, "View and play around with available installed custom field extensions."))));
599
+ return /* @__PURE__ */ React.createElement("div", { style: props.style }, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h4", component: "h2", className: classes.introText }, "Get started by choosing one of the options below"), /* @__PURE__ */ React.createElement(
600
+ "div",
601
+ {
602
+ style: {
603
+ display: "flex",
604
+ flexFlow: "row wrap",
605
+ alignItems: "flex-start",
606
+ justifyContent: "center",
607
+ alignContent: "flex-start"
608
+ }
609
+ },
610
+ supportsLoad && cardLoadLocal,
611
+ cardFormEditor,
612
+ !supportsLoad && cardLoadLocal,
613
+ cardFieldExplorer
614
+ ));
599
615
  }
600
616
 
601
617
  const useStyles$7 = makeStyles$1((theme) => ({
@@ -617,7 +633,7 @@ const useStyles$7 = makeStyles$1((theme) => ({
617
633
  function DryRunResultsList() {
618
634
  const classes = useStyles$7();
619
635
  const dryRun = useDryRun();
620
- return /* @__PURE__ */ React.createElement(List$1, { className: classes.root, dense: true }, dryRun.results.map((result) => {
636
+ return /* @__PURE__ */ React.createElement(List, { className: classes.root, dense: true }, dryRun.results.map((result) => {
621
637
  var _a;
622
638
  const failed = result.log.some((l) => l.body.status === "failed");
623
639
  return /* @__PURE__ */ React.createElement(
@@ -629,13 +645,13 @@ function DryRunResultsList() {
629
645
  onClick: () => dryRun.selectResult(result.id)
630
646
  },
631
647
  /* @__PURE__ */ React.createElement(
632
- ListItemIcon$1,
648
+ ListItemIcon,
633
649
  {
634
650
  className: failed ? classes.iconFailure : classes.iconSuccess
635
651
  },
636
652
  failed ? /* @__PURE__ */ React.createElement(Cancel, null) : /* @__PURE__ */ React.createElement(Check, null)
637
653
  ),
638
- /* @__PURE__ */ React.createElement(ListItemText$1, { primary: `Result ${result.id}` }),
654
+ /* @__PURE__ */ React.createElement(ListItemText, { primary: `Result ${result.id}` }),
639
655
  /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(
640
656
  IconButton,
641
657
  {
@@ -972,7 +988,7 @@ function TemplateEditorBrowser(props) {
972
988
  }
973
989
  props.onClose();
974
990
  };
975
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: classes.buttons }, /* @__PURE__ */ React.createElement(Tooltip, { title: "Save all files" }, /* @__PURE__ */ React.createElement(
991
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: classes.buttons }, /* @__PURE__ */ React.createElement(Tooltip$1, { title: "Save all files" }, /* @__PURE__ */ React.createElement(
976
992
  IconButton$1,
977
993
  {
978
994
  className: classes.button,
@@ -980,14 +996,14 @@ function TemplateEditorBrowser(props) {
980
996
  onClick: () => directoryEditor.save()
981
997
  },
982
998
  /* @__PURE__ */ React.createElement(SaveIcon, null)
983
- )), /* @__PURE__ */ React.createElement(Tooltip, { title: "Reload directory" }, /* @__PURE__ */ React.createElement(
999
+ )), /* @__PURE__ */ React.createElement(Tooltip$1, { title: "Reload directory" }, /* @__PURE__ */ React.createElement(
984
1000
  IconButton$1,
985
1001
  {
986
1002
  className: classes.button,
987
1003
  onClick: () => directoryEditor.reload()
988
1004
  },
989
1005
  /* @__PURE__ */ React.createElement(RefreshIcon, null)
990
- )), /* @__PURE__ */ React.createElement("div", { className: classes.buttonsGap }), /* @__PURE__ */ React.createElement(Tooltip, { title: "Close directory" }, /* @__PURE__ */ React.createElement(IconButton$1, { className: classes.button, onClick: handleClose }, /* @__PURE__ */ React.createElement(CloseIcon, null)))), /* @__PURE__ */ React.createElement(Divider$1, { className: classes.buttonsDivider }), /* @__PURE__ */ React.createElement(
1006
+ )), /* @__PURE__ */ React.createElement("div", { className: classes.buttonsGap }), /* @__PURE__ */ React.createElement(Tooltip$1, { title: "Close directory" }, /* @__PURE__ */ React.createElement(IconButton$1, { className: classes.button, onClick: handleClose }, /* @__PURE__ */ React.createElement(CloseIcon, null)))), /* @__PURE__ */ React.createElement(Divider$1, { className: classes.buttonsDivider }), /* @__PURE__ */ React.createElement(
991
1007
  FileBrowser,
992
1008
  {
993
1009
  selected: (_b = (_a = directoryEditor.selectedFile) == null ? void 0 : _a.path) != null ? _b : "",
@@ -1055,7 +1071,7 @@ function TemplateEditorTextArea(props) {
1055
1071
  value: props.content,
1056
1072
  onChange: props.onUpdate
1057
1073
  }
1058
- ), (props.onSave || props.onReload) && /* @__PURE__ */ React.createElement("div", { className: classes.floatingButtons }, /* @__PURE__ */ React.createElement(Paper, null, props.onSave && /* @__PURE__ */ React.createElement(Tooltip, { title: "Save file" }, /* @__PURE__ */ React.createElement(
1074
+ ), (props.onSave || props.onReload) && /* @__PURE__ */ React.createElement("div", { className: classes.floatingButtons }, /* @__PURE__ */ React.createElement(Paper, null, props.onSave && /* @__PURE__ */ React.createElement(Tooltip$1, { title: "Save file" }, /* @__PURE__ */ React.createElement(
1059
1075
  IconButton$1,
1060
1076
  {
1061
1077
  className: classes.floatingButton,
@@ -1065,7 +1081,7 @@ function TemplateEditorTextArea(props) {
1065
1081
  }
1066
1082
  },
1067
1083
  /* @__PURE__ */ React.createElement(SaveIcon, null)
1068
- )), props.onReload && /* @__PURE__ */ React.createElement(Tooltip, { title: "Reload file" }, /* @__PURE__ */ React.createElement(
1084
+ )), props.onReload && /* @__PURE__ */ React.createElement(Tooltip$1, { title: "Reload file" }, /* @__PURE__ */ React.createElement(
1069
1085
  IconButton$1,
1070
1086
  {
1071
1087
  className: classes.floatingButton,
@@ -1101,86 +1117,264 @@ function TemplateEditorDirectoryEditorTextArea(props) {
1101
1117
  }
1102
1118
  TemplateEditorTextArea.DirectoryEditor = TemplateEditorDirectoryEditorTextArea;
1103
1119
 
1104
- const useStyles = makeStyles$1((theme) => ({
1105
- introText: {
1106
- textAlign: "center",
1107
- marginTop: theme.spacing(2)
1120
+ const DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS = [
1121
+ {
1122
+ component: EntityPicker,
1123
+ name: "EntityPicker",
1124
+ schema: EntityPickerSchema
1108
1125
  },
1109
- card: {
1110
- position: "relative",
1111
- maxWidth: 340,
1112
- marginTop: theme.spacing(4),
1113
- margin: theme.spacing(0, 2)
1126
+ {
1127
+ component: EntityNamePicker,
1128
+ name: "EntityNamePicker",
1129
+ validation: entityNamePickerValidation,
1130
+ schema: EntityNamePickerSchema
1114
1131
  },
1115
- infoIcon: {
1116
- position: "absolute",
1117
- top: theme.spacing(1),
1118
- right: theme.spacing(1)
1132
+ {
1133
+ component: EntityTagsPicker,
1134
+ name: "EntityTagsPicker",
1135
+ schema: EntityTagsPickerSchema
1136
+ },
1137
+ {
1138
+ component: RepoUrlPicker,
1139
+ name: "RepoUrlPicker",
1140
+ validation: repoPickerValidation,
1141
+ schema: RepoUrlPickerSchema
1142
+ },
1143
+ {
1144
+ component: OwnerPicker,
1145
+ name: "OwnerPicker",
1146
+ schema: OwnerPickerSchema
1147
+ },
1148
+ {
1149
+ component: OwnedEntityPicker,
1150
+ name: "OwnedEntityPicker",
1151
+ schema: OwnedEntityPickerSchema
1152
+ },
1153
+ {
1154
+ component: MyGroupsPicker,
1155
+ name: "MyGroupsPicker",
1156
+ schema: MyGroupsPickerSchema
1119
1157
  }
1120
- }));
1121
- function TemplateEditorIntro(props) {
1158
+ ];
1159
+
1160
+ const useStyles = makeStyles(
1161
+ (theme) => ({
1162
+ root: {
1163
+ backgroundColor: "rgba(0, 0, 0, .11)",
1164
+ boxShadow: "none",
1165
+ margin: theme.spacing(1, 0, 1, 0)
1166
+ },
1167
+ title: {
1168
+ margin: theme.spacing(1, 0, 0, 1),
1169
+ textTransform: "uppercase",
1170
+ fontSize: 12,
1171
+ fontWeight: "bold"
1172
+ },
1173
+ listIcon: {
1174
+ minWidth: 30,
1175
+ color: theme.palette.text.primary
1176
+ },
1177
+ menuItem: {
1178
+ minHeight: theme.spacing(6)
1179
+ },
1180
+ groupWrapper: {
1181
+ margin: theme.spacing(1, 1, 2, 1)
1182
+ }
1183
+ }),
1184
+ {
1185
+ name: "ScaffolderReactOwnerListPicker"
1186
+ }
1187
+ );
1188
+ function getFilterGroups() {
1189
+ return [
1190
+ {
1191
+ name: "Task Owner",
1192
+ items: [
1193
+ {
1194
+ id: "owned",
1195
+ label: "Owned",
1196
+ icon: SettingsIcon
1197
+ },
1198
+ {
1199
+ id: "all",
1200
+ label: "All",
1201
+ icon: AllIcon
1202
+ }
1203
+ ]
1204
+ }
1205
+ ];
1206
+ }
1207
+ const OwnerListPicker = (props) => {
1208
+ const { filter, onSelectOwner } = props;
1122
1209
  const classes = useStyles();
1123
- const supportsLoad = WebFileSystemAccess.isSupported();
1124
- const cardLoadLocal = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(
1125
- CardActionArea,
1210
+ const filterGroups = getFilterGroups();
1211
+ return /* @__PURE__ */ React.createElement(Card$1, { className: classes.root }, filterGroups.map((group) => /* @__PURE__ */ React.createElement(Fragment, { key: group.name }, /* @__PURE__ */ React.createElement(
1212
+ Typography,
1126
1213
  {
1127
- disabled: !supportsLoad,
1128
- onClick: () => {
1129
- var _a;
1130
- return (_a = props.onSelect) == null ? void 0 : _a.call(props, "local");
1131
- }
1214
+ variant: "subtitle2",
1215
+ component: "span",
1216
+ className: classes.title
1132
1217
  },
1133
- /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(
1134
- Typography$1,
1135
- {
1136
- variant: "h4",
1137
- component: "h3",
1138
- gutterBottom: true,
1139
- color: supportsLoad ? void 0 : "textSecondary",
1140
- style: { display: "flex", flexFlow: "row nowrap" }
1141
- },
1142
- "Load Template Directory"
1143
- ), /* @__PURE__ */ React.createElement(
1144
- Typography$1,
1145
- {
1146
- variant: "body1",
1147
- color: supportsLoad ? void 0 : "textSecondary"
1148
- },
1149
- "Load a local template directory, allowing you to both edit and try executing your own template."
1150
- ))
1151
- ), !supportsLoad && /* @__PURE__ */ React.createElement("div", { className: classes.infoIcon }, /* @__PURE__ */ React.createElement(
1152
- Tooltip$1,
1218
+ group.name
1219
+ ), /* @__PURE__ */ React.createElement(Card$1, { className: classes.groupWrapper }, /* @__PURE__ */ React.createElement(List$1, { disablePadding: true, dense: true, role: "menu" }, group.items.map((item) => /* @__PURE__ */ React.createElement(
1220
+ MenuItem,
1153
1221
  {
1154
- placement: "top",
1155
- title: "Only supported in some Chromium-based browsers"
1222
+ key: item.id,
1223
+ button: true,
1224
+ divider: true,
1225
+ ContainerProps: { role: "menuitem" },
1226
+ onClick: () => onSelectOwner(item.id),
1227
+ selected: item.id === filter,
1228
+ className: classes.menuItem,
1229
+ "data-testid": `owner-picker-${item.id}`
1156
1230
  },
1157
- /* @__PURE__ */ React.createElement(InfoOutlinedIcon, null)
1158
- )));
1159
- const cardFormEditor = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(CardActionArea, { onClick: () => {
1160
- var _a;
1161
- return (_a = props.onSelect) == null ? void 0 : _a.call(props, "form");
1162
- } }, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h4", component: "h3", gutterBottom: true }, "Edit Template Form"), /* @__PURE__ */ React.createElement(Typography$1, { variant: "body1" }, "Preview and edit a template form, either using a sample template or by loading a template from the catalog."))));
1163
- const cardFieldExplorer = /* @__PURE__ */ React.createElement(Card$1, { className: classes.card, elevation: 4 }, /* @__PURE__ */ React.createElement(CardActionArea, { onClick: () => {
1164
- var _a;
1165
- return (_a = props.onSelect) == null ? void 0 : _a.call(props, "field-explorer");
1166
- } }, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h4", component: "h3", gutterBottom: true }, "Custom Field Explorer"), /* @__PURE__ */ React.createElement(Typography$1, { variant: "body1" }, "View and play around with available installed custom field extensions."))));
1167
- return /* @__PURE__ */ React.createElement("div", { style: props.style }, /* @__PURE__ */ React.createElement(Typography$1, { variant: "h4", component: "h2", className: classes.introText }, "Get started by choosing one of the options below"), /* @__PURE__ */ React.createElement(
1168
- "div",
1231
+ item.icon && /* @__PURE__ */ React.createElement(ListItemIcon$1, { className: classes.listIcon }, /* @__PURE__ */ React.createElement(item.icon, { fontSize: "small" })),
1232
+ /* @__PURE__ */ React.createElement(ListItemText$1, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, item.label))
1233
+ )))))));
1234
+ };
1235
+
1236
+ const CreatedAtColumn = ({ createdAt }) => {
1237
+ const createdAtTime = DateTime.fromISO(createdAt);
1238
+ const formatted = Interval.fromDateTimes(createdAtTime, DateTime.local()).toDuration().valueOf();
1239
+ return /* @__PURE__ */ React.createElement(Typography$1, { paragraph: true }, humanizeDuration(formatted, { round: true }), " ago");
1240
+ };
1241
+
1242
+ const OwnerEntityColumn = ({ entityRef }) => {
1243
+ var _a, _b, _c;
1244
+ const catalogApi = useApi(catalogApiRef);
1245
+ const { value, loading, error } = useAsync(
1246
+ () => catalogApi.getEntityByRef(entityRef || ""),
1247
+ [catalogApi, entityRef]
1248
+ );
1249
+ if (!entityRef) {
1250
+ return /* @__PURE__ */ React.createElement(Typography$1, { paragraph: true }, "Unknown");
1251
+ }
1252
+ if (loading || error) {
1253
+ return null;
1254
+ }
1255
+ return /* @__PURE__ */ React.createElement(
1256
+ EntityRefLink,
1169
1257
  {
1170
- style: {
1171
- display: "flex",
1172
- flexFlow: "row wrap",
1173
- alignItems: "flex-start",
1174
- justifyContent: "center",
1175
- alignContent: "flex-start"
1258
+ entityRef: parseEntityRef(entityRef),
1259
+ title: (_c = (_b = (_a = value == null ? void 0 : value.spec) == null ? void 0 : _a.profile) == null ? void 0 : _b.displayName) != null ? _c : value == null ? void 0 : value.metadata.name
1260
+ }
1261
+ );
1262
+ };
1263
+
1264
+ const TaskStatusColumn = ({ status }) => {
1265
+ switch (status) {
1266
+ case "processing":
1267
+ return /* @__PURE__ */ React.createElement(StatusPending, null, status);
1268
+ case "completed":
1269
+ return /* @__PURE__ */ React.createElement(StatusOK, null, status);
1270
+ case "error":
1271
+ default:
1272
+ return /* @__PURE__ */ React.createElement(StatusError, null, status);
1273
+ }
1274
+ };
1275
+
1276
+ const TemplateTitleColumn = ({ entityRef }) => {
1277
+ const scaffolder = useApi(scaffolderApiRef);
1278
+ const { value, loading, error } = useAsync(
1279
+ () => scaffolder.getTemplateParameterSchema(entityRef || ""),
1280
+ [scaffolder, entityRef]
1281
+ );
1282
+ if (loading || error || !entityRef) {
1283
+ return null;
1284
+ }
1285
+ return /* @__PURE__ */ React.createElement(EntityRefLink, { entityRef: parseEntityRef(entityRef), title: value == null ? void 0 : value.title });
1286
+ };
1287
+
1288
+ const ListTaskPageContent = (props) => {
1289
+ var _a;
1290
+ const { initiallySelectedFilter = "owned" } = props;
1291
+ const scaffolderApi = useApi(scaffolderApiRef);
1292
+ const rootLink = useRouteRef(rootRouteRef);
1293
+ const [ownerFilter, setOwnerFilter] = useState(initiallySelectedFilter);
1294
+ const { value, loading, error } = useAsync(() => {
1295
+ var _a2;
1296
+ if (scaffolderApi.listTasks) {
1297
+ return (_a2 = scaffolderApi.listTasks) == null ? void 0 : _a2.call(scaffolderApi, { filterByOwnership: ownerFilter });
1298
+ }
1299
+ console.warn(
1300
+ "listTasks is not implemented in the scaffolderApi, please make sure to implement this method."
1301
+ );
1302
+ return Promise.resolve({ tasks: [] });
1303
+ }, [scaffolderApi, ownerFilter]);
1304
+ if (loading) {
1305
+ return /* @__PURE__ */ React.createElement(Progress, null);
1306
+ }
1307
+ if (error) {
1308
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ErrorPanel, { error }), /* @__PURE__ */ React.createElement(
1309
+ EmptyState,
1310
+ {
1311
+ missing: "info",
1312
+ title: "No information to display",
1313
+ description: "There is no Tasks or there was an issue communicating with backend."
1176
1314
  }
1177
- },
1178
- supportsLoad && cardLoadLocal,
1179
- cardFormEditor,
1180
- !supportsLoad && cardLoadLocal,
1181
- cardFieldExplorer
1182
- ));
1183
- }
1315
+ ));
1316
+ }
1317
+ return /* @__PURE__ */ React.createElement(CatalogFilterLayout, null, /* @__PURE__ */ React.createElement(CatalogFilterLayout.Filters, null, /* @__PURE__ */ React.createElement(
1318
+ OwnerListPicker,
1319
+ {
1320
+ filter: ownerFilter,
1321
+ onSelectOwner: (id) => setOwnerFilter(id)
1322
+ }
1323
+ )), /* @__PURE__ */ React.createElement(CatalogFilterLayout.Content, null, /* @__PURE__ */ React.createElement(
1324
+ Table$1,
1325
+ {
1326
+ data: (_a = value == null ? void 0 : value.tasks) != null ? _a : [],
1327
+ title: "Tasks",
1328
+ columns: [
1329
+ {
1330
+ title: "Task ID",
1331
+ field: "id",
1332
+ render: (row) => /* @__PURE__ */ React.createElement(Link, { to: `${rootLink()}/tasks/${row.id}` }, row.id)
1333
+ },
1334
+ {
1335
+ title: "Template",
1336
+ render: (row) => {
1337
+ var _a2;
1338
+ return /* @__PURE__ */ React.createElement(
1339
+ TemplateTitleColumn,
1340
+ {
1341
+ entityRef: (_a2 = row.spec.templateInfo) == null ? void 0 : _a2.entityRef
1342
+ }
1343
+ );
1344
+ }
1345
+ },
1346
+ {
1347
+ title: "Created",
1348
+ field: "createdAt",
1349
+ render: (row) => /* @__PURE__ */ React.createElement(CreatedAtColumn, { createdAt: row.createdAt })
1350
+ },
1351
+ {
1352
+ title: "Owner",
1353
+ field: "createdBy",
1354
+ render: (row) => {
1355
+ var _a2, _b;
1356
+ return /* @__PURE__ */ React.createElement(OwnerEntityColumn, { entityRef: (_b = (_a2 = row.spec) == null ? void 0 : _a2.user) == null ? void 0 : _b.ref });
1357
+ }
1358
+ },
1359
+ {
1360
+ title: "Status",
1361
+ field: "status",
1362
+ render: (row) => /* @__PURE__ */ React.createElement(TaskStatusColumn, { status: row.status })
1363
+ }
1364
+ ]
1365
+ }
1366
+ )));
1367
+ };
1368
+ const ListTasksPage = (props) => {
1369
+ return /* @__PURE__ */ React.createElement(Page, { themeId: "home" }, /* @__PURE__ */ React.createElement(
1370
+ Header,
1371
+ {
1372
+ pageTitleOverride: "Templates Tasks",
1373
+ title: /* @__PURE__ */ React.createElement(React.Fragment, null, "List template tasks ", /* @__PURE__ */ React.createElement(Lifecycle, { shorthand: true, alpha: true })),
1374
+ subtitle: "All tasks that have been started"
1375
+ }
1376
+ ), /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ListTaskPageContent, { ...props })));
1377
+ };
1184
1378
 
1185
- export { ActionsPage as A, DirectoryEditorProvider as D, OwnerListPicker as O, TemplateEditorBrowser as T, WebFileSystemAccess as W, useDirectoryEditor as a, DryRunProvider as b, TemplateEditorTextArea as c, DryRunResults as d, TemplateEditorIntro as e, useDryRun as u };
1186
- //# sourceMappingURL=TemplateEditorIntro-580933c6.esm.js.map
1379
+ export { ActionsPage as A, DirectoryEditorProvider as D, ListTasksPage as L, TemplateEditorBrowser as T, WebFileSystemAccess as W, useDirectoryEditor as a, DryRunProvider as b, TemplateEditorTextArea as c, DryRunResults as d, TemplateEditorIntro as e, DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS as f, useDryRun as u };
1380
+ //# sourceMappingURL=ListTasksPage-44381650.esm.js.map