chore: remove FMP panel — economic calendar not available on free plan

FMP /economic_calendar endpoint returns 403 on free plan (requires Starter
at $14.99/month). The panel adds clutter without value. Removed component,
its import, and the TEKeyStatus type that was only used by it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-26 20:57:10 +02:00
parent 87ded9434f
commit dd2876325a

View File

@@ -1,6 +1,6 @@
import { useState, useEffect, useCallback, useRef, KeyboardEvent, useId } from 'react'
import clsx from 'clsx'
import { RefreshCw, ChevronDown, AlertTriangle, Check, Key, Upload } from 'lucide-react'
import { RefreshCw, ChevronDown, AlertTriangle, Check, Upload } from 'lucide-react'
const API = ''
@@ -38,7 +38,6 @@ interface ImportStatus {
last_result: { inserted?: number; skipped?: number; total?: number; error?: string } | null
}
interface TEKeyStatus { configured: boolean; preview: string }
// ── Constants ─────────────────────────────────────────────────────────────────
@@ -366,123 +365,6 @@ function ImpactFilter({ selected, onChange }: { selected: string[]; onChange: (v
)
}
// ── FMP Panel — upcoming forecasts (free API) ─────────────────────────────────
function FMPPanel({ onSynced }: { onSynced: () => void }) {
const [keyStatus, setKeyStatus] = useState<TEKeyStatus | null>(null)
const [keyInput, setKeyInput] = useState('')
const [saving, setSaving] = useState(false)
const [syncMsg, setSyncMsg] = useState<string | null>(null)
const [syncing, setSyncing] = useState(false)
const pollRef = useRef<ReturnType<typeof setInterval> | null>(null)
useEffect(() => {
fetch(`${API}/api/eco/fmp-key`).then(r => r.json()).then(setKeyStatus).catch(() => {})
return () => { if (pollRef.current) clearInterval(pollRef.current) }
}, [])
const saveKey = async () => {
if (!keyInput.trim()) return
setSaving(true)
try {
const r = await fetch(`${API}/api/eco/fmp-key?key=${encodeURIComponent(keyInput.trim())}`, { method: 'POST' }).then(x => x.json())
setKeyStatus({ configured: true, preview: r.preview })
setKeyInput('')
} finally { setSaving(false) }
}
const startSync = async () => {
setSyncing(true)
setSyncMsg('syncing…')
try {
await fetch(`${API}/api/eco/fmp-sync?weeks=6`, { method: 'POST' })
pollRef.current = setInterval(async () => {
const s = await fetch(`${API}/api/eco/fmp-sync/status`).then(x => x.json())
if (!s.running) {
clearInterval(pollRef.current!)
setSyncing(false)
const r = s.last_result || {}
if (r.error) { setSyncMsg(`Error: ${r.error}`); return }
setSyncMsg(`${r.total_upserted} events (${r.date_from}${r.date_to})`)
onSynced()
}
}, 2000)
} catch { setSyncing(false); setSyncMsg('sync error') }
}
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => { if (e.key === 'Enter') saveKey() }
const configured = keyStatus?.configured
return (
<div className="bg-slate-800 border border-indigo-800/50 rounded-lg p-4 mb-4 text-sm">
<div className="flex flex-wrap items-center gap-4">
<div className="flex items-center gap-2">
<Key size={14} className="text-indigo-400" />
<span className="text-slate-300 font-medium">FMP Calendar</span>
<span className="text-xs text-slate-500">upcoming forecasts free API (250 req/day)</span>
</div>
{configured ? (
<span className="text-emerald-400 text-xs flex items-center gap-1">
<Check size={11} /> key: {keyStatus?.preview}
</span>
) : (
<div className="flex items-center gap-2">
<input
type="password"
value={keyInput}
onChange={e => setKeyInput(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Paste FMP API key…"
className="bg-slate-700 border border-slate-600 rounded px-2 py-1 text-xs text-white w-52 focus:outline-none focus:border-indigo-500"
/>
<button
onClick={saveKey}
disabled={saving || !keyInput.trim()}
className="px-2.5 py-1 rounded bg-indigo-600 hover:bg-indigo-500 disabled:opacity-40 text-xs text-white"
>
{saving ? 'Saving…' : 'Save'}
</button>
<a
href="https://financialmodelingprep.com/developer/docs"
target="_blank" rel="noreferrer"
className="text-xs text-indigo-400 hover:text-indigo-300 underline"
>
Free key (financialmodelingprep.com)
</a>
</div>
)}
{configured && (
<button
onClick={startSync}
disabled={syncing}
className="px-3 py-1.5 rounded bg-indigo-700 hover:bg-indigo-600 disabled:opacity-50 flex items-center gap-1.5 text-white"
>
<RefreshCw size={13} className={clsx(syncing && 'animate-spin')} />
Sync upcoming (6 weeks)
</button>
)}
{configured && (
<button
onClick={() => { setKeyStatus({ configured: false, preview: '' }); setKeyInput('') }}
className="text-xs text-slate-500 hover:text-slate-400 underline"
>
Change key
</button>
)}
{syncMsg && (
<span className={clsx('text-xs', syncMsg.startsWith('✓') ? 'text-emerald-400' : syncMsg.startsWith('Error') ? 'text-red-400' : 'text-yellow-400')}>
{syncMsg}
</span>
)}
</div>
</div>
)
}
// ── Main Page ─────────────────────────────────────────────────────────────────
@@ -581,8 +463,6 @@ export default function CalendarPage() {
{/* Import Panel */}
<ImportPanel stats={stats} onImported={() => { fetchStats(); fetchEvents() }} />
{/* FMP Panel — upcoming forecasts */}
<FMPPanel onSynced={() => { fetchStats(); fetchEvents() }} />
{/* Period Tabs */}
<div className="flex flex-wrap gap-1 mb-3">