Edit external actions (#2391)

* Initial action editing

* Set previous values in form when editing

* Show previous values when editing an action

* Fix typo
This commit is contained in:
Philipp 2022-11-29 23:14:11 +01:00 committed by GitHub
parent 9049815a31
commit 1b4a88392e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,7 +1,9 @@
import { DeleteOutlined } from '@ant-design/icons'; import { DeleteOutlined, EditOutlined } from '@ant-design/icons';
import { Button, Checkbox, Input, Modal, Space, Table, Typography } from 'antd'; import { Button, Checkbox, Form, Input, Modal, Space, Table, Typography } from 'antd';
import _ from 'lodash';
import React, { useContext, useEffect, useState } from 'react'; import React, { useContext, useEffect, useState } from 'react';
import { FormStatusIndicator } from '../../components/config/FormStatusIndicator'; import { FormStatusIndicator } from '../../components/config/FormStatusIndicator';
import { ExternalAction } from '../../interfaces/external-action';
import { import {
API_EXTERNAL_ACTIONS, API_EXTERNAL_ACTIONS,
postConfigUpdateToAPI, postConfigUpdateToAPI,
@ -16,12 +18,21 @@ let resetTimer = null;
interface Props { interface Props {
onCancel: () => void; onCancel: () => void;
onOk: any; // todo: make better type onOk: (
oldAction: ExternalAction | null,
actionUrl: string,
actionTitle: string,
actionDescription: string,
actionIcon: string,
actionColor: string,
openExternally: boolean,
) => void;
open: boolean; open: boolean;
action: ExternalAction | null;
} }
const NewActionModal = (props: Props) => { const ActionModal = (props: Props) => {
const { onOk, onCancel, open } = props; const { onOk, onCancel, open, action } = props;
const [actionUrl, setActionUrl] = useState(''); const [actionUrl, setActionUrl] = useState('');
const [actionTitle, setActionTitle] = useState(''); const [actionTitle, setActionTitle] = useState('');
@ -30,8 +41,25 @@ const NewActionModal = (props: Props) => {
const [actionColor, setActionColor] = useState(''); const [actionColor, setActionColor] = useState('');
const [openExternally, setOpenExternally] = useState(false); const [openExternally, setOpenExternally] = useState(false);
useEffect(() => {
setActionUrl(action?.url || '');
setActionTitle(action?.title || '');
setActionDescription(action?.description || '');
setActionIcon(action?.icon || '');
setActionColor(action?.color || '');
setOpenExternally(action?.openExternally || false);
}, [action]);
function save() { function save() {
onOk(actionUrl, actionTitle, actionDescription, actionIcon, actionColor, openExternally); onOk(
action,
actionUrl,
actionTitle,
actionDescription,
actionIcon,
actionColor,
openExternally,
);
setActionUrl(''); setActionUrl('');
setActionTitle(''); setActionTitle('');
setActionDescription(''); setActionDescription('');
@ -63,13 +91,14 @@ const NewActionModal = (props: Props) => {
return ( return (
<Modal <Modal
title="Create New Action" destroyOnClose
title={action == null ? 'Create New Action' : 'Edit Action'}
open={open} open={open}
onOk={save} onOk={save}
onCancel={onCancel} onCancel={onCancel}
okButtonProps={okButtonProps} okButtonProps={okButtonProps}
> >
<div> <Form initialValues={action}>
Add the URL for the external action you want to present.{' '} Add the URL for the external action you want to present.{' '}
<strong>Only HTTPS urls are supported.</strong> <strong>Only HTTPS urls are supported.</strong>
<p> <p>
@ -81,54 +110,57 @@ const NewActionModal = (props: Props) => {
Read more about external actions. Read more about external actions.
</a> </a>
</p> </p>
<p> <Form.Item name="url">
<Input <Input
value={actionUrl}
required required
placeholder="https://myserver.com/action (required)" placeholder="https://myserver.com/action (required)"
onChange={input => setActionUrl(input.currentTarget.value.trim())} onChange={input => setActionUrl(input.currentTarget.value.trim())}
type="url" type="url"
pattern={DEFAULT_TEXTFIELD_URL_PATTERN} pattern={DEFAULT_TEXTFIELD_URL_PATTERN}
/> />
</p> </Form.Item>
<p> <Form.Item name="title">
<Input <Input
value={actionTitle} value={actionTitle}
required required
placeholder="Your action title (required)" placeholder="Your action title (required)"
onChange={input => setActionTitle(input.currentTarget.value)} onChange={input => setActionTitle(input.currentTarget.value)}
/> />
</p> </Form.Item>
<p> <Form.Item name="description">
<Input <Input
value={actionDescription} value={actionDescription}
placeholder="Optional description" placeholder="Optional description"
onChange={input => setActionDescription(input.currentTarget.value)} onChange={input => setActionDescription(input.currentTarget.value)}
/> />
</p> </Form.Item>
<p> <Form.Item name="icon">
<Input <Input
value={actionIcon} value={actionIcon}
placeholder="https://myserver.com/action/icon.png (optional)" placeholder="https://myserver.com/action/icon.png (optional)"
onChange={input => setActionIcon(input.currentTarget.value)} onChange={input => setActionIcon(input.currentTarget.value)}
/> />
</p> </Form.Item>
<p> <div>
<Input <Form.Item name="color" style={{ marginBottom: '0px' }}>
type="color" <Input
value={actionColor} type="color"
onChange={input => setActionColor(input.currentTarget.value)} value={actionColor}
/> onChange={input => setActionColor(input.currentTarget.value)}
/>
</Form.Item>
Optional background color of the action button. Optional background color of the action button.
</p> </div>
<Checkbox <Form.Item name="openExternally">
checked={openExternally} <Checkbox
defaultChecked={openExternally} checked={openExternally}
onChange={onOpenExternallyChanged} defaultChecked={openExternally}
> onChange={onOpenExternallyChanged}
Open in a new tab instead of within your page. >
</Checkbox> Open in a new tab instead of within your page.
</div> </Checkbox>
</Form.Item>
</Form>
</Modal> </Modal>
); );
}; };
@ -137,9 +169,10 @@ const Actions = () => {
const serverStatusData = useContext(ServerStatusContext); const serverStatusData = useContext(ServerStatusContext);
const { serverConfig, setFieldInConfigState } = serverStatusData || {}; const { serverConfig, setFieldInConfigState } = serverStatusData || {};
const { externalActions } = serverConfig; const { externalActions } = serverConfig;
const [actions, setActions] = useState([]); const [actions, setActions] = useState<ExternalAction[]>(null);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [submitStatus, setSubmitStatus] = useState(null); const [submitStatus, setSubmitStatus] = useState(null);
const [editAction, setEditAction] = useState<ExternalAction>(null);
const resetStates = () => { const resetStates = () => {
setSubmitStatus(null); setSubmitStatus(null);
@ -182,6 +215,7 @@ const Actions = () => {
} }
async function handleSave( async function handleSave(
oldAction: ExternalAction | null,
url: string, url: string,
title: string, title: string,
description: string, description: string,
@ -191,26 +225,43 @@ const Actions = () => {
) { ) {
try { try {
const actionsData = [...actions]; const actionsData = [...actions];
const updatedActions = actionsData.concat({
const newAction: ExternalAction = {
url, url,
title, title,
description, description,
icon, icon,
color, color,
openExternally, openExternally,
}); };
setActions(updatedActions);
await save(updatedActions); // Replace old action if edited or append the new action
const index = oldAction ? actions.findIndex(item => _.isEqual(item, oldAction)) : -1;
if (index >= 0) {
actionsData[index] = newAction;
} else {
actionsData.push(newAction);
}
setActions(actionsData);
await save(actionsData);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
} }
async function handleEdit(action: ExternalAction) {
setEditAction(action);
setIsModalOpen(true);
}
const showCreateModal = () => { const showCreateModal = () => {
setEditAction(null);
setIsModalOpen(true); setIsModalOpen(true);
}; };
const handleModalSaveButton = ( const handleModalSaveButton = (
oldAction: ExternalAction | null,
actionUrl: string, actionUrl: string,
actionTitle: string, actionTitle: string,
actionDescription: string, actionDescription: string,
@ -219,7 +270,16 @@ const Actions = () => {
openExternally: boolean, openExternally: boolean,
) => { ) => {
setIsModalOpen(false); setIsModalOpen(false);
handleSave(actionUrl, actionTitle, actionDescription, actionIcon, actionColor, openExternally); handleSave(
oldAction,
actionUrl,
actionTitle,
actionDescription,
actionIcon,
actionColor,
openExternally,
);
setEditAction(null);
}; };
const handleModalCancelButton = () => { const handleModalCancelButton = () => {
@ -229,10 +289,11 @@ const Actions = () => {
const columns = [ const columns = [
{ {
title: '', title: '',
key: 'delete', key: 'delete-edit',
render: (text, record) => ( render: (text, record) => (
<Space size="middle"> <Space size="middle">
<Button onClick={() => handleDelete(record)} icon={<DeleteOutlined />} /> <Button onClick={() => handleDelete(record)} icon={<DeleteOutlined />} />
<Button onClick={() => handleEdit(record)} icon={<EditOutlined />} />
</Space> </Space>
), ),
}, },
@ -303,7 +364,8 @@ const Actions = () => {
</Button> </Button>
<FormStatusIndicator status={submitStatus} /> <FormStatusIndicator status={submitStatus} />
<NewActionModal <ActionModal
action={editAction}
open={isModalOpen} open={isModalOpen}
onOk={handleModalSaveButton} onOk={handleModalSaveButton}
onCancel={handleModalCancelButton} onCancel={handleModalCancelButton}