Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | 83x 83x 83x 83x 83x 83x 27x 9x 9x 9x 9x 9x 9x 83x 83x 3x 3x 3x 3x 3x 3x 2x 1x 3x 83x 2x | /**
* Macro test dialog component
*/
import { useState, useEffect } from 'react';
import { AppDialog } from '@/components/common/AppDialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Play, CheckCircle2, AlertCircle, Clock, Hash } from 'lucide-react';
import type { Macro, MacroTestResponse } from '@/types/macro';
import { testMacro } from '@/services/macros.service';
import { handleApiError } from '@/lib/api';
interface MacroTestDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
macro: Macro | null;
}
export default function MacroTestDialog({
open,
onOpenChange,
macro,
}: MacroTestDialogProps) {
const [paramValues, setParamValues] = useState<unknown[]>([]);
const [paramNames, setParamNames] = useState<string[]>([]);
const [testResult, setTestResult] = useState<MacroTestResponse | null>(null);
const [isTesting, setIsTesting] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (open && macro) {
try {
const names = JSON.parse(macro.parameters);
setParamNames(names);
setParamValues(new Array(names.length).fill(''));
} catch {
setParamNames([]);
setParamValues([]);
}
setTestResult(null);
setError(null);
}
}, [open, macro]);
const handleParamChange = (index: number, value: string) => {
const newValues = [...paramValues];
newValues[index] = value;
setParamValues(newValues);
};
const handleTest = async () => {
Iif (!macro) return;
setIsTesting(true);
setError(null);
setTestResult(null);
try {
const result = await testMacro(macro.id, {
parameters: paramValues,
});
setTestResult(result);
} catch (err) {
setError(handleApiError(err));
} finally {
setIsTesting(false);
}
};
return (
<AppDialog
open={open}
onOpenChange={onOpenChange}
title={`Test Macro: @${macro?.name}`}
description="Execute the macro in a transactional test mode. Changes will be rolled back."
className="max-w-2xl"
footer={
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={isTesting}>
Close
</Button>
}
>
<div className="space-y-6">
{/* SQL Preview */}
<div className="space-y-2">
<Label className="text-xs uppercase text-muted-foreground font-semibold">SQL Query</Label>
<div className="bg-muted p-3 rounded-md font-mono text-sm overflow-x-auto whitespace-pre">
{macro?.sql_query}
</div>
</div>
{/* Parameters Input */}
<div className="space-y-4">
<Label className="text-xs uppercase text-muted-foreground font-semibold">Test Parameters</Label>
{paramNames.length === 0 ? (
<p className="text-sm text-muted-foreground italic">No parameters required for this macro.</p>
) : (
<div className="grid gap-4">
{paramNames.map((name, index) => (
<div key={index} className="space-y-2">
<Label htmlFor={`param-${index}`} className="text-sm font-medium">
{name}
</Label>
<Input
id={`param-${index}`}
value={paramValues[index] as string}
onChange={(e) => handleParamChange(index, e.target.value)}
placeholder={`Value for ${name}`}
disabled={isTesting}
/>
</div>
))}
</div>
)}
</div>
{/* Actions */}
<Button
onClick={handleTest}
className="w-full gap-2"
disabled={isTesting || !macro}
>
{isTesting ? (
<Play className="h-4 w-4 animate-pulse" />
) : (
<Play className="h-4 w-4" />
)}
{isTesting ? 'Executing...' : 'Run Test'}
</Button>
{/* Results */}
{testResult && (
<div className="space-y-4 p-4 border rounded-lg bg-green-50/30 border-green-100">
<div className="flex items-center gap-2 text-green-700 font-semibold">
<CheckCircle2 className="h-5 w-5" />
<span>Execution Successful</span>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-1">
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
<Hash className="h-3 w-3" />
Result
</div>
<div className="font-mono text-sm break-all font-semibold">
{testResult.result ?? 'null'}
</div>
</div>
<div className="space-y-1">
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
<Clock className="h-3 w-3" />
Execution Time
</div>
<div className="text-sm font-semibold">
{testResult.execution_time.toFixed(2)} ms
</div>
</div>
</div>
{testResult.rows_affected !== undefined && (
<div className="text-xs text-muted-foreground mt-2 italic px-1">
Note: This was a read-only test. Any data modifications were rolled back.
</div>
)}
</div>
)}
{error && (
<div className="space-y-3 p-4 border rounded-lg bg-destructive/5 border-destructive/10">
<div className="flex items-center gap-2 text-destructive font-semibold">
<AlertCircle className="h-5 w-5" />
<span>Execution Failed</span>
</div>
<div className="text-sm text-muted-foreground whitespace-pre-wrap font-mono p-2 bg-destructive/5 rounded">
{error}
</div>
</div>
)}
</div>
</AppDialog>
);
}
|