diff --git a/src/openforms/conf/base.py b/src/openforms/conf/base.py index cb0aebf0d1..67665c1cea 100644 --- a/src/openforms/conf/base.py +++ b/src/openforms/conf/base.py @@ -224,6 +224,7 @@ "openforms.registrations.contrib.objects_api", "openforms.registrations.contrib.microsoft_graph.apps.MicrosoftGraphApp", "openforms.registrations.contrib.camunda.apps.CamundaApp", + "openforms.registrations.contrib.new_plugin", "openforms.prefill", "openforms.prefill.contrib.demo.apps.DemoApp", "openforms.prefill.contrib.kvk.apps.KVKPrefillApp", diff --git a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js index a35a3378e5..986af24c15 100644 --- a/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js +++ b/src/openforms/js/components/admin/form_design/RegistrationFields.stories.js @@ -390,6 +390,21 @@ export default { type: 'object', }, }, + { + id: 'json', + label: 'JSON', + schema: { + properties: { + // TODO-4098: add actual relevant properties + extraLine: { + minLength: 1, + title: 'Extra print statement', + type: 'string', + }, + }, + type: 'object', + }, + }, ], configuredBackends: [], onChange: fn(), @@ -663,6 +678,12 @@ export const ConfiguredBackends = { ], }, }, + { + key: 'backend11', + name: 'JSON', + backend: 'json', + options: {extraLine: 'We are checking.'}, + }, ], validationErrors: [ ['form.registrationBackends.1.options.zgwApiGroup', 'You sure about this?'], @@ -910,3 +931,25 @@ export const STUFZDS = { await userEvent.click(canvas.getByRole('button', {name: 'Opties instellen'})); }, }; + + +export const JSON = { + args: { + configuredBackends: [ + { + key: 'backend11', + name: 'JSON', + backend: 'json', + options: { + extraLine: 'We are checking.', + }, + }, + ], + }, + + play: async ({canvasElement}) => { + const canvas = within(canvasElement); + + await userEvent.click(canvas.getByRole('button', {name: 'Opties instellen'})); + }, +}; diff --git a/src/openforms/js/components/admin/form_design/registrations/index.js b/src/openforms/js/components/admin/form_design/registrations/index.js index c5de3f8053..4c549674ee 100644 --- a/src/openforms/js/components/admin/form_design/registrations/index.js +++ b/src/openforms/js/components/admin/form_design/registrations/index.js @@ -1,6 +1,7 @@ import CamundaOptionsForm from './camunda'; import DemoOptionsForm from './demo'; import EmailOptionsForm from './email'; +import JSONOptionsForm from './json'; import MSGraphOptionsForm from './ms_graph'; import ObjectsApiOptionsForm from './objectsapi/ObjectsApiOptionsForm'; import ObjectsApiSummaryHandler from './objectsapi/ObjectsApiSummaryHandler'; @@ -46,6 +47,7 @@ export const BACKEND_OPTIONS_FORMS = { form: StufZDSOptionsForm, }, 'microsoft-graph': {form: MSGraphOptionsForm}, + 'json': {form: JSONOptionsForm}, // demo plugins demo: {form: DemoOptionsForm}, 'failing-demo': {form: DemoOptionsForm}, diff --git a/src/openforms/js/components/admin/form_design/registrations/json/JSONOptionsForm.js b/src/openforms/js/components/admin/form_design/registrations/json/JSONOptionsForm.js new file mode 100644 index 0000000000..1dde8ff9f0 --- /dev/null +++ b/src/openforms/js/components/admin/form_design/registrations/json/JSONOptionsForm.js @@ -0,0 +1,74 @@ +import {useField} from 'formik'; +import PropTypes from 'prop-types'; +import React, {useContext} from 'react'; +import {FormattedMessage} from 'react-intl'; + +import Field from 'components/admin/forms/Field'; +import Fieldset from 'components/admin/forms/Fieldset'; +import FormRow from 'components/admin/forms/FormRow'; +import {TextInput} from 'components/admin/forms/Inputs'; +import ModalOptionsConfiguration from 'components/admin/forms/ModalOptionsConfiguration'; +import { + ValidationErrorContext, + ValidationErrorsProvider, + filterErrors, +} from 'components/admin/forms/ValidationErrors'; + + +const ExtraLine = () => { + const [fieldProps] = useField('extraLine'); + return ( + + + } + > + + + + ); +}; + + +const JSONOptionsForm = ({name, label, formData, onChange}) => { + const validationErrors = useContext(ValidationErrorContext); + const relevantErrors = filterErrors(name, validationErrors); + return ( + + } + initialFormData={{extraLine: '', ...formData}} + onSubmit={values => onChange({formData: values})} + modalSize="small" + > + +
+ +
+
+
+ ); +}; + +JSONOptionsForm.propTypes = { + name: PropTypes.string.isRequired, + label: PropTypes.node.isRequired, + formData: PropTypes.shape({ + extraLine: PropTypes.string, + }), + onChange: PropTypes.func.isRequired, +}; + +export default JSONOptionsForm; diff --git a/src/openforms/js/components/admin/form_design/registrations/json/index.js b/src/openforms/js/components/admin/form_design/registrations/json/index.js new file mode 100644 index 0000000000..97a74031a6 --- /dev/null +++ b/src/openforms/js/components/admin/form_design/registrations/json/index.js @@ -0,0 +1,3 @@ +import JSONOptionsForm from './JSONOptionsForm'; + +export default JSONOptionsForm; diff --git a/src/openforms/registrations/contrib/new_plugin/__init__.py b/src/openforms/registrations/contrib/new_plugin/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/openforms/registrations/contrib/new_plugin/apps.py b/src/openforms/registrations/contrib/new_plugin/apps.py new file mode 100644 index 0000000000..70b007c941 --- /dev/null +++ b/src/openforms/registrations/contrib/new_plugin/apps.py @@ -0,0 +1,12 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +# TODO-4908: come up with good name: 'JSON' or '(Form) Variables API'? +class NewPluginConfig(AppConfig): + name = "openforms.registrations.contrib.new_plugin" + label = "registrations_new_plugin" + verbose_name = _("New plugin") + + def ready(self): + from . import plugin # noqa diff --git a/src/openforms/registrations/contrib/new_plugin/config.py b/src/openforms/registrations/contrib/new_plugin/config.py new file mode 100644 index 0000000000..48d26487fa --- /dev/null +++ b/src/openforms/registrations/contrib/new_plugin/config.py @@ -0,0 +1,33 @@ +from django.utils.translation import gettext_lazy as _ + +from rest_framework import serializers +from zgw_consumers.models import Service + +from openforms.api.fields import PrimaryKeyRelatedAsChoicesField +from openforms.formio.api.fields import FormioVariableKeyField +from openforms.utils.mixins import JsonSchemaSerializerMixin + + +class NewPluginOptionsSerializer(JsonSchemaSerializerMixin, serializers.Serializer): + # TODO-4098: is service enough, or do we need an API group like the ObjectsAPI? + service = PrimaryKeyRelatedAsChoicesField( + queryset=Service.objects.all(), + label=_("Service"), + help_text=_("Which service to use."), + ) + # TODO-4098: show the complete API endpoint as a (tooltip) hint after user entry? + api_endpoint = serializers.CharField( + max_length=255, + label=_("Relative API endpoint"), + help_text=_("The API endpoint to send the data to (relative to the service API root)."), + ) + # TODO-4098: should be linked to the checkboxes in the form variable table + # TODO-4098: is it possible to have a choices field of variable keys, where you can select them + # from a drop-down list? + form_variables = serializers.ListField( + child=FormioVariableKeyField(max_length=50), + label=_("Form variable key list"), + help_text=_( + "A comma-separated list of form variables (can also include static variables) to use." + ) + ) diff --git a/src/openforms/registrations/contrib/new_plugin/plugin.py b/src/openforms/registrations/contrib/new_plugin/plugin.py new file mode 100644 index 0000000000..d729e032da --- /dev/null +++ b/src/openforms/registrations/contrib/new_plugin/plugin.py @@ -0,0 +1,18 @@ +from django.utils.translation import gettext_lazy as _ + +from openforms.submissions.models import Submission +from ...base import BasePlugin, OptionsT # openforms.registrations.base +from ...registry import register # openforms.registrations.registry +from .config import NewPluginOptionsSerializer + + +@register("new_plugin") +class NewPlugin(BasePlugin): + verbose_name = _("New fancy plugin") + configuration_options = NewPluginOptionsSerializer + + def register_submission(self, submission: Submission, options: OptionsT) -> None: + print(options) + + def check_config(self): + pass