JairoDanielMT commited on
Commit
161780e
·
verified ·
1 Parent(s): ddcd034

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +120 -87
index.html CHANGED
@@ -7,38 +7,55 @@
7
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
8
  <style>
9
  body { font-family: Inter, system-ui, Arial; margin: 18px; background:#f7f8fb; color:#111; }
10
- header { display:flex; gap:12px; align-items:center; margin-bottom:12px; }
11
  .card { background:white; border-radius:8px; padding:12px; box-shadow:0 6px 18px rgba(20,20,40,0.06); }
12
- #controls { display:flex; gap:12px; align-items:center; margin-bottom:12px; }
13
  .small { font-size:0.9rem; color:#555; }
14
- #grid { display:grid; grid-template-columns: 1fr 300px; gap:12px; }
15
- #chartCard { min-height:360px; }
 
 
 
 
 
 
 
 
 
 
16
  input[type="number"]{ width:120px; padding:6px; border-radius:6px; border:1px solid #ddd; }
17
  button { padding:8px 10px; border-radius:6px; border:none; background:#2563eb; color:white; cursor:pointer; }
18
  button.secondary { background:#e5e7eb; color:#111; }
19
  .asset-toggle { display:flex; gap:6px; flex-wrap:wrap; }
20
  .asset-toggle label { display:flex; gap:6px; align-items:center; font-size:0.9rem; }
 
 
 
 
21
  </style>
22
  </head>
23
  <body>
24
  <header>
25
- <h2>Crypto 7d — XRP, PAXG, WLD, USDC</h2>
26
- <div class="small">Fuente de precios: CoinGecko · Est. rendement Morpho para USDC (ejemplo).</div>
27
  </header>
28
 
29
  <div id="grid">
30
  <div class="card" id="chartCard">
 
31
  <canvas id="priceChart"></canvas>
 
32
  </div>
33
 
34
  <div class="card">
35
- <h3>Earn Morpho — Estimador (USDC)</h3>
36
- <div class="small" style="margin-bottom:8px;">Tasa usada (30d APY de referencia): <span id="morphoRateDisplay">4.00%</span></div>
 
 
37
 
38
  <label>Monto en USDC:
39
  <input id="amount" type="number" min="0" step="0.01" value="1000">
40
  </label>
41
- <div style="margin-top:8px; display:flex; gap:8px;">
42
  <button id="calcBtn">Calcular (7 días)</button>
43
  <button id="setRateBtn" class="secondary">Usar otra tasa</button>
44
  </div>
@@ -50,33 +67,31 @@
50
  </div>
51
 
52
  <hr style="margin:12px 0;" />
53
- <h4>Visibilidad activos</h4>
54
- <div class="asset-toggle" id="toggles">
55
- <!-- toggles inserted dynamically -->
56
- </div>
57
 
58
  <div style="margin-top:12px;" class="small">
59
- Nota: CoinGecko API pública se usa para datos históricos. Ajusta la tasa Morpho según la plataforma o fuente que prefieras. Fuentes: CoinGecko y pools/agg. de rendimiento (ejemplo). :contentReference[oaicite:2]{index=2}
60
  </div>
61
  </div>
62
  </div>
63
 
64
  <script>
65
  const assets = [
66
- { id: 'xrp', label: 'XRP' },
67
- { id: 'pax-gold', label: 'PAXG' },
68
- { id: 'worldcoin', label: 'WLD' },
69
- { id: 'usd-coin', label: 'USDC' }
 
70
  ];
71
 
72
- // CoinGecko market_chart endpoint: /coins/{id}/market_chart?vs_currency=usd&days=7
73
  async function fetchPriceSeries(assetId) {
74
  const url = `https://api.coingecko.com/api/v3/coins/${assetId}/market_chart?vs_currency=usd&days=7&interval=daily`;
75
  const res = await fetch(url);
76
- if(!res.ok) throw new Error('Error fetching ' + assetId + ' -> ' + res.status);
77
  const j = await res.json();
78
- // j.prices -> [ [timestamp, price], ... ]
79
- return j.prices.map(p => ({ t: p[0], price: p[1] }));
80
  }
81
 
82
  function tsToLabel(ts) {
@@ -89,93 +104,111 @@ function colorFor(label) {
89
  return map[label] || '#888';
90
  }
91
 
92
- (async function init(){
93
- try {
94
- // fetch all series in parallel
95
- const promises = assets.map(a => fetchPriceSeries(a.id));
96
- const all = await Promise.all(promises);
97
-
98
- // build common labels from first series timestamps
99
- const labels = all[0].map(p => tsToLabel(p.t));
100
-
101
- const datasets = assets.map((a, i) => ({
102
- label: a.label,
103
- data: all[i].map(pt => Number(pt.price.toFixed(6))),
104
- borderColor: colorFor(a.label),
105
- backgroundColor: colorFor(a.label),
106
- tension: 0.2,
107
- fill: false,
108
- pointRadius: 3
109
- }));
110
-
111
- // create toggles
112
- const togglesDiv = document.getElementById('toggles');
113
- datasets.forEach((ds, i) => {
114
- const id = 'chk_' + i;
115
- const wrapper = document.createElement('label');
116
- wrapper.innerHTML = `<input type="checkbox" id="${id}" checked data-idx="${i}"> <span style="width:64px;display:inline-block">${ds.label}</span>`;
117
- togglesDiv.appendChild(wrapper);
118
- wrapper.querySelector('input').addEventListener('change', e=>{
119
- const idx = Number(e.target.dataset.idx);
120
- myChart.data.datasets[idx].hidden = !e.target.checked;
121
- myChart.update();
122
- });
123
- });
124
 
125
- // Chart.js
126
- const ctx = document.getElementById('priceChart').getContext('2d');
127
- window.myChart = new Chart(ctx, {
128
- type: 'line',
129
- data: { labels, datasets },
130
- options: {
131
- responsive:true,
132
- interaction: { mode: 'index', intersect: false },
133
- plugins: { legend: { display: true } },
134
- scales: {
135
- y: { ticks: { callback: function(v){ return v; } } }
136
- }
137
- }
138
- });
 
 
 
139
 
140
- } catch (err) {
141
- console.error(err);
142
- document.getElementById('chartCard').innerHTML = '<div class="card"><strong>Error cargando datos:</strong> ' + err.message + '</div>';
143
  }
144
- })();
145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
  // --- Morpho estimator ---
148
- let morphoAPY = 0.04; // referencia 4% (30d APY ejemplo). Cámbiala si tienes otro valor.
149
- document.getElementById('morphoRateDisplay').innerText = (morphoAPY*100).toFixed(2) + '%';
150
 
151
  function estimateCompound(amount, apy, days) {
152
- // interest compounded daily approximation
153
  const daily = apy / 365;
154
  const total = amount * Math.pow(1 + daily, days);
155
  return { total, gain: total - amount };
156
  }
157
 
158
- document.getElementById('calcBtn').addEventListener('click', ()=>{
159
  const amt = parseFloat(document.getElementById('amount').value) || 0;
160
  const res = estimateCompound(amt, morphoAPY, 7);
161
  document.getElementById('gain7').innerText = res.gain.toFixed(6) + ' USDC';
162
  document.getElementById('total7').innerText = res.total.toFixed(6) + ' USDC';
163
  });
164
 
165
- // quick set rate button => prompt for new %
166
- document.getElementById('setRateBtn').addEventListener('click', ()=>{
167
- const v = prompt('Nueva tasa APY (%) — ejemplo 4 para 4%:', (morphoAPY*100).toString());
168
- if(v !== null) {
169
- const n = parseFloat(v)/100;
170
- if(!isNaN(n) && n >= 0) {
171
- morphoAPY = n;
172
- document.getElementById('morphoRateDisplay').innerText = (morphoAPY*100).toFixed(2) + '%';
173
- document.getElementById('calcBtn').click();
174
- } else alert('Valor inválido');
175
  }
176
  });
177
 
178
- // run initial calc
179
  document.getElementById('calcBtn').click();
180
  </script>
181
  </body>
 
7
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
8
  <style>
9
  body { font-family: Inter, system-ui, Arial; margin: 18px; background:#f7f8fb; color:#111; }
10
+ header { display:flex; gap:12px; align-items:center; margin-bottom:12px; flex-wrap:wrap; }
11
  .card { background:white; border-radius:8px; padding:12px; box-shadow:0 6px 18px rgba(20,20,40,0.06); }
 
12
  .small { font-size:0.9rem; color:#555; }
13
+ #grid { display:grid; grid-template-columns: 1fr 300px; gap:12px; align-items:start; }
14
+ #chartCard { min-height:360px; position:relative; }
15
+ .banner {
16
+ display:none;
17
+ margin-bottom:10px;
18
+ padding:10px 12px;
19
+ background:#fff7ed;
20
+ border:1px solid #fed7aa;
21
+ color:#9a3412;
22
+ border-radius:8px;
23
+ font-size:0.95rem;
24
+ }
25
  input[type="number"]{ width:120px; padding:6px; border-radius:6px; border:1px solid #ddd; }
26
  button { padding:8px 10px; border-radius:6px; border:none; background:#2563eb; color:white; cursor:pointer; }
27
  button.secondary { background:#e5e7eb; color:#111; }
28
  .asset-toggle { display:flex; gap:6px; flex-wrap:wrap; }
29
  .asset-toggle label { display:flex; gap:6px; align-items:center; font-size:0.9rem; }
30
+ .muted { color:#6b7280; font-size:0.85rem; }
31
+ @media (max-width: 900px){
32
+ #grid { grid-template-columns: 1fr; }
33
+ }
34
  </style>
35
  </head>
36
  <body>
37
  <header>
38
+ <h2 style="margin:0;">Crypto 7d — XRP, PAXG, WLD, USDC</h2>
39
+ <div class="small">Fuente de precios: CoinGecko · Est. rendimiento Morpho para USDC (ejemplo).</div>
40
  </header>
41
 
42
  <div id="grid">
43
  <div class="card" id="chartCard">
44
+ <div id="errorBox" class="banner"></div>
45
  <canvas id="priceChart"></canvas>
46
+ <div class="muted" style="margin-top:8px;">Tip: puedes ocultar/mostrar activos a la derecha.</div>
47
  </div>
48
 
49
  <div class="card">
50
+ <h3 style="margin-top:0;">Earn Morpho — Estimador (USDC)</h3>
51
+ <div class="small" style="margin-bottom:8px;">
52
+ Tasa usada (30d APY de referencia): <span id="morphoRateDisplay">4.00%</span>
53
+ </div>
54
 
55
  <label>Monto en USDC:
56
  <input id="amount" type="number" min="0" step="0.01" value="1000">
57
  </label>
58
+ <div style="margin-top:8px; display:flex; gap:8px; flex-wrap:wrap;">
59
  <button id="calcBtn">Calcular (7 días)</button>
60
  <button id="setRateBtn" class="secondary">Usar otra tasa</button>
61
  </div>
 
67
  </div>
68
 
69
  <hr style="margin:12px 0;" />
70
+ <h4 style="margin:0 0 8px;">Visibilidad activos</h4>
71
+ <div class="asset-toggle" id="toggles"></div>
 
 
72
 
73
  <div style="margin-top:12px;" class="small">
74
+ Nota: CoinGecko API pública se usa para datos históricos. Ajusta la tasa Morpho según la plataforma o fuente que prefieras.
75
  </div>
76
  </div>
77
  </div>
78
 
79
  <script>
80
  const assets = [
81
+ // IDs correctos en CoinGecko:
82
+ { id: 'ripple', label: 'XRP' },
83
+ { id: 'pax-gold', label: 'PAXG' },
84
+ { id: 'worldcoin-wld', label: 'WLD' },
85
+ { id: 'usd-coin', label: 'USDC' }
86
  ];
87
 
88
+ // CoinGecko market_chart: /coins/{id}/market_chart?vs_currency=usd&days=7&interval=daily
89
  async function fetchPriceSeries(assetId) {
90
  const url = `https://api.coingecko.com/api/v3/coins/${assetId}/market_chart?vs_currency=usd&days=7&interval=daily`;
91
  const res = await fetch(url);
92
+ if (!res.ok) throw new Error(`${assetId} -> ${res.status}`);
93
  const j = await res.json();
94
+ return j.prices.map(p => ({ t: p[0], price: p[1] })); // [timestamp, price]
 
95
  }
96
 
97
  function tsToLabel(ts) {
 
104
  return map[label] || '#888';
105
  }
106
 
107
+ function showError(msg) {
108
+ const box = document.getElementById('errorBox');
109
+ box.style.display = 'block';
110
+ box.textContent = msg;
111
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
+ (async function init(){
114
+ const results = await Promise.allSettled(assets.map(a => fetchPriceSeries(a.id)));
115
+
116
+ // Separa OK vs errores
117
+ const ok = [];
118
+ const fails = [];
119
+ results.forEach((r, i) => {
120
+ if (r.status === 'fulfilled') ok.push({ asset: assets[i], series: r.value });
121
+ else fails.push({ asset: assets[i], error: r.reason });
122
+ });
123
+
124
+ if (fails.length) {
125
+ showError(
126
+ 'Algunos activos no cargaron: ' +
127
+ fails.map(f => `${f.asset.label} (${String(f.error.message || f.error)})`).join(' · ')
128
+ );
129
+ }
130
 
131
+ if (!ok.length) {
132
+ showError('No se pudo cargar ningún activo (revisa conectividad / límites de API).');
133
+ return;
134
  }
 
135
 
136
+ // Labels desde el primer activo exitoso
137
+ const labels = ok[0].series.map(p => tsToLabel(p.t));
138
+
139
+ const datasets = ok.map(({asset, series}) => ({
140
+ label: asset.label,
141
+ data: series.map(pt => Number(pt.price.toFixed(6))),
142
+ borderColor: colorFor(asset.label),
143
+ backgroundColor: colorFor(asset.label),
144
+ tension: 0.2,
145
+ fill: false,
146
+ pointRadius: 3
147
+ }));
148
+
149
+ // Chart.js
150
+ const ctx = document.getElementById('priceChart').getContext('2d');
151
+ window.myChart = new Chart(ctx, {
152
+ type: 'line',
153
+ data: { labels, datasets },
154
+ options: {
155
+ responsive: true,
156
+ interaction: { mode: 'index', intersect: false },
157
+ plugins: { legend: { display: true } },
158
+ scales: { y: { ticks: { callback: (v) => v } } }
159
+ }
160
+ });
161
+
162
+ // toggles (solo para los datasets realmente cargados)
163
+ const togglesDiv = document.getElementById('toggles');
164
+ togglesDiv.innerHTML = '';
165
+ datasets.forEach((ds, i) => {
166
+ const id = 'chk_' + i;
167
+ const wrapper = document.createElement('label');
168
+ wrapper.innerHTML = `
169
+ <input type="checkbox" id="${id}" checked data-idx="${i}">
170
+ <span style="width:64px;display:inline-block">${ds.label}</span>
171
+ `;
172
+ togglesDiv.appendChild(wrapper);
173
+ wrapper.querySelector('input').addEventListener('change', e => {
174
+ const idx = Number(e.target.dataset.idx);
175
+ myChart.data.datasets[idx].hidden = !e.target.checked;
176
+ myChart.update();
177
+ });
178
+ });
179
+ })();
180
 
181
  // --- Morpho estimator ---
182
+ let morphoAPY = 0.04; // ejemplo 4% APY
183
+ document.getElementById('morphoRateDisplay').innerText = (morphoAPY * 100).toFixed(2) + '%';
184
 
185
  function estimateCompound(amount, apy, days) {
 
186
  const daily = apy / 365;
187
  const total = amount * Math.pow(1 + daily, days);
188
  return { total, gain: total - amount };
189
  }
190
 
191
+ document.getElementById('calcBtn').addEventListener('click', () => {
192
  const amt = parseFloat(document.getElementById('amount').value) || 0;
193
  const res = estimateCompound(amt, morphoAPY, 7);
194
  document.getElementById('gain7').innerText = res.gain.toFixed(6) + ' USDC';
195
  document.getElementById('total7').innerText = res.total.toFixed(6) + ' USDC';
196
  });
197
 
198
+ document.getElementById('setRateBtn').addEventListener('click', () => {
199
+ const v = prompt('Nueva tasa APY (%) — ejemplo 4 para 4%:', (morphoAPY * 100).toString());
200
+ if (v === null) return;
201
+ const n = parseFloat(v) / 100;
202
+ if (!isNaN(n) && n >= 0) {
203
+ morphoAPY = n;
204
+ document.getElementById('morphoRateDisplay').innerText = (morphoAPY * 100).toFixed(2) + '%';
205
+ document.getElementById('calcBtn').click();
206
+ } else {
207
+ alert('Valor inválido');
208
  }
209
  });
210
 
211
+ // initial calc
212
  document.getElementById('calcBtn').click();
213
  </script>
214
  </body>