diff --git a/package.json b/package.json index dcf66732..7e99bb36 100644 --- a/package.json +++ b/package.json @@ -170,19 +170,26 @@ "@radix-ui/react-tooltip": "^1.1.3", "@storybook/addon-actions": "8.3.5", "@tanstack/react-query": "^5.59.15", + "@types/papaparse": "^5.3.15", "@uiw/react-markdown-preview": "^5.1.3", + "@uiw/react-md-editor": "^4.0.4", "cmdk": "1.0.0", "embla-carousel-react": "^8.3.0", "graphql-request": "^7.1.0", + "idb": "^8.0.1", "input-otp": "^1.2.4", "moment": "^2.30.1", + "moment-timezone": "^0.5.46", "next-themes": "^0.3.0", + "papaparse": "^5.4.1", "react-day-picker": "9.3.2", "react-hook-form": "^7.53.0", "react-markdown": "^9.0.1", "react-resizable-panels": "^2.1.4", + "react-use": "^17.6.0", "recharts": "^2.13.0", "rehype-raw": "^7.0.0", + "rehype-sanitize": "^6.0.0", "remark-gfm": "^4.0.0", "sonner": "^1.5.0", "tailwind-variants": "^0.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10172a80..f7af4e22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -104,9 +104,15 @@ importers: '@tanstack/react-query': specifier: ^5.59.15 version: 5.62.8(react@18.3.1) + '@types/papaparse': + specifier: ^5.3.15 + version: 5.3.15 '@uiw/react-markdown-preview': specifier: ^5.1.3 version: 5.1.3(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@uiw/react-md-editor': + specifier: ^4.0.4 + version: 4.0.5(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) cmdk: specifier: 1.0.0 version: 1.0.0(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -116,15 +122,24 @@ importers: graphql-request: specifier: ^7.1.0 version: 7.1.2(graphql@16.10.0) + idb: + specifier: ^8.0.1 + version: 8.0.1 input-otp: specifier: ^1.2.4 version: 1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) moment: specifier: ^2.30.1 version: 2.30.1 + moment-timezone: + specifier: ^0.5.46 + version: 0.5.46 next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + papaparse: + specifier: ^5.4.1 + version: 5.4.1 react-day-picker: specifier: 9.3.2 version: 9.3.2(react@18.3.1) @@ -137,12 +152,18 @@ importers: react-resizable-panels: specifier: ^2.1.4 version: 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-use: + specifier: ^17.6.0 + version: 17.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) recharts: specifier: ^2.13.0 version: 2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) rehype-raw: specifier: ^7.0.0 version: 7.0.0 + rehype-sanitize: + specifier: ^6.0.0 + version: 6.0.0 remark-gfm: specifier: ^4.0.0 version: 4.0.0 @@ -2660,6 +2681,9 @@ packages: '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + '@types/js-cookie@2.2.7': + resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -2681,6 +2705,9 @@ packages: '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/papaparse@5.3.15': + resolution: {integrity: sha512-JHe6vF6x/8Z85nCX4yFdDslN11d+1pr12E526X8WAfhadOeaOTx5AuIkvDKIBopfvlzpzkdMx4YyvSKCM9oqtw==} + '@types/prismjs@1.26.5': resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} @@ -2784,6 +2811,12 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' + '@uiw/react-md-editor@4.0.5': + resolution: {integrity: sha512-x+S7ZMz1B+KwVODOZk663H2gdOZhcFsrPUF8V69K4L2BbTZ3A4sBCw/uTrBE9dHH0gajAzRAYbAYCHsUQaTcyA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + '@ungap/structured-clone@1.2.1': resolution: {integrity: sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==} @@ -2868,6 +2901,9 @@ packages: '@vue/shared@3.5.13': resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} + '@xobotyi/scrollbar-width@1.9.5': + resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} + abitype@1.0.7: resolution: {integrity: sha512-ZfYYSktDQUwc2eduYu8C4wOs+RDPmnRYMh7zNfzeMtGGgb0U+6tLGjixUic6mXf5xKKCcgT5Qp6cv39tOARVFw==} peerDependencies: @@ -3392,6 +3428,9 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} + copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -3433,9 +3472,16 @@ packages: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} engines: {node: '>=12'} + css-in-js-utils@3.1.0: + resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} + css-selector-parser@3.0.5: resolution: {integrity: sha512-3itoDFbKUNx1eKmVpYMFyqKX04Ww9osZ+dLgrk6GEv6KMVeXUhUnp4I5X+evw+u3ZxVU6RFXSSRxlTeMh8bA+g==} + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} @@ -3708,6 +3754,9 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -3886,6 +3935,12 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-shallow-equal@1.0.0: + resolution: {integrity: sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw==} + + fastest-stable-stringify@2.0.2: + resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==} + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} @@ -4187,9 +4242,15 @@ packages: hast-util-raw@9.1.0: resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + hast-util-sanitize@5.0.2: + resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==} + hast-util-select@6.0.3: resolution: {integrity: sha512-OVRQlQ1XuuLP8aFVLYmC2atrfWHS5UD3shonxpnyrjcCkwtvmt/+N6kYJdcY4mkMJhxp4kj2EFIxQ9kvkkt/eQ==} + hast-util-to-html@9.0.4: + resolution: {integrity: sha512-wxQzXtdbhiwGAUKrnQJXlOPmHnEehzphwkK7aluUPQ+lEc1xefC8pblMgpp2w5ldBTEfveRIrADcrhGIWrlTDA==} + hast-util-to-jsx-runtime@2.3.2: resolution: {integrity: sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==} @@ -4284,10 +4345,16 @@ packages: engines: {node: '>=18'} hasBin: true + hyphenate-style-name@1.1.0: + resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} + iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + idb@8.0.1: + resolution: {integrity: sha512-EkBCzUZSdhJV8PxMSbeEV//xguVKZu9hZZulM+2gHXI0t2hGVU3eYE6/XnH77DS6FM2FY8wl17aDcu9vXpvLWQ==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -4344,6 +4411,9 @@ packages: inline-style-parser@0.2.4: resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + inline-style-prefixer@7.0.1: + resolution: {integrity: sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==} + input-otp@1.4.1: resolution: {integrity: sha512-+yvpmKYKHi9jIGngxagY9oWiiblPB7+nEO75F2l2o4vs+6vpPZZmUl4tBNYuTCvQjhvEIbdNeJu70bhfYP2nbw==} peerDependencies: @@ -4696,6 +4766,9 @@ packages: joi@17.13.3: resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} + js-cookie@2.2.1: + resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4961,6 +5034,9 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + memoizerific@1.11.3: resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} @@ -5126,6 +5202,9 @@ packages: mlly@1.7.3: resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==} + moment-timezone@0.5.46: + resolution: {integrity: sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw==} + moment@2.30.1: resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} @@ -5161,6 +5240,12 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nano-css@5.6.2: + resolution: {integrity: sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw==} + peerDependencies: + react: '*' + react-dom: '*' + nanoid@3.3.8: resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -5425,6 +5510,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + papaparse@5.4.1: + resolution: {integrity: sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -5853,6 +5941,18 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' + react-universal-interface@0.6.2: + resolution: {integrity: sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==} + peerDependencies: + react: '*' + tslib: '*' + + react-use@17.6.0: + resolution: {integrity: sha512-OmedEScUMKFfzn1Ir8dBxiLLSOzhKe/dPZwVxcujweSj45aNM7BEGPb9BEVIgVEqEXx6f3/TsXzwIktNgUR02g==} + peerDependencies: + react: '*' + react-dom: '*' + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -5931,9 +6031,18 @@ packages: resolution: {integrity: sha512-rjLJ3z6fIV11phwCqHp/KRo8xuUCO8o9bFJCNw5o6O2wlLk6g8r323aRswdGBQwfXPFYeSuZdAjp4tzo6RGqEg==} engines: {node: '>=16.0.0'} + rehype-sanitize@6.0.0: + resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} + rehype-slug@6.0.0: resolution: {integrity: sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==} + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + + rehype@13.0.2: + resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} + release-zalgo@1.0.0: resolution: {integrity: sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==} engines: {node: '>=4'} @@ -5968,6 +6077,9 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} @@ -6013,6 +6125,9 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rtl-css-js@1.16.1: + resolution: {integrity: sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -6031,6 +6146,10 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + screenfull@5.2.0: + resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} + engines: {node: '>=0.10.0'} + secure-compare@3.0.1: resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} @@ -6068,6 +6187,10 @@ packages: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} + set-harmonic-interval@1.0.1: + resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} + engines: {node: '>=6.9'} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -6149,6 +6272,10 @@ packages: source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + source-map@0.5.6: + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} + engines: {node: '>=0.10.0'} + source-map@0.5.7: resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} engines: {node: '>=0.10.0'} @@ -6188,6 +6315,9 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stack-generator@2.0.10: + resolution: {integrity: sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -6195,6 +6325,15 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + stacktrace-gps@3.1.2: + resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==} + + stacktrace-js@2.0.2: + resolution: {integrity: sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -6297,6 +6436,9 @@ packages: style-to-object@1.0.8: resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==} + stylis@4.3.4: + resolution: {integrity: sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==} + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -6367,6 +6509,10 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + throttle-debounce@3.0.1: + resolution: {integrity: sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==} + engines: {node: '>=10'} + through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} @@ -6406,6 +6552,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + tough-cookie@4.1.4: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} @@ -6434,6 +6583,9 @@ packages: resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} engines: {node: '>=6.10'} + ts-easing@0.2.0: + resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} + ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -9458,6 +9610,8 @@ snapshots: dependencies: '@types/istanbul-lib-report': 3.0.3 + '@types/js-cookie@2.2.7': {} + '@types/json-schema@7.0.15': {} '@types/junit-report-builder@3.0.2': {} @@ -9476,6 +9630,10 @@ snapshots: '@types/normalize-package-data@2.4.4': {} + '@types/papaparse@5.3.15': + dependencies: + '@types/node': 22.10.2 + '@types/prismjs@1.26.5': {} '@types/prop-types@15.7.14': {} @@ -9615,6 +9773,18 @@ snapshots: - '@types/react' - supports-color + '@uiw/react-md-editor@4.0.5(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@uiw/react-markdown-preview': 5.1.3(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + rehype: 13.0.2 + rehype-prism-plus: 2.0.0 + transitivePeerDependencies: + - '@types/react' + - supports-color + '@ungap/structured-clone@1.2.1': {} '@vitejs/plugin-react-swc@3.7.2(vite@6.0.3(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))': @@ -9743,6 +9913,8 @@ snapshots: '@vue/shared@3.5.13': {} + '@xobotyi/scrollbar-width@1.9.5': {} + abitype@1.0.7(typescript@5.4.2)(zod@3.24.1): optionalDependencies: typescript: 5.4.2 @@ -10263,6 +10435,10 @@ snapshots: cookie@0.7.2: {} + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + core-util-is@1.0.3: {} corser@2.0.1: {} @@ -10312,8 +10488,17 @@ snapshots: dependencies: type-fest: 1.4.0 + css-in-js-utils@3.1.0: + dependencies: + hyphenate-style-name: 1.1.0 + css-selector-parser@3.0.5: {} + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + css.escape@1.5.1: {} cssesc@3.0.0: {} @@ -10530,6 +10715,10 @@ snapshots: dependencies: is-arrayish: 0.2.1 + error-stack-parser@2.1.4: + dependencies: + stackframe: 1.3.4 + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -10779,6 +10968,10 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-shallow-equal@1.0.0: {} + + fastest-stable-stringify@2.0.2: {} + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -11116,6 +11309,12 @@ snapshots: web-namespaces: 2.0.1 zwitch: 2.0.4 + hast-util-sanitize@5.0.2: + dependencies: + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.2.1 + unist-util-position: 5.0.0 + hast-util-select@6.0.3: dependencies: '@types/hast': 3.0.4 @@ -11134,6 +11333,20 @@ snapshots: unist-util-visit: 5.0.0 zwitch: 2.0.4 + hast-util-to-html@9.0.4: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.0 + property-information: 6.5.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + hast-util-to-jsx-runtime@2.3.2: dependencies: '@types/estree': 1.0.6 @@ -11276,10 +11489,14 @@ snapshots: husky@9.1.7: {} + hyphenate-style-name@1.1.0: {} + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 + idb@8.0.1: {} + ignore@5.3.2: {} import-fresh@3.3.0: @@ -11324,6 +11541,10 @@ snapshots: inline-style-parser@0.2.4: {} + inline-style-prefixer@7.0.1: + dependencies: + css-in-js-utils: 3.1.0 + input-otp@1.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -11880,6 +12101,8 @@ snapshots: '@sideway/formula': 3.0.1 '@sideway/pinpoint': 2.0.0 + js-cookie@2.2.1: {} + js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -12245,6 +12468,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 + mdn-data@2.0.14: {} + memoizerific@1.11.3: dependencies: map-or-similar: 1.5.0 @@ -12498,6 +12723,10 @@ snapshots: pkg-types: 1.2.1 ufo: 1.5.4 + moment-timezone@0.5.46: + dependencies: + moment: 2.30.1 + moment@2.30.1: {} ms@2.1.3: {} @@ -12544,6 +12773,19 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + nano-css@5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + css-tree: 1.1.3 + csstype: 3.1.3 + fastest-stable-stringify: 2.0.2 + inline-style-prefixer: 7.0.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + rtl-css-js: 1.16.1 + stacktrace-js: 2.0.2 + stylis: 4.3.4 + nanoid@3.3.8: {} natural-compare@1.4.0: {} @@ -12750,6 +12992,8 @@ snapshots: package-json-from-dist@1.0.1: {} + papaparse@5.4.1: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -13122,6 +13366,30 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + react-universal-interface@0.6.2(react@18.3.1)(tslib@2.8.1): + dependencies: + react: 18.3.1 + tslib: 2.8.1 + + react-use@17.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@types/js-cookie': 2.2.7 + '@xobotyi/scrollbar-width': 1.9.5 + copy-to-clipboard: 3.3.3 + fast-deep-equal: 3.1.3 + fast-shallow-equal: 1.0.0 + js-cookie: 2.2.1 + nano-css: 5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-universal-interface: 0.6.2(react@18.3.1)(tslib@2.8.1) + resize-observer-polyfill: 1.5.1 + screenfull: 5.2.0 + set-harmonic-interval: 1.0.1 + throttle-debounce: 3.0.1 + ts-easing: 0.2.0 + tslib: 2.8.1 + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -13254,6 +13522,11 @@ snapshots: unified: 11.0.5 unist-util-visit: 5.0.0 + rehype-sanitize@6.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-sanitize: 5.0.2 + rehype-slug@6.0.0: dependencies: '@types/hast': 3.0.4 @@ -13262,6 +13535,19 @@ snapshots: hast-util-to-string: 3.0.1 unist-util-visit: 5.0.0 + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.4 + unified: 11.0.5 + + rehype@13.0.2: + dependencies: + '@types/hast': 3.0.4 + rehype-parse: 9.0.1 + rehype-stringify: 10.0.1 + unified: 11.0.5 + release-zalgo@1.0.0: dependencies: es6-error: 4.1.1 @@ -13312,6 +13598,8 @@ snapshots: requires-port@1.0.0: {} + resize-observer-polyfill@1.5.1: {} + resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 @@ -13371,6 +13659,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.28.1 fsevents: 2.3.3 + rtl-css-js@1.16.1: + dependencies: + '@babel/runtime': 7.26.0 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -13389,6 +13681,8 @@ snapshots: dependencies: loose-envify: 1.4.0 + screenfull@5.2.0: {} + secure-compare@3.0.1: {} semantic-release@24.2.0(typescript@5.4.2): @@ -13451,6 +13745,8 @@ snapshots: gopd: 1.2.0 has-property-descriptors: 1.0.2 + set-harmonic-interval@1.0.1: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -13536,6 +13832,8 @@ snapshots: buffer-from: 1.1.2 source-map: 0.6.1 + source-map@0.5.6: {} + source-map@0.5.7: {} source-map@0.6.1: {} @@ -13582,12 +13880,29 @@ snapshots: sprintf-js@1.0.3: {} + stack-generator@2.0.10: + dependencies: + stackframe: 1.3.4 + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 stackback@0.0.2: {} + stackframe@1.3.4: {} + + stacktrace-gps@3.1.2: + dependencies: + source-map: 0.5.6 + stackframe: 1.3.4 + + stacktrace-js@2.0.2: + dependencies: + error-stack-parser: 2.1.4 + stack-generator: 2.0.10 + stacktrace-gps: 3.1.2 + statuses@2.0.1: {} std-env@3.8.0: {} @@ -13686,6 +14001,8 @@ snapshots: dependencies: inline-style-parser: 0.2.4 + stylis@4.3.4: {} + sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.8 @@ -13783,6 +14100,8 @@ snapshots: dependencies: any-promise: 1.3.0 + throttle-debounce@3.0.1: {} + through2@2.0.5: dependencies: readable-stream: 2.3.8 @@ -13812,6 +14131,8 @@ snapshots: dependencies: is-number: 7.0.0 + toggle-selection@1.0.6: {} + tough-cookie@4.1.4: dependencies: psl: 1.15.0 @@ -13833,6 +14154,8 @@ snapshots: ts-dedent@2.2.0: {} + ts-easing@0.2.0: {} + ts-interface-checker@0.1.13: {} ts-node@10.9.2(@swc/core@1.10.1)(@types/node@22.10.2)(typescript@5.4.2): diff --git a/src/components/Form/Form.stories.tsx b/src/components/Form/Form.stories.tsx new file mode 100644 index 00000000..14cff7b2 --- /dev/null +++ b/src/components/Form/Form.stories.tsx @@ -0,0 +1,80 @@ +import { Meta, StoryObj } from "@storybook/react"; + +import { Form, FormProps } from "@/components/Form"; +import { FormField } from "@/components/Form/types/fieldTypes"; + +const fields: FormField[] = [ + { + field: { + name: "roundName", + label: "Round name", + className: "border-grey-300", + validation: { stringValidation: { minLength: 7 } }, + }, + component: "Input", + placeholder: "your cool round name", + }, + + { + field: { + name: "roundDescription", + label: "Round description", + validation: { required: true }, + }, + component: "MarkdownEditor", + }, + { + field: { + name: "select", + label: "Select", + validation: { required: true }, + }, + component: "Select", + options: [ + { + items: [ + { label: "Apple", value: "apple" }, + { label: "Banana", value: "banana" }, + ], + }, + { + items: [ + { label: "Carrot", value: "carrot" }, + { label: "Lettuce", value: "lettuce" }, + ], + }, + ], + placeholder: "Select", + variant: "filled", + size: "md", + }, + { + field: { + name: "fileUpload", + label: "File upload", + validation: { required: true }, + }, + component: "FileUpload", + mimeTypes: ["image/png", "image/jpeg", "image/jpg", "image/webp", "image/svg+xml"], + }, +]; + +export default { + title: "Components/Form", + component: Form, +} as Meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + fields, + persistKey: "storybook-form", + }, + render: (args) => { + return ( +
+
+
+ ); + }, +}; diff --git a/src/components/Form/Form.tsx b/src/components/Form/Form.tsx new file mode 100644 index 00000000..cc2ad949 --- /dev/null +++ b/src/components/Form/Form.tsx @@ -0,0 +1,83 @@ +import { ForwardedRef, forwardRef, useImperativeHandle } from "react"; +import { FormProvider, useFormContext } from "react-hook-form"; + +import { useFormWithPersist } from "@/hooks"; + +import { FormField } from "./types/fieldTypes"; +import { buildSchemaFromFields } from "./utils/buildSchemaFromFields"; +import { componentRegistry } from "./utils/componentRegistry"; + +export interface FormProps { + persistKey: string; + fields: FormField[]; + defaultValues?: any; + dbName: string; + storeName: string; +} + +export const Form = forwardRef(function Form( + { persistKey, fields, defaultValues, dbName, storeName }: FormProps, + ref: ForwardedRef<{ isFormValid: () => Promise }>, +) { + const schema = buildSchemaFromFields(fields); + + const form = useFormWithPersist({ schema, defaultValues, persistKey, dbName, storeName }); + + useImperativeHandle(ref, () => ({ + isFormValid: async () => { + try { + const isValid = await form.trigger(); // Trigger validation on all fields + if (!isValid) return false; + + return true; + } catch (error) { + console.error(error); + return false; + } + }, + })); + + return ( + + void 0)} className="flex flex-col gap-4"> + {fields.map((field) => ( + + ))} + + + ); +}); +function FormControl({ field }: { field: FormField }) { + const { + register, + formState: { errors }, + control, + } = useFormContext(); + + type FieldOfType = Extract; + const { field: fieldProps, ...componentProps } = field as FieldOfType; + + const registryEntry = componentRegistry[field.component]; + const { Component, isControlled } = registryEntry; + + const props = isControlled + ? { ...componentProps, name: fieldProps.name, control } + : { ...componentProps, ...register(fieldProps.name) }; + + return ( +
+
+ + {fieldProps.validation?.required && ( +
*Required
+ )} +
+ + {errors[fieldProps.name]?.message && ( +

{String(errors[fieldProps.name]?.message)}

+ )} +
+ ); +} diff --git a/src/components/Form/FormControllers/AllowlistFormController/AllowlistFormController.tsx b/src/components/Form/FormControllers/AllowlistFormController/AllowlistFormController.tsx new file mode 100644 index 00000000..9dcb1150 --- /dev/null +++ b/src/components/Form/FormControllers/AllowlistFormController/AllowlistFormController.tsx @@ -0,0 +1,100 @@ +// src/components/Form/FormAllowlistController.tsx +import React, { useRef } from "react"; +import { useFormContext, Controller } from "react-hook-form"; + +import { UploadIcon } from "@heroicons/react/solid"; +import Papa from "papaparse"; +import { getAddress, isAddress } from "viem"; + +import { Button } from "@/primitives/Button"; +import { TextArea } from "@/primitives/TextArea"; + +export interface AllowlistFormControllerProps { + /** The name of the form field */ + name: string; +} + +export const AllowlistFormController: React.FC = ({ name }) => { + const { control, setValue } = useFormContext(); + const fileInputRef = useRef(null); + + /** + * Handles CSV file upload and extracts valid addresses + * @param event - The input change event + */ + const handleCSVUpload = (event: React.ChangeEvent): void => { + const file = event.target.files?.[0]; + if (!file) return; + + Papa.parse(file, { + complete: ({ data }) => { + const uniqueAddresses = Array.from( + new Set( + data + .flat() + .filter((item): item is string => typeof item === "string" && isAddress(item.trim())), + ), + ).join(","); + + const formattedAddresses = uniqueAddresses.split(",").map((addr) => getAddress(addr)); + + setValue(name, formattedAddresses); + }, + skipEmptyLines: true, + }); + + // Reset the input value to allow re-uploading the same file if needed + event.target.value = ""; + }; + + return ( +
+
+
+ + ( +