diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 52ff91b..ddc757b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ importers: '@mui/x-date-pickers': specifier: ^7.6.2 version: 7.22.1(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.7(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(react@18.3.1))(@types/react@18.3.12)(date-fns@3.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-pdf/renderer': + specifier: ^4.1.4 + version: 4.1.5(react@18.3.1) '@tanstack/react-query': specifier: ^5.45.0 version: 5.59.16(react@18.3.1) @@ -889,6 +892,49 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + '@react-pdf/fns@3.0.0': + resolution: {integrity: sha512-ICbIWR93PE6+xf2Xd/fXYO1dAuiOAJaszEuGGv3wp5lLSeeelDXlEYLh6R05okxh28YqMzc0Qd85x6n6MtaLUQ==} + + '@react-pdf/font@3.0.1': + resolution: {integrity: sha512-s+0xrQabGoYDDZwVpz8PXp1ylwabqiMhzfyetvxBqjDuQ15PuoSkmUkKUOkfDzauuAqs0MLMvt+Pcv+NioLfzw==} + + '@react-pdf/image@3.0.1': + resolution: {integrity: sha512-Hd5F1LzjuzG4bL/ytaOYxwN/5ip8oFBYDHdpccOfYY87J/Ca7AL31SsuneLk9DtnwNM1BSAKXtBo/WDFY3r57A==} + + '@react-pdf/layout@4.1.3': + resolution: {integrity: sha512-EvIRg/QGACGyDB/+N6OCpaxBg4r3dTF1sXEQev9yrunq+f5HpoGsYEGt+wC7I63Gbza+k2/+NqJitlsDRrt9nA==} + + '@react-pdf/pdfkit@4.0.0': + resolution: {integrity: sha512-HaaAoBpoRGJ6c1ZOANNQZ3q6Ehmagqa8n40x+OZ5s9HcmUviZ34SCm+QBa42s1o4299M+Lgw3UoqpW7sHv3/Hg==} + + '@react-pdf/png-js@3.0.0': + resolution: {integrity: sha512-eSJnEItZ37WPt6Qv5pncQDxLJRK15eaRwPT+gZoujP548CodenOVp49GST8XJvKMFt9YqIBzGBV/j9AgrOQzVA==} + + '@react-pdf/primitives@4.0.0': + resolution: {integrity: sha512-yp4E0rDL03NaUp/CnDBz3HQNfH2Mzdlgku57yhTMGNzetwB0NJusXcjYg5XsTGIXnR7Tv80JKI4O4ajj+oaLeQ==} + + '@react-pdf/reconciler@1.1.3': + resolution: {integrity: sha512-4vqY0klmUH32kTFvuqdAszkOpwfZYKMLO4VpJ5xZWTsoUOLQSyhC2QM2QCj9eaxpB2Nd5Kl9uW+KfyutvZnMzQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@react-pdf/render@4.0.2': + resolution: {integrity: sha512-5QJB9sS0uU5ALTLxrtT073VT1imZhrzuOun+7kvo0nykeAr9I4lv0Shmy8rS4QhpmXn8ASmhd17WjCVm4DcJlw==} + + '@react-pdf/renderer@4.1.5': + resolution: {integrity: sha512-SGaaVloGtBNCsgu7TrS9C8QrcMegqEzgaw2Y/DnpZJtru13WgBs+auM9ana58ytQ7PIMB1RYJtFVvYhM1hf/+w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@react-pdf/stylesheet@5.1.0': + resolution: {integrity: sha512-QOPCGzlTz+irGLXbsQtKkEqkf78n9l3hRU0Omkvpk/gwLf/IchBBt358dV6FEtc+ujMFEpN+a+fWSS6v2tB0AQ==} + + '@react-pdf/textkit@5.0.1': + resolution: {integrity: sha512-4GdDiPA9l+If203hkh48slvRQmcmM3ecPLFTpXNMPrep/3retgvxUEXKMxI+xKclpw8tMzK/W6Z4hN9DgnxWMg==} + + '@react-pdf/types@2.7.0': + resolution: {integrity: sha512-7KrPPCpgRPKR+g+T127PE4bpw9Q84ZiY07EYRwXKVtTEVW9wJ5BZiF9smT9IvH19s+MQaDLmYRgjESsnqlyH0Q==} + '@react-spring/animated@9.7.5': resolution: {integrity: sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==} peerDependencies: @@ -1026,6 +1072,9 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@tanstack/match-sorter-utils@8.19.4': resolution: {integrity: sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==} engines: {node: '>=12'} @@ -1209,6 +1258,9 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 + abs-svg-path@0.1.1: + resolution: {integrity: sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -1337,6 +1389,12 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} @@ -1364,6 +1422,12 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + brotli@1.3.3: + resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + browserslist@4.24.2: resolution: {integrity: sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1416,6 +1480,10 @@ packages: resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} engines: {node: '>= 10.0'} + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} @@ -1427,6 +1495,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -1495,6 +1566,9 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + css-color-keywords@1.0.0: resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} engines: {node: '>=4'} @@ -1641,6 +1715,9 @@ packages: engines: {node: '>=0.10'} hasBin: true + dfa@1.2.0: + resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} + diacritics@1.3.0: resolution: {integrity: sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==} @@ -1703,6 +1780,9 @@ packages: electron-to-chromium@1.5.50: resolution: {integrity: sha512-eMVObiUQ2LdgeO1F/ySTXsvqvxb6ZH2zPGaMYsWzRDdOddUa77tdmI0ltg+L16UpbWdhPmuF3wIQYyQq65WfZw==} + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1929,6 +2009,10 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + express@4.21.1: resolution: {integrity: sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==} engines: {node: '>= 0.10.0'} @@ -1990,6 +2074,9 @@ packages: debug: optional: true + fontkit@2.0.4: + resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==} + for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -2129,6 +2216,12 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hsl-to-hex@1.0.0: + resolution: {integrity: sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==} + + hsl-to-rgb-for-reals@1.1.1: + resolution: {integrity: sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==} + html-minifier-terser@6.1.0: resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} engines: {node: '>=12'} @@ -2141,6 +2234,9 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + hyphen@1.10.6: + resolution: {integrity: sha512-fXHXcGFTXOvZTSkPJuGOQf5Lv5T/R2itiiCVPg9LxAje5D00O0pP83yJShFq5V89Ly//Gt6acj7z8pbBr34stw==} + hyphenate-style-name@1.1.0: resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} @@ -2199,6 +2295,9 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + is-async-function@2.0.0: resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} engines: {node: '>= 0.4'} @@ -2304,6 +2403,9 @@ packages: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -2333,6 +2435,9 @@ packages: engines: {node: '>=10'} hasBin: true + jay-peg@1.1.0: + resolution: {integrity: sha512-WhyKySfx5CEFoKDnpmHyJUrpX5fUrr/X3kqVHISmiO9jrJC73RQBOAZJB8bDrWT4PHEkl0QgNZLlWJfAWAIFew==} + jiti@1.21.6: resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true @@ -2475,6 +2580,9 @@ packages: react: '>=17.0' react-dom: '>=17.0' + media-engine@1.0.3: + resolution: {integrity: sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -2571,6 +2679,9 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-svg-path@1.1.0: + resolution: {integrity: sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==} + normalize-url@1.9.1: resolution: {integrity: sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==} engines: {node: '>=4'} @@ -2640,6 +2751,12 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + pako@0.2.9: + resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + papaparse@5.4.1: resolution: {integrity: sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==} @@ -2654,6 +2771,9 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-svg-path@0.1.2: + resolution: {integrity: sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==} + parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -2812,6 +2932,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + queue@6.0.2: + resolution: {integrity: sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -2922,6 +3045,10 @@ packages: remove-accents@0.5.0: resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2934,6 +3061,9 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + restructure@3.0.2: + resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} + reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -2976,6 +3106,9 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + scheduler@0.25.0-rc-603e6108-20241029: + resolution: {integrity: sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -3033,6 +3166,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -3097,6 +3233,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3139,6 +3278,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svg-arc-to-cubic-bezier@3.2.0: + resolution: {integrity: sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==} + synckit@0.8.8: resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -3166,6 +3308,9 @@ packages: tiny-case@1.0.3: resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + tiny-warning@1.0.3: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} @@ -3254,6 +3399,12 @@ packages: undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + unicode-properties@1.4.1: + resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} + + unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -3286,6 +3437,10 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vite-compatible-readable-stream@3.6.1: + resolution: {integrity: sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==} + engines: {node: '>= 6'} + vite-plugin-html@3.2.2: resolution: {integrity: sha512-vb9C9kcdzcIo/Oc3CLZVS03dL5pDlOFuhGlZYDCJ840BhWl/0nGeZWf3Qy7NlOayscY4Cm/QRgULCQkEZige5Q==} peerDependencies: @@ -3391,6 +3546,9 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yoga-layout@3.1.0: + resolution: {integrity: sha512-auzJ8lEovThZIpR8wLGWNo/JEj4VTO79q9/gOJ0dWb3shAYPFdX3t9VN0fC0v+jeQF77STUdCzebLwRMqzn5gQ==} + yup@1.4.0: resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==} @@ -4095,6 +4253,108 @@ snapshots: '@popperjs/core@2.11.8': {} + '@react-pdf/fns@3.0.0': + dependencies: + '@babel/runtime': 7.26.0 + + '@react-pdf/font@3.0.1': + dependencies: + '@babel/runtime': 7.26.0 + '@react-pdf/types': 2.7.0 + fontkit: 2.0.4 + is-url: 1.2.4 + + '@react-pdf/image@3.0.1': + dependencies: + '@babel/runtime': 7.26.0 + '@react-pdf/png-js': 3.0.0 + jay-peg: 1.1.0 + + '@react-pdf/layout@4.1.3': + dependencies: + '@babel/runtime': 7.26.0 + '@react-pdf/fns': 3.0.0 + '@react-pdf/image': 3.0.1 + '@react-pdf/pdfkit': 4.0.0 + '@react-pdf/primitives': 4.0.0 + '@react-pdf/stylesheet': 5.1.0 + '@react-pdf/textkit': 5.0.1 + '@react-pdf/types': 2.7.0 + emoji-regex: 10.4.0 + queue: 6.0.2 + yoga-layout: 3.1.0 + + '@react-pdf/pdfkit@4.0.0': + dependencies: + '@babel/runtime': 7.26.0 + '@react-pdf/png-js': 3.0.0 + browserify-zlib: 0.2.0 + crypto-js: 4.2.0 + fontkit: 2.0.4 + jay-peg: 1.1.0 + vite-compatible-readable-stream: 3.6.1 + + '@react-pdf/png-js@3.0.0': + dependencies: + browserify-zlib: 0.2.0 + + '@react-pdf/primitives@4.0.0': {} + + '@react-pdf/reconciler@1.1.3(react@18.3.1)': + dependencies: + object-assign: 4.1.1 + react: 18.3.1 + scheduler: 0.25.0-rc-603e6108-20241029 + + '@react-pdf/render@4.0.2': + dependencies: + '@babel/runtime': 7.26.0 + '@react-pdf/fns': 3.0.0 + '@react-pdf/primitives': 4.0.0 + '@react-pdf/textkit': 5.0.1 + '@react-pdf/types': 2.7.0 + abs-svg-path: 0.1.1 + color-string: 1.9.1 + normalize-svg-path: 1.1.0 + parse-svg-path: 0.1.2 + svg-arc-to-cubic-bezier: 3.2.0 + + '@react-pdf/renderer@4.1.5(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@react-pdf/font': 3.0.1 + '@react-pdf/layout': 4.1.3 + '@react-pdf/pdfkit': 4.0.0 + '@react-pdf/primitives': 4.0.0 + '@react-pdf/reconciler': 1.1.3(react@18.3.1) + '@react-pdf/render': 4.0.2 + '@react-pdf/types': 2.7.0 + events: 3.3.0 + object-assign: 4.1.1 + prop-types: 15.8.1 + queue: 6.0.2 + react: 18.3.1 + + '@react-pdf/stylesheet@5.1.0': + dependencies: + '@babel/runtime': 7.26.0 + '@react-pdf/fns': 3.0.0 + '@react-pdf/types': 2.7.0 + color-string: 1.9.1 + hsl-to-hex: 1.0.0 + media-engine: 1.0.3 + postcss-value-parser: 4.2.0 + + '@react-pdf/textkit@5.0.1': + dependencies: + '@babel/runtime': 7.26.0 + '@react-pdf/fns': 3.0.0 + bidi-js: 1.0.3 + hyphen: 1.10.6 + unicode-properties: 1.4.1 + + '@react-pdf/types@2.7.0': {} + '@react-spring/animated@9.7.5(react@18.3.1)': dependencies: '@react-spring/shared': 9.7.5(react@18.3.1) @@ -4194,6 +4454,10 @@ snapshots: '@rtsao/scc@1.1.0': {} + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + '@tanstack/match-sorter-utils@8.19.4': dependencies: remove-accents: 0.5.0 @@ -4411,6 +4675,8 @@ snapshots: transitivePeerDependencies: - supports-color + abs-svg-path@0.1.1: {} + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -4559,6 +4825,12 @@ snapshots: balanced-match@1.0.2: {} + base64-js@1.5.1: {} + + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + big.js@5.2.2: {} big.js@6.2.2: {} @@ -4597,6 +4869,14 @@ snapshots: dependencies: fill-range: 7.1.1 + brotli@1.3.3: + dependencies: + base64-js: 1.5.1 + + browserify-zlib@0.2.0: + dependencies: + pako: 1.0.11 + browserslist@4.24.2: dependencies: caniuse-lite: 1.0.30001676 @@ -4656,6 +4936,8 @@ snapshots: dependencies: source-map: 0.6.1 + clone@2.1.2: {} + clsx@2.1.1: {} color-convert@2.0.1: @@ -4664,6 +4946,11 @@ snapshots: color-name@1.1.4: {} + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + colorette@2.0.20: {} combined-stream@1.0.8: @@ -4722,6 +5009,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crypto-js@4.2.0: {} + css-color-keywords@1.0.0: {} css-hot-loader@1.4.4: @@ -4855,6 +5144,8 @@ snapshots: detect-libc@1.0.3: {} + dfa@1.2.0: {} + diacritics@1.3.0: {} didyoumean@1.2.2: {} @@ -4915,6 +5206,8 @@ snapshots: electron-to-chromium@1.5.50: {} + emoji-regex@10.4.0: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -5275,6 +5568,8 @@ snapshots: etag@1.8.1: {} + events@3.3.0: {} + express@4.21.1: dependencies: accepts: 1.3.8 @@ -5372,6 +5667,18 @@ snapshots: follow-redirects@1.15.9: {} + fontkit@2.0.4: + dependencies: + '@swc/helpers': 0.5.15 + brotli: 1.3.3 + clone: 2.1.2 + dfa: 1.2.0 + fast-deep-equal: 3.1.3 + restructure: 3.0.2 + tiny-inflate: 1.0.3 + unicode-properties: 1.4.1 + unicode-trie: 2.0.0 + for-each@0.3.3: dependencies: is-callable: 1.2.7 @@ -5525,6 +5832,12 @@ snapshots: dependencies: react-is: 16.13.1 + hsl-to-hex@1.0.0: + dependencies: + hsl-to-rgb-for-reals: 1.1.1 + + hsl-to-rgb-for-reals@1.1.1: {} + html-minifier-terser@6.1.0: dependencies: camel-case: 4.1.2 @@ -5547,6 +5860,8 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + hyphen@1.10.6: {} + hyphenate-style-name@1.1.0: {} i18n-iso-countries@7.12.0: @@ -5602,6 +5917,8 @@ snapshots: is-arrayish@0.2.1: {} + is-arrayish@0.3.2: {} + is-async-function@2.0.0: dependencies: has-tostringtag: 1.0.2 @@ -5690,6 +6007,8 @@ snapshots: dependencies: which-typed-array: 1.1.15 + is-url@1.2.4: {} + is-weakmap@2.0.2: {} is-weakref@1.0.2: @@ -5726,6 +6045,10 @@ snapshots: filelist: 1.0.4 minimatch: 3.1.2 + jay-peg@1.1.0: + dependencies: + restructure: 3.0.2 + jiti@1.21.6: {} js-tokens@4.0.0: {} @@ -5878,6 +6201,8 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + media-engine@1.0.3: {} + media-typer@0.3.0: {} merge-descriptors@1.0.3: {} @@ -5951,6 +6276,10 @@ snapshots: normalize-path@3.0.0: {} + normalize-svg-path@1.1.0: + dependencies: + svg-arc-to-cubic-bezier: 3.2.0 + normalize-url@1.9.1: dependencies: object-assign: 4.1.1 @@ -6033,6 +6362,10 @@ snapshots: package-json-from-dist@1.0.1: {} + pako@0.2.9: {} + + pako@1.0.11: {} + papaparse@5.4.1: {} param-case@3.0.4: @@ -6051,6 +6384,8 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-svg-path@0.1.2: {} + parseurl@1.3.3: {} pascal-case@3.1.2: @@ -6173,6 +6508,10 @@ snapshots: queue-microtask@1.2.3: {} + queue@6.0.2: + dependencies: + inherits: 2.0.4 + range-parser@1.2.1: {} raw-body@2.5.2: @@ -6282,6 +6621,8 @@ snapshots: remove-accents@0.5.0: {} + require-from-string@2.0.2: {} + resolve-from@4.0.0: {} resolve@1.22.8: @@ -6296,6 +6637,8 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + restructure@3.0.2: {} + reusify@1.0.4: {} rimraf@3.0.2: @@ -6360,6 +6703,8 @@ snapshots: dependencies: loose-envify: 1.4.0 + scheduler@0.25.0-rc-603e6108-20241029: {} + semver@6.3.1: {} semver@7.6.3: {} @@ -6432,6 +6777,10 @@ snapshots: signal-exit@4.1.0: {} + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + slash@3.0.0: {} solid-js@1.9.3: @@ -6516,6 +6865,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -6562,6 +6915,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svg-arc-to-cubic-bezier@3.2.0: {} + synckit@0.8.8: dependencies: '@pkgr/core': 0.1.1 @@ -6613,6 +6968,8 @@ snapshots: tiny-case@1.0.3: {} + tiny-inflate@1.0.3: {} + tiny-warning@1.0.3: {} to-regex-range@5.0.1: @@ -6702,6 +7059,16 @@ snapshots: undici-types@6.19.8: {} + unicode-properties@1.4.1: + dependencies: + base64-js: 1.5.1 + unicode-trie: 2.0.0 + + unicode-trie@2.0.0: + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + universalify@2.0.1: {} unpipe@1.0.0: {} @@ -6724,6 +7091,12 @@ snapshots: vary@1.1.2: {} + vite-compatible-readable-stream@3.6.1: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + vite-plugin-html@3.2.2(vite@5.4.10(@types/node@20.17.5)(sass@1.80.5)(terser@5.36.0)): dependencies: '@rollup/pluginutils': 4.2.1 @@ -6837,6 +7210,8 @@ snapshots: yocto-queue@0.1.0: {} + yoga-layout@3.1.0: {} + yup@1.4.0: dependencies: property-expr: 2.0.6 diff --git a/src/pages/Deployment/InformedConsentCard/index.tsx b/src/pages/Deployment/InformedConsentCard/index.tsx index 0ec8774..b14cc39 100644 --- a/src/pages/Deployment/InformedConsentCard/index.tsx +++ b/src/pages/Deployment/InformedConsentCard/index.tsx @@ -14,6 +14,7 @@ import { useEffect, useState } from "react"; import { InformedConsent } from "@carp-dk/client/models/InputDataTypes"; import FileDownloadOutlinedIcon from "@mui/icons-material/FileDownloadOutlined"; import { convertICToReactPdf, formatDateTime } from "@Utils/utility"; +import { pdf } from "@react-pdf/renderer"; import { DownloadButton, LastUploadText, @@ -24,12 +25,6 @@ import { } from "./styles"; import LoadingSkeleton from "../LoadingSkeleton"; -interface FileInfo { - data: string; - fileName: string; - fileType: string; -} - const InformedConsentCard = () => { const { t } = useTranslation(); const { id: studyId, deploymentId } = useParams(); @@ -48,10 +43,12 @@ const InformedConsentCard = () => { const [consents, setConsents] = useState<{ participant: ParticipantData; consent: InformedConsent }[]>(); - const downloadFile = ({ data, fileName, fileType }: FileInfo) => { - const blob = new Blob([data], { type: fileType }); + const downloadPdf = async (consent: InformedConsent) => { + const blob = await pdf( + await convertICToReactPdf(JSON.parse(consent.consent)), + ).toBlob(); const a = document.createElement("a"); - a.download = fileName; + a.download = "informedConsent.pdf"; a.href = window.URL.createObjectURL(blob); const clickEvt = new MouseEvent("click", { view: window, @@ -62,19 +59,6 @@ const InformedConsentCard = () => { a.remove(); }; - const exportToJson = ( - e: React.MouseEvent, - participantId: string, - consent: InformedConsent, - ) => { - e.preventDefault(); - downloadFile({ - data: JSON.stringify(consent), - fileName: `${participantId}_informedConsent.json`, - fileType: "text/json", - }); - }; - useEffect(() => { if (statuses && participantData) { const participantGroup = statuses.groups.find( @@ -157,10 +141,7 @@ const InformedConsentCard = () => { })} - + downloadPdf(consent)}> {t("common:export_data")} diff --git a/src/pages/Deployment/InformedConsentCard/styles.ts b/src/pages/Deployment/InformedConsentCard/styles.ts index b239b7a..7606fdb 100644 --- a/src/pages/Deployment/InformedConsentCard/styles.ts +++ b/src/pages/Deployment/InformedConsentCard/styles.ts @@ -4,8 +4,8 @@ import { Accordion, AccordionSummary, Stack, + Button, } from "@mui/material"; -import { PDFDownloadLink } from "@react-pdf/renderer"; import { styled } from "@Utils/theme"; export const StyledAccordion = styled(Accordion)(({ expanded }) => ({ @@ -66,7 +66,7 @@ export const NameContainer = styled("div")({ gap: 6, }); -export const DownloadButton = styled(PDFDownloadLink)(({ theme }) => ({ +export const DownloadButton = styled(Button)(({ theme }) => ({ display: "flex", alignItems: "center", height: "36px", @@ -76,7 +76,7 @@ export const DownloadButton = styled(PDFDownloadLink)(({ theme }) => ({ borderColor: theme.palette.grey[700], borderRadius: 16, cursor: "pointer", - textDecoration: "none", + textTransform: "none", padding: "8px 16px", gap: 8, })); diff --git a/src/pages/Participant/InformedConsent/index.tsx b/src/pages/Participant/InformedConsent/index.tsx index 84464d3..9d7e0c3 100644 --- a/src/pages/Participant/InformedConsent/index.tsx +++ b/src/pages/Participant/InformedConsent/index.tsx @@ -9,6 +9,7 @@ import { useGetParticipantData, useParticipantGroupsAccountsAndStatus, } from "@Utils/queries/participants"; +import { pdf } from "@react-pdf/renderer"; import LoadingSkeleton from "../LoadingSkeleton"; import { DownloadButton, @@ -34,6 +35,22 @@ const InformedConsent = () => { error: participantGroupStatusError, } = useParticipantGroupsAccountsAndStatus(studyId); + const downloadPdf = async () => { + const blob = await pdf( + await convertICToReactPdf(JSON.parse(consent.consent)), + ).toBlob(); + const a = document.createElement("a"); + a.download = "informedConsent.pdf"; + a.href = window.URL.createObjectURL(blob); + const clickEvt = new MouseEvent("click", { + view: window, + bubbles: true, + cancelable: true, + }); + a.dispatchEvent(clickEvt); + a.remove(); + }; + useEffect(() => { if (!isLoading && !participantGroupStatusLoading) { const participant = participantGroupStatus.groups @@ -96,10 +113,7 @@ const InformedConsent = () => { {consent && ( <> - + downloadPdf()}> Export diff --git a/src/pages/Participant/InformedConsent/styles.ts b/src/pages/Participant/InformedConsent/styles.ts index c4cb19a..04da9c9 100644 --- a/src/pages/Participant/InformedConsent/styles.ts +++ b/src/pages/Participant/InformedConsent/styles.ts @@ -1,5 +1,4 @@ -import { Card, Divider, Typography } from "@mui/material"; -import { PDFDownloadLink } from "@react-pdf/renderer"; +import { Button, Card, Divider, Typography } from "@mui/material"; import { styled } from "@Utils/theme"; export const StyledCard = styled(Card)({ @@ -28,14 +27,14 @@ export const StyledDivider = styled(Divider)(({ theme }) => ({ height: 20, })); -export const DownloadButton = styled(PDFDownloadLink)(({ theme }) => ({ +export const DownloadButton = styled(Button)(({ theme }) => ({ display: "flex", alignItems: "center", color: theme.palette.primary.main, backgroundColor: "transparent", border: "none", cursor: "pointer", - textDecoration: "none", + textTransform: "none", gap: 4, })); diff --git a/src/utils/utility.tsx b/src/utils/utility.tsx index 23dfda8..b939a97 100644 --- a/src/utils/utility.tsx +++ b/src/utils/utility.tsx @@ -18,7 +18,13 @@ import RadioButtonCheckedIcon from "@mui/icons-material/RadioButtonChecked"; import SmartphoneIcon from "@mui/icons-material/Smartphone"; import TimelineRoundedIcon from "@mui/icons-material/TimelineRounded"; import WatchRoundedIcon from "@mui/icons-material/WatchRounded"; -import { Document, Page, Text, StyleSheet } from "@react-pdf/renderer"; +import { + Document, + Page, + Text, + StyleSheet, + Image as PdfImage, +} from "@react-pdf/renderer"; import getSerializer = kotlinx.serialization.getSerializer; import DefaultSerializer = carpCommon.dk.cachet.carp.common.infrastructure.serialization.JSON; @@ -378,6 +384,18 @@ const styles = StyleSheet.create({ fontWeight: 300, fontFamily: "Times-Italic", }, + date: { + margin: "0 12 12 12", + fontSize: 12, + textAlign: "justify", + fontWeight: 300, + }, + signatureName: { + margin: "6 12 6 12", + fontSize: 12, + textAlign: "justify", + fontWeight: 300, + }, pageNumber: { position: "absolute", fontSize: 12, @@ -389,7 +407,33 @@ const styles = StyleSheet.create({ }, }); -export const convertICToReactPdf = (consent: ConsentObject) => { +export const convertByteArrayToImage = async (byteArray: number[]) => { + const imageByteArray = new Uint8Array(byteArray); + const imageBlob = new Blob([imageByteArray], { type: "image/png" }); + const imageBitmap = await createImageBitmap(imageBlob); + + const canvas = document.createElement("canvas"); + canvas.width = imageBitmap.width; + canvas.height = imageBitmap.height; + + const ctx = canvas.getContext("2d"); + + ctx.drawImage(imageBitmap, 0, 0); + + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + + const { data } = imageData; + for (let i = 0; i < data.length; i += 4) { + data[i] = 0; + data[i + 1] = 0; + data[i + 2] = 0; + } + ctx.putImageData(imageData, 0, 0); + + return canvas.toDataURL("image/png"); +}; + +export const convertICToReactPdf = async (consent: ConsentObject) => { return ( @@ -405,6 +449,22 @@ export const convertICToReactPdf = (consent: ConsentObject) => { ); })} + + {`${consent.signature.firstName} ${consent.signature.lastName}`} + + {formatDateTime(consent.endDate, { + year: "numeric", + month: "long", + day: "2-digit", + })} +