Add master dashboard reporter and settings UI
- New reporter module that pushes firm stats (total accounts, accounts traded, in trade) to a configurable master dashboard every 30 seconds - Add Instance Name and Dashboard URL fields to the settings page - Register master_dashboard_url and instance_name in settings API - Seed default (empty) values for new settings in db Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
15ef2d7adc
commit
f112118090
+48
-1
@@ -17,12 +17,16 @@ interface ResolvedContract {
|
||||
|
||||
interface AppSettings {
|
||||
max_concurrent_accounts: string | null;
|
||||
master_dashboard_url: string | null;
|
||||
instance_name: string | null;
|
||||
}
|
||||
|
||||
export default function SettingsPage() {
|
||||
const [instruments, setInstruments] = useState<Instrument[]>([]);
|
||||
const [contracts, setContracts] = useState<Record<string, ResolvedContract | null>>({});
|
||||
const [maxConcurrent, setMaxConcurrent] = useState<string>('5');
|
||||
const [masterUrl, setMasterUrl] = useState<string>('');
|
||||
const [instanceName, setInstanceName] = useState<string>('');
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [saved, setSaved] = useState(false);
|
||||
|
||||
@@ -37,6 +41,12 @@ export default function SettingsPage() {
|
||||
if (s.max_concurrent_accounts != null) {
|
||||
setMaxConcurrent(s.max_concurrent_accounts);
|
||||
}
|
||||
if (s.master_dashboard_url != null) {
|
||||
setMasterUrl(s.master_dashboard_url);
|
||||
}
|
||||
if (s.instance_name != null) {
|
||||
setInstanceName(s.instance_name);
|
||||
}
|
||||
});
|
||||
|
||||
// Load cached contracts; if cache is empty, auto-resolve
|
||||
@@ -91,7 +101,7 @@ export default function SettingsPage() {
|
||||
await fetch('/api/settings', {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ max_concurrent_accounts: maxConcurrent }),
|
||||
body: JSON.stringify({ max_concurrent_accounts: maxConcurrent, master_dashboard_url: masterUrl, instance_name: instanceName }),
|
||||
});
|
||||
setSaving(false);
|
||||
setSaved(true);
|
||||
@@ -148,6 +158,43 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Master Dashboard ── */}
|
||||
<h2 className="text-sm font-semibold uppercase tracking-wider text-slate-500 mb-3">
|
||||
Master Dashboard
|
||||
</h2>
|
||||
<div className="bg-white border border-slate-200 rounded-xl shadow-sm mb-8">
|
||||
<div className="flex items-center justify-between px-4 py-3 border-b border-slate-100">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-800">Instance Name</p>
|
||||
<p className="text-xs text-slate-400 mt-0.5">
|
||||
Identifier for this autotrader on the master dashboard
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="e.g. VPS-1"
|
||||
value={instanceName}
|
||||
onChange={(e) => setInstanceName(e.target.value)}
|
||||
className="w-48 rounded-lg border border-slate-200 bg-slate-50 px-3 py-1.5 text-sm font-mono text-slate-800 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-between px-4 py-3">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-slate-800">Dashboard URL</p>
|
||||
<p className="text-xs text-slate-400 mt-0.5">
|
||||
Reports state to this URL every 30 seconds
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="e.g. http://master-ip:4000"
|
||||
value={masterUrl}
|
||||
onChange={(e) => setMasterUrl(e.target.value)}
|
||||
className="w-72 rounded-lg border border-slate-200 bg-slate-50 px-3 py-1.5 text-sm font-mono text-slate-800 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Instruments ── */}
|
||||
<h2 className="text-sm font-semibold uppercase tracking-wider text-slate-500 mb-3">
|
||||
Instruments
|
||||
|
||||
Reference in New Issue
Block a user