diff --git a/app/api/exchange/bidv/route.js b/app/api/exchange/bidv/route.ts similarity index 86% rename from app/api/exchange/bidv/route.js rename to app/api/exchange/bidv/route.ts index dced990..ea9094b 100644 --- a/app/api/exchange/bidv/route.js +++ b/app/api/exchange/bidv/route.ts @@ -1,16 +1,16 @@ -import { NextResponse } from "next/server" +import { type NextRequest, NextResponse } from "next/server" -async function safeFetch(bidvUrl, tries = 2) { +async function safeFetch(bidvUrl: string, tries = 2) { const headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125 Safari/537.36", Accept: "application/json, text/javascript, */*; q=0.01", Referer: "https://bidv.com.vn/", "X-Requested-With": "XMLHttpRequest", - } + } as const // 1️⃣ helper that really performs the fetch - const doFetch = (target) => + const doFetch = (target: string) => fetch(target, { headers, cache: "no-store", @@ -41,7 +41,7 @@ async function safeFetch(bidvUrl, tries = 2) { throw new Error("fetch failed") } -export async function GET(request) { +export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url) const date = searchParams.get("date") @@ -69,7 +69,7 @@ export async function GET(request) { } // Step 2: Find the record with highest time value - const latestRecord = timeData.data.reduce((prev, current) => (current.time > prev.time ? current : prev)) + const latestRecord = timeData.data.reduce((prev: any, current: any) => (current.time > prev.time ? current : prev)) // Step 3: Get exchange rates using the namerecord const rateUrl = `https://bidv.com.vn/ServicesBIDV/ExchangeDetailServlet?date=${bidvDate}&time=${latestRecord.namerecord}` @@ -82,7 +82,7 @@ export async function GET(request) { const rateData = await rateRes.json() return NextResponse.json(rateData) } catch (error) { - console.error("BIDV API error:", error.message) + console.error("BIDV API error:", (error as Error).message) return NextResponse.json({ error: "Failed to fetch from BIDV" }, { status: 502 }) } } diff --git a/app/api/exchange/techcombank/route.js b/app/api/exchange/techcombank/route.ts similarity index 86% rename from app/api/exchange/techcombank/route.js rename to app/api/exchange/techcombank/route.ts index 10853d7..0d7bf05 100644 --- a/app/api/exchange/techcombank/route.js +++ b/app/api/exchange/techcombank/route.ts @@ -1,6 +1,6 @@ -import { NextResponse } from "next/server" +import { type NextRequest, NextResponse } from "next/server" -export async function GET(request) { +export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url) const date = searchParams.get("date") diff --git a/app/page.jsx b/app/page.tsx similarity index 93% rename from app/page.jsx rename to app/page.tsx index 60f2457..774d98b 100644 --- a/app/page.jsx +++ b/app/page.tsx @@ -10,6 +10,19 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@ import { Download, Loader2 } from "lucide-react" import * as XLSX from "xlsx" +interface ExchangeRateData { + date: string + bank: string + currency: string + askRate: string + bidRateCK: string + bidRateTM: string + askRateTM: string + inputDate: string +} + +type BankType = "techcombank" | "bidv" + // Currency options for each bank const BANK_CURRENCIES = { techcombank: [ @@ -71,24 +84,24 @@ const getDefaultDates = () => { } export default function ExchangeRateExporter() { - const [bank, setBank] = useState("techcombank") + const [bank, setBank] = useState("techcombank") const [currency, setCurrency] = useState("USD (50,100)") const defaultDates = getDefaultDates() const [startDate, setStartDate] = useState(defaultDates.startDate) const [endDate, setEndDate] = useState(defaultDates.endDate) - const [data, setData] = useState([]) + const [data, setData] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState("") // Update currency when bank changes - const handleBankChange = (newBank) => { + const handleBankChange = (newBank: BankType) => { setBank(newBank) // Set default currency for the selected bank const defaultCurrency = newBank === "techcombank" ? "USD (50,100)" : "USD" setCurrency(defaultCurrency) } - const generateDateRange = (start, end) => { + const generateDateRange = (start: string, end: string): string[] => { const dates = [] const startDate = new Date(start) const endDate = new Date(end) @@ -100,7 +113,7 @@ export default function ExchangeRateExporter() { return dates } - const fetchTechcombankRate = async (date) => { + const fetchTechcombankRate = async (date: string): Promise => { try { const res = await fetch(`/api/exchange/techcombank?date=${date}`) if (!res.ok) { @@ -112,7 +125,7 @@ export default function ExchangeRateExporter() { } const result = await res.json() - const currencyData = result.exchangeRate?.data?.find((item) => item.label === currency) + const currencyData = result.exchangeRate?.data?.find((item: any) => item.label === currency) if (!currencyData) return null return { @@ -131,7 +144,7 @@ export default function ExchangeRateExporter() { } } - const fetchBIDVRate = async (date) => { + const fetchBIDVRate = async (date: string): Promise => { try { const res = await fetch(`/api/exchange/bidv?date=${date}`) if (!res.ok) { @@ -143,7 +156,7 @@ export default function ExchangeRateExporter() { } const result = await res.json() - const currencyData = result.data?.find((item) => item.currency === currency) + const currencyData = result.data?.find((item: any) => item.currency === currency) if (!currencyData) return null return { @@ -162,7 +175,7 @@ export default function ExchangeRateExporter() { } } - const fetchExchangeRate = async (date) => { + const fetchExchangeRate = async (date: string): Promise => { if (bank === "techcombank") { return fetchTechcombankRate(date) } else { @@ -187,7 +200,7 @@ export default function ExchangeRateExporter() { try { const dates = generateDateRange(startDate, endDate) - const results = [] + const results: ExchangeRateData[] = [] for (const date of dates) { const rateData = await fetchExchangeRate(date) @@ -280,7 +293,7 @@ export default function ExchangeRateExporter() { URL.revokeObjectURL(url) } - const formatNumber = (value) => { + const formatNumber = (value: string) => { if (value === "N/A" || !value || value === "-") return value // Remove commas and parse const numValue = Number.parseFloat(value.replace(/,/g, "")) diff --git a/components/ui/select.jsx b/components/ui/select.jsx deleted file mode 100644 index fcc6779..0000000 --- a/components/ui/select.jsx +++ /dev/null @@ -1,124 +0,0 @@ -"use client" - -import * as React from "react" -import * as SelectPrimitive from "@radix-ui/react-select" -import { Check, ChevronDown, ChevronUp } from "lucide-react" - -import { cn } from "@/lib/utils" - -const Select = SelectPrimitive.Root - -const SelectGroup = SelectPrimitive.Group - -const SelectValue = SelectPrimitive.Value - -const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => ( - span]:line-clamp-1", - className, - )} - {...props} - > - {children} - - - - -)) -SelectTrigger.displayName = SelectPrimitive.Trigger.displayName - -const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => ( - - - -)) -SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName - -const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => ( - - - -)) -SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName - -const SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => ( - - - - - {children} - - - - -)) -SelectContent.displayName = SelectPrimitive.Content.displayName - -const SelectLabel = React.forwardRef(({ className, ...props }, ref) => ( - -)) -SelectLabel.displayName = SelectPrimitive.Label.displayName - -const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => ( - - - - - - - - {children} - -)) -SelectItem.displayName = SelectPrimitive.Item.displayName - -const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => ( - -)) -SelectSeparator.displayName = SelectPrimitive.Separator.displayName - -export { - Select, - SelectGroup, - SelectValue, - SelectTrigger, - SelectContent, - SelectLabel, - SelectItem, - SelectSeparator, - SelectScrollUpButton, - SelectScrollDownButton, -} diff --git a/components/ui/select.tsx b/components/ui/select.tsx index cbe5a36..6138eb4 100644 --- a/components/ui/select.tsx +++ b/components/ui/select.tsx @@ -20,7 +20,7 @@ const SelectTrigger = React.forwardRef< ref={ref} className={cn( "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", - className + className, )} {...props} > @@ -38,10 +38,7 @@ const SelectScrollUpButton = React.forwardRef< >(({ className, ...props }, ref) => ( @@ -55,17 +52,13 @@ const SelectScrollDownButton = React.forwardRef< >(({ className, ...props }, ref) => ( )) -SelectScrollDownButton.displayName = - SelectPrimitive.ScrollDownButton.displayName +SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName const SelectContent = React.forwardRef< React.ElementRef, @@ -78,7 +71,7 @@ const SelectContent = React.forwardRef< "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", - className + className, )} position={position} {...props} @@ -88,7 +81,7 @@ const SelectContent = React.forwardRef< className={cn( "p-1", position === "popper" && - "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]" + "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]", )} > {children} @@ -103,11 +96,7 @@ const SelectLabel = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )) SelectLabel.displayName = SelectPrimitive.Label.displayName @@ -119,7 +108,7 @@ const SelectItem = React.forwardRef< ref={ref} className={cn( "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", - className + className, )} {...props} > @@ -138,11 +127,7 @@ const SelectSeparator = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( - + )) SelectSeparator.displayName = SelectPrimitive.Separator.displayName diff --git a/components/ui/table.jsx b/components/ui/table.jsx deleted file mode 100644 index d8c6db7..0000000 --- a/components/ui/table.jsx +++ /dev/null @@ -1,57 +0,0 @@ -import * as React from "react" -import { cn } from "@/lib/utils" - -const Table = React.forwardRef(({ className, ...props }, ref) => ( -
- - -)) -Table.displayName = "Table" - -const TableHeader = React.forwardRef(({ className, ...props }, ref) => ( - -)) -TableHeader.displayName = "TableHeader" - -const TableBody = React.forwardRef(({ className, ...props }, ref) => ( - -)) -TableBody.displayName = "TableBody" - -const TableFooter = React.forwardRef(({ className, ...props }, ref) => ( - tr]:last:border-b-0", className)} {...props} /> -)) -TableFooter.displayName = "TableFooter" - -const TableRow = React.forwardRef(({ className, ...props }, ref) => ( - -)) -TableRow.displayName = "TableRow" - -const TableHead = React.forwardRef(({ className, ...props }, ref) => ( -
-)) -TableHead.displayName = "TableHead" - -const TableCell = React.forwardRef(({ className, ...props }, ref) => ( - -)) -TableCell.displayName = "TableCell" - -const TableCaption = React.forwardRef(({ className, ...props }, ref) => ( -
-)) -TableCaption.displayName = "TableCaption" - -export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption } diff --git a/components/ui/table.tsx b/components/ui/table.tsx index 7f3502f..5bde561 100644 --- a/components/ui/table.tsx +++ b/components/ui/table.tsx @@ -1,117 +1,71 @@ import * as React from "react" - import { cn } from "@/lib/utils" -const Table = React.forwardRef< - HTMLTableElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
- - -)) +const Table = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+
+ + ), +) Table.displayName = "Table" -const TableHeader = React.forwardRef< - HTMLTableSectionElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( - -)) +const TableHeader = React.forwardRef>( + ({ className, ...props }, ref) => , +) TableHeader.displayName = "TableHeader" -const TableBody = React.forwardRef< - HTMLTableSectionElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( - -)) +const TableBody = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +) TableBody.displayName = "TableBody" -const TableFooter = React.forwardRef< - HTMLTableSectionElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( - tr]:last:border-b-0", - className - )} - {...props} - /> -)) +const TableFooter = React.forwardRef>( + ({ className, ...props }, ref) => ( + tr]:last:border-b-0", className)} {...props} /> + ), +) TableFooter.displayName = "TableFooter" -const TableRow = React.forwardRef< - HTMLTableRowElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( - -)) +const TableRow = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +) TableRow.displayName = "TableRow" -const TableHead = React.forwardRef< - HTMLTableCellElement, - React.ThHTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) +const TableHead = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +) TableHead.displayName = "TableHead" -const TableCell = React.forwardRef< - HTMLTableCellElement, - React.TdHTMLAttributes ->(({ className, ...props }, ref) => ( - -)) +const TableCell = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +) TableCell.displayName = "TableCell" -const TableCaption = React.forwardRef< - HTMLTableCaptionElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)) +const TableCaption = React.forwardRef>( + ({ className, ...props }, ref) => ( + + ), +) TableCaption.displayName = "TableCaption" -export { - Table, - TableHeader, - TableBody, - TableFooter, - TableHead, - TableRow, - TableCell, - TableCaption, -} +export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }