From 8a99e302663eca69e93e6558a42dfa0db54530d3 Mon Sep 17 00:00:00 2001 From: Antigravity Date: Sat, 28 Mar 2026 10:41:36 +0100 Subject: [PATCH] feat(ci): bundle analysis and gzip size budget (#143) - Add rollup-plugin-visualizer treemap (ANALYZE) and size-limit config - CI job uploads dist/stats.html and enforces budget on dist/assets JS/CSS - Fix PaymentHistory modal wiring and unused import so tsc build passes Made-with: Cursor --- .github/workflows/ci.yml | 40 +++ frontend/.size-limit.json | 8 + frontend/README.md | 5 + frontend/package.json | 6 + frontend/pnpm-lock.yaml | 289 ++++++++++++++++++ .../PaymentDetailModal/PaymentDetailModal.tsx | 2 +- .../PaymentHistory/PaymentHistory.tsx | 3 + frontend/vite.config.js | 17 +- frontend/vite.config.ts | 18 +- 9 files changed, 385 insertions(+), 3 deletions(-) create mode 100644 frontend/.size-limit.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a699ac..339ae34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -147,3 +147,43 @@ jobs: name: build-output path: frontend/dist retention-days: 7 + + bundle-budget: + name: Bundle analysis & size budget + needs: [lint, test] + runs-on: ubuntu-latest + defaults: + run: + working-directory: frontend + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + cache-dependency-path: frontend/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build with rollup visualizer + run: pnpm run build:analyze + + - name: Enforce size budget (size-limit) + run: pnpm run size + + - name: Upload bundle analysis (treemap) + uses: actions/upload-artifact@v4 + with: + name: bundle-stats + path: frontend/dist/stats.html + retention-days: 14 diff --git a/frontend/.size-limit.json b/frontend/.size-limit.json new file mode 100644 index 0000000..2c571ac --- /dev/null +++ b/frontend/.size-limit.json @@ -0,0 +1,8 @@ +[ + { + "name": "All production JS/CSS (gzip)", + "path": ["dist/assets/*.js", "dist/assets/*.css"], + "limit": "2400 kB", + "gzip": true + } +] diff --git a/frontend/README.md b/frontend/README.md index 18bc70e..396148f 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -7,6 +7,11 @@ Currently, two official plugins are available: - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh +## Bundle size & analysis + +- **Budget (CI):** [`size-limit`](https://github.com/ai/size-limit) reads `dist/assets/*.{js,css}` after a production build; limits live in [`.size-limit.json`](./.size-limit.json). +- **Treemap:** `pnpm run build:analyze` sets `ANALYZE=true`, runs `rollup-plugin-visualizer`, and writes `dist/stats.html`. The GitHub Actions **Bundle analysis & size budget** job uploads that file as the `bundle-stats` artifact. + ## React Compiler The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). diff --git a/frontend/package.json b/frontend/package.json index 5c69554..57495da 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,6 +6,8 @@ "scripts": { "dev": "vite", "build": "tsc -b && vite build", + "build:analyze": "cross-env ANALYZE=true tsc -b && cross-env ANALYZE=true vite build", + "size": "size-limit", "lint": "eslint .", "preview": "vite preview", "test": "vitest run", @@ -23,6 +25,7 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", + "@size-limit/file": "^12.0.1", "@tailwindcss/postcss": "^4.2.1", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", @@ -34,12 +37,15 @@ "@typescript-eslint/parser": "^8.0.0", "@vitejs/plugin-react": "^5.1.1", "autoprefixer": "^10.4.24", + "cross-env": "^10.1.0", "eslint": "^9.39.1", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.4.24", "globals": "^16.5.0", "jsdom": "^28.1.0", "postcss": "^8.5.6", + "rollup-plugin-visualizer": "^7.0.1", + "size-limit": "^12.0.1", "tailwindcss": "^4.2.1", "typescript": "^5.6.0", "vite": "^7.3.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index f16cdcf..688248f 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -36,6 +36,9 @@ importers: '@eslint/js': specifier: ^9.39.1 version: 9.39.3 + '@size-limit/file': + specifier: ^12.0.1 + version: 12.0.1(size-limit@12.0.1(jiti@2.6.1)) '@tailwindcss/postcss': specifier: ^4.2.1 version: 4.2.1 @@ -69,6 +72,9 @@ importers: autoprefixer: specifier: ^10.4.24 version: 10.4.24(postcss@8.5.6) + cross-env: + specifier: ^10.1.0 + version: 10.1.0 eslint: specifier: ^9.39.1 version: 9.39.3(jiti@2.6.1) @@ -87,6 +93,12 @@ importers: postcss: specifier: ^8.5.6 version: 8.5.6 + rollup-plugin-visualizer: + specifier: ^7.0.1 + version: 7.0.1(rollup@4.59.0) + size-limit: + specifier: ^12.0.1 + version: 12.0.1(jiti@2.6.1) tailwindcss: specifier: ^4.2.1 version: 4.2.1 @@ -244,6 +256,9 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} + '@epic-web/invariant@1.0.0': + resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} + '@esbuild/aix-ppc64@0.27.3': resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} @@ -618,6 +633,12 @@ packages: cpu: [x64] os: [win32] + '@size-limit/file@12.0.1': + resolution: {integrity: sha512-Kvbnz46iV7WeHaANf1HmWjXBVMU2KkCU+0xJ78FzIjZwlVKKEqy+QCZprdBMfIWrzrvYeqP4cfuzKG8z6xVivg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + size-limit: 12.0.1 + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -932,6 +953,10 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -940,6 +965,10 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -1000,6 +1029,14 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + bytes-iec@3.1.1: + resolution: {integrity: sha512-fey6+4jDK7TFtFg/klGSvNKJctyU7n2aQdnM+CO0ruLPbqqMOM8Tio0Pc+deqUeVKX1tL5DQep1zQ7+37aTAsA==} + engines: {node: '>= 0.8'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1019,6 +1056,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + cliui@9.0.1: + resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} + engines: {node: '>=20'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -1044,6 +1085,11 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} + cross-env@10.1.0: + resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} + engines: {node: '>=20'} + hasBin: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1128,6 +1174,18 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} + engines: {node: '>=18'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1153,6 +1211,9 @@ packages: electron-to-chromium@1.5.302: resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + enhanced-resolve@5.19.0: resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} engines: {node: '>=10.13.0'} @@ -1324,6 +1385,14 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.5.0: + resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} + engines: {node: '>=18'} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1418,6 +1487,11 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1426,9 +1500,22 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-in-ssh@1.0.0: + resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==} + engines: {node: '>=20'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} + engines: {node: '>=16'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1548,6 +1635,10 @@ packages: resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -1608,6 +1699,9 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanospinner@1.2.2: + resolution: {integrity: sha512-Zt/AmG6qRU3e+WnzGGLuMCEAO/dAu45stNbHY223tUxldaDAeE+FxSPsd9Q+j+paejmm0ZbrNVs5Sraqy3dRxA==} + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -1617,6 +1711,10 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + open@11.0.0: + resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==} + engines: {node: '>=20'} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -1661,6 +1759,10 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + powershell-utils@0.1.0: + resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} + engines: {node: '>=20'} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -1755,11 +1857,28 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + rollup-plugin-visualizer@7.0.1: + resolution: {integrity: sha512-UJUT4+1Ho4OcWmPYU3sYXgUqI8B8Ayfe06MX7y0qCJ1K8aGoKtR/NDd/2nZqM7ADkrzny+I99Ul7GgyoiVNAgg==} + engines: {node: '>=22'} + hasBin: true + peerDependencies: + rolldown: 1.x || ^1.0.0-beta || ^1.0.0-rc + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + rollup@4.59.0: resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -1795,16 +1914,38 @@ packages: siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + size-limit@12.0.1: + resolution: {integrity: sha512-vuFj+6lDOoBJQu6OLhcMQv7jnbXjuoEn4WsQHlSLOV/8EFfOka/tfjtLQ/rZig5Gagi3R0GnU/0kd4EY/y2etg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + jiti: ^2.0.0 + peerDependenciesMeta: + jiti: + optional: true + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} + engines: {node: '>=12'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -2003,6 +2144,14 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + wsl-utils@0.3.1: + resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} + engines: {node: '>=20'} + xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} @@ -2010,9 +2159,21 @@ packages: xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + + yargs@18.0.0: + resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2192,6 +2353,8 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} + '@epic-web/invariant@1.0.0': {} + '@esbuild/aix-ppc64@0.27.3': optional: true @@ -2437,6 +2600,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true + '@size-limit/file@12.0.1(size-limit@12.0.1(jiti@2.6.1))': + dependencies: + size-limit: 12.0.1(jiti@2.6.1) + '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} @@ -2787,12 +2954,16 @@ snapshots: ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} + argparse@2.0.1: {} aria-query@5.3.0: @@ -2856,6 +3027,12 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + + bytes-iec@3.1.1: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -2872,6 +3049,12 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + cliui@9.0.1: + dependencies: + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + clsx@2.1.1: {} color-convert@2.0.1: @@ -2890,6 +3073,11 @@ snapshots: cookie@1.1.1: {} + cross-env@10.1.0: + dependencies: + '@epic-web/invariant': 1.0.0 + cross-spawn: 7.0.6 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -2967,6 +3155,15 @@ snapshots: deep-is@0.1.4: {} + default-browser-id@5.0.1: {} + + default-browser@5.5.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + define-lazy-prop@3.0.0: {} + delayed-stream@1.0.0: {} dequal@2.0.3: {} @@ -2985,6 +3182,8 @@ snapshots: electron-to-chromium@1.5.302: {} + emoji-regex@10.6.0: {} + enhanced-resolve@5.19.0: dependencies: graceful-fs: 4.2.11 @@ -3182,6 +3381,10 @@ snapshots: gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + + get-east-asian-width@1.5.0: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -3271,14 +3474,26 @@ snapshots: internmap@2.0.3: {} + is-docker@3.0.0: {} + is-extglob@2.1.1: {} is-glob@4.0.3: dependencies: is-extglob: 2.1.1 + is-in-ssh@1.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + is-potential-custom-element-name@1.0.1: {} + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + isexe@2.0.0: {} jiti@2.6.1: {} @@ -3384,6 +3599,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.31.1 lightningcss-win32-x64-msvc: 1.31.1 + lilconfig@3.1.3: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -3430,12 +3647,25 @@ snapshots: nanoid@3.3.11: {} + nanospinner@1.2.2: + dependencies: + picocolors: 1.1.1 + natural-compare@1.4.0: {} node-releases@2.0.27: {} obug@2.1.1: {} + open@11.0.0: + dependencies: + default-browser: 5.5.0 + define-lazy-prop: 3.0.0 + is-in-ssh: 1.0.0 + is-inside-container: 1.0.0 + powershell-utils: 0.1.0 + wsl-utils: 0.3.1 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -3479,6 +3709,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + powershell-utils@0.1.0: {} + prelude-ls@1.2.1: {} pretty-format@27.5.1: @@ -3564,6 +3796,15 @@ snapshots: resolve-from@4.0.0: {} + rollup-plugin-visualizer@7.0.1(rollup@4.59.0): + dependencies: + open: 11.0.0 + picomatch: 4.0.3 + source-map: 0.7.6 + yargs: 18.0.0 + optionalDependencies: + rollup: 4.59.0 + rollup@4.59.0: dependencies: '@types/estree': 1.0.8 @@ -3595,6 +3836,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 + run-applescript@7.1.0: {} + saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -3617,12 +3860,34 @@ snapshots: siginfo@2.0.0: {} + size-limit@12.0.1(jiti@2.6.1): + dependencies: + bytes-iec: 3.1.1 + lilconfig: 3.1.3 + nanospinner: 1.2.2 + picocolors: 1.1.1 + tinyglobby: 0.2.15 + optionalDependencies: + jiti: 2.6.1 + source-map-js@1.2.1: {} + source-map@0.7.6: {} + stackback@0.0.2: {} std-env@3.10.0: {} + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 @@ -3790,12 +4055,36 @@ snapshots: word-wrap@1.2.5: {} + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + wsl-utils@0.3.1: + dependencies: + is-wsl: 3.1.1 + powershell-utils: 0.1.0 + xml-name-validator@5.0.0: {} xmlchars@2.2.0: {} + y18n@5.0.8: {} + yallist@3.1.1: {} + yargs-parser@22.0.0: {} + + yargs@18.0.0: + dependencies: + cliui: 9.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + string-width: 7.2.0 + y18n: 5.0.8 + yargs-parser: 22.0.0 + yocto-queue@0.1.0: {} zod-validation-error@4.0.2(zod@4.3.6): diff --git a/frontend/src/pages/Payments/PaymentDetailModal/PaymentDetailModal.tsx b/frontend/src/pages/Payments/PaymentDetailModal/PaymentDetailModal.tsx index 7509950..94cc0a7 100644 --- a/frontend/src/pages/Payments/PaymentDetailModal/PaymentDetailModal.tsx +++ b/frontend/src/pages/Payments/PaymentDetailModal/PaymentDetailModal.tsx @@ -1,6 +1,6 @@ // frontend/src/pages/Payments/PaymentDetailModal/PaymentDetailModal.tsx import React, { useEffect } from 'react'; -import { X, ExternalLink, ShieldCheck, Clock, MapPin } from 'lucide-react'; +import { X, ExternalLink, ShieldCheck, MapPin } from 'lucide-react'; import './PaymentDetailModal.css'; interface PaymentDetailModalProps { diff --git a/frontend/src/pages/Payments/PaymentHistory/PaymentHistory.tsx b/frontend/src/pages/Payments/PaymentHistory/PaymentHistory.tsx index f260c2a..753f025 100644 --- a/frontend/src/pages/Payments/PaymentHistory/PaymentHistory.tsx +++ b/frontend/src/pages/Payments/PaymentHistory/PaymentHistory.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { Link } from "react-router-dom"; import { ChevronDown, ChevronLeft, ChevronRight, ExternalLink, ArrowUpDown } from "lucide-react"; +import PaymentDetailModal from "../PaymentDetailModal/PaymentDetailModal"; type PaymentStatus = "Pending" | "Escrowed" | "Released" | "Failed"; @@ -17,6 +18,8 @@ const statusClasses: Record = { const PaymentHistory: React.FC = () => { const [isLoading] = useState(false); + const [selectedPayment, setSelectedPayment] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); const [filterStatus, setFilterStatus] = useState("All"); const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc"); const [currentPage, setCurrentPage] = useState(1); diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 917eac4..c27b3d0 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -4,11 +4,26 @@ import { configDefaults, defineConfig } from "vitest/config"; import react from "@vitejs/plugin-react"; import { resolve } from "path"; import { fileURLToPath } from "url"; +import { visualizer } from "rollup-plugin-visualizer"; // import tailwindcss from '@tailwindcss/vite' const __dirname = fileURLToPath(new URL(".", import.meta.url)); // https://vite.dev/config/ +const analyze = process.env.ANALYZE === "true"; export default defineConfig({ - plugins: [react()], // tailwindcss() removed temporarily for CI + plugins: [ + react(), // tailwindcss() removed temporarily for CI + ...(analyze + ? [ + visualizer({ + filename: "dist/stats.html", + gzipSize: true, + brotliSize: true, + open: false, + template: "treemap", + }), + ] + : []), + ], resolve: { alias: { "@": resolve(__dirname, "./src"), diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 2afe732..a4e763a 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -4,13 +4,29 @@ import { configDefaults, defineConfig } from "vitest/config"; import react from "@vitejs/plugin-react"; import { resolve } from "path"; import { fileURLToPath } from "url"; +import { visualizer } from "rollup-plugin-visualizer"; // import tailwindcss from '@tailwindcss/vite' const __dirname = fileURLToPath(new URL(".", import.meta.url)); // https://vite.dev/config/ +const analyze = process.env.ANALYZE === "true"; + export default defineConfig({ - plugins: [react()], // tailwindcss() removed temporarily for CI + plugins: [ + react(), // tailwindcss() removed temporarily for CI + ...(analyze + ? [ + visualizer({ + filename: "dist/stats.html", + gzipSize: true, + brotliSize: true, + open: false, + template: "treemap", + }), + ] + : []), + ], resolve: { alias: { "@": resolve(__dirname, "./src"),