From ef4aabafb55a2159c321d88c737560d9d9d885dd Mon Sep 17 00:00:00 2001 From: Mukan Erkin Date: Sun, 19 Apr 2026 06:50:00 +0300 Subject: [PATCH] feat: modal ile bot ekleme, TomSelect minChars 0 --- src/web/index.html | 170 +++++++++++++++++++++++++++++---------------- 1 file changed, 109 insertions(+), 61 deletions(-) diff --git a/src/web/index.html b/src/web/index.html index 09207ce..dc400fe 100644 --- a/src/web/index.html +++ b/src/web/index.html @@ -45,26 +45,41 @@ .btn-delete:hover { background: rgba(231,76,60,0.1); } .btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; } .btn-primary:hover { background: #5a52d5; } - .form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; padding: 18px; } - .form-group { display: flex; flex-direction: column; gap: 5px; } - .form-group label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; } - .form-group input, .form-group select { background: var(--bg); border: 1px solid var(--border); border-radius: 5px; padding: 7px 10px; color: var(--text); font-size: 13px; } - .form-group input:focus, .form-group select:focus { outline: none; border-color: var(--accent); } - .form-group select option { background: var(--surface); } - .form-actions { padding: 0 18px 18px; } .empty { padding: 24px 18px; color: var(--muted); font-size: 13px; text-align: center; } - #auth-overlay { position: fixed; inset: 0; background: var(--bg); display: flex; align-items: center; justify-content: center; z-index: 100; } + + /* Auth overlay */ + #auth-overlay { position: fixed; inset: 0; background: var(--bg); display: flex; align-items: center; justify-content: center; z-index: 200; } .auth-box { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 32px; width: 320px; display: flex; flex-direction: column; gap: 16px; } .auth-box h2 { font-size: 16px; color: var(--accent); } .auth-box input { background: var(--bg); border: 1px solid var(--border); border-radius: 5px; padding: 9px 12px; color: var(--text); font-size: 14px; width: 100%; } .auth-box input:focus { outline: none; border-color: var(--accent); } .auth-error { color: var(--red); font-size: 12px; display: none; } - .ts-wrapper .ts-control { background: var(--bg); border-color: var(--border); color: var(--text); min-height: 32px; padding: 4px 10px; border-radius: 5px; } + + /* Modal */ + #modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display: none; align-items: center; justify-content: center; z-index: 100; } + #modal-overlay.open { display: flex; } + .modal { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; width: 480px; max-width: 95vw; display: flex; flex-direction: column; } + .modal-header { padding: 16px 20px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; } + .modal-header h2 { font-size: 15px; font-weight: 600; } + .modal-close { background: none; border: none; color: var(--muted); font-size: 20px; cursor: pointer; line-height: 1; padding: 0 4px; } + .modal-close:hover { color: var(--text); } + .modal-body { padding: 20px; display: flex; flex-direction: column; gap: 14px; } + .modal-footer { padding: 14px 20px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 8px; } + .form-group { display: flex; flex-direction: column; gap: 5px; } + .form-group label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; } + .form-group input, .form-group select { background: var(--bg); border: 1px solid var(--border); border-radius: 5px; padding: 7px 10px; color: var(--text); font-size: 13px; } + .form-group input:focus, .form-group select:focus { outline: none; border-color: var(--accent); } + .form-group select option { background: var(--surface); } + .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } + + /* TomSelect dark theme */ + .ts-wrapper .ts-control { background: var(--bg); border-color: var(--border); color: var(--text); min-height: 34px; padding: 4px 10px; border-radius: 5px; } .ts-wrapper.focus .ts-control { border-color: var(--accent); box-shadow: none; } - .ts-dropdown { background: var(--surface); border-color: var(--border); color: var(--text); } + .ts-dropdown { background: var(--surface); border-color: var(--border); color: var(--text); z-index: 150; } .ts-dropdown .option { padding: 7px 12px; } .ts-dropdown .option.active { background: rgba(108,99,255,0.2); color: var(--text); } .ts-wrapper .ts-control .item { color: var(--text); background: rgba(108,99,255,0.15); border-radius: 3px; padding: 1px 6px; } + .ts-control input { color: var(--text) !important; } @@ -72,12 +87,66 @@

Mukan Special Edition

- + Geçersiz token
+ +

MSE Dashboard

@@ -86,47 +155,16 @@
-
Botlar 0
+
+ Botlar 0 + +
İsimSembolTimeframeUSDTKar %TestnetDurum
Yükleniyor...
-
-
Yeni Bot Ekle
-
-
-
- - -
-
- -
-
- - -
-
-
- -
-
-
-
-
Açık Pozisyonlar
@@ -148,6 +186,7 @@ let AUTH_TOKEN = localStorage.getItem('mse_token') || ''; let sseSource = null; let symbolSelect = null; +let allSymbols = []; function login() { const t = document.getElementById('token-input').value.trim(); @@ -191,14 +230,15 @@ async function loadAll() { async function loadSymbols() { const res = await api('GET', '/symbols'); - const symbols = await res.json(); + allSymbols = await res.json(); symbolSelect = new TomSelect('#f-symbol', { valueField: 'symbol', labelField: 'symbol', searchField: 'symbol', - options: symbols, - placeholder: 'Ara: DOGE, BTC...', + options: allSymbols, + placeholder: 'Ara veya seç...', + minChars: 0, render: { option: (data) => `
@@ -208,7 +248,7 @@ async function loadSymbols() { item: (data) => `
${data.symbol}
`, }, onChange(value) { - const item = symbols.find(s => s.symbol === value); + const item = allSymbols.find(s => s.symbol === value); if (!item) return; document.getElementById('f-usdt').value = item.min_notional; document.getElementById('f-min-label').textContent = `(min ${item.min_notional} USDT)`; @@ -216,6 +256,17 @@ async function loadSymbols() { }); } +function openModal() { + document.getElementById('modal-overlay').classList.add('open'); + if (symbolSelect) symbolSelect.focus(); +} + +function closeModal() { + document.getElementById('modal-overlay').classList.remove('open'); +} + +document.addEventListener('keydown', e => { if (e.key === 'Escape') closeModal(); }); + async function loadBots() { const res = await api('GET', '/bots'); const bots = await res.json(); @@ -276,13 +327,19 @@ async function loadClosed() { async function createBot() { const name = document.getElementById('f-name').value.trim(); - const symbol = document.getElementById('f-symbol').value.trim().toUpperCase(); + const symbol = symbolSelect ? symbolSelect.getValue() : ''; const timeframe = document.getElementById('f-timeframe').value; const usdt_amount = parseFloat(document.getElementById('f-usdt').value); const profit_percent = parseFloat(document.getElementById('f-profit').value); const testnet = document.getElementById('f-testnet').value === 'true'; if (!name || !symbol || !usdt_amount || !profit_percent) { alert('Tüm alanları doldurun'); return; } await api('POST', '/bots', { name, symbol, timeframe, usdt_amount, profit_percent, testnet }); + closeModal(); + document.getElementById('f-name').value = ''; + if (symbolSelect) symbolSelect.clear(); + document.getElementById('f-usdt').value = ''; + document.getElementById('f-profit').value = ''; + document.getElementById('f-min-label').textContent = ''; loadBots(); } @@ -304,17 +361,8 @@ async function deleteBot(id, name) { function connectSSE() { if (sseSource) sseSource.close(); - sseSource = new EventSource('/api/events', { - headers: { 'Authorization': 'Bearer ' + AUTH_TOKEN } - }); - // EventSource doesn't support custom headers natively; use URL param workaround - sseSource.close(); sseSource = new EventSource(`/api/events?token=${encodeURIComponent(AUTH_TOKEN)}`); - - sseSource.addEventListener('trade', () => { - loadPositions(); - loadClosed(); - }); + sseSource.addEventListener('trade', () => { loadPositions(); loadClosed(); }); sseSource.onopen = () => { document.getElementById('sse-dot').className = 'status-dot connected'; document.getElementById('sse-label').textContent = 'canlı';