apps/meteor/client/views/admin/integrations/incoming/IncomingWebhookForm.tsx
import type { IIncomingIntegration, Serialized } from '@rocket.chat/core-typings';
import type { SelectOption } from '@rocket.chat/fuselage';
import {
FieldError,
IconButton,
Accordion,
AccordionItem,
Field,
TextInput,
Box,
ToggleSwitch,
Icon,
TextAreaInput,
FieldGroup,
Select,
FieldLabel,
FieldRow,
FieldHint,
} from '@rocket.chat/fuselage';
import { useUniqueId } from '@rocket.chat/fuselage-hooks';
import { useAbsoluteUrl, useTranslation } from '@rocket.chat/ui-contexts';
import React, { useMemo } from 'react';
import { Controller, useFormContext } from 'react-hook-form';
import useClipboardWithToast from '../../../../hooks/useClipboardWithToast';
import { useHighlightedCode } from '../../../../hooks/useHighlightedCode';
import { useExampleData } from '../hooks/useExampleIncomingData';
const IncomingWebhookForm = ({ webhookData }: { webhookData?: Serialized<IIncomingIntegration> }) => {
const t = useTranslation();
const absoluteUrl = useAbsoluteUrl();
const {
control,
watch,
formState: { errors },
} = useFormContext();
const { alias, emoji, avatar } = watch();
const url = absoluteUrl(`hooks/${webhookData?._id}/${webhookData?.token}`);
const additionalFields = useMemo(
() => ({
...(alias && { alias }),
...(emoji && { emoji }),
...(avatar && { avatar }),
}),
[alias, avatar, emoji],
);
const [exampleData, curlData] = useExampleData({
additionalFields,
url,
});
const { copy: copyWebhookUrl } = useClipboardWithToast(url);
const { copy: copyToken } = useClipboardWithToast(`${webhookData?._id}/${webhookData?.token}`);
const { copy: copyCurlData } = useClipboardWithToast(curlData);
const scriptEngineOptions: SelectOption[] = useMemo(
() => [
['vm2', t('Script_Engine_vm2')],
['isolated-vm', t('Script_Engine_isolated_vm')],
],
[t],
);
const hilightedExampleJson = useHighlightedCode('json', JSON.stringify(exampleData, null, 2));
const enabledField = useUniqueId();
const nameField = useUniqueId();
const channelField = useUniqueId();
const usernameField = useUniqueId();
const aliasField = useUniqueId();
const avatarField = useUniqueId();
const emojiField = useUniqueId();
const overrideDestinationChannelEnabledField = useUniqueId();
const scriptEnabledField = useUniqueId();
const scriptEngineField = useUniqueId();
const scriptField = useUniqueId();
const webhookUrlField = useUniqueId();
const tokenField = useUniqueId();
const curlField = useUniqueId();
return (
<Box maxWidth='x600' alignSelf='center' w='full'>
<Accordion>
<AccordionItem defaultExpanded={Boolean(webhookData?._id)} title={t('Instructions')}>
<FieldGroup>
<Field>
<FieldLabel htmlFor={webhookUrlField}>{t('Webhook_URL')}</FieldLabel>
<FieldRow>
<TextInput
id={webhookUrlField}
value={webhookData?._id ? url : t('Will_be_available_here_after_saving')}
readOnly
addon={webhookData?._id ? <IconButton mini onClick={() => copyWebhookUrl()} title={t('Copy')} icon='copy' /> : undefined}
aria-describedby={`${webhookUrlField}-hint`}
/>
</FieldRow>
<FieldHint id={`${webhookUrlField}-hint`}>{t('Send_your_JSON_payloads_to_this_URL')}</FieldHint>
</Field>
<Field>
<FieldLabel htmlFor={tokenField}>{t('Token')}</FieldLabel>
<FieldRow>
<TextInput
id={tokenField}
value={webhookData?._id ? `${webhookData?._id}/${webhookData?.token}` : t('Will_be_available_here_after_saving')}
readOnly
addon={webhookData?._id ? <IconButton mini onClick={() => copyToken()} title={t('Copy')} icon='copy' /> : undefined}
/>
</FieldRow>
</Field>
<Field>
<FieldLabel>{t('Example_payload')}</FieldLabel>
<FieldRow>
<Box fontScale='p2' withRichContent flexGrow={1}>
<pre>
<code dangerouslySetInnerHTML={{ __html: hilightedExampleJson }}></code>
</pre>
</Box>
</FieldRow>
</Field>
{webhookData?._id && (
<Field>
<FieldLabel htmlFor={curlField}>Curl</FieldLabel>
<FieldRow>
<TextInput
id={curlField}
value={curlData}
readOnly
addon={webhookData?._id ? <IconButton mini onClick={() => copyCurlData()} title={t('Copy')} icon='copy' /> : undefined}
/>
</FieldRow>
</Field>
)}
</FieldGroup>
</AccordionItem>
<AccordionItem title={t('Settings')} defaultExpanded>
<FieldGroup>
<Field>
<FieldRow>
<FieldLabel htmlFor={enabledField}>{t('Enabled')}</FieldLabel>
<Controller
name='enabled'
control={control}
render={({ field: { value, ...field } }) => <ToggleSwitch id={enabledField} {...field} checked={value} />}
/>
</FieldRow>
</Field>
<Field>
<FieldLabel htmlFor={nameField}>{t('Name')}</FieldLabel>
<FieldRow>
<Controller
name='name'
control={control}
render={({ field }) => <TextInput id={nameField} {...field} aria-describedby={`${nameField}-hint`} />}
/>
</FieldRow>
<FieldHint id={`${nameField}-hint`}>{t('You_should_name_it_to_easily_manage_your_integrations')}</FieldHint>
</Field>
<Field>
<FieldLabel htmlFor={channelField} required>
{t('Post_to_Channel')}
</FieldLabel>
<FieldRow>
<Controller
name='channel'
control={control}
rules={{ required: t('The_field_is_required', t('Post_to_Channel')) }}
render={({ field }) => (
<TextInput
id={channelField}
{...field}
addon={<Icon name='at' size='x20' />}
aria-describedby={`${channelField}-hint-1 ${channelField}-hint-2 ${channelField}-error`}
aria-required={true}
aria-invalid={Boolean(errors?.channel)}
/>
)}
/>
</FieldRow>
<FieldHint id={`${channelField}-hint-1`}>{t('Messages_that_are_sent_to_the_Incoming_WebHook_will_be_posted_here')}</FieldHint>
<FieldHint
id={`${channelField}-hint-2`}
dangerouslySetInnerHTML={{
__html: t('Start_with_s_for_user_or_s_for_channel_Eg_s_or_s', '@', '#', '@john', '#general'),
}}
/>
{errors?.channel && (
<FieldError aria-live='assertive' id={`${channelField}-error`}>
{errors?.channel.message}
</FieldError>
)}
</Field>
<Field>
<FieldLabel htmlFor={usernameField} required>
{t('Post_as')}
</FieldLabel>
<FieldRow>
<Controller
name='username'
control={control}
rules={{ required: t('The_field_is_required', t('Post_to_Channel')) }}
render={({ field }) => (
<TextInput
id={usernameField}
{...field}
addon={<Icon name='user' size='x20' />}
aria-describedby={`${usernameField}-hint-1 ${usernameField}-hint-2 ${usernameField}-error`}
aria-required={true}
aria-invalid={Boolean(errors?.username)}
/>
)}
/>
</FieldRow>
<FieldHint id={`${usernameField}-hint-1`}>{t('Choose_the_username_that_this_integration_will_post_as')}</FieldHint>
<FieldHint id={`${usernameField}-hint-2`}>{t('Should_exists_a_user_with_this_username')}</FieldHint>
{errors?.username && (
<FieldError aria-live='assertive' id={`${usernameField}-error`}>
{errors.username.message}
</FieldError>
)}
</Field>
<Field>
<FieldLabel htmlFor={aliasField}>{t('Alias')}</FieldLabel>
<FieldRow>
<Controller
name='alias'
control={control}
render={({ field }) => (
<TextInput id={aliasField} {...field} aria-describedby={`${aliasField}-hint`} addon={<Icon name='edit' size='x20' />} />
)}
/>
</FieldRow>
<FieldHint id={`${aliasField}-hint`}>{t('Choose_the_alias_that_will_appear_before_the_username_in_messages')}</FieldHint>
</Field>
<Field>
<FieldLabel htmlFor={avatarField}>{t('Avatar_URL')}</FieldLabel>
<FieldRow>
<Controller
name='avatar'
control={control}
render={({ field }) => (
<TextInput
id={avatarField}
{...field}
aria-describedby={`${avatarField}-hint-1 ${avatarField}-hint-2`}
addon={<Icon name='user-rounded' size='x20' alignSelf='center' />}
/>
)}
/>
</FieldRow>
<FieldHint id={`${avatarField}-hint-1`}>{t('You_can_change_a_different_avatar_too')}</FieldHint>
<FieldHint id={`${avatarField}-hint-2`}>{t('Should_be_a_URL_of_an_image')}</FieldHint>
</Field>
<Field>
<FieldLabel htmlFor={emojiField}>{t('Emoji')}</FieldLabel>
<FieldRow>
<Controller
name='emoji'
control={control}
render={({ field }) => (
<TextInput
id={emojiField}
{...field}
aria-describedby={`${emojiField}-hint-1 ${emojiField}-hint-2`}
addon={<Icon name='emoji' size='x20' alignSelf='center' />}
/>
)}
/>
</FieldRow>
<FieldHint id={`${emojiField}-hint-1`}>{t('You_can_use_an_emoji_as_avatar')}</FieldHint>
<FieldHint id={`${emojiField}-hint-2`} dangerouslySetInnerHTML={{ __html: t('Example_s', ':ghost:') }} />
</Field>
<Field>
<FieldRow>
<FieldLabel htmlFor={overrideDestinationChannelEnabledField}>{t('Override_Destination_Channel')}</FieldLabel>
<Controller
name='overrideDestinationChannelEnabled'
control={control}
render={({ field: { value, ...field } }) => (
<ToggleSwitch id={overrideDestinationChannelEnabledField} {...field} checked={value} />
)}
/>
</FieldRow>
</Field>
<Field>
<FieldRow>
<FieldLabel htmlFor={scriptEnabledField}>{t('Script_Enabled')}</FieldLabel>
<Controller
name='scriptEnabled'
control={control}
render={({ field: { value, ...field } }) => <ToggleSwitch id={scriptEnabledField} {...field} checked={value} />}
/>
</FieldRow>
</Field>
<Field>
<FieldLabel htmlFor={scriptEngineField}>{t('Script_Engine')}</FieldLabel>
<FieldRow>
<Controller
name='scriptEngine'
control={control}
render={({ field }) => (
<Select
id={scriptEngineField}
aria-describedby={`${scriptEngineField}-hint`}
{...field}
options={scriptEngineOptions}
/>
)}
/>
</FieldRow>
<FieldHint id={`${scriptEngineField}-hint`}>{t('Script_Engine_Description')}</FieldHint>
</Field>
<Field>
<FieldLabel htmlFor={scriptField}>{t('Script')}</FieldLabel>
<FieldRow>
<Controller
name='script'
control={control}
render={({ field }) => (
<TextAreaInput id={scriptField} {...field} rows={10} addon={<Icon name='code' size='x20' alignSelf='center' />} />
)}
/>
</FieldRow>
</Field>
</FieldGroup>
</AccordionItem>
</Accordion>
</Box>
);
};
export default IncomingWebhookForm;