From c0cf73be43045ee8bf75b50bbfa9b33456d77825 Mon Sep 17 00:00:00 2001 From: dfkadyr Date: Fri, 19 Jun 2026 12:46:49 +0300 Subject: [PATCH] update DatePicker ui, replace to exceljs, improve export rewards logic --- package.json | 4 +- pnpm-lock.yaml | 507 +++++++++++++++--- .../DatePicker/Calendar/Calendar.tsx | 270 +++++----- .../Day.module.scss => Cell/Cell.module.scss} | 0 .../Calendar/{Day/Day.tsx => Cell/Cell.tsx} | 10 +- .../Days.module.scss} | 0 .../DatePicker/Calendar/Days/Days.tsx | 88 +++ .../DatePicker/Calendar/Header/Header.tsx | 83 +++ .../DatePicker/Calendar/Months/Months.tsx | 57 ++ .../DatePicker/Calendar/Years/Years.tsx | 51 ++ .../DatePicker/Calendar/util/useDays.ts | 127 +++-- src/helpers/requests/fetchXlsxFile.ts | 36 +- .../modals/ExportRewardsModal/util/columns.ts | 15 + .../ExportRewardsModal/util/messages.ts | 52 +- .../ExportRewardsModal/util/useRewards.ts | 34 +- .../modals/ExportRewardsModal/util/useXLSX.ts | 19 +- 16 files changed, 1037 insertions(+), 316 deletions(-) rename src/components/DatePicker/Calendar/{Day/Day.module.scss => Cell/Cell.module.scss} (100%) rename src/components/DatePicker/Calendar/{Day/Day.tsx => Cell/Cell.tsx} (81%) rename src/components/DatePicker/Calendar/{Calendar.module.scss => Days/Days.module.scss} (100%) create mode 100644 src/components/DatePicker/Calendar/Days/Days.tsx create mode 100644 src/components/DatePicker/Calendar/Header/Header.tsx create mode 100644 src/components/DatePicker/Calendar/Months/Months.tsx create mode 100644 src/components/DatePicker/Calendar/Years/Years.tsx create mode 100644 src/layouts/modals/ExportRewardsModal/util/columns.ts diff --git a/package.json b/package.json index ba9106a0..5de3444a 100644 --- a/package.json +++ b/package.json @@ -105,6 +105,7 @@ "dayjs": "1.11.13", "destyle.css": "4.0.1", "ethers": "6.14.3", + "exceljs": "4.4.0", "fast-deep-equal": "3.1.3", "focus-visible": "5.2.1", "husky": "9.1.7", @@ -117,8 +118,7 @@ "react-redux": "9.2.0", "swiper": "11.2.8", "ua-parser-js": "2.0.3", - "viem": "2.44.1", - "xlsx": "0.18.5" + "viem": "2.44.1" }, "pnpm": { "overrides": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4cab5957..b23f9efb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -108,6 +108,9 @@ importers: ethers: specifier: 6.14.3 version: 6.14.3 + exceljs: + specifier: 4.4.0 + version: 4.4.0 fast-deep-equal: specifier: 3.1.3 version: 3.1.3 @@ -147,9 +150,6 @@ importers: viem: specifier: 2.44.1 version: 2.44.1(typescript@6.0.3)(zod@4.3.5) - xlsx: - specifier: 0.18.5 - version: 0.18.5 devDependencies: '@eslint/js': specifier: 9.28.0 @@ -714,6 +714,12 @@ packages: '@ethersproject/web@5.8.0': resolution: {integrity: sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==} + '@fast-csv/format@4.3.5': + resolution: {integrity: sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==} + + '@fast-csv/parse@4.3.6': + resolution: {integrity: sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==} + '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} @@ -1727,6 +1733,9 @@ packages: '@types/node-fetch@2.6.13': resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} + '@types/node@14.18.63': + resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} + '@types/node@22.10.0': resolution: {integrity: sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==} @@ -2160,10 +2169,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - adler-32@1.3.1: - resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} - engines: {node: '>=0.8'} - aes-js@4.0.0-beta.5: resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} @@ -2214,6 +2219,18 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + archiver-utils@2.1.0: + resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} + engines: {node: '>= 6'} + + archiver-utils@3.0.4: + resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} + engines: {node: '>= 10'} + + archiver@5.3.2: + resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} + engines: {node: '>= 10'} + arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -2325,6 +2342,9 @@ packages: async-mutex@0.5.0: resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -2407,6 +2427,10 @@ packages: resolution: {integrity: sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==} hasBin: true + big-integer@1.6.52: + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} + big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} @@ -2423,12 +2447,21 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + binary@0.3.0: + resolution: {integrity: sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bl@5.1.0: resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} blakejs@1.2.1: resolution: {integrity: sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==} + bluebird@3.4.7: + resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} + bn.js@4.12.2: resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==} @@ -2441,6 +2474,9 @@ packages: brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@2.1.1: + resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} + brace-expansion@5.0.6: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} @@ -2464,12 +2500,26 @@ packages: bs58@6.0.0: resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer-indexof-polyfill@1.0.2: + resolution: {integrity: sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==} + engines: {node: '>=0.10'} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + buffers@0.1.1: + resolution: {integrity: sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==} + engines: {node: '>=0.2.0'} + bundle-require@5.1.0: resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2544,9 +2594,8 @@ packages: ccount@1.1.0: resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} - cfb@1.2.2: - resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} - engines: {node: '>=0.8'} + chainsaw@0.1.0: + resolution: {integrity: sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==} chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} @@ -2624,10 +2673,6 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} - codepage@1.15.0: - resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} - engines: {node: '>=0.8'} - collapse-white-space@1.0.6: resolution: {integrity: sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==} @@ -2673,6 +2718,10 @@ packages: component-emitter@1.3.1: resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + compress-commons@4.1.2: + resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} + engines: {node: '>= 10'} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2695,6 +2744,9 @@ packages: resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} engines: {node: '>=0.10.0'} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cosmiconfig@5.2.1: resolution: {integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==} engines: {node: '>=4'} @@ -2713,6 +2765,10 @@ packages: engines: {node: '>=0.8'} hasBin: true + crc32-stream@4.0.3: + resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} + engines: {node: '>= 10'} + cross-fetch@3.2.0: resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} @@ -2916,6 +2972,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + duplexer2@0.1.4: + resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + duplexify@4.1.3: resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} @@ -3174,6 +3233,10 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + exceljs@4.4.0: + resolution: {integrity: sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==} + engines: {node: '>=8.3.0'} + execa@9.5.2: resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} engines: {node: ^18.19.0 || >=20.5.0} @@ -3207,6 +3270,10 @@ packages: fancy-canvas@2.1.0: resolution: {integrity: sha512-nifxXJ95JNLFR2NgRV4/MxVP45G9909wJTEKz5fg/TZS20JJZA6hfgRVh/bC9bwl2zBtBNcYPjiBE4njQHVBwQ==} + fast-csv@4.3.6: + resolution: {integrity: sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==} + engines: {node: '>=10.0.0'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -3341,10 +3408,6 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} - frac@1.1.2: - resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} - engines: {node: '>=0.8'} - fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -3352,6 +3415,9 @@ packages: resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} engines: {node: '>=0.10.0'} + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -3369,6 +3435,11 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + fstream@1.0.12: + resolution: {integrity: sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==} + engines: {node: '>=0.6'} + deprecated: This package is no longer supported. + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -3611,6 +3682,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + immer@10.2.0: resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} @@ -4045,6 +4119,9 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + just-performance@4.3.0: resolution: {integrity: sha512-L7RjvtJsL0QO8xFs5wEoDDzzJwoiowRw6Rn/GnvldlchS2JQr9wFYPiwZcDfrbbujEKqKN0tvENdbjXdYhDp5Q==} @@ -4090,6 +4167,10 @@ packages: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + leven@2.1.0: resolution: {integrity: sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==} engines: {node: '>=0.10.0'} @@ -4098,6 +4179,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lightningcss-darwin-arm64@1.30.1: resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} engines: {node: '>= 12.0.0'} @@ -4178,6 +4262,9 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + listenercount@1.0.1: + resolution: {integrity: sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==} + lit-element@4.2.2: resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} @@ -4218,6 +4305,40 @@ packages: lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.difference@4.5.0: + resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} + + lodash.escaperegexp@4.1.2: + resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + + lodash.flatten@4.4.0: + resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} + + lodash.groupby@4.6.0: + resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash.isfunction@3.0.9: + resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==} + + lodash.isnil@4.0.0: + resolution: {integrity: sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isundefined@3.0.1: + resolution: {integrity: sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==} + lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -4227,6 +4348,12 @@ packages: lodash.truncate@4.4.2: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + lodash.union@4.6.0: + resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -4364,6 +4491,10 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + minimist-options@3.0.2: resolution: {integrity: sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==} engines: {node: '>= 4'} @@ -4644,6 +4775,9 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -4916,6 +5050,9 @@ packages: resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} engines: {node: '>=18'} + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + process-warning@1.0.0: resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} @@ -5058,10 +5195,16 @@ packages: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -5207,6 +5350,9 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -5254,6 +5400,10 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + saxes@5.0.1: + resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} + engines: {node: '>=10'} + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -5308,6 +5458,9 @@ packages: resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} engines: {node: '>=0.10.0'} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -5443,10 +5596,6 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - ssf@0.11.2: - resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} - engines: {node: '>=0.8'} - stable-hash@0.0.5: resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} @@ -5506,6 +5655,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -5643,6 +5795,10 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + tar@7.5.2: resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==} engines: {node: '>=18'} @@ -5702,6 +5858,10 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tmp@0.2.7: + resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} + engines: {node: '>=14.14'} + to-object-path@0.3.0: resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} engines: {node: '>=0.10.0'} @@ -5724,6 +5884,9 @@ packages: tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + traverse@0.3.9: + resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -5999,6 +6162,9 @@ packages: uploadthing: optional: true + unzipper@0.10.14: + resolution: {integrity: sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==} + update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -6041,6 +6207,11 @@ packages: resolution: {integrity: sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==} hasBin: true + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). @@ -6153,18 +6324,10 @@ packages: engines: {node: '>= 8'} hasBin: true - wmf@1.0.2: - resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} - engines: {node: '>=0.8'} - word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - word@0.3.0: - resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} - engines: {node: '>=0.8'} - wordwrapjs@4.0.1: resolution: {integrity: sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==} engines: {node: '>=8.0.0'} @@ -6251,10 +6414,8 @@ packages: x-is-string@0.1.0: resolution: {integrity: sha512-GojqklwG8gpzOVEVki5KudKNoq7MbbjYZCbyWzEz7tyPA7eleiE0+ePwOWQQRb5fm86rD3S8Tc0tSFf3AOv50w==} - xlsx@0.18.5: - resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} - engines: {node: '>=0.8'} - hasBin: true + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} xstate@5.19.2: resolution: {integrity: sha512-B8fL2aP0ogn5aviAXFzI5oZseAMqN00fg/TeDa3ZtatyDcViYLIfuQl4y8qmHCiKZgGEzmnTyNtNQL9oeJE2gw==} @@ -6311,6 +6472,10 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + zip-stream@4.1.1: + resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} + engines: {node: '>= 10'} + zod-validation-error@4.0.2: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} engines: {node: '>=18.0.0'} @@ -6915,6 +7080,25 @@ snapshots: '@ethersproject/properties': 5.8.0 '@ethersproject/strings': 5.8.0 + '@fast-csv/format@4.3.5': + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.isboolean: 3.0.3 + lodash.isequal: 4.5.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + + '@fast-csv/parse@4.3.6': + dependencies: + '@types/node': 14.18.63 + lodash.escaperegexp: 4.1.2 + lodash.groupby: 4.6.0 + lodash.isfunction: 3.0.9 + lodash.isnil: 4.0.0 + lodash.isundefined: 3.0.1 + lodash.uniq: 4.5.0 + '@floating-ui/core@1.7.3': dependencies: '@floating-ui/utils': 0.2.10 @@ -8174,6 +8358,8 @@ snapshots: '@types/node': 22.15.21 form-data: 4.0.5 + '@types/node@14.18.63': {} + '@types/node@22.10.0': dependencies: undici-types: 6.20.0 @@ -9047,8 +9233,6 @@ snapshots: acorn@8.15.0: {} - adler-32@1.3.1: {} - aes-js@4.0.0-beta.5: {} ajv-formats@2.1.1(ajv@8.17.1): @@ -9097,6 +9281,42 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + archiver-utils@2.1.0: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 2.3.8 + + archiver-utils@3.0.4: + dependencies: + glob: 7.2.3 + graceful-fs: 4.2.11 + lazystream: 1.0.1 + lodash.defaults: 4.2.0 + lodash.difference: 4.5.0 + lodash.flatten: 4.4.0 + lodash.isplainobject: 4.0.6 + lodash.union: 4.6.0 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + + archiver@5.3.2: + dependencies: + archiver-utils: 2.1.0 + async: 3.2.6 + buffer-crc32: 0.2.13 + readable-stream: 3.6.2 + readdir-glob: 1.1.3 + tar-stream: 2.2.0 + zip-stream: 4.1.1 + arg@5.0.2: {} argparse@1.0.10: @@ -9212,6 +9432,8 @@ snapshots: dependencies: tslib: 2.8.1 + async@3.2.6: {} + asynckit@0.4.0: {} atob@2.1.2: {} @@ -9310,6 +9532,8 @@ snapshots: baseline-browser-mapping@2.9.14: {} + big-integer@1.6.52: {} + big.js@5.2.2: {} big.js@6.2.2: {} @@ -9320,6 +9544,17 @@ snapshots: binary-extensions@2.3.0: {} + binary@0.3.0: + dependencies: + buffers: 0.1.1 + chainsaw: 0.1.0 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + bl@5.1.0: dependencies: buffer: 6.0.3 @@ -9328,6 +9563,8 @@ snapshots: blakejs@1.2.1: {} + bluebird@3.4.7: {} + bn.js@4.12.2: {} bn.js@5.2.2: {} @@ -9339,6 +9576,10 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@2.1.1: + dependencies: + balanced-match: 1.0.2 + brace-expansion@5.0.6: dependencies: balanced-match: 4.0.4 @@ -9376,13 +9617,24 @@ snapshots: dependencies: base-x: 5.0.1 + buffer-crc32@0.2.13: {} + buffer-from@1.1.2: {} + buffer-indexof-polyfill@1.0.2: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + buffer@6.0.3: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + buffers@0.1.1: {} + bundle-require@5.1.0(esbuild@0.24.2): dependencies: esbuild: 0.24.2 @@ -9463,10 +9715,9 @@ snapshots: ccount@1.1.0: {} - cfb@1.2.2: + chainsaw@0.1.0: dependencies: - adler-32: 1.3.1 - crc-32: 1.2.2 + traverse: 0.3.9 chalk@2.4.2: dependencies: @@ -9552,8 +9803,6 @@ snapshots: clsx@2.1.1: {} - codepage@1.15.0: {} - collapse-white-space@1.0.6: {} collection-visit@1.0.0: @@ -9599,6 +9848,13 @@ snapshots: component-emitter@1.3.1: {} + compress-commons@4.1.2: + dependencies: + buffer-crc32: 0.2.13 + crc32-stream: 4.0.3 + normalize-path: 3.0.0 + readable-stream: 3.6.2 + concat-map@0.0.1: {} concurrently@9.1.2: @@ -9619,6 +9875,8 @@ snapshots: copy-descriptor@0.1.1: {} + core-util-is@1.0.3: {} + cosmiconfig@5.2.1: dependencies: import-fresh: 2.0.0 @@ -9637,6 +9895,11 @@ snapshots: crc-32@1.2.2: {} + crc32-stream@4.0.3: + dependencies: + crc-32: 1.2.2 + readable-stream: 3.6.2 + cross-fetch@3.2.0: dependencies: node-fetch: 2.7.0 @@ -9814,6 +10077,10 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + duplexer2@0.1.4: + dependencies: + readable-stream: 2.3.8 + duplexify@4.1.3: dependencies: end-of-stream: 1.4.5 @@ -10259,6 +10526,18 @@ snapshots: events@3.3.0: {} + exceljs@4.4.0: + dependencies: + archiver: 5.3.2 + dayjs: 1.11.13 + fast-csv: 4.3.6 + jszip: 3.10.1 + readable-stream: 3.6.2 + saxes: 5.0.1 + tmp: 0.2.7 + unzipper: 0.10.14 + uuid: 8.3.2 + execa@9.5.2: dependencies: '@sindresorhus/merge-streams': 4.0.0 @@ -10318,6 +10597,11 @@ snapshots: fancy-canvas@2.1.0: {} + fast-csv@4.3.6: + dependencies: + '@fast-csv/format': 4.3.5 + '@fast-csv/parse': 4.3.6 + fast-deep-equal@3.1.3: {} fast-glob@2.2.7: @@ -10457,14 +10741,14 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 - frac@1.1.2: {} - fraction.js@4.3.7: {} fragment-cache@0.2.1: dependencies: map-cache: 0.2.2 + fs-constants@1.0.0: {} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -10479,6 +10763,13 @@ snapshots: fsevents@2.3.3: optional: true + fstream@1.0.12: + dependencies: + graceful-fs: 4.2.11 + inherits: 2.0.4 + mkdirp: 0.5.6 + rimraf: 2.6.3 + function-bind@1.1.2: {} function.prototype.name@1.1.8: @@ -10743,6 +11034,8 @@ snapshots: ignore@7.0.5: {} + immediate@3.0.6: {} + immer@10.2.0: {} immutable@5.1.4: {} @@ -11148,6 +11441,13 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + just-performance@4.3.0: {} keccak@3.0.4: @@ -11188,6 +11488,10 @@ snapshots: dependencies: language-subtag-registry: 0.3.23 + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + leven@2.1.0: {} levn@0.4.1: @@ -11195,6 +11499,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lightningcss-darwin-arm64@1.30.1: optional: true @@ -11254,6 +11562,8 @@ snapshots: lines-and-columns@1.2.4: {} + listenercount@1.0.1: {} + lit-element@4.2.2: dependencies: '@lit-labs/ssr-dom-shim': 1.6.0 @@ -11302,12 +11612,38 @@ snapshots: lodash.camelcase@4.3.0: {} + lodash.defaults@4.2.0: {} + + lodash.difference@4.5.0: {} + + lodash.escaperegexp@4.1.2: {} + + lodash.flatten@4.4.0: {} + + lodash.groupby@4.6.0: {} + + lodash.isboolean@3.0.3: {} + + lodash.isequal@4.5.0: {} + + lodash.isfunction@3.0.9: {} + + lodash.isnil@4.0.0: {} + + lodash.isplainobject@4.0.6: {} + + lodash.isundefined@3.0.1: {} + lodash.merge@4.6.2: {} lodash.sortby@4.7.0: {} lodash.truncate@4.4.2: {} + lodash.union@4.6.0: {} + + lodash.uniq@4.5.0: {} + lodash@4.17.21: {} log-symbols@2.2.0: @@ -11453,6 +11789,10 @@ snapshots: dependencies: brace-expansion: 1.1.12 + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.1 + minimist-options@3.0.2: dependencies: arrify: 1.0.1 @@ -11776,6 +12116,8 @@ snapshots: p-try@2.2.0: {} + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -12014,6 +12356,8 @@ snapshots: dependencies: parse-ms: 4.0.0 + process-nextick-args@2.0.1: {} + process-warning@1.0.0: {} prompts@2.4.2: @@ -12179,12 +12523,26 @@ snapshots: parse-json: 5.2.0 type-fest: 0.6.0 + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.9 + readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -12376,6 +12734,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safe-json-utils@1.1.1: {} @@ -12412,6 +12772,10 @@ snapshots: optionalDependencies: '@parcel/watcher': 2.5.4 + saxes@5.0.1: + dependencies: + xmlchars: 2.2.0 + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -12474,6 +12838,8 @@ snapshots: is-plain-object: 2.0.4 split-string: 3.1.0 + setimmediate@1.0.5: {} + sharp@0.34.5: dependencies: '@img/colour': 1.0.0 @@ -12644,10 +13010,6 @@ snapshots: sprintf-js@1.0.3: {} - ssf@0.11.2: - dependencies: - frac: 1.1.2 - stable-hash@0.0.5: {} stable@0.1.8: {} @@ -12732,6 +13094,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -12987,6 +13353,14 @@ snapshots: tapable@2.3.0: {} + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + tar@7.5.2: dependencies: '@isaacs/fs-minipass': 4.0.1 @@ -13039,6 +13413,8 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tmp@0.2.7: {} + to-object-path@0.3.0: dependencies: kind-of: 3.2.2 @@ -13065,6 +13441,8 @@ snapshots: dependencies: punycode: 2.3.1 + traverse@0.3.9: {} + tree-kill@1.2.2: {} trim-newlines@2.0.0: {} @@ -13346,6 +13724,19 @@ snapshots: optionalDependencies: idb-keyval: 6.2.2 + unzipper@0.10.14: + dependencies: + big-integer: 1.6.52 + binary: 0.3.0 + bluebird: 3.4.7 + buffer-indexof-polyfill: 1.0.2 + duplexer2: 0.1.4 + fstream: 1.0.12 + graceful-fs: 4.2.11 + listenercount: 1.0.1 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: browserslist: 4.28.1 @@ -13379,6 +13770,8 @@ snapshots: uuid@11.0.3: {} + uuid@8.3.2: {} + uuid@9.0.1: {} validate-npm-package-license@3.0.4: @@ -13573,12 +13966,8 @@ snapshots: dependencies: isexe: 2.0.0 - wmf@1.0.2: {} - word-wrap@1.2.5: {} - word@0.3.0: {} - wordwrapjs@4.0.1: dependencies: reduce-flatten: 2.0.0 @@ -13619,15 +14008,7 @@ snapshots: x-is-string@0.1.0: {} - xlsx@0.18.5: - dependencies: - adler-32: 1.3.1 - cfb: 1.2.2 - codepage: 1.15.0 - crc-32: 1.2.2 - ssf: 0.11.2 - wmf: 1.0.2 - word: 0.3.0 + xmlchars@2.2.0: {} xstate@5.19.2: {} @@ -13684,6 +14065,12 @@ snapshots: yoctocolors@2.1.2: {} + zip-stream@4.1.1: + dependencies: + archiver-utils: 3.0.4 + compress-commons: 4.1.2 + readable-stream: 3.6.2 + zod-validation-error@4.0.2(zod@4.3.5): dependencies: zod: 4.3.5 diff --git a/src/components/DatePicker/Calendar/Calendar.tsx b/src/components/DatePicker/Calendar/Calendar.tsx index 9008561f..2b6ce497 100644 --- a/src/components/DatePicker/Calendar/Calendar.tsx +++ b/src/components/DatePicker/Calendar/Calendar.tsx @@ -1,18 +1,15 @@ -import React, { useMemo } from 'react' +import React, { useCallback, useMemo, useState } from 'react' import forms from 'modules/forms' import date from 'modules/date' import cx from 'classnames' -import Bone from '../../Bone/Bone' -import Icon from '../../Icon/Icon' -import Text from '../../Text/Text' -import ButtonBase from '../../ButtonBase/ButtonBase' +import Days from './Days/Days' +import Years from './Years/Years' +import Months from './Months/Months' +import Header from './Header/Header' -import Day from './Day/Day' import { useDays, getDate } from './util' -import s from './Calendar.module.scss' - export type CalendarProps = { className?: string @@ -22,158 +19,149 @@ export type CalendarProps = { onChange?: () => void } +type Level = 'month' | 'year' | 'decade' + const Calendar: React.FC = (props) => { const { className, field, minDate, maxDate, onChange } = props + const [ level, setLevel ] = useState('month') + const { value: selectedDay } = forms.useFieldValue(field) - const { days, title, weekDays, yearMonth, weeksOffset, weeksCount, isLocaleFetching, setMonth } = useDays({ - field, - animationDuration: 350, - }) + + const { + days, title, year, months, years, weekDays, yearMonth, isLocaleFetching, + decadeStart, yearTitle, decadeTitle, setMonth, setYear, selectMonth, selectYear, + } = useDays({ field }) const minimalDate = useMemo(() => getDate(minDate), [ minDate ]) const maximalDate = useMemo(() => getDate(maxDate), [ maxDate ]) + const selectedDate = useMemo(() => getDate(selectedDay), [ selectedDay ]) - const [ firstDayOfMonth, lastDayOfMonth ] = useMemo(() => { + const [ rangeStart, rangeEnd ] = useMemo(() => { const yearMonthDate = date.time(yearMonth, 'YYYY-MM') - return [ - yearMonthDate.startOf('month'), - yearMonthDate.endOf('month'), - ] - }, [ yearMonth ]) + if (level === 'year') { + return [ yearMonthDate.startOf('year'), yearMonthDate.endOf('year') ] + } + + if (level === 'decade') { + return [ + yearMonthDate.set('year', decadeStart).startOf('year'), + yearMonthDate.set('year', decadeStart + 9).endOf('year'), + ] + } + + return [ yearMonthDate.startOf('month'), yearMonthDate.endOf('month') ] + }, [ level, yearMonth, decadeStart ]) + + const isLeftButtonDisabled = useMemo(() => ( + minimalDate ? !rangeStart.isAfter(minimalDate) : false + ), [ rangeStart, minimalDate ]) + + const isRightButtonDisabled = useMemo(() => ( + maximalDate ? !rangeEnd.isBefore(maximalDate) : false + ), [ rangeEnd, maximalDate ]) + + const headerTitle = { + month: title, + year: yearTitle, + decade: decadeTitle, + }[level] - const isLeftButtonDisabled = useMemo(() => { - if (minimalDate) { - return !firstDayOfMonth.isAfter(minimalDate) + const handlePrev = useCallback(() => { + if (level === 'month') { + setMonth(-1) + + return } - }, [ firstDayOfMonth, minimalDate ]) - const isRightButtonDisabled = useMemo(() => { - if (maximalDate) { - return !lastDayOfMonth.isBefore(maximalDate) + setYear(level === 'decade' ? -10 : -1) + }, [ level, setMonth, setYear ]) + + const handleNext = useCallback(() => { + if (level === 'month') { + setMonth(1) + + return } - }, [ lastDayOfMonth, maximalDate ]) + + setYear(level === 'decade' ? 10 : 1) + }, [ level, setMonth, setYear ]) + + const handleTitleClick = useCallback(() => { + setLevel(level === 'month' ? 'year' : 'decade') + }, [ level ]) + + const handleSelectDay = useCallback((fullDate: string) => { + field.setValue(fullDate) + + if (typeof onChange === 'function') { + onChange() + } + }, [ field, onChange ]) + + const handleSelectMonth = useCallback((month: number) => { + selectMonth(month) + setLevel('month') + }, [ selectMonth ]) + + const handleSelectYear = useCallback((value: number) => { + selectYear(value) + setLevel('year') + }, [ selectYear ]) return (
-
- setMonth(-1)} - > - + { + level === 'month' && ( + + ) + } + { + level === 'year' && ( + - - { - isLocaleFetching ? ( -
- -
- ) : ( - - ) - } - setMonth(1)} - > - - -
-
- { - weekDays.map((weekDay) => ( - isLocaleFetching ? ( -
- -
- ) : ( - - ) - )) - } -
-
-
0 ? `-${weeksOffset * 32}px` : '0', - }} - > - { - days.map((day) => { - const dayDate = getDate(day.fullDate) as Date.Time - - return ( - { - field.setValue(day.fullDate) - - if (typeof onChange === 'function') { - onChange() - } - }} - /> - ) - }) - } -
-
+ ) + }
) } diff --git a/src/components/DatePicker/Calendar/Day/Day.module.scss b/src/components/DatePicker/Calendar/Cell/Cell.module.scss similarity index 100% rename from src/components/DatePicker/Calendar/Day/Day.module.scss rename to src/components/DatePicker/Calendar/Cell/Cell.module.scss diff --git a/src/components/DatePicker/Calendar/Day/Day.tsx b/src/components/DatePicker/Calendar/Cell/Cell.tsx similarity index 81% rename from src/components/DatePicker/Calendar/Day/Day.tsx rename to src/components/DatePicker/Calendar/Cell/Cell.tsx index 553fd5e9..bdbe5728 100644 --- a/src/components/DatePicker/Calendar/Day/Day.tsx +++ b/src/components/DatePicker/Calendar/Cell/Cell.tsx @@ -4,10 +4,10 @@ import cx from 'classnames' import ButtonBase from '../../../ButtonBase/ButtonBase' import Text from '../../../Text/Text' -import s from './Day.module.scss' +import s from './Cell.module.scss' -type DayProps = { +type CellProps = { className?: string title: string isActive?: boolean @@ -16,7 +16,7 @@ type DayProps = { onClick: () => void } -const Day: React.FC = (props) => { +const Cell: React.FC = (props) => { const { className, title, isActive, isSelected, isDisabled, onClick } = props const defaultClassName = cx({ @@ -29,7 +29,7 @@ const Day: React.FC = (props) => { return ( = (props) => { } -export default React.memo(Day) +export default React.memo(Cell) diff --git a/src/components/DatePicker/Calendar/Calendar.module.scss b/src/components/DatePicker/Calendar/Days/Days.module.scss similarity index 100% rename from src/components/DatePicker/Calendar/Calendar.module.scss rename to src/components/DatePicker/Calendar/Days/Days.module.scss diff --git a/src/components/DatePicker/Calendar/Days/Days.tsx b/src/components/DatePicker/Calendar/Days/Days.tsx new file mode 100644 index 00000000..2a597af3 --- /dev/null +++ b/src/components/DatePicker/Calendar/Days/Days.tsx @@ -0,0 +1,88 @@ +import React from 'react' +import cx from 'classnames' + +import Bone from '../../../Bone/Bone' +import Text from '../../../Text/Text' + +import Cell from '../Cell/Cell' +import { getDate } from '../util' + +import s from './Days.module.scss' + + +type DayItem = { + date: string + fullDate: string + yearMonth: string +} + +type DaysProps = { + days: DayItem[] + weekDays: string[] + yearMonth: string + selectedDay?: string + isLocaleFetching: boolean + minimalDate: Date.Time | null + maximalDate: Date.Time | null + onSelect: (fullDate: string) => void +} + +const Days: React.FC = (props) => { + const { days, weekDays, yearMonth, selectedDay, isLocaleFetching, minimalDate, maximalDate, onSelect } = props + + return ( + <> +
+ { + weekDays.map((weekDay) => ( + isLocaleFetching ? ( +
+ +
+ ) : ( + + ) + )) + } +
+
+ { + days.map((day) => { + const dayDate = getDate(day.fullDate) as Date.Time + + return ( + onSelect(day.fullDate)} + /> + ) + }) + } +
+ + ) +} + + +export default React.memo(Days) diff --git a/src/components/DatePicker/Calendar/Header/Header.tsx b/src/components/DatePicker/Calendar/Header/Header.tsx new file mode 100644 index 00000000..1079702d --- /dev/null +++ b/src/components/DatePicker/Calendar/Header/Header.tsx @@ -0,0 +1,83 @@ +import React from 'react' +import cx from 'classnames' + +import Bone from '../../../Bone/Bone' +import Icon from '../../../Icon/Icon' +import Text from '../../../Text/Text' +import ButtonBase from '../../../ButtonBase/ButtonBase' + + +type HeaderProps = { + title: string + isLocaleFetching: boolean + isTitleDisabled: boolean + isLeftDisabled?: boolean + isRightDisabled?: boolean + onPrev: () => void + onNext: () => void + onTitleClick: () => void +} + +const Header: React.FC = (props) => { + const { title, isLocaleFetching, isTitleDisabled, isLeftDisabled, isRightDisabled, onPrev, onNext, onTitleClick } = props + + return ( +
+ + + + { + isLocaleFetching ? ( +
+ +
+ ) : ( + + + + ) + } + + + +
+ ) +} + + +export default React.memo(Header) diff --git a/src/components/DatePicker/Calendar/Months/Months.tsx b/src/components/DatePicker/Calendar/Months/Months.tsx new file mode 100644 index 00000000..e224f989 --- /dev/null +++ b/src/components/DatePicker/Calendar/Months/Months.tsx @@ -0,0 +1,57 @@ +import React from 'react' +import date from 'modules/date' + +import Cell from '../Cell/Cell' + + +type MonthItem = { + month: number + title: string +} + +type MonthsProps = { + year: number + months: MonthItem[] + yearMonth: string + selectedDate: Date.Time | null + minimalDate: Date.Time | null + maximalDate: Date.Time | null + onSelect: (month: number) => void +} + +const Months: React.FC = (props) => { + const { year, months, yearMonth, selectedDate, minimalDate, maximalDate, onSelect } = props + + const base = date.time(yearMonth, 'YYYY-MM') + + return ( +
+ { + months.map(({ month, title }) => { + const monthDate = base.set('month', month) + + const isDisabled = ( + minimalDate?.isAfter(monthDate.endOf('month')) + || maximalDate?.isBefore(monthDate.startOf('month')) + ) + const isSelected = selectedDate?.year() === year && selectedDate?.month() === month + + return ( + onSelect(month)} + /> + ) + }) + } +
+ ) +} + + +export default React.memo(Months) diff --git a/src/components/DatePicker/Calendar/Years/Years.tsx b/src/components/DatePicker/Calendar/Years/Years.tsx new file mode 100644 index 00000000..d90314f5 --- /dev/null +++ b/src/components/DatePicker/Calendar/Years/Years.tsx @@ -0,0 +1,51 @@ +import React from 'react' +import date from 'modules/date' + +import Cell from '../Cell/Cell' + + +type YearsProps = { + years: number[] + yearMonth: string + decadeStart: number + selectedDate: Date.Time | null + minimalDate: Date.Time | null + maximalDate: Date.Time | null + onSelect: (year: number) => void +} + +const Years: React.FC = (props) => { + const { years, yearMonth, decadeStart, selectedDate, minimalDate, maximalDate, onSelect } = props + + const base = date.time(yearMonth, 'YYYY-MM') + + return ( +
+ { + years.map((value) => { + const yearDate = base.set('year', value) + + const isDisabled = ( + minimalDate?.isAfter(yearDate.endOf('year')) + || maximalDate?.isBefore(yearDate.startOf('year')) + ) + + return ( + = decadeStart && value <= decadeStart + 9} + isSelected={selectedDate?.year() === value} + isDisabled={isDisabled} + onClick={() => onSelect(value)} + /> + ) + }) + } +
+ ) +} + + +export default React.memo(Years) diff --git a/src/components/DatePicker/Calendar/util/useDays.ts b/src/components/DatePicker/Calendar/util/useDays.ts index d6abe537..dd13fdea 100644 --- a/src/components/DatePicker/Calendar/util/useDays.ts +++ b/src/components/DatePicker/Calendar/util/useDays.ts @@ -1,9 +1,13 @@ import { useCallback, useMemo } from 'react' import intl from 'modules/intl' import date from 'modules/date' -import { useChangeEffect, useFieldListener, useObjectState } from 'hooks' +import { useFieldListener, useObjectState } from 'hooks' +const format = 'YYYY-MM' + +const weeksInGrid = 6 + type GetCalendarRangeInput = { firstDay: Date.Time lastDay: Date.Time @@ -11,12 +15,9 @@ type GetCalendarRangeInput = { type Input = { field: Forms.Field - animationDuration: number } -const format = 'YYYY-MM' - -const useDays = ({ field, animationDuration }: Input) => { +const useDays = ({ field }: Input) => { const time = date.useTime() const { locale } = intl.useIntl() @@ -35,7 +36,7 @@ const useDays = ({ field, animationDuration }: Input) => { return result }, [ field ]) - const getStateFromField = useCallback((initialDate?: string) => { + const getYearMonthFromField = useCallback((initialDate?: string) => { const { year, month } = parseFieldDate() let currentDate = initialDate ? time(initialDate, format) : time() @@ -48,17 +49,14 @@ const useDays = ({ field, animationDuration }: Input) => { currentDate = currentDate.set('month', month - 1) } - const yearMonth = currentDate.format(format) - const initialYearMonth = yearMonth - - return { yearMonth, initialYearMonth } + return currentDate.format(format) }, [ time, parseFieldDate ]) - const [ { yearMonth, initialYearMonth }, setState ] = useObjectState(getStateFromField()) + const [ { yearMonth }, setState ] = useObjectState({ yearMonth: getYearMonthFromField() }) const onFieldChange = useCallback(() => { - setState({ yearMonth: getStateFromField(yearMonth).yearMonth }) - }, [ yearMonth, setState, getStateFromField ]) + setState({ yearMonth: getYearMonthFromField(yearMonth) }) + }, [ yearMonth, setState, getYearMonthFromField ]) useFieldListener(field, onFieldChange) @@ -72,6 +70,28 @@ const useDays = ({ field, animationDuration }: Input) => { }) }, [ yearMonth, time, setState ]) + const setYear = useCallback((count: number) => { + const nextYearMonth = time(yearMonth, format) + .add(count, 'year') + .format(format) + + setState({ + yearMonth: nextYearMonth, + }) + }, [ yearMonth, time, setState ]) + + const selectMonth = useCallback((month: number) => { + setState({ + yearMonth: time(yearMonth, format).set('month', month).format(format), + }) + }, [ yearMonth, time, setState ]) + + const selectYear = useCallback((value: number) => { + setState({ + yearMonth: time(yearMonth, format).set('year', value).format(format), + }) + }, [ yearMonth, time, setState ]) + const weekDays = useMemo(() => { return new Array(7) .fill(null) @@ -81,15 +101,11 @@ const useDays = ({ field, animationDuration }: Input) => { }, [ time ]) const getCalendarRange = useCallback((yearMonth: string) => { - const selectedMonth = time(yearMonth, format) - const startOfMonth = selectedMonth.startOf('month') - const endOfMonth = selectedMonth.endOf('month') - + const startOfMonth = time(yearMonth, format).startOf('month') const startDiff = weekDays.indexOf(startOfMonth.format('dd')) - const endDiff = 6 - weekDays.indexOf(endOfMonth.format('dd')) const firstDay = startOfMonth.subtract(startDiff, 'day') - const lastDay = endOfMonth.add(endDiff, 'day') + const lastDay = firstDay.add(weeksInGrid * 7 - 1, 'day') return { firstDay, @@ -97,14 +113,6 @@ const useDays = ({ field, animationDuration }: Input) => { } }, [ weekDays, time ]) - const monthRange = useMemo(() => { - return getCalendarRange(yearMonth) - }, [ yearMonth, getCalendarRange ]) - - const initialMonthRange = useMemo(() => { - return getCalendarRange(initialYearMonth) - }, [ initialYearMonth, getCalendarRange ]) - const getDays = useCallback(({ firstDay, lastDay }: GetCalendarRangeInput) => { return new Array(lastDay.diff(firstDay, 'day') + 1) .fill(null) @@ -122,59 +130,66 @@ const useDays = ({ field, animationDuration }: Input) => { }, []) const days = useMemo(() => { - const daysDiff = monthRange.firstDay.diff(initialMonthRange.firstDay, 'day') + return getDays(getCalendarRange(yearMonth)) + }, [ yearMonth, getCalendarRange, getDays ]) - const firstDay = daysDiff > 0 - ? initialMonthRange.firstDay - : monthRange.firstDay + const title = useMemo(() => time(yearMonth, format).format('MMMM, YYYY'), [ yearMonth, time ]) - const lastDay = daysDiff > 0 - ? monthRange.lastDay - : initialMonthRange.lastDay + const current = useMemo(() => time(yearMonth, format), [ yearMonth, time ]) - return getDays({ firstDay, lastDay }) - }, [ monthRange, initialMonthRange, getDays ]) + const year = useMemo(() => current.year(), [ current ]) - const weeksCount = useMemo(() => { - return monthRange.lastDay.diff(monthRange.firstDay, 'week') + 1 - }, [ monthRange ]) + const decadeStart = useMemo(() => Math.floor(year / 10) * 10, [ year ]) - const weeksOffset = useMemo(() => { - return initialMonthRange.firstDay.diff(monthRange.firstDay, 'week') - }, [ monthRange, initialMonthRange ]) + const months = useMemo(() => ( + new Array(12).fill(null).map((_, index) => ({ + month: index, + title: current.set('month', index).format('MMM'), + })) + ), [ current ]) - const title = useMemo(() => time(yearMonth, format).format('MMMM, YYYY'), [ yearMonth, time ]) + const years = useMemo(() => ( + new Array(12).fill(null).map((_, index) => decadeStart - 1 + index) + ), [ decadeStart ]) - const isLocaleFetching = useMemo(() => locale !== time().locale(), [ time, locale ]) + const yearTitle = useMemo(() => String(year), [ year ]) - useChangeEffect<[ string, number ]>(() => { - const timeout = setTimeout(() => { - setState({ initialYearMonth: yearMonth }) - }, animationDuration) + const decadeTitle = useMemo(() => `${decadeStart} – ${decadeStart + 9}`, [ decadeStart ]) - return () => { - clearTimeout(timeout) - } - }, [ yearMonth, animationDuration ]) + const isLocaleFetching = useMemo(() => locale !== time().locale(), [ time, locale ]) return useMemo(() => ({ days, + year, title, + years, + months, weekDays, + yearTitle, yearMonth, - weeksCount, - weeksOffset, + decadeStart, + decadeTitle, isLocaleFetching, + setYear, setMonth, + selectYear, + selectMonth, }), [ + year, days, title, + years, + months, weekDays, yearMonth, - weeksCount, - weeksOffset, + yearTitle, + decadeStart, + decadeTitle, isLocaleFetching, + setYear, setMonth, + selectYear, + selectMonth, ]) } diff --git a/src/helpers/requests/fetchXlsxFile.ts b/src/helpers/requests/fetchXlsxFile.ts index c1fdd7f3..71c11218 100644 --- a/src/helpers/requests/fetchXlsxFile.ts +++ b/src/helpers/requests/fetchXlsxFile.ts @@ -1,36 +1,38 @@ type FileFormat = 'xlsx' | 'csv' +const contentTypes: Record = { + csv: 'text/csv', + xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', +} + const fetchXlsxFile = async (data: any, format: FileFormat = 'xlsx') => { - const contentType = { - csv: 'text/csv', - xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - }[format] + const { Workbook } = await import('exceljs') - const { utils, write } = await import('xlsx') + const workbook = new Workbook() + const workSheet = workbook.addWorksheet('Sheet') - const workbook = utils.book_new() - const workSheet = utils.aoa_to_sheet(data) + workSheet.addRows(data) const maxWidths: number[] = [] - data.forEach((row: any) => { - row.forEach((cell: any, index: number) => { + data.forEach((row: any[]) => { + row.forEach((cell, index) => { const cellLength = (cell) ? cell.toString().length : 0 maxWidths[index] = Math.max(maxWidths[index] || 0, cellLength) }) }) - workSheet['!cols'] = maxWidths.map((width) => ({ wch: Math.max(width, 10) })) - - utils.book_append_sheet(workbook, workSheet) + maxWidths.forEach((width, index) => { + workSheet.getColumn(index + 1).width = Math.max(width, 10) + }) - const result = write(workbook, { type: 'base64', bookType: format }) + const buffer = (format === 'csv') + ? await workbook.csv.writeBuffer({ formatterOptions: { writeBOM: true } }) + : await workbook.xlsx.writeBuffer() - const blob = new Blob([ Buffer.from(result, 'base64') ], { - type: contentType, - }) + const blob = new Blob([ buffer ], { type: contentTypes[format] }) - return result ? URL.createObjectURL(blob) : '' + return URL.createObjectURL(blob) } diff --git a/src/layouts/modals/ExportRewardsModal/util/columns.ts b/src/layouts/modals/ExportRewardsModal/util/columns.ts new file mode 100644 index 00000000..dd1eea80 --- /dev/null +++ b/src/layouts/modals/ExportRewardsModal/util/columns.ts @@ -0,0 +1,15 @@ +type ExportColumn = { + key: ColumnKey + boostOnly?: boolean +} + + +export type ColumnKey = 'stake' | 'boost' | 'total' | 'totalFiat' | 'date' + +export const exportColumns: ExportColumn[] = [ + { key: 'stake', boostOnly: true }, + { key: 'boost', boostOnly: true }, + { key: 'total' }, + { key: 'totalFiat' }, + { key: 'date' }, +] diff --git a/src/layouts/modals/ExportRewardsModal/util/messages.ts b/src/layouts/modals/ExportRewardsModal/util/messages.ts index bef146ff..84e074bd 100644 --- a/src/layouts/modals/ExportRewardsModal/util/messages.ts +++ b/src/layouts/modals/ExportRewardsModal/util/messages.ts @@ -32,25 +32,43 @@ export default { }, file: { headings: { - 0: { - en: 'Reward ({token})', - ru: 'Вознаграждение ({token})', - fr: 'Récompense ({token})', - es: 'Recompensa ({token})', - pt: 'Recompensa ({token})', - de: 'Belohnung ({token})', - zh: '奖励 ({token})', + stake: { + en: 'Stake ({token})', + ru: 'Стейкинг ({token})', + fr: 'Staking ({token})', + es: 'Staking ({token})', + pt: 'Staking ({token})', + de: 'Staking ({token})', + zh: '质押 ({token})', }, - 1: { - en: 'Reward ({currency})', - ru: 'Награда ({currency})', - fr: 'Récompense ({currency})', - es: 'Recompensa ({currency})', - pt: 'Recompensa ({currency})', - de: 'Belohnung ({currency})', - zh: '奖励 ({currency})', + boost: { + en: 'Boost ({token})', + ru: 'Буст ({token})', + fr: 'Boost ({token})', + es: 'Boost ({token})', + pt: 'Boost ({token})', + de: 'Boost ({token})', + zh: 'Boost ({token})', }, - 2: { + total: { + en: 'Total ({token})', + ru: 'Всего ({token})', + fr: 'Total ({token})', + es: 'Total ({token})', + pt: 'Total ({token})', + de: 'Gesamt ({token})', + zh: '总计 ({token})', + }, + totalFiat: { + en: 'Total ({currency})', + ru: 'Всего ({currency})', + fr: 'Total ({currency})', + es: 'Total ({currency})', + pt: 'Total ({currency})', + de: 'Gesamt ({currency})', + zh: '总计 ({currency})', + }, + date: { en: 'Date (YYYY-MM-DD)', ru: 'Дата (ГГГГ-ММ-ДД)', fr: 'Date (AAAA-MM-JJ)', diff --git a/src/layouts/modals/ExportRewardsModal/util/useRewards.ts b/src/layouts/modals/ExportRewardsModal/util/useRewards.ts index df42616c..01abc9b5 100644 --- a/src/layouts/modals/ExportRewardsModal/util/useRewards.ts +++ b/src/layouts/modals/ExportRewardsModal/util/useRewards.ts @@ -6,6 +6,7 @@ import forms from 'modules/forms' import { useStore } from 'hooks' import date from 'modules/date' +import { exportColumns } from './columns' import type { ExportForm } from './useForm' @@ -24,6 +25,7 @@ type Input = { const storeSelector = (store: Store) => ({ currency: store.currency.selected, + totalBoostEarnedAssets: store.vault.user.balances.totalBoostEarnedAssets, }) const formatFiat = (value: number) => { @@ -35,9 +37,11 @@ const useRewards = (input: Input) => { const { sdk, address } = useConfig() - const { currency } = useStore(storeSelector) + const { currency, totalBoostEarnedAssets } = useStore(storeSelector) const { values: { from, to } } = forms.useFormValues(form) + const hasBoost = totalBoostEarnedAssets > 0n + return useCallback(async () => { if (!address || !from || !to) { return @@ -70,6 +74,8 @@ const useRewards = (input: Input) => { dailyRewardsJpy, dailyRewardsKrw, dailyRewardsAud, + dailyStakeRewards, + dailyBoostRewards, } = values const currentFiatValue = { @@ -85,20 +91,26 @@ const useRewards = (input: Input) => { return ({ date, value: dailyRewards, + stakeValue: dailyStakeRewards, + boostValue: dailyBoostRewards, fiatValue: currentFiatValue[currency], }) }) - const rewards = response.map(({ date: rewardsDate, value, fiatValue }) => { + const columns = exportColumns.filter((column) => hasBoost || !column.boostOnly) + + const rewards = response.map(({ date: rewardsDate, value, stakeValue, boostValue, fiatValue }) => { const formattedDate = modifiers.formatDateToNumerical(rewardsDate) - const reportDate = `${formattedDate} 00:00 UTC` - const formattedFiatValue = Number(fiatValue.replace(',', '.')) - - return [ - value, - formattedFiatValue, - reportDate, - ] + + const row = { + stake: stakeValue, + boost: boostValue, + total: value, + totalFiat: Number(fiatValue.replace(',', '.')), + date: `${formattedDate} 00:00 UTC`, + } + + return columns.map((column) => row[column.key]) }) return rewards @@ -106,7 +118,7 @@ const useRewards = (input: Input) => { catch (error: any) { console.error('Fetch user rewards fail', error) } - }, [ sdk, address, vaultAddress, currency, from, to ]) + }, [ sdk, address, vaultAddress, currency, from, to, hasBoost ]) } diff --git a/src/layouts/modals/ExportRewardsModal/util/useXLSX.ts b/src/layouts/modals/ExportRewardsModal/util/useXLSX.ts index cc64fb2d..3ab06556 100644 --- a/src/layouts/modals/ExportRewardsModal/util/useXLSX.ts +++ b/src/layouts/modals/ExportRewardsModal/util/useXLSX.ts @@ -6,6 +6,7 @@ import { useConfig } from 'config' import { useStore } from 'hooks' import type { ExportForm } from './useForm' +import { exportColumns } from './columns' import messages from './messages' @@ -16,6 +17,7 @@ type Input = { const storeSelector = (store: Store) => ({ currency: store.currency.selected, + totalBoostEarnedAssets: store.vault.user.balances.totalBoostEarnedAssets, }) const useXLSX = (input: Input) => { @@ -23,16 +25,19 @@ const useXLSX = (input: Input) => { const { sdk, address } = useConfig() const { formatMessage } = intl.useIntl() - const { currency } = useStore(storeSelector) + const { currency, totalBoostEarnedAssets } = useStore(storeSelector) const { values: { from, to, format } } = forms.useFormValues(form) - const titles = useMemo(() => ( - Object.keys(messages.file.headings).map((key) => { - const title = messages.file.headings[key as unknown as keyof typeof messages.file.headings] + const hasBoost = totalBoostEarnedAssets > 0n - return formatMessage(title, { token: sdk.config.tokens.depositToken, currency }) - }) - ), [ currency, sdk, formatMessage ]) + const titles = useMemo(() => { + const columns = exportColumns.filter((column) => hasBoost || !column.boostOnly) + + return columns.map((column) => formatMessage(messages.file.headings[column.key], { + token: sdk.config.tokens.depositToken, + currency, + })) + }, [ hasBoost, currency, sdk, formatMessage ]) return useCallback(async (data: (string | number)[][]) => { const response = await requests.fetchXlsxFile([ titles, ...data ], format)