feat(ui): Add React based UI for the vibes at /app

This adds a completely separate frontend based on React because I
found that code gen works better with React once the application gets
bigger. In particular it was getting very hard to move past add
connectors and actions.

The idea is to replace the standard UI with this once it has been
tested. But for now it is available at /app in addition to the
original at /

Signed-off-by: Richard Palethorpe <io@richiejp.com>
This commit is contained in:
Richard Palethorpe
2025-03-24 14:36:18 +00:00
parent 438a65caf6
commit 71e66c651c
61 changed files with 6452 additions and 2 deletions

View File

@@ -0,0 +1,138 @@
import { useState } from 'react';
// Import connector components
import TelegramConnector from './connectors/TelegramConnector';
import SlackConnector from './connectors/SlackConnector';
import DiscordConnector from './connectors/DiscordConnector';
import GithubIssuesConnector from './connectors/GithubIssuesConnector';
import GithubPRsConnector from './connectors/GithubPRsConnector';
import IRCConnector from './connectors/IRCConnector';
import TwitterConnector from './connectors/TwitterConnector';
import FallbackConnector from './connectors/FallbackConnector';
/**
* ConnectorForm component
* Provides specific form templates for different connector types
*/
function ConnectorForm({
connectors = [],
onAddConnector,
onRemoveConnector,
onConnectorNameChange,
onConnectorConfigChange
}) {
const [newConfigKey, setNewConfigKey] = useState('');
// Render a specific connector form based on its type
const renderConnectorForm = (connector, index) => {
// Ensure connector is an object with expected properties
const safeConnector = connector || {};
return (
<div key={index} className="connector-item mb-4">
<div className="connector-header">
<h4>Connector #{index + 1}</h4>
<button
type="button"
className="remove-btn"
onClick={() => onRemoveConnector(index)}
>
<i className="fas fa-times"></i>
</button>
</div>
<div className="connector-type mb-3">
<label htmlFor={`connectorName${index}`}>Connector Type</label>
<select
id={`connectorName${index}`}
value={safeConnector.type || ''}
onChange={(e) => onConnectorNameChange(index, e.target.value)}
className="form-control"
>
<option value="">Select a connector type</option>
<option value="telegram">Telegram</option>
<option value="slack">Slack</option>
<option value="discord">Discord</option>
<option value="github-issues">GitHub Issues</option>
<option value="github-prs">GitHub PRs</option>
<option value="irc">IRC</option>
<option value="twitter">Twitter</option>
<option value="custom">Custom</option>
</select>
</div>
{/* Render specific connector template based on type */}
{renderConnectorTemplate(safeConnector, index)}
</div>
);
};
// Get the appropriate form template based on connector type
const renderConnectorTemplate = (connector, index) => {
// Check if connector.type exists, if not use empty string to avoid errors
const connectorType = (connector.type || '').toLowerCase();
// Common props for all connector components
const connectorProps = {
connector,
index,
onConnectorConfigChange,
getConfigValue
};
switch (connectorType) {
case 'telegram':
return <TelegramConnector {...connectorProps} />;
case 'slack':
return <SlackConnector {...connectorProps} />;
case 'discord':
return <DiscordConnector {...connectorProps} />;
case 'github-issues':
return <GithubIssuesConnector {...connectorProps} />;
case 'github-prs':
return <GithubPRsConnector {...connectorProps} />;
case 'irc':
return <IRCConnector {...connectorProps} />;
case 'twitter':
return <TwitterConnector {...connectorProps} />;
default:
return <FallbackConnector {...connectorProps} />;
}
};
// Helper function to safely get config values
const getConfigValue = (connector, key, defaultValue = '') => {
if (!connector || !connector.config) return defaultValue;
// If config is a string (JSON), try to parse it
let config = connector.config;
if (typeof config === 'string') {
try {
config = JSON.parse(config);
} catch (err) {
console.error('Error parsing config:', err);
return defaultValue;
}
}
return config[key] !== undefined ? config[key] : defaultValue;
};
return (
<div className="connectors-container">
{connectors && connectors.map((connector, index) => (
renderConnectorForm(connector, index)
))}
<button
type="button"
className="add-btn"
onClick={onAddConnector}
>
<i className="fas fa-plus"></i> Add Connector
</button>
</div>
);
}
export default ConnectorForm;