import { Button, Checkbox, Form, Input, Modal, Select, Space, Table, Typography } from 'antd'; import CodeMirror from '@uiw/react-codemirror'; import { bbedit } from '@uiw/codemirror-theme-bbedit'; import { html as codeMirrorHTML } from '@codemirror/lang-html'; import dynamic from 'next/dynamic'; import React, { ReactElement, useContext, useEffect, useState } from 'react'; import { FormStatusIndicator } from '../../components/admin/FormStatusIndicator'; import { ExternalAction } from '../../interfaces/external-action'; import { API_EXTERNAL_ACTIONS, postConfigUpdateToAPI, RESET_TIMEOUT, } from '../../utils/config-constants'; import { createInputStatus, STATUS_ERROR, STATUS_SUCCESS } from '../../utils/input-statuses'; import { ServerStatusContext } from '../../utils/server-status-context'; import { isValidUrl, DEFAULT_TEXTFIELD_URL_PATTERN } from '../../utils/urls'; import { AdminLayout } from '../../components/layouts/AdminLayout'; const { Title, Paragraph } = Typography; // Lazy loaded components const DeleteOutlined = dynamic(() => import('@ant-design/icons/DeleteOutlined'), { ssr: false, }); const EditOutlined = dynamic(() => import('@ant-design/icons/EditOutlined'), { ssr: false, }); let resetTimer = null; interface Props { onCancel: () => void; onOk: ( oldAction: ExternalAction | null, oldActionIndex: number | null, actionUrl: string, actionHTML: string, actionTitle: string, actionDescription: string, actionIcon: string, actionColor: string, openExternally: boolean, ) => void; open: boolean; action: ExternalAction | null; index: number | null; } // ActionType is only used here to save either only the URL or only the HTML. type ActionType = 'url' | 'html'; const ActionModal = (props: Props) => { const { onOk, onCancel, open, action } = props; const [actionType, setActionType] = useState('url'); const [actionUrl, setActionUrl] = useState(''); const [actionHTML, setActionHTML] = useState(''); const [actionTitle, setActionTitle] = useState(''); const [actionDescription, setActionDescription] = useState(''); const [actionIcon, setActionIcon] = useState(''); const [actionColor, setActionColor] = useState(''); const [openExternally, setOpenExternally] = useState(false); useEffect(() => { setActionType((action?.html?.length || 0) > 0 ? 'html' : 'url'); setActionUrl(action?.url || ''); setActionHTML(action?.html || ''); setActionTitle(action?.title || ''); setActionDescription(action?.description || ''); setActionIcon(action?.icon || ''); setActionColor(action?.color || ''); setOpenExternally(action?.openExternally || false); }, [action]); function save() { onOk( action, props.index, // Save only one of the properties actionType === 'html' ? '' : actionUrl, actionType === 'html' ? actionHTML : '', actionTitle, actionDescription, actionIcon, actionColor, openExternally, ); setActionUrl(''); setActionHTML(''); setActionTitle(''); setActionDescription(''); setActionIcon(''); setActionColor(''); setOpenExternally(false); } function canSave(): Boolean { if (actionType === 'html') { return actionHTML !== '' && actionTitle !== ''; } return isValidUrl(actionUrl, ['https:']) && actionTitle !== ''; } const okButtonProps = { disabled: !canSave(), }; const onOpenExternallyChanged = checkbox => { setOpenExternally(checkbox.target.checked); }; const onActionHTMLChanged = (newActionHTML: string) => { setActionHTML(newActionHTML); }; return (
Add the URL for the external action you want to present.{' '} Only HTTPS URLs and embeds are supported.

Read more about external actions.

setActionUrl(input.currentTarget.value.trim())} type="url" pattern={DEFAULT_TEXTFIELD_URL_PATTERN} /> )} setActionTitle(input.currentTarget.value)} /> setActionDescription(input.currentTarget.value)} /> setActionIcon(input.currentTarget.value)} />
setActionColor(input.currentTarget.value)} /> Optional background color of the action button.
{actionType === 'html' ? null : ( Open in a new tab instead of within your page. )}
); }; const Actions = () => { const serverStatusData = useContext(ServerStatusContext); const { serverConfig, setFieldInConfigState } = serverStatusData || {}; const { externalActions } = serverConfig; const [actions, setActions] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); const [submitStatus, setSubmitStatus] = useState(null); const [editAction, setEditAction] = useState(null); const [editActionIndex, setEditActionIndex] = useState(-1); const resetStates = () => { setSubmitStatus(null); resetTimer = null; clearTimeout(resetTimer); }; useEffect(() => { setActions(externalActions || []); }, [externalActions]); async function save(actionsData) { await postConfigUpdateToAPI({ apiPath: API_EXTERNAL_ACTIONS, data: { value: actionsData }, onSuccess: () => { setFieldInConfigState({ fieldName: 'externalActions', value: actionsData, path: '' }); setSubmitStatus(createInputStatus(STATUS_SUCCESS, 'Updated.')); resetTimer = setTimeout(resetStates, RESET_TIMEOUT); }, onError: (message: string) => { console.log(message); setSubmitStatus(createInputStatus(STATUS_ERROR, message)); resetTimer = setTimeout(resetStates, RESET_TIMEOUT); }, }); } async function handleDelete(action, index) { const actionsData = [...actions]; actionsData.splice(index, 1); try { setActions(actionsData); save(actionsData); } catch (error) { console.error(error); } } async function handleSave( oldAction: ExternalAction | null, oldActionIndex: number, url: string, html: string, title: string, description: string, icon: string, color: string, openExternally: boolean, ) { try { const actionsData = [...actions]; const newAction: ExternalAction = { url, html, title, description, icon, color, openExternally, }; // Replace old action if edited or append the new action if (oldActionIndex >= 0) { actionsData[oldActionIndex] = newAction; } else { actionsData.push(newAction); } setActions(actionsData); await save(actionsData); } catch (error) { console.error(error); } } async function handleEdit(action: ExternalAction, index) { setEditActionIndex(index); setEditAction(action); setIsModalOpen(true); } const showCreateModal = () => { setEditAction(null); setEditActionIndex(-1); setIsModalOpen(true); }; const handleModalSaveButton = ( oldAction: ExternalAction | null, oldActionIndex: number, actionUrl: string, actionHTML: string, actionTitle: string, actionDescription: string, actionIcon: string, actionColor: string, openExternally: boolean, ) => { setIsModalOpen(false); handleSave( oldAction, oldActionIndex, actionUrl, actionHTML, actionTitle, actionDescription, actionIcon, actionColor, openExternally, ); setEditAction(null); setEditActionIndex(-1); }; const handleModalCancelButton = () => { setIsModalOpen(false); }; const columns = [ { title: '', key: 'delete-edit', render: (text, record, index) => (