mirror of
https://github.com/tiennm99/exchange-rate-export.git
synced 2026-10-11 03:13:21 +00:00
chore: clear all Dependabot advisories and add CI (#16)
* build(deps): remove unused frameworks and clear all advisories The v0 scaffold declared @remix-run/react, @sveltejs/kit, svelte, vue and vue-router, none of which are imported anywhere in the app. Removing them drops react-router, react-router-dom, turbo-stream, cookie, vite and their trees, clearing 13 advisories outright. Replace xlsx with write-excel-file. The npm release of xlsx is abandoned at 0.18.5, so its prototype pollution and ReDoS advisories have no registry fix, and SheetJS only ships newer builds from its own CDN as a tarball with no integrity hash, which pnpm rejects. write-excel-file is maintained, carries no advisories, and pulls in only fflate. The export writes the same eight columns in the same order to a sheet named "Exchange Rates", with every value still written as a string. It also cuts the route's first-load JS from 264 kB to 163 kB. Add pnpm overrides forcing the lowest release that closes every advisory affecting the previously resolved version of each remaining transitive dep. postcss is pinned exactly because pnpm's minimumReleaseAge policy rejects releases younger than 24h during CI installs. * ci: add typecheck and build workflow The repository had no workflows. Type errors are skipped during builds via next.config.mjs, so check them here where a regression fails the run. Installs use --frozen-lockfile so dependency overrides cannot silently stop applying.
This commit is contained in:
1 parent
5583743cc3
commit
b74c97c74f
5 files changed
+498
-2002
No files matched your search
@@ -0,0 +1,41 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Typecheck and build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 11
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: pnpm
|
||||
|
||||
# Fails if pnpm-lock.yaml drifts from package.json, so dependency
|
||||
# overrides cannot silently stop applying.
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
# next.config.mjs skips type errors during builds, so check types here
|
||||
# where a regression actually fails the run.
|
||||
- name: Typecheck
|
||||
run: pnpm exec tsc --noEmit
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build
|
||||
+27
-33
@@ -8,7 +8,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
|
||||
import { Download, Loader2 } from "lucide-react"
|
||||
import * as XLSX from "xlsx"
|
||||
import writeXlsxFile, { type Column } from "write-excel-file/browser"
|
||||
|
||||
interface ExchangeRateData {
|
||||
date: string
|
||||
@@ -21,6 +21,19 @@ interface ExchangeRateData {
|
||||
inputDate: string
|
||||
}
|
||||
|
||||
// Mirrors the column order of the CSV export. Every rate arrives from the API
|
||||
// as a string and is written as-is, so no cell is coerced to a number.
|
||||
const XLSX_COLUMNS: Column<ExchangeRateData>[] = [
|
||||
{ header: "Date", cell: (row) => row.date },
|
||||
{ header: "Bank", cell: (row) => row.bank },
|
||||
{ header: "Currency", cell: (row) => row.currency },
|
||||
{ header: "Ask Rate", cell: (row) => row.askRate },
|
||||
{ header: "Bid Rate CK", cell: (row) => row.bidRateCK },
|
||||
{ header: "Bid Rate TM", cell: (row) => row.bidRateTM },
|
||||
{ header: "Ask Rate TM", cell: (row) => row.askRateTM },
|
||||
{ header: "Input Date", cell: (row) => row.inputDate },
|
||||
]
|
||||
|
||||
type BankType = "techcombank" | "bidv"
|
||||
|
||||
// Currency options for each bank
|
||||
@@ -257,40 +270,21 @@ export default function ExchangeRateExporter() {
|
||||
document.body.removeChild(link)
|
||||
}
|
||||
|
||||
const exportToXLSX = () => {
|
||||
const exportToXLSX = async () => {
|
||||
if (data.length === 0) return
|
||||
|
||||
const worksheet = XLSX.utils.json_to_sheet(
|
||||
data.map((row) => ({
|
||||
Date: row.date,
|
||||
Bank: row.bank,
|
||||
Currency: row.currency,
|
||||
"Ask Rate": row.askRate,
|
||||
"Bid Rate CK": row.bidRateCK,
|
||||
"Bid Rate TM": row.bidRateTM,
|
||||
"Ask Rate TM": row.askRateTM,
|
||||
"Input Date": row.inputDate,
|
||||
})),
|
||||
)
|
||||
const workbook = XLSX.utils.book_new()
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Exchange Rates")
|
||||
|
||||
const arrayBuffer = XLSX.write(workbook, {
|
||||
bookType: "xlsx",
|
||||
type: "array",
|
||||
})
|
||||
|
||||
const blob = new Blob([arrayBuffer], {
|
||||
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8",
|
||||
})
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement("a")
|
||||
link.href = url
|
||||
link.download = `exchange_rates_${bank}_${currency.replace(/[^a-zA-Z0-9]/g, "_")}_${startDate}_to_${endDate}.xlsx`
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
// toFile triggers the browser download, so no manual object URL is needed.
|
||||
try {
|
||||
await writeXlsxFile(data, {
|
||||
columns: XLSX_COLUMNS,
|
||||
sheet: "Exchange Rates",
|
||||
}).toFile(
|
||||
`exchange_rates_${bank}_${currency.replace(/[^a-zA-Z0-9]/g, "_")}_${startDate}_to_${endDate}.xlsx`,
|
||||
)
|
||||
} catch (error) {
|
||||
setError("An error occurred while exporting to XLSX")
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
const formatNumber = (value: string) => {
|
||||
|
||||
+2
-7
@@ -37,8 +37,6 @@
|
||||
"@radix-ui/react-toggle": "1.1.1",
|
||||
"@radix-ui/react-toggle-group": "1.1.1",
|
||||
"@radix-ui/react-tooltip": "1.1.6",
|
||||
"@remix-run/react": "latest",
|
||||
"@sveltejs/kit": "latest",
|
||||
"@vercel/analytics": "latest",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
@@ -57,20 +55,17 @@
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"recharts": "2.15.0",
|
||||
"sonner": "^1.7.1",
|
||||
"svelte": "latest",
|
||||
"tailwind-merge": "^2.5.5",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vaul": "^0.9.6",
|
||||
"vue": "latest",
|
||||
"vue-router": "latest",
|
||||
"xlsx": "latest",
|
||||
"write-excel-file": "^4.1.1",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"postcss": "^8.5",
|
||||
"postcss": "8.5.22",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5"
|
||||
}
|
||||
|
||||
Generated
+406
-1962
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,22 @@
|
||||
# Force patched versions of transitive dependencies that carry known advisories.
|
||||
# Each entry is the lowest release that closes every advisory affecting the
|
||||
# version previously resolved in pnpm-lock.yaml.
|
||||
overrides:
|
||||
brace-expansion: ^5.0.8
|
||||
glob: ^10.5.0
|
||||
lodash: ^4.18.1
|
||||
minimatch: ^9.0.7
|
||||
picomatch: ^2.3.2
|
||||
# Pinned exactly rather than with a caret: pnpm's minimumReleaseAge policy
|
||||
# rejects releases younger than 24h in CI, and a caret would keep drifting
|
||||
# onto whatever postcss published today. 8.5.22 clears every advisory,
|
||||
# which need 8.5.18 at the highest.
|
||||
postcss: 8.5.22
|
||||
sharp: ^0.35.3
|
||||
yaml: ^2.8.3
|
||||
|
||||
# next lists sharp as an optional dependency for image optimization, which this
|
||||
# app disables (images.unoptimized in next.config.mjs). Skipping its build
|
||||
# script keeps installs hermetic and free of native compilation.
|
||||
allowBuilds:
|
||||
sharp: false
|
||||
Reference in new issue
Block a user