Antaram commited on
Commit
c2ddef6
·
verified ·
1 Parent(s): 6a6bf32

Upload 40 files

Browse files
Files changed (10) hide show
  1. .firebaserc +5 -5
  2. README.md +10 -10
  3. firebase.json +10 -10
  4. package-lock.json +27 -97
  5. pages/PartyLedger.tsx +22 -4
  6. services/db.ts +356 -356
  7. types.ts +3 -0
  8. utils/LedgerPdfGenerator.ts +326 -239
  9. utils/exportToExcel.ts +135 -14
  10. vite.config.ts +16 -16
.firebaserc CHANGED
@@ -1,5 +1,5 @@
1
- {
2
- "projects": {
3
- "default": "pattanshetti-03-12"
4
- }
5
- }
 
1
+ {
2
+ "projects": {
3
+ "default": "pattanshetti-03-12"
4
+ }
5
+ }
README.md CHANGED
@@ -1,10 +1,10 @@
1
- ---
2
- title: Stagingfrontend
3
- emoji: 🐨
4
- colorFrom: indigo
5
- colorTo: pink
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ ---
2
+ title: Stagingfrontend
3
+ emoji: 🐨
4
+ colorFrom: indigo
5
+ colorTo: pink
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
firebase.json CHANGED
@@ -1,10 +1,10 @@
1
- {
2
- "hosting": {
3
- "public": "dist",
4
- "ignore": [
5
- "firebase.json",
6
- "**/.*",
7
- "**/node_modules/**"
8
- ]
9
- }
10
- }
 
1
+ {
2
+ "hosting": {
3
+ "public": "dist",
4
+ "ignore": [
5
+ "firebase.json",
6
+ "**/.*",
7
+ "**/node_modules/**"
8
+ ]
9
+ }
10
+ }
package-lock.json CHANGED
@@ -57,6 +57,7 @@
57
  "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
58
  "dev": true,
59
  "license": "MIT",
 
60
  "dependencies": {
61
  "@babel/code-frame": "^7.27.1",
62
  "@babel/generator": "^7.28.5",
@@ -764,7 +765,6 @@
764
  "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
765
  "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
766
  "license": "MIT",
767
- "peer": true,
768
  "dependencies": {
769
  "eslint-visitor-keys": "^3.4.3"
770
  },
@@ -783,7 +783,6 @@
783
  "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
784
  "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
785
  "license": "Apache-2.0",
786
- "peer": true,
787
  "engines": {
788
  "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
789
  },
@@ -796,7 +795,6 @@
796
  "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
797
  "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
798
  "license": "MIT",
799
- "peer": true,
800
  "engines": {
801
  "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
802
  }
@@ -806,7 +804,6 @@
806
  "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
807
  "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
808
  "license": "Apache-2.0",
809
- "peer": true,
810
  "dependencies": {
811
  "@eslint/object-schema": "^2.1.7",
812
  "debug": "^4.3.1",
@@ -821,7 +818,6 @@
821
  "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
822
  "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
823
  "license": "Apache-2.0",
824
- "peer": true,
825
  "dependencies": {
826
  "@eslint/core": "^0.17.0"
827
  },
@@ -834,7 +830,6 @@
834
  "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
835
  "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
836
  "license": "Apache-2.0",
837
- "peer": true,
838
  "dependencies": {
839
  "@types/json-schema": "^7.0.15"
840
  },
@@ -847,7 +842,6 @@
847
  "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
848
  "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
849
  "license": "MIT",
850
- "peer": true,
851
  "dependencies": {
852
  "ajv": "^6.12.4",
853
  "debug": "^4.3.2",
@@ -871,7 +865,6 @@
871
  "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
872
  "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
873
  "license": "MIT",
874
- "peer": true,
875
  "engines": {
876
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
877
  },
@@ -884,7 +877,6 @@
884
  "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
885
  "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
886
  "license": "Apache-2.0",
887
- "peer": true,
888
  "engines": {
889
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
890
  }
@@ -894,7 +886,6 @@
894
  "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
895
  "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
896
  "license": "Apache-2.0",
897
- "peer": true,
898
  "dependencies": {
899
  "@eslint/core": "^0.17.0",
900
  "levn": "^0.4.1"
@@ -908,7 +899,6 @@
908
  "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
909
  "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
910
  "license": "Apache-2.0",
911
- "peer": true,
912
  "engines": {
913
  "node": ">=18.18.0"
914
  }
@@ -918,7 +908,6 @@
918
  "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
919
  "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
920
  "license": "Apache-2.0",
921
- "peer": true,
922
  "dependencies": {
923
  "@humanfs/core": "^0.19.1",
924
  "@humanwhocodes/retry": "^0.4.0"
@@ -932,7 +921,6 @@
932
  "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
933
  "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
934
  "license": "Apache-2.0",
935
- "peer": true,
936
  "engines": {
937
  "node": ">=12.22"
938
  },
@@ -946,7 +934,6 @@
946
  "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
947
  "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
948
  "license": "Apache-2.0",
949
- "peer": true,
950
  "engines": {
951
  "node": ">=18.18"
952
  },
@@ -1476,8 +1463,7 @@
1476
  "version": "7.0.15",
1477
  "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
1478
  "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
1479
- "license": "MIT",
1480
- "peer": true
1481
  },
1482
  "node_modules/@types/node": {
1483
  "version": "22.19.1",
@@ -1485,6 +1471,7 @@
1485
  "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==",
1486
  "dev": true,
1487
  "license": "MIT",
 
1488
  "dependencies": {
1489
  "undici-types": "~6.21.0"
1490
  }
@@ -1554,7 +1541,6 @@
1554
  "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
1555
  "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
1556
  "license": "MIT",
1557
- "peer": true,
1558
  "peerDependencies": {
1559
  "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
1560
  }
@@ -1573,7 +1559,6 @@
1573
  "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
1574
  "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
1575
  "license": "MIT",
1576
- "peer": true,
1577
  "dependencies": {
1578
  "fast-deep-equal": "^3.1.1",
1579
  "fast-json-stable-stringify": "^2.0.0",
@@ -1590,7 +1575,6 @@
1590
  "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
1591
  "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
1592
  "license": "MIT",
1593
- "peer": true,
1594
  "dependencies": {
1595
  "color-convert": "^2.0.1"
1596
  },
@@ -1605,15 +1589,13 @@
1605
  "version": "2.0.1",
1606
  "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
1607
  "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
1608
- "license": "Python-2.0",
1609
- "peer": true
1610
  },
1611
  "node_modules/balanced-match": {
1612
  "version": "1.0.2",
1613
  "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
1614
  "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
1615
- "license": "MIT",
1616
- "peer": true
1617
  },
1618
  "node_modules/base64-arraybuffer": {
1619
  "version": "1.0.2",
@@ -1639,7 +1621,6 @@
1639
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
1640
  "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
1641
  "license": "MIT",
1642
- "peer": true,
1643
  "dependencies": {
1644
  "balanced-match": "^1.0.0",
1645
  "concat-map": "0.0.1"
@@ -1665,6 +1646,7 @@
1665
  }
1666
  ],
1667
  "license": "MIT",
 
1668
  "dependencies": {
1669
  "baseline-browser-mapping": "^2.8.25",
1670
  "caniuse-lite": "^1.0.30001754",
@@ -1684,7 +1666,6 @@
1684
  "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
1685
  "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
1686
  "license": "MIT",
1687
- "peer": true,
1688
  "engines": {
1689
  "node": ">=6"
1690
  }
@@ -1748,7 +1729,6 @@
1748
  "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
1749
  "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
1750
  "license": "MIT",
1751
- "peer": true,
1752
  "dependencies": {
1753
  "ansi-styles": "^4.1.0",
1754
  "supports-color": "^7.1.0"
@@ -1783,7 +1763,6 @@
1783
  "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
1784
  "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
1785
  "license": "MIT",
1786
- "peer": true,
1787
  "dependencies": {
1788
  "color-name": "~1.1.4"
1789
  },
@@ -1795,8 +1774,7 @@
1795
  "version": "1.1.4",
1796
  "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
1797
  "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
1798
- "license": "MIT",
1799
- "peer": true
1800
  },
1801
  "node_modules/commander": {
1802
  "version": "2.17.1",
@@ -1808,8 +1786,7 @@
1808
  "version": "0.0.1",
1809
  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
1810
  "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
1811
- "license": "MIT",
1812
- "peer": true
1813
  },
1814
  "node_modules/convert-source-map": {
1815
  "version": "2.0.0",
@@ -1856,7 +1833,6 @@
1856
  "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
1857
  "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
1858
  "license": "MIT",
1859
- "peer": true,
1860
  "dependencies": {
1861
  "path-key": "^3.1.0",
1862
  "shebang-command": "^2.0.0",
@@ -2023,8 +1999,7 @@
2023
  "version": "0.1.4",
2024
  "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
2025
  "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
2026
- "license": "MIT",
2027
- "peer": true
2028
  },
2029
  "node_modules/dompurify": {
2030
  "version": "3.3.0",
@@ -2110,7 +2085,6 @@
2110
  "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
2111
  "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
2112
  "license": "MIT",
2113
- "peer": true,
2114
  "engines": {
2115
  "node": ">=10"
2116
  },
@@ -2195,7 +2169,6 @@
2195
  "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
2196
  "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
2197
  "license": "BSD-2-Clause",
2198
- "peer": true,
2199
  "dependencies": {
2200
  "esrecurse": "^4.3.0",
2201
  "estraverse": "^5.2.0"
@@ -2212,7 +2185,6 @@
2212
  "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
2213
  "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
2214
  "license": "Apache-2.0",
2215
- "peer": true,
2216
  "engines": {
2217
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2218
  },
@@ -2225,7 +2197,6 @@
2225
  "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
2226
  "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
2227
  "license": "BSD-2-Clause",
2228
- "peer": true,
2229
  "dependencies": {
2230
  "acorn": "^8.15.0",
2231
  "acorn-jsx": "^5.3.2",
@@ -2243,7 +2214,6 @@
2243
  "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
2244
  "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
2245
  "license": "BSD-3-Clause",
2246
- "peer": true,
2247
  "dependencies": {
2248
  "estraverse": "^5.1.0"
2249
  },
@@ -2256,7 +2226,6 @@
2256
  "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
2257
  "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
2258
  "license": "BSD-2-Clause",
2259
- "peer": true,
2260
  "dependencies": {
2261
  "estraverse": "^5.2.0"
2262
  },
@@ -2269,7 +2238,6 @@
2269
  "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
2270
  "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
2271
  "license": "BSD-2-Clause",
2272
- "peer": true,
2273
  "engines": {
2274
  "node": ">=4.0"
2275
  }
@@ -2279,7 +2247,6 @@
2279
  "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
2280
  "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
2281
  "license": "BSD-2-Clause",
2282
- "peer": true,
2283
  "engines": {
2284
  "node": ">=0.10.0"
2285
  }
@@ -2303,22 +2270,19 @@
2303
  "version": "3.1.3",
2304
  "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
2305
  "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
2306
- "license": "MIT",
2307
- "peer": true
2308
  },
2309
  "node_modules/fast-json-stable-stringify": {
2310
  "version": "2.1.0",
2311
  "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
2312
  "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
2313
- "license": "MIT",
2314
- "peer": true
2315
  },
2316
  "node_modules/fast-levenshtein": {
2317
  "version": "2.0.6",
2318
  "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
2319
  "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
2320
- "license": "MIT",
2321
- "peer": true
2322
  },
2323
  "node_modules/fast-png": {
2324
  "version": "6.4.0",
@@ -2360,7 +2324,6 @@
2360
  "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
2361
  "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
2362
  "license": "MIT",
2363
- "peer": true,
2364
  "dependencies": {
2365
  "flat-cache": "^4.0.0"
2366
  },
@@ -2373,7 +2336,6 @@
2373
  "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
2374
  "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
2375
  "license": "MIT",
2376
- "peer": true,
2377
  "dependencies": {
2378
  "locate-path": "^6.0.0",
2379
  "path-exists": "^4.0.0"
@@ -2390,7 +2352,6 @@
2390
  "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
2391
  "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
2392
  "license": "MIT",
2393
- "peer": true,
2394
  "dependencies": {
2395
  "flatted": "^3.2.9",
2396
  "keyv": "^4.5.4"
@@ -2403,8 +2364,7 @@
2403
  "version": "3.3.3",
2404
  "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
2405
  "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
2406
- "license": "ISC",
2407
- "peer": true
2408
  },
2409
  "node_modules/frac": {
2410
  "version": "1.1.2",
@@ -2445,7 +2405,6 @@
2445
  "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
2446
  "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
2447
  "license": "ISC",
2448
- "peer": true,
2449
  "dependencies": {
2450
  "is-glob": "^4.0.3"
2451
  },
@@ -2458,7 +2417,6 @@
2458
  "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
2459
  "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
2460
  "license": "MIT",
2461
- "peer": true,
2462
  "engines": {
2463
  "node": ">=18"
2464
  },
@@ -2471,7 +2429,6 @@
2471
  "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
2472
  "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
2473
  "license": "MIT",
2474
- "peer": true,
2475
  "engines": {
2476
  "node": ">=8"
2477
  }
@@ -2494,7 +2451,6 @@
2494
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
2495
  "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
2496
  "license": "MIT",
2497
- "peer": true,
2498
  "engines": {
2499
  "node": ">= 4"
2500
  }
@@ -2514,7 +2470,6 @@
2514
  "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
2515
  "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
2516
  "license": "MIT",
2517
- "peer": true,
2518
  "dependencies": {
2519
  "parent-module": "^1.0.0",
2520
  "resolve-from": "^4.0.0"
@@ -2531,7 +2486,6 @@
2531
  "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
2532
  "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
2533
  "license": "MIT",
2534
- "peer": true,
2535
  "engines": {
2536
  "node": ">=0.8.19"
2537
  }
@@ -2556,7 +2510,6 @@
2556
  "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
2557
  "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
2558
  "license": "MIT",
2559
- "peer": true,
2560
  "engines": {
2561
  "node": ">=0.10.0"
2562
  }
@@ -2566,7 +2519,6 @@
2566
  "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
2567
  "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
2568
  "license": "MIT",
2569
- "peer": true,
2570
  "dependencies": {
2571
  "is-extglob": "^2.1.1"
2572
  },
@@ -2578,8 +2530,7 @@
2578
  "version": "2.0.0",
2579
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
2580
  "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
2581
- "license": "ISC",
2582
- "peer": true
2583
  },
2584
  "node_modules/js-tokens": {
2585
  "version": "4.0.0",
@@ -2593,7 +2544,6 @@
2593
  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
2594
  "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
2595
  "license": "MIT",
2596
- "peer": true,
2597
  "dependencies": {
2598
  "argparse": "^2.0.1"
2599
  },
@@ -2618,22 +2568,19 @@
2618
  "version": "3.0.1",
2619
  "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
2620
  "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
2621
- "license": "MIT",
2622
- "peer": true
2623
  },
2624
  "node_modules/json-schema-traverse": {
2625
  "version": "0.4.1",
2626
  "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
2627
  "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
2628
- "license": "MIT",
2629
- "peer": true
2630
  },
2631
  "node_modules/json-stable-stringify-without-jsonify": {
2632
  "version": "1.0.1",
2633
  "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
2634
  "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
2635
- "license": "MIT",
2636
- "peer": true
2637
  },
2638
  "node_modules/json5": {
2639
  "version": "2.2.3",
@@ -2653,6 +2600,7 @@
2653
  "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.4.tgz",
2654
  "integrity": "sha512-dc6oQ8y37rRcHn316s4ngz/nOjayLF/FFxBF4V9zamQKRqXxyiH1zagkCdktdWhtoQId5K20xt1lB90XzkB+hQ==",
2655
  "license": "MIT",
 
2656
  "dependencies": {
2657
  "@babel/runtime": "^7.28.4",
2658
  "fast-png": "^6.2.0",
@@ -2679,7 +2627,6 @@
2679
  "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
2680
  "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
2681
  "license": "MIT",
2682
- "peer": true,
2683
  "dependencies": {
2684
  "json-buffer": "3.0.1"
2685
  }
@@ -2689,7 +2636,6 @@
2689
  "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
2690
  "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
2691
  "license": "MIT",
2692
- "peer": true,
2693
  "dependencies": {
2694
  "prelude-ls": "^1.2.1",
2695
  "type-check": "~0.4.0"
@@ -2703,7 +2649,6 @@
2703
  "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
2704
  "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
2705
  "license": "MIT",
2706
- "peer": true,
2707
  "dependencies": {
2708
  "p-locate": "^5.0.0"
2709
  },
@@ -2718,8 +2663,7 @@
2718
  "version": "4.6.2",
2719
  "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
2720
  "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
2721
- "license": "MIT",
2722
- "peer": true
2723
  },
2724
  "node_modules/lru-cache": {
2725
  "version": "5.1.1",
@@ -2745,7 +2689,6 @@
2745
  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
2746
  "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
2747
  "license": "ISC",
2748
- "peer": true,
2749
  "dependencies": {
2750
  "brace-expansion": "^1.1.7"
2751
  },
@@ -2782,8 +2725,7 @@
2782
  "version": "1.4.0",
2783
  "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
2784
  "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
2785
- "license": "MIT",
2786
- "peer": true
2787
  },
2788
  "node_modules/node-releases": {
2789
  "version": "2.0.27",
@@ -2797,7 +2739,6 @@
2797
  "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
2798
  "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
2799
  "license": "MIT",
2800
- "peer": true,
2801
  "dependencies": {
2802
  "deep-is": "^0.1.3",
2803
  "fast-levenshtein": "^2.0.6",
@@ -2815,7 +2756,6 @@
2815
  "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
2816
  "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
2817
  "license": "MIT",
2818
- "peer": true,
2819
  "dependencies": {
2820
  "yocto-queue": "^0.1.0"
2821
  },
@@ -2831,7 +2771,6 @@
2831
  "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
2832
  "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
2833
  "license": "MIT",
2834
- "peer": true,
2835
  "dependencies": {
2836
  "p-limit": "^3.0.2"
2837
  },
@@ -2853,7 +2792,6 @@
2853
  "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
2854
  "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
2855
  "license": "MIT",
2856
- "peer": true,
2857
  "dependencies": {
2858
  "callsites": "^3.0.0"
2859
  },
@@ -2866,7 +2804,6 @@
2866
  "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
2867
  "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
2868
  "license": "MIT",
2869
- "peer": true,
2870
  "engines": {
2871
  "node": ">=8"
2872
  }
@@ -2876,7 +2813,6 @@
2876
  "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
2877
  "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
2878
  "license": "MIT",
2879
- "peer": true,
2880
  "engines": {
2881
  "node": ">=8"
2882
  }
@@ -2901,6 +2837,7 @@
2901
  "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
2902
  "dev": true,
2903
  "license": "MIT",
 
2904
  "engines": {
2905
  "node": ">=12"
2906
  },
@@ -2942,7 +2879,6 @@
2942
  "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
2943
  "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
2944
  "license": "MIT",
2945
- "peer": true,
2946
  "engines": {
2947
  "node": ">= 0.8.0"
2948
  }
@@ -2964,7 +2900,6 @@
2964
  "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
2965
  "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
2966
  "license": "MIT",
2967
- "peer": true,
2968
  "engines": {
2969
  "node": ">=6"
2970
  }
@@ -2984,6 +2919,7 @@
2984
  "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
2985
  "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
2986
  "license": "MIT",
 
2987
  "engines": {
2988
  "node": ">=0.10.0"
2989
  }
@@ -2993,6 +2929,7 @@
2993
  "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
2994
  "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
2995
  "license": "MIT",
 
2996
  "dependencies": {
2997
  "scheduler": "^0.27.0"
2998
  },
@@ -3012,6 +2949,7 @@
3012
  "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
3013
  "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
3014
  "license": "MIT",
 
3015
  "dependencies": {
3016
  "@types/use-sync-external-store": "^0.0.6",
3017
  "use-sync-external-store": "^1.4.0"
@@ -3113,7 +3051,8 @@
3113
  "version": "5.0.1",
3114
  "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
3115
  "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
3116
- "license": "MIT"
 
3117
  },
3118
  "node_modules/redux-thunk": {
3119
  "version": "3.1.0",
@@ -3142,7 +3081,6 @@
3142
  "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
3143
  "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
3144
  "license": "MIT",
3145
- "peer": true,
3146
  "engines": {
3147
  "node": ">=4"
3148
  }
@@ -3226,7 +3164,6 @@
3226
  "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
3227
  "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
3228
  "license": "MIT",
3229
- "peer": true,
3230
  "dependencies": {
3231
  "shebang-regex": "^3.0.0"
3232
  },
@@ -3239,7 +3176,6 @@
3239
  "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
3240
  "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
3241
  "license": "MIT",
3242
- "peer": true,
3243
  "engines": {
3244
  "node": ">=8"
3245
  }
@@ -3281,7 +3217,6 @@
3281
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
3282
  "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
3283
  "license": "MIT",
3284
- "peer": true,
3285
  "engines": {
3286
  "node": ">=8"
3287
  },
@@ -3294,7 +3229,6 @@
3294
  "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
3295
  "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
3296
  "license": "MIT",
3297
- "peer": true,
3298
  "dependencies": {
3299
  "has-flag": "^4.0.0"
3300
  },
@@ -3349,7 +3283,6 @@
3349
  "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
3350
  "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
3351
  "license": "MIT",
3352
- "peer": true,
3353
  "dependencies": {
3354
  "prelude-ls": "^1.2.1"
3355
  },
@@ -3414,7 +3347,6 @@
3414
  "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
3415
  "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
3416
  "license": "BSD-2-Clause",
3417
- "peer": true,
3418
  "dependencies": {
3419
  "punycode": "^2.1.0"
3420
  }
@@ -3465,6 +3397,7 @@
3465
  "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
3466
  "dev": true,
3467
  "license": "MIT",
 
3468
  "dependencies": {
3469
  "esbuild": "^0.25.0",
3470
  "fdir": "^6.4.4",
@@ -3539,7 +3472,6 @@
3539
  "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
3540
  "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
3541
  "license": "ISC",
3542
- "peer": true,
3543
  "dependencies": {
3544
  "isexe": "^2.0.0"
3545
  },
@@ -3573,7 +3505,6 @@
3573
  "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
3574
  "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
3575
  "license": "MIT",
3576
- "peer": true,
3577
  "engines": {
3578
  "node": ">=0.10.0"
3579
  }
@@ -3679,7 +3610,6 @@
3679
  "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
3680
  "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
3681
  "license": "MIT",
3682
- "peer": true,
3683
  "engines": {
3684
  "node": ">=10"
3685
  },
 
57
  "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
58
  "dev": true,
59
  "license": "MIT",
60
+ "peer": true,
61
  "dependencies": {
62
  "@babel/code-frame": "^7.27.1",
63
  "@babel/generator": "^7.28.5",
 
765
  "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
766
  "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
767
  "license": "MIT",
 
768
  "dependencies": {
769
  "eslint-visitor-keys": "^3.4.3"
770
  },
 
783
  "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
784
  "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
785
  "license": "Apache-2.0",
 
786
  "engines": {
787
  "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
788
  },
 
795
  "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
796
  "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
797
  "license": "MIT",
 
798
  "engines": {
799
  "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
800
  }
 
804
  "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
805
  "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
806
  "license": "Apache-2.0",
 
807
  "dependencies": {
808
  "@eslint/object-schema": "^2.1.7",
809
  "debug": "^4.3.1",
 
818
  "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
819
  "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
820
  "license": "Apache-2.0",
 
821
  "dependencies": {
822
  "@eslint/core": "^0.17.0"
823
  },
 
830
  "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
831
  "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
832
  "license": "Apache-2.0",
 
833
  "dependencies": {
834
  "@types/json-schema": "^7.0.15"
835
  },
 
842
  "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
843
  "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
844
  "license": "MIT",
 
845
  "dependencies": {
846
  "ajv": "^6.12.4",
847
  "debug": "^4.3.2",
 
865
  "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz",
866
  "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==",
867
  "license": "MIT",
 
868
  "engines": {
869
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
870
  },
 
877
  "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
878
  "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
879
  "license": "Apache-2.0",
 
880
  "engines": {
881
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
882
  }
 
886
  "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
887
  "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
888
  "license": "Apache-2.0",
 
889
  "dependencies": {
890
  "@eslint/core": "^0.17.0",
891
  "levn": "^0.4.1"
 
899
  "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
900
  "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
901
  "license": "Apache-2.0",
 
902
  "engines": {
903
  "node": ">=18.18.0"
904
  }
 
908
  "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
909
  "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
910
  "license": "Apache-2.0",
 
911
  "dependencies": {
912
  "@humanfs/core": "^0.19.1",
913
  "@humanwhocodes/retry": "^0.4.0"
 
921
  "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
922
  "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
923
  "license": "Apache-2.0",
 
924
  "engines": {
925
  "node": ">=12.22"
926
  },
 
934
  "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
935
  "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
936
  "license": "Apache-2.0",
 
937
  "engines": {
938
  "node": ">=18.18"
939
  },
 
1463
  "version": "7.0.15",
1464
  "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
1465
  "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
1466
+ "license": "MIT"
 
1467
  },
1468
  "node_modules/@types/node": {
1469
  "version": "22.19.1",
 
1471
  "integrity": "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==",
1472
  "dev": true,
1473
  "license": "MIT",
1474
+ "peer": true,
1475
  "dependencies": {
1476
  "undici-types": "~6.21.0"
1477
  }
 
1541
  "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
1542
  "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
1543
  "license": "MIT",
 
1544
  "peerDependencies": {
1545
  "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
1546
  }
 
1559
  "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
1560
  "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
1561
  "license": "MIT",
 
1562
  "dependencies": {
1563
  "fast-deep-equal": "^3.1.1",
1564
  "fast-json-stable-stringify": "^2.0.0",
 
1575
  "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
1576
  "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
1577
  "license": "MIT",
 
1578
  "dependencies": {
1579
  "color-convert": "^2.0.1"
1580
  },
 
1589
  "version": "2.0.1",
1590
  "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
1591
  "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
1592
+ "license": "Python-2.0"
 
1593
  },
1594
  "node_modules/balanced-match": {
1595
  "version": "1.0.2",
1596
  "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
1597
  "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
1598
+ "license": "MIT"
 
1599
  },
1600
  "node_modules/base64-arraybuffer": {
1601
  "version": "1.0.2",
 
1621
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
1622
  "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
1623
  "license": "MIT",
 
1624
  "dependencies": {
1625
  "balanced-match": "^1.0.0",
1626
  "concat-map": "0.0.1"
 
1646
  }
1647
  ],
1648
  "license": "MIT",
1649
+ "peer": true,
1650
  "dependencies": {
1651
  "baseline-browser-mapping": "^2.8.25",
1652
  "caniuse-lite": "^1.0.30001754",
 
1666
  "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
1667
  "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
1668
  "license": "MIT",
 
1669
  "engines": {
1670
  "node": ">=6"
1671
  }
 
1729
  "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
1730
  "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
1731
  "license": "MIT",
 
1732
  "dependencies": {
1733
  "ansi-styles": "^4.1.0",
1734
  "supports-color": "^7.1.0"
 
1763
  "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
1764
  "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
1765
  "license": "MIT",
 
1766
  "dependencies": {
1767
  "color-name": "~1.1.4"
1768
  },
 
1774
  "version": "1.1.4",
1775
  "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
1776
  "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
1777
+ "license": "MIT"
 
1778
  },
1779
  "node_modules/commander": {
1780
  "version": "2.17.1",
 
1786
  "version": "0.0.1",
1787
  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
1788
  "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
1789
+ "license": "MIT"
 
1790
  },
1791
  "node_modules/convert-source-map": {
1792
  "version": "2.0.0",
 
1833
  "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
1834
  "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
1835
  "license": "MIT",
 
1836
  "dependencies": {
1837
  "path-key": "^3.1.0",
1838
  "shebang-command": "^2.0.0",
 
1999
  "version": "0.1.4",
2000
  "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
2001
  "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
2002
+ "license": "MIT"
 
2003
  },
2004
  "node_modules/dompurify": {
2005
  "version": "3.3.0",
 
2085
  "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
2086
  "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
2087
  "license": "MIT",
 
2088
  "engines": {
2089
  "node": ">=10"
2090
  },
 
2169
  "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
2170
  "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
2171
  "license": "BSD-2-Clause",
 
2172
  "dependencies": {
2173
  "esrecurse": "^4.3.0",
2174
  "estraverse": "^5.2.0"
 
2185
  "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
2186
  "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
2187
  "license": "Apache-2.0",
 
2188
  "engines": {
2189
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
2190
  },
 
2197
  "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
2198
  "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
2199
  "license": "BSD-2-Clause",
 
2200
  "dependencies": {
2201
  "acorn": "^8.15.0",
2202
  "acorn-jsx": "^5.3.2",
 
2214
  "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
2215
  "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
2216
  "license": "BSD-3-Clause",
 
2217
  "dependencies": {
2218
  "estraverse": "^5.1.0"
2219
  },
 
2226
  "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
2227
  "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
2228
  "license": "BSD-2-Clause",
 
2229
  "dependencies": {
2230
  "estraverse": "^5.2.0"
2231
  },
 
2238
  "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
2239
  "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
2240
  "license": "BSD-2-Clause",
 
2241
  "engines": {
2242
  "node": ">=4.0"
2243
  }
 
2247
  "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
2248
  "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
2249
  "license": "BSD-2-Clause",
 
2250
  "engines": {
2251
  "node": ">=0.10.0"
2252
  }
 
2270
  "version": "3.1.3",
2271
  "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
2272
  "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
2273
+ "license": "MIT"
 
2274
  },
2275
  "node_modules/fast-json-stable-stringify": {
2276
  "version": "2.1.0",
2277
  "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
2278
  "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
2279
+ "license": "MIT"
 
2280
  },
2281
  "node_modules/fast-levenshtein": {
2282
  "version": "2.0.6",
2283
  "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
2284
  "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
2285
+ "license": "MIT"
 
2286
  },
2287
  "node_modules/fast-png": {
2288
  "version": "6.4.0",
 
2324
  "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
2325
  "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
2326
  "license": "MIT",
 
2327
  "dependencies": {
2328
  "flat-cache": "^4.0.0"
2329
  },
 
2336
  "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
2337
  "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
2338
  "license": "MIT",
 
2339
  "dependencies": {
2340
  "locate-path": "^6.0.0",
2341
  "path-exists": "^4.0.0"
 
2352
  "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
2353
  "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
2354
  "license": "MIT",
 
2355
  "dependencies": {
2356
  "flatted": "^3.2.9",
2357
  "keyv": "^4.5.4"
 
2364
  "version": "3.3.3",
2365
  "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
2366
  "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
2367
+ "license": "ISC"
 
2368
  },
2369
  "node_modules/frac": {
2370
  "version": "1.1.2",
 
2405
  "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
2406
  "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
2407
  "license": "ISC",
 
2408
  "dependencies": {
2409
  "is-glob": "^4.0.3"
2410
  },
 
2417
  "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
2418
  "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
2419
  "license": "MIT",
 
2420
  "engines": {
2421
  "node": ">=18"
2422
  },
 
2429
  "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
2430
  "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
2431
  "license": "MIT",
 
2432
  "engines": {
2433
  "node": ">=8"
2434
  }
 
2451
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
2452
  "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
2453
  "license": "MIT",
 
2454
  "engines": {
2455
  "node": ">= 4"
2456
  }
 
2470
  "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
2471
  "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
2472
  "license": "MIT",
 
2473
  "dependencies": {
2474
  "parent-module": "^1.0.0",
2475
  "resolve-from": "^4.0.0"
 
2486
  "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
2487
  "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
2488
  "license": "MIT",
 
2489
  "engines": {
2490
  "node": ">=0.8.19"
2491
  }
 
2510
  "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
2511
  "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
2512
  "license": "MIT",
 
2513
  "engines": {
2514
  "node": ">=0.10.0"
2515
  }
 
2519
  "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
2520
  "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
2521
  "license": "MIT",
 
2522
  "dependencies": {
2523
  "is-extglob": "^2.1.1"
2524
  },
 
2530
  "version": "2.0.0",
2531
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
2532
  "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
2533
+ "license": "ISC"
 
2534
  },
2535
  "node_modules/js-tokens": {
2536
  "version": "4.0.0",
 
2544
  "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
2545
  "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
2546
  "license": "MIT",
 
2547
  "dependencies": {
2548
  "argparse": "^2.0.1"
2549
  },
 
2568
  "version": "3.0.1",
2569
  "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
2570
  "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
2571
+ "license": "MIT"
 
2572
  },
2573
  "node_modules/json-schema-traverse": {
2574
  "version": "0.4.1",
2575
  "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
2576
  "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
2577
+ "license": "MIT"
 
2578
  },
2579
  "node_modules/json-stable-stringify-without-jsonify": {
2580
  "version": "1.0.1",
2581
  "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
2582
  "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
2583
+ "license": "MIT"
 
2584
  },
2585
  "node_modules/json5": {
2586
  "version": "2.2.3",
 
2600
  "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.4.tgz",
2601
  "integrity": "sha512-dc6oQ8y37rRcHn316s4ngz/nOjayLF/FFxBF4V9zamQKRqXxyiH1zagkCdktdWhtoQId5K20xt1lB90XzkB+hQ==",
2602
  "license": "MIT",
2603
+ "peer": true,
2604
  "dependencies": {
2605
  "@babel/runtime": "^7.28.4",
2606
  "fast-png": "^6.2.0",
 
2627
  "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
2628
  "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
2629
  "license": "MIT",
 
2630
  "dependencies": {
2631
  "json-buffer": "3.0.1"
2632
  }
 
2636
  "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
2637
  "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
2638
  "license": "MIT",
 
2639
  "dependencies": {
2640
  "prelude-ls": "^1.2.1",
2641
  "type-check": "~0.4.0"
 
2649
  "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
2650
  "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
2651
  "license": "MIT",
 
2652
  "dependencies": {
2653
  "p-locate": "^5.0.0"
2654
  },
 
2663
  "version": "4.6.2",
2664
  "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
2665
  "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
2666
+ "license": "MIT"
 
2667
  },
2668
  "node_modules/lru-cache": {
2669
  "version": "5.1.1",
 
2689
  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
2690
  "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
2691
  "license": "ISC",
 
2692
  "dependencies": {
2693
  "brace-expansion": "^1.1.7"
2694
  },
 
2725
  "version": "1.4.0",
2726
  "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
2727
  "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
2728
+ "license": "MIT"
 
2729
  },
2730
  "node_modules/node-releases": {
2731
  "version": "2.0.27",
 
2739
  "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
2740
  "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
2741
  "license": "MIT",
 
2742
  "dependencies": {
2743
  "deep-is": "^0.1.3",
2744
  "fast-levenshtein": "^2.0.6",
 
2756
  "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
2757
  "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
2758
  "license": "MIT",
 
2759
  "dependencies": {
2760
  "yocto-queue": "^0.1.0"
2761
  },
 
2771
  "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
2772
  "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
2773
  "license": "MIT",
 
2774
  "dependencies": {
2775
  "p-limit": "^3.0.2"
2776
  },
 
2792
  "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
2793
  "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
2794
  "license": "MIT",
 
2795
  "dependencies": {
2796
  "callsites": "^3.0.0"
2797
  },
 
2804
  "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
2805
  "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
2806
  "license": "MIT",
 
2807
  "engines": {
2808
  "node": ">=8"
2809
  }
 
2813
  "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
2814
  "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
2815
  "license": "MIT",
 
2816
  "engines": {
2817
  "node": ">=8"
2818
  }
 
2837
  "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
2838
  "dev": true,
2839
  "license": "MIT",
2840
+ "peer": true,
2841
  "engines": {
2842
  "node": ">=12"
2843
  },
 
2879
  "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
2880
  "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
2881
  "license": "MIT",
 
2882
  "engines": {
2883
  "node": ">= 0.8.0"
2884
  }
 
2900
  "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
2901
  "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
2902
  "license": "MIT",
 
2903
  "engines": {
2904
  "node": ">=6"
2905
  }
 
2919
  "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
2920
  "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
2921
  "license": "MIT",
2922
+ "peer": true,
2923
  "engines": {
2924
  "node": ">=0.10.0"
2925
  }
 
2929
  "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
2930
  "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
2931
  "license": "MIT",
2932
+ "peer": true,
2933
  "dependencies": {
2934
  "scheduler": "^0.27.0"
2935
  },
 
2949
  "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
2950
  "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
2951
  "license": "MIT",
2952
+ "peer": true,
2953
  "dependencies": {
2954
  "@types/use-sync-external-store": "^0.0.6",
2955
  "use-sync-external-store": "^1.4.0"
 
3051
  "version": "5.0.1",
3052
  "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
3053
  "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
3054
+ "license": "MIT",
3055
+ "peer": true
3056
  },
3057
  "node_modules/redux-thunk": {
3058
  "version": "3.1.0",
 
3081
  "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
3082
  "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
3083
  "license": "MIT",
 
3084
  "engines": {
3085
  "node": ">=4"
3086
  }
 
3164
  "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
3165
  "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
3166
  "license": "MIT",
 
3167
  "dependencies": {
3168
  "shebang-regex": "^3.0.0"
3169
  },
 
3176
  "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
3177
  "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
3178
  "license": "MIT",
 
3179
  "engines": {
3180
  "node": ">=8"
3181
  }
 
3217
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
3218
  "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
3219
  "license": "MIT",
 
3220
  "engines": {
3221
  "node": ">=8"
3222
  },
 
3229
  "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
3230
  "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
3231
  "license": "MIT",
 
3232
  "dependencies": {
3233
  "has-flag": "^4.0.0"
3234
  },
 
3283
  "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
3284
  "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
3285
  "license": "MIT",
 
3286
  "dependencies": {
3287
  "prelude-ls": "^1.2.1"
3288
  },
 
3347
  "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
3348
  "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
3349
  "license": "BSD-2-Clause",
 
3350
  "dependencies": {
3351
  "punycode": "^2.1.0"
3352
  }
 
3397
  "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==",
3398
  "dev": true,
3399
  "license": "MIT",
3400
+ "peer": true,
3401
  "dependencies": {
3402
  "esbuild": "^0.25.0",
3403
  "fdir": "^6.4.4",
 
3472
  "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
3473
  "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
3474
  "license": "ISC",
 
3475
  "dependencies": {
3476
  "isexe": "^2.0.0"
3477
  },
 
3505
  "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
3506
  "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
3507
  "license": "MIT",
 
3508
  "engines": {
3509
  "node": ">=0.10.0"
3510
  }
 
3610
  "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
3611
  "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
3612
  "license": "MIT",
 
3613
  "engines": {
3614
  "node": ">=10"
3615
  },
pages/PartyLedger.tsx CHANGED
@@ -1,6 +1,7 @@
1
  import React, { useEffect, useState, useMemo } from 'react';
2
  import { getParties, getTransactions, updateTransactionPayment } from '../services/db';
3
- import { Party, Transaction, PartyType, BillType } from '../types';
 
4
  import { Users, Filter, Search, ArrowLeft, Eye, Edit, Save, X, Printer, Download, Calendar, ArrowUpDown, FileText } from 'lucide-react';
5
  import PrintInvoice from '../components/PrintInvoice.tsx';
6
  import PdfInvoice from '../components/PdfInvoice.tsx';
@@ -28,11 +29,18 @@ const PartyLedger = () => {
28
  const [toDate, setToDate] = useState('');
29
  const [sortOrder, setSortOrder] = useState<'newest' | 'oldest'>('newest');
30
 
31
- const loadData = async () => {
32
  setParties(await getParties());
 
 
 
33
  setTransactions(await getTransactions());
34
  };
35
 
 
 
 
 
36
  useEffect(() => {
37
  loadData();
38
  }, []);
@@ -174,6 +182,7 @@ const PartyLedger = () => {
174
  const amount = parseFloat(paymentAmount);
175
  if (isNaN(amount) || amount <= 0) {
176
  alert('Please enter a valid amount');
 
177
  return;
178
  }
179
  if (amount > tx.balance_amount) {
@@ -191,14 +200,23 @@ const PartyLedger = () => {
191
  ...t,
192
  paid_amount: t.paid_amount + amount,
193
  balance_amount: t.balance_amount - amount,
 
 
 
 
 
 
 
 
 
194
  }
195
  : t,
196
  ),
197
  );
198
  setEditingTxId(null);
199
  setPaymentAmount('');
200
- // Reload data to refresh party balances
201
- loadData();
202
  } else {
203
  alert(res.message);
204
  }
 
1
  import React, { useEffect, useState, useMemo } from 'react';
2
  import { getParties, getTransactions, updateTransactionPayment } from '../services/db';
3
+ import { Party, Transaction, PartyType, BillType, PaymentMode } from '../types';
4
+
5
  import { Users, Filter, Search, ArrowLeft, Eye, Edit, Save, X, Printer, Download, Calendar, ArrowUpDown, FileText } from 'lucide-react';
6
  import PrintInvoice from '../components/PrintInvoice.tsx';
7
  import PdfInvoice from '../components/PdfInvoice.tsx';
 
29
  const [toDate, setToDate] = useState('');
30
  const [sortOrder, setSortOrder] = useState<'newest' | 'oldest'>('newest');
31
 
32
+ const refreshParties = async () => {
33
  setParties(await getParties());
34
+ };
35
+
36
+ const refreshTransactions = async () => {
37
  setTransactions(await getTransactions());
38
  };
39
 
40
+ const loadData = async () => {
41
+ await Promise.all([refreshParties(), refreshTransactions()]);
42
+ };
43
+
44
  useEffect(() => {
45
  loadData();
46
  }, []);
 
182
  const amount = parseFloat(paymentAmount);
183
  if (isNaN(amount) || amount <= 0) {
184
  alert('Please enter a valid amount');
185
+
186
  return;
187
  }
188
  if (amount > tx.balance_amount) {
 
200
  ...t,
201
  paid_amount: t.paid_amount + amount,
202
  balance_amount: t.balance_amount - amount,
203
+ payments: [
204
+ ...(t.payments || []),
205
+ {
206
+ mode: PaymentMode.CASH,
207
+ amount,
208
+ payment_date: new Date().toISOString().split('T')[0],
209
+ reference: `Due Payment - ${new Date().toLocaleDateString('en-IN')}`,
210
+ }
211
+ ]
212
  }
213
  : t,
214
  ),
215
  );
216
  setEditingTxId(null);
217
  setPaymentAmount('');
218
+ // Refresh party balances without overwriting the locally updated transaction amounts
219
+ refreshParties();
220
  } else {
221
  alert(res.message);
222
  }
services/db.ts CHANGED
@@ -1,357 +1,357 @@
1
- import {
2
- Party, MirchiType, Lot, Transaction, PartyType,
3
- LotStatus, BillType, ApiResponse, PaymentMode
4
- } from '../types';
5
-
6
- const API_BASE = 'https://antaram-stagingbackend.hf.space/api';
7
-
8
- // Helper function to parse numeric values from API
9
- const parseNumeric = (value: any): number => {
10
- if (typeof value === 'string') {
11
- return parseFloat(value);
12
- }
13
- return value || 0;
14
- };
15
-
16
- // Helper function to parse poti_weights from string to array
17
- const parsePotiWeights = (weights: any): number[] => {
18
- if (typeof weights === 'string') {
19
- try {
20
- return JSON.parse(weights);
21
- } catch {
22
- return [];
23
- }
24
- }
25
- return weights || [];
26
- };
27
-
28
- // Helper function to normalize transaction data from API
29
- const normalizeTransaction = (tx: any): Transaction => {
30
- // Filter out null items that come from PostgreSQL json_agg when there are no items
31
- const rawItems = tx.items || [];
32
- const validItems = Array.isArray(rawItems)
33
- ? rawItems.filter((item: any) => item && item.id !== null)
34
- : [];
35
-
36
- const grossWeightTotal = parseNumeric(tx.gross_weight_total);
37
- const netWeightTotal = parseNumeric(tx.net_weight_total);
38
- const subtotal = parseNumeric(tx.subtotal);
39
- const totalExpenses = parseNumeric(tx.total_expenses);
40
- const totalAmount = parseNumeric(tx.total_amount);
41
- const paidAmount = parseNumeric(tx.paid_amount);
42
- const balanceAmount = parseNumeric(tx.balance_amount);
43
-
44
- // Normalize expenses and reconstruct other_expenses if backend didn't send it
45
- let normalizedExpenses;
46
- if (tx.expenses) {
47
- const hasOtherExpensesField = Object.prototype.hasOwnProperty.call(tx.expenses, 'other_expenses');
48
-
49
- const baseExpenses = {
50
- cess_percent: parseNumeric(tx.expenses.cess_percent),
51
- cess_amount: parseNumeric(tx.expenses.cess_amount),
52
- adat_percent: parseNumeric(tx.expenses.adat_percent),
53
- adat_amount: parseNumeric(tx.expenses.adat_amount),
54
- poti_rate: parseNumeric(tx.expenses.poti_rate),
55
- poti_amount: parseNumeric(tx.expenses.poti_amount),
56
- hamali_per_poti: parseNumeric(tx.expenses.hamali_per_poti),
57
- hamali_amount: parseNumeric(tx.expenses.hamali_amount),
58
- packaging_hamali_per_poti: parseNumeric(tx.expenses.packaging_hamali_per_poti),
59
- packaging_hamali_amount: parseNumeric(tx.expenses.packaging_hamali_amount),
60
- gaadi_bharni: parseNumeric(tx.expenses.gaadi_bharni),
61
- };
62
-
63
- let otherExpensesValue = hasOtherExpensesField
64
- ? parseNumeric(tx.expenses.other_expenses)
65
- : 0;
66
-
67
- // If backend doesn't provide other_expenses but total_expenses is known,
68
- // infer other_expenses as the remaining part after subtracting known components.
69
- if (!hasOtherExpensesField && totalExpenses) {
70
- const knownComponents =
71
- baseExpenses.poti_amount +
72
- baseExpenses.cess_amount +
73
- baseExpenses.adat_amount +
74
- baseExpenses.hamali_amount +
75
- baseExpenses.packaging_hamali_amount +
76
- baseExpenses.gaadi_bharni;
77
-
78
- const diff = totalExpenses - knownComponents;
79
- if (diff > 0) {
80
- otherExpensesValue = diff;
81
- }
82
- }
83
-
84
- normalizedExpenses = {
85
- ...baseExpenses,
86
- other_expenses: otherExpensesValue,
87
- };
88
- } else {
89
- normalizedExpenses = {
90
- cess_percent: 0,
91
- cess_amount: 0,
92
- adat_percent: 0,
93
- adat_amount: 0,
94
- poti_rate: 0,
95
- poti_amount: 0,
96
- hamali_per_poti: 0,
97
- hamali_amount: 0,
98
- packaging_hamali_per_poti: 0,
99
- packaging_hamali_amount: 0,
100
- gaadi_bharni: 0,
101
- other_expenses: 0,
102
- };
103
- }
104
-
105
- return {
106
- ...tx,
107
- gross_weight_total: grossWeightTotal,
108
- net_weight_total: netWeightTotal,
109
- subtotal,
110
- total_expenses: totalExpenses,
111
- total_amount: totalAmount,
112
- paid_amount: paidAmount,
113
- balance_amount: balanceAmount,
114
- items: validItems.map((item: any) => ({
115
- ...item,
116
- poti_weights: parsePotiWeights(item.poti_weights),
117
- gross_weight: parseNumeric(item.gross_weight),
118
- poti_count: parseNumeric(item.poti_count),
119
- total_potya: parseNumeric(item.total_potya),
120
- net_weight: parseNumeric(item.net_weight),
121
- rate_per_kg: parseNumeric(item.rate_per_kg),
122
- item_total: parseNumeric(item.item_total),
123
- })),
124
- expenses: normalizedExpenses,
125
- payments: (tx.payments || []).map((p: any) => ({
126
- ...p,
127
- amount: parseNumeric(p.amount),
128
- })),
129
- };
130
- };
131
-
132
- // Parties API
133
- export const getParties = async (): Promise<Party[]> => {
134
- try {
135
- const res = await fetch(`${API_BASE}/parties`);
136
- if (!res.ok) throw new Error('Failed to load parties');
137
- const data = await res.json();
138
- return data.map((p: any) => ({
139
- ...p,
140
- current_balance: parseNumeric(p.current_balance)
141
- }));
142
- } catch (error) {
143
- console.error('Error fetching parties:', error);
144
- return [];
145
- }
146
- };
147
-
148
- export const saveParty = async (party: Party): Promise<ApiResponse<Party>> => {
149
- try {
150
- const res = await fetch(`${API_BASE}/parties`, {
151
- method: 'POST',
152
- headers: { 'Content-Type': 'application/json' },
153
- body: JSON.stringify(party),
154
- });
155
-
156
- const data = await res.json();
157
- if (!res.ok || !data.success) {
158
- return { success: false, message: data?.message || 'Error saving party' };
159
- }
160
-
161
- return {
162
- success: true,
163
- data: {
164
- ...data.data,
165
- current_balance: parseNumeric(data.data.current_balance)
166
- },
167
- message: data.message || 'Party saved successfully'
168
- };
169
- } catch (e: any) {
170
- return { success: false, message: e.message || 'Database error' };
171
- }
172
- };
173
-
174
- // Mirchi Types API
175
- export const apiGetMirchiTypes = async (): Promise<MirchiType[]> => {
176
- try {
177
- const res = await fetch(`${API_BASE}/mirchi-types`);
178
- if (!res.ok) throw new Error('Failed to load mirchi types');
179
- const data = await res.json();
180
- return data.map((m: any) => ({
181
- ...m,
182
- current_rate: parseNumeric(m.current_rate)
183
- }));
184
- } catch (error) {
185
- console.error('Error fetching mirchi types:', error);
186
- return [];
187
- }
188
- };
189
-
190
- export const apiSaveMirchiType = async (type: MirchiType): Promise<ApiResponse<MirchiType>> => {
191
- try {
192
- const res = await fetch(`${API_BASE}/mirchi-types`, {
193
- method: 'POST',
194
- headers: { 'Content-Type': 'application/json' },
195
- body: JSON.stringify(type),
196
- });
197
- const data = await res.json();
198
- if (!res.ok || !data.success) {
199
- return { success: false, message: data?.message || 'Error saving type' };
200
- }
201
- return {
202
- success: true,
203
- data: {
204
- ...data.data,
205
- current_rate: parseNumeric(data.data.current_rate)
206
- },
207
- message: data.message
208
- };
209
- } catch (e: any) {
210
- return { success: false, message: e.message || 'Error saving type' };
211
- }
212
- };
213
-
214
- // Alias for compatibility
215
- export const getMirchiTypes = apiGetMirchiTypes;
216
- export const saveMirchiType = apiSaveMirchiType;
217
-
218
- // Lots API
219
- export const getActiveLots = async (): Promise<Lot[]> => {
220
- try {
221
- const res = await fetch(`${API_BASE}/lots/active`);
222
- if (!res.ok) throw new Error('Failed to load active lots');
223
- const data = await res.json();
224
- return data.map((l: any) => ({
225
- ...l,
226
- total_quantity: parseNumeric(l.total_quantity),
227
- remaining_quantity: parseNumeric(l.remaining_quantity),
228
- avg_rate: parseNumeric(l.avg_rate)
229
- }));
230
- } catch (error) {
231
- console.error('Error fetching active lots:', error);
232
- return [];
233
- }
234
- };
235
-
236
- // Check if lot number is unique
237
- export const checkLotUnique = async (lotNumber: string): Promise<boolean> => {
238
- try {
239
- const res = await fetch(`${API_BASE}/lots/check-unique`, {
240
- method: 'POST',
241
- headers: { 'Content-Type': 'application/json' },
242
- body: JSON.stringify({ lot_number: lotNumber }),
243
- });
244
- if (!res.ok) throw new Error('Failed to check lot uniqueness');
245
- const data = await res.json();
246
- return !data.exists; // Return true if unique (not exists)
247
- } catch (error) {
248
- console.error('Error checking lot uniqueness:', error);
249
- return false;
250
- }
251
- };
252
-
253
- // Get available lots for a specific mirchi type
254
- export const getAvailableLotsByMirchi = async (mirchiTypeId: string): Promise<Lot[]> => {
255
- try {
256
- const res = await fetch(`${API_BASE}/lots/available/${mirchiTypeId}`);
257
- if (!res.ok) throw new Error('Failed to load available lots');
258
- const data = await res.json();
259
- return data.map((l: any) => ({
260
- ...l,
261
- total_quantity: parseNumeric(l.total_quantity),
262
- remaining_quantity: parseNumeric(l.remaining_quantity),
263
- avg_rate: parseNumeric(l.avg_rate)
264
- }));
265
- } catch (error) {
266
- console.error('Error fetching available lots:', error);
267
- return [];
268
- }
269
- };
270
-
271
- // Transactions API
272
- export const getTransactions = async (): Promise<Transaction[]> => {
273
- try {
274
- const res = await fetch(`${API_BASE}/transactions`);
275
- if (!res.ok) throw new Error('Failed to load transactions');
276
- const data = await res.json();
277
- return data.map(normalizeTransaction);
278
- } catch (error) {
279
- console.error('Error fetching transactions:', error);
280
- return [];
281
- }
282
- };
283
-
284
- export const generateBillNumber = (type: BillType, isReturn: boolean = false): string => {
285
- const prefix = type === BillType.JAWAAK ? (isReturn ? 'JAWAAK-RET' : 'JAWAAK') : (isReturn ? 'AWAAK-RET' : 'AWAAK');
286
- const timestamp = Date.now();
287
- return `${prefix}-${new Date().getFullYear()}-${String(timestamp).slice(-4)}`;
288
- };
289
-
290
- export const saveTransaction = async (transaction: Transaction): Promise<ApiResponse<Transaction>> => {
291
- try {
292
- // Validate Payload
293
- if (!transaction.party_id) return { success: false, message: "Party is required" };
294
- if (!transaction.items || transaction.items.length === 0) return { success: false, message: "At least one item is required" };
295
-
296
- console.log('=== SAVING TRANSACTION ===');
297
- console.log('Transaction ID:', transaction.id);
298
- console.log('Items count:', transaction.items?.length);
299
- console.log('Items data:', JSON.stringify(transaction.items, null, 2));
300
-
301
- const res = await fetch(`${API_BASE}/transactions`, {
302
- method: 'POST',
303
- headers: { 'Content-Type': 'application/json' },
304
- body: JSON.stringify(transaction)
305
- });
306
-
307
- if (!res.ok) {
308
- const error = await res.json();
309
- console.error('Save transaction error:', error);
310
- throw new Error(error.message || 'Failed to save transaction');
311
- }
312
-
313
- const result = await res.json();
314
- console.log('=== SAVE RESPONSE ===');
315
- console.log('Response data:', result.data);
316
- console.log('Response items count:', result.data?.items?.length);
317
- console.log('Response items:', JSON.stringify(result.data?.items, null, 2));
318
-
319
- return {
320
- success: true,
321
- data: normalizeTransaction(result.data),
322
- message: result.message
323
- };
324
- } catch (error: any) {
325
- console.error('Error saving transaction:', error);
326
- return {
327
- success: false,
328
- message: error.message || 'Failed to save transaction'
329
- };
330
- }
331
- };
332
-
333
- export const updateTransactionPayment = async (transactionId: string, amount: number): Promise<ApiResponse<Transaction>> => {
334
- try {
335
- // Validate amount
336
- if (amount <= 0) return { success: false, message: "Amount must be greater than 0" };
337
-
338
- const res = await fetch(`${API_BASE}/transactions/${transactionId}/payment`, {
339
- method: 'PATCH',
340
- headers: { 'Content-Type': 'application/json' },
341
- body: JSON.stringify({ amount }),
342
- });
343
-
344
- const data = await res.json();
345
- if (!res.ok || !data.success) {
346
- return { success: false, message: data?.message || 'Error updating payment' };
347
- }
348
-
349
- return {
350
- success: true,
351
- message: data.message || 'Payment updated successfully'
352
- };
353
- } catch (e: any) {
354
- console.error('Error updating payment:', e);
355
- return { success: false, message: e.message || 'Error updating payment' };
356
- }
357
  };
 
1
+ import {
2
+ Party, MirchiType, Lot, Transaction, PartyType,
3
+ LotStatus, BillType, ApiResponse, PaymentMode
4
+ } from '../types';
5
+
6
+ const API_BASE = 'https://antaram-stagingbackend.hf.space/api';
7
+
8
+ // Helper function to parse numeric values from API
9
+ const parseNumeric = (value: any): number => {
10
+ if (typeof value === 'string') {
11
+ return parseFloat(value);
12
+ }
13
+ return value || 0;
14
+ };
15
+
16
+ // Helper function to parse poti_weights from string to array
17
+ const parsePotiWeights = (weights: any): number[] => {
18
+ if (typeof weights === 'string') {
19
+ try {
20
+ return JSON.parse(weights);
21
+ } catch {
22
+ return [];
23
+ }
24
+ }
25
+ return weights || [];
26
+ };
27
+
28
+ // Helper function to normalize transaction data from API
29
+ const normalizeTransaction = (tx: any): Transaction => {
30
+ // Filter out null items that come from PostgreSQL json_agg when there are no items
31
+ const rawItems = tx.items || [];
32
+ const validItems = Array.isArray(rawItems)
33
+ ? rawItems.filter((item: any) => item && item.id !== null)
34
+ : [];
35
+
36
+ const grossWeightTotal = parseNumeric(tx.gross_weight_total);
37
+ const netWeightTotal = parseNumeric(tx.net_weight_total);
38
+ const subtotal = parseNumeric(tx.subtotal);
39
+ const totalExpenses = parseNumeric(tx.total_expenses);
40
+ const totalAmount = parseNumeric(tx.total_amount);
41
+ const paidAmount = parseNumeric(tx.paid_amount);
42
+ const balanceAmount = parseNumeric(tx.balance_amount);
43
+
44
+ // Normalize expenses and reconstruct other_expenses if backend didn't send it
45
+ let normalizedExpenses;
46
+ if (tx.expenses) {
47
+ const hasOtherExpensesField = Object.prototype.hasOwnProperty.call(tx.expenses, 'other_expenses');
48
+
49
+ const baseExpenses = {
50
+ cess_percent: parseNumeric(tx.expenses.cess_percent),
51
+ cess_amount: parseNumeric(tx.expenses.cess_amount),
52
+ adat_percent: parseNumeric(tx.expenses.adat_percent),
53
+ adat_amount: parseNumeric(tx.expenses.adat_amount),
54
+ poti_rate: parseNumeric(tx.expenses.poti_rate),
55
+ poti_amount: parseNumeric(tx.expenses.poti_amount),
56
+ hamali_per_poti: parseNumeric(tx.expenses.hamali_per_poti),
57
+ hamali_amount: parseNumeric(tx.expenses.hamali_amount),
58
+ packaging_hamali_per_poti: parseNumeric(tx.expenses.packaging_hamali_per_poti),
59
+ packaging_hamali_amount: parseNumeric(tx.expenses.packaging_hamali_amount),
60
+ gaadi_bharni: parseNumeric(tx.expenses.gaadi_bharni),
61
+ };
62
+
63
+ let otherExpensesValue = hasOtherExpensesField
64
+ ? parseNumeric(tx.expenses.other_expenses)
65
+ : 0;
66
+
67
+ // If backend doesn't provide other_expenses but total_expenses is known,
68
+ // infer other_expenses as the remaining part after subtracting known components.
69
+ if (!hasOtherExpensesField && totalExpenses) {
70
+ const knownComponents =
71
+ baseExpenses.poti_amount +
72
+ baseExpenses.cess_amount +
73
+ baseExpenses.adat_amount +
74
+ baseExpenses.hamali_amount +
75
+ baseExpenses.packaging_hamali_amount +
76
+ baseExpenses.gaadi_bharni;
77
+
78
+ const diff = totalExpenses - knownComponents;
79
+ if (diff > 0) {
80
+ otherExpensesValue = diff;
81
+ }
82
+ }
83
+
84
+ normalizedExpenses = {
85
+ ...baseExpenses,
86
+ other_expenses: otherExpensesValue,
87
+ };
88
+ } else {
89
+ normalizedExpenses = {
90
+ cess_percent: 0,
91
+ cess_amount: 0,
92
+ adat_percent: 0,
93
+ adat_amount: 0,
94
+ poti_rate: 0,
95
+ poti_amount: 0,
96
+ hamali_per_poti: 0,
97
+ hamali_amount: 0,
98
+ packaging_hamali_per_poti: 0,
99
+ packaging_hamali_amount: 0,
100
+ gaadi_bharni: 0,
101
+ other_expenses: 0,
102
+ };
103
+ }
104
+
105
+ return {
106
+ ...tx,
107
+ gross_weight_total: grossWeightTotal,
108
+ net_weight_total: netWeightTotal,
109
+ subtotal,
110
+ total_expenses: totalExpenses,
111
+ total_amount: totalAmount,
112
+ paid_amount: paidAmount,
113
+ balance_amount: balanceAmount,
114
+ items: validItems.map((item: any) => ({
115
+ ...item,
116
+ poti_weights: parsePotiWeights(item.poti_weights),
117
+ gross_weight: parseNumeric(item.gross_weight),
118
+ poti_count: parseNumeric(item.poti_count),
119
+ total_potya: parseNumeric(item.total_potya),
120
+ net_weight: parseNumeric(item.net_weight),
121
+ rate_per_kg: parseNumeric(item.rate_per_kg),
122
+ item_total: parseNumeric(item.item_total),
123
+ })),
124
+ expenses: normalizedExpenses,
125
+ payments: (tx.payments || []).map((p: any) => ({
126
+ ...p,
127
+ amount: parseNumeric(p.amount),
128
+ })),
129
+ };
130
+ };
131
+
132
+ // Parties API
133
+ export const getParties = async (): Promise<Party[]> => {
134
+ try {
135
+ const res = await fetch(`${API_BASE}/parties`);
136
+ if (!res.ok) throw new Error('Failed to load parties');
137
+ const data = await res.json();
138
+ return data.map((p: any) => ({
139
+ ...p,
140
+ current_balance: parseNumeric(p.current_balance)
141
+ }));
142
+ } catch (error) {
143
+ console.error('Error fetching parties:', error);
144
+ return [];
145
+ }
146
+ };
147
+
148
+ export const saveParty = async (party: Party): Promise<ApiResponse<Party>> => {
149
+ try {
150
+ const res = await fetch(`${API_BASE}/parties`, {
151
+ method: 'POST',
152
+ headers: { 'Content-Type': 'application/json' },
153
+ body: JSON.stringify(party),
154
+ });
155
+
156
+ const data = await res.json();
157
+ if (!res.ok || !data.success) {
158
+ return { success: false, message: data?.message || 'Error saving party' };
159
+ }
160
+
161
+ return {
162
+ success: true,
163
+ data: {
164
+ ...data.data,
165
+ current_balance: parseNumeric(data.data.current_balance)
166
+ },
167
+ message: data.message || 'Party saved successfully'
168
+ };
169
+ } catch (e: any) {
170
+ return { success: false, message: e.message || 'Database error' };
171
+ }
172
+ };
173
+
174
+ // Mirchi Types API
175
+ export const apiGetMirchiTypes = async (): Promise<MirchiType[]> => {
176
+ try {
177
+ const res = await fetch(`${API_BASE}/mirchi-types`);
178
+ if (!res.ok) throw new Error('Failed to load mirchi types');
179
+ const data = await res.json();
180
+ return data.map((m: any) => ({
181
+ ...m,
182
+ current_rate: parseNumeric(m.current_rate)
183
+ }));
184
+ } catch (error) {
185
+ console.error('Error fetching mirchi types:', error);
186
+ return [];
187
+ }
188
+ };
189
+
190
+ export const apiSaveMirchiType = async (type: MirchiType): Promise<ApiResponse<MirchiType>> => {
191
+ try {
192
+ const res = await fetch(`${API_BASE}/mirchi-types`, {
193
+ method: 'POST',
194
+ headers: { 'Content-Type': 'application/json' },
195
+ body: JSON.stringify(type),
196
+ });
197
+ const data = await res.json();
198
+ if (!res.ok || !data.success) {
199
+ return { success: false, message: data?.message || 'Error saving type' };
200
+ }
201
+ return {
202
+ success: true,
203
+ data: {
204
+ ...data.data,
205
+ current_rate: parseNumeric(data.data.current_rate)
206
+ },
207
+ message: data.message
208
+ };
209
+ } catch (e: any) {
210
+ return { success: false, message: e.message || 'Error saving type' };
211
+ }
212
+ };
213
+
214
+ // Alias for compatibility
215
+ export const getMirchiTypes = apiGetMirchiTypes;
216
+ export const saveMirchiType = apiSaveMirchiType;
217
+
218
+ // Lots API
219
+ export const getActiveLots = async (): Promise<Lot[]> => {
220
+ try {
221
+ const res = await fetch(`${API_BASE}/lots/active`);
222
+ if (!res.ok) throw new Error('Failed to load active lots');
223
+ const data = await res.json();
224
+ return data.map((l: any) => ({
225
+ ...l,
226
+ total_quantity: parseNumeric(l.total_quantity),
227
+ remaining_quantity: parseNumeric(l.remaining_quantity),
228
+ avg_rate: parseNumeric(l.avg_rate)
229
+ }));
230
+ } catch (error) {
231
+ console.error('Error fetching active lots:', error);
232
+ return [];
233
+ }
234
+ };
235
+
236
+ // Check if lot number is unique
237
+ export const checkLotUnique = async (lotNumber: string): Promise<boolean> => {
238
+ try {
239
+ const res = await fetch(`${API_BASE}/lots/check-unique`, {
240
+ method: 'POST',
241
+ headers: { 'Content-Type': 'application/json' },
242
+ body: JSON.stringify({ lot_number: lotNumber }),
243
+ });
244
+ if (!res.ok) throw new Error('Failed to check lot uniqueness');
245
+ const data = await res.json();
246
+ return !data.exists; // Return true if unique (not exists)
247
+ } catch (error) {
248
+ console.error('Error checking lot uniqueness:', error);
249
+ return false;
250
+ }
251
+ };
252
+
253
+ // Get available lots for a specific mirchi type
254
+ export const getAvailableLotsByMirchi = async (mirchiTypeId: string): Promise<Lot[]> => {
255
+ try {
256
+ const res = await fetch(`${API_BASE}/lots/available/${mirchiTypeId}`);
257
+ if (!res.ok) throw new Error('Failed to load available lots');
258
+ const data = await res.json();
259
+ return data.map((l: any) => ({
260
+ ...l,
261
+ total_quantity: parseNumeric(l.total_quantity),
262
+ remaining_quantity: parseNumeric(l.remaining_quantity),
263
+ avg_rate: parseNumeric(l.avg_rate)
264
+ }));
265
+ } catch (error) {
266
+ console.error('Error fetching available lots:', error);
267
+ return [];
268
+ }
269
+ };
270
+
271
+ // Transactions API
272
+ export const getTransactions = async (): Promise<Transaction[]> => {
273
+ try {
274
+ const res = await fetch(`${API_BASE}/transactions`);
275
+ if (!res.ok) throw new Error('Failed to load transactions');
276
+ const data = await res.json();
277
+ return data.map(normalizeTransaction);
278
+ } catch (error) {
279
+ console.error('Error fetching transactions:', error);
280
+ return [];
281
+ }
282
+ };
283
+
284
+ export const generateBillNumber = (type: BillType, isReturn: boolean = false): string => {
285
+ const prefix = type === BillType.JAWAAK ? (isReturn ? 'JAWAAK-RET' : 'JAWAAK') : (isReturn ? 'AWAAK-RET' : 'AWAAK');
286
+ const timestamp = Date.now();
287
+ return `${prefix}-${new Date().getFullYear()}-${String(timestamp).slice(-4)}`;
288
+ };
289
+
290
+ export const saveTransaction = async (transaction: Transaction): Promise<ApiResponse<Transaction>> => {
291
+ try {
292
+ // Validate Payload
293
+ if (!transaction.party_id) return { success: false, message: "Party is required" };
294
+ if (!transaction.items || transaction.items.length === 0) return { success: false, message: "At least one item is required" };
295
+
296
+ console.log('=== SAVING TRANSACTION ===');
297
+ console.log('Transaction ID:', transaction.id);
298
+ console.log('Items count:', transaction.items?.length);
299
+ console.log('Items data:', JSON.stringify(transaction.items, null, 2));
300
+
301
+ const res = await fetch(`${API_BASE}/transactions`, {
302
+ method: 'POST',
303
+ headers: { 'Content-Type': 'application/json' },
304
+ body: JSON.stringify(transaction)
305
+ });
306
+
307
+ if (!res.ok) {
308
+ const error = await res.json();
309
+ console.error('Save transaction error:', error);
310
+ throw new Error(error.message || 'Failed to save transaction');
311
+ }
312
+
313
+ const result = await res.json();
314
+ console.log('=== SAVE RESPONSE ===');
315
+ console.log('Response data:', result.data);
316
+ console.log('Response items count:', result.data?.items?.length);
317
+ console.log('Response items:', JSON.stringify(result.data?.items, null, 2));
318
+
319
+ return {
320
+ success: true,
321
+ data: normalizeTransaction(result.data),
322
+ message: result.message
323
+ };
324
+ } catch (error: any) {
325
+ console.error('Error saving transaction:', error);
326
+ return {
327
+ success: false,
328
+ message: error.message || 'Failed to save transaction'
329
+ };
330
+ }
331
+ };
332
+
333
+ export const updateTransactionPayment = async (transactionId: string, amount: number): Promise<ApiResponse<Transaction>> => {
334
+ try {
335
+ // Validate amount
336
+ if (amount <= 0) return { success: false, message: "Amount must be greater than 0" };
337
+
338
+ const res = await fetch(`${API_BASE}/transactions/${transactionId}/payment`, {
339
+ method: 'PATCH',
340
+ headers: { 'Content-Type': 'application/json' },
341
+ body: JSON.stringify({ amount }),
342
+ });
343
+
344
+ const data = await res.json();
345
+ if (!res.ok || !data.success) {
346
+ return { success: false, message: data?.message || 'Error updating payment' };
347
+ }
348
+
349
+ return {
350
+ success: true,
351
+ message: data.message || 'Payment updated successfully'
352
+ };
353
+ } catch (e: any) {
354
+ console.error('Error updating payment:', e);
355
+ return { success: false, message: e.message || 'Error updating payment' };
356
+ }
357
  };
types.ts CHANGED
@@ -65,6 +65,9 @@ export interface Payment {
65
  mode: PaymentMode;
66
  amount: number;
67
  reference?: string;
 
 
 
68
  }
69
 
70
  export interface Expenses {
 
65
  mode: PaymentMode;
66
  amount: number;
67
  reference?: string;
68
+ payment_date?: string;
69
+ created_at?: string;
70
+ date?: string;
71
  }
72
 
73
  export interface Expenses {
utils/LedgerPdfGenerator.ts CHANGED
@@ -1,240 +1,327 @@
1
- import jsPDF from 'jspdf';
2
- import autoTable from 'jspdf-autotable';
3
- import { Party, Transaction } from '../types';
4
-
5
- // --- CONFIGURATION ---
6
- const BG_COLOR: [number, number, number] = [240, 234, 214]; // Beige
7
- const BORDER_COLOR: [number, number, number] = [0, 0, 0]; // Black
8
-
9
- // Helper: Currency Format
10
- const FORMAT_CURRENCY = (amount: number) => {
11
- return new Intl.NumberFormat('en-IN', {
12
- minimumFractionDigits: 2,
13
- maximumFractionDigits: 2
14
- }).format(amount);
15
- };
16
-
17
- // Helper: Date Format
18
- const FORMAT_DATE = (dateStr: string) => {
19
- if (!dateStr) return "";
20
- const dateObj = new Date(dateStr);
21
- return `${dateObj.getDate().toString().padStart(2, '0')}/${(dateObj.getMonth() + 1).toString().padStart(2, '0')}/${dateObj.getFullYear().toString().slice(-2)}`;
22
- };
23
-
24
- export const generateLedgerPDF = (party: Party, transactions: Transaction[]) => {
25
- const doc = new jsPDF();
26
-
27
- // --- 1. PAGE CONFIGURATION ---
28
- const PAGE_WIDTH = 210;
29
- const PAGE_HEIGHT = 297;
30
- const MARGIN = 14;
31
- const CONTENT_WIDTH = PAGE_WIDTH - (MARGIN * 2);
32
-
33
- // --- 2. SETUP & BACKGROUND ---
34
- doc.setFillColor(...BG_COLOR);
35
- doc.rect(0, 0, PAGE_WIDTH, PAGE_HEIGHT, 'F');
36
-
37
- // --- 3. DATA PREPARATION ---
38
- const sortedTxs = [...transactions].sort((a, b) =>
39
- new Date(a.bill_date).getTime() - new Date(b.bill_date).getTime()
40
- );
41
-
42
- let runningBalance = 0;
43
-
44
- const tableRows = sortedTxs.flatMap(tx => {
45
- const rows = [];
46
- const dateStr = FORMAT_DATE(tx.bill_date);
47
-
48
- // --- DETERMINE TYPE (AWAAK vs JAWAAK) ---
49
- // AWAAK = Purchase (We Owe - Credit)
50
- // JAWAAK = Sales (They Owe - Debit)
51
- const typeStr = String(tx.bill_type).toUpperCase();
52
- const numStr = tx.bill_number ? tx.bill_number.toUpperCase() : "";
53
-
54
- const isAwaak = typeStr === 'AWAAK' || numStr.includes('AWAAK');
55
-
56
- // --- A. BILL ROW ---
57
- // 1. Particulars Construction
58
- let itemDetails = "";
59
- if (tx.items && tx.items.length > 0) {
60
- tx.items.forEach(item => {
61
- let wStr = "";
62
- if (Array.isArray(item.poti_weights)) wStr = item.poti_weights.join(",");
63
- else if (item.poti_weights) wStr = String(item.poti_weights);
64
-
65
- const prefix = itemDetails ? "\n" : "";
66
- itemDetails += `${prefix}${item.mirchi_name}`;
67
- if (wStr) itemDetails += ` (${wStr})`;
68
- });
69
- }
70
-
71
- let billParticulars = `No: ${tx.bill_number}`;
72
- if (tx.is_return) billParticulars += " (RETURN)";
73
- if (itemDetails) billParticulars += `\n${itemDetails}`;
74
-
75
- let totalPoti = 0;
76
- tx.items?.forEach(i => totalPoti += Number(i.poti_count) || 0);
77
-
78
- // 2. FINANCIAL LOGIC (THE FIX)
79
- let billDebit = 0;
80
- let billCredit = 0;
81
-
82
- if (isAwaak) {
83
- // AWAAK (Purchase):
84
- // - Normal Bill: CREDIT (Liability increases)
85
- // - Return Bill: DEBIT (Liability decreases)
86
- if (tx.is_return) billDebit = tx.total_amount;
87
- else billCredit = tx.total_amount;
88
- } else {
89
- // JAWAAK (Sales):
90
- // - Normal Bill: DEBIT (Asset increases - They owe us)
91
- // - Return Bill: CREDIT (Asset decreases)
92
- if (tx.is_return) billCredit = tx.total_amount;
93
- else billDebit = tx.total_amount;
94
- }
95
-
96
- // Running Balance = Old + Debit - Credit
97
- runningBalance = runningBalance + billDebit - billCredit;
98
-
99
- // Push Bill Row
100
- rows.push({
101
- date: dateStr,
102
- particulars: billParticulars,
103
- poti: totalPoti > 0 ? totalPoti.toString() : "-",
104
- credit: billCredit > 0 ? FORMAT_CURRENCY(billCredit) : "-",
105
- debit: billDebit > 0 ? FORMAT_CURRENCY(billDebit) : "-",
106
- balance: `${FORMAT_CURRENCY(Math.abs(runningBalance))} ${runningBalance >= 0 ? 'Dr' : 'Cr'}`,
107
- isMainRow: true
108
- });
109
-
110
- // --- B. PAYMENT ROWS ---
111
- if (tx.payments && tx.payments.length > 0) {
112
- const validPayments = tx.payments.filter(p => p.mode.toLowerCase() !== 'due');
113
-
114
- validPayments.forEach(p => {
115
- let payDebit = 0;
116
- let payCredit = 0;
117
-
118
- if (isAwaak) {
119
- // AWAAK (Purchase) Payment:
120
- // - We Pay Money: DEBIT (Liability decreases)
121
- // - Refund (Return): CREDIT
122
- if (tx.is_return) payCredit = p.amount;
123
- else payDebit = p.amount;
124
- } else {
125
- // JAWAAK (Sales) Payment:
126
- // - They Pay Money: CREDIT (Asset decreases - Debt paid off)
127
- // - Refund (Return): DEBIT
128
- if (tx.is_return) payDebit = p.amount;
129
- else payCredit = p.amount;
130
- }
131
-
132
- runningBalance = runningBalance + payDebit - payCredit;
133
-
134
- // Description
135
- const modeStr = p.mode.charAt(0).toUpperCase() + p.mode.slice(1);
136
- let payDesc = ` [${modeStr}]`;
137
- if (p.reference) payDesc += ` ${p.reference}`;
138
-
139
- rows.push({
140
- date: dateStr,
141
- particulars: payDesc,
142
- poti: "-",
143
- credit: payCredit > 0 ? FORMAT_CURRENCY(payCredit) : "-",
144
- debit: payDebit > 0 ? FORMAT_CURRENCY(payDebit) : "-",
145
- balance: `${FORMAT_CURRENCY(Math.abs(runningBalance))} ${runningBalance >= 0 ? 'Dr' : 'Cr'}`,
146
- isMainRow: false
147
- });
148
- });
149
- }
150
- return rows;
151
- });
152
-
153
- // --- 4. GENERATE TABLE ---
154
- autoTable(doc, {
155
- startY: 35,
156
- tableWidth: CONTENT_WIDTH,
157
- margin: { left: MARGIN, right: MARGIN },
158
-
159
- head: [[
160
- 'Date',
161
- 'Particulars',
162
- 'Poti',
163
- 'Credit\n(Jama)',
164
- 'Debit\n(Nave)',
165
- 'Balance'
166
- ]],
167
-
168
- body: tableRows.map(r => [r.date, r.particulars, r.poti, r.credit, r.debit, r.balance]),
169
-
170
- theme: 'grid',
171
-
172
- styles: {
173
- fillColor: false,
174
- textColor: 0,
175
- lineColor: 0,
176
- lineWidth: 0.1,
177
- font: 'helvetica',
178
- fontSize: 9,
179
- valign: 'top',
180
- cellPadding: 3,
181
- overflow: 'linebreak'
182
- },
183
-
184
- headStyles: {
185
- fillColor: false,
186
- textColor: 0,
187
- fontStyle: 'bold',
188
- halign: 'center',
189
- valign: 'middle',
190
- lineWidth: 0.1,
191
- },
192
-
193
- columnStyles: {
194
- 0: { cellWidth: 20, halign: 'center' }, // Date
195
- 1: { cellWidth: 72, halign: 'left' }, // Particulars
196
- 2: { cellWidth: 12, halign: 'center' }, // Poti
197
- 3: { cellWidth: 26, halign: 'right' }, // Credit
198
- 4: { cellWidth: 26, halign: 'right' }, // Debit
199
- 5: { cellWidth: 26, halign: 'right', fontStyle: 'bold' } // Balance
200
- },
201
-
202
- didParseCell: (data) => {
203
- const rowIdx = data.row.index;
204
- const rowData = tableRows[rowIdx];
205
- if (data.section === 'body' && data.column.index === 1 && rowData?.isMainRow) {
206
- data.cell.styles.fontStyle = 'bold';
207
- }
208
- },
209
-
210
- didDrawPage: (data) => {
211
- // Border
212
- doc.setDrawColor(...BORDER_COLOR);
213
- doc.setLineWidth(0.4);
214
- doc.rect(MARGIN, MARGIN, CONTENT_WIDTH, PAGE_HEIGHT - (MARGIN * 2));
215
-
216
- // Header
217
- if (data.pageNumber === 1) {
218
- const startX = MARGIN + 4;
219
- const startY = 25;
220
-
221
- doc.setFontSize(14);
222
- doc.setFont("helvetica", "bold");
223
- doc.text("Name of A/c :", startX, startY);
224
-
225
- const labelWidth = doc.getTextWidth("Name of A/c : ");
226
- doc.text(party.name, startX + labelWidth, startY);
227
-
228
- if (party.city || party.phone) {
229
- doc.setFontSize(9);
230
- doc.setFont("helvetica", "normal");
231
- const subText = [party.city, party.phone].filter(Boolean).join(" - ");
232
- doc.text(subText, startX, startY + 6);
233
- }
234
- }
235
- }
236
- });
237
-
238
- const cleanName = party.name.replace(/[^a-zA-Z0-9]/g, '_');
239
- doc.save(`${cleanName}_Ledger.pdf`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  };
 
1
+ import jsPDF from 'jspdf';
2
+ import autoTable from 'jspdf-autotable';
3
+ import { Party, Transaction } from '../types';
4
+
5
+ // --- CONFIGURATION ---
6
+ const BG_COLOR: [number, number, number] = [240, 234, 214]; // Beige
7
+ const BORDER_COLOR: [number, number, number] = [0, 0, 0]; // Black
8
+
9
+ // Helper: Currency Format
10
+ const FORMAT_CURRENCY = (amount: number) => {
11
+ return new Intl.NumberFormat('en-IN', {
12
+ minimumFractionDigits: 2,
13
+ maximumFractionDigits: 2
14
+ }).format(amount);
15
+ };
16
+
17
+ // Helper: Date Format
18
+ const FORMAT_DATE = (dateStr: string) => {
19
+ if (!dateStr) return "";
20
+ const dateObj = PARSE_DATE(dateStr) || new Date(dateStr);
21
+ return `${dateObj.getDate().toString().padStart(2, '0')}/${(dateObj.getMonth() + 1).toString().padStart(2, '0')}/${dateObj.getFullYear().toString().slice(-2)}`;
22
+ };
23
+
24
+ const PARSE_DATE = (value?: string): Date | undefined => {
25
+ if (!value) return undefined;
26
+
27
+ const trimmed = String(value).trim();
28
+ if (!trimmed) return undefined;
29
+
30
+ const iso = trimmed.match(/^\d{4}-\d{2}-\d{2}(?:[T\s].*)?$/);
31
+ if (iso) {
32
+ const d = new Date(trimmed);
33
+ return isNaN(d.getTime()) ? undefined : d;
34
+ }
35
+
36
+ const slash = trimmed.match(/^\d{1,2}\/\d{1,2}\/(\d{2}|\d{4})$/);
37
+ if (slash) {
38
+ const [aStr, bStr, yyStr] = trimmed.split('/');
39
+ const a = Number(aStr);
40
+ const b = Number(bStr);
41
+ const yy = Number(yyStr);
42
+ const year = yyStr.length === 2 ? 2000 + yy : yy;
43
+ if (!a || !b || !year) return undefined;
44
+
45
+ // Prefer en-IN DD/MM/YYYY, but support MM/DD/YYYY when clearly indicated.
46
+ // - If first part > 12 -> it's DD/MM
47
+ // - If second part > 12 -> it's MM/DD
48
+ // - If both <= 12 -> default DD/MM
49
+ const day = a > 12 ? a : b > 12 ? b : a;
50
+ const month = a > 12 ? b : b > 12 ? a : b;
51
+
52
+ const d = new Date(year, month - 1, day);
53
+ return isNaN(d.getTime()) ? undefined : d;
54
+ }
55
+
56
+ const d = new Date(trimmed);
57
+ return isNaN(d.getTime()) ? undefined : d;
58
+ };
59
+
60
+ const TO_ISO_DATE = (value?: string): string | undefined => {
61
+ const d = PARSE_DATE(value);
62
+ return d ? d.toISOString() : undefined;
63
+ };
64
+
65
+ const EXTRACT_DATE_FROM_TEXT = (text?: string): string | undefined => {
66
+ if (!text) return undefined;
67
+
68
+ const iso = text.match(/\b\d{4}-\d{2}-\d{2}\b/);
69
+ if (iso?.[0]) return TO_ISO_DATE(iso[0]);
70
+
71
+ const mdyOrDmy = text.match(/\b\d{1,2}\/\d{1,2}\/\d{4}\b/);
72
+ if (!mdyOrDmy?.[0]) return undefined;
73
+
74
+ return TO_ISO_DATE(mdyOrDmy[0]);
75
+ };
76
+
77
+ export const generateLedgerPDF = (party: Party, transactions: Transaction[]) => {
78
+ const doc = new jsPDF();
79
+
80
+ // --- 1. PAGE CONFIGURATION ---
81
+ const PAGE_WIDTH = 210;
82
+ const PAGE_HEIGHT = 297;
83
+ const MARGIN = 14;
84
+ const CONTENT_WIDTH = PAGE_WIDTH - (MARGIN * 2);
85
+
86
+ // --- 2. SETUP & BACKGROUND ---
87
+ doc.setFillColor(...BG_COLOR);
88
+ doc.rect(0, 0, PAGE_WIDTH, PAGE_HEIGHT, 'F');
89
+
90
+ // --- 3. DATA PREPARATION ---
91
+ const ledgerEvents: Array<
92
+ | { kind: 'bill'; date: string; tx: Transaction }
93
+ | { kind: 'payment'; date: string; tx: Transaction; payment: any }
94
+ > = [];
95
+
96
+ for (const tx of transactions) {
97
+ const billDateIso = TO_ISO_DATE(tx.bill_date) || tx.bill_date;
98
+ ledgerEvents.push({ kind: 'bill', date: billDateIso, tx });
99
+
100
+ const payments = tx.payments || [];
101
+ const validPayments = payments.filter((p: any) => String(p.mode).toLowerCase() !== 'due');
102
+
103
+ for (const p of validPayments) {
104
+ const paymentDateRaw =
105
+ (p as any).payment_date ||
106
+ (p as any).created_at ||
107
+ (p as any).date ||
108
+ EXTRACT_DATE_FROM_TEXT((p as any).reference) ||
109
+ tx.bill_date;
110
+
111
+ const paymentDateIso = TO_ISO_DATE(paymentDateRaw) || TO_ISO_DATE(tx.bill_date) || tx.bill_date;
112
+
113
+ const billTime = (PARSE_DATE(billDateIso) || new Date(billDateIso)).getTime();
114
+ const payTime = (PARSE_DATE(paymentDateIso) || new Date(paymentDateIso)).getTime();
115
+ const safePaymentDate = payTime < billTime ? billDateIso : paymentDateIso;
116
+
117
+ ledgerEvents.push({ kind: 'payment', date: safePaymentDate, tx, payment: p });
118
+ }
119
+
120
+ const recordedPaid = validPayments.reduce((sum: number, p: any) => sum + (Number(p.amount) || 0), 0);
121
+ const extraPaid = (Number(tx.paid_amount) || 0) - recordedPaid;
122
+
123
+ if (extraPaid > 0) {
124
+ const paymentDateRaw =
125
+ (tx as any).updated_at ||
126
+ (tx as any).created_at ||
127
+ tx.bill_date;
128
+
129
+ const paymentDateIso = TO_ISO_DATE(paymentDateRaw) || billDateIso;
130
+ const billTime = (PARSE_DATE(billDateIso) || new Date(billDateIso)).getTime();
131
+ const payTime = (PARSE_DATE(paymentDateIso) || new Date(paymentDateIso)).getTime();
132
+ const safePaymentDate = payTime < billTime ? billDateIso : paymentDateIso;
133
+
134
+ ledgerEvents.push({
135
+ kind: 'payment',
136
+ date: safePaymentDate,
137
+ tx,
138
+ payment: { mode: 'payment', amount: extraPaid, reference: undefined, __synthetic: true },
139
+ });
140
+ }
141
+ }
142
+
143
+ ledgerEvents.sort((a, b) => {
144
+ const ta = new Date(a.date).getTime();
145
+ const tb = new Date(b.date).getTime();
146
+ if (ta !== tb) return ta - tb;
147
+ if (a.kind !== b.kind) return a.kind === 'bill' ? -1 : 1;
148
+ const na = a.tx.bill_number || '';
149
+ const nb = b.tx.bill_number || '';
150
+ return na.localeCompare(nb);
151
+ });
152
+
153
+ let runningBalance = 0;
154
+
155
+ const tableRows = ledgerEvents.map(ev => {
156
+ const tx = ev.tx;
157
+
158
+ const typeStr = String(tx.bill_type).toUpperCase();
159
+ const numStr = tx.bill_number ? tx.bill_number.toUpperCase() : "";
160
+
161
+ const isJawaakFromNumber = numStr.startsWith('JAWAAK');
162
+ const isAwaakFromNumber = numStr.startsWith('AWAAK');
163
+ const isAwaak = isAwaakFromNumber ? true : isJawaakFromNumber ? false : typeStr === 'AWAAK';
164
+
165
+ if (ev.kind === 'bill') {
166
+ let itemDetails = "";
167
+ if (tx.items && tx.items.length > 0) {
168
+ tx.items.forEach(item => {
169
+ let wStr = "";
170
+ if (Array.isArray(item.poti_weights)) wStr = item.poti_weights.join(",");
171
+ else if (item.poti_weights) wStr = String(item.poti_weights);
172
+
173
+ const prefix = itemDetails ? "\n" : "";
174
+ itemDetails += `${prefix}${item.mirchi_name}`;
175
+ if (wStr) itemDetails += ` (${wStr})`;
176
+ });
177
+ }
178
+
179
+ let billParticulars = `No: ${tx.bill_number}`;
180
+ if (tx.is_return) billParticulars += " (RETURN)";
181
+ if (itemDetails) billParticulars += `\n${itemDetails}`;
182
+
183
+ let totalPoti = 0;
184
+ tx.items?.forEach(i => totalPoti += Number(i.poti_count) || 0);
185
+
186
+ let billDebit = 0;
187
+ let billCredit = 0;
188
+
189
+ if (isAwaak) {
190
+ if (tx.is_return) billDebit = tx.total_amount;
191
+ else billCredit = tx.total_amount;
192
+ } else {
193
+ if (tx.is_return) billCredit = tx.total_amount;
194
+ else billDebit = tx.total_amount;
195
+ }
196
+
197
+ runningBalance = runningBalance + billDebit - billCredit;
198
+
199
+ return {
200
+ date: FORMAT_DATE(ev.date),
201
+ particulars: billParticulars,
202
+ poti: totalPoti > 0 ? totalPoti.toString() : "-",
203
+ credit: billCredit > 0 ? FORMAT_CURRENCY(billCredit) : "-",
204
+ debit: billDebit > 0 ? FORMAT_CURRENCY(billDebit) : "-",
205
+ balance: `${FORMAT_CURRENCY(Math.abs(runningBalance))} ${runningBalance >= 0 ? 'Dr' : 'Cr'}`,
206
+ isMainRow: true
207
+ };
208
+ }
209
+
210
+ const p = ev.payment;
211
+ const amount = Number(p.amount) || 0;
212
+ let payDebit = 0;
213
+ let payCredit = 0;
214
+
215
+ if (isAwaak) {
216
+ if (tx.is_return) payCredit = amount;
217
+ else payDebit = amount;
218
+ } else {
219
+ if (tx.is_return) payDebit = amount;
220
+ else payCredit = amount;
221
+ }
222
+
223
+ runningBalance = runningBalance + payDebit - payCredit;
224
+
225
+ const modeStr = String(p.mode || '').charAt(0).toUpperCase() + String(p.mode || '').slice(1);
226
+ let payDesc = (p as any).__synthetic ? ` [Payment]` : ` [${modeStr}]`;
227
+ if (p.reference) payDesc += ` ${p.reference}`;
228
+
229
+ return {
230
+ date: FORMAT_DATE(ev.date),
231
+ particulars: payDesc,
232
+ poti: "-",
233
+ credit: payCredit > 0 ? FORMAT_CURRENCY(payCredit) : "-",
234
+ debit: payDebit > 0 ? FORMAT_CURRENCY(payDebit) : "-",
235
+ balance: `${FORMAT_CURRENCY(Math.abs(runningBalance))} ${runningBalance >= 0 ? 'Dr' : 'Cr'}`,
236
+ isMainRow: false
237
+ };
238
+ });
239
+
240
+ // --- 4. GENERATE TABLE ---
241
+ autoTable(doc, {
242
+ startY: 35,
243
+ tableWidth: CONTENT_WIDTH,
244
+ margin: { left: MARGIN, right: MARGIN },
245
+
246
+ head: [[
247
+ 'Date',
248
+ 'Particulars',
249
+ 'Poti',
250
+ 'Credit\n(Jama)',
251
+ 'Debit\n(Nave)',
252
+ 'Balance'
253
+ ]],
254
+
255
+ body: tableRows.map(r => [r.date, r.particulars, r.poti, r.credit, r.debit, r.balance]),
256
+
257
+ theme: 'grid',
258
+
259
+ styles: {
260
+ fillColor: false,
261
+ textColor: 0,
262
+ lineColor: 0,
263
+ lineWidth: 0.1,
264
+ font: 'helvetica',
265
+ fontSize: 9,
266
+ valign: 'top',
267
+ cellPadding: 3,
268
+ overflow: 'linebreak'
269
+ },
270
+
271
+ headStyles: {
272
+ fillColor: false,
273
+ textColor: 0,
274
+ fontStyle: 'bold',
275
+ halign: 'center',
276
+ valign: 'middle',
277
+ lineWidth: 0.1,
278
+ },
279
+
280
+ columnStyles: {
281
+ 0: { cellWidth: 20, halign: 'center' }, // Date
282
+ 1: { cellWidth: 72, halign: 'left' }, // Particulars
283
+ 2: { cellWidth: 12, halign: 'center' }, // Poti
284
+ 3: { cellWidth: 26, halign: 'right' }, // Credit
285
+ 4: { cellWidth: 26, halign: 'right' }, // Debit
286
+ 5: { cellWidth: 26, halign: 'right', fontStyle: 'bold' } // Balance
287
+ },
288
+
289
+ didParseCell: (data) => {
290
+ const rowIdx = data.row.index;
291
+ const rowData = tableRows[rowIdx];
292
+ if (data.section === 'body' && data.column.index === 1 && rowData?.isMainRow) {
293
+ data.cell.styles.fontStyle = 'bold';
294
+ }
295
+ },
296
+
297
+ didDrawPage: (data) => {
298
+ // Border
299
+ doc.setDrawColor(...BORDER_COLOR);
300
+ doc.setLineWidth(0.4);
301
+ doc.rect(MARGIN, MARGIN, CONTENT_WIDTH, PAGE_HEIGHT - (MARGIN * 2));
302
+
303
+ // Header
304
+ if (data.pageNumber === 1) {
305
+ const startX = MARGIN + 4;
306
+ const startY = 25;
307
+
308
+ doc.setFontSize(14);
309
+ doc.setFont("helvetica", "bold");
310
+ doc.text("Name of A/c :", startX, startY);
311
+
312
+ const labelWidth = doc.getTextWidth("Name of A/c : ");
313
+ doc.text(party.name, startX + labelWidth, startY);
314
+
315
+ if (party.city || party.phone) {
316
+ doc.setFontSize(9);
317
+ doc.setFont("helvetica", "normal");
318
+ const subText = [party.city, party.phone].filter(Boolean).join(" - ");
319
+ doc.text(subText, startX, startY + 6);
320
+ }
321
+ }
322
+ }
323
+ });
324
+
325
+ const cleanName = party.name.replace(/[^a-zA-Z0-9]/g, '_');
326
+ doc.save(`${cleanName}_Ledger.pdf`);
327
  };
utils/exportToExcel.ts CHANGED
@@ -306,17 +306,98 @@ export const exportLotDetailAnalysis = (
306
  XLSX.writeFile(wb, `Lot_${safeName}_Analysis.xlsx`);
307
  };
308
 
309
-
310
- // ==========================================
311
  // 5. OTHER EXPORT FUNCTIONS (Parties/Ledger)
312
  // ==========================================
313
 
314
  export const exportPartyLedger = (party: Party, transactions: Transaction[], dateRange?: { from: string; to: string }) => {
315
- // Filter & Sort
316
- const filtered = (dateRange ? transactions.filter(t => {
317
- const d = new Date(t.bill_date);
318
- return d >= new Date(dateRange.from) && d <= new Date(dateRange.to);
319
- }) : [...transactions]).sort((a, b) => new Date(a.bill_date).getTime() - new Date(b.bill_date).getTime());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
 
321
  const data: any[][] = [];
322
  data.push([c(`Ledger: ${party.name}`, STYLES.TITLE)]);
@@ -324,21 +405,61 @@ export const exportPartyLedger = (party: Party, transactions: Transaction[], dat
324
  data.push(['Date', 'Particulars', 'Credit', 'Debit', 'Balance', 'Dr/Cr'].map(h => c(h, STYLES.TABLE_HEAD)));
325
 
326
  let running = 0, tCr = 0, tDr = 0;
327
- filtered.forEach(tx => {
 
 
 
328
  let cr = 0, dr = 0;
329
- if (tx.bill_type === BillType.AWAAK) tx.is_return ? (cr = tx.total_amount) : (dr = tx.total_amount);
330
- else tx.is_return ? (dr = tx.total_amount) : (cr = tx.total_amount);
331
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  tCr += cr; tDr += dr; running += (dr - cr);
333
- const desc = `${tx.bill_number} (${tx.bill_type})` + (tx.items.length ? ` - ${tx.items[0].mirchi_name}` : "");
334
 
335
  data.push([
336
- c(new Date(tx.bill_date).toLocaleDateString('en-IN'), STYLES.CELL_TEXT),
337
  c(desc, STYLES.CELL_TEXT),
338
  c(cr || "", STYLES.CELL_NUM, FMT_CURRENCY),
339
  c(dr || "", STYLES.CELL_NUM, FMT_CURRENCY),
340
  c(Math.abs(running), STYLES.CELL_NUM, FMT_CURRENCY),
341
- c(running > 0 ? 'Dr' : 'Cr', STYLES.CELL_TEXT)
342
  ]);
343
  });
344
 
 
306
  XLSX.writeFile(wb, `Lot_${safeName}_Analysis.xlsx`);
307
  };
308
 
 
 
309
  // 5. OTHER EXPORT FUNCTIONS (Parties/Ledger)
310
  // ==========================================
311
 
312
  export const exportPartyLedger = (party: Party, transactions: Transaction[], dateRange?: { from: string; to: string }) => {
313
+ const parseDate = (value?: string): Date | undefined => {
314
+ if (!value) return undefined;
315
+ const trimmed = String(value).trim();
316
+ if (!trimmed) return undefined;
317
+
318
+ const iso = trimmed.match(/^\d{4}-\d{2}-\d{2}(?:[T\s].*)?$/);
319
+ if (iso) {
320
+ const d = new Date(trimmed);
321
+ return isNaN(d.getTime()) ? undefined : d;
322
+ }
323
+ const slash = trimmed.match(/^\d{1,2}\/\d{1,2}\/(\d{2}|\d{4})$/);
324
+ if (slash) {
325
+ const [aStr, bStr, yyStr] = trimmed.split('/');
326
+ const a = Number(aStr);
327
+ const b = Number(bStr);
328
+ const yy = Number(yyStr);
329
+ const year = yyStr.length === 2 ? 2000 + yy : yy;
330
+
331
+ const day = a > 12 ? a : b > 12 ? b : a;
332
+ const month = a > 12 ? b : b > 12 ? a : b;
333
+
334
+ const d = new Date(year, month - 1, day);
335
+ return isNaN(d.getTime()) ? undefined : d;
336
+ }
337
+
338
+ const d = new Date(trimmed);
339
+ return isNaN(d.getTime()) ? undefined : d;
340
+ };
341
+
342
+ const extractDateFromText = (text?: string): Date | undefined => {
343
+ if (!text) return undefined;
344
+ const iso = text.match(/\b\d{4}-\d{2}-\d{2}\b/);
345
+ if (iso?.[0]) return parseDate(iso[0]);
346
+ const dmy = text.match(/\b\d{1,2}\/\d{1,2}\/\d{4}\b/);
347
+ if (dmy?.[0]) return parseDate(dmy[0]);
348
+ return undefined;
349
+ };
350
+
351
+ const isAwaakTx = (tx: Transaction) => {
352
+ const typeStr = String(tx.bill_type).toUpperCase();
353
+ const numStr = tx.bill_number ? tx.bill_number.toUpperCase() : "";
354
+ const isJawaakFromNumber = numStr.startsWith('JAWAAK');
355
+ const isAwaakFromNumber = numStr.startsWith('AWAAK');
356
+ return isAwaakFromNumber ? true : isJawaakFromNumber ? false : typeStr === 'AWAAK';
357
+ };
358
+
359
+ const inRange = (d: Date) => {
360
+ if (!dateRange) return true;
361
+ const from = new Date(dateRange.from);
362
+ const to = new Date(dateRange.to);
363
+ return d >= from && d <= to;
364
+ };
365
+
366
+ type LedgerEvent =
367
+ | { kind: 'bill'; date: Date; tx: Transaction }
368
+ | { kind: 'payment'; date: Date; tx: Transaction; payment: any };
369
+
370
+ const events: LedgerEvent[] = [];
371
+
372
+ transactions.forEach(tx => {
373
+ const billDate = parseDate(tx.bill_date) || new Date(tx.bill_date);
374
+ if (!inRange(billDate)) return;
375
+
376
+ events.push({ kind: 'bill', date: billDate, tx });
377
+
378
+ const payments = tx.payments || [];
379
+ const validPayments = payments.filter((p: any) => String(p.mode).toLowerCase() !== 'due');
380
+ validPayments.forEach((p: any) => {
381
+ const paymentDate =
382
+ parseDate(p.payment_date) ||
383
+ parseDate(p.created_at) ||
384
+ parseDate(p.date) ||
385
+ extractDateFromText(p.reference) ||
386
+ billDate;
387
+
388
+ const safePaymentDate = paymentDate < billDate ? billDate : paymentDate;
389
+ if (!inRange(safePaymentDate)) return;
390
+ events.push({ kind: 'payment', date: safePaymentDate, tx, payment: p });
391
+ });
392
+ });
393
+
394
+ events.sort((a, b) => {
395
+ const ta = a.date.getTime();
396
+ const tb = b.date.getTime();
397
+ if (ta !== tb) return ta - tb;
398
+ if (a.kind !== b.kind) return a.kind === 'bill' ? -1 : 1;
399
+ return (a.tx.bill_number || '').localeCompare(b.tx.bill_number || '');
400
+ });
401
 
402
  const data: any[][] = [];
403
  data.push([c(`Ledger: ${party.name}`, STYLES.TITLE)]);
 
405
  data.push(['Date', 'Particulars', 'Credit', 'Debit', 'Balance', 'Dr/Cr'].map(h => c(h, STYLES.TABLE_HEAD)));
406
 
407
  let running = 0, tCr = 0, tDr = 0;
408
+ events.forEach(ev => {
409
+ const tx = ev.tx;
410
+ const isAwaak = isAwaakTx(tx);
411
+
412
  let cr = 0, dr = 0;
413
+
414
+ if (ev.kind === 'bill') {
415
+ if (isAwaak) {
416
+ if (tx.is_return) dr = tx.total_amount;
417
+ else cr = tx.total_amount;
418
+ } else {
419
+ if (tx.is_return) cr = tx.total_amount;
420
+ else dr = tx.total_amount;
421
+ }
422
+
423
+ const desc = `${tx.bill_number}` + (tx.is_return ? ' (RETURN)' : '') + (tx.items.length ? ` - ${tx.items[0].mirchi_name}` : "");
424
+
425
+ tCr += cr; tDr += dr; running += (dr - cr);
426
+
427
+ data.push([
428
+ c(ev.date.toLocaleDateString('en-IN'), STYLES.CELL_TEXT),
429
+ c(desc, STYLES.CELL_TEXT),
430
+ c(cr || "", STYLES.CELL_NUM, FMT_CURRENCY),
431
+ c(dr || "", STYLES.CELL_NUM, FMT_CURRENCY),
432
+ c(Math.abs(running), STYLES.CELL_NUM, FMT_CURRENCY),
433
+ c(running >= 0 ? 'Dr' : 'Cr', STYLES.CELL_TEXT)
434
+ ]);
435
+
436
+ return;
437
+ }
438
+
439
+ const p = ev.payment;
440
+ const amount = Number(p.amount) || 0;
441
+
442
+ if (isAwaak) {
443
+ if (tx.is_return) cr = amount;
444
+ else dr = amount;
445
+ } else {
446
+ if (tx.is_return) dr = amount;
447
+ else cr = amount;
448
+ }
449
+
450
+ const modeStr = String(p.mode || '').charAt(0).toUpperCase() + String(p.mode || '').slice(1);
451
+ let desc = ` [${modeStr}]`;
452
+ if (p.reference) desc += ` ${p.reference}`;
453
+
454
  tCr += cr; tDr += dr; running += (dr - cr);
 
455
 
456
  data.push([
457
+ c(ev.date.toLocaleDateString('en-IN'), STYLES.CELL_TEXT),
458
  c(desc, STYLES.CELL_TEXT),
459
  c(cr || "", STYLES.CELL_NUM, FMT_CURRENCY),
460
  c(dr || "", STYLES.CELL_NUM, FMT_CURRENCY),
461
  c(Math.abs(running), STYLES.CELL_NUM, FMT_CURRENCY),
462
+ c(running >= 0 ? 'Dr' : 'Cr', STYLES.CELL_TEXT)
463
  ]);
464
  });
465
 
vite.config.ts CHANGED
@@ -1,16 +1,16 @@
1
- import { defineConfig } from 'vite'
2
- import react from '@vitejs/plugin-react'
3
-
4
- export default defineConfig({
5
- plugins: [react()],
6
- server: {
7
- host: true, // Listen on all addresses
8
- port: 3000
9
- },
10
- preview: {
11
- host: true, // Listen on all addresses
12
- port: 7860,
13
- strictPort: true,
14
- allowedHosts: ['antaram-stagingfrontend.hf.space'] // Add this line
15
- }
16
- })
 
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: {
7
+ host: true, // Listen on all addresses
8
+ port: 3000
9
+ },
10
+ preview: {
11
+ host: true, // Listen on all addresses
12
+ port: 7860,
13
+ strictPort: true,
14
+ allowedHosts: ['antaram-stagingfrontend.hf.space'] // Add this line
15
+ }
16
+ })