diff --git a/src/app/components/ExchangeRateViewer.js b/src/app/components/ExchangeRateViewer.js index b8706c5..ca5b102 100644 --- a/src/app/components/ExchangeRateViewer.js +++ b/src/app/components/ExchangeRateViewer.js @@ -3,7 +3,7 @@ import React, { useState, useEffect, useCallback } from "react"; import DatePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; -import { format, subDays, startOfMonth } from "date-fns"; +import { format, subDays, startOfMonth, isSameDay } from "date-fns"; import * as XLSX from "xlsx"; import { LoadingSpinner, ErrorMessage, EmptyState } from "./exchange-rate-status"; import { RateTable, ComparisonTable } from "./exchange-rate-table"; @@ -49,11 +49,46 @@ const DATE_PRESETS = [ { label: "This month", start: () => startOfMonth(new Date()), end: () => new Date() }, ]; +function getActivePreset(startDate, endDate) { + if (!startDate || !endDate) return null; + for (const preset of DATE_PRESETS) { + if (isSameDay(startDate, preset.start()) && isSameDay(endDate, preset.end())) { + return preset.label; + } + } + return null; +} + +// Inline SVG icons (16x16) to avoid adding an icon library dependency +function FetchIcon() { + return ( + + ); +} + +function ExcelIcon() { + return ( + + ); +} + +function CsvIcon() { + return ( + + ); +} + export default function ExchangeRateViewer() { - const [startDate, setStartDate] = useState(() => loadSavedSettings()?.startDate || subDays(new Date(), 7)); - const [endDate, setEndDate] = useState(() => loadSavedSettings()?.endDate || new Date()); - const [selectedBank, setSelectedBank] = useState(() => loadSavedSettings()?.bank || "bidv"); - const [selectedCurrency, setSelectedCurrency] = useState(() => loadSavedSettings()?.currency || "USD"); + const [startDate, setStartDate] = useState(() => subDays(new Date(), 7)); + const [endDate, setEndDate] = useState(() => new Date()); + const [selectedBank, setSelectedBank] = useState("bidv"); + const [selectedCurrency, setSelectedCurrency] = useState("USD"); const [compareMode, setCompareMode] = useState(false); const [showChart, setShowChart] = useState(false); const [isLoading, setIsLoading] = useState(false); @@ -62,9 +97,23 @@ export default function ExchangeRateViewer() { const [hasFetched, setHasFetched] = useState(false); const [progress, setProgress] = useState(null); + // Restore saved settings on client mount only (avoids SSR hydration mismatch) + const [hydrated, setHydrated] = useState(false); useEffect(() => { + const saved = loadSavedSettings(); + if (saved) { + if (saved.bank) setSelectedBank(saved.bank); + if (saved.currency) setSelectedCurrency(saved.currency); + if (saved.startDate) setStartDate(saved.startDate); + if (saved.endDate) setEndDate(saved.endDate); + } + setHydrated(true); + }, []); + + useEffect(() => { + if (!hydrated) return; saveSettings(selectedBank, selectedCurrency, startDate, endDate); - }, [selectedBank, selectedCurrency, startDate, endDate]); + }, [selectedBank, selectedCurrency, startDate, endDate, hydrated]); const fetchBank = async (bank) => { const response = await fetch("/api/exchange-rates", { @@ -129,8 +178,8 @@ export default function ExchangeRateViewer() { }, [startDate, endDate, selectedBank, selectedCurrency, compareMode]); useEffect(() => { - handleFetch(); - }, []); // eslint-disable-line react-hooks/exhaustive-deps + if (hydrated) handleFetch(); + }, [hydrated]); // eslint-disable-line react-hooks/exhaustive-deps const getExportRows = () => { if (compareMode) { @@ -170,27 +219,23 @@ export default function ExchangeRateViewer() { URL.revokeObjectURL(url); }; - const inputStyle = { - background: "var(--input-bg)", - color: "var(--foreground)", - borderColor: "var(--input-border)", - }; + const activePreset = getActivePreset(startDate, endDate); return (
+
Fetch exchange rates from Vietnamese banks and export to CSV.