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 (
+
+ );
+}
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 (
+ <>
+