mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-07 16:41:06 +08:00
Refactor code structure for improved readability and maintainability
This commit is contained in:
266
ccw/frontend/src/components/codexlens/SchemaFormRenderer.tsx
Normal file
266
ccw/frontend/src/components/codexlens/SchemaFormRenderer.tsx
Normal file
@@ -0,0 +1,266 @@
|
||||
// ========================================
|
||||
// SchemaFormRenderer Component
|
||||
// ========================================
|
||||
// Renders structured form groups from EnvVarGroupsSchema definition
|
||||
// Supports select, number, checkbox, text, and model-select field types
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import {
|
||||
Box,
|
||||
ArrowUpDown,
|
||||
Cpu,
|
||||
GitBranch,
|
||||
Scissors,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react';
|
||||
import { Label } from '@/components/ui/Label';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
import { Checkbox } from '@/components/ui/Checkbox';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/Select';
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
CollapsibleTrigger,
|
||||
} from '@/components/ui/Collapsible';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { evaluateShowWhen } from './envVarSchema';
|
||||
import { ModelSelectField } from './ModelSelectField';
|
||||
import type { EnvVarGroupsSchema, EnvVarFieldSchema } from '@/types/codexlens';
|
||||
import type { CodexLensModel } from '@/lib/api';
|
||||
|
||||
// Icon mapping for group icons
|
||||
const iconMap: Record<string, LucideIcon> = {
|
||||
box: Box,
|
||||
'arrow-up-down': ArrowUpDown,
|
||||
cpu: Cpu,
|
||||
'git-branch': GitBranch,
|
||||
scissors: Scissors,
|
||||
};
|
||||
|
||||
interface SchemaFormRendererProps {
|
||||
/** The schema defining all groups and fields */
|
||||
groups: EnvVarGroupsSchema;
|
||||
/** Current form values keyed by env var name */
|
||||
values: Record<string, string>;
|
||||
/** Called when a field value changes */
|
||||
onChange: (key: string, value: string) => void;
|
||||
/** Whether the form is disabled (loading state) */
|
||||
disabled?: boolean;
|
||||
/** Local embedding models (installed) for model-select */
|
||||
localEmbeddingModels?: CodexLensModel[];
|
||||
/** Local reranker models (installed) for model-select */
|
||||
localRerankerModels?: CodexLensModel[];
|
||||
}
|
||||
|
||||
export function SchemaFormRenderer({
|
||||
groups,
|
||||
values,
|
||||
onChange,
|
||||
disabled = false,
|
||||
localEmbeddingModels = [],
|
||||
localRerankerModels = [],
|
||||
}: SchemaFormRendererProps) {
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const groupEntries = useMemo(() => Object.entries(groups), [groups]);
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{groupEntries.map(([groupKey, group]) => {
|
||||
const IconComponent = iconMap[group.icon] || Box;
|
||||
|
||||
return (
|
||||
<Collapsible key={groupKey} defaultOpen>
|
||||
<div className="border border-border rounded-lg">
|
||||
<CollapsibleTrigger className="flex w-full items-center gap-2 p-3 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors">
|
||||
<IconComponent className="w-3.5 h-3.5" />
|
||||
{formatMessage({ id: group.labelKey, defaultMessage: groupKey })}
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<div className="px-3 pb-3 space-y-2">
|
||||
{Object.entries(group.vars).map(([varKey, field]) => {
|
||||
const visible = evaluateShowWhen(field, values);
|
||||
if (!visible) return null;
|
||||
|
||||
return (
|
||||
<FieldRenderer
|
||||
key={varKey}
|
||||
field={field}
|
||||
value={values[varKey] ?? field.default ?? ''}
|
||||
onChange={(val) => onChange(varKey, val)}
|
||||
allValues={values}
|
||||
disabled={disabled}
|
||||
localModels={
|
||||
varKey.includes('EMBEDDING')
|
||||
? localEmbeddingModels
|
||||
: localRerankerModels
|
||||
}
|
||||
formatMessage={formatMessage}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</CollapsibleContent>
|
||||
</div>
|
||||
</Collapsible>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// Individual Field Renderer
|
||||
// ========================================
|
||||
|
||||
interface FieldRendererProps {
|
||||
field: EnvVarFieldSchema;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
allValues: Record<string, string>;
|
||||
disabled: boolean;
|
||||
localModels: CodexLensModel[];
|
||||
formatMessage: (descriptor: { id: string; defaultMessage?: string }) => string;
|
||||
}
|
||||
|
||||
function FieldRenderer({
|
||||
field,
|
||||
value,
|
||||
onChange,
|
||||
allValues,
|
||||
disabled,
|
||||
localModels,
|
||||
formatMessage,
|
||||
}: FieldRendererProps) {
|
||||
const label = formatMessage({ id: field.labelKey, defaultMessage: field.key });
|
||||
|
||||
switch (field.type) {
|
||||
case 'select':
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Label
|
||||
className="text-xs text-muted-foreground w-28 flex-shrink-0"
|
||||
title={field.key}
|
||||
>
|
||||
{label}
|
||||
</Label>
|
||||
<Select
|
||||
value={value}
|
||||
onValueChange={onChange}
|
||||
disabled={disabled}
|
||||
>
|
||||
<SelectTrigger className={cn('flex-1 h-8 text-xs')}>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{(field.options || []).map((opt) => (
|
||||
<SelectItem key={opt} value={opt} className="text-xs">
|
||||
{opt}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'number':
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Label
|
||||
className="text-xs text-muted-foreground w-28 flex-shrink-0"
|
||||
title={field.key}
|
||||
>
|
||||
{label}
|
||||
</Label>
|
||||
<Input
|
||||
type="number"
|
||||
className="flex-1 h-8 text-xs"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={field.placeholder}
|
||||
min={field.min}
|
||||
max={field.max}
|
||||
step={field.step ?? 1}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'checkbox':
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Label
|
||||
className="text-xs text-muted-foreground w-28 flex-shrink-0"
|
||||
title={field.key}
|
||||
>
|
||||
{label}
|
||||
</Label>
|
||||
<div className="flex-1 flex items-center h-8">
|
||||
<Checkbox
|
||||
checked={value === 'true'}
|
||||
onCheckedChange={(checked) => onChange(checked ? 'true' : 'false')}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'model-select': {
|
||||
// Determine backend type from related backend env var
|
||||
const isEmbedding = field.key.includes('EMBEDDING');
|
||||
const backendKey = isEmbedding
|
||||
? 'CODEXLENS_EMBEDDING_BACKEND'
|
||||
: 'CODEXLENS_RERANKER_BACKEND';
|
||||
const backendType = allValues[backendKey] === 'api' ? 'api' : 'local';
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Label
|
||||
className="text-xs text-muted-foreground w-28 flex-shrink-0"
|
||||
title={field.key}
|
||||
>
|
||||
{label}
|
||||
</Label>
|
||||
<ModelSelectField
|
||||
field={field}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
localModels={localModels}
|
||||
backendType={backendType}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
case 'text':
|
||||
default:
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<Label
|
||||
className="text-xs text-muted-foreground w-28 flex-shrink-0"
|
||||
title={field.key}
|
||||
>
|
||||
{label}
|
||||
</Label>
|
||||
<Input
|
||||
type="text"
|
||||
className="flex-1 h-8 text-xs"
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={field.placeholder}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SchemaFormRenderer;
|
||||
Reference in New Issue
Block a user