import { useCallback, useEffect, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Play, Plus, Trash2, Download, ClipboardList } from 'lucide-react'; import { parse as yamlParse } from 'yaml'; import { environments, scenarios } from '../../api'; import type { Environment, Scenario } from '../../api'; import { Breadcrumbs, Button, Modal, Select, Table, type TableColumn, Timestamp, UuidBadge, useToast, } from '../../ui'; import styles from '../Page.module.css'; export function ScenariosPage() { const { t } = useTranslation(); const navigate = useNavigate(); const toast = useToast(); const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [deleteId, setDeleteId] = useState(null); const [deleting, setDeleting] = useState(false); const [runScenarioId, setRunScenarioId] = useState(null); const [running, setRunning] = useState(false); const [saveSessionFlag, setSaveSessionFlag] = useState(false); const [envs, setEnvs] = useState([]); const [selectedEnvId, setSelectedEnvId] = useState(''); const fileInputRef = useRef(null); const load = useCallback(() => { Promise.all([scenarios.list(), environments.list(1, 200)]) .then(([scenarioRes, envRes]) => { setItems(scenarioRes.data); setEnvs(envRes.data); }) .catch((err: Error) => setError(err.message)) .finally(() => setLoading(false)); }, []); useEffect(() => { load(); }, [load]); const openRunModal = (id: string) => { setRunScenarioId(id); if (!selectedEnvId && envs.length > 0) { setSelectedEnvId(envs[0].id); } }; const handleRun = async () => { if (!runScenarioId || !selectedEnvId) return; setRunning(true); try { const run = await scenarios.run(runScenarioId, selectedEnvId, saveSessionFlag); toast.success('Scenario run started'); navigate(`/scenarios/${runScenarioId}/runs/${run.id}`); setRunScenarioId(null); setSaveSessionFlag(false); } catch (err) { toast.error((err as Error).message); } finally { setRunning(false); } }; const handleDelete = async () => { if (!deleteId) return; setDeleting(true); try { await scenarios.remove(deleteId); setLoading(true); load(); setDeleteId(null); } finally { setDeleting(false); } }; const handleImport = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; e.target.value = ''; try { const text = await file.text(); const payload = yamlParse(text) as unknown; const imported = await scenarios.importScenario(payload); navigate(`/scenarios/${imported.id}`); } catch (err) { setError((err as Error).message); } }; const columns: TableColumn[] = [ { key: 'id', header: t('scenarios.col_id'), render: (s) => , width: 60 }, { key: 'name', header: t('scenarios.col_name'), render: (s) => s.name }, { key: 'updated', header: t('scenarios.col_updated'), width: 140, render: (s) => , }, { key: 'actions', header: '', width: 100, align: 'right', render: (s) => ( ), }, ]; return (
}]} />
s.id} loading={loading} emptyMessage={t('scenarios.empty')} pageSize={10} pageSizeOptions={[10, 25, 50]} onRowClick={(s) => navigate(`/scenarios/${s.id}`)} /> !deleting && setDeleteId(null)} footer={( <> )} > Are you sure you want to delete this scenario? !running && setRunScenarioId(null)} footer={( <> )} > {envs.length === 0 ? (

{t('scenarios.run_modal_no_env')}

) : ( <> setSaveSessionFlag(e.target.checked)} /> Save session )}
); }