Redesign the frontend with a dark-tech theme: add Tailwind + PostCSS, design tokens, and shadcn-style primitives (Button/Card/Input/Dialog/ DropdownMenu/Tabs/ScrollArea/etc.); restyle the app shell, navigation, sidebar tree, content view, markdown rendering, editors, modals and settings panels. Behavior/props unchanged; Font Awesome replaced with lucide-react. Add the patch cards feature UI: patch-queries hooks and a PatchCards component rendered below the markdown body, with an Add Patch button and create/edit dialog. Fix tree expandability: folders with an index page now expand on name click (and navigate), and the chevron+folder icon is one larger toggle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
122 lines
4.4 KiB
JavaScript
122 lines
4.4 KiB
JavaScript
import React, {useEffect, useState} from "react";
|
|
import { Plus, Trash2, ChevronDown, ChevronRight } from "lucide-react";
|
|
import TypeEditor from "./TypeEditor";
|
|
import { Input, Label } from "../ui/input";
|
|
import { Button } from "../ui/button";
|
|
|
|
const ParametersManager = ({ parameters, onChange }) => {
|
|
const [_parameters, setParameters] = useState(parameters || []);
|
|
const [expandedStates, setExpandedStates] = useState({});
|
|
|
|
const handleAdd = () => {
|
|
const updated = [
|
|
..._parameters,
|
|
{
|
|
name: "",
|
|
type: {
|
|
base_type: "string",
|
|
definition: {}
|
|
}
|
|
}
|
|
];
|
|
setParameters(updated);
|
|
onChange(updated);
|
|
};
|
|
|
|
const handleTypeChange = (index, newType) => {
|
|
const updated = [..._parameters];
|
|
if (newType.base_type === "list" && !newType.extend_type) {
|
|
newType.extend_type = {
|
|
base_type: "string",
|
|
definition: {}
|
|
};
|
|
}
|
|
updated[index].type = newType;
|
|
setParameters(updated);
|
|
onChange(updated);
|
|
};
|
|
|
|
useEffect(() => {
|
|
setParameters(parameters);
|
|
}, [parameters]);
|
|
|
|
const handleNameChange = (index, newName) => {
|
|
const updated = [..._parameters];
|
|
updated[index].name = newName;
|
|
setParameters(updated);
|
|
onChange(updated);
|
|
};
|
|
|
|
const handleDelete = (index) => {
|
|
const updated = [..._parameters];
|
|
updated.splice(index, 1);
|
|
setParameters(updated);
|
|
onChange(updated);
|
|
};
|
|
|
|
const toggleExpand = (index) => {
|
|
setExpandedStates(prev => ({
|
|
...prev,
|
|
[index]: !prev[index]
|
|
}));
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-4 rounded-lg border border-border bg-card p-5">
|
|
<Button type="button" onClick={handleAdd}>
|
|
<Plus className="h-4 w-4" /> Add Parameter
|
|
</Button>
|
|
<div className="max-h-[50vh] space-y-3 overflow-y-auto">
|
|
{_parameters.map((param, index) => (
|
|
<div key={index} className="space-y-3 rounded-md border border-border bg-surface/40 p-4">
|
|
<div className="flex items-end gap-2">
|
|
<div className="flex-1 space-y-1.5">
|
|
<Label>Name</Label>
|
|
<Input
|
|
type="text"
|
|
value={param.name}
|
|
onChange={(e) => handleNameChange(index, e.target.value)}
|
|
placeholder="Parameter name"
|
|
/>
|
|
</div>
|
|
<Button
|
|
type="button"
|
|
variant="destructive"
|
|
size="icon"
|
|
onClick={() => handleDelete(index)}
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<Label>Type</Label>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
onClick={() => toggleExpand(index)}
|
|
>
|
|
{expandedStates[index] ? (
|
|
<ChevronDown className="h-4 w-4" />
|
|
) : (
|
|
<ChevronRight className="h-4 w-4" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
{expandedStates[index] && (
|
|
<TypeEditor
|
|
type={param.type}
|
|
onChange={(newType) => handleTypeChange(index, newType)}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ParametersManager;
|