From 849f5995b5c8e59deb5e160b08d29cb648621769 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 20 Mar 2026 14:10:22 +0100 Subject: [PATCH] scaffolder: deduplicate OngoingTask and pick up page formFields via app tree Signed-off-by: Patrik Oldsberg Made-with: Cursor --- plugins/scaffolder/src/alpha/formFieldsApi.ts | 35 ++++++++++---- .../components/OngoingTask/OngoingTask.tsx | 48 ++++++++----------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/plugins/scaffolder/src/alpha/formFieldsApi.ts b/plugins/scaffolder/src/alpha/formFieldsApi.ts index b065af9a82..03d6f0796a 100644 --- a/plugins/scaffolder/src/alpha/formFieldsApi.ts +++ b/plugins/scaffolder/src/alpha/formFieldsApi.ts @@ -16,6 +16,7 @@ import { ApiBlueprint, + appTreeApiRef, createExtensionInput, } from '@backstage/frontend-plugin-api'; import { @@ -39,18 +40,18 @@ export const formFieldsApi = ApiBlueprint.makeWithOverrides({ return originalFactory(defineParams => defineParams({ api: formFieldsApiRef, - deps: {}, - factory: () => ({ + deps: { appTreeApi: appTreeApiRef }, + factory: ({ appTreeApi }) => ({ async loadFormFields() { + const pageFormFieldLoaders = getPageFormFieldLoaders(appTreeApi); + const formFields = await Promise.all( - formFieldLoaders.map(loader => loader()), + [...formFieldLoaders, ...pageFormFieldLoaders].map(loader => + loader(), + ), ); - const internalFormFields = formFields.map( - OpaqueFormField.toInternal, - ); - - return internalFormFields; + return formFields.map(OpaqueFormField.toInternal); }, }), }), @@ -58,6 +59,24 @@ export const formFieldsApi = ApiBlueprint.makeWithOverrides({ }, }); +function getPageFormFieldLoaders(appTreeApi: typeof appTreeApiRef.T) { + const { tree } = appTreeApi.getTree(); + const pageNode = tree.nodes.get('page:scaffolder'); + if (!pageNode?.instance) { + return []; + } + const formFieldNodes = pageNode.edges.attachments.get('formFields') ?? []; + return formFieldNodes.flatMap(node => { + if (!node.instance) { + return []; + } + const loader = node.instance.getData( + FormFieldBlueprint.dataRefs.formFieldLoader, + ); + return loader ? [loader] : []; + }); +} + export { formFieldsApiRef, type ScaffolderFormFieldsApi, diff --git a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx index 9f8b2a958a..16e4bc9158 100644 --- a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx +++ b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx @@ -15,6 +15,7 @@ */ import { ComponentType, + ReactNode, useCallback, useEffect, useMemo, @@ -79,15 +80,7 @@ const useStyles = makeStyles(theme => ({ }, })); -/** - * @public - */ -export const OngoingTask = (props: { - TemplateOutputsComponent?: ComponentType<{ - output?: ScaffolderTaskOutput; - }>; -}) => { - // todo(blam): check that task Id actually exists, and that it's valid. otherwise redirect to something more useful. +function OngoingTaskAnalyticsContext(props: { children: ReactNode }) { const { taskId } = useParams(); const taskStream = useTaskEventStream(taskId!); const { namespace, name } = @@ -106,10 +99,25 @@ export const OngoingTask = (props: { taskId, }} > + {props.children} + + ); +} + +/** + * @public + */ +export const OngoingTask = (props: { + TemplateOutputsComponent?: ComponentType<{ + output?: ScaffolderTaskOutput; + }>; +}) => { + return ( + - + ); }; @@ -124,26 +132,10 @@ export function OngoingTaskBody(props: { output?: ScaffolderTaskOutput; }>; }) { - const { taskId } = useParams(); - const taskStream = useTaskEventStream(taskId!); - const { namespace, name } = - taskStream.task?.spec.templateInfo?.entity?.metadata ?? {}; - return ( - + - + ); }