From c1e37861d175ec47c4127c56d978dc0ecd715549 Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sat, 4 Apr 2026 13:25:14 +0700 Subject: [PATCH] feat: add loading progress indicator with day count and progress bar Show total days being fetched and an animated progress bar during data loading to give users better feedback on long date ranges. --- src/app/api/exchange-rates/route.js | 2 +- src/app/components/ExchangeRateViewer.js | 5 +++-- src/app/components/exchange-rate-status.js | 11 ++++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/app/api/exchange-rates/route.js b/src/app/api/exchange-rates/route.js index f46a66b..8576418 100644 --- a/src/app/api/exchange-rates/route.js +++ b/src/app/api/exchange-rates/route.js @@ -180,7 +180,7 @@ export async function POST(request) { results.push(...chunkResults); } - return Response.json({ data: results, total: dates.length }); + return Response.json({ data: results, total: dates.length, chunks: Math.ceil(dates.length / PARALLEL_CHUNK_SIZE) }); } catch (error) { console.error("Server error:", error); return Response.json( diff --git a/src/app/components/ExchangeRateViewer.js b/src/app/components/ExchangeRateViewer.js index ca5b102..5870671 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, isSameDay } from "date-fns"; +import { format, subDays, startOfMonth, isSameDay, differenceInCalendarDays } from "date-fns"; import * as XLSX from "xlsx"; import { LoadingSpinner, ErrorMessage, EmptyState } from "./exchange-rate-status"; import { RateTable, ComparisonTable } from "./exchange-rate-table"; @@ -146,7 +146,8 @@ export default function ExchangeRateViewer() { setIsLoading(true); setResults([]); setHasFetched(false); - setProgress(null); + const totalDays = differenceInCalendarDays(endDate, startDate) + 1; + setProgress({ current: 0, total: totalDays }); try { if (compareMode) { const [bidvData, tcbData] = await Promise.all([fetchBank("bidv"), fetchBank("tcb")]); diff --git a/src/app/components/exchange-rate-status.js b/src/app/components/exchange-rate-status.js index 3fd88a2..8d0f549 100644 --- a/src/app/components/exchange-rate-status.js +++ b/src/app/components/exchange-rate-status.js @@ -5,11 +5,16 @@ import React from "react"; export function LoadingSpinner({ progress }) { return (
-
+
-

- {progress ? `Fetching ${progress.current}/${progress.total} days...` : "Fetching exchange rates..."} +

+ {progress?.total ? `Fetching rates for ${progress.total} day${progress.total > 1 ? "s" : ""}...` : "Fetching exchange rates..."}

+ {progress?.total > 1 && ( +
+
+
+ )}
);