import { useState, useEffect } from 'react'
import './App.css'
const API_BASE = (import.meta.env.VITE_API_URL || 'https://vycka12-binance-data-backend.hf.space') + '/api';
function App() {
const [symbols, setSymbols] = useState(['BTC/USDT']);
const [selectedSymbol, setSelectedSymbol] = useState('BTC/USDT');
const [interval, setInterval] = useState('1m');
const [dataType, setDataType] = useState('Klines (OHLCV)');
// Date setup (last 7 days default)
const defaultEnd = new Date().toISOString().split('T')[0];
const defaultStart = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
const [startDate, setStartDate] = useState(defaultStart);
const [endDate, setEndDate] = useState(defaultEnd);
// Status and data
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [successMsg, setSuccessMsg] = useState(null);
const [previewData, setPreviewData] = useState([]);
const [csvData, setCsvData] = useState(null);
const [modalCmd, setModalCmd] = useState(null);
const [dollarThreshold, setDollarThreshold] = useState(1000000);
const [aggMode, setAggMode] = useState('Standard (Klines + Liq)');
// Progress simulation (since real SSE would require more backend work)
const [progress, setProgress] = useState(0);
const intervals = ['1s', '1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w', '1mo'];
const dataTypes = ['Klines (OHLCV)', 'Liquidations', 'AggTrades', 'Dollar Bars (ML Ready)', 'VPIN (Flow Toxicity)', 'Time-Series Aggregator (Cloud)'];
useEffect(() => {
fetch(`${API_BASE}/symbols`)
.then(res => res.json())
.then(data => {
if (data.symbols && data.symbols.length > 0) {
setSymbols(data.symbols);
if (data.symbols.includes('BTC/USDT')) {
setSelectedSymbol('BTC/USDT');
} else {
setSelectedSymbol(data.symbols[0]);
}
}
})
.catch(err => console.error("Error fetching symbols:", err));
}, []);
// Simulate progress bar — slower for bigger date ranges
useEffect(() => {
let intervalId;
if (loading && progress < 90) {
const start = new Date(startDate);
const end = new Date(endDate);
const daysDiff = Math.max(1, (end - start) / (1000 * 60 * 60 * 24));
// Slower increments for bigger date ranges
const increment = daysDiff > 365 ? 0.5 : daysDiff > 30 ? 2 : 10;
intervalId = window.setInterval(() => {
setProgress(p => Math.min(p + Math.random() * increment, 90));
}, 1000);
} else if (!loading && progress > 0 && progress < 100) {
setProgress(100);
setTimeout(() => setProgress(0), 1000);
}
return () => clearInterval(intervalId);
}, [loading, progress, startDate, endDate]);
const handleDownload = async () => {
setLoading(true);
setError(null);
setSuccessMsg(null);
setPreviewData([]);
setCsvData(null);
setModalCmd(null);
setProgress(2);
try {
const response = await fetch(`${API_BASE}/download`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
symbol: selectedSymbol,
interval: interval,
data_type: dataType,
start_date: startDate,
end_date: endDate,
threshold: dollarThreshold,
agg_mode: aggMode
})
});
const result = await response.json();
if (!response.ok) {
throw new Error(result.detail || "Įvyko nežinoma klaida");
}
if (result.success) {
let msg = `✅ Apdorojimas baigtas! Iš viso: ${result.row_count} eilučių.`;
if (result.hf_url) {
msg += ` Failas automatiškai įkeltas į Hugging Face!`;
}
setSuccessMsg(msg);
setPreviewData(result.preview);
setCsvData(result.csv_data);
if (result.hf_url) {
// You could also store this URL in state if you want to show a dedicated button
console.log("HF URL:", result.hf_url);
}
} else {
setError(result.message || "Duomenų nerasta.");
}
} catch (err) {
setError(`❌ Klaida: ${err.message}`);
} finally {
setLoading(false);
}
};
const downloadCsvFile = () => {
if (!csvData) return;
const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${selectedSymbol.replace('/', '_')}_${dataType.split(' ')[0]}_${startDate}_${endDate}.csv`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
};
return (
{/* Sidebar */}
{/* Main Content */}
{loading ? (
Kraunami duomenys...
Apdorojama: {dataType}
{(() => {
const days = Math.max(1, (new Date(endDate) - new Date(startDate)) / (1000 * 60 * 60 * 24));
if (days > 365) return
⏱️ Didelis laikotarpis ({Math.round(days / 365)} m.) — gali užtrukti 2-5 min. ;
if (days > 30) return
⏱️ ~{Math.round(days / 30)} mėn. duomenų — gali užtrukti ~1-2 min. ;
return null;
})()}
) : (
<>
{error && {error}
}
{successMsg && {successMsg}
}
{modalCmd && (
🌩️ Modal Cloud Data Processing
Ši funkcija skirta didelių duomenų kiekių apjungimui (2+ metai). Vykdykite terminale:
{modalCmd}
)}
{!modalCmd && previewData.length > 0 ? (
<>
Duomenų peržiūra (Paskutinės 100 eilučių)
💾 Atsisiųsti CSV failą
{Object.keys(previewData[0]).map(key => (
{key}
))}
{previewData.map((row, i) => (
{Object.values(row).map((val, j) => (
{val}
))}
))}
>
) : (
!error && !loading && !modalCmd && (
Pasirinkite nustatymus ir spauskite "Vykdyti", kad atsisiųstumėte duomenis.
)
)}
>
)}
)
}
export default App