From ecf9733a66e22a7e47271312aaca16ec7793f979 Mon Sep 17 00:00:00 2001 From: An Date: Thu, 30 Sep 2021 16:37:31 -0400 Subject: [PATCH] add: rule settings editor (#39875) --- public/app/features/live/pages/AddNewRule.tsx | 51 +++++++ .../features/live/pages/PipelineAdminPage.tsx | 45 +++++- public/app/features/live/pages/RuleModal.tsx | 132 +++++++++--------- .../live/pages/RuleSettingsEditor.tsx | 47 +++++++ public/app/features/live/pages/types.ts | 34 ++++- public/app/features/live/pages/utils.ts | 24 ++++ 6 files changed, 257 insertions(+), 76 deletions(-) create mode 100644 public/app/features/live/pages/AddNewRule.tsx create mode 100644 public/app/features/live/pages/RuleSettingsEditor.tsx create mode 100644 public/app/features/live/pages/utils.ts diff --git a/public/app/features/live/pages/AddNewRule.tsx b/public/app/features/live/pages/AddNewRule.tsx new file mode 100644 index 00000000000..44304b4ea7f --- /dev/null +++ b/public/app/features/live/pages/AddNewRule.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { Input, Form, Field, Button } from '@grafana/ui'; +import { getBackendSrv } from '@grafana/runtime'; +import { Rule } from './types'; + +interface Props { + onClose: (state: boolean) => void; +} +export function AddNewRule({ onClose }: Props) { + const onSubmit = (formData: Rule) => { + getBackendSrv() + .post(`api/live/channel-rules`, { + pattern: formData.pattern, + settings: { + output: formData.settings.output, + converter: formData.settings.converter, + }, + }) + .then(() => { + // close modal + onClose(false); + }) + .catch((e) => console.error(e)); + }; + + return ( +
+ {({ register, errors }) => ( + <> + + + + + + )} +
+ ); +} diff --git a/public/app/features/live/pages/PipelineAdminPage.tsx b/public/app/features/live/pages/PipelineAdminPage.tsx index b7d03506046..46a1291db49 100644 --- a/public/app/features/live/pages/PipelineAdminPage.tsx +++ b/public/app/features/live/pages/PipelineAdminPage.tsx @@ -1,12 +1,13 @@ import React, { useEffect, useState, ChangeEvent } from 'react'; import { getBackendSrv } from '@grafana/runtime'; -import { Input, Tag, useStyles } from '@grafana/ui'; +import { Input, Tag, useStyles, Button, Modal, IconButton } from '@grafana/ui'; import Page from 'app/core/components/Page/Page'; import { useNavModel } from 'app/core/hooks/useNavModel'; import { css } from '@emotion/css'; import { GrafanaTheme } from '@grafana/data'; -import { Rule, Output } from './types'; +import { Rule, Output, RuleType } from './types'; import { RuleModal } from './RuleModal'; +import { AddNewRule } from './AddNewRule'; function renderOutputTags(key: string, output?: Output): React.ReactNode { if (!output?.type) { @@ -24,7 +25,9 @@ export default function PipelineAdminPage() { const [selectedRule, setSelectedRule] = useState(); const [defaultRules, setDefaultRules] = useState([]); const navModel = useNavModel('live-pipeline'); + const [isOpenEditor, setOpenEditor] = useState(false); const [error, setError] = useState(); + const [clickColumn, setClickColumn] = useState('converter'); const styles = useStyles(getStyles); useEffect(() => { @@ -39,13 +42,14 @@ export default function PipelineAdminPage() { setError(JSON.stringify(e.data, null, 2)); } }); - }, []); + }, [isOpenEditor, isOpen]); const onRowClick = (event: any) => { const pattern = event.target.getAttribute('data-pattern'); const column = event.target.getAttribute('data-column'); - console.log('show:', column); - // setActiveTab(column); + if (column) { + setClickColumn(column); + } setSelectedRule(rules.filter((rule) => rule.pattern === pattern)[0]); setOpen(true); }; @@ -53,12 +57,16 @@ export default function PipelineAdminPage() { const onSearchQueryChange = (e: ChangeEvent) => { if (e.target.value) { setRules(rules.filter((rule) => rule.pattern.toLowerCase().includes(e.target.value.toLowerCase()))); - console.log(e.target.value, rules); } else { setRules(defaultRules); } }; + const onRemoveRule = (pattern: string) => { + getBackendSrv() + .delete(`api/live/channel-rules`, JSON.stringify({ pattern: pattern })) + .catch((e) => console.error(e)); + }; return ( @@ -66,6 +74,9 @@ export default function PipelineAdminPage() {
+
@@ -93,12 +104,29 @@ export default function PipelineAdminPage() { {renderOutputTags('out', rule.settings?.output)} + + onRemoveRule(rule.pattern)}> + ))}
- {isOpen && selectedRule && setOpen(false)} />} + {isOpenEditor && ( + setOpenEditor(false)} title="Add a new rule"> + + + )} + {isOpen && selectedRule && ( + { + setOpen(false); + }} + clickColumn={clickColumn} + /> + )}
); @@ -109,5 +137,8 @@ const getStyles = (theme: GrafanaTheme) => { row: css` cursor: pointer; `, + addNew: css` + margin-left: 10px; + `, }; }; diff --git a/public/app/features/live/pages/RuleModal.tsx b/public/app/features/live/pages/RuleModal.tsx index fa61f1becbf..01cd60bfafb 100644 --- a/public/app/features/live/pages/RuleModal.tsx +++ b/public/app/features/live/pages/RuleModal.tsx @@ -1,23 +1,65 @@ -import React, { useState } from 'react'; -import { Modal, TabContent, TabsBar, Tab, CodeEditor } from '@grafana/ui'; -import { Rule } from './types'; +import React, { useState, useMemo } from 'react'; +import { Modal, TabContent, TabsBar, Tab, Button, useStyles } from '@grafana/ui'; +import { Rule, RuleType, PipeLineEntitiesInfo, RuleSetting } from './types'; +import { getBackendSrv } from '@grafana/runtime'; +import { css } from '@emotion/css'; +import { GrafanaTheme } from '@grafana/data'; +import { RuleSettingsEditor } from './RuleSettingsEditor'; +import { getPipeLineEntities } from './utils'; interface Props { rule: Rule; isOpen: boolean; onClose: () => void; + clickColumn: RuleType; } - -const tabs = [ +interface TabType { + label: string; + value: RuleType; +} +const tabs: TabType[] = [ { label: 'Converter', value: 'converter' }, { label: 'Processor', value: 'processor' }, { label: 'Output', value: 'output' }, ]; -const height = 600; export const RuleModal: React.FC = (props) => { - const { rule, isOpen, onClose } = props; - const [activeTab, setActiveTab] = useState('converter'); + const { isOpen, onClose, clickColumn } = props; + const [rule, setRule] = useState(props.rule); + const [activeTab, setActiveTab] = useState(clickColumn); + // to show color of Save button + const [hasChange, setChange] = useState(false); + const [ruleSetting, setRuleSetting] = useState(rule?.settings?.[activeTab]); + const [entitiesInfo, setEntitiesInfo] = useState(); + const styles = useStyles(getStyles); + + const onRuleSettingChange = (value: RuleSetting) => { + setChange(true); + setRule({ + ...rule, + settings: { + ...rule.settings, + [activeTab]: value, + }, + }); + setRuleSetting(value); + }; + + // load pipeline entities info + useMemo(() => { + getPipeLineEntities().then((data) => { + setEntitiesInfo(data); + }); + }, []); + + const onSave = () => { + getBackendSrv() + .put(`api/live/channel-rules`, rule) + .then(() => { + setChange(false); + }) + .catch((e) => console.error(e)); + }; return ( @@ -30,70 +72,34 @@ export const RuleModal: React.FC = (props) => { active={tab.value === activeTab} onChangeTab={() => { setActiveTab(tab.value); + // to notify children of the new rule + setRuleSetting(rule?.settings?.[tab.value]); }} /> ); })} - {activeTab === 'converter' && } - {activeTab === 'processor' && } - {activeTab === 'output' && } + {entitiesInfo && rule && ( + + )} + ); }; -export const ConverterEditor: React.FC = ({ rule }) => { - const { converter } = rule.settings; - if (!converter) { - return
No converter defined
; - } - - return ( - - ); -}; - -export const ProcessorEditor: React.FC = ({ rule }) => { - const { processor } = rule.settings; - if (!processor) { - return
No processor defined
; - } - - return ( - - ); -}; - -export const OutputEditor: React.FC = ({ rule }) => { - const { output } = rule.settings; - if (!output) { - return
No output defined
; - } - - return ( - - ); +const getStyles = (theme: GrafanaTheme) => { + return { + save: css` + margin-top: 5px; + `, + }; }; diff --git a/public/app/features/live/pages/RuleSettingsEditor.tsx b/public/app/features/live/pages/RuleSettingsEditor.tsx new file mode 100644 index 00000000000..e7c7411127b --- /dev/null +++ b/public/app/features/live/pages/RuleSettingsEditor.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { CodeEditor, Select } from '@grafana/ui'; +import { RuleType, RuleSetting, PipeLineEntitiesInfo } from './types'; + +interface Props { + ruleType: RuleType; + onChange: (value: RuleSetting) => void; + value: RuleSetting; + entitiesInfo: PipeLineEntitiesInfo; +} + +export const RuleSettingsEditor: React.FC = ({ onChange, value, ruleType, entitiesInfo }) => { + return ( + <> +