Skip to content

Commit

Permalink
feat(open-forms-embed): create strapi open-forms-embed field
Browse files Browse the repository at this point in the history
  • Loading branch information
AliKdhim87 committed Feb 22, 2024
1 parent a481c82 commit 1941b14
Show file tree
Hide file tree
Showing 32 changed files with 544 additions and 10 deletions.
22 changes: 12 additions & 10 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,25 +50,27 @@ module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: [
'./apps/kennisbank-dashboard/src/admin/tsconfig.json',
'./apps/kennisbank-dashboard/tsconfig.json',
'./apps/kennisbank-frontend/tsconfig.json',
'./apps/pdc-dashboard/src/admin/tsconfig.json',
'./apps/pdc-dashboard/tsconfig.json',
'./apps/pdc-frontend/tsconfig.json',
'./apps/pdc-sc/tsconfig.json',
'./apps/pdc-dashboard/tsconfig.json',
'./apps/pdc-dashboard/src/admin/tsconfig.json',
'./apps/vth-frontend/tsconfig.json',
'./apps/vth-dashboard/tsconfig.json',
'./apps/vth-dashboard/src/admin/tsconfig.json',
'./apps/kennisbank-frontend/tsconfig.json',
'./apps/kennisbank-dashboard/tsconfig.json',
'./apps/kennisbank-dashboard/src/admin/tsconfig.json',
'./apps/vth-dashboard/tsconfig.json',
'./apps/vth-frontend/tsconfig.json',
'./packages/catalogi-data/tsconfig.json',
'./packages/ui/tsconfig.json',
'./packages/upl/tsconfig.json',
'./packages/provider-upload-vercel/tsconfig.json',
'./packages/samenwerkende-catalogi/tsconfig.json',
'./packages/strapi-plugin-open-forms-embed/tsconfig.json',
'./packages/strapi-plugin-open-forms-embed/tsconfig.server.json',
'./packages/strapi-plugin-uniform-product-name/tsconfig.json',
'./packages/strapi-plugin-uniform-product-name/tsconfig.server.json',
'./packages/provider-upload-vercel/tsconfig.json',
'./packages/strapi-tiptap-editor/tsconfig.json',
'./packages/strapi-tiptap-editor/tsconfig.server.json',
'./packages/ui/tsconfig.json',
'./packages/upl/tsconfig.json',
],
tsconfigRootDir: __dirname,
},
Expand Down
34 changes: 34 additions & 0 deletions packages/strapi-plugin-open-forms-embed/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Strapi Plugin: strapi-plugin-open-forms-embed

This custom field serves as a dropdown representation of available OpenForms within the Strapi dashboard. It retrieves the `name` and `uuid` through a fetch function that calls the OpenForms API.

## Installation

```shell

yarn install @frameless/strapi-plugin-open-forms-embed

```

## Usage

Navigate to the Strapi dashboard's `config/plugins.ts`.

```ts
export default ({ env }) => ({
"open-forms-embed": {
enabled: true,
config: {
api_url: env("OPEN_FORMS_API_URL"),
token: env("OPEN_FORMS_API_TOKEN"),
},
},
});
```

**Build the dashboard**:

```shell
yarn build

```
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Flex } from '@strapi/design-system/Flex';
import { Icon } from '@strapi/design-system/Icon';
import User from '@strapi/icons/User';
import React from 'react';
import styled from 'styled-components';

const IconBox = styled(Flex)`
background-color: #f0f0ff; /* primary100 */
border: 1px solid #d9d8ff; /* primary200 */
svg > path {
fill: #4945ff; /* primary600 */
}
`;

function ComboboxIcon() {
return (
<IconBox justifyContent="center" alignItems="center" width={7} height={6} hasRadius aria-hidden>
<Icon as={User} />
</IconBox>
);
}

export default ComboboxIcon;
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Combobox, ComboboxOption } from '@strapi/design-system/Combobox';
import { Field, FieldError, FieldHint, FieldLabel } from '@strapi/design-system/Field';
import { Stack } from '@strapi/design-system/Stack';
import React, { useEffect } from 'react';
import { useIntl } from 'react-intl';
import usePluginConfig from '../../hooks/use-plugin-config';

interface CustomComboboxProps {
intlLabel: any;
// eslint-disable-next-line no-unused-vars
onChange: (param: any) => {};
attribute: any;
name: string;
description: any;
disabled: boolean;
error: string;
labelAction: any;
required: boolean;
value: string;
placeholder: any;
}

function CustomCombobox({
value,
onChange,
name,
intlLabel,
labelAction,
required,
attribute,
description,
placeholder,
disabled,
error,
}: CustomComboboxProps) {
const { formatMessage } = useIntl();
const [openForms, setOpenForms] = React.useState([]);
const { config } = usePluginConfig();
const apiUrl = config.api_url?.endsWith('/') ? `${config.api_url}forms` : `${config.api_url}/forms`;

const fetchAllOpenForms = async () => {
try {
const response = await fetch(apiUrl, {
mode: 'cors',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Token ${config.token}`,
},
});
const data = await response.json();
setOpenForms(data);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
};

useEffect(() => {
if (config?.api_url && config?.token && apiUrl) {
fetchAllOpenForms();
}
}, [config?.api_url, config?.token]);

const generateOpenFormsData = (params) => {
return new URLSearchParams({ ...params }).toString();
};
return (
<Field name={name} id={name} error={error} hint={description && formatMessage(description)}>
<Stack spacing={1}>
<FieldLabel action={labelAction} required={required}>
{formatMessage(intlLabel)}
</FieldLabel>
<Combobox
placeholder={placeholder && formatMessage(placeholder)}
aria-label={formatMessage(intlLabel)}
aria-disabled={disabled}
disabled={disabled}
value={value}
onChange={(url: string) => onChange({ target: { name, value: url, type: attribute.type } })}
>
{openForms?.length > 0 &&
openForms?.map(({ uuid, name, slug }) => (
<ComboboxOption
value={generateOpenFormsData({
uuid,
slug,
label: name,
})}
key={uuid}
>
{name}
</ComboboxOption>
))}
</Combobox>
<FieldHint />
<FieldError />
</Stack>
</Field>
);
}

export default CustomCombobox;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import pluginId from './pluginId';

export const RESOLVE_CONFIG = `${pluginId}/resolve-config`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useFetchClient, useNotification } from '@strapi/helper-plugin';
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { RESOLVE_CONFIG } from '../constants';
import pluginId from '../pluginId';

const usePluginConfig = () => {
const dispatch = useDispatch();
const toggleNotification = useNotification();

const { isLoading, config } = useSelector((state: any) => state[`${pluginId}_config`]);
const client = useFetchClient();

useEffect(() => {
// Do nothing if we have already loaded the config data.
if (!isLoading && !!config) {
return;
}

const abortController = new AbortController();

// eslint-disable-next-line consistent-return
const fetchData = async () => {
try {
const endpoint = `/${pluginId}/config`;
const { data } = await client.get(endpoint, {
signal: abortController.signal,
});

return data ?? {};
} catch (err) {
// eslint-disable-next-line no-console
console.error(err);

if (!abortController.signal.aborted) {
toggleNotification({
type: 'warning',
message: { id: 'notification.error' },
});

return err;
}
}
};

fetchData()
.then((data) => dispatch({ type: RESOLVE_CONFIG, data }))
.catch((error) => {
// eslint-disable-next-line no-console
console.error(error);
});

// eslint-disable-next-line consistent-return
return () => abortController.abort();
}, [client, config, dispatch, isLoading, toggleNotification]);

return { config: config, isLoading: isLoading };
};

export default usePluginConfig;
74 changes: 74 additions & 0 deletions packages/strapi-plugin-open-forms-embed/admin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { prefixPluginTranslations } from '@strapi/helper-plugin';
import ComboboxIcon from './components/ComboboxIcon';
import pluginId from './pluginId';
import reducers from './reducers';
import getTrad from './utils/getTrad';

export default {
register(app: any) {
app.addReducers(reducers);
app.customFields.register({
name: pluginId,
pluginId,
type: 'string',
icon: ComboboxIcon,
intlLabel: {
id: getTrad('open-forms-embed.label'),
defaultMessage: 'Choose an Embedded Open Form',
},
intlDescription: {
id: getTrad('open-forms-embed.description'),
defaultMessage:
'Upon selecting a form, its unique identifier (UUID) will be stored in the database for future reference.',
},
components: {
Input: async () => import('./components/CustomCombobox'),
},
options: {
advanced: [
{
sectionTitle: {
id: 'global.settings',
defaultMessage: 'Settings',
},
items: [
{
name: 'required',
type: 'checkbox',
intlLabel: {
id: 'form.attribute.item.requiredField',
defaultMessage: 'Required field',
},
description: {
id: 'form.attribute.item.requiredField.description',
defaultMessage: "You won't be able to create an entry if this field is empty",
},
},
],
},
],
},
});
},
async registerTrads({ locales }: any) {
const importedTrads = await Promise.all(
locales.map((locale: any) => {
return import(`./translations/${locale}.json`)
.then(({ default: data }) => {
return {
data: prefixPluginTranslations(data, pluginId),
locale,
};
})
.catch(() => {
return {
data: {},
locale,
};
});
}),
);

return Promise.resolve(importedTrads);
},
};
5 changes: 5 additions & 0 deletions packages/strapi-plugin-open-forms-embed/admin/src/pluginId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import pluginPkg from '../../package.json';

const pluginId = pluginPkg.name.replace(/^@frameless\/(@[^-,.][\w,-]+\/|strapi-)plugin-/i, '');

export default pluginId;
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import produce from 'immer';
import { RESOLVE_CONFIG } from '../constants';

const initialState = {
isLoading: true,
config: {},
};

const configReducer = produce((state = initialState, action) => {
switch (action.type) {
case RESOLVE_CONFIG: {
state.isLoading = false;
state.config = action.data;
break;
}

default:
return state;
}

return state;
});

export default configReducer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import config from './config';
import pluginId from '../pluginId';

const reducers = {
[`${pluginId}_config`]: config,
};

export default reducers;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"open-forms-embed.label": "Choose an Embedded Open Form",
"open-forms-embed.description": "Upon selecting a form, its unique identifier (UUID) will be stored in the database for future reference."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"open-forms-embed.label": "Kies een ingebed open formulier",
"open-forms-embed.description": "Bij het selecteren van een formulier wordt de unieke identificatie (UUID) ervan opgeslagen in de database voor toekomstige referentie."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import pluginId from '../pluginId';

const getTrad = (id: string) => `${pluginId}.${id}`;

export default getTrad;
Loading

0 comments on commit 1941b14

Please sign in to comment.