feat: confluence autocomplete + explanatory text
- Replace plain text inputs with SymbolAutocomplete (existing component) in both CorridorPanel and ConfluencePanel - Add explanatory text to both panels describing what the visualization shows and how to interpret results - Fix symbol reset bug: panels now sync with global activeSymbol store so symbol persists across tab switches and page reloads - Add 'Run evaluation now' button when no corridor snapshot exists, wired to existing runEvaluationNow mutation
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
'use client';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { api } from '@/lib/trpc';
|
||||
import { useActiveSymbol } from '@/stores/active-symbol-store';
|
||||
import { CollapsibleSection } from '@/components/CollapsibleSection';
|
||||
import { SymbolAutocomplete } from '@/components/SymbolAutocomplete';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types (subset of server types for display)
|
||||
@@ -44,9 +45,15 @@ const BODY_BADGE: Record<string, string> = {
|
||||
|
||||
export function ConfluencePanel() {
|
||||
const activeSymbol = useActiveSymbol((s) => s.activeSymbol);
|
||||
const setActiveSymbol = useActiveSymbol((s) => s.setActive);
|
||||
const [symbol, setSymbol] = useState(activeSymbol);
|
||||
const [selectedRackId, setSelectedRackId] = useState<string | null>(null);
|
||||
|
||||
const pickSymbol = useCallback((sym: string) => {
|
||||
setSymbol(sym);
|
||||
setActiveSymbol(sym);
|
||||
}, [setActiveSymbol]);
|
||||
|
||||
// Racks
|
||||
const [racks, setRacks] = useState<{ system: Array<{ id: string; name: string }>; user: Array<{ id: string; name: string }> }>({ system: [], user: [] });
|
||||
useEffect(() => {
|
||||
@@ -80,6 +87,18 @@ export function ConfluencePanel() {
|
||||
.catch(() => {});
|
||||
}, []);
|
||||
|
||||
const [evaluating, setEvaluating] = useState(false);
|
||||
|
||||
const runEvaluation = async () => {
|
||||
setEvaluating(true);
|
||||
try {
|
||||
await api.confluence.runEvaluationNow({ symbol });
|
||||
window.location.reload();
|
||||
} finally {
|
||||
setEvaluating(false);
|
||||
}
|
||||
};
|
||||
|
||||
const latest = (evaluation?.latest as Record<string, unknown>) ?? null;
|
||||
const quality = (latest?.quality as Quality) ?? null;
|
||||
const assessments = (latest?.assessments as Array<{ id: string; state: string; note?: string }>) ?? [];
|
||||
@@ -89,16 +108,24 @@ export function ConfluencePanel() {
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Explanatory text */}
|
||||
<div className="text-xs text-fg-muted leading-relaxed">
|
||||
Slot Picture combines technical, seasonal, and valuation signals into a single
|
||||
directional read. Each slot fires (green dot) when its condition is met, or stays
|
||||
dormant. The overall quality grade reflects how many slots fired and in which direction.
|
||||
Use the rack selector to switch between different signal subsets.
|
||||
</div>
|
||||
|
||||
{/* Header */}
|
||||
<div className="flex flex-wrap items-end gap-4">
|
||||
<div>
|
||||
<div className="w-48">
|
||||
<label className="block text-xs text-fg-muted mb-1">Symbol</label>
|
||||
<input
|
||||
type="text"
|
||||
<SymbolAutocomplete
|
||||
onPick={pickSymbol}
|
||||
onSubmitRaw={(raw) => pickSymbol(raw.toUpperCase())}
|
||||
value={symbol}
|
||||
onChange={(e) => setSymbol(e.target.value.toUpperCase().trim())}
|
||||
className="w-24 rounded bg-bg-secondary border border-fg-muted/20 px-2 py-1.5 text-sm text-fg"
|
||||
maxLength={12}
|
||||
placeholder="Search symbol…"
|
||||
ariaLabel="Select symbol for slot picture"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -140,7 +167,16 @@ export function ConfluencePanel() {
|
||||
)}
|
||||
</div>
|
||||
) : evaluation !== null ? (
|
||||
<div className="text-sm text-fg-muted">No evaluations yet for {symbol}. Data will appear once the confluence evaluator runs.</div>
|
||||
<div className="flex items-center gap-3 text-sm text-fg-muted">
|
||||
<span>No evaluations yet for {symbol}.</span>
|
||||
<button
|
||||
onClick={runEvaluation}
|
||||
disabled={evaluating}
|
||||
className="rounded bg-bg-secondary border border-fg-muted/30 px-3 py-1.5 text-xs text-fg hover:border-fg-muted/60 disabled:opacity-50"
|
||||
>
|
||||
{evaluating ? 'Evaluating…' : 'Run evaluation now'}
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Slot evidence grid */}
|
||||
|
||||
Reference in New Issue
Block a user