The logs reveal two separate issues:
NewsPanel — MOCK_NEWS timestamps are computed with Date.now() at module parse time on the server, then timeAgo() re-reads Date.now() on the client at a different moment → mismatch. Fix: use fixed ISO strings for mock data and defer timeAgo rendering to the client with useEffect./coins/markets endpoint does not support 2y, 3y, or 5y in price_change_percentage. Those fields will always be undefined. We need to fetch individual coin history data via /coins/{id}/market_chart to derive those changes. The cleanest fix is to compute them lazily per selected period from the chart endpoint (done when the user picks that period), and show a loading state in the meantime.Let me fix the hydration bug first, then handle the long-period data fetching properly.
Now fix the 2y/3y/5y percentage issue — CoinGecko doesn't support these in /coins/markets. We need to fetch them from the market chart API when those periods are selected:
Now pass coinsWithLongPeriod to the heatmap and add a loading indicator for long periods:
Also update the mock data generator to use the same price_change_percentage_${period}_in_currency field name dynamically for the long periods, since they're now set via cache merge:
Now make sure the heatmap's getPriceChange can also read the dynamically-keyed field we merge in:
The getPriceChange switch cases are correct but the dynamic merge key from page.tsx uses the template literal price_change_percentage_2y_in_currency — which matches the existing CoinData type fields, so that's fine. However, the merge uses [price_change_percentage_$period_in_currency] which for 2y produces price_change_percentage_2y_in_currency — exactly what the switch reads. The logic is consistent. The real issue was that the coinsWithLongPeriod spread was reading from the cache which requires the key format to match — let me verify the cache key lookup matches what's being set:
The logic looks correct. However there's one remaining issue: fetchLongPeriod has longPeriodCache in its dependency array, causing it to re-run infinitely as the cache grows. Let me fix that with a ref:
Finally, add the missing useRef import:
Kolme ongelmaa on korjattu:
NewsPanel) — MOCK_NEWS-aikaleimät muutettiin kiinteiksi ISO-merkkijonoiksi, ja timeAgo-renderöinti siirrettiin clientille useEffect-pohjaiseen TimeAgoLabel-komponenttiin, jolloin palvelin ja selain tuottavat identtisen HTML:n./coins/markets-endpointissa, joten ne haetaan nyt yksitellen /coins/{id}/market_chart-rajapinnasta kun käyttäjä valitsee ko. aikavälin. Tulokset cachetetaan state-muuttujaan, jolloin jo haetut kolikot eivät aiheuta ylimääräisiä pyyntöjä.fetchLongPeriod:n useCallback-riippuvuudet korjattiin useRef-patternilla, jottei cache-päivitys laukaise uudelleenhaettua kutsua.The log shows the hydration error is still in the old timeAgo inline span at news-panel.tsx:162 — my previous edit added the TimeAgoLabel component but didn't successfully replace the old span in the render. Let me read the full news-panel to see its exact current state.
The file has duplicate declarations — MOCK_NEWS and timeAgo are both defined twice, causing a compile error that breaks the whole page. The previous edit prepended the new code without removing the old. I'll rewrite the file cleanly:
Ongelma oli se, että edellinen muokkaus oli lisännyt uuden koodin tiedoston alkuun poistamatta vanhaa — MOCK_NEWS ja timeAgo olivat molemmat määritelty kahdesti, mikä aiheutti käännösvirheen joka romutti koko sivun. Nyt tiedosto on kirjoitettu puhtaasti: MOCK_NEWS käyttää kiinteitä ISO-aikaleimoja, ja TimeAgoLabel renderöi aikaleimat pelkästään clientillä useEffect:n kautta, jolloin hydraatiovirhe poistuu.
You can send messages while the preview loads