Big refactoring
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { RecommendationConfig } from '../../lib/types';
|
||||
import { useRecommendationSettings, useUpdateRecommendationSettings } from '../../hooks/useAdmin';
|
||||
import { SkeletonTable } from '../ui/Skeleton';
|
||||
|
||||
const DEFAULTS: RecommendationConfig = {
|
||||
high_confidence_threshold: 70,
|
||||
moderate_confidence_threshold: 50,
|
||||
confidence_diff_threshold: 20,
|
||||
signal_alignment_weight: 0.15,
|
||||
sr_strength_weight: 0.2,
|
||||
distance_penalty_factor: 0.1,
|
||||
momentum_technical_divergence_threshold: 30,
|
||||
fundamental_technical_divergence_threshold: 40,
|
||||
};
|
||||
|
||||
function NumberInput({
|
||||
label,
|
||||
value,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
onChange,
|
||||
}: {
|
||||
label: string;
|
||||
value: number;
|
||||
min: number;
|
||||
max: number;
|
||||
step?: number;
|
||||
onChange: (v: number) => void;
|
||||
}) {
|
||||
return (
|
||||
<label className="block space-y-1">
|
||||
<span className="text-xs text-gray-400">{label}</span>
|
||||
<input
|
||||
type="number"
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
value={value}
|
||||
onChange={(e) => onChange(Number(e.target.value))}
|
||||
className="w-full input-glass px-3 py-2 text-sm"
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
export function RecommendationSettings() {
|
||||
const { data, isLoading, isError, error } = useRecommendationSettings();
|
||||
const update = useUpdateRecommendationSettings();
|
||||
|
||||
const [form, setForm] = useState<RecommendationConfig>(DEFAULTS);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) setForm(data);
|
||||
}, [data]);
|
||||
|
||||
const setField = (field: keyof RecommendationConfig, value: number) => {
|
||||
setForm((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
const onSave = () => {
|
||||
update.mutate(form as unknown as Record<string, number>);
|
||||
};
|
||||
|
||||
const onReset = () => {
|
||||
setForm(DEFAULTS);
|
||||
update.mutate(DEFAULTS as unknown as Record<string, number>);
|
||||
};
|
||||
|
||||
if (isLoading) return <SkeletonTable rows={6} cols={2} />;
|
||||
if (isError) return <p className="text-sm text-red-400">{(error as Error)?.message || 'Failed to load recommendation settings'}</p>;
|
||||
|
||||
return (
|
||||
<div className="glass p-5 space-y-4">
|
||||
<h3 className="text-sm font-semibold text-gray-200">Recommendation Configuration</h3>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-3">
|
||||
<NumberInput label="High Confidence Threshold (%)" value={form.high_confidence_threshold} min={0} max={100} onChange={(v) => setField('high_confidence_threshold', v)} />
|
||||
<NumberInput label="Moderate Confidence Threshold (%)" value={form.moderate_confidence_threshold} min={0} max={100} onChange={(v) => setField('moderate_confidence_threshold', v)} />
|
||||
<NumberInput label="Confidence Difference Threshold (%)" value={form.confidence_diff_threshold} min={0} max={100} onChange={(v) => setField('confidence_diff_threshold', v)} />
|
||||
|
||||
<NumberInput label="Signal Alignment Weight" value={form.signal_alignment_weight} min={0} max={1} step={0.01} onChange={(v) => setField('signal_alignment_weight', v)} />
|
||||
<NumberInput label="S/R Strength Weight" value={form.sr_strength_weight} min={0} max={1} step={0.01} onChange={(v) => setField('sr_strength_weight', v)} />
|
||||
<NumberInput label="Distance Penalty Factor" value={form.distance_penalty_factor} min={0} max={1} step={0.01} onChange={(v) => setField('distance_penalty_factor', v)} />
|
||||
|
||||
<NumberInput label="Momentum-Technical Divergence Threshold" value={form.momentum_technical_divergence_threshold} min={0} max={100} onChange={(v) => setField('momentum_technical_divergence_threshold', v)} />
|
||||
<NumberInput label="Fundamental-Technical Divergence Threshold" value={form.fundamental_technical_divergence_threshold} min={0} max={100} onChange={(v) => setField('fundamental_technical_divergence_threshold', v)} />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<button className="btn-gradient px-4 py-2 text-sm" onClick={onSave} disabled={update.isPending}>
|
||||
{update.isPending ? 'Saving…' : 'Save Configuration'}
|
||||
</button>
|
||||
<button className="px-4 py-2 text-sm rounded border border-white/[0.1] text-gray-300 hover:text-white" onClick={onReset} disabled={update.isPending}>
|
||||
Reset to Defaults
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user