diff --git a/elastic-recorder-dashboard.jsx b/elastic-recorder-dashboard.jsx new file mode 100644 index 0000000..8b74db3 --- /dev/null +++ b/elastic-recorder-dashboard.jsx @@ -0,0 +1,327 @@ +import React, { useState, useCallback } from 'react'; +import { RefreshCw, Settings, Save, AlertCircle, CheckCircle } from 'lucide-react'; + +export default function ElasticRecorderDashboard() { + const [apiKey, setApiKey] = useState(''); + const [baseUrl, setBaseUrl] = useState('https://us-east-1.gvampp.com'); + const [channels, setChannels] = useState([]); + const [loading, setLoading] = useState(false); + const [authenticated, setAuthenticated] = useState(false); + const [error, setError] = useState(''); + const [success, setSuccess] = useState(''); + const [editingId, setEditingId] = useState(null); + const [editForm, setEditForm] = useState({}); + + // Get auth token + const getToken = useCallback(async () => { + try { + const [username, password] = atob(apiKey).split(':'); + + const response = await fetch(`${baseUrl}/identity/connect/token`, { + method: 'POST', + headers: { + 'Authorization': `Basic ${apiKey}`, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + grant_type: 'client_credentials', + scope: 'platform' + }) + }); + + if (!response.ok) { + throw new Error(`Authentication failed: ${response.statusText}`); + } + + const data = await response.json(); + return data.access_token; + } catch (err) { + throw new Error(`Token error: ${err.message}`); + } + }, [apiKey, baseUrl]); + + // Fetch channels + const fetchChannels = useCallback(async () => { + setLoading(true); + setError(''); + try { + const token = await getToken(); + + const response = await fetch(`${baseUrl}/api/store/channel/v1/channels`, { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + + if (!response.ok) { + throw new Error(`Failed to fetch channels: ${response.statusText}`); + } + + const data = await response.json(); + setChannels(data); + setAuthenticated(true); + setSuccess(`Loaded ${data.length} channels`); + setTimeout(() => setSuccess(''), 3000); + } catch (err) { + setError(err.message); + setAuthenticated(false); + } finally { + setLoading(false); + } + }, [getToken, baseUrl]); + + // Handle login + const handleLogin = (e) => { + e.preventDefault(); + if (apiKey && baseUrl) { + fetchChannels(); + } + }; + + // Start editing a channel + const startEdit = (channel) => { + setEditingId(channel['channel:id']); + setEditForm({ + 'source:text': channel['source:text'] || '', + 'destinationId:int': channel['destinationId:int'] || '', + 'name:text': channel['name:text'] || '' + }); + }; + + // Cancel edit + const cancelEdit = () => { + setEditingId(null); + setEditForm({}); + }; + + // Save changes + const saveChanges = async () => { + setLoading(true); + setError(''); + try { + const token = await getToken(); + + const updateData = { + 'source:text': editForm['source:text'], + 'destinationId:int': editForm['destinationId:int'] ? parseInt(editForm['destinationId:int']) : null + }; + + const response = await fetch(`${baseUrl}/api/store/channel/v1/channels/${editingId}`, { + method: 'PATCH', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(updateData) + }); + + if (!response.ok) { + throw new Error(`Failed to update channel: ${response.statusText}`); + } + + setSuccess('Channel updated successfully'); + setEditingId(null); + setTimeout(() => fetchChannels(), 500); + } catch (err) { + setError(err.message); + } finally { + setLoading(false); + } + }; + + // Login form + if (!authenticated) { + return ( +
+
+
+

Elastic Recorder Control

+

Manage recording settings and folder assignments

+
+ +
+
+ +