diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormRawJsonFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormRawJsonFieldInput.tsx
index 14f227e11..588d210c4 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormRawJsonFieldInput.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormRawJsonFieldInput.tsx
@@ -8,11 +8,14 @@ import { InputLabel } from '@/ui/input/components/InputLabel';
import { useId } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { turnIntoEmptyStringIfWhitespacesOnly } from '~/utils/string/turnIntoEmptyStringIfWhitespacesOnly';
+import { InputErrorHelper } from '@/ui/input/components/InputErrorHelper';
type FormRawJsonFieldInputProps = {
label?: string;
+ error?: string;
defaultValue: string | null | undefined;
onChange: (value: string | null) => void;
+ onBlur?: () => void;
readonly?: boolean;
VariablePicker?: VariablePickerComponent;
placeholder?: string;
@@ -20,9 +23,11 @@ type FormRawJsonFieldInputProps = {
export const FormRawJsonFieldInput = ({
label,
+ error,
defaultValue,
placeholder,
onChange,
+ onBlur,
readonly,
VariablePicker,
}: FormRawJsonFieldInputProps) => {
@@ -68,6 +73,7 @@ export const FormRawJsonFieldInput = ({
@@ -80,6 +86,7 @@ export const FormRawJsonFieldInput = ({
/>
)}
+ {error}
);
};
diff --git a/packages/twenty-front/src/modules/ui/input/components/InputErrorHelper.tsx b/packages/twenty-front/src/modules/ui/input/components/InputErrorHelper.tsx
index 9d20e6962..5f3d46909 100644
--- a/packages/twenty-front/src/modules/ui/input/components/InputErrorHelper.tsx
+++ b/packages/twenty-front/src/modules/ui/input/components/InputErrorHelper.tsx
@@ -5,7 +5,7 @@ const StyledInputErrorHelper = styled.div`
color: ${({ theme }) => theme.color.red};
font-size: ${({ theme }) => theme.font.size.xs};
position: absolute;
- top: calc(100% + ${({ theme }) => theme.spacing(0.25)});
+ margin-top: ${({ theme }) => theme.spacing(0.25)};
`;
export const InputErrorHelper = ({
diff --git a/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts b/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts
index 3215ca6d3..3496f7ae7 100644
--- a/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts
+++ b/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts
@@ -209,9 +209,19 @@ export const workflowCronTriggerSchema = baseTriggerSchema.extend({
export const workflowWebhookTriggerSchema = baseTriggerSchema.extend({
type: z.literal('WEBHOOK'),
- settings: z.object({
- outputSchema: z.object({}).passthrough(),
- }),
+ settings: z.discriminatedUnion('httpMethod', [
+ z.object({
+ outputSchema: z.object({}).passthrough(),
+ httpMethod: z.literal('GET'),
+ authentication: z.literal('API_KEY').nullable(),
+ }),
+ z.object({
+ outputSchema: z.object({}).passthrough(),
+ httpMethod: z.literal('POST'),
+ expectedBody: z.object({}).passthrough(),
+ authentication: z.literal('API_KEY').nullable(),
+ }),
+ ]),
});
// Combined trigger schema
diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerWebhookForm.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerWebhookForm.tsx
index e818579c4..9436d8e5f 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerWebhookForm.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerWebhookForm.tsx
@@ -14,6 +14,13 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { isDefined } from 'twenty-shared/utils';
import { useIcons, IconCopy } from 'twenty-ui/display';
+import { Select } from '@/ui/input/components/Select';
+import { WEBHOOK_TRIGGER_HTTP_METHOD_OPTIONS } from '@/workflow/workflow-trigger/constants/WebhookTriggerHttpMethodOptions';
+import { getWebhookTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getWebhookTriggerDefaultSettings';
+import { WEBHOOK_TRIGGER_AUTHENTICATION_OPTIONS } from '@/workflow/workflow-trigger/constants/WebhookTriggerAuthenticationOptions';
+import { FormRawJsonFieldInput } from '@/object-record/record-field/form-types/components/FormRawJsonFieldInput';
+import { useState } from 'react';
+import { getFunctionOutputSchema } from '@/serverless-functions/utils/getFunctionOutputSchema';
type WorkflowEditTriggerWebhookFormProps = {
trigger: WorkflowWebhookTrigger;
@@ -24,9 +31,17 @@ type WorkflowEditTriggerWebhookFormProps = {
}
| {
readonly?: false;
- onTriggerUpdate: (trigger: WorkflowWebhookTrigger) => void;
+ onTriggerUpdate: (
+ trigger: WorkflowWebhookTrigger,
+ options?: { computeOutputSchema: boolean },
+ ) => void;
};
};
+
+type FormErrorMessages = {
+ expectedBody?: string | undefined;
+};
+
export const WorkflowEditTriggerWebhookForm = ({
trigger,
triggerOptions,
@@ -34,10 +49,16 @@ export const WorkflowEditTriggerWebhookForm = ({
const { enqueueSnackBar } = useSnackBar();
const theme = useTheme();
const { t } = useLingui();
+ const [errorMessages, setErrorMessages] = useState({});
+ const [errorMessagesVisible, setErrorMessagesVisible] = useState(false);
const { getIcon } = useIcons();
const workflowId = useRecoilValue(workflowIdState);
const currentWorkspace = useRecoilValue(currentWorkspaceState);
+ const onBlur = () => {
+ setErrorMessagesVisible(true);
+ };
+
const headerTitle = isDefined(trigger.name) ? trigger.name : 'Webhook';
const headerIcon = getTriggerIcon({
@@ -93,6 +114,97 @@ export const WorkflowEditTriggerWebhookForm = ({
onRightIconClick={copyToClipboardDebounced}
readOnly
/>
+