AdminX integrations — static components (#17713)

refs. https://github.com/TryGhost/Product/issues/3729

- added static setting group for Integrations in AdminX
- added static built-in integration modals with forms
- added static custom integration list
This commit is contained in:
Peter Zimon 2023-08-15 12:20:46 +02:00 committed by GitHub
parent bed89b7bec
commit 81f13012e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 543 additions and 1 deletions

View File

@ -0,0 +1,4 @@
<svg width="256" height="256" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<title>amp</title>
<path d="m171.887 116.28-53.696 89.36h-9.728l9.617-58.227-30.2.047a4.854 4.854 0 0 1-4.855-4.855c0-1.152 1.07-3.102 1.07-3.102l53.52-89.254 9.9.043-9.86 58.317 30.413-.043a4.854 4.854 0 0 1 4.855 4.855c0 1.088-.427 2.044-1.033 2.854l.004.004-.007.001zM128 0C57.306 0 0 57.3 0 128s57.306 128 128 128 128-57.306 128-128S198.7 0 128 0z" fill="#0379C4" fill-rule="evenodd"/>
</svg>

After

Width:  |  Height:  |  Size: 492 B

View File

@ -0,0 +1,14 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_2155_3527)">
<path d="M87.4883 44.5547C86.4414 41.9023 84.8242 39.6016 82.6914 37.7148C80.5703 35.8594 77.8789 34.4141 74.6758 33.4062C71.5273 32.4141 67.7891 31.9141 63.5586 31.9141H56.1328V41.293H63.5586C68.418 41.293 72.0469 42.3711 74.3711 44.4805C76.6875 46.5977 77.8125 49.4844 77.8125 53.3203C77.8125 55.1523 77.5078 56.8555 76.9023 58.3711C76.3047 59.8633 75.4141 61.1562 74.2539 62.2305C73.082 63.3008 71.5859 64.1602 69.832 64.7734C68.0352 65.3984 65.9297 65.707 63.5586 65.707H56.1328V75.125H63.5586C67.5898 75.125 71.2227 74.5898 74.3555 73.5391C77.5273 72.4805 80.2266 70.9688 82.3984 69.0469C84.5547 67.1211 86.2344 64.7891 87.3711 62.1094C88.4883 59.4492 89.0586 56.4961 89.0586 53.3203C89.0586 50.1172 88.5273 47.1758 87.4883 44.5547Z" fill="#444448"/>
<path d="M50.2656 91.5742V31.9141H41.5273L26.3594 44.6211L29.7227 49.2422C30.0234 49.707 30.4648 50.1055 31.0547 50.4258C31.9805 50.9258 33.1328 51.0547 34.3125 50.6562C34.7617 50.4922 35.2148 50.2344 35.6289 49.8984L39.1406 47.1914C39.1367 47.7227 39.1289 48.2617 39.1289 48.8008V91.5742H30.5938V100.02H58.5V91.5742H50.2656Z" fill="#55ABE3"/>
<path d="M39.7969 56.1875V56.2031L39.9414 56.1758V56.1641C39.8945 56.168 39.8398 56.1797 39.7969 56.1875Z" fill="#444448"/>
<path d="M13.9416 73.4297C15.1018 73.7305 16.3323 74.0195 17.6838 74.2695C21.8791 75.043 24.2502 75.1133 31.762 75.1133V65.4609C31.762 65.4609 -3.89429 68.3984 5.62524 49.3594C5.62524 49.3594 -0.0427259 53.125 0.000243083 59.1367C0.504149 67.4141 8.51977 71.9023 13.9416 73.4297Z" fill="#444448"/>
<path d="M44.1222 16.2383C17.4504 32.2187 4.88397 50.1406 10.2863 56.2305C12.3605 58.5664 17.298 59.3242 24.0597 58.5156C12.4777 56.5 19.3801 45.4883 19.3801 45.4883L19.3957 45.5117C24.2238 36.1992 38.5324 23.0898 60.9152 10.9648C69.1769 6.48437 77.2512 2.85938 84.8527 0C74.1496 1.70703 59.4933 7.03125 44.1222 16.2383Z" fill="#55ABE3"/>
</g>
<defs>
<clipPath id="clip0_2155_3527">
<rect width="100" height="100" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1 @@
<svg enable-background="new 0 0 2447.6 2452.5" viewBox="0 0 2447.6 2452.5" xmlns="http://www.w3.org/2000/svg"><g clip-rule="evenodd" fill-rule="evenodd"><path d="m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z" fill="#36c5f0"/><path d="m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z" fill="#2eb67d"/><path d="m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z" fill="#ecb22e"/><path d="m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0" fill="#e01e5a"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.43 122.41" fill="currentColor">
<title>unsplash</title>
<path d="M83.86 54.15v34.13H38.57V54.15H0v68.26h122.43V54.15H83.86zM38.57 0h45.3v34.13h-45.3z"/>
</svg>

After

Width:  |  Height:  |  Size: 220 B

View File

@ -0,0 +1,4 @@
<svg width="256" height="256" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<title>zapier</title>
<path d="M159.999 128.056a76.545 76.545 0 0 1-4.915 27.024 76.73 76.73 0 0 1-27.032 4.923h-.108c-9.508-.012-18.618-1.75-27.024-4.919A76.567 76.567 0 0 1 96 128.056v-.112a76.608 76.608 0 0 1 4.91-27.02A76.49 76.49 0 0 1 127.945 96h.108a76.476 76.476 0 0 1 27.032 4.923 76.493 76.493 0 0 1 4.915 27.02v.112l-.001.001zm94.223-21.389h-74.716l52.829-52.833a128.586 128.586 0 0 0-13.828-16.349v-.004a128.995 128.995 0 0 0-16.345-13.816l-52.833 52.833V1.782A128.634 128.634 0 0 0 128.064 0h-.132c-7.248.004-14.347.62-21.265 1.782v74.716L53.834 23.665A127.85 127.85 0 0 0 37.497 37.49l-.028.02A128.97 128.97 0 0 0 23.66 53.834l52.837 52.833H1.782S0 120.7 0 127.956v.088c0 7.256.615 14.367 1.782 21.289h74.716l-52.837 52.833a128.923 128.923 0 0 0 30.173 30.173l52.833-52.837v74.72a129.417 129.417 0 0 0 21.24 1.778h.181a129.09 129.09 0 0 0 21.24-1.778v-74.72l52.838 52.837a129.06 129.06 0 0 0 16.341-13.82l.012-.012a129.181 129.181 0 0 0 13.816-16.341l-52.837-52.833h74.724c1.163-6.91 1.77-14 1.778-21.24v-.186c-.008-7.24-.615-14.33-1.778-21.24z" fill="#FF4A00"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

View File

@ -1,14 +1,20 @@
import AddNewsletterModal from '../settings/email/newsletters/AddNewsletterModal';
import AmpModal from '../settings/advanced/integrations/AmpModal';
import ChangeThemeModal from '../settings/site/ThemeModal';
import DesignModal from '../settings/site/DesignModal';
import FirstpromoterModal from '../settings/advanced/integrations/FirstPromoterModal';
import HistoryModal from '../settings/advanced/HistoryModal';
import InviteUserModal from '../settings/general/InviteUserModal';
import NavigationModal from '../settings/site/NavigationModal';
import NiceModal from '@ebay/nice-modal-react';
import PinturaModal from '../settings/advanced/integrations/PinturaModal';
import PortalModal from '../settings/membership/portal/PortalModal';
import React, {createContext, useCallback, useEffect, useRef, useState} from 'react';
import SlackModal from '../settings/advanced/integrations/SlackModal';
import StripeConnectModal from '../settings/membership/stripe/StripeConnectModal';
import TierDetailModal from '../settings/membership/tiers/TierDetailModal';
import UnsplashModal from '../settings/advanced/integrations/UnsplashModal';
import ZapierModal from '../settings/advanced/integrations/ZapierModal';
export type RouteParams = {[key: string]: string}
@ -102,6 +108,18 @@ const handleNavigation = (scroll: boolean = true) => {
NiceModal.show(AddNewsletterModal);
} else if (pathName === 'history/view') {
NiceModal.show(HistoryModal);
} else if (pathName === 'integrations/zapier') {
NiceModal.show(ZapierModal);
} else if (pathName === 'integrations/slack') {
NiceModal.show(SlackModal);
} else if (pathName === 'integrations/amp') {
NiceModal.show(AmpModal);
} else if (pathName === 'integrations/unsplash') {
NiceModal.show(UnsplashModal);
} else if (pathName === 'integrations/firstpromoter') {
NiceModal.show(FirstpromoterModal);
} else if (pathName === 'integrations/pintura') {
NiceModal.show(PinturaModal);
}
if (scroll) {

View File

@ -1,9 +1,11 @@
import CodeInjection from './CodeInjection';
import History from './History';
import Integrations from './Integrations';
import React from 'react';
import SettingSection from '../../../admin-x-ds/settings/SettingSection';
const searchKeywords = {
integrations: ['integration', 'zapier', 'slack', 'amp', 'unsplash', 'first promoter', 'firstpromoter', 'pintura', 'disqus', 'analytics', 'ulysses', 'typeform', 'buffer', 'plausible', 'github'],
codeInjection: ['newsletter', 'enable', 'disable', 'turn on'],
history: ['history', 'log', 'events', 'user events', 'staff']
};
@ -11,6 +13,7 @@ const searchKeywords = {
const AdvancedSettings: React.FC = () => {
return (
<SettingSection keywords={Object.values(searchKeywords).flat()} title='Advanced'>
<Integrations keywords={searchKeywords.integrations} />
<CodeInjection keywords={searchKeywords.codeInjection} />
<History keywords={searchKeywords.history} />
</SettingSection>

View File

@ -0,0 +1,134 @@
import Button from '../../../admin-x-ds/global/Button';
import List from '../../../admin-x-ds/global/List';
import ListItem from '../../../admin-x-ds/global/ListItem';
import React, {useState} from 'react';
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
import TabView from '../../../admin-x-ds/global/TabView';
import useRouting from '../../../hooks/useRouting';
import {ReactComponent as AmpIcon} from '../../../assets/icons/amp.svg';
import {ReactComponent as FirstPromoterIcon} from '../../../assets/icons/firstpromoter.svg';
import {ReactComponent as PinturaIcon} from '../../../assets/icons/pintura.svg';
import {ReactComponent as SlackIcon} from '../../../assets/icons/slack.svg';
import {ReactComponent as UnsplashIcon} from '../../../assets/icons/unsplash.svg';
import {ReactComponent as ZapierIcon} from '../../../assets/icons/zapier.svg';
const Integration: React.FC<{icon?: React.ReactNode, title: string, detail:string, action:() => void}> = ({
icon,
title,
detail,
action
}) => {
return <ListItem
action={<Button color='green' label='Configure' link onClick={action} />}
avatar={icon}
detail={detail}
title={title}
hideActions
onClick={action}
/>;
};
const BuiltInIntegrations: React.FC = () => {
const {updateRoute} = useRouting();
const openModal = (modal: string) => {
updateRoute(modal);
};
return (
<List titleSeparator={false}>
<Integration
action={() => {
openModal('integrations/zapier');
}}
detail='Automation for your apps'
icon={<ZapierIcon className='h-8 w-8' />}
title='Zapier' />
<Integration
action={() => {
openModal('integrations/slack');
}}
detail='A messaging app for teams'
icon={<SlackIcon className='h-8 w-8' />}
title='Slack' />
<Integration
action={() => {
openModal('integrations/amp');
}}
detail='Google Accelerated Mobile Pages'
icon={<AmpIcon className='h-8 w-8' />}
title='AMP' />
<Integration
action={() => {
openModal('integrations/unsplash');
}}
detail='Beautiful, free photos'
icon={<UnsplashIcon className='h-8 w-8' />}
title='Unsplash' />
<Integration
action={() => {
openModal('integrations/firstpromoter');
}}
detail='Launch your member referral program'
icon={<FirstPromoterIcon className='h-8 w-8' />}
title='FirstPromoter' />
<Integration
action={() => {
openModal('integrations/pintura');
}}
detail='Advanced image editing' icon=
{<PinturaIcon className='h-8 w-8' />} title
='Pintura' />
</List>
);
};
const CustomIntegrations: React.FC = () => {
return (
<List>
<Integration action={() => {}} detail='Here we go' title='A custom integration' />
</List>
);
};
const Integrations: React.FC<{ keywords: string[] }> = ({keywords}) => {
const [selectedTab, setSelectedTab] = useState<'built-in' | 'custom'>('built-in');
const tabs = [
{
id: 'built-in',
title: 'Built-in',
contents: <BuiltInIntegrations />
},
{
id: 'custom',
title: 'Custom',
contents: <CustomIntegrations />
}
] as const;
const buttons = (
<Button color='green' label='Add custom integration' link={true} onClick={() => {
// showInviteModal();
}} />
);
return (
<SettingGroup
customButtons={buttons}
description="Make Ghost work with apps and tools"
keywords={keywords}
navid='integrations'
testId='integrations'
title="Integrations"
>
<TabView<'built-in' | 'custom'> selectedTab={selectedTab} tabs={tabs} onTabChange={setSelectedTab} />
</SettingGroup>
);
};
export default Integrations;

View File

@ -0,0 +1,52 @@
import Form from '../../../../admin-x-ds/global/form/Form';
import IntegrationHeader from './IntegrationHeader';
import Modal from '../../../../admin-x-ds/global/modal/Modal';
import NiceModal from '@ebay/nice-modal-react';
import TextField from '../../../../admin-x-ds/global/form/TextField';
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
import {ReactComponent as Icon} from '../../../../assets/icons/amp.svg';
import {useState} from 'react';
const AmpModal = NiceModal.create(() => {
const modal = NiceModal.useModal();
const [enabled, setEnabled] = useState(false);
return (
<Modal
cancelLabel=''
okColor='black'
okLabel='Save'
title=''
onOk={() => {
modal.remove();
}}
>
<IntegrationHeader
detail='Accelerated Mobile Pages'
icon={<Icon className='h-14 w-14' />}
title='AMP'
/>
<div className='mt-7'>
<Form marginBottom={false} title='AMP configuration' grouped>
<Toggle
direction='rtl'
hint={<>Enable Google Accelerated Mobile Pages <strong className='text-red'>[&larr; link to be set]</strong> for your posts</>}
label='Enable AMP'
onChange={(e) => {
setEnabled(e.target.checked);
}}
/>
{enabled && (
<TextField
hint='Tracks AMP traffic in Google Analytics'
placeholder='UA-XXXXXXX-X'
title='Google Analytics Tracking ID'
/>
)}
</Form>
</div>
</Modal>
);
});
export default AmpModal;

View File

@ -0,0 +1,54 @@
import Form from '../../../../admin-x-ds/global/form/Form';
import IntegrationHeader from './IntegrationHeader';
import Modal from '../../../../admin-x-ds/global/modal/Modal';
import NiceModal from '@ebay/nice-modal-react';
import TextField from '../../../../admin-x-ds/global/form/TextField';
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
import {ReactComponent as Icon} from '../../../../assets/icons/firstpromoter.svg';
import {useState} from 'react';
const FirstpromoterModal = NiceModal.create(() => {
const modal = NiceModal.useModal();
const [enabled, setEnabled] = useState(false);
return (
<Modal
cancelLabel=''
okColor='black'
okLabel='Save'
title=''
onOk={() => {
modal.remove();
}}
>
<IntegrationHeader
detail='Launch your own member referral program'
icon={<Icon className='-mt-2 h-14 w-14' />}
title='FirstPromoter'
/>
<div className='mt-7'>
<Form marginBottom={false} title='FirstPromoter configuration' grouped>
<Toggle
direction='rtl'
hint={<>Enable <a className='text-green' href="https://firstpromoter.com/?fpr=ghost&fp_sid=admin" rel="noopener noreferrer" target="_blank">FirstPromoter</a> for tracking referrals</>}
label='Enable FirstPromoter'
onChange={(e) => {
setEnabled(e.target.checked);
}}
/>
{enabled && (
<TextField
hint={<>
Affiliate and referral tracking, find your ID <a className='text-green' href="https://ghost.org/help/firstpromoter-id/" rel="noopener noreferrer" target="_blank">here</a>
</>}
placeholder='XXXXXXXX'
title='FirstPromoter account ID'
/>
)}
</Form>
</div>
</Modal>
);
});
export default FirstpromoterModal;

View File

@ -0,0 +1,30 @@
import React from 'react';
interface IntegrationHeaderProps {
icon?: React.ReactNode;
title?: React.ReactNode;
detail?: React.ReactNode;
extra?: React.ReactNode;
}
const IntegrationHeader: React.FC<IntegrationHeaderProps> = ({
icon,
title,
detail,
extra
}) => {
return (
<div className='flex w-full gap-4'>
<div className='h-14 w-14'>{icon}</div>
<div className='flex flex-col'>
<h3>{title}</h3>
<div className='text-grey-600'>{detail}</div>
{extra && (
<div className='mt-4'>{extra}</div>
)}
</div>
</div>
);
};
export default IntegrationHeader;

View File

@ -0,0 +1,75 @@
import Button from '../../../../admin-x-ds/global/Button';
import Form from '../../../../admin-x-ds/global/form/Form';
import IntegrationHeader from './IntegrationHeader';
import Modal from '../../../../admin-x-ds/global/modal/Modal';
import NiceModal from '@ebay/nice-modal-react';
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
import pinturaScreenshot from '../../../../assets/images/pintura-screenshot.png';
import {ReactComponent as Icon} from '../../../../assets/icons/pintura.svg';
import {useState} from 'react';
const PinturaModal = NiceModal.create(() => {
const modal = NiceModal.useModal();
const [enabled, setEnabled] = useState(false);
return (
<Modal
cancelLabel=''
okColor='black'
okLabel='Save'
title=''
onOk={() => {
modal.remove();
}}
>
<IntegrationHeader
detail='Advanced image editing'
icon={<Icon className='h-12 w-12' />}
title='Pintura'
/>
<div className='mt-7'>
<div className='mb-7 flex items-stretch justify-between gap-4 rounded-sm bg-grey-75 p-7'>
<div className='basis-1/2'>
<p className='mb-4 font-bold'>Add advanced image editing to Ghost, with Pintura</p>
<p className='mb-4 text-sm'>Pintura is a powerful JavaScript image editor that allows you to crop, rotate, annotate and modify images directly inside Ghost.</p>
<p className='text-sm'>Try a demo, purchase a license, and download the required CSS/JS files from pqina.nl/pintura/ to activate this feature.</p>
</div>
<div className='flex grow basis-1/2 flex-col items-end justify-between'>
<img alt='Pintura screenshot' src={pinturaScreenshot} />
<a className='-mb-1 text-sm font-bold text-green' href="https://pqina.nl/pintura/?ref=ghost.org" rel="noopener noreferrer" target="_blank">Find out more &rarr;</a>
</div>
</div>
<Form marginBottom={false} title='Pintura configuration' grouped>
<Toggle
direction='rtl'
hint={<>Enable <a className='text-green' href="https://pqina.nl/pintura/?ref=ghost.org" rel="noopener noreferrer" target="_blank">Pintura</a> for editing your images in Ghost</>}
label='Enable Pintura'
onChange={(e) => {
setEnabled(e.target.checked);
}}
/>
{enabled && (
<>
<div className='flex items-center justify-between'>
<div>
<div>Upload Pintura script</div>
<div className='text-xs text-grey-600'>Upload the <code>pintura-umd.js</code> file from the Pintura package</div>
</div>
<Button color='outline' label='Upload' />
</div>
<div className='flex items-center justify-between'>
<div>
<div>Upload Pintura styles</div>
<div className='text-xs text-grey-600'>Upload the <code>pintura.css</code> file from the Pintura package</div>
</div>
<Button color='outline' label='Upload' />
</div>
</>
)}
</Form>
</div>
</Modal>
);
});
export default PinturaModal;

View File

@ -0,0 +1,50 @@
import Button from '../../../../admin-x-ds/global/Button';
import Form from '../../../../admin-x-ds/global/form/Form';
import IntegrationHeader from './IntegrationHeader';
import Modal from '../../../../admin-x-ds/global/modal/Modal';
import NiceModal from '@ebay/nice-modal-react';
import TextField from '../../../../admin-x-ds/global/form/TextField';
import {ReactComponent as Icon} from '../../../../assets/icons/slack.svg';
const SlackModal = NiceModal.create(() => {
const modal = NiceModal.useModal();
return (
<Modal
cancelLabel=''
okColor='black'
okLabel='Save'
title=''
onOk={() => {
modal.remove();
}}
>
<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
hint={<>
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 here <strong className='text-red'>[&larr; link to be set]</strong>, and grab the URL.
</>}
placeholder='https://hooks.slack.com/services/...'
title='Webhook URL'
/>
<div className='flex w-full items-center gap-2'>
<TextField
containerClassName='flex-grow'
hint='The username to display messages from'
title='Username'
/>
<Button color='outline' label='Send test notification' />
</div>
</Form>
</div>
</Modal>
);
});
export default SlackModal;

View File

@ -0,0 +1,39 @@
import Form from '../../../../admin-x-ds/global/form/Form';
import IntegrationHeader from './IntegrationHeader';
import Modal from '../../../../admin-x-ds/global/modal/Modal';
import NiceModal from '@ebay/nice-modal-react';
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
import {ReactComponent as Icon} from '../../../../assets/icons/unsplash.svg';
const UnsplashModal = NiceModal.create(() => {
const modal = NiceModal.useModal();
return (
<Modal
cancelLabel=''
okColor='black'
okLabel='Close'
title=''
onOk={() => {
modal.remove();
}}
>
<IntegrationHeader
detail='Beautiful, free photos'
icon={<Icon className='h-12 w-12' />}
title='Unsplash'
/>
<div className='mt-7'>
<Form marginBottom={false} grouped>
<Toggle
direction='rtl'
hint={<>Enable <a className='text-green' href="https://unsplash.com" rel="noopener noreferrer" target="_blank">Unsplash</a> image integration for your posts</>}
label='Enable Unsplash'
/>
</Form>
</div>
</Modal>
);
});
export default UnsplashModal;

View File

@ -0,0 +1,45 @@
import IntegrationHeader from './IntegrationHeader';
import Modal from '../../../../admin-x-ds/global/modal/Modal';
import NiceModal from '@ebay/nice-modal-react';
import {ReactComponent as Icon} from '../../../../assets/icons/zapier.svg';
const APIKeys: React.FC = () => {
return (
<table className='m-0'>
<tr>
<td className='p-0 pb-1.5 pr-4 text-grey-600'>Admin API key</td>
<td className='p-0 pb-1.5'>abcdef123456</td>
</tr>
<tr>
<td className='p-0 pb-1.5 pr-4 text-grey-600'>API URL</td>
<td className='p-0 pb-1.5'>https://example.com</td>
</tr>
</table>
);
};
const ZapierModal = NiceModal.create(() => {
const modal = NiceModal.useModal();
return (
<Modal
cancelLabel=''
okColor='black'
okLabel='Close'
title=''
onOk={() => {
modal.remove();
}}
>
<IntegrationHeader
detail='Automation for your favorite apps'
extra={<APIKeys />}
icon={<Icon className='h-14 w-14' />}
title='Zapier'
/>
TBD
</Modal>
);
});
export default ZapierModal;

View File

@ -70,7 +70,7 @@ const UsersList: React.FC<UsersListProps> = ({users}) => {
}
return (
<List>
<List titleSeparator={false}>
{users.map((user) => {
let title = user.name || '';
if (user.status === 'inactive') {