diff --git a/packages/open-workflow-diagram-editor/src/core/taskDetails.ts b/packages/open-workflow-diagram-editor/src/core/taskDetails.ts index 1e849281..12396185 100644 --- a/packages/open-workflow-diagram-editor/src/core/taskDetails.ts +++ b/packages/open-workflow-diagram-editor/src/core/taskDetails.ts @@ -24,7 +24,11 @@ const MAX_DEPTH = 4; /* Flattened task row - kind: how the view should render it */ export type DetailField = - | { path: string; kind: "text"; display: string } + | { path: string; kind: "scalar"; value: string | number | boolean } + | { path: string; kind: "enum"; value: string; options: string[] } + | { path: string; kind: "runtime-expression"; value: string } + | { path: string; kind: "duration"; value: string } + | { path: string; kind: "long-string"; value: string } | { path: string; kind: "array"; count: number } | { path: string; kind: "object" }; @@ -32,6 +36,27 @@ function isPlainObject(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } +// This is temporary function until we dynamically build the form with field types +function isLongStringField(path: string): boolean { + return path === "run.shell.command" || path === "run.script.code"; +} + +function isRuntimeExpressionField(path: string): boolean { + return path === "if"; +} + +function isDurationField(path: string): boolean { + return path === "timeout" || path === "timeout.after"; +} + +const ENUM_FIELDS: Record = { + "with.output": ["raw", "content", "response"], +}; + +function getEnumOptions(path: string): string[] | undefined { + return ENUM_FIELDS[path]; +} + function flattenFields( value: unknown, path: string = "", @@ -57,9 +82,58 @@ function flattenFields( for (const [key, val] of Object.entries(value)) { flattenFields(val, path ? `${path}.${key}` : key, depth + 1, outputFields); } + + return; + } + + if (typeof value === "string" && isLongStringField(path)) { + outputFields.push({ + path, + kind: "long-string", + value, + }); return; } - outputFields.push({ path, kind: "text", display: String(value) }); + + if (typeof value === "string" && isRuntimeExpressionField(path)) { + outputFields.push({ + path, + kind: "runtime-expression", + value, + }); + return; + } + + if (typeof value === "string" && isDurationField(path)) { + outputFields.push({ + path, + kind: "duration", + value, + }); + return; + } + + if (typeof value === "string") { + const options = getEnumOptions(path); + + if (options) { + outputFields.push({ + path, + kind: "enum", + value, + options, + }); + return; + } + } + + if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { + outputFields.push({ + path, + kind: "scalar", + value, + }); + } } /* Builds the flattened detail rows for a task: task-specific fields first, inherited base fields last */ diff --git a/packages/open-workflow-diagram-editor/src/i18n/locales/en.ts b/packages/open-workflow-diagram-editor/src/i18n/locales/en.ts index 55298bd5..170c4b5f 100644 --- a/packages/open-workflow-diagram-editor/src/i18n/locales/en.ts +++ b/packages/open-workflow-diagram-editor/src/i18n/locales/en.ts @@ -55,6 +55,9 @@ export const en = { "toast.clipboard.error": "Failed to copy", "toast.download.success": "Download started", "toast.download.error": "Download failed", + "sidebar.duration.title": "Enter an ISO 8601 duration, for example PT30S or PT5M", + "sidebar.field.item": "item", + "sidebar.field.items": "items", } as const; export type TranslationKeys = keyof typeof en; diff --git a/packages/open-workflow-diagram-editor/src/side-panel/FieldControls.tsx b/packages/open-workflow-diagram-editor/src/side-panel/FieldControls.tsx new file mode 100644 index 00000000..e6df46da --- /dev/null +++ b/packages/open-workflow-diagram-editor/src/side-panel/FieldControls.tsx @@ -0,0 +1,135 @@ +/* + * Copyright 2021-Present The Open Workflow Specification Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { DetailField } from "@/core/taskDetails"; +import { + Combobox, + ComboboxContent, + ComboboxItem, + ComboboxList, + ComboboxTrigger, + ComboboxValue, +} from "@/components/ui/combobox"; +import { Input } from "@/components/ui/input"; +import { Switch } from "@/components/ui/switch"; +import { Textarea } from "@/components/ui/textarea"; +import { useI18n } from "@openworkflowspec/i18n"; + +type ControlProps = { + field: Extract; + isReadOnly: boolean; +}; + +const ISO_8601_DURATION_REGEX = + /^P(?=\d|T)(?:\d+Y)?(?:\d+M)?(?:\d+W)?(?:\d+D)?(?:T(?=\d)(?:\d+H)?(?:\d+M)?(?:\d+(?:\.\d+)?S)?)?$/; + +function LongStringControl({ field, isReadOnly }: ControlProps<"long-string">) { + return