From 2a49a720d258806fd723ff7daed47e338eaa15ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Garc=C3=ADa?= Date: Thu, 22 Jan 2026 16:08:22 +0100 Subject: [PATCH 1/6] feat: add integration logos --- forms-bridge/includes/class-addon.php | 6 +----- forms-bridge/includes/class-integration.php | 9 +++++++++ .../integrations/formidable/assets/logo.png | Bin 0 -> 6692 bytes forms-bridge/integrations/gf/assets/logo.png | Bin 0 -> 5015 bytes forms-bridge/integrations/ninja/assets/logo.png | Bin 0 -> 7951 bytes forms-bridge/integrations/woo/assets/logo.png | Bin 0 -> 17563 bytes forms-bridge/integrations/wpcf7/assets/logo.png | Bin 0 -> 6986 bytes .../integrations/wpforms/assets/logo.png | Bin 0 -> 8127 bytes 8 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 forms-bridge/integrations/formidable/assets/logo.png create mode 100644 forms-bridge/integrations/gf/assets/logo.png create mode 100644 forms-bridge/integrations/ninja/assets/logo.png create mode 100644 forms-bridge/integrations/woo/assets/logo.png create mode 100644 forms-bridge/integrations/wpcf7/assets/logo.png create mode 100644 forms-bridge/integrations/wpforms/assets/logo.png diff --git a/forms-bridge/includes/class-addon.php b/forms-bridge/includes/class-addon.php index a722cb9d..f219f881 100644 --- a/forms-bridge/includes/class-addon.php +++ b/forms-bridge/includes/class-addon.php @@ -197,11 +197,7 @@ function ( $data ) { $registry = self::registry(); $addons = array(); foreach ( self::$addons as $name => $addon ) { - $logo_path = - FORMS_BRIDGE_ADDONS_DIR . - '/' . - $addon::NAME . - '/assets/logo.png'; + $logo_path = FORMS_BRIDGE_ADDONS_DIR . '/' . $addon::NAME . '/assets/logo.png'; if ( is_file( $logo_path ) && is_readable( $logo_path ) ) { $logo = plugin_dir_url( $logo_path ) . 'logo.png'; diff --git a/forms-bridge/includes/class-integration.php b/forms-bridge/includes/class-integration.php index 1e7d9335..26cc564a 100644 --- a/forms-bridge/includes/class-integration.php +++ b/forms-bridge/includes/class-integration.php @@ -197,10 +197,19 @@ function ( $data ) { $registry = self::registry(); $integrations = array(); foreach ( self::$integrations as $name => $integration ) { + $logo_path = FORMS_BRIDGE_INTEGRATIONS_DIR . '/' . $integration::NAME . '/assets/logo.png'; + + if ( is_file( $logo_path ) && is_readable( $logo_path ) ) { + $logo = plugin_dir_url( $logo_path ) . 'logo.png'; + } else { + $logo = ''; + } + $integrations[ $name ] = array( 'name' => $name, 'title' => $integration::TITLE, 'enabled' => $registry[ $name ] ?? false, + 'logo' => $logo, ); } diff --git a/forms-bridge/integrations/formidable/assets/logo.png b/forms-bridge/integrations/formidable/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d4dcb15fea71072868c9ed0d0a2a4d25db5b18c0 GIT binary patch literal 6692 zcmeHLc{r5o`=3F!QDMkdyoO3?%!-*Yw#GIX30Y1|#>`6-X2vWGk~KvtT14q2MYJcf zlg7@`~KI=^}h2y_wsq}`}5q-eLe4Fd3(9e zR@GO9!C6c{mTej}Y?xHWh)75`Y%()N4o&1-R;@>!Zd$XVR-&%jQMs=Aw=Fc!oV^Hi zs+H~*HN)ORnIgj;x%g~bA+tRGQ^y0Y`oX&2PP$AjixRb%PQSjq86|jDKJKa?q>pok z_ZMHRakmUPwPxTAp2l`ue68;3f$6dS#ATo6AfM(07ZonlA8uSK5T*7!+*Dt3usf)r z?~2k3zL(OlJRB3W_(U~q3fFNccB(JNzIaHKYH4T`3+T92C@Ya3~MuqY(=ZQgixJHH5FfuIti z%6wn8vBJ*uUUD}$TT-F1Lc`UMab&&mgEL+6Th98QtbAnq4nZR)fraB$F1gL9e84dy zZK=CitIMv~{aT16ljoa>;q0-2B|D z^3Nh(F@i#e=RFR3`RGq-`>1?wSiwdx0 zU>O1$xR&ddAOw9Ay!_Y+k!&&tWpAfSl~N#pXi&@oq|s445k+c?n!%+&&oVIv188Eo!Y;ETCX8adB}LaRdv# zFam=klgSt?9)riDAq%u9o+oBW(L9lf4B`uh6DVQ}xdJhl&jVzbtT4VrY>Prcdf=b< zq6G}bH+Y`tD+>@G7%5AD!C7E2(b1S6Jw#&XSP0~6K>yK0Gsch=gF$iPvn8_d=uWmM+58j^pUve^W*)<_I6NN2;n0>u zQaGB34-ZF^aMnaLn`lMAk;4eMFdX>@C^}ChX7Sjd36#gRd=2v{7BXoV(+g@b4!$C8W= z1KBJzi9{e0*jPM=jJKYF;;<<$d|@;TT25{>D+0s_co8!S8Q~O1Z@MiCZ-Mz)sEV&z31kE2mBl@zVe8HG6qc6iK?#xyK zfSFA}VX?oYAY#RWoS8TwtS>6|S{5$?gu2Jqa{Win{ZFNUWn(R^iFl|A*jO~t5=TI@ z!r6Fq7}?s=l1Sj-tTL;2 zLw_fX@Rcyk*Mu>$p7Bq{RLuY4ggRsJLz990zQ~~F1@%JAw`Ta2Gg;gDFMhtR#eZ=I z2>RC~|BByVbp4|1Uor5njDJ7edFqL0Z5`Ea0?Q_U;>^J;u~r)L=4EE5yVk=D3LJOI8Q1K9A zH_RXQ`z{}IxT%_1k8<|Pl$u((Nm%HOfN{;&>e*V&

_b_l45gEyBNQ-Yy)|(Djuw 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 TSKqfm(-NX1VnqkSm#_X0VTxve literal 0 HcmV?d00001 diff --git a/forms-bridge/integrations/ninja/assets/logo.png b/forms-bridge/integrations/ninja/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..563d43e0a6981f2955b3e3efc0734e59ea31a09b GIT binary patch literal 7951 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 PmJA}9S`zN!j)ngZa(y0? literal 0 HcmV?d00001 diff --git a/forms-bridge/integrations/woo/assets/logo.png b/forms-bridge/integrations/woo/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f2b8c877090ae751764d8b0797767531531e19f2 GIT binary patch literal 17563 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 literal 0 HcmV?d00001 diff --git a/forms-bridge/integrations/wpcf7/assets/logo.png b/forms-bridge/integrations/wpcf7/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bb9de3ae59399a6b4c18cc222d9b99861da270c7 GIT binary patch literal 6986 zcmeHMc{r5o`yWw?P_h(5W2A^Ni*06*>^qa4P_w+lU}l(k?K?#z`$=VMqaz_y)(Dl8 ztWio@P9#fFib~}7Qm50ozTfNmUFW)fzyF$R-sgRu`?>GWbKjr)exB=nXOc)Zra}Tz z0x%d%$lT1x4!YMtSJir6s8zaBPzK#v!W>*cJ903>pTnjz`~U

JK1*5C$Cv3mIaP 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 literal 0 HcmV?d00001 diff --git a/forms-bridge/integrations/wpforms/assets/logo.png b/forms-bridge/integrations/wpforms/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8762c451619e298751a937ce437f9fd2acaedcfe GIT binary patch literal 8127 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 bzH{@=K$K?P3o+nO2{JY?)4#UgHR^u=9LPx~ literal 0 HcmV?d00001 From 2f472a4d82b38545546594e345abe1029dd05d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Garc=C3=ADa?= Date: Thu, 22 Jan 2026 16:08:50 +0100 Subject: [PATCH 2/6] feat: sort fields on do submission --- forms-bridge/includes/class-form-bridge.php | 14 ++++++-------- forms-bridge/includes/class-forms-bridge.php | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/forms-bridge/includes/class-form-bridge.php b/forms-bridge/includes/class-form-bridge.php index a3a3afd3..4406579f 100644 --- a/forms-bridge/includes/class-form-bridge.php +++ b/forms-bridge/includes/class-form-bridge.php @@ -194,21 +194,19 @@ public static function schema( $addon = null ) { 'default' => array(), ), 'is_valid' => array( - 'description' => __( - 'Validation result of the bridge setting', - 'forms-bridge' - ), + 'description' => __( 'Validation result of the bridge setting', 'forms-bridge' ), 'type' => 'boolean', 'default' => true, ), 'enabled' => array( - 'description' => __( - 'Boolean flag to enable/disable a bridge', - 'forms-bridge' - ), + 'description' => __( 'Boolean flag to enable/disable a bridge', 'forms-bridge' ), 'type' => 'boolean', 'default' => true, ), + 'order' => array( + 'description' => __( 'Order in which the bridge should be submitted in the submission loop', 'forms-bridge' ), + 'type' => 'integer', + ), ), 'required' => array( 'name', diff --git a/forms-bridge/includes/class-forms-bridge.php b/forms-bridge/includes/class-forms-bridge.php index 890f1e8c..c048692a 100644 --- a/forms-bridge/includes/class-forms-bridge.php +++ b/forms-bridge/includes/class-forms-bridge.php @@ -283,6 +283,20 @@ function ( $field ) { return; } + $sorted_bridges = array(); + foreach ( $bridges as $bridge ) { + $order = is_int( $bridge->order ) ? $bridge->order : 100 + count( $sorted_bridges ); + + while ( isset( $sorted_bridges[ $order ] ) ) { + ++$order; + } + + $sorted_bridges[ $order ] = $bridge; + } + + ksort( $sorted_bridges ); + $bridges = array_values( $sorted_bridges ); + foreach ( $bridges as $bridge ) { if ( ! $bridge->enabled ) { Logger::log( From 7fbd7cc6b761c2b9a72b9742328e0ba808b4567d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Garc=C3=ADa?= Date: Thu, 22 Jan 2026 16:09:48 +0100 Subject: [PATCH 3/6] feat: bridged forms admin tab --- src/components/Backend/index.jsx | 2 +- src/components/Bridge/index.jsx | 2 +- src/components/Bridges/index.jsx | 4 +- src/components/Credential/index.jsx | 2 +- src/components/Form/Bridges.jsx | 163 ++++++++++++++++++++++++++++ src/components/Form/Fields.jsx | 42 +++++++ src/components/Form/index.jsx | 79 ++++++++++++++ src/components/Forms/index.jsx | 80 ++++++++++++++ src/components/Jobs/index.jsx | 2 +- src/components/Settings/index.jsx | 37 +++++-- src/hooks/useBridges.js | 34 +++++- src/providers/Forms.jsx | 6 +- src/providers/Jobs.jsx | 2 +- src/providers/Schemas.jsx | 3 +- 14 files changed, 435 insertions(+), 23 deletions(-) create mode 100644 src/components/Form/Bridges.jsx create mode 100644 src/components/Form/Fields.jsx create mode 100644 src/components/Form/index.jsx create mode 100644 src/components/Forms/index.jsx diff --git a/src/components/Backend/index.jsx b/src/components/Backend/index.jsx index d82123d2..53288db1 100644 --- a/src/components/Backend/index.jsx +++ b/src/components/Backend/index.jsx @@ -120,7 +120,7 @@ export default function Backend({ update, remove, data, copy }) { padding: "6px 6px", }} onClick={copy} - label={__("Duplaicate", "forms-bridge")} + label={__("Duplicate", "forms-bridge")} showTooltip __next40pxDefaultSize > diff --git a/src/components/Bridge/index.jsx b/src/components/Bridge/index.jsx index 371dbb5c..c671d23b 100644 --- a/src/components/Bridge/index.jsx +++ b/src/components/Bridge/index.jsx @@ -251,7 +251,7 @@ export default function Bridge({ data, update, remove, schema, copy, names }) { padding: "6px 6px", }} onClick={copy} - label={__("Duplaicate", "forms-bridge")} + label={__("Duplicate", "forms-bridge")} showTooltip __next40pxDefaultSize > diff --git a/src/components/Bridges/index.jsx b/src/components/Bridges/index.jsx index dd3ea15f..ee1ee04b 100644 --- a/src/components/Bridges/index.jsx +++ b/src/components/Bridges/index.jsx @@ -12,8 +12,8 @@ const { TabPanel } = wp.components; const { useEffect, useMemo, useRef } = wp.element; const { __ } = wp.i18n; -const CSS = `.bridges-tabs-panel .components-tab-panel__tabs{overflow-x:auto;} -.bridges-tabs-panel .components-tab-panel__tabs>button{flex-shrink:0;}`; +const CSS = `.bridges-tabs-panel>.components-tab-panel__tabs{overflow-x:auto;} +.bridges-tabs-panel>.components-tab-panel__tabs>button{flex-shrink:0;}`; const DEFAULTS = { name: "bridge-" + Date.now(), diff --git a/src/components/Credential/index.jsx b/src/components/Credential/index.jsx index f0d08b24..2011337c 100644 --- a/src/components/Credential/index.jsx +++ b/src/components/Credential/index.jsx @@ -172,7 +172,7 @@ export default function Credential({ padding: "6px 6px", }} onClick={copy} - label={__("Duplaicate", "forms-bridge")} + label={__("Duplicate", "forms-bridge")} showTooltip __next40pxDefaultSize > diff --git a/src/components/Form/Bridges.jsx b/src/components/Form/Bridges.jsx new file mode 100644 index 00000000..4fbec810 --- /dev/null +++ b/src/components/Form/Bridges.jsx @@ -0,0 +1,163 @@ +const { + Button, + Modal, + __experimentalItemGroup: ItemGroup, + __experimentalItem: Item, +} = wp.components; +const { useState, useMemo } = wp.element; +const { __ } = wp.i18n; + +export default function FormBridges({ bridges, setBridges }) { + const [open, setOpen] = useState(false); + + const orderedBridges = useMemo(() => { + return bridges.sort((a, b) => { + if (isNaN(a.order)) return 1; + if (isNaN(b.order)) return -1; + return a.order - b.order; + }); + }, [bridges]); + + const move = (from, to) => { + const bridge = orderedBridges[from]; + + const slicedBridges = orderedBridges + .slice(0, from) + .concat(orderedBridges.slice(from + 1)); + + const newBridges = slicedBridges + .slice(0, to) + .concat(bridge) + .concat(slicedBridges.slice(to)); + + newBridges.forEach((bridge, index) => (bridge.order = index)); + setBridges(newBridges); + }; + + return ( + <> + + {open && ( + setOpen(false)} + > +

+ {__("Manage the chain of bridges of the form", "forms-bridge")} +

+
+
+ + {orderedBridges.map((bridge, i) => ( + + move(i, i + direction)} + isLast={i === bridges.length - 1} + /> + + ))} + +
+
+ + )} + + ); +} + +function BridgeStep({ name, index, move, isLast }) { + return ( +
+

+ {index + 1}. {name} + +

+
+ + +
+
+ ); +} diff --git a/src/components/Form/Fields.jsx b/src/components/Form/Fields.jsx new file mode 100644 index 00000000..49d3643e --- /dev/null +++ b/src/components/Form/Fields.jsx @@ -0,0 +1,42 @@ +// source +import StagePayload from "../Workflow/Payload"; + +const { __ } = wp.i18n; + +const DIFF_MOCK = { + enter: new Set(), + exit: new Set(), + mutated: new Set(), + missing: new Set(), +}; + +export default function FormFields({ fields }) { + return ( +
+
+

{__("Submission", "forms-bridge")}

+
+
+ +
+
+ ); +} diff --git a/src/components/Form/index.jsx b/src/components/Form/index.jsx new file mode 100644 index 00000000..f2754112 --- /dev/null +++ b/src/components/Form/index.jsx @@ -0,0 +1,79 @@ +// source +import { useIntegrations } from "../../hooks/useGeneral"; +import useResponsive from "../../hooks/useResponsive"; +import FormFields from "./Fields"; +import FormBridges from "./Bridges"; + +const { useMemo } = wp.element; + +export default function Form({ data, setBridges }) { + const isResponsive = useResponsive(); + + const [integrations] = useIntegrations(); + const integration = useMemo(() => { + const name = data._id.split(":")[0]; + return integrations.find((integration) => integration.name === name); + }, [integrations, data._id]); + + return ( +
+
+ +

{data.title}

+

{data._id}

+
+
+ +
+
+ +
+
+
+
+ ); +} diff --git a/src/components/Forms/index.jsx b/src/components/Forms/index.jsx new file mode 100644 index 00000000..c02dcce2 --- /dev/null +++ b/src/components/Forms/index.jsx @@ -0,0 +1,80 @@ +// source +import useBridges from "../../hooks/useBridges"; +import { useForms } from "../../providers/Forms"; +import Form from "../Form"; + +const { TabPanel } = wp.components; +const { useEffect, useMemo, useRef } = wp.element; +const { __ } = wp.i18n; + +const CSS = `.forms-tabs-panel>.components-tab-panel__tabs{overflow-x:auto;} +.forms-tabs-panel>.components-tab-panel__tabs>button{flex-shrink:0;}`; + +export default function Forms() { + const [forms] = useForms(); + const [bridges, setBridges] = useBridges(); + + const bridgedForms = useMemo(() => { + return forms.reduce((bridged, form) => { + const formBridges = bridges.filter( + (bridge) => bridge.form_id === form._id + ); + + if (!formBridges.length) return bridged; + return bridged.concat({ ...form, bridges: formBridges }); + }, []); + }, [forms, bridges]); + + const tabs = useMemo(() => { + return bridgedForms.map(({ title }, index) => ({ + index, + name: title, + title, + })); + }, [bridgedForms]); + + const style = useRef(document.createElement("style")); + useEffect(() => { + style.current.appendChild(document.createTextNode(CSS)); + document.head.appendChild(style.current); + + return () => { + document.head.removeChild(style.current); + }; + }, []); + + useEffect(() => { + const img = document.querySelector("#forms .addon-logo"); + if (!img) return; + img.removeAttribute("src"); + }, []); + + const updateBridges = (formBridges) => { + const order = formBridges.map(({ name }) => name); + + const newBridges = bridges.map((bridge) => { + const index = order.findIndex((name) => name === bridge.name); + if (index !== -1) { + return { ...bridge, order: index }; + } + + return bridge; + }); + + setBridges(newBridges); + }; + + return ( +
+

+ {__("Bridged forms", "forms-bridge")} +

+ + {(tab) => { + const form = bridgedForms[tab.index]; + return
; + }} + +
+ ); +} diff --git a/src/components/Jobs/index.jsx b/src/components/Jobs/index.jsx index e7f8bbe4..cfd91c46 100644 --- a/src/components/Jobs/index.jsx +++ b/src/components/Jobs/index.jsx @@ -209,7 +209,7 @@ function JobsContent({ loading, config, setEdit, reset, copy }) { padding: "6px 6px", }} onClick={copy} - label={__("Duplaicate", "forms-bridge")} + label={__("Duplicate", "forms-bridge")} showTooltip __next40pxDefaultSize > diff --git a/src/components/Settings/index.jsx b/src/components/Settings/index.jsx index f4841b48..670863d5 100644 --- a/src/components/Settings/index.jsx +++ b/src/components/Settings/index.jsx @@ -4,6 +4,7 @@ import GeneralSetting from "../General"; import HttpSetting from "../HttpSetting"; import Addon from "../Addon"; import useTab from "../../hooks/useTab"; +import Forms from "../Forms"; const { Card, @@ -15,24 +16,43 @@ const { const { useEffect, useMemo, useRef } = wp.element; const { __ } = wp.i18n; -const CSS = `.settings-tabs-panel .components-tab-panel__tabs{overflow-x:auto;} -.settings-tabs-panel .components-tab-panel__tabs>button{flex-shrink:0;}`; +const CSS = `.settings-tabs-panel>.components-tab-panel__tabs{overflow-x:auto;} +.settings-tabs-panel>.components-tab-panel__tabs>button{flex-shrink:0;}`; export default function Settings() { const [tab, setTab] = useTab(); const [addons] = useAddons(); + const tabRef = useRef(tab); + const panelRef = useRef(); + const tabs = useMemo(() => { + const tabs = [ + { name: "general", title: __("General", "forms-bridge") }, + { name: "http", title: __("HTTP", "forms-bridge") }, + { name: "forms", title: __("Forms", "forms-bridge") }, + ]; + const addonTabs = addons .filter(({ enabled }) => enabled) .map(({ name, title }) => ({ name, title })); - return [ - { name: "general", title: __("General", "forms-bridge") }, - { name: "http", title: __("HTTP", "forms-bridge") }, - ].concat(addonTabs); + return tabs.concat(addonTabs); }, [addons]); + const onSelectTab = (tab) => { + tabRef.current = tab; + setTab(tab); + }; + + useEffect(() => { + if (tab === tabRef.current || !panelRef.current) return; + + const index = tabs.findIndex(({ name }) => tab === name); + const button = panelRef.current.querySelectorAll("button")[index]; + button.click(); + }, [tab, tabs]); + const style = useRef(document.createElement("style")); useEffect(() => { style.current.appendChild(document.createTextNode(CSS)); @@ -47,9 +67,10 @@ export default function Settings() {
{(tab) => (
@@ -74,6 +95,8 @@ export default function Settings() { ) : tab.name === "http" ? ( + ) : tab.name === "forms" ? ( + ) : ( )} diff --git a/src/hooks/useBridges.js b/src/hooks/useBridges.js index 1463b81a..07036848 100644 --- a/src/hooks/useBridges.js +++ b/src/hooks/useBridges.js @@ -1,13 +1,35 @@ -import { useApis } from "../providers/Settings"; +import { useAddons } from "../providers/Settings"; const { useMemo } = wp.element; export default function useBridges() { - const [apis] = useApis(); + const [addons, patch] = useAddons(); - return useMemo(() => { - return Object.keys(apis).reduce((bridges, api) => { - return bridges.concat(apis[api].backends || []); + const bridges = useMemo(() => { + return Object.keys(addons).reduce((bridges, addon) => { + const addonBridges = addons[addon].bridges || []; + return bridges.concat( + addonBridges.map((bridge) => ({ ...bridge, addon })) + ); }, []); - }, [apis]); + }, [addons]); + + const setBridges = (bridges) => { + const newAddons = Object.keys(addons).reduce((newAddons, addon) => { + const addonBridges = bridges + .filter((bridge) => bridge.addon === addon) + .map((bridge) => { + const newBridge = { ...bridge }; + delete newBridge.addon; + return newBridge; + }); + + newAddons[addon] = { ...addons[addon], bridges: addonBridges }; + return newAddons; + }, {}); + + patch(newAddons); + }; + + return [bridges, setBridges]; } diff --git a/src/providers/Forms.jsx b/src/providers/Forms.jsx index 648ed6e8..7ff4ec3a 100644 --- a/src/providers/Forms.jsx +++ b/src/providers/Forms.jsx @@ -2,6 +2,7 @@ import { useLoading } from "./Loading"; import { useError } from "./Error"; import { useIntegrations } from "../hooks/useGeneral"; import diff from "../lib/diff"; +import { useSettings } from "./Settings"; const { createContext, useContext, useState, useEffect, useRef } = wp.element; const apiFetch = wp.apiFetch; @@ -10,6 +11,7 @@ const { __ } = wp.i18n; const FormsContext = createContext([]); export default function FormsProvider({ children }) { + const [settings] = useSettings(); const [loading, setLoading] = useLoading(); const [, setError] = useError(); const [forms, setForms] = useState([]); @@ -29,11 +31,11 @@ export default function FormsProvider({ children }) { }, [integrations]); useEffect(() => { - if (loading) return; + if (loading || window.__wpfbInvalidated) return; if (invalid.current) { fetch().then(() => (invalid.current = false)); } - }, [loading, integrations]); + }, [settings, loading, integrations]); const fetch = useRef(() => { setLoading(true); diff --git a/src/providers/Jobs.jsx b/src/providers/Jobs.jsx index 134c2823..67bb683f 100644 --- a/src/providers/Jobs.jsx +++ b/src/providers/Jobs.jsx @@ -99,7 +99,7 @@ export default function JobsProvider({ children }) { } }) .catch((err) => { - console.log(err); + console.error(err); setError(__("Job reset error", "forms-bridge")); }) .finally(() => setLoading(false)); diff --git a/src/providers/Schemas.jsx b/src/providers/Schemas.jsx index 5fcfdb6f..492125bd 100644 --- a/src/providers/Schemas.jsx +++ b/src/providers/Schemas.jsx @@ -52,7 +52,8 @@ export default function SchemasProvider({ children }) { }, []); useEffect(() => { - if (tab && tab !== "general" && tab !== "http") fetch(tab); + if (tab && tab !== "general" && tab !== "http" && tab !== "forms") + fetch(tab); }, [fetch, tab]); const schema = useMemo(() => { From a5a5b76438212671579fc0a8cfca145d95dd237d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Garc=C3=ADa?= Date: Thu, 22 Jan 2026 21:59:35 +0100 Subject: [PATCH 4/6] feat: form bridge allow_failure property --- forms-bridge/includes/class-form-bridge.php | 8 ++++ forms-bridge/includes/class-forms-bridge.php | 6 ++- src/components/Form/Bridges.jsx | 48 ++++++++++++++++++-- src/components/Forms/index.jsx | 6 ++- src/components/Mutations/index.jsx | 13 ++++++ 5 files changed, 74 insertions(+), 7 deletions(-) diff --git a/forms-bridge/includes/class-form-bridge.php b/forms-bridge/includes/class-form-bridge.php index 4406579f..1f3bab3b 100644 --- a/forms-bridge/includes/class-form-bridge.php +++ b/forms-bridge/includes/class-form-bridge.php @@ -203,9 +203,15 @@ public static function schema( $addon = null ) { 'type' => 'boolean', 'default' => true, ), + 'allow_failure' => array( + 'description' => __( 'Whether an error on bridge submission should stop the submission loop or not', 'forms-bridge' ), + 'type' => 'boolean', + 'default' => true, + ), 'order' => array( 'description' => __( 'Order in which the bridge should be submitted in the submission loop', 'forms-bridge' ), 'type' => 'integer', + 'default' => -1, ), ), 'required' => array( @@ -219,6 +225,8 @@ public static function schema( $addon = null ) { 'workflow', 'is_valid', 'enabled', + 'allow_failure', + 'order', ), 'additionalProperties' => false, ); diff --git a/forms-bridge/includes/class-forms-bridge.php b/forms-bridge/includes/class-forms-bridge.php index c048692a..3b8d1bcd 100644 --- a/forms-bridge/includes/class-forms-bridge.php +++ b/forms-bridge/includes/class-forms-bridge.php @@ -285,7 +285,7 @@ function ( $field ) { $sorted_bridges = array(); foreach ( $bridges as $bridge ) { - $order = is_int( $bridge->order ) ? $bridge->order : 100 + count( $sorted_bridges ); + $order = 0 <= $bridge->order ? $bridge->order : 100 + count( $sorted_bridges ); while ( isset( $sorted_bridges[ $order ] ) ) { ++$order; @@ -450,6 +450,10 @@ function ( $field ) { $payload ?? $submission, $attachments ?? array() ); + + if ( false === $bridge->allow_failure ) { + break; + } } finally { self::$current_bridge = null; } diff --git a/src/components/Form/Bridges.jsx b/src/components/Form/Bridges.jsx index 4fbec810..a30fdcf8 100644 --- a/src/components/Form/Bridges.jsx +++ b/src/components/Form/Bridges.jsx @@ -34,10 +34,16 @@ export default function FormBridges({ bridges, setBridges }) { setBridges(newBridges); }; + const setFailure = (index, policy) => { + const newBridges = bridges.map((bridge) => ({ ...bridge })); + newBridges[index].allow_failure = !!policy; + setBridges(newBridges); + }; + return ( <> {open && ( - {__("Manage the chain of bridges of the form", "forms-bridge")} + {__( + "Manage the form's bridge chain and their submission failure policies", + "forms-bridge" + )}

setFailure(i, policy)} move={(direction) => move(i, i + direction)} isLast={i === bridges.length - 1} /> @@ -98,7 +109,7 @@ export default function FormBridges({ bridges, setBridges }) { ); } -function BridgeStep({ name, index, move, isLast }) { +function BridgeStep({ index, name, failure, setFailure, move, isLast }) { return (

+ setFailure(!failure)} + style={{ + fontSize: "1.25em", + marginRight: "1em", + marginLeft: "-0.7em", + cursor: "pointer", + }} + __next40pxDefaultSize + > + {failure === false ? "🔴" : "🟢"} + {index + 1}. {name} - ⬆ + + ⬆ +

diff --git a/src/components/Forms/index.jsx b/src/components/Forms/index.jsx index c02dcce2..6ba13f24 100644 --- a/src/components/Forms/index.jsx +++ b/src/components/Forms/index.jsx @@ -55,7 +55,11 @@ export default function Forms() { const newBridges = bridges.map((bridge) => { const index = order.findIndex((name) => name === bridge.name); if (index !== -1) { - return { ...bridge, order: index }; + return { + ...bridge, + allow_failure: formBridges[index].allow_failure, + order: index, + }; } return bridge; diff --git a/src/components/Mutations/index.jsx b/src/components/Mutations/index.jsx index 5384480f..f5dd0a5a 100644 --- a/src/components/Mutations/index.jsx +++ b/src/components/Mutations/index.jsx @@ -100,8 +100,21 @@ export default function Mutations({ onRequestClose={onClose} className="no-scrollable" > +

+ {__( + "Transform the form submission with field mappings and value mutations", + "forms-bridge" + )} +

Date: Thu, 22 Jan 2026 22:25:30 +0100 Subject: [PATCH 5/6] feat: usort bridges on submission --- forms-bridge/includes/class-forms-bridge.php | 21 ++++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/forms-bridge/includes/class-forms-bridge.php b/forms-bridge/includes/class-forms-bridge.php index 3b8d1bcd..762158f1 100644 --- a/forms-bridge/includes/class-forms-bridge.php +++ b/forms-bridge/includes/class-forms-bridge.php @@ -283,19 +283,18 @@ function ( $field ) { return; } - $sorted_bridges = array(); - foreach ( $bridges as $bridge ) { - $order = 0 <= $bridge->order ? $bridge->order : 100 + count( $sorted_bridges ); + usort( + $bridges, + function ( $a, $b ) { + if ( 0 > $a->order ) { + return 1; + } elseif ( 0 > $b->order ) { + return -1; + } - while ( isset( $sorted_bridges[ $order ] ) ) { - ++$order; + return $a->order - $b->order; } - - $sorted_bridges[ $order ] = $bridge; - } - - ksort( $sorted_bridges ); - $bridges = array_values( $sorted_bridges ); + ); foreach ( $bridges as $bridge ) { if ( ! $bridge->enabled ) { From 553256c9eeb401bd5301017f8fcd67c607bfc36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Garc=C3=ADa?= Date: Thu, 22 Jan 2026 23:15:54 +0100 Subject: [PATCH 6/6] feat: submission loop early exist error log --- forms-bridge/includes/class-forms-bridge.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/forms-bridge/includes/class-forms-bridge.php b/forms-bridge/includes/class-forms-bridge.php index 762158f1..ff670892 100644 --- a/forms-bridge/includes/class-forms-bridge.php +++ b/forms-bridge/includes/class-forms-bridge.php @@ -296,7 +296,10 @@ function ( $a, $b ) { } ); - foreach ( $bridges as $bridge ) { + $l = count( $bridges ); + for ( $i = 0; $i < $l; ++$i ) { + $bridge = $bridges[ $i ]; + if ( ! $bridge->enabled ) { Logger::log( 'Skip submission for disabled bridge ' . $bridge->name @@ -450,7 +453,12 @@ function ( $a, $b ) { $attachments ?? array() ); - if ( false === $bridge->allow_failure ) { + if ( + false === $bridge->allow_failure + && count( $bridges ) > 1 + && $i < count( $bridges ) - 1 + ) { + Logger::log( 'Early exit from the submission loop due to an error', Logger::ERROR ); break; } } finally {