chore(ui): Refactor action and connector form fields into single component
This commit is contained in:
15
jsconfig.json
Normal file
15
jsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"target": "ES2022",
|
||||||
|
"jsx": "react",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strictFunctionTypes": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"**/node_modules/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import GithubIssueCommenterAction from './actions/GithubIssueCommenterAction';
|
|||||||
import GithubRepositoryAction from './actions/GithubRepositoryAction';
|
import GithubRepositoryAction from './actions/GithubRepositoryAction';
|
||||||
import TwitterPostAction from './actions/TwitterPostAction';
|
import TwitterPostAction from './actions/TwitterPostAction';
|
||||||
import SendMailAction from './actions/SendMailAction';
|
import SendMailAction from './actions/SendMailAction';
|
||||||
|
import GenerateImageAction from './actions/GenerateImageAction';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActionForm component for configuring an action
|
* ActionForm component for configuring an action
|
||||||
@@ -103,46 +104,7 @@ const ActionForm = ({ actions = [], onChange, onRemove, onAdd }) => {
|
|||||||
case 'send-mail':
|
case 'send-mail':
|
||||||
return <SendMailAction {...actionProps} />;
|
return <SendMailAction {...actionProps} />;
|
||||||
case 'generate_image':
|
case 'generate_image':
|
||||||
return (
|
return <GenerateImageAction {...actionProps} />;
|
||||||
<div className="generate-image-action">
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`apiKey${index}`}>OpenAI API Key</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`apiKey${index}`}
|
|
||||||
value={getConfigValue(action, 'apiKey', '')}
|
|
||||||
onChange={(e) => onActionConfigChange(index, 'apiKey', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="sk-..."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`apiURL${index}`}>API URL (Optional)</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`apiURL${index}`}
|
|
||||||
value={getConfigValue(action, 'apiURL', 'https://api.openai.com/v1')}
|
|
||||||
onChange={(e) => onActionConfigChange(index, 'apiURL', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="https://api.openai.com/v1"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`model${index}`}>Model</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`model${index}`}
|
|
||||||
value={getConfigValue(action, 'model', 'dall-e-3')}
|
|
||||||
onChange={(e) => onActionConfigChange(index, 'model', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="dall-e-3"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Image generation model (e.g., dall-e-3)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
default:
|
default:
|
||||||
return <FallbackAction {...actionProps} />;
|
return <FallbackAction {...actionProps} />;
|
||||||
}
|
}
|
||||||
|
|||||||
44
webui/react-ui/src/components/actions/BaseAction.jsx
Normal file
44
webui/react-ui/src/components/actions/BaseAction.jsx
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import FormFieldDefinition from '../common/FormFieldDefinition';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base action component that renders form fields based on field definitions
|
||||||
|
*
|
||||||
|
* @param {Object} props Component props
|
||||||
|
* @param {number} props.index Action index
|
||||||
|
* @param {Function} props.onActionConfigChange Handler for config changes
|
||||||
|
* @param {Function} props.getConfigValue Helper to get config values
|
||||||
|
* @param {Array} props.fields Field definitions for this action
|
||||||
|
*/
|
||||||
|
const BaseAction = ({
|
||||||
|
index,
|
||||||
|
onActionConfigChange,
|
||||||
|
getConfigValue,
|
||||||
|
fields = []
|
||||||
|
}) => {
|
||||||
|
// Create an object with all the current values
|
||||||
|
const currentValues = {};
|
||||||
|
|
||||||
|
// Pre-populate with current values or defaults
|
||||||
|
fields.forEach(field => {
|
||||||
|
currentValues[field.name] = getConfigValue(field.name, field.defaultValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle field value changes
|
||||||
|
const handleFieldChange = (name, value) => {
|
||||||
|
onActionConfigChange(name, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="action-template">
|
||||||
|
<FormFieldDefinition
|
||||||
|
fields={fields}
|
||||||
|
values={currentValues}
|
||||||
|
onChange={handleFieldChange}
|
||||||
|
idPrefix={`action${index}_`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BaseAction;
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import BaseAction from './BaseAction';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate Image action component
|
||||||
|
*/
|
||||||
|
const GenerateImageAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for Generate Image action
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'apiKey',
|
||||||
|
label: 'OpenAI API Key',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'sk-...',
|
||||||
|
helpText: 'Your OpenAI API key for image generation',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'apiURL',
|
||||||
|
label: 'API URL',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'http://localai:8081',
|
||||||
|
helpText: 'OpenAI compatible API endpoint URL',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'model',
|
||||||
|
label: 'Model',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'dall-e-3',
|
||||||
|
helpText: 'Image generation model',
|
||||||
|
required: false,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseAction
|
||||||
|
index={index}
|
||||||
|
onActionConfigChange={onActionConfigChange}
|
||||||
|
getConfigValue={getConfigValue}
|
||||||
|
fields={fields}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GenerateImageAction;
|
||||||
@@ -1,61 +1,57 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseAction from './BaseAction';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub Issue Closer action component
|
* GitHub Issue Closer action component
|
||||||
*/
|
*/
|
||||||
const GithubIssueCloserAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
const GithubIssueCloserAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for GitHub Issue Closer action
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'token',
|
||||||
|
label: 'GitHub Token',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'ghp_...',
|
||||||
|
helpText: 'Personal access token with repo scope',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'owner',
|
||||||
|
label: 'Repository Owner',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'username or organization',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'repository',
|
||||||
|
label: 'Repository Name',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'repository-name',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customActionName',
|
||||||
|
label: 'Custom Action Name (Optional)',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'close_github_issue',
|
||||||
|
helpText: 'Custom name for this action (optional)',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="github-issue-closer-action">
|
<BaseAction
|
||||||
<div className="form-group mb-3">
|
index={index}
|
||||||
<label htmlFor={`githubToken${index}`}>GitHub Token</label>
|
onActionConfigChange={onActionConfigChange}
|
||||||
<input
|
getConfigValue={getConfigValue}
|
||||||
type="text"
|
fields={fields}
|
||||||
id={`githubToken${index}`}
|
/>
|
||||||
value={getConfigValue('token', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('token', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="ghp_..."
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Personal access token with repo scope</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubOwner${index}`}>Repository Owner</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubOwner${index}`}
|
|
||||||
value={getConfigValue('owner', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('owner', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="username or organization"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubRepo${index}`}>Repository Name</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubRepo${index}`}
|
|
||||||
value={getConfigValue('repository', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('repository', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="repository-name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`customActionName${index}`}>Custom Action Name (Optional)</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`customActionName${index}`}
|
|
||||||
value={getConfigValue('customActionName', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('customActionName', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="close_github_issue"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Custom name for this action (optional)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,61 +1,57 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseAction from './BaseAction';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub Issue Commenter action component
|
* GitHub Issue Commenter action component
|
||||||
*/
|
*/
|
||||||
const GithubIssueCommenterAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
const GithubIssueCommenterAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for GitHub Issue Commenter action
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'token',
|
||||||
|
label: 'GitHub Token',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'ghp_...',
|
||||||
|
helpText: 'Personal access token with repo scope',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'owner',
|
||||||
|
label: 'Repository Owner',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'username or organization',
|
||||||
|
helpText: 'Owner of the repository',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'repository',
|
||||||
|
label: 'Repository Name',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'repository-name',
|
||||||
|
helpText: 'Name of the repository',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customActionName',
|
||||||
|
label: 'Custom Action Name (Optional)',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'comment_on_github_issue',
|
||||||
|
helpText: 'Custom name for this action (optional)',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="github-issue-commenter-action">
|
<BaseAction
|
||||||
<div className="form-group mb-3">
|
index={index}
|
||||||
<label htmlFor={`githubToken${index}`}>GitHub Token</label>
|
onActionConfigChange={onActionConfigChange}
|
||||||
<input
|
getConfigValue={getConfigValue}
|
||||||
type="text"
|
fields={fields}
|
||||||
id={`githubToken${index}`}
|
/>
|
||||||
value={getConfigValue('token', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('token', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="ghp_..."
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Personal access token with repo scope</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubOwner${index}`}>Repository Owner</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubOwner${index}`}
|
|
||||||
value={getConfigValue('owner', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('owner', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="username or organization"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubRepo${index}`}>Repository Name</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubRepo${index}`}
|
|
||||||
value={getConfigValue('repository', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('repository', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="repository-name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`customActionName${index}`}>Custom Action Name (Optional)</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`customActionName${index}`}
|
|
||||||
value={getConfigValue('customActionName', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('customActionName', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="comment_on_github_issue"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Custom name for this action (optional)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,74 +1,66 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseAction from './BaseAction';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub Issue Labeler action component
|
* GitHub Issue Labeler action component
|
||||||
*/
|
*/
|
||||||
const GithubIssueLabelerAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
const GithubIssueLabelerAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for GitHub Issue Labeler action
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'token',
|
||||||
|
label: 'GitHub Token',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'ghp_...',
|
||||||
|
helpText: 'Personal access token with repo scope',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'owner',
|
||||||
|
label: 'Repository Owner',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'username or organization',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'repository',
|
||||||
|
label: 'Repository Name',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'repository-name',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'availableLabels',
|
||||||
|
label: 'Available Labels',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: 'bug,enhancement',
|
||||||
|
placeholder: 'bug,enhancement,documentation',
|
||||||
|
helpText: 'Comma-separated list of available labels',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customActionName',
|
||||||
|
label: 'Custom Action Name (Optional)',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'add_label_to_issue',
|
||||||
|
helpText: 'Custom name for this action (optional)',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="github-issue-labeler-action">
|
<BaseAction
|
||||||
<div className="form-group mb-3">
|
index={index}
|
||||||
<label htmlFor={`githubToken${index}`}>GitHub Token</label>
|
onActionConfigChange={onActionConfigChange}
|
||||||
<input
|
getConfigValue={getConfigValue}
|
||||||
type="text"
|
fields={fields}
|
||||||
id={`githubToken${index}`}
|
/>
|
||||||
value={getConfigValue('token', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('token', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="ghp_..."
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Personal access token with repo scope</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubOwner${index}`}>Repository Owner</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubOwner${index}`}
|
|
||||||
value={getConfigValue('owner', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('owner', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="username or organization"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubRepo${index}`}>Repository Name</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubRepo${index}`}
|
|
||||||
value={getConfigValue('repository', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('repository', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="repository-name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`availableLabels${index}`}>Available Labels</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`availableLabels${index}`}
|
|
||||||
value={getConfigValue('availableLabels', 'bug,enhancement')}
|
|
||||||
onChange={(e) => onActionConfigChange('availableLabels', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="bug,enhancement,documentation"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Comma-separated list of available labels</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`customActionName${index}`}>Custom Action Name (Optional)</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`customActionName${index}`}
|
|
||||||
value={getConfigValue('customActionName', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('customActionName', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="add_label_to_issue"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Custom name for this action (optional)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,61 +1,57 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseAction from './BaseAction';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub Issue Opener action component
|
* GitHub Issue Opener action component
|
||||||
*/
|
*/
|
||||||
const GithubIssueOpenerAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
const GithubIssueOpenerAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for GitHub Issue Opener action
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'token',
|
||||||
|
label: 'GitHub Token',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'ghp_...',
|
||||||
|
helpText: 'Personal access token with repo scope',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'owner',
|
||||||
|
label: 'Repository Owner',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'username or organization',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'repository',
|
||||||
|
label: 'Repository Name',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'repository-name',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customActionName',
|
||||||
|
label: 'Custom Action Name (Optional)',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'open_github_issue',
|
||||||
|
helpText: 'Custom name for this action (optional)',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="github-issue-opener-action">
|
<BaseAction
|
||||||
<div className="form-group mb-3">
|
index={index}
|
||||||
<label htmlFor={`githubToken${index}`}>GitHub Token</label>
|
onActionConfigChange={onActionConfigChange}
|
||||||
<input
|
getConfigValue={getConfigValue}
|
||||||
type="text"
|
fields={fields}
|
||||||
id={`githubToken${index}`}
|
/>
|
||||||
value={getConfigValue('token', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('token', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="ghp_..."
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Personal access token with repo scope</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubOwner${index}`}>Repository Owner</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubOwner${index}`}
|
|
||||||
value={getConfigValue('owner', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('owner', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="username or organization"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubRepo${index}`}>Repository Name</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubRepo${index}`}
|
|
||||||
value={getConfigValue('repository', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('repository', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="repository-name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`customActionName${index}`}>Custom Action Name (Optional)</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`customActionName${index}`}
|
|
||||||
value={getConfigValue('customActionName', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('customActionName', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="open_github_issue"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Custom name for this action (optional)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseAction from './BaseAction';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub Repository action component for repository-related actions
|
* GitHub Repository action component for repository-related actions
|
||||||
@@ -8,58 +9,53 @@ import React from 'react';
|
|||||||
* - github-readme
|
* - github-readme
|
||||||
*/
|
*/
|
||||||
const GithubRepositoryAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
const GithubRepositoryAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for GitHub Repository action
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'token',
|
||||||
|
label: 'GitHub Token',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'ghp_...',
|
||||||
|
helpText: 'Personal access token with repo scope',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'owner',
|
||||||
|
label: 'Repository Owner',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'username or organization',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'repository',
|
||||||
|
label: 'Repository Name',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'repository-name',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'customActionName',
|
||||||
|
label: 'Custom Action Name (Optional)',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'github_repo_action',
|
||||||
|
helpText: 'Custom name for this action (optional)',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="github-repository-action">
|
<BaseAction
|
||||||
<div className="form-group mb-3">
|
index={index}
|
||||||
<label htmlFor={`githubToken${index}`}>GitHub Token</label>
|
onActionConfigChange={onActionConfigChange}
|
||||||
<input
|
getConfigValue={getConfigValue}
|
||||||
type="text"
|
fields={fields}
|
||||||
id={`githubToken${index}`}
|
/>
|
||||||
value={getConfigValue('token', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('token', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="ghp_..."
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Personal access token with repo scope</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubOwner${index}`}>Repository Owner</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubOwner${index}`}
|
|
||||||
value={getConfigValue('owner', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('owner', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="username or organization"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubRepo${index}`}>Repository Name</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubRepo${index}`}
|
|
||||||
value={getConfigValue('repository', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('repository', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="repository-name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`customActionName${index}`}>Custom Action Name (Optional)</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`customActionName${index}`}
|
|
||||||
value={getConfigValue('customActionName', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('customActionName', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="github_repo_action"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Custom name for this action (optional)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,74 +1,66 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseAction from './BaseAction';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SendMail action component
|
* SendMail action component
|
||||||
*/
|
*/
|
||||||
const SendMailAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
const SendMailAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for SendMail action
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'email',
|
||||||
|
label: 'Email',
|
||||||
|
type: 'email',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'your-email@example.com',
|
||||||
|
helpText: 'Email address to send from',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'username',
|
||||||
|
label: 'Username',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'SMTP username (often same as email)',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'password',
|
||||||
|
label: 'Password',
|
||||||
|
type: 'password',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'SMTP password or app password',
|
||||||
|
helpText: 'For Gmail, use an app password',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'smtpHost',
|
||||||
|
label: 'SMTP Host',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'smtp.gmail.com',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'smtpPort',
|
||||||
|
label: 'SMTP Port',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '587',
|
||||||
|
placeholder: '587',
|
||||||
|
helpText: 'Common ports: 587 (TLS), 465 (SSL)',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="send-mail-action">
|
<BaseAction
|
||||||
<div className="form-group mb-3">
|
index={index}
|
||||||
<label htmlFor={`email${index}`}>Email</label>
|
onActionConfigChange={onActionConfigChange}
|
||||||
<input
|
getConfigValue={getConfigValue}
|
||||||
type="email"
|
fields={fields}
|
||||||
id={`email${index}`}
|
/>
|
||||||
value={getConfigValue('email', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('email', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="your-email@example.com"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Email address to send from</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`username${index}`}>Username</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`username${index}`}
|
|
||||||
value={getConfigValue('username', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('username', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="SMTP username (often same as email)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`password${index}`}>Password</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id={`password${index}`}
|
|
||||||
value={getConfigValue('password', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('password', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="SMTP password or app password"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">For Gmail, use an app password</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`smtpHost${index}`}>SMTP Host</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`smtpHost${index}`}
|
|
||||||
value={getConfigValue('smtpHost', '')}
|
|
||||||
onChange={(e) => onActionConfigChange('smtpHost', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="smtp.gmail.com"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`smtpPort${index}`}>SMTP Port</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`smtpPort${index}`}
|
|
||||||
value={getConfigValue('smtpPort', '587')}
|
|
||||||
onChange={(e) => onActionConfigChange('smtpPort', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="587"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Common ports: 587 (TLS), 465 (SSL)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +1,38 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseAction from './BaseAction';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Twitter Post action component
|
* Twitter Post action component
|
||||||
*/
|
*/
|
||||||
const TwitterPostAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
const TwitterPostAction = ({ index, onActionConfigChange, getConfigValue }) => {
|
||||||
return (
|
// Field definitions for Twitter Post action
|
||||||
<div className="twitter-post-action">
|
const fields = [
|
||||||
<div className="form-group mb-3">
|
{
|
||||||
<label htmlFor={`twitterToken${index}`}>Twitter API Token</label>
|
name: 'token',
|
||||||
<input
|
label: 'Twitter API Token',
|
||||||
type="text"
|
type: 'text',
|
||||||
id={`twitterToken${index}`}
|
defaultValue: '',
|
||||||
value={getConfigValue('token', '')}
|
placeholder: 'Twitter API token',
|
||||||
onChange={(e) => onActionConfigChange('token', e.target.value)}
|
helpText: 'Twitter API token with posting permissions',
|
||||||
className="form-control"
|
required: true,
|
||||||
placeholder="Twitter API token"
|
},
|
||||||
/>
|
{
|
||||||
<small className="form-text text-muted">Twitter API token with posting permissions</small>
|
name: 'noCharacterLimits',
|
||||||
</div>
|
label: 'Disable character limit (280 characters)',
|
||||||
|
type: 'checkbox',
|
||||||
|
defaultValue: 'false',
|
||||||
|
helpText: 'Enable to bypass the 280 character limit check',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
return (
|
||||||
<div className="form-check">
|
<BaseAction
|
||||||
<input
|
index={index}
|
||||||
type="checkbox"
|
onActionConfigChange={onActionConfigChange}
|
||||||
id={`noCharacterLimits${index}`}
|
getConfigValue={getConfigValue}
|
||||||
checked={getConfigValue('noCharacterLimits', '') === 'true'}
|
fields={fields}
|
||||||
onChange={(e) => onActionConfigChange('noCharacterLimits', e.target.checked ? 'true' : 'false')}
|
/>
|
||||||
className="form-check-input"
|
|
||||||
/>
|
|
||||||
<label className="form-check-label" htmlFor={`noCharacterLimits${index}`}>
|
|
||||||
Disable character limit (280 characters)
|
|
||||||
</label>
|
|
||||||
<small className="form-text text-muted d-block">Enable to bypass the 280 character limit check</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
107
webui/react-ui/src/components/common/FormField.jsx
Normal file
107
webui/react-ui/src/components/common/FormField.jsx
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reusable form field component that handles different input types
|
||||||
|
*
|
||||||
|
* @param {Object} props Component props
|
||||||
|
* @param {string} props.id Unique identifier for the input
|
||||||
|
* @param {string} props.label Label text for the field
|
||||||
|
* @param {string} props.type Input type (text, checkbox, select, textarea)
|
||||||
|
* @param {string|boolean} props.value Current value of the field
|
||||||
|
* @param {Function} props.onChange Handler for value changes
|
||||||
|
* @param {string} props.placeholder Placeholder text
|
||||||
|
* @param {string} props.helpText Help text to display below the field
|
||||||
|
* @param {Array} props.options Options for select inputs
|
||||||
|
* @param {boolean} props.required Whether the field is required
|
||||||
|
*/
|
||||||
|
const FormField = ({
|
||||||
|
id,
|
||||||
|
label,
|
||||||
|
type = 'text',
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
placeholder = '',
|
||||||
|
helpText = '',
|
||||||
|
options = [],
|
||||||
|
required = false,
|
||||||
|
}) => {
|
||||||
|
// Render different input types
|
||||||
|
const renderInput = () => {
|
||||||
|
switch (type) {
|
||||||
|
case 'checkbox':
|
||||||
|
return (
|
||||||
|
<div className="form-check">
|
||||||
|
<label className="checkbox-label" htmlFor={id}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id={id}
|
||||||
|
checked={value === true || value === 'true'}
|
||||||
|
onChange={(e) => onChange(e.target.checked ? 'true' : 'false')}
|
||||||
|
/>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
{helpText && <small className="form-text text-muted d-block">{helpText}</small>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
case 'select':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label htmlFor={id}>{label}</label>
|
||||||
|
<select
|
||||||
|
id={id}
|
||||||
|
value={value || ''}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
className="form-control"
|
||||||
|
required={required}
|
||||||
|
>
|
||||||
|
{options.map((option) => (
|
||||||
|
<option key={option.value} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
{helpText && <small className="form-text text-muted">{helpText}</small>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
case 'textarea':
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label htmlFor={id}>{label}</label>
|
||||||
|
<textarea
|
||||||
|
id={id}
|
||||||
|
value={value || ''}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
className="form-control"
|
||||||
|
placeholder={placeholder}
|
||||||
|
required={required}
|
||||||
|
/>
|
||||||
|
{helpText && <small className="form-text text-muted">{helpText}</small>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<label htmlFor={id}>{label}</label>
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
id={id}
|
||||||
|
value={value || ''}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
className="form-control"
|
||||||
|
placeholder={placeholder}
|
||||||
|
required={required}
|
||||||
|
/>
|
||||||
|
{helpText && <small className="form-text text-muted">{helpText}</small>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="form-group mb-3">
|
||||||
|
{renderInput()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormField;
|
||||||
42
webui/react-ui/src/components/common/FormFieldDefinition.jsx
Normal file
42
webui/react-ui/src/components/common/FormFieldDefinition.jsx
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import FormField from './FormField';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that renders a form based on a field definition
|
||||||
|
*
|
||||||
|
* @param {Object} props Component props
|
||||||
|
* @param {Array} props.fields Array of field definitions
|
||||||
|
* @param {Object} props.values Current values for the fields
|
||||||
|
* @param {Function} props.onChange Handler for field value changes
|
||||||
|
* @param {string} props.idPrefix Prefix for field IDs
|
||||||
|
*/
|
||||||
|
const FormFieldDefinition = ({
|
||||||
|
fields,
|
||||||
|
values,
|
||||||
|
onChange,
|
||||||
|
idPrefix = '',
|
||||||
|
}) => {
|
||||||
|
// Ensure values is an object
|
||||||
|
const safeValues = values || {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="form-fields">
|
||||||
|
{fields.map((field) => (
|
||||||
|
<FormField
|
||||||
|
key={field.name}
|
||||||
|
id={`${idPrefix}${field.name}`}
|
||||||
|
label={field.label}
|
||||||
|
type={field.type}
|
||||||
|
value={safeValues[field.name] !== undefined ? safeValues[field.name] : field.defaultValue}
|
||||||
|
onChange={(value) => onChange(field.name, value)}
|
||||||
|
placeholder={field.placeholder || ''}
|
||||||
|
helpText={field.helpText || ''}
|
||||||
|
options={field.options || []}
|
||||||
|
required={field.required || false}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FormFieldDefinition;
|
||||||
46
webui/react-ui/src/components/connectors/BaseConnector.jsx
Normal file
46
webui/react-ui/src/components/connectors/BaseConnector.jsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import FormFieldDefinition from '../common/FormFieldDefinition';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base connector component that renders form fields based on field definitions
|
||||||
|
*
|
||||||
|
* @param {Object} props Component props
|
||||||
|
* @param {Object} props.connector Connector data
|
||||||
|
* @param {number} props.index Connector index
|
||||||
|
* @param {Function} props.onConnectorConfigChange Handler for config changes
|
||||||
|
* @param {Function} props.getConfigValue Helper to get config values
|
||||||
|
* @param {Array} props.fields Field definitions for this connector
|
||||||
|
*/
|
||||||
|
const BaseConnector = ({
|
||||||
|
connector,
|
||||||
|
index,
|
||||||
|
onConnectorConfigChange,
|
||||||
|
getConfigValue,
|
||||||
|
fields = []
|
||||||
|
}) => {
|
||||||
|
// Create an object with all the current values
|
||||||
|
const currentValues = {};
|
||||||
|
|
||||||
|
// Pre-populate with current values or defaults
|
||||||
|
fields.forEach(field => {
|
||||||
|
currentValues[field.name] = getConfigValue(connector, field.name, field.defaultValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle field value changes
|
||||||
|
const handleFieldChange = (name, value) => {
|
||||||
|
onConnectorConfigChange(index, name, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="connector-template">
|
||||||
|
<FormFieldDefinition
|
||||||
|
fields={fields}
|
||||||
|
values={currentValues}
|
||||||
|
onChange={handleFieldChange}
|
||||||
|
idPrefix={`connector${index}_`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BaseConnector;
|
||||||
@@ -1,37 +1,40 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseConnector from './BaseConnector';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discord connector template
|
* Discord connector template
|
||||||
*/
|
*/
|
||||||
const DiscordConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
const DiscordConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
||||||
return (
|
// Field definitions for Discord connector
|
||||||
<div className="connector-template">
|
const fields = [
|
||||||
<div className="form-group mb-3">
|
{
|
||||||
<label htmlFor={`discordToken${index}`}>Discord Bot Token</label>
|
name: 'token',
|
||||||
<input
|
label: 'Discord Bot Token',
|
||||||
type="text"
|
type: 'text',
|
||||||
id={`discordToken${index}`}
|
defaultValue: '',
|
||||||
value={getConfigValue(connector, 'token', '')}
|
placeholder: 'Bot token from Discord Developer Portal',
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'token', e.target.value)}
|
helpText: 'Get this from the Discord Developer Portal',
|
||||||
className="form-control"
|
required: true,
|
||||||
placeholder="Bot token from Discord Developer Portal"
|
},
|
||||||
/>
|
{
|
||||||
<small className="form-text text-muted">Get this from the Discord Developer Portal</small>
|
name: 'defaultChannel',
|
||||||
</div>
|
label: 'Default Channel',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: '123456789012345678',
|
||||||
|
helpText: 'Channel ID to always answer even if not mentioned',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
return (
|
||||||
<label htmlFor={`discordDefaultChannel${index}`}>Default Channel</label>
|
<BaseConnector
|
||||||
<input
|
connector={connector}
|
||||||
type="text"
|
index={index}
|
||||||
id={`discordDefaultChannel${index}`}
|
onConnectorConfigChange={onConnectorConfigChange}
|
||||||
value={getConfigValue(connector, 'defaultChannel', '')}
|
getConfigValue={getConfigValue}
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'defaultChannel', e.target.value)}
|
fields={fields}
|
||||||
className="form-control"
|
/>
|
||||||
placeholder="123456789012345678"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Channel ID to always answer even if not mentioned</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,74 +1,70 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseConnector from './BaseConnector';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub Issues connector template
|
* GitHub Issues connector template
|
||||||
*/
|
*/
|
||||||
const GithubIssuesConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
const GithubIssuesConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for GitHub Issues connector
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'token',
|
||||||
|
label: 'GitHub Personal Access Token',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'ghp_...',
|
||||||
|
helpText: 'Personal access token with repo scope',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'owner',
|
||||||
|
label: 'Repository Owner',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'username or organization',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'repository',
|
||||||
|
label: 'Repository Name',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'repository-name',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'replyIfNoReplies',
|
||||||
|
label: 'Reply Behavior',
|
||||||
|
type: 'select',
|
||||||
|
defaultValue: 'false',
|
||||||
|
options: [
|
||||||
|
{ value: 'false', label: 'Reply to all issues' },
|
||||||
|
{ value: 'true', label: 'Only reply to issues with no comments' },
|
||||||
|
],
|
||||||
|
helpText: '',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pollInterval',
|
||||||
|
label: 'Poll Interval',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '10m',
|
||||||
|
placeholder: '10m',
|
||||||
|
helpText: 'How often to check for new issues (e.g., 10m, 1h)',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="connector-template">
|
<BaseConnector
|
||||||
<div className="form-group mb-3">
|
connector={connector}
|
||||||
<label htmlFor={`githubToken${index}`}>GitHub Personal Access Token</label>
|
index={index}
|
||||||
<input
|
onConnectorConfigChange={onConnectorConfigChange}
|
||||||
type="text"
|
getConfigValue={getConfigValue}
|
||||||
id={`githubToken${index}`}
|
fields={fields}
|
||||||
value={getConfigValue(connector, 'token', '')}
|
/>
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'token', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="ghp_..."
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Personal access token with repo scope</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubOwner${index}`}>Repository Owner</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubOwner${index}`}
|
|
||||||
value={getConfigValue(connector, 'owner', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'owner', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="username or organization"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubRepo${index}`}>Repository Name</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubRepo${index}`}
|
|
||||||
value={getConfigValue(connector, 'repository', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'repository', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="repository-name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`replyIfNoReplies${index}`}>Reply Behavior</label>
|
|
||||||
<select
|
|
||||||
id={`replyIfNoReplies${index}`}
|
|
||||||
value={getConfigValue(connector, 'replyIfNoReplies', 'false')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'replyIfNoReplies', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
>
|
|
||||||
<option value="false">Reply to all issues</option>
|
|
||||||
<option value="true">Only reply to issues with no comments</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`pollInterval${index}`}>Poll Interval</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`pollInterval${index}`}
|
|
||||||
value={getConfigValue(connector, 'pollInterval', '10m')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'pollInterval', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="10m"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">How often to check for new issues (e.g., 10m, 1h)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,74 +1,70 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseConnector from './BaseConnector';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GitHub PRs connector template
|
* GitHub PRs connector template
|
||||||
*/
|
*/
|
||||||
const GithubPRsConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
const GithubPRsConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for GitHub PRs connector
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'token',
|
||||||
|
label: 'GitHub Personal Access Token',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'ghp_...',
|
||||||
|
helpText: 'Personal access token with repo scope',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'owner',
|
||||||
|
label: 'Repository Owner',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'username or organization',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'repository',
|
||||||
|
label: 'Repository Name',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'repository-name',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'replyIfNoReplies',
|
||||||
|
label: 'Reply Behavior',
|
||||||
|
type: 'select',
|
||||||
|
defaultValue: 'false',
|
||||||
|
options: [
|
||||||
|
{ value: 'false', label: 'Reply to all PRs' },
|
||||||
|
{ value: 'true', label: 'Only reply to PRs with no comments' },
|
||||||
|
],
|
||||||
|
helpText: '',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pollInterval',
|
||||||
|
label: 'Poll Interval',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '10m',
|
||||||
|
placeholder: '10m',
|
||||||
|
helpText: 'How often to check for new PRs (e.g., 10m, 1h)',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="connector-template">
|
<BaseConnector
|
||||||
<div className="form-group mb-3">
|
connector={connector}
|
||||||
<label htmlFor={`githubToken${index}`}>GitHub Personal Access Token</label>
|
index={index}
|
||||||
<input
|
onConnectorConfigChange={onConnectorConfigChange}
|
||||||
type="text"
|
getConfigValue={getConfigValue}
|
||||||
id={`githubToken${index}`}
|
fields={fields}
|
||||||
value={getConfigValue(connector, 'token', '')}
|
/>
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'token', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="ghp_..."
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Personal access token with repo scope</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubOwner${index}`}>Repository Owner</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubOwner${index}`}
|
|
||||||
value={getConfigValue(connector, 'owner', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'owner', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="username or organization"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`githubRepo${index}`}>Repository Name</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`githubRepo${index}`}
|
|
||||||
value={getConfigValue(connector, 'repository', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'repository', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="repository-name"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`replyIfNoReplies${index}`}>Reply Behavior</label>
|
|
||||||
<select
|
|
||||||
id={`replyIfNoReplies${index}`}
|
|
||||||
value={getConfigValue(connector, 'replyIfNoReplies', 'false')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'replyIfNoReplies', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
>
|
|
||||||
<option value="false">Reply to all PRs</option>
|
|
||||||
<option value="true">Only reply to PRs with no comments</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`pollInterval${index}`}>Poll Interval</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`pollInterval${index}`}
|
|
||||||
value={getConfigValue(connector, 'pollInterval', '10m')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'pollInterval', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="10m"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">How often to check for new PRs (e.g., 10m, 1h)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,75 +1,66 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseConnector from './BaseConnector';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IRC connector template
|
* IRC connector template
|
||||||
*/
|
*/
|
||||||
const IRCConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
const IRCConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for IRC connector
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'server',
|
||||||
|
label: 'IRC Server',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'irc.libera.chat',
|
||||||
|
helpText: 'IRC server address',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'port',
|
||||||
|
label: 'Port',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '6667',
|
||||||
|
placeholder: '6667',
|
||||||
|
helpText: 'IRC server port',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nickname',
|
||||||
|
label: 'Nickname',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'MyAgentBot',
|
||||||
|
helpText: 'Bot nickname',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'channel',
|
||||||
|
label: 'Channel',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: '#channel1',
|
||||||
|
helpText: 'Channel to join',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'alwaysReply',
|
||||||
|
label: 'Always Reply',
|
||||||
|
type: 'checkbox',
|
||||||
|
defaultValue: 'false',
|
||||||
|
helpText: 'If checked, the agent will reply to all messages in the channel',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="connector-template">
|
<BaseConnector
|
||||||
<div className="form-group mb-3">
|
connector={connector}
|
||||||
<label htmlFor={`ircServer${index}`}>IRC Server</label>
|
index={index}
|
||||||
<input
|
onConnectorConfigChange={onConnectorConfigChange}
|
||||||
type="text"
|
getConfigValue={getConfigValue}
|
||||||
id={`ircServer${index}`}
|
fields={fields}
|
||||||
value={getConfigValue(connector, 'server', '')}
|
/>
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'server', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="irc.libera.chat"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`ircPort${index}`}>Port</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`ircPort${index}`}
|
|
||||||
value={getConfigValue(connector, 'port', '6667')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'port', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="6667"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`ircNick${index}`}>Nickname</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`ircNick${index}`}
|
|
||||||
value={getConfigValue(connector, 'nickname', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'nickname', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="MyAgentBot"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`ircChannels${index}`}>Channel</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`ircChannels${index}`}
|
|
||||||
value={getConfigValue(connector, 'channel', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'channel', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="#channel1"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Channel to join</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<div className="form-check">
|
|
||||||
<label className="checkbox-label" htmlFor={`ircAlwaysReply${index}`}>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id={`ircAlwaysReply${index}`}
|
|
||||||
checked={getConfigValue(connector, 'alwaysReply', '') === 'true'}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'alwaysReply', e.target.checked ? 'true' : 'false')}
|
|
||||||
/>
|
|
||||||
Always Reply
|
|
||||||
</label>
|
|
||||||
<small className="form-text text-muted d-block">If checked, the agent will reply to all messages in the channel</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,66 +1,57 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseConnector from './BaseConnector';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Slack connector template
|
* Slack connector template
|
||||||
*/
|
*/
|
||||||
const SlackConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
const SlackConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for Slack connector
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'appToken',
|
||||||
|
label: 'Slack App Token',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'xapp-...',
|
||||||
|
helpText: 'App-level token starting with xapp-',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'botToken',
|
||||||
|
label: 'Slack Bot Token',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'xoxb-...',
|
||||||
|
helpText: 'Bot token starting with xoxb-',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'channelID',
|
||||||
|
label: 'Slack Channel ID',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'C1234567890',
|
||||||
|
helpText: 'Optional: Specific channel ID to join',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'alwaysReply',
|
||||||
|
label: 'Always Reply',
|
||||||
|
type: 'checkbox',
|
||||||
|
defaultValue: 'false',
|
||||||
|
helpText: 'If checked, the agent will reply to all messages in the channel',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="connector-template">
|
<BaseConnector
|
||||||
<div className="form-group mb-3">
|
connector={connector}
|
||||||
<label htmlFor={`slackAppToken${index}`}>Slack App Token</label>
|
index={index}
|
||||||
<input
|
onConnectorConfigChange={onConnectorConfigChange}
|
||||||
type="text"
|
getConfigValue={getConfigValue}
|
||||||
id={`slackAppToken${index}`}
|
fields={fields}
|
||||||
value={getConfigValue(connector, 'appToken', '')}
|
/>
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'appToken', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="xapp-..."
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">App-level token starting with xapp-</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`slackBotToken${index}`}>Slack Bot Token</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`slackBotToken${index}`}
|
|
||||||
value={getConfigValue(connector, 'botToken', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'botToken', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="xoxb-..."
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Bot token starting with xoxb-</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`slackChannelID${index}`}>Slack Channel ID</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`slackChannelID${index}`}
|
|
||||||
value={getConfigValue(connector, 'channelID', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'channelID', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="C1234567890"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Optional: Specific channel ID to join</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<div className="form-check">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
id={`slackAlwaysReply${index}`}
|
|
||||||
checked={getConfigValue(connector, 'alwaysReply', '') === 'true'}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'alwaysReply', e.target.checked ? 'true' : 'false')}
|
|
||||||
className="form-check-input"
|
|
||||||
/>
|
|
||||||
<label className="form-check-label" htmlFor={`slackAlwaysReply${index}`}>
|
|
||||||
Always Reply
|
|
||||||
</label>
|
|
||||||
<small className="form-text text-muted d-block">If checked, the agent will reply to all messages in the channel</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,31 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseConnector from './BaseConnector';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Telegram connector template
|
* Telegram connector template
|
||||||
*/
|
*/
|
||||||
const TelegramConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
const TelegramConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for Telegram connector
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'token',
|
||||||
|
label: 'Telegram Bot Token',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11',
|
||||||
|
helpText: 'Get this from @BotFather on Telegram',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="connector-template">
|
<BaseConnector
|
||||||
<div className="form-group mb-3">
|
connector={connector}
|
||||||
<label htmlFor={`telegramToken${index}`}>Telegram Bot Token</label>
|
index={index}
|
||||||
<input
|
onConnectorConfigChange={onConnectorConfigChange}
|
||||||
type="text"
|
getConfigValue={getConfigValue}
|
||||||
id={`telegramToken${index}`}
|
fields={fields}
|
||||||
value={getConfigValue(connector, 'token', '')}
|
/>
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'token', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
|
|
||||||
/>
|
|
||||||
<small className="form-text text-muted">Get this from @BotFather on Telegram</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,71 +1,67 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import BaseConnector from './BaseConnector';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Twitter connector template
|
* Twitter connector template
|
||||||
*/
|
*/
|
||||||
const TwitterConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
const TwitterConnector = ({ connector, index, onConnectorConfigChange, getConfigValue }) => {
|
||||||
|
// Field definitions for Twitter connector
|
||||||
|
const fields = [
|
||||||
|
{
|
||||||
|
name: 'apiKey',
|
||||||
|
label: 'API Key',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'Twitter API Key',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'apiSecret',
|
||||||
|
label: 'API Secret',
|
||||||
|
type: 'password',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'Twitter API Secret',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accessToken',
|
||||||
|
label: 'Access Token',
|
||||||
|
type: 'text',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'Twitter Access Token',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accessSecret',
|
||||||
|
label: 'Access Token Secret',
|
||||||
|
type: 'password',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'Twitter Access Token Secret',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'bearerToken',
|
||||||
|
label: 'Bearer Token',
|
||||||
|
type: 'password',
|
||||||
|
defaultValue: '',
|
||||||
|
placeholder: 'Twitter Bearer Token',
|
||||||
|
helpText: '',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="connector-template">
|
<BaseConnector
|
||||||
<div className="form-group mb-3">
|
connector={connector}
|
||||||
<label htmlFor={`twitterApiKey${index}`}>API Key</label>
|
index={index}
|
||||||
<input
|
onConnectorConfigChange={onConnectorConfigChange}
|
||||||
type="text"
|
getConfigValue={getConfigValue}
|
||||||
id={`twitterApiKey${index}`}
|
fields={fields}
|
||||||
value={getConfigValue(connector, 'apiKey', '')}
|
/>
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'apiKey', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="Twitter API Key"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`twitterApiSecret${index}`}>API Secret</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id={`twitterApiSecret${index}`}
|
|
||||||
value={getConfigValue(connector, 'apiSecret', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'apiSecret', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="Twitter API Secret"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`twitterAccessToken${index}`}>Access Token</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id={`twitterAccessToken${index}`}
|
|
||||||
value={getConfigValue(connector, 'accessToken', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'accessToken', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="Twitter Access Token"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`twitterAccessSecret${index}`}>Access Token Secret</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id={`twitterAccessSecret${index}`}
|
|
||||||
value={getConfigValue(connector, 'accessSecret', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'accessSecret', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="Twitter Access Token Secret"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-group mb-3">
|
|
||||||
<label htmlFor={`twitterBearerToken${index}`}>Bearer Token</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id={`twitterBearerToken${index}`}
|
|
||||||
value={getConfigValue(connector, 'bearerToken', '')}
|
|
||||||
onChange={(e) => onConnectorConfigChange(index, 'bearerToken', e.target.value)}
|
|
||||||
className="form-control"
|
|
||||||
placeholder="Twitter Bearer Token"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user