TryGhost/Ghost

View on GitHub
apps/admin-x-settings/src/components/settings/advanced/integrations/SlackModal.tsx

Summary

Maintainability
A
3 hrs
Test Coverage
import IntegrationHeader from './IntegrationHeader';
import NiceModal from '@ebay/nice-modal-react';
import toast from 'react-hot-toast';
import useSettingGroup from '../../../../hooks/useSettingGroup';
import validator from 'validator';
import {Button, Form, Modal, TextField, showToast} from '@tryghost/admin-x-design-system';
import {ReactComponent as Icon} from '../../../../assets/icons/slack.svg';
import {getSettingValues, useTestSlack} from '@tryghost/admin-x-framework/api/settings';
import {useRouting} from '@tryghost/admin-x-framework/routing';

const SlackModal = NiceModal.create(() => {
    const {updateRoute} = useRouting();
    const modal = NiceModal.useModal();

    const {localSettings, updateSetting, handleSave, validate, errors, clearError} = useSettingGroup({
        onValidate: () => {
            const newErrors: Record<string, string> = {};

            if (slackUrl && !validator.isURL(slackUrl, {require_protocol: true})) {
                newErrors.slackUrl = 'The URL must be in a format like https://hooks.slack.com/services/<your personal key>';
            }

            return newErrors;
        }
    });
    const [slackUrl, slackUsername] = getSettingValues<string>(localSettings, ['slack_url', 'slack_username']);

    const {mutateAsync: testSlack} = useTestSlack();

    const handleTestClick = async () => {
        toast.remove();
        if (await handleSave()) {
            await testSlack(null);
            showToast({
                title: 'Check your Slack channel for the test message',
                type: 'info'
            });
        }
    };

    return (
        <Modal
            afterClose={() => {
                updateRoute('integrations');
            }}
            dirty={localSettings.some(setting => setting.dirty)}
            okColor='black'
            okLabel='Save & close'
            testId='slack-modal'
            title=''
            onOk={async () => {
                toast.remove();
                if (await handleSave()) {
                    modal.remove();
                    updateRoute('integrations');
                }
            }}
        >
            <IntegrationHeader
                detail='A messaging app for teams'
                icon={<Icon className='h-14 w-14' />}
                title='Slack'
            />
            <div className='mt-7'>
                <Form marginBottom={false} title='Slack configuration' grouped>
                    <TextField
                        error={Boolean(errors.slackUrl)}
                        hint={errors.slackUrl || <>
                            Automatically send newly published posts to a channel in Slack or any Slack-compatible service like Discord or Mattermost. Set up a new incoming webhook <a href='https://my.slack.com/apps/new/A0F7XDUAZ-incoming-webhooks'>here</a>, and grab the URL.
                        </>}
                        placeholder='https://hooks.slack.com/services/...'
                        title='Webhook URL'
                        value={slackUrl}
                        onBlur={validate}
                        onChange={e => updateSetting('slack_url', e.target.value)}
                        onKeyDown={() => clearError('slackUrl')}
                    />
                    <div className='flex w-full flex-col gap-2 md:flex-row md:items-center'>
                        <TextField
                            containerClassName='flex-grow'
                            hint='The username to display messages from'
                            title='Username'
                            value={slackUsername}
                            onChange={e => updateSetting('slack_username', e.target.value)}
                        />
                        <Button color='outline' label='Send test notification' onClick={handleTestClick} />
                    </div>
                </Form>
            </div>
        </Modal>
    );
});

export default SlackModal;