Merge pull request #28 from WIT-LAB-LLC/revert-27-revert-26-payment

Revert "Revert "Payment""
This commit is contained in:
pennyteenycat 2025-10-07 00:48:35 +02:00 committed by GitHub
commit 60ef9d18d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
70 changed files with 5951 additions and 599 deletions

26
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "0.1.0",
"dependencies": {
"@hookform/resolvers": "^5.2.2",
"@lottiefiles/dotlottie-react": "^0.17.4",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-checkbox": "^1.3.3",
@ -21,6 +22,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dotenv": "^17.2.2",
"idb": "^8.0.3",
"lucide-react": "^0.544.0",
"mongoose": "^8.18.2",
"next": "15.5.3",
@ -1711,6 +1713,24 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@lottiefiles/dotlottie-react": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-react/-/dotlottie-react-0.17.4.tgz",
"integrity": "sha512-PsWq0l+Q/sGwnjWMiRJC1GUmsXFYB8zc5TacWblfaU9EQzqJzBeblk5rqtac/EDQi9QiXqpojPgWsofJX97swg==",
"license": "MIT",
"dependencies": {
"@lottiefiles/dotlottie-web": "0.54.0"
},
"peerDependencies": {
"react": "^17 || ^18 || ^19"
}
},
"node_modules/@lottiefiles/dotlottie-web": {
"version": "0.54.0",
"resolved": "https://registry.npmjs.org/@lottiefiles/dotlottie-web/-/dotlottie-web-0.54.0.tgz",
"integrity": "sha512-Jc/n4i9siOXo9/1CVhKkrWC8pxxsKqKwxYfrL4DFQP/cLUAeAO0TqFPQFx9Klh1m7T+/1RPFriycOcF8gW3ZtQ==",
"license": "MIT"
},
"node_modules/@mdx-js/react": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz",
@ -7510,6 +7530,12 @@
"dev": true,
"license": "MIT"
},
"node_modules/idb": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/idb/-/idb-8.0.3.tgz",
"integrity": "sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==",
"license": "ISC"
},
"node_modules/ignore": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",

View File

@ -21,6 +21,7 @@
},
"dependencies": {
"@hookform/resolvers": "^5.2.2",
"@lottiefiles/dotlottie-react": "^0.17.4",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-checkbox": "^1.3.3",
@ -33,6 +34,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dotenv": "^17.2.2",
"idb": "^8.0.3",
"lucide-react": "^0.544.0",
"mongoose": "^8.18.2",
"next": "15.5.3",

View File

@ -0,0 +1,522 @@
<svg width="252" height="168" viewBox="0 0 252 168" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M200.799 116.696C201.232 116.893 219.738 107.804 231.881 116.696C244.025 125.587 234.195 139.503 227.399 142.98C227.399 142.98 234.339 142.684 239.543 146.383H184.609L200.802 116.696H200.799Z" fill="url(#paint0_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.5" d="M200.799 116.696C201.232 116.893 219.738 107.804 231.881 116.696C244.025 125.587 234.195 139.503 227.399 142.98C227.399 142.98 234.339 142.684 239.543 146.383H184.609L200.802 116.696H200.799Z" fill="url(#paint1_linear_36_5452)"/>
<path d="M108.421 128.187C108.743 127.928 75.9896 105.555 52.5687 101.441C29.1478 97.3257 31.9679 124.208 42.1608 129.899C42.1608 129.899 38.4744 133.851 42.3789 137.113C42.3789 137.113 39.5588 142.549 30.8865 137.113C22.2143 131.677 9.85254 138.776 13.757 146.313H131.086L108.421 128.187Z" fill="url(#paint2_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.5" d="M108.421 128.187C108.743 127.928 75.9896 105.555 52.5687 101.441C29.1478 97.3257 31.9679 124.208 42.1608 129.899C42.1608 129.899 38.4744 133.851 42.3789 137.113C42.3789 137.113 39.5588 142.549 30.8865 137.113C22.2143 131.677 9.85254 138.776 13.757 146.313H131.086L108.421 128.187Z" fill="url(#paint3_linear_36_5452)"/>
<path d="M200.227 146.313C200.227 146.313 223.759 133.344 221.261 115.588C218.552 96.3224 203.177 102.433 203.177 102.433L200.227 65.5412C200.227 65.5412 203.376 62.9263 201.985 58.4788C200.83 54.7859 196.384 56.4953 198.08 50.1475C199.96 43.119 196.477 27.0631 177.215 39.3861C177.215 39.3861 170.193 21.667 150.037 21.5592C129.882 21.4514 136.06 36.2352 136.06 36.2352C136.06 36.2352 127.206 33.953 124.804 49.6054L115.677 54.1699C115.677 54.1699 110.906 47.9022 101.076 47.6127C91.2452 47.3232 86.6403 55.2941 86.1949 60.8011C86.1949 60.8011 73.4799 57.7581 62.2701 62.107C51.0604 66.4559 42.9595 77.8549 45.5124 94.0063C45.5124 94.0063 37.4822 90.683 37.4668 99.1529C37.4607 103.745 46.4309 110.715 47.8747 113.441C49.3186 116.167 43.8996 123.851 52.6824 128.089C61.4653 132.327 64.5004 130.479 68.7305 133.199C68.7305 133.199 56.5869 140.625 52.5749 146.387L200.224 146.313H200.227Z" fill="url(#paint4_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M200.227 146.313C200.227 146.313 223.759 133.344 221.261 115.588C218.552 96.3224 203.177 102.433 203.177 102.433L200.227 65.5412C200.227 65.5412 203.376 62.9263 201.985 58.4788C200.83 54.7859 196.384 56.4953 198.08 50.1475C199.96 43.119 196.477 27.0631 177.215 39.3861C177.215 39.3861 170.193 21.667 150.037 21.5592C129.882 21.4514 136.06 36.2352 136.06 36.2352C136.06 36.2352 127.206 33.953 124.804 49.6054L115.677 54.1699C115.677 54.1699 110.906 47.9022 101.076 47.6127C91.2452 47.3232 86.6403 55.2941 86.1949 60.8011C86.1949 60.8011 73.4799 57.7581 62.2701 62.107C51.0604 66.4559 42.9595 77.8549 45.5124 94.0063C45.5124 94.0063 37.4822 90.683 37.4668 99.1529C37.4607 103.745 46.4309 110.715 47.8747 113.441C49.3186 116.167 43.8996 123.851 52.6824 128.089C61.4653 132.327 64.5004 130.479 68.7305 133.199C68.7305 133.199 56.5869 140.625 52.5749 146.387L200.224 146.313H200.227Z" fill="url(#paint5_linear_36_5452)"/>
<path d="M112.595 55.6533C112.595 55.6533 111.646 50.8701 111.972 46.3056C111.972 46.3056 114.457 50.1833 119.563 48.6433C124.063 47.285 119.237 43.3889 115.495 46.5767C115.495 46.5767 108.574 43.9864 110.454 31.6665C110.454 31.6665 115.032 40.0718 129.439 47.3189C143.847 54.5661 151.171 56.3063 151.171 56.3063C151.171 56.3063 144.424 52.2838 137.955 46.6321C137.955 46.6321 155.622 44.8919 163.44 57.6122C163.44 57.6122 164.015 56.0168 166.69 57.0332C169.366 58.0495 168.174 64.3512 175.854 66.5996C175.854 66.5996 173.486 64.4251 171.243 60.7291C171.243 60.7291 170.014 51.597 175.854 41.4146C175.854 41.4146 173.989 50.5098 176.641 56.9592L180.133 57.683C180.133 57.683 180.364 62.0443 183.169 60.8C185.049 59.9653 185.626 58.1081 187.288 58.0465C191.119 57.9017 193.036 64.0986 193.036 64.0986C193.036 64.0986 200.657 62.9806 206.878 69.1344C211.938 74.1424 210.709 86.6379 210.709 86.6379C210.709 86.6379 210.202 83.1544 206.949 81.0723C206.949 81.0723 202.252 82.4891 202.756 88.3103C203.262 94.1345 205.935 100.082 205.935 100.082C205.935 100.082 203.855 93.3676 209.637 91.3441C209.637 91.3441 208.752 96.3398 209.477 99.1487C210.199 101.958 213.019 108.336 213.019 114.422C213.019 114.422 204.78 127.685 189.816 135.147C189.816 135.147 204.851 127.392 212.079 127.096C212.079 127.096 217.572 131.525 221.258 141.526C221.258 141.526 206.15 134.858 199.284 140.439C193.853 144.852 200.224 146.309 200.224 146.309H109.566C109.566 146.309 95.7056 141.671 95.2725 119.061C94.8393 96.4506 106.55 77.515 106.55 77.515L112.595 55.6503V55.6533Z" fill="url(#paint6_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M112.595 55.6533C112.595 55.6533 111.646 50.8701 111.972 46.3056C111.972 46.3056 114.457 50.1833 119.563 48.6433C124.063 47.285 119.237 43.3889 115.495 46.5767C115.495 46.5767 108.574 43.9864 110.454 31.6665C110.454 31.6665 115.032 40.0718 129.439 47.3189C143.847 54.5661 151.171 56.3063 151.171 56.3063C151.171 56.3063 144.424 52.2838 137.955 46.6321C137.955 46.6321 155.622 44.8919 163.44 57.6122C163.44 57.6122 164.015 56.0168 166.69 57.0332C169.366 58.0495 168.174 64.3512 175.854 66.5996C175.854 66.5996 173.486 64.4251 171.243 60.7291C171.243 60.7291 170.014 51.597 175.854 41.4146C175.854 41.4146 173.989 50.5098 176.641 56.9592L180.133 57.683C180.133 57.683 180.364 62.0443 183.169 60.8C185.049 59.9653 185.626 58.1081 187.288 58.0465C191.119 57.9017 193.036 64.0986 193.036 64.0986C193.036 64.0986 200.657 62.9806 206.878 69.1344C211.938 74.1424 210.709 86.6379 210.709 86.6379C210.709 86.6379 210.202 83.1544 206.949 81.0723C206.949 81.0723 202.252 82.4891 202.756 88.3103C203.262 94.1345 205.935 100.082 205.935 100.082C205.935 100.082 203.855 93.3676 209.637 91.3441C209.637 91.3441 208.752 96.3398 209.477 99.1487C210.199 101.958 213.019 108.336 213.019 114.422C213.019 114.422 204.78 127.685 189.816 135.147C189.816 135.147 204.851 127.392 212.079 127.096C212.079 127.096 217.572 131.525 221.258 141.526C221.258 141.526 206.15 134.858 199.284 140.439C193.853 144.852 200.224 146.309 200.224 146.309H109.566C109.566 146.309 95.7056 141.671 95.2725 119.061C94.8393 96.4506 106.55 77.515 106.55 77.515L112.595 55.6503V55.6533Z" fill="url(#paint7_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M129.439 146.313C129.439 146.313 134.956 125.905 131.104 101.983C127.252 78.061 118.506 58.4785 118.506 58.4785C118.506 58.4785 125.633 63.9855 128.524 70.2194V64.2411C128.524 64.2411 145.641 63.117 159.996 71.5222C174.352 79.9274 163.111 132.398 141.988 146.31L137.488 146.384C137.488 146.384 142.612 143.267 143.484 133.701C143.484 133.701 135.347 137.929 130.622 146.384L129.439 146.31V146.313Z" fill="url(#paint8_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M199.287 140.442C206.153 134.861 221.261 141.529 221.261 141.529C217.575 131.528 212.082 127.099 212.082 127.099C205.517 127.37 192.514 133.789 190.182 134.965C190.062 135.027 189.939 135.089 189.819 135.15C189.819 135.15 189.948 135.086 190.182 134.965C204.912 127.469 213.022 114.425 213.022 114.425C213.022 108.339 210.202 101.96 209.48 99.1515C208.758 96.3426 209.64 91.3469 209.64 91.3469C203.859 93.3673 205.938 100.085 205.938 100.085C205.938 100.085 203.263 94.1373 202.759 88.3131C202.252 82.4889 206.952 81.0752 206.952 81.0752C209.698 82.8338 210.485 85.5904 210.666 86.4066C207.674 70.2337 185.436 63.1035 185.436 63.1035C185.436 63.1035 196.47 71.9615 199.797 80.8134C203.121 89.6621 199.797 110.052 199.797 110.052C199.797 110.052 209.483 109.648 206.593 114.136C203.702 118.623 191.34 123.924 191.34 123.924L185.436 123.489C176.711 130.881 155.895 138.273 155.895 138.273C158.184 131.316 159.315 116.701 159.315 116.701C151.954 139.12 134.164 146.318 134.164 146.318H200.23C200.23 146.318 193.859 144.864 199.29 140.448L199.287 140.442Z" fill="url(#paint9_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" d="M86.1977 126.038C117.83 113.287 118.868 82.7765 107.72 74.5961C103.883 71.781 100.24 71.0264 97.0787 71.279C98.5379 71.1866 104.657 71.2882 105.803 79.4348C107.109 88.6993 97.432 109.514 81.1811 111.531C67.1759 113.268 61.2377 103.579 61.0288 103.228C64.8319 111.987 72.7485 120.617 86.1977 126.041V126.038Z" fill="url(#paint10_linear_36_5452)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M69.6055 73.5737C69.6055 73.5737 62.2419 75.6404 60.067 85.2068C57.892 94.7732 63.1912 104.37 63.1912 104.37C63.1912 104.37 62.1037 92.919 69.6055 73.5737Z" fill="url(#paint11_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" d="M82.6495 76.8691C86.3451 82.3175 91.5091 84.1163 91.5091 84.1163C91.5091 84.1163 94.6395 70.9679 89.4232 73.9523C87.3834 75.2398 86.1946 76.4995 86.1946 76.4995C86.1946 76.4995 80.6803 70.6537 73.0249 71.2758C73.1171 71.2758 78.9815 71.4606 82.6464 76.866L82.6495 76.8691Z" fill="url(#paint12_linear_36_5452)"/>
<path d="M107.72 74.596C106.814 73.9307 105.917 73.3794 105.035 72.9297L105.837 74.1494L103.023 75.4707L107.996 75.1935C109.83 77.1616 111.216 82.0064 111.216 82.0064C110.402 89.6447 106.654 92.004 106.654 92.004L104.694 92.3274L105.103 95.395L103.88 97.203L98.5807 92.004C98.5807 92.004 101.109 98.4811 101.109 99.9534C101.109 101.426 100.295 104.693 100.295 104.693L102.74 109.692C99.8863 112.406 95.4841 114.5 95.4841 114.5L89.2879 114.01L86.6798 114.934L80.075 114.01L82.0318 117.035C82.0318 117.035 84.1515 116.055 85.3742 117.035C86.5968 118.014 86.1974 119.976 86.1974 119.976C81.4481 120.46 73.1138 118.464 72.9233 118.421C76.5667 121.322 80.9597 123.922 86.1974 126.035C117.83 113.283 118.868 82.7733 107.72 74.5929V74.596ZM86.9501 120.99C87.5215 119.517 87.3863 117.885 87.3863 117.885L89.4783 120.309L86.9501 120.99ZM93.7699 118.319L90.3446 115.159C90.3446 115.159 95.1277 115.378 98.851 117.149L93.7699 118.319ZM104.857 107.598L101.637 104.533L102.147 102.735H103.106C102.209 100.526 101.189 97.7081 101.189 97.7081L105.182 102.097L104.857 107.601V107.598ZM108.688 93.1713C108.771 95.0932 107.751 96.972 107.751 96.972L105.754 94.1322L108.688 93.1713ZM108.009 100.529L106.405 96.55L109.256 99.4852C109.256 99.4852 108.445 100.394 108.009 100.529Z" fill="url(#paint13_linear_36_5452)"/>
<path d="M93.5429 111.642C93.5429 111.642 90.0347 111.204 87.5955 110.006V108.811L85.8076 104.693L92.4401 108.337C92.4401 108.337 92.5814 110.986 93.5429 111.639V111.642Z" fill="url(#paint14_linear_36_5452)"/>
<path d="M69.4492 111.288C69.4492 111.288 73.6609 113.114 78.444 113.85L76.4595 114.937L73.1971 113.906L72.7363 113.225L69.4492 111.291V111.288Z" fill="url(#paint15_linear_36_5452)"/>
<path d="M101.625 87.0953L103.419 89.6363L102.848 85.8418C102.848 85.8418 101.871 86.5502 101.625 87.0953Z" fill="url(#paint16_linear_36_5452)"/>
<path d="M93.8931 78.9878L95.6042 76.1265C95.6042 76.1265 98.5809 75.4304 100.823 75.5936L96.2984 77.2784H98.4181L93.8931 78.9878Z" fill="url(#paint17_linear_36_5452)"/>
<path d="M78.8527 104.7C78.8527 104.7 81.6789 106.262 83.7433 106.311L83.5252 104.374L78.8496 104.7H78.8527Z" fill="url(#paint18_linear_36_5452)"/>
<g filter="url(#filter0_d_36_5452)">
<path d="M150.041 114.799C150.041 114.799 154.692 125.887 141.686 129.509C128.679 133.131 113.042 121.92 98.911 122.225C84.7798 122.53 71.7976 121.713 72.7099 104.022C73.6223 86.3307 94.1679 53.5505 135.145 59.963C168.308 65.1528 173.96 89.6878 173.96 98.6475C173.96 105.528 170.393 119.36 150.044 114.799H150.041Z" fill="url(#paint19_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.35" d="M115.746 97.7353C115.746 97.7353 123.767 98.1696 133.087 103.387C142.407 108.605 148.717 110.148 156.714 103.292C165.73 95.5578 173.816 96.0013 173.816 96.0013C173.914 96.9838 173.957 97.877 173.957 98.6501C173.957 105.531 170.39 119.363 150.041 114.801C141.633 112.981 138.506 109.184 131.474 104.259C124.442 99.3338 115.746 97.7384 115.746 97.7384V97.7353Z" fill="url(#paint20_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.35" d="M141.686 129.508C128.679 133.13 113.042 121.919 98.911 122.224C84.7798 122.528 71.7976 121.712 72.7099 104.021C73.3458 91.6856 83.5326 72.0076 103.534 63.4053C83.9535 80.801 91.7963 112.793 103.534 118.454C111.147 122.125 118.292 118.494 130.27 114.798C140.337 111.693 150.041 114.798 150.041 114.798C150.041 114.798 154.689 125.886 141.686 129.508Z" fill="url(#paint21_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M150.041 114.799C150.041 114.799 137.784 110.342 130.559 111.5C123.334 112.658 118.418 113.992 115.746 113.977C113.073 113.964 103.439 104.395 100.161 95.9957C96.8867 87.5936 97.8974 70.9309 103.968 71.6578C110.038 72.3816 110.615 80.9316 110.615 86.725C110.615 92.5184 105.703 92.5215 104.69 90.4918C103.679 88.4621 104.112 82.2344 104.112 82.2344C104.112 82.2344 104.64 89.0442 105.654 89.8173C106.668 90.5904 109.556 89.9621 109.411 87.2579C109.267 84.5536 109.411 77.405 107.436 75.2336C105.461 73.0592 102.957 72.6249 101.992 73.977C101.028 75.3291 99.1262 80.6421 99.9218 87.0176C100.717 93.3932 101.657 98.1024 105.703 103.462C109.749 108.824 113.002 111.938 116.397 112.446C119.791 112.954 129.637 109.043 134.893 109.837C140.15 110.635 150.041 114.802 150.041 114.802V114.799Z" fill="url(#paint22_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M104.521 85.4673C104.895 87.3984 104.76 90.5153 106.95 90.1119C106.95 90.1119 105.362 91.6488 104.686 90.4907C104.011 89.3327 104.517 85.4673 104.517 85.4673H104.521Z" fill="url(#paint23_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M162.495 107.877C162.495 107.877 170.443 104.979 166.397 101.791C162.351 98.6037 156.281 99.1827 152.524 100.344C148.767 101.502 130.414 98.114 127.812 93.2168C125.211 88.3196 132.003 82.2336 135.615 77.8878C139.228 73.5419 130.411 65.571 126.799 64.8472C126.799 64.8472 132.725 67.6469 133.975 72.3808C135.228 77.1147 133.493 79.5294 130.024 82.5231C126.556 85.5169 123.281 90.9161 127.616 94.689C131.95 98.462 140.128 100.587 146.542 101.262C152.954 101.939 159.989 100.199 162.204 100.972C164.418 101.745 169.718 103.581 162.492 107.877H162.495Z" fill="url(#paint24_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M130.924 96.9109C130.924 96.9109 126.27 94.7641 125.668 91.564C125.066 88.367 127.858 84.7881 127.858 84.7881C127.858 84.7881 124.448 92.7498 130.924 96.9078V96.9109Z" fill="url(#paint25_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M126.802 64.844C126.802 64.844 121.069 61.8502 113.073 64.0709C105.076 66.2915 106.907 69.8674 112.108 72.3776C117.312 74.8877 118.08 79.1381 121.548 78.365C125.017 77.592 128.869 73.6311 124.439 69.6703C120.006 65.7094 116.925 66.4825 116.925 66.4825C116.925 66.4825 122.676 68.0287 124.184 71.3119C125.693 74.5952 123.189 76.4308 120.876 76.7203C118.563 77.0099 117.214 72.8118 114.034 71.6261C110.855 70.4403 107.869 68.5091 109.989 66.578C112.108 64.6468 119.718 62.7157 126.802 64.8378V64.844Z" fill="url(#paint26_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M123.908 75.2083C123.908 75.2083 122.317 78.6979 120.584 78.4546C118.851 78.2113 117.334 76.3263 116.504 75.35C115.675 74.3736 118.928 76.6528 120.443 76.7637C122.467 76.9084 123.911 75.2114 123.911 75.2114L123.908 75.2083Z" fill="url(#paint27_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M135.615 77.8845C135.615 77.8845 145.298 75.9934 152.09 78.4605C158.883 80.9275 164.52 86.145 157.002 87.5926C149.488 89.0402 143.418 94.6857 140.239 90.8481C137.059 87.0105 137.735 82.3258 147.369 82.5199C147.369 82.5199 142.647 81.2632 138.696 82.9049C134.746 84.5465 136.383 89.3974 139.179 91.5596C141.971 93.7217 144.96 94.5163 150.643 91.5596C156.327 88.6028 161.915 88.7476 162.108 86.3575C162.302 83.9674 160.372 79.6216 154.788 77.7859C149.2 75.9503 139.471 76.1443 135.615 77.8814V77.8845Z" fill="url(#paint28_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M115.746 113.976C115.746 113.976 106.281 114.543 100.932 110.339C95.5841 106.138 95.5841 100.486 88.5032 101.355C81.4222 102.223 75.0632 104.398 74.4856 109.467C73.9081 114.537 77.1091 117.848 77.1091 117.848C77.1091 117.848 74.0525 114.974 73.7146 111.013C73.3767 107.053 73.6193 103.431 82.3868 101.111C91.1543 98.7921 93.4675 99.2757 95.9221 102.852C98.3797 106.424 102.137 112.443 115.746 113.973V113.976Z" fill="url(#paint29_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M78.8537 102.3C78.8537 102.3 84.8901 100.486 88.0697 96.1403C91.2492 91.7945 94.9325 77.8053 91.3936 78.4613C87.8516 79.1173 79.5449 86.7248 79.978 89.8387C80.4112 92.9525 81.2775 95.3457 81.2775 95.3457C81.2775 95.3457 80.362 90.7103 81.7598 88.3911C83.1575 86.0719 88.936 80.2785 90.5979 80.4941C92.2599 80.7127 88.0697 93.6794 86.334 95.78C84.5983 97.8805 78.8506 102.303 78.8506 102.303L78.8537 102.3Z" fill="url(#paint30_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M129.066 95.9922C129.066 95.9922 122.225 92.2963 115.386 93.4944C108.545 94.6925 103.538 98.5055 108.594 104.835C112.941 110.271 116.597 110.973 123.312 108.774C130.031 106.575 134.172 109.831 134.172 109.831C134.172 109.831 131.668 106.572 126.802 105.848C121.936 105.124 118.566 102.999 118.566 102.999C118.566 102.999 123.672 106.091 122.034 106.96C120.397 107.829 115.193 109.375 111.436 105.848C107.679 102.322 107.101 98.9428 109.894 96.8177C112.686 94.6925 119.239 93.5283 129.066 95.9922Z" fill="url(#paint31_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M135.615 77.8847C135.615 77.8847 142.177 65.6079 154.499 71.6323C166.821 77.6568 172.498 89.3576 172.498 89.3576C172.498 89.3576 171.742 86.1945 169.045 81.9441C166.348 77.6937 157.605 70.2556 150.85 68.5185C144.094 66.7783 126.799 64.8472 126.799 64.8472C126.799 64.8472 136.383 66.2948 140.528 67.6469C144.672 68.999 137.059 70.3511 135.615 77.8847Z" fill="url(#paint32_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M117.217 125.985C117.217 125.985 123.38 128.015 132.052 126.469C140.724 124.923 142.65 120.866 138.605 119.225C134.559 117.583 133.305 119.031 127.816 120.192C122.326 121.353 127.911 124.153 127.911 124.153C127.911 124.153 120.492 122.317 125.309 119.708C130.126 117.099 135.619 115.917 138.989 117.848C142.362 119.779 144.441 124.658 148.776 125.335L148.244 125.933C148.244 125.933 146.275 125.948 145.302 124.972C144.325 123.992 142.881 121.747 142.374 122.763C141.867 123.78 137.342 127.34 132.221 127.725C125.466 128.234 120.606 127.143 117.22 125.985H117.217Z" fill="url(#paint33_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M79.0139 119.245C79.0139 119.245 78.9678 113.002 84.7493 108.773C90.5308 104.545 99.3935 114.302 108.545 117.585C117.696 120.868 109.605 123.573 101.559 118.358C95.7102 114.567 90.4325 110.92 88.2145 112.371C85.9996 113.818 87.0594 118.263 87.0594 118.263C87.0594 118.263 87.637 113.338 89.8519 113.338C92.0668 113.338 100.739 120.678 107.289 122.033C113.841 123.385 116.007 120.798 113.841 118.845C112.514 117.647 109.119 117.425 105.074 114.804C101.028 112.18 91.8732 105.321 87.5387 105.854C83.2041 106.386 78.4517 110.203 77.7451 118.389C77.7451 118.389 78.808 119.131 79.0108 119.251L79.0139 119.245Z" fill="url(#paint34_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M129.066 95.9917C129.066 95.9917 121.745 92.2988 121.745 87.3C121.745 82.3012 119.288 80.7797 113.147 76.1444C107.006 71.509 100.846 69.7996 108.404 61.5977L107.298 61.958C107.298 61.958 104.078 64.305 103.826 67.8377C103.682 69.8427 105.814 71.7246 108.379 73.7543C110.944 75.784 117.448 80.2376 118.965 82.3752C120.483 84.5127 119.177 88.2179 121.856 91.2147C125.551 95.3449 129.069 95.9948 129.069 95.9948L129.066 95.9917Z" fill="url(#paint35_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M152.523 100.341C152.523 100.341 156.499 97.082 160.978 95.271C165.457 93.46 173.929 97.5132 173.929 97.5132L173.815 95.9979C173.815 95.9979 165.914 92.2773 161.34 94.1129C156.763 95.9486 152.523 100.344 152.523 100.344V100.341Z" fill="url(#paint36_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M133.738 70.4971C133.738 70.4971 139.471 74.4579 134.251 79.4659C134.251 79.4659 135.425 74.4579 133.738 70.4971Z" fill="url(#paint37_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M92.0544 78.5409C92.6842 78.7812 93.5627 81.6517 90.8164 86.6259C90.8164 86.6259 92.0175 81.3653 90.5983 80.4936C89.8026 80.0039 86.3374 83.2502 86.3374 83.2502C86.3374 83.2502 90.0392 77.7679 92.0513 78.544L92.0544 78.5409Z" fill="url(#paint38_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M159.371 81.6143C159.371 81.6143 163.073 84.5495 161.93 86.9457C161.266 88.3348 158.05 88.7136 158.05 88.7136C158.05 88.7136 160.471 87.1551 160.578 85.4889C160.686 83.8226 159.368 81.6143 159.368 81.6143H159.371Z" fill="url(#paint39_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.2" d="M157.003 87.5928C149.488 89.0404 143.418 94.686 140.239 90.8484C138.521 88.7756 137.929 86.4533 139.268 84.787C139.67 87.6329 143.418 89.7642 147.175 87.8793C150.932 85.9943 156.063 87.0107 157.147 84.1833C158.231 81.359 155.347 79.896 155.347 79.896C160.4 82.5633 163.257 86.3824 157.003 87.5867V87.5928Z" fill="url(#paint40_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.2" d="M105.654 89.8128C104.852 89.1999 104.355 84.8047 104.183 83.003C104.183 83.0122 104.745 85.5162 106.533 85.6117C108.318 85.7072 109.193 82.1067 109.199 82.082C109.344 84.1271 109.353 86.0983 109.414 87.2533C109.559 89.9575 106.668 90.5858 105.657 89.8128H105.654Z" fill="url(#paint41_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.2" d="M122.031 106.959C120.393 107.828 115.19 109.374 111.432 105.847C107.675 102.321 107.098 98.9421 109.89 96.8169C109.098 98.7326 110.83 101.067 114.299 101.597C117.767 102.127 118.563 102.998 118.563 102.998C118.563 102.998 118.581 103.008 118.609 103.026C119.109 103.334 123.576 106.137 122.031 106.956V106.959Z" fill="url(#paint42_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.2" d="M108.591 104.834C112.938 110.271 116.593 110.973 123.309 108.774C129.391 106.781 133.36 109.263 134.058 109.75C128.654 109.424 119.616 112.922 116.394 112.442C112.999 111.934 109.746 108.82 105.7 103.458C101.654 98.0955 100.714 93.3893 99.9186 87.0137C99.7097 85.3321 99.6882 83.7274 99.7865 82.249C99.7865 82.3876 99.851 83.595 101.362 89.3576C102.984 95.5422 107.212 97.6889 107.212 97.6889C106.066 99.5277 106.253 101.915 108.588 104.834H108.591Z" fill="url(#paint43_linear_36_5452)"/>
<path style="mix-blend-mode:multiply" opacity="0.2" d="M121.742 87.304C121.742 83.2446 120.123 81.4798 116.225 78.4768C117.058 79.0867 120.062 81.0578 125.137 81.8709C131.047 82.8196 133.57 78.4891 133.677 78.3013C132.949 79.7858 131.686 81.0886 130.028 82.5208C126.559 85.5146 123.285 90.9138 127.619 94.6867C128.184 95.1795 128.817 95.6415 129.502 96.0789L129.496 96.1097L129.066 95.9957C129.066 95.9957 121.745 92.3028 121.745 87.304H121.742Z" fill="url(#paint44_linear_36_5452)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M165.432 91.3592C161.749 90.7832 159.073 90.9249 155.411 93.8231C151.75 96.7214 142.472 100.343 136.442 96.2379C130.412 92.1323 133.975 89.197 137.01 91.1713C140.045 93.1456 142.217 96.974 150.383 93.4936C156.511 90.8818 159.408 89.1693 163.985 90.121C168.563 91.0697 168.514 91.8427 165.429 91.3592H165.432Z" fill="url(#paint45_linear_36_5452)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M113.26 83.9524C115.103 84.4175 117.441 85.052 118.215 87.1402C118.986 89.2284 120.101 83.1979 117.189 81.0388C114.28 78.8797 112.145 80.3181 111.93 81.5193C111.721 82.6835 112.065 83.6475 113.26 83.9494V83.9524Z" fill="url(#paint46_linear_36_5452)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M98.3243 75.3494C97.2368 81.189 96.4503 90.6322 99.5592 96.9739C102.665 103.312 104.963 109.617 99.7312 103.457C94.4996 97.3004 93.9958 89.3294 95.2246 81.6141C96.4534 73.8987 98.8373 72.6082 98.3273 75.3463L98.3243 75.3494Z" fill="url(#paint47_linear_36_5452)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M158.351 70.1085C151.703 66.7759 142.407 63.6374 127.017 62.9968C116.151 62.5441 112.925 61.0041 118.166 60.1355C123.404 59.267 143.083 59.5318 158.354 70.1085H158.351Z" fill="url(#paint48_linear_36_5452)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M131.619 101.597C131.619 101.597 123.045 97.578 117.552 97.3654C112.059 97.1529 115.678 94.6705 120.501 95.3327C125.324 95.9949 130.307 99.6939 131.619 101.594V101.597Z" fill="url(#paint49_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.35" d="M153.777 84.7882C153.777 84.7882 150.791 84.8097 148.189 86.0294C145.587 87.2491 142.601 87.3784 142.119 85.4073C141.637 83.4361 147.323 81.9823 153.777 84.7851V84.7882Z" fill="url(#paint50_linear_36_5452)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M116.897 67.21C116.897 67.21 122 67.9245 123.207 71.5096C124.055 74.0352 121.84 75.994 120.685 74.9807C119.53 73.9674 120.387 70.1575 116.897 67.213V67.21Z" fill="url(#paint51_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M144.177 86.9258C144.177 86.9258 142.604 87.2677 141.863 86.2143C141.123 85.161 142.847 84.0737 142.847 84.0737C142.847 84.0737 142.604 85.7154 144.174 86.9258H144.177Z" fill="url(#paint52_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M120.584 68.5186C120.584 68.5186 123.395 70.0247 123.585 72.5595C123.834 75.8735 122.141 75.7842 121.742 73.5389C121.346 71.2936 121.232 69.6212 120.584 68.5186Z" fill="url(#paint53_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M128.254 61.0847C128.274 60.6991 126.427 60.2893 124.13 60.1693C121.832 60.0494 119.954 60.2648 119.934 60.6504C119.914 61.036 121.76 61.4458 124.057 61.5658C126.355 61.6857 128.234 61.4703 128.254 61.0847Z" fill="url(#paint54_linear_36_5452)"/>
<path d="M115.481 54.2278C115.678 54.2278 115.678 53.9229 115.481 53.9229C115.285 53.9229 115.285 54.2278 115.481 54.2278Z" fill="url(#paint55_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M114.558 81.7447C114.694 80.9396 114.181 80.1816 113.413 80.0517C112.646 79.9217 111.913 80.4691 111.778 81.2742C111.642 82.0793 112.155 82.8373 112.923 82.9672C113.69 83.0971 114.423 82.5498 114.558 81.7447Z" fill="url(#paint56_linear_36_5452)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M167.048 91.6991C167.048 91.6991 162.578 90.618 159.334 91.582C159.334 91.582 161.967 89.6232 165.53 90.2268C169.094 90.8305 168.59 92.1826 167.048 91.6991Z" fill="url(#paint57_linear_36_5452)"/>
</g>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M151.607 146.387C151.607 146.387 165.532 136.423 181.903 131.64L170.598 141.099L176.561 140.446L178.306 137.837C178.306 137.837 197.954 131.966 208.47 129.903C208.47 129.903 191.595 135.447 175.854 144.36C175.854 144.36 165.628 142.186 159.997 146.316L151.61 146.39L151.607 146.387Z" fill="url(#paint58_linear_36_5452)"/>
<path d="M178.303 34.781L190.397 5.57666C190.397 5.57666 190.108 16.3011 189.168 19.2025L178.303 34.784V34.781Z" fill="url(#paint59_linear_36_5452)"/>
<path d="M151.438 41.957L143.74 35.4336L146.016 33.9829L151.438 41.957Z" fill="url(#paint60_linear_36_5452)"/>
<path d="M182.88 46.8858L185.844 42.5368C185.844 42.5368 189.82 40.6519 192.64 40.5811L182.883 46.8858H182.88Z" fill="url(#paint61_linear_36_5452)"/>
<path d="M215.188 100.083C215.188 100.083 215.91 89.8115 217.501 84.4369L221.91 81.5264L215.188 100.083Z" fill="url(#paint62_linear_36_5452)"/>
<path d="M218.875 125.105C218.875 125.105 223.28 125.471 224.945 123.866V120.95L218.875 125.105Z" fill="url(#paint63_linear_36_5452)"/>
<defs>
<filter id="filter0_d_36_5452" x="42.6646" y="23.9229" width="161.295" height="136.307" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="15"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_36_5452"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_36_5452" result="shape"/>
</filter>
<linearGradient id="paint0_linear_36_5452" x1="194.126" y1="118.87" x2="219.602" y2="141.51" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint1_linear_36_5452" x1="207.29" y1="156.772" x2="231.135" y2="105.291" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.01" stop-color="#FE9600" stop-opacity="0.02"/>
<stop offset="0.23" stop-color="#FEAF04" stop-opacity="0.31"/>
<stop offset="0.43" stop-color="#FDC408" stop-opacity="0.56"/>
<stop offset="0.62" stop-color="#FCD40B" stop-opacity="0.75"/>
<stop offset="0.78" stop-color="#FCE00E" stop-opacity="0.89"/>
<stop offset="0.91" stop-color="#FCE70F" stop-opacity="0.97"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint2_linear_36_5452" x1="27.3722" y1="106.07" x2="78.2287" y2="151.267" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint3_linear_36_5452" x1="79.7989" y1="156.215" x2="11.2656" y2="64.7467" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.01" stop-color="#FE9600" stop-opacity="0.02"/>
<stop offset="0.23" stop-color="#FEAF04" stop-opacity="0.31"/>
<stop offset="0.43" stop-color="#FDC408" stop-opacity="0.56"/>
<stop offset="0.62" stop-color="#FCD40B" stop-opacity="0.75"/>
<stop offset="0.78" stop-color="#FCE00E" stop-opacity="0.89"/>
<stop offset="0.91" stop-color="#FCE70F" stop-opacity="0.97"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint4_linear_36_5452" x1="118.589" y1="29.9583" x2="133.302" y2="135.283" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint5_linear_36_5452" x1="120.226" y1="224.825" x2="133.307" y2="61.3393" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.01" stop-color="#FE9600" stop-opacity="0.02"/>
<stop offset="0.23" stop-color="#FEAF04" stop-opacity="0.31"/>
<stop offset="0.43" stop-color="#FDC408" stop-opacity="0.56"/>
<stop offset="0.62" stop-color="#FCD40B" stop-opacity="0.75"/>
<stop offset="0.78" stop-color="#FCE00E" stop-opacity="0.89"/>
<stop offset="0.91" stop-color="#FCE70F" stop-opacity="0.97"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint6_linear_36_5452" x1="114.887" y1="43.8848" x2="207.602" y2="185.849" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint7_linear_36_5452" x1="166.045" y1="13.1251" x2="156.562" y2="153.023" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint8_linear_36_5452" x1="170.205" y1="31.2177" x2="99.0084" y2="166.079" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint9_linear_36_5452" x1="153.174" y1="90.1272" x2="203.752" y2="153.592" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint10_linear_36_5452" x1="79.5161" y1="87.8" x2="196.665" y2="181.667" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5" stop-opacity="0"/>
<stop offset="0.31" stop-color="#7EEBF3" stop-opacity="0.35"/>
<stop offset="0.65" stop-color="#68ECF2" stop-opacity="0.7"/>
<stop offset="0.88" stop-color="#5AECF2" stop-opacity="0.92"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint11_linear_36_5452" x1="76.4376" y1="118.868" x2="60.7131" y2="73.1183" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint12_linear_36_5452" x1="101.035" y1="82.6748" x2="26.4661" y2="53.3806" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint13_linear_36_5452" x1="75.9615" y1="93.5532" x2="120.767" y2="107.004" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5" stop-opacity="0"/>
<stop offset="0.31" stop-color="#7EEBF3" stop-opacity="0.35"/>
<stop offset="0.65" stop-color="#68ECF2" stop-opacity="0.7"/>
<stop offset="0.88" stop-color="#5AECF2" stop-opacity="0.92"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint14_linear_36_5452" x1="85.2362" y1="104.262" x2="94.1718" y2="113.393" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5" stop-opacity="0"/>
<stop offset="0.31" stop-color="#7EEBF3" stop-opacity="0.35"/>
<stop offset="0.65" stop-color="#68ECF2" stop-opacity="0.7"/>
<stop offset="0.88" stop-color="#5AECF2" stop-opacity="0.92"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint15_linear_36_5452" x1="70.2817" y1="110.622" x2="82.6075" y2="117.826" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5" stop-opacity="0"/>
<stop offset="0.31" stop-color="#7EEBF3" stop-opacity="0.35"/>
<stop offset="0.65" stop-color="#68ECF2" stop-opacity="0.7"/>
<stop offset="0.88" stop-color="#5AECF2" stop-opacity="0.92"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint16_linear_36_5452" x1="99.7972" y1="82.7464" x2="105.282" y2="91.0709" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5" stop-opacity="0"/>
<stop offset="0.31" stop-color="#7EEBF3" stop-opacity="0.35"/>
<stop offset="0.65" stop-color="#68ECF2" stop-opacity="0.7"/>
<stop offset="0.88" stop-color="#5AECF2" stop-opacity="0.92"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint17_linear_36_5452" x1="92.0929" y1="77.8944" x2="103.387" y2="76.307" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5" stop-opacity="0"/>
<stop offset="0.31" stop-color="#7EEBF3" stop-opacity="0.35"/>
<stop offset="0.65" stop-color="#68ECF2" stop-opacity="0.7"/>
<stop offset="0.88" stop-color="#5AECF2" stop-opacity="0.92"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint18_linear_36_5452" x1="78.441" y1="103.179" x2="87.1467" y2="107.467" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5" stop-opacity="0"/>
<stop offset="0.31" stop-color="#7EEBF3" stop-opacity="0.35"/>
<stop offset="0.65" stop-color="#68ECF2" stop-opacity="0.7"/>
<stop offset="0.88" stop-color="#5AECF2" stop-opacity="0.92"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint19_linear_36_5452" x1="104.822" y1="72.3014" x2="148.326" y2="137.388" gradientUnits="userSpaceOnUse">
<stop stop-color="#55EDF2"/>
<stop offset="0.65" stop-color="#3FBDE6"/>
<stop offset="1" stop-color="#36A9E1"/>
</linearGradient>
<linearGradient id="paint20_linear_36_5452" x1="191.295" y1="110.489" x2="110.643" y2="96.6502" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint21_linear_36_5452" x1="68.2002" y1="104.363" x2="141.312" y2="93.8979" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint22_linear_36_5452" x1="157.298" y1="139.534" x2="71.584" y2="54.7307" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint23_linear_36_5452" x1="107.808" y1="90.925" x2="101.825" y2="85.5074" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint24_linear_36_5452" x1="165.687" y1="116.366" x2="92.2321" y2="45.0516" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint25_linear_36_5452" x1="133.876" y1="95.7281" x2="120.128" y2="82.3813" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint26_linear_36_5452" x1="148.471" y1="88.3811" x2="100.146" y2="56.4186" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint27_linear_36_5452" x1="127.508" y1="77.4505" x2="113.539" y2="75.8173" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint28_linear_36_5452" x1="175.923" y1="88.5504" x2="125.041" y2="82.6027" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint29_linear_36_5452" x1="137.489" y1="115.061" x2="58.3828" y2="105.813" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint30_linear_36_5452" x1="97.7864" y1="91.8407" x2="75.6204" y2="89.2483" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint31_linear_36_5452" x1="149.449" y1="105.288" x2="96.109" y2="99.0518" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint32_linear_36_5452" x1="198.121" y1="82.7972" x2="108.222" y2="72.2838" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint33_linear_36_5452" x1="164.769" y1="126.336" x2="106.495" y2="119.524" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint34_linear_36_5452" x1="133.904" y1="118.435" x2="65.1205" y2="110.392" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint35_linear_36_5452" x1="145.317" y1="81.9193" x2="92.4838" y2="75.7418" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint36_linear_36_5452" x1="184.426" y1="99.9803" x2="145.415" y2="95.4176" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint37_linear_36_5452" x1="138.116" y1="75.4651" x2="132.042" y2="74.7542" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint38_linear_36_5452" x1="95.5872" y1="83.0562" x2="84.2937" y2="81.7355" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint39_linear_36_5452" x1="164.126" y1="85.7999" x2="157.106" y2="84.9787" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint40_linear_36_5452" x1="149.525" y1="100.593" x2="149.621" y2="56.3558" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint41_linear_36_5452" x1="106.763" y1="101.957" x2="106.856" y2="59.4812" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint42_linear_36_5452" x1="115.113" y1="165.799" x2="115.505" y2="-13.9728" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint43_linear_36_5452" x1="116.747" y1="165.803" x2="117.139" y2="-13.9693" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint44_linear_36_5452" x1="124.913" y1="96.0974" x2="124.953" y2="78.2828" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint45_linear_36_5452" x1="197.068" y1="127.022" x2="127.102" y2="75.6274" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint46_linear_36_5452" x1="103.364" y1="66.1172" x2="121.277" y2="91.1336" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint47_linear_36_5452" x1="152.017" y1="102.77" x2="74.9727" y2="83.8505" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint48_linear_36_5452" x1="176.516" y1="92.4475" x2="111.517" y2="48.628" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint49_linear_36_5452" x1="152.521" y1="126.105" x2="103.249" y2="80.8301" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint50_linear_36_5452" x1="154.997" y1="79.8017" x2="136.617" y2="92.4651" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint51_linear_36_5452" x1="141.357" y1="91.8374" x2="114.714" y2="65.9516" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint52_linear_36_5452" x1="144.588" y1="84.3232" x2="141.644" y2="87.3555" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint53_linear_36_5452" x1="124.166" y1="69.3717" x2="119.456" y2="74.2259" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint54_linear_36_5452" x1="123.322" y1="62.6315" x2="124.757" y2="59.3563" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint55_linear_36_5452" x1="115.534" y1="54.2185" x2="115.426" y2="53.9323" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint56_linear_36_5452" x1="112.36" y1="84.6993" x2="113.606" y2="79.8131" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint57_linear_36_5452" x1="163.991" y1="88.9086" x2="163.729" y2="92.1148" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint58_linear_36_5452" x1="225.074" y1="104.967" x2="150.563" y2="159.57" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint59_linear_36_5452" x1="190.422" y1="11.2561" x2="171.115" y2="45.2839" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint60_linear_36_5452" x1="142.677" y1="32.175" x2="155.075" y2="46.3849" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint61_linear_36_5452" x1="196.28" y1="38.0432" x2="180.068" y2="47.0656" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint62_linear_36_5452" x1="221.903" y1="75.4773" x2="213.041" y2="105.612" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint63_linear_36_5452" x1="223.575" y1="118.794" x2="221.013" y2="127.513" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -0,0 +1,213 @@
<svg width="252" height="167" viewBox="0 0 252 167" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_35_5249)">
<path d="M131.693 52.3307C131.693 52.3307 150.168 55.6057 163.131 64.1718C176.094 72.7379 182.204 61.5076 170.91 58.2513C170.91 58.2513 174.181 51.695 182.788 49.9344C191.396 48.1739 196.508 38.863 191.618 32.1167C191.618 32.1167 200.888 42.8952 196.033 53.3434C191.177 63.7916 187.203 65.6208 191.177 76.0347C195.151 86.4487 209.274 110.757 198.168 129.338C189.492 143.853 192.737 148.035 192.737 148.035C192.737 148.035 194.047 137.478 209.274 138.138C209.274 138.138 200.666 143.124 202.433 153.828H129.357C129.357 153.828 113.262 68.3629 131.693 52.3307Z" fill="url(#paint0_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M131.693 52.3307C131.693 52.3307 150.168 55.6057 163.131 64.1718C176.094 72.7379 182.204 61.5076 170.91 58.2513C170.91 58.2513 174.181 51.695 182.788 49.9344C191.396 48.1739 196.417 38.7166 191.618 32.1167C191.618 32.1167 200.888 42.8952 196.033 53.3434C191.177 63.7916 187.203 65.6208 191.177 76.0347C195.151 86.4487 209.274 110.757 198.168 129.338C189.492 143.853 192.737 148.035 192.737 148.035C192.737 148.035 194.047 137.478 209.274 138.138C209.274 138.138 200.666 143.124 202.433 153.828H129.357C129.357 153.828 113.262 68.3629 131.693 52.3307Z" fill="url(#paint1_linear_35_5249)"/>
<path d="M71.3803 153.828C71.3803 153.828 58.5799 150.023 61.816 139.026C65.052 128.03 50.5476 124.945 48.8687 120.311C46.111 112.698 54.1651 110.125 49.8973 102.372C49.8973 102.372 53.5023 104.628 53.7243 112.029C53.9463 119.429 61.2282 120.557 70.0577 120.417C70.0577 120.417 51.5763 96.2362 57.4043 83.9059C62.8853 72.311 78.8873 67.9765 79.3282 65.2967C79.769 62.6169 73.4439 61.492 73.4439 61.492C73.4439 61.492 80.5444 55.8176 86.5006 59.5195C92.4599 63.2214 101.286 67.238 110.116 63.0095C110.116 63.0095 111.476 58.6034 104.488 53.8825C98.2504 49.6696 95.5491 44.93 106.549 34.569C115.891 25.7692 111.11 21.0359 111.11 21.0359C111.11 21.0359 118.283 20.0855 117.842 32.7711C117.401 45.4597 121.931 50.3208 131.693 52.3307C141.454 54.3406 148.739 64.3837 150.506 76.2248C152.272 88.0659 147.01 93.4941 154.514 104.771C162.018 116.048 165.845 128.453 164.666 138.322C164.666 138.322 160.92 125.951 151.056 119.678C138.587 111.748 138.146 103.715 138.146 103.715C138.146 103.715 140.022 108.685 136.492 113.97C132.962 119.255 127.994 125.178 132.518 137.019L133.731 130.043C133.731 130.043 144.546 137.019 145.869 143.152C147.191 149.284 141.548 153.831 141.548 153.831H71.3834L71.3803 153.828Z" fill="url(#paint2_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M71.3803 153.828C71.3803 153.828 58.5799 150.023 61.816 139.026C65.052 128.03 50.5476 124.945 48.8687 120.311C46.111 112.698 54.1651 110.125 49.8973 102.372C49.8973 102.372 53.5023 104.628 53.7243 112.029C53.9463 119.429 61.2282 120.557 70.0577 120.417C70.0577 120.417 51.5763 96.2362 57.4043 83.9059C62.8853 72.311 78.8873 67.9765 79.3282 65.2967C79.769 62.6169 73.4439 61.492 73.4439 61.492C73.4439 61.492 80.5444 55.8176 86.5006 59.5195C92.4599 63.2214 101.286 67.238 110.116 63.0095C110.116 63.0095 111.476 58.6034 104.488 53.8825C98.2504 49.6696 95.5491 44.93 106.549 34.569C115.891 25.7692 111.11 21.0359 111.11 21.0359C111.11 21.0359 118.283 20.0855 117.842 32.7711C117.401 45.4597 121.931 50.3208 131.693 52.3307C141.454 54.3406 148.739 64.3837 150.506 76.2248C152.272 88.0659 147.01 93.4941 154.514 104.771C162.018 116.048 165.845 128.453 164.666 138.322C164.666 138.322 160.92 125.951 151.056 119.678C138.587 111.748 138.146 103.715 138.146 103.715C138.146 103.715 140.022 108.685 136.492 113.97C132.962 119.255 127.994 125.178 132.518 137.019L133.731 130.043C133.731 130.043 144.546 137.019 145.869 143.152C147.191 149.284 141.548 153.831 141.548 153.831H71.3834L71.3803 153.828Z" fill="url(#paint3_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M193.262 153.827C193.262 153.827 181.766 145.13 183.229 134.386C184.817 122.735 209.565 99.1615 182.701 77.5671C159.073 58.5715 133.838 55.8699 133.838 55.8699C133.838 55.8699 160.495 61.4944 167.196 85.7717C172.877 106.35 156.781 124.202 157.828 138.134C157.828 138.134 160.968 135.165 163.713 140.225C166.455 145.286 159.767 150.895 159.767 150.895C159.767 150.895 155.874 131.475 143.399 126.776C143.399 126.776 149.408 143.672 141.545 153.827H193.262Z" fill="url(#paint4_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M169.769 153.833C169.769 153.833 170.935 137.078 180.906 119.207C190.876 101.336 183.094 79.4427 169.178 69.6333C169.178 69.6333 205.521 84.6652 196.398 120.566C196.398 120.566 187.734 125.688 185.289 134.385C181.647 147.336 190.733 153.827 190.733 153.827L169.766 153.833H169.769Z" fill="url(#paint5_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M71.3804 153.826C71.3804 153.826 63.7671 149.704 64.7582 140.505C65.7525 131.307 57.3731 126.935 52.9521 123.061C48.5311 119.185 50.9667 112.454 50.9667 112.454C50.9667 112.454 48.8687 119.995 57.4044 122.039C65.9369 124.084 77.2678 129.724 77.2678 129.724C77.2678 129.724 70.1673 137.124 74.582 144.896C78.9968 152.667 104.047 153.83 104.047 153.83H71.3835L71.3804 153.826Z" fill="url(#paint6_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M104.044 153.826C104.044 153.826 78.7374 154.531 68.5853 142.266C68.5853 142.266 67.8505 129.496 81.8265 123.264C95.8056 117.032 91.8317 101.729 91.8317 101.729C91.8317 101.729 101.249 124.644 92.4195 135.781C92.4195 135.781 79.5441 131.833 76.677 137.542C73.8067 143.251 80.9792 152.343 104.044 153.823V153.826Z" fill="url(#paint7_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M148.977 153.827C148.977 153.827 154.495 143.677 145.456 130.285C136.417 116.892 144.015 108.516 144.631 94.026C145.247 79.5363 139.803 71.0762 139.803 71.0762C139.803 71.0762 145.459 91.2153 134.066 109.13C124.186 124.663 116.704 138.034 135.538 147.199C135.538 147.199 136.68 143.266 141.554 144.625C147.42 146.264 148.983 153.823 148.983 153.823L148.977 153.827Z" fill="url(#paint8_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M139.825 126.093C139.825 126.093 150.005 129.49 153.492 133.369L151.553 133.347C151.553 133.347 153.826 139.072 150.653 147.111C150.653 147.111 147.479 135.731 139.825 126.093Z" fill="url(#paint9_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M124.129 144.31C112.433 142.512 108.349 140.608 108.349 140.608C108.349 140.608 111.22 145.578 120.931 148.432C120.931 148.432 113.317 154.777 99.3008 151.075C85.2874 147.373 80.4317 153.823 80.4317 153.823H71.3833C71.3833 153.823 108.903 141.219 122.585 118.552C136.267 95.8861 127.881 76.9964 116.407 67.4082C104.929 57.8232 108.202 45.2934 112.139 38.65C117.363 29.8408 113.611 22.437 113.611 22.437C113.611 22.437 117.732 24.6214 116.847 34.7704C115.966 44.9195 119.79 51.4757 128.838 54.2958C128.838 54.2958 119.164 58.6645 125.564 67.4425C131.965 76.2174 141.438 98.1608 133.131 114.53C124.823 130.898 116.625 139.795 124.129 144.307V144.31Z" fill="url(#paint10_linear_35_5249)"/>
<path d="M103.897 26.461C103.897 26.461 106.808 21.0733 101.102 17.1003C97.9565 14.9128 91.228 15.8912 91.228 15.8912C91.228 15.8912 98.4818 24.1177 103.897 26.4579V26.461Z" fill="url(#paint11_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M89.9211 63.9596C89.9211 63.9596 101.949 69.1416 111.107 63.9596C111.107 63.9596 112.098 58.8741 104.591 53.0689C97.0842 47.2636 100.111 39.9471 108.406 33.5623C108.406 33.5623 104.038 43.864 108.975 52.187C114.787 61.984 115.153 73.1209 115.153 73.1209C115.153 73.1209 98.5255 71.569 89.918 63.9565L89.9211 63.9596Z" fill="url(#paint12_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M111.992 21.4561C111.992 21.4561 114.862 25.7562 111.551 30.2652C108.24 34.7773 99.1824 41.7386 100.805 48.3353C102.428 54.9352 110.742 53.5953 112.286 64.1681C112.286 64.1681 110.888 54.1593 106.88 49.7906C102.872 45.4218 114.049 34.7991 115.156 30.689C115.156 30.689 115.672 24.0642 111.992 21.4561Z" fill="url(#paint13_linear_35_5249)"/>
<path d="M125.849 44.5741C125.849 44.5741 123.173 42.826 125.849 38.8655C125.849 38.8655 128.888 41.0779 125.849 44.5741Z" fill="url(#paint14_linear_35_5249)"/>
<path d="M64.1951 62.8619C64.1951 62.8619 51.7512 67.67 49.9878 60.3878C49.9878 60.3878 50.2629 59.3283 53.4834 58.5742C53.4834 58.5742 57.3197 62.7311 64.1951 62.8619Z" fill="url(#paint15_linear_35_5249)"/>
<path d="M47.8401 98.2861C47.8401 98.2861 43.5723 95.6779 44.3102 89.2651C45.0418 82.8771 42.9126 79.3279 42.9126 79.3279C42.9126 79.3279 46.8834 83.064 48.281 89.4084C48.281 89.4084 45.8547 92.7924 47.8401 98.2892V98.2861Z" fill="url(#paint16_linear_35_5249)"/>
<path d="M65.571 26.1212C65.571 26.1212 63.3793 24.0927 64.5267 21.0171C64.5267 21.0171 67.5095 23.0675 68.1974 26.732L65.571 26.1212Z" fill="url(#paint17_linear_35_5249)"/>
<path d="M189.632 37.9461C189.632 37.9461 191.177 33.3281 185.383 25.4071C181.569 20.1939 182.569 13.8589 182.569 13.8589C182.569 13.8589 178.595 20.789 180.915 28.707C180.915 28.707 186.765 31.3463 189.632 37.9461Z" fill="url(#paint18_linear_35_5249)"/>
<path d="M203.899 130.544C203.899 130.544 206.713 127.873 208.257 123.75C208.257 123.75 208.589 126.39 208.148 129.085L203.899 130.547V130.544Z" fill="url(#paint19_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M187.453 76.9869C187.453 76.9869 176.941 67.5296 179.367 60.9298C181.794 54.3299 192.94 48.9422 195.148 43.4424C195.148 43.4424 193.859 54.4047 190.292 59.0601C185.658 65.1084 187.453 76.9869 187.453 76.9869Z" fill="url(#paint20_linear_35_5249)"/>
<g filter="url(#filter0_d_35_5249)">
<path d="M160.577 67.6125C142.161 54.179 126.575 70.607 126.575 70.607C126.575 70.607 110.992 54.1759 92.5733 67.6125C74.9642 80.457 76.6026 128.367 126.575 148.391C176.545 128.367 178.186 80.457 160.577 67.6125Z" fill="url(#paint21_linear_35_5249)"/>
<path style="mix-blend-mode:multiply" d="M126.578 148.391C176.548 128.367 178.189 80.4571 160.58 67.6126C154.518 63.1909 148.765 62.0037 143.771 62.4026C146.079 62.2561 155.74 62.415 157.553 75.2065C159.614 89.7523 144.331 122.44 118.658 125.606C96.5345 128.336 87.1547 113.12 86.8232 112.568C92.8326 126.323 105.336 139.878 126.581 148.391H126.578Z" fill="url(#paint22_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M100.368 66.0109C100.368 66.0109 88.7338 69.2548 85.3008 84.2774C81.8677 99.3 90.2377 114.369 90.2377 114.369C90.2377 114.369 88.5212 96.3896 100.368 66.0078V66.0109Z" fill="url(#paint23_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M118.915 142.999C118.915 142.999 140.004 142.043 158.051 119.704C158.051 119.704 148.28 135.016 127.673 146.851C127.673 146.851 122.329 145.76 118.915 142.999Z" fill="url(#paint24_linear_35_5249)"/>
<path style="mix-blend-mode:multiply" d="M120.976 71.1863C126.813 79.7431 134.97 82.5662 134.97 82.5662C134.97 82.5662 139.917 61.916 131.678 66.6057C128.458 68.628 126.578 70.6067 126.578 70.6067C126.578 70.6067 117.868 61.4237 105.774 62.4052C105.918 62.4083 115.182 62.695 120.976 71.1863Z" fill="url(#paint25_linear_35_5249)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M104.939 66.0109C96.5693 66.0109 86.8268 71.6978 84.4724 88.135C82.3995 102.609 90.2254 128.416 116.708 122.131C143.193 115.846 157.904 75.3373 148.524 69.8375C139.144 64.3376 134.398 79.3508 135.061 89.3877C135.061 89.3877 119.722 66.0078 104.936 66.0078L104.939 66.0109Z" fill="url(#paint26_linear_35_5249)"/>
</g>
</g>
<defs>
<filter id="filter0_d_35_5249" x="52.396" y="32.3291" width="148.361" height="146.062" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="15"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_35_5249"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_35_5249" result="shape"/>
</filter>
<linearGradient id="paint0_linear_35_5249" x1="175.713" y1="47.3387" x2="149.622" y2="213.434" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint1_linear_35_5249" x1="206.535" y1="42.0196" x2="162.603" y2="109.905" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint2_linear_35_5249" x1="78.6997" y1="50.38" x2="139.682" y2="185.508" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint3_linear_35_5249" x1="36.0964" y1="49.7506" x2="152.379" y2="129.299" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint4_linear_35_5249" x1="237.341" y1="95.899" x2="99.59" y2="112.722" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint5_linear_35_5249" x1="221.505" y1="103.726" x2="129.051" y2="121.657" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint6_linear_35_5249" x1="56.4195" y1="90.1464" x2="110.346" y2="210.924" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint7_linear_35_5249" x1="54.7875" y1="87.6098" x2="117.815" y2="173.443" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint8_linear_35_5249" x1="119.946" y1="61.005" x2="145.702" y2="148.701" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint9_linear_35_5249" x1="143.408" y1="117.25" x2="152.163" y2="151.111" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint10_linear_35_5249" x1="44.3944" y1="45.8793" x2="173.975" y2="160.396" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint11_linear_35_5249" x1="93.7387" y1="13.0026" x2="142.813" y2="76.5534" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint12_linear_35_5249" x1="82.0514" y1="25.663" x2="132.444" y2="94.1455" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint13_linear_35_5249" x1="101.815" y1="18.0377" x2="119.667" y2="75.7349" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint14_linear_35_5249" x1="126.037" y1="31.7889" x2="125.804" y2="53.8102" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint15_linear_35_5249" x1="50.903" y1="59.9928" x2="69.5693" y2="68.2737" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint16_linear_35_5249" x1="42.6937" y1="82.2103" x2="52.4333" y2="105.582" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint17_linear_35_5249" x1="64.6143" y1="21.4752" x2="70.4408" y2="34.4279" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint18_linear_35_5249" x1="179.858" y1="17.1401" x2="194.564" y2="49.7311" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint19_linear_35_5249" x1="205.318" y1="125.474" x2="208.166" y2="131.788" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint20_linear_35_5249" x1="177.71" y1="88.2265" x2="193.831" y2="33.3168" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint21_linear_35_5249" x1="163.566" y1="127.366" x2="114.633" y2="86.3865" gradientUnits="userSpaceOnUse">
<stop stop-color="#EF4B9F"/>
<stop offset="1" stop-color="#E6332A"/>
</linearGradient>
<linearGradient id="paint22_linear_35_5249" x1="105.908" y1="77.905" x2="243.813" y2="201.203" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint23_linear_35_5249" x1="78.9944" y1="58.4357" x2="134.591" y2="167.436" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCEA10"/>
<stop offset="0.33" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="0.66" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.89" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="1" stop-color="#FF9500" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint24_linear_35_5249" x1="94.6244" y1="175.756" x2="163.584" y2="106.894" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCEA10"/>
<stop offset="0.33" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="0.66" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.89" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="1" stop-color="#FF9500" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint25_linear_35_5249" x1="150.016" y1="80.2977" x2="32.4086" y2="33.8185" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint26_linear_35_5249" x1="76.0368" y1="43.9024" x2="149.33" y2="125.17" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCEA10"/>
<stop offset="0.33" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="0.66" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.89" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="1" stop-color="#FF9500" stop-opacity="0"/>
</linearGradient>
<clipPath id="clip0_35_5249">
<rect width="251" height="167" fill="white" transform="translate(0.5)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,13 @@
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M40 0C62.0914 0 80 17.9086 80 40C80 62.0914 62.0914 80 40 80C17.9086 80 0 62.0914 0 40C0 17.9086 17.9086 0 40 0Z" fill="url(#paint0_linear_116_2792)"/>
<path d="M40 0C62.0914 0 80 17.9086 80 40C80 62.0914 62.0914 80 40 80C17.9086 80 0 62.0914 0 40C0 17.9086 17.9086 0 40 0Z" stroke="#E5E7EB"/>
<path d="M55 58H25V22H55V58Z" stroke="#E5E7EB"/>
<path d="M55 54.75H25V24.75H55V54.75Z" stroke="#E5E7EB"/>
<path d="M27.7891 42.3515L38.377 52.2363C38.8164 52.6464 39.3965 52.8749 40 52.8749C40.6035 52.8749 41.1836 52.6464 41.623 52.2363L52.2109 42.3515C53.9922 40.6933 55 38.3671 55 35.9355V35.5957C55 31.4999 52.041 28.0078 48.0039 27.3339C45.332 26.8886 42.6133 27.7617 40.7031 29.6718L40 30.3749L39.2969 29.6718C37.3867 27.7617 34.668 26.8886 31.9961 27.3339C27.959 28.0078 25 31.4999 25 35.5957V35.9355C25 38.3671 26.0078 40.6933 27.7891 42.3515Z" fill="#EC4899"/>
<defs>
<linearGradient id="paint0_linear_116_2792" x1="0" y1="40" x2="80" y2="40" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCE7F3"/>
<stop offset="1" stop-color="#F3E8FF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,572 @@
<svg width="252" height="153" viewBox="0 0 252 153" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M119.605 56.9129C119.605 56.9129 118.626 52.1736 118.962 47.6502C118.962 47.6502 121.523 51.4943 126.785 49.9643C131.424 48.6184 126.45 44.7584 122.593 47.9169C122.593 47.9169 115.46 45.3488 117.398 33.1436C117.398 33.1436 122.115 41.473 136.964 48.6533C151.813 55.8336 159.361 57.5573 159.361 57.5573C159.361 57.5573 152.409 53.5735 145.741 47.9708C145.741 47.9708 163.949 46.2472 172.007 58.8492C172.007 58.8492 172.599 57.2684 175.357 58.2747C178.114 59.2809 176.886 65.528 184.801 67.7532C184.801 67.7532 182.36 65.5978 180.049 61.9378C180.049 61.9378 178.782 52.891 184.801 42.803C184.801 42.803 182.879 51.8149 185.612 58.2048L189.212 58.9222C189.212 58.9222 189.449 63.2425 192.34 62.0109C194.277 61.1855 194.873 59.3444 196.585 59.2809C200.534 59.1381 202.509 65.2772 202.509 65.2772C202.509 65.2772 210.364 64.1694 216.776 70.2673C221.99 75.2287 220.724 87.6118 220.724 87.6118C220.724 87.6118 220.201 84.1613 216.848 82.098C216.848 82.098 212.007 83.501 212.527 89.2719C213.049 95.0429 215.804 100.934 215.804 100.934C215.804 100.934 213.66 94.281 219.619 92.278C219.619 92.278 218.707 97.2268 219.454 100.011C220.201 102.795 223.105 109.115 223.105 115.146C223.105 115.146 214.613 128.284 199.191 135.681C199.191 135.681 214.686 127.999 222.136 127.703C222.136 127.703 227.797 132.09 231.596 142.001C231.596 142.001 216.025 135.395 208.949 140.925C203.351 145.299 209.918 146.74 209.918 146.74H116.483C116.483 146.74 102.197 142.143 101.751 119.742C101.304 97.3411 113.374 78.5776 113.374 78.5776L119.605 56.9129Z" fill="url(#paint0_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M119.605 56.9129C119.605 56.9129 118.626 52.1736 118.962 47.6502C118.962 47.6502 121.523 51.4943 126.785 49.9643C131.424 48.6184 126.45 44.7584 122.593 47.9169C122.593 47.9169 115.46 45.3488 117.398 33.1436C117.398 33.1436 122.115 41.473 136.964 48.6533C151.813 55.8336 159.361 57.5573 159.361 57.5573C159.361 57.5573 152.409 53.5735 145.741 47.9708C145.741 47.9708 163.949 46.2472 172.007 58.8492C172.007 58.8492 172.599 57.2684 175.357 58.2747C178.114 59.2809 176.886 65.528 184.801 67.7532C184.801 67.7532 182.36 65.5978 180.049 61.9378C180.049 61.9378 178.782 52.891 184.801 42.803C184.801 42.803 182.879 51.8149 185.612 58.2048L189.212 58.9222C189.212 58.9222 189.449 63.2425 192.34 62.0109C194.277 61.1855 194.873 59.3444 196.585 59.2809C200.534 59.1381 202.509 65.2772 202.509 65.2772C202.509 65.2772 210.364 64.1694 216.776 70.2673C221.99 75.2287 220.724 87.6118 220.724 87.6118C220.724 87.6118 220.201 84.1613 216.848 82.098C216.848 82.098 212.007 83.501 212.527 89.2719C213.049 95.0429 215.804 100.934 215.804 100.934C215.804 100.934 213.66 94.281 219.619 92.278C219.619 92.278 218.707 97.2268 219.454 100.011C220.201 102.795 223.105 109.115 223.105 115.146C223.105 115.146 214.613 128.284 199.191 135.681C199.191 135.681 214.686 127.999 222.136 127.703C222.136 127.703 227.797 132.09 231.596 142.001C231.596 142.001 216.025 135.395 208.949 140.925C203.351 145.299 209.918 146.74 209.918 146.74H116.483C116.483 146.74 102.197 142.143 101.751 119.742C101.304 97.3411 113.374 78.5776 113.374 78.5776L119.605 56.9129Z" fill="url(#paint1_linear_36_5540)"/>
<path d="M106.503 43.3428C106.503 43.3428 128.644 45.8568 131.021 82.9583C132.12 100.109 146.586 112.553 146.586 112.553C146.586 112.553 137.73 122.336 120.44 116.597C120.44 116.597 118.588 118.879 123.356 126.92C128.124 134.96 128.533 144.728 124.008 146.737H101.741L96.8369 52.6816L106.506 43.3428H106.503Z" fill="url(#paint2_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M136.964 146.737C136.964 146.737 142.65 126.52 138.68 102.817C134.71 79.1143 125.696 59.7129 125.696 59.7129C125.696 59.7129 133.041 65.1695 136.02 71.3436V65.4203C136.02 65.4203 153.662 64.3061 168.457 72.6356C183.253 80.965 171.668 132.951 149.898 146.737L145.259 146.81C145.259 146.81 150.54 143.721 151.439 134.246C151.439 134.246 143.052 138.436 138.183 146.81L136.964 146.737Z" fill="url(#paint3_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M208.952 140.922C216.029 135.392 231.6 141.998 231.6 141.998C227.8 132.088 222.139 127.701 222.139 127.701C215.373 127.967 201.971 134.329 199.568 135.497C199.445 135.557 199.318 135.621 199.194 135.681C199.194 135.681 199.327 135.614 199.568 135.497C214.75 128.069 223.108 115.143 223.108 115.143C223.108 109.112 220.202 102.792 219.458 100.008C218.713 97.2241 219.622 92.2754 219.622 92.2754C213.664 94.2784 215.807 100.932 215.807 100.932C215.807 100.932 213.049 95.037 212.53 89.2693C212.008 83.4984 216.852 82.0953 216.852 82.0953C219.682 83.838 220.493 86.5679 220.68 87.3774C217.596 71.3534 194.676 64.2905 194.676 64.2905C194.676 64.2905 206.049 73.0675 209.478 81.8382C212.904 90.6057 209.478 110.807 209.478 110.807C209.478 110.807 219.461 110.407 216.481 114.854C213.502 119.302 200.762 124.552 200.762 124.552L194.676 124.12C185.685 131.443 164.231 138.77 164.231 138.77C166.59 131.878 167.755 117.397 167.755 117.397C160.169 139.611 141.834 146.74 141.834 146.74H209.924C209.924 146.74 203.358 145.299 208.956 140.925L208.952 140.922Z" fill="url(#paint4_linear_36_5540)"/>
<path d="M45.4319 146.738C45.4319 146.738 32.4698 142.862 35.7467 131.659C39.0237 120.457 24.3361 117.315 22.6359 112.594C19.8434 104.84 27.9993 102.218 23.6775 94.3199C23.6775 94.3199 27.328 96.6181 27.5528 104.157C27.7776 111.696 35.1515 112.845 44.0926 112.702C44.0926 112.702 25.3777 88.0697 31.2793 75.5089C36.8295 63.6972 53.0337 59.2817 53.4801 56.5518C53.9265 53.8219 47.5215 52.6759 47.5215 52.6759C47.5215 52.6759 54.7117 46.8955 60.7432 50.6666C66.7778 54.4377 75.7157 58.5294 84.6568 54.2218C84.6568 54.2218 86.0341 49.7334 78.9578 44.9243C72.6414 40.6326 69.9059 35.8044 81.0443 25.2498C90.5046 16.2855 85.6636 11.4637 85.6636 11.4637C85.6636 11.4637 92.9267 10.4955 92.4803 23.4182C92.0339 36.3441 96.6215 41.296 106.506 43.3434C116.391 45.3909 123.768 55.6217 125.557 67.6841C127.345 79.7466 122.017 85.2762 129.616 96.7641C137.214 108.252 139.874 109.35 138.683 119.4C138.683 119.4 136.106 118.34 126.114 111.95C113.487 103.871 113.041 95.688 113.041 95.688C113.041 95.688 114.941 100.751 111.366 106.135C107.792 111.518 102.761 117.553 107.342 129.615L108.57 122.508C108.57 122.508 119.522 129.615 120.861 135.862C122.201 142.109 116.486 146.741 116.486 146.741H45.435L45.4319 146.738Z" fill="url(#paint5_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M45.4319 146.738C45.4319 146.738 32.4698 142.862 35.7467 131.659C39.0237 120.457 24.3361 117.315 22.6359 112.594C19.8434 104.84 27.9993 102.218 23.6775 94.3199C23.6775 94.3199 27.328 96.6181 27.5528 104.157C27.7776 111.696 35.1515 112.845 44.0926 112.702C44.0926 112.702 25.3777 88.0697 31.2793 75.5089C36.8295 63.6972 53.0337 59.2817 53.4801 56.5518C53.9265 53.8219 47.5215 52.6759 47.5215 52.6759C47.5215 52.6759 54.7117 46.8955 60.7432 50.6666C66.7778 54.4377 75.7157 58.5294 84.6568 54.2218C84.6568 54.2218 86.0341 49.7334 78.9578 44.9243C72.6414 40.6326 69.9059 35.8044 81.0443 25.2498C90.5046 16.2855 85.6636 11.4637 85.6636 11.4637C85.6636 11.4637 92.9267 10.4955 92.4803 23.4182C92.0339 36.3441 96.6215 41.296 106.506 43.3434C116.391 45.3909 123.768 55.6217 125.557 67.6841C127.345 79.7466 122.017 85.2762 129.616 96.7641C137.214 108.252 139.874 109.35 138.683 119.4C138.683 119.4 136.106 118.34 126.114 111.95C113.487 103.871 113.041 95.688 113.041 95.688C113.041 95.688 114.941 100.751 111.366 106.135C107.792 111.518 102.761 117.553 107.342 129.615L108.57 122.508C108.57 122.508 119.522 129.615 120.861 135.862C122.201 142.109 116.486 146.741 116.486 146.741H45.435L45.4319 146.738Z" fill="url(#paint6_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M45.4319 146.737C45.4319 146.737 37.7224 142.537 38.726 133.167C39.7329 123.796 31.2477 119.342 26.7708 115.397C22.2939 111.448 24.7603 104.591 24.7603 104.591C24.7603 104.591 22.6359 112.273 31.2793 114.356C39.9197 116.438 51.3936 122.183 51.3936 122.183C51.3936 122.183 44.2034 129.722 48.674 137.639C53.1445 145.556 78.5114 146.74 78.5114 146.74H45.435L45.4319 146.737Z" fill="url(#paint7_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M78.5083 146.737C78.5083 146.737 52.8818 147.454 42.6015 134.96C42.6015 134.96 41.8574 121.951 56.0099 115.603C70.1656 109.254 66.1415 93.665 66.1415 93.665C66.1415 93.665 75.6778 117.009 66.7367 128.354C66.7367 128.354 53.6987 124.332 50.7954 130.148C47.8889 135.963 55.1519 145.226 78.5083 146.733V146.737Z" fill="url(#paint8_linear_36_5540)"/>
<path d="M186.624 117.742C186.624 117.742 190.652 127.347 179.387 130.484C168.122 133.62 154.577 123.909 142.337 124.176C130.097 124.44 118.854 123.732 119.642 108.409C120.431 93.0868 138.23 64.6925 173.719 70.2475C202.442 74.7424 207.337 95.9913 207.337 103.756C207.337 109.714 204.247 121.697 186.621 117.745L186.624 117.742Z" fill="url(#paint9_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.35" d="M156.92 102.962C156.92 102.962 163.867 103.339 171.94 107.856C180.014 112.373 185.478 113.713 192.406 107.774C200.217 101.076 207.217 101.46 207.217 101.46C207.302 102.311 207.34 103.085 207.34 103.755C207.34 109.713 204.25 121.696 186.625 117.744C179.343 116.167 176.632 112.878 170.541 108.612C164.449 104.346 156.917 102.965 156.917 102.965L156.92 102.962Z" fill="url(#paint10_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.35" d="M179.387 130.483C168.122 133.62 154.577 123.909 142.337 124.176C130.097 124.439 118.854 123.732 119.642 108.409C120.193 97.7243 129.014 80.6813 146.342 73.228C129.381 88.2965 136.176 116.005 146.342 120.91C152.934 124.09 159.124 120.944 169.499 117.745C178.218 115.056 186.624 117.745 186.624 117.745C186.624 117.745 190.649 127.35 179.387 130.486V130.483Z" fill="url(#paint11_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M186.625 117.742C186.625 117.742 176.009 113.882 169.749 114.885C163.49 115.888 159.234 117.043 156.92 117.03C154.606 117.018 146.26 108.733 143.423 101.454C140.586 94.1785 141.463 79.7449 146.719 80.3734C151.975 81.0019 152.478 88.4044 152.478 93.4262C152.478 98.4448 148.223 98.4448 147.346 96.6894C146.469 94.934 146.846 89.5377 146.846 89.5377C146.846 89.5377 147.305 95.4356 148.182 96.1053C149.059 96.7751 151.56 96.2323 151.436 93.8896C151.313 91.547 151.436 85.3571 149.727 83.4747C148.017 81.5923 145.845 81.2146 145.012 82.3859C144.18 83.5572 142.53 88.16 143.217 93.6802C143.904 99.2035 144.718 103.279 148.223 107.923C151.728 112.567 154.542 115.266 157.487 115.704C160.428 116.142 168.954 112.755 173.507 113.443C178.06 114.132 186.628 117.745 186.628 117.745L186.625 117.742Z" fill="url(#paint12_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M147.197 92.3371C147.52 94.01 147.406 96.7082 149.302 96.359C149.302 96.359 147.925 97.6922 147.342 96.686C146.76 95.6797 147.197 92.334 147.197 92.334V92.3371Z" fill="url(#paint13_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M197.411 111.745C197.411 111.745 204.294 109.234 200.789 106.476C197.285 103.714 192.029 104.216 188.774 105.222C185.519 106.225 169.622 103.292 167.371 99.048C165.117 94.8039 170.999 89.5346 174.131 85.7698C177.259 82.0051 169.625 75.1041 166.494 74.4756C166.494 74.4756 171.626 76.9008 172.712 81.002C173.798 85.1 172.294 87.1919 169.29 89.7853C166.285 92.3788 163.448 97.0545 167.203 100.321C170.958 103.587 178.041 105.428 183.597 106.016C189.151 106.6 195.242 105.095 197.161 105.765C199.08 106.435 203.671 108.025 197.411 111.745Z" fill="url(#paint14_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M170.066 102.248C170.066 102.248 166.035 100.388 165.513 97.6164C164.99 94.8452 167.409 91.7471 167.409 91.7471C167.409 91.7471 164.455 98.6449 170.066 102.245V102.248Z" fill="url(#paint15_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M166.494 74.4757C166.494 74.4757 161.53 71.8823 154.602 73.8059C147.675 75.7296 149.261 78.8245 153.767 81.0021C158.272 83.1765 158.94 86.8587 161.945 86.189C164.949 85.5192 168.286 82.0909 164.449 78.6595C160.612 75.228 157.939 75.8978 157.939 75.8978C157.939 75.8978 162.92 77.2374 164.227 80.0816C165.535 82.9258 163.366 84.5161 161.362 84.7669C159.358 85.0176 158.19 81.3799 155.438 80.3545C152.684 79.3292 150.097 77.6564 151.933 75.9835C153.77 74.3106 160.362 72.6378 166.494 74.4789V74.4757Z" fill="url(#paint16_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M163.99 83.4492C163.99 83.4492 162.613 86.4743 161.112 86.2616C159.611 86.0489 158.297 84.4173 157.578 83.573C156.86 82.7254 159.678 84.703 160.988 84.7951C162.739 84.9221 163.99 83.4492 163.99 83.4492Z" fill="url(#paint17_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M174.131 85.7697C174.131 85.7697 182.518 84.1317 188.401 86.2681C194.283 88.4044 199.166 92.9214 192.656 94.1785C186.146 95.4323 180.891 100.321 178.136 97.0004C175.382 93.6769 175.967 89.6201 184.31 89.7884C184.31 89.7884 180.223 88.6996 176.8 90.1217C173.378 91.5438 174.796 95.7466 177.218 97.6194C179.637 99.4923 182.224 100.181 187.147 97.6194C192.07 95.0578 196.911 95.1847 197.076 93.1119C197.244 91.0422 195.575 87.2775 190.734 85.6872C185.893 84.0968 177.465 84.2651 174.128 85.7697H174.131Z" fill="url(#paint18_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M156.92 117.03C156.92 117.03 148.72 117.519 144.091 113.881C139.459 110.243 139.459 105.349 133.326 106.101C127.193 106.853 121.684 108.736 121.184 113.129C120.684 117.522 123.457 120.385 123.457 120.385C123.457 120.385 120.81 117.897 120.519 114.465C120.228 111.034 120.437 107.898 128.029 105.888C135.621 103.879 137.625 104.298 139.753 107.393C141.881 110.488 145.136 115.7 156.923 117.027L156.92 117.03Z" fill="url(#paint19_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M124.964 106.917C124.964 106.917 130.195 105.346 132.946 101.581C135.701 97.8161 138.892 85.6997 135.824 86.2679C132.756 86.8361 125.56 93.426 125.936 96.1242C126.313 98.8223 127.063 100.892 127.063 100.892C127.063 100.892 126.272 96.8765 127.481 94.8671C128.691 92.8578 133.7 87.8392 135.137 88.0265C136.574 88.2137 132.946 99.4477 131.445 101.267C129.945 103.085 124.964 106.917 124.964 106.917Z" fill="url(#paint20_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M168.454 101.454C168.454 101.454 162.53 98.2542 156.604 99.2891C150.68 100.327 146.339 103.632 150.721 109.11C154.485 113.821 157.651 114.427 163.471 112.523C169.29 110.618 172.877 113.437 172.877 113.437C172.877 113.437 170.709 110.615 166.494 109.987C162.28 109.358 159.361 107.52 159.361 107.52C159.361 107.52 163.784 110.196 162.366 110.952C160.947 111.704 156.442 113.043 153.187 109.99C149.933 106.936 149.432 104.009 151.851 102.168C154.27 100.327 159.947 99.3176 168.457 101.454H168.454Z" fill="url(#paint21_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M174.131 85.7701C174.131 85.7701 179.814 75.1361 190.487 80.3547C201.16 85.5733 206.077 95.7057 206.077 95.7057C206.077 95.7057 205.425 92.9663 203.088 89.284C200.752 85.6018 193.178 79.1611 187.327 77.6533C181.476 76.1487 166.498 74.4727 166.498 74.4727C166.498 74.4727 174.799 75.7265 178.389 76.8978C181.977 78.0692 175.385 79.2405 174.134 85.7669L174.131 85.7701Z" fill="url(#paint22_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M158.193 127.432C158.193 127.432 163.531 129.191 171.041 127.851C178.551 126.512 180.219 122.998 176.714 121.576C173.209 120.154 172.124 121.408 167.368 122.414C162.613 123.417 167.45 125.845 167.45 125.845C167.45 125.845 161.026 124.255 165.196 121.998C169.369 119.738 174.124 118.716 177.047 120.389C179.966 122.061 181.767 126.286 185.522 126.874L185.06 127.391C185.06 127.391 183.354 127.407 182.512 126.559C181.669 125.712 180.416 123.766 179.975 124.645C179.535 125.525 175.619 128.61 171.183 128.943C165.332 129.381 161.124 128.439 158.189 127.436L158.193 127.432Z" fill="url(#paint23_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M125.104 121.592C125.104 121.592 125.063 116.183 130.071 112.519C135.077 108.856 142.755 117.306 150.683 120.15C158.611 122.995 151.601 125.337 144.632 120.82C139.567 117.535 134.995 114.38 133.076 115.633C131.157 116.887 132.076 120.738 132.076 120.738C132.076 120.738 132.576 116.471 134.494 116.471C136.413 116.471 143.923 122.83 149.597 124.001C155.27 125.172 157.148 122.931 155.27 121.239C154.121 120.201 151.183 120.011 147.678 117.738C144.173 115.465 136.245 109.526 132.49 109.986C128.735 110.447 124.619 113.754 124.008 120.842C124.008 120.842 124.926 121.484 125.104 121.592Z" fill="url(#paint24_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M168.454 101.454C168.454 101.454 162.112 98.2541 162.112 93.9275C162.112 89.5978 159.985 88.2804 154.666 84.2649C149.347 80.2494 144.012 78.767 150.559 71.666L149.6 71.9803C149.6 71.9803 146.814 74.0118 146.592 77.0719C146.465 78.8082 148.314 80.4399 150.534 82.1953C152.753 83.9507 158.389 87.8106 159.703 89.6612C161.017 91.5119 159.887 94.7211 162.207 97.3177C165.408 100.895 168.454 101.457 168.454 101.457V101.454Z" fill="url(#paint25_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M188.774 105.222C188.774 105.222 192.216 102.396 196.098 100.828C199.979 99.2602 207.315 102.771 207.315 102.771L207.217 101.46C207.217 101.46 200.375 98.2381 196.411 99.8252C192.447 101.416 188.774 105.222 188.774 105.222Z" fill="url(#paint26_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M172.503 79.3672C172.503 79.3672 177.468 82.7986 172.947 87.1348C172.947 87.1348 173.963 82.7986 172.503 79.3672Z" fill="url(#paint27_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M136.4 86.3379C136.945 86.5474 137.705 89.0329 135.327 93.3405C135.327 93.3405 136.369 88.7853 135.14 88.0267C134.453 87.6045 131.452 90.4137 131.452 90.4137C131.452 90.4137 134.659 85.665 136.4 86.3347V86.3379Z" fill="url(#paint28_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.5" d="M194.704 89.0012C194.704 89.0012 197.912 91.5439 196.921 93.6199C196.348 94.8229 193.562 95.1499 193.562 95.1499C193.562 95.1499 195.657 93.8008 195.752 92.3565C195.847 90.9122 194.704 88.998 194.704 88.998V89.0012Z" fill="url(#paint29_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.2" d="M192.656 94.1756C186.146 95.4295 180.891 100.318 178.136 96.9976C176.648 95.2009 176.135 93.1915 177.297 91.7472C177.645 94.2137 180.891 96.058 184.145 94.4264C187.4 92.7948 191.842 93.674 192.783 91.2266C193.72 88.7792 191.225 87.5127 191.225 87.5127C195.6 89.8236 198.076 93.1312 192.659 94.1756H192.656Z" fill="url(#paint30_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.2" d="M148.178 96.1021C147.485 95.572 147.051 91.766 146.902 90.2011C146.902 90.2106 147.387 92.3786 148.938 92.4612C150.486 92.5437 151.243 89.4233 151.246 89.4043C151.373 91.1756 151.379 92.8834 151.433 93.8833C151.56 96.2259 149.055 96.7687 148.178 96.099V96.1021Z" fill="url(#paint31_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.2" d="M162.362 110.952C160.944 111.704 156.439 113.044 153.184 109.99C149.929 106.936 149.429 104.01 151.848 102.168C151.164 103.829 152.661 105.847 155.666 106.308C158.671 106.768 159.358 107.52 159.358 107.52C159.358 107.52 159.374 107.53 159.399 107.546C159.833 107.812 163.702 110.241 162.362 110.952Z" fill="url(#paint32_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.2" d="M150.724 109.11C154.488 113.821 157.654 114.427 163.474 112.523C168.742 110.796 172.181 112.945 172.785 113.367C168.106 113.084 160.279 116.116 157.484 115.697C154.542 115.259 151.724 112.561 148.219 107.917C144.715 103.273 143.901 99.1936 143.214 93.6735C143.033 92.2165 143.014 90.8261 143.1 89.5469C143.1 89.6643 143.157 90.7119 144.464 95.7051C145.87 101.06 149.53 102.92 149.53 102.92C148.536 104.514 148.701 106.58 150.724 109.11Z" fill="url(#paint33_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" opacity="0.2" d="M162.113 93.9243C162.113 90.4103 160.71 88.8803 157.335 86.2774C158.057 86.8075 160.659 88.5121 165.054 89.2168C170.174 90.0389 172.358 86.2869 172.45 86.125C171.82 87.4106 170.728 88.5406 169.287 89.7818C166.282 92.3752 163.446 97.051 167.201 100.317C167.691 100.743 168.239 101.146 168.831 101.524L168.825 101.549L168.451 101.451C168.451 101.451 162.109 98.2509 162.109 93.9243H162.113Z" fill="url(#paint34_linear_36_5540)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M199.957 97.4382C196.766 96.9398 194.448 97.0604 191.279 99.5713C188.106 102.082 180.071 105.218 174.847 101.663C169.623 98.1079 172.709 95.5653 175.341 97.2763C177.968 98.9872 179.852 102.301 186.925 99.2888C192.235 97.0255 194.742 95.5431 198.706 96.3652C202.67 97.1874 202.629 97.8572 199.957 97.4382Z" fill="url(#paint35_linear_36_5540)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M154.767 91.026C156.363 91.4292 158.389 91.9783 159.057 93.7877C159.725 95.597 160.691 90.3721 158.171 88.5056C155.651 86.6359 153.802 87.8803 153.615 88.9214C153.428 89.9626 153.732 90.7626 154.767 91.026Z" fill="url(#paint36_linear_36_5540)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M141.83 83.5729C140.89 88.6296 140.209 96.8098 142.9 102.301C145.592 107.793 147.58 113.253 143.049 107.917C138.519 102.584 138.082 95.6798 139.145 88.9978C140.209 82.3159 142.274 81.1953 141.83 83.5697V83.5729Z" fill="url(#paint37_linear_36_5540)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M193.824 79.0339C188.065 76.1484 180.014 73.428 166.684 72.8757C157.271 72.4853 154.479 71.1489 159.019 70.3966C163.556 69.6442 180.599 69.8728 193.827 79.0339H193.824Z" fill="url(#paint38_linear_36_5540)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M170.667 106.308C170.667 106.308 163.24 102.825 158.484 102.644C153.729 102.46 156.86 100.311 161.039 100.886C165.18 101.457 169.531 104.663 170.667 106.311V106.308Z" fill="url(#paint39_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.35" d="M189.86 91.7501C189.86 91.7501 187.274 91.766 185.019 92.8262C182.765 93.8864 180.178 93.9944 179.764 92.2866C179.349 90.5788 184.269 89.3186 189.86 91.7469V91.7501Z" fill="url(#paint40_linear_36_5540)"/>
<path style="mix-blend-mode:screen" opacity="0.7" d="M157.917 76.5229C157.917 76.5229 162.337 77.1419 163.382 80.2464C164.117 82.4335 162.198 84.1286 161.198 83.2525C160.197 82.3732 160.938 79.0751 157.917 76.5229Z" fill="url(#paint41_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M181.546 93.6005C181.546 93.6005 180.185 93.8957 179.542 92.9847C178.899 92.0736 180.394 91.1309 180.394 91.1309C180.394 91.1309 180.181 92.553 181.543 93.6005H181.546Z" fill="url(#paint42_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M161.109 77.6562C161.109 77.6562 163.544 78.9609 163.708 81.1575C163.924 84.0271 162.458 83.9509 162.113 82.0051C161.767 80.0592 161.669 78.6117 161.109 77.6562Z" fill="url(#paint43_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M167.753 71.2151C167.77 70.8807 166.17 70.5254 164.18 70.4215C162.189 70.3176 160.561 70.5045 160.544 70.8389C160.526 71.1733 162.126 71.5286 164.117 71.6325C166.107 71.7364 167.735 71.5495 167.753 71.2151Z" fill="url(#paint44_linear_36_5540)"/>
<path d="M156.689 65.3016C156.892 65.3016 156.892 64.9873 156.689 64.9873C156.486 64.9873 156.486 65.3016 156.689 65.3016Z" fill="url(#paint45_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M155.889 89.112C156.007 88.4136 155.564 87.7564 154.9 87.644C154.236 87.5317 153.603 88.0068 153.485 88.7052C153.368 89.4036 153.811 90.0608 154.474 90.1731C155.138 90.2854 155.772 89.8104 155.889 89.112Z" fill="url(#paint46_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M201.353 97.7338C201.353 97.7338 197.481 96.7974 194.673 97.6322C194.673 97.6322 196.952 95.9339 200.039 96.4577C203.126 96.9815 202.689 98.1528 201.353 97.7338Z" fill="url(#paint47_linear_36_5540)"/>
<path opacity="0.7" d="M122.387 99.0483C119.243 106.803 126.709 103.264 126.709 103.264C126.709 103.264 124.461 96.3026 125.696 93.8425C126.93 91.3824 130.73 87.4589 136.615 84.5671C136.615 84.5671 141.026 77.136 142.761 75.5679C142.761 75.5679 129.295 82.0086 122.387 99.0515V99.0483Z" fill="url(#paint48_linear_36_5540)"/>
<path opacity="0.7" d="M123.207 119.24C123.207 119.24 121.048 113.863 122.834 110.876C124.622 107.889 133.735 106.095 135.223 106.479C136.714 106.863 137.657 110.098 137.657 110.098C137.657 110.098 134.07 108.485 131.449 109.304C128.346 110.276 124.546 111.768 123.204 119.237L123.207 119.24Z" fill="url(#paint49_linear_36_5540)"/>
<path opacity="0.7" d="M131.195 119.399C131.195 119.399 127.254 113.421 131.791 112.041C136.331 110.66 143.046 119.399 143.046 119.399C143.046 119.399 137.759 115.532 134.713 114.863C131.667 114.19 130.996 117.786 131.192 119.399H131.195Z" fill="url(#paint50_linear_36_5540)"/>
<path opacity="0.7" d="M127.602 97.766C127.602 97.766 127.706 95.8868 128.533 94.4044C129.359 92.922 134.007 89.1382 134.007 89.1382C134.007 89.1382 130.99 95.1123 127.599 97.766H127.602Z" fill="url(#paint51_linear_36_5540)"/>
<path d="M97.6729 68.0971C81.6935 56.3711 68.171 70.7127 68.171 70.7127C68.171 70.7127 54.6486 56.3711 38.6692 68.0971C23.3896 79.3088 24.8143 121.127 68.171 138.605C111.528 121.127 112.952 79.3088 97.6729 68.0971Z" fill="url(#paint52_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" d="M68.1742 138.605C111.531 121.127 112.956 79.3083 97.676 68.0966C92.4171 64.2366 87.4242 63.2018 83.0929 63.5478C85.0939 63.4176 93.4778 63.5605 95.0513 74.7246C96.8402 87.4219 83.5774 115.953 61.3038 118.718C42.1076 121.098 33.9707 107.82 33.6826 107.338C38.8972 119.343 49.7443 131.174 68.1774 138.605H68.1742Z" fill="url(#paint53_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M45.4319 66.7002C45.4319 66.7002 35.3384 69.5317 32.359 82.6448C29.3797 95.7579 36.6428 108.912 36.6428 108.912C36.6428 108.912 35.1515 93.2185 45.4319 66.7002Z" fill="url(#paint54_linear_36_5540)"/>
<path opacity="0.7" d="M61.5225 133.901C61.5225 133.901 78.8791 133.066 94.5387 113.566C94.5387 113.566 87.0002 126.933 69.1211 137.259C69.1211 137.259 64.4828 136.307 61.5225 133.897V133.901Z" fill="url(#paint55_linear_36_5540)"/>
<path style="mix-blend-mode:multiply" d="M63.3109 71.2169C68.3766 78.6861 75.4529 81.1494 75.4529 81.1494C75.4529 81.1494 79.743 63.1256 72.5971 67.2173C69.8014 68.9822 68.174 70.7091 68.174 70.7091C68.174 70.7091 60.6165 62.6939 50.124 63.551C50.2507 63.5541 58.2863 63.8049 63.314 71.2138L63.3109 71.2169Z" fill="url(#paint56_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M45.4316 146.737C45.4316 146.737 83.425 133.897 97.2799 110.807C111.135 87.717 102.643 68.4742 91.0236 58.7068C79.4008 48.9426 82.7158 36.1787 86.7019 29.411C91.9925 20.4372 88.1931 12.895 88.1931 12.895C88.1931 12.895 92.3661 15.1202 91.4701 25.459C90.5772 35.7978 94.4494 42.4765 103.612 45.3493C103.612 45.3493 93.8161 49.7997 100.297 58.7418C106.778 67.6807 117.505 93.1324 108.754 107.503C98.9801 123.549 102.944 130.507 110.543 135.103C110.543 135.103 108.456 128.713 109.799 124.765C109.799 124.765 124.512 133.811 116.485 146.737C116.485 146.737 110.695 138.874 98.8471 137.043C87.0027 135.211 82.8677 133.272 82.8677 133.272C82.8677 133.272 85.7742 138.335 95.6082 141.242C95.6082 141.242 87.8987 147.705 73.705 143.934C59.5145 140.163 54.5975 146.734 54.5975 146.734H45.4348L45.4316 146.737Z" fill="url(#paint57_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M124.005 146.737C124.005 146.737 129.593 136.399 120.44 122.755C111.287 109.112 118.981 100.579 119.604 85.8188C120.228 71.0582 114.716 62.4399 114.716 62.4399C114.716 62.4399 120.443 82.9556 108.906 101.205C98.901 117.029 91.3245 130.65 110.397 139.985C110.397 139.985 111.553 135.979 116.489 137.364C122.428 139.033 124.011 146.734 124.011 146.734L124.005 146.737Z" fill="url(#paint58_linear_36_5540)"/>
<path d="M78.3594 16.9895C78.3594 16.9895 81.307 11.5011 75.5288 7.45387C72.3437 5.22549 65.5303 6.22223 65.5303 6.22223C65.5303 6.22223 72.8756 14.6024 78.3594 16.9864V16.9895Z" fill="url(#paint59_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M64.2039 55.1892C64.2039 55.1892 76.3839 60.4681 85.6574 55.1892C85.6574 55.1892 86.6611 50.0087 79.0592 44.0949C71.4574 38.1811 74.5222 30.7278 82.9219 24.2236C82.9219 24.2236 78.4988 34.7179 83.4981 43.1966C89.3839 53.1766 89.7544 64.5217 89.7544 64.5217C89.7544 64.5217 72.917 62.9408 64.2007 55.186L64.2039 55.1892Z" fill="url(#paint60_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M86.5534 11.8916C86.5534 11.8916 89.4599 16.2722 86.107 20.8654C82.7541 25.4618 73.5818 32.5533 75.225 39.2733C76.8683 45.9965 85.2869 44.6316 86.851 55.402C86.851 55.402 85.4358 45.2061 81.3768 40.7557C77.3178 36.3053 88.6367 25.4841 89.7575 21.2971C89.7575 21.2971 90.2799 14.5485 86.5534 11.8916Z" fill="url(#paint61_linear_36_5540)"/>
<path d="M100.589 35.4418C100.589 35.4418 97.8786 33.661 100.589 29.6265C100.589 29.6265 103.666 31.8802 100.589 35.4418Z" fill="url(#paint62_linear_36_5540)"/>
<path d="M38.1529 54.072C38.1529 54.072 25.5518 58.97 23.7661 51.5516C23.7661 51.5516 24.0447 50.4723 27.3058 49.7041C27.3058 49.7041 31.1906 53.9387 38.1529 54.072Z" fill="url(#paint63_linear_36_5540)"/>
<path d="M21.5943 90.1573C21.5943 90.1573 17.2725 87.5004 18.0197 80.9676C18.7669 74.4349 16.6045 70.8447 16.6045 70.8447C16.6045 70.8447 20.6255 74.6507 22.0407 81.1137C22.0407 81.1137 19.5838 84.561 21.5943 90.1605V90.1573Z" fill="url(#paint64_linear_36_5540)"/>
<path d="M39.5493 16.6434C39.5493 16.6434 37.3299 14.5769 38.4919 11.4438C38.4919 11.4438 41.5123 13.5326 42.2089 17.2656L39.5493 16.6434Z" fill="url(#paint65_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M114.738 118.485C114.738 118.485 125.047 121.945 128.577 125.897L126.614 125.875C126.614 125.875 128.916 131.706 125.703 139.896C125.703 139.896 122.489 128.304 114.738 118.485Z" fill="url(#paint66_linear_36_5540)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M159.811 146.81C159.811 146.81 174.163 136.938 191.035 132.199L179.384 141.569L185.529 140.922L187.328 138.338C187.328 138.338 207.578 132.523 218.416 130.475C218.416 130.475 201.024 135.967 184.801 144.801C184.801 144.801 174.261 142.645 168.458 146.74L159.814 146.813L159.811 146.81Z" fill="url(#paint67_linear_36_5540)"/>
<path d="M187.324 36.2321L199.789 7.29492C199.789 7.29492 199.492 17.9226 198.523 20.7953L187.324 36.2321Z" fill="url(#paint68_linear_36_5540)"/>
<path d="M159.637 43.3428L151.703 36.8799L154.049 35.4419L159.637 43.3428Z" fill="url(#paint69_linear_36_5540)"/>
<path d="M192.042 48.2246L195.097 43.917C195.097 43.917 199.194 42.0505 202.101 41.9775L192.045 48.2246H192.042Z" fill="url(#paint70_linear_36_5540)"/>
<path d="M225.34 100.932C225.34 100.932 226.084 90.755 227.724 85.4284L232.267 82.543L225.34 100.932Z" fill="url(#paint71_linear_36_5540)"/>
<path d="M114.713 27.0232L110.803 18.9985C110.803 18.9985 111.753 18.0843 113.374 17.3828L114.716 27.0232H114.713Z" fill="url(#paint72_linear_36_5540)"/>
<path d="M229.139 125.723C229.139 125.723 233.679 126.088 235.395 124.497V121.605L229.139 125.723Z" fill="url(#paint73_linear_36_5540)"/>
<defs>
<linearGradient id="paint0_linear_36_5540" x1="123.125" y1="44.6187" x2="216.449" y2="187.525" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint1_linear_36_5540" x1="174.375" y1="14.9039" x2="164.98" y2="153.517" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint2_linear_36_5540" x1="99.164" y1="91.5544" x2="142.541" y2="116.151" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF8F01" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FF8203" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FF6C08" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FF540D" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint3_linear_36_5540" x1="177.984" y1="32.1121" x2="106.963" y2="166.641" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint4_linear_36_5540" x1="162.347" y1="91.3739" x2="212.994" y2="154.929" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint5_linear_36_5540" x1="52.8406" y1="41.3563" x2="115.211" y2="178.731" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint6_linear_36_5540" x1="66.3282" y1="51.8919" x2="90.773" y2="154.819" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint7_linear_36_5540" x1="30.282" y1="81.8663" x2="85.4324" y2="204.657" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint8_linear_36_5540" x1="28.6294" y1="79.2822" x2="92.9459" y2="166.353" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint9_linear_36_5540" x1="147.456" y1="80.9323" x2="185.138" y2="137.307" gradientUnits="userSpaceOnUse">
<stop stop-color="#55EDF2"/>
<stop offset="0.65" stop-color="#3FBDE6"/>
<stop offset="1" stop-color="#36A9E1"/>
</linearGradient>
<linearGradient id="paint10_linear_36_5540" x1="222.357" y1="114.011" x2="152.503" y2="102.022" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint11_linear_36_5540" x1="145.566" y1="135.035" x2="166.316" y2="80.1042" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint12_linear_36_5540" x1="192.909" y1="139.165" x2="118.67" y2="65.7131" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint13_linear_36_5540" x1="150.046" y1="97.0637" x2="144.861" y2="92.3727" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint14_linear_36_5540" x1="200.175" y1="119.1" x2="136.554" y2="57.3308" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint15_linear_36_5540" x1="172.624" y1="101.222" x2="160.718" y2="89.6638" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint16_linear_36_5540" x1="185.266" y1="94.8612" x2="143.407" y2="67.1762" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint17_linear_36_5540" x1="167.108" y1="85.3918" x2="155.007" y2="83.977" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint18_linear_36_5540" x1="209.041" y1="95.007" x2="164.972" y2="89.856" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint19_linear_36_5540" x1="175.752" y1="117.97" x2="107.236" y2="109.957" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint20_linear_36_5540" x1="141.365" y1="97.8573" x2="122.167" y2="95.6119" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint21_linear_36_5540" x1="186.112" y1="109.504" x2="139.912" y2="104.1" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint22_linear_36_5540" x1="228.268" y1="90.0237" x2="150.402" y2="80.9188" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint23_linear_36_5540" x1="199.381" y1="127.737" x2="148.906" y2="121.835" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint24_linear_36_5540" x1="172.646" y1="120.89" x2="113.072" y2="113.923" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint25_linear_36_5540" x1="182.534" y1="89.2645" x2="136.774" y2="83.9146" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint26_linear_36_5540" x1="216.408" y1="104.907" x2="182.617" y2="100.957" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint27_linear_36_5540" x1="176.296" y1="83.6716" x2="171.034" y2="83.0557" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint28_linear_36_5540" x1="139.459" y1="90.2455" x2="129.678" y2="89.1023" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint29_linear_36_5540" x1="198.824" y1="92.6263" x2="192.744" y2="91.9157" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint30_linear_36_5540" x1="186.178" y1="105.438" x2="186.264" y2="67.1208" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint31_linear_36_5540" x1="149.138" y1="106.622" x2="149.22" y2="69.8282" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint32_linear_36_5540" x1="156.372" y1="161.916" x2="156.713" y2="6.20866" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint33_linear_36_5540" x1="157.787" y1="161.918" x2="158.128" y2="6.21136" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint34_linear_36_5540" x1="164.861" y1="101.546" x2="164.896" y2="86.1123" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E1F5E"/>
<stop offset="0.18" stop-color="#1F2061" stop-opacity="0.95"/>
<stop offset="0.4" stop-color="#22246D" stop-opacity="0.79"/>
<stop offset="0.64" stop-color="#282A7F" stop-opacity="0.54"/>
<stop offset="0.89" stop-color="#303399" stop-opacity="0.18"/>
<stop offset="1" stop-color="#3538A7" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint35_linear_36_5540" x1="227.357" y1="128.328" x2="166.755" y2="83.8147" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint36_linear_36_5540" x1="146.195" y1="75.5666" x2="161.709" y2="97.2315" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint37_linear_36_5540" x1="188.337" y1="107.323" x2="121.605" y2="90.9351" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint38_linear_36_5540" x1="209.557" y1="98.3846" x2="153.257" y2="60.428" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint39_linear_36_5540" x1="188.771" y1="127.534" x2="146.095" y2="88.3209" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint40_linear_36_5540" x1="190.918" y1="87.4299" x2="174.997" y2="98.3967" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint41_linear_36_5540" x1="179.102" y1="97.8544" x2="156.026" y2="75.4304" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.11" stop-color="#FF9A0D" stop-opacity="0.92"/>
<stop offset="0.34" stop-color="#FFA931" stop-opacity="0.71"/>
<stop offset="0.66" stop-color="#FFC16A" stop-opacity="0.38"/>
<stop offset="1" stop-color="#FFDCAB" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint42_linear_36_5540" x1="181.904" y1="91.3435" x2="179.351" y2="93.9716" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint43_linear_36_5540" x1="164.215" y1="78.3959" x2="160.132" y2="82.6008" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint44_linear_36_5540" x1="163.482" y1="72.5529" x2="164.726" y2="69.721" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint45_linear_36_5540" x1="156.746" y1="65.2952" x2="156.635" y2="64.997" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint46_linear_36_5540" x1="153.988" y1="91.6714" x2="155.066" y2="87.4375" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint47_linear_36_5540" x1="198.71" y1="95.315" x2="198.481" y2="98.0956" gradientUnits="userSpaceOnUse">
<stop stop-color="#FC9D91" stop-opacity="0"/>
<stop offset="0.18" stop-color="#FC9D91" stop-opacity="0.22"/>
<stop offset="0.48" stop-color="#FC9D91" stop-opacity="0.55"/>
<stop offset="0.73" stop-color="#FC9D91" stop-opacity="0.79"/>
<stop offset="0.91" stop-color="#FC9D91" stop-opacity="0.94"/>
<stop offset="1" stop-color="#FC9D91"/>
</linearGradient>
<linearGradient id="paint48_linear_36_5540" x1="118.053" y1="99.242" x2="151.284" y2="79.7127" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEC65"/>
<stop offset="0.34" stop-color="#FFCB3E" stop-opacity="0.62"/>
<stop offset="0.66" stop-color="#FFAE1D" stop-opacity="0.29"/>
<stop offset="0.89" stop-color="#FF9B08" stop-opacity="0.08"/>
<stop offset="1" stop-color="#FF9500" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint49_linear_36_5540" x1="122.321" y1="108.622" x2="141.344" y2="120.158" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEC65"/>
<stop offset="0.34" stop-color="#FFCB3E" stop-opacity="0.62"/>
<stop offset="0.66" stop-color="#FFAE1D" stop-opacity="0.29"/>
<stop offset="0.89" stop-color="#FF9B08" stop-opacity="0.08"/>
<stop offset="1" stop-color="#FF9500" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint50_linear_36_5540" x1="128.678" y1="113.859" x2="146.051" y2="121.815" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEC65"/>
<stop offset="0.34" stop-color="#FFCB3E" stop-opacity="0.62"/>
<stop offset="0.66" stop-color="#FFAE1D" stop-opacity="0.29"/>
<stop offset="0.89" stop-color="#FF9B08" stop-opacity="0.08"/>
<stop offset="1" stop-color="#FF9500" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint51_linear_36_5540" x1="124.838" y1="97.2137" x2="140.504" y2="87.3908" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEC65"/>
<stop offset="0.34" stop-color="#FFCB3E" stop-opacity="0.62"/>
<stop offset="0.66" stop-color="#FFAE1D" stop-opacity="0.29"/>
<stop offset="0.89" stop-color="#FF9B08" stop-opacity="0.08"/>
<stop offset="1" stop-color="#FF9500" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint52_linear_36_5540" x1="47.9586" y1="72.5189" x2="97.3659" y2="121.46" gradientUnits="userSpaceOnUse">
<stop stop-color="#EF4B9F"/>
<stop offset="1" stop-color="#E6332A"/>
</linearGradient>
<linearGradient id="paint53_linear_36_5540" x1="50.2382" y1="77.0799" x2="170.528" y2="183.989" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint54_linear_36_5540" x1="26.8848" y1="60.0881" x2="75.5827" y2="154.993" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCEA10"/>
<stop offset="0.33" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="0.66" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.89" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="1" stop-color="#FF9500" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint55_linear_36_5540" x1="75.0829" y1="160.038" x2="77.8898" y2="125.645" gradientUnits="userSpaceOnUse">
<stop stop-color="#36A9E1" stop-opacity="0"/>
<stop offset="0.09" stop-color="#36AAE1" stop-opacity="0.03"/>
<stop offset="0.22" stop-color="#39B0E2" stop-opacity="0.11"/>
<stop offset="0.38" stop-color="#3DB9E5" stop-opacity="0.24"/>
<stop offset="0.56" stop-color="#43C5E8" stop-opacity="0.42"/>
<stop offset="0.75" stop-color="#4AD5EC" stop-opacity="0.65"/>
<stop offset="0.95" stop-color="#52E8F0" stop-opacity="0.93"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint56_linear_36_5540" x1="88.5068" y1="79.1718" x2="-13.6965" y2="39.0194" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint57_linear_36_5540" x1="12.1875" y1="31.5505" x2="172.305" y2="172.209" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint58_linear_36_5540" x1="94.6078" y1="52.1805" x2="120.977" y2="141.431" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint59_linear_36_5540" x1="68.0695" y1="3.27963" x2="118.135" y2="67.7293" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint60_linear_36_5540" x1="56.2379" y1="16.1767" x2="107.66" y2="85.6465" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint61_linear_36_5540" x1="76.2477" y1="8.40937" x2="94.5263" y2="67.1234" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint62_linear_36_5540" x1="100.779" y1="22.4176" x2="100.54" y2="44.8505" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint63_linear_36_5540" x1="24.7084" y1="51.1457" x2="43.6475" y2="59.4975" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint64_linear_36_5540" x1="16.3829" y1="73.781" x2="26.3461" y2="97.5478" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint65_linear_36_5540" x1="38.5773" y1="11.9105" x2="44.5362" y2="25.0787" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500"/>
<stop offset="0.36" stop-color="#FF8004"/>
<stop offset="1" stop-color="#FF540D"/>
</linearGradient>
<linearGradient id="paint66_linear_36_5540" x1="118.37" y1="109.477" x2="127.332" y2="143.945" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint67_linear_36_5540" x1="235.215" y1="104.681" x2="158.938" y2="160.576" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9500" stop-opacity="0"/>
<stop offset="0.11" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="0.34" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.67" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="1" stop-color="#FCEA10"/>
</linearGradient>
<linearGradient id="paint68_linear_36_5540" x1="199.777" y1="12.9452" x2="180.502" y2="46.9185" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint69_linear_36_5540" x1="150.636" y1="33.5786" x2="163.169" y2="47.9451" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint70_linear_36_5540" x1="205.742" y1="39.2603" x2="189.204" y2="48.4643" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint71_linear_36_5540" x1="232.185" y1="76.5086" x2="223.373" y2="106.481" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint72_linear_36_5540" x1="115.055" y1="15.1576" x2="111.066" y2="28.7254" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
<linearGradient id="paint73_linear_36_5540" x1="233.933" y1="119.418" x2="231.362" y2="128.163" gradientUnits="userSpaceOnUse">
<stop stop-color="#95EBF5"/>
<stop offset="0.63" stop-color="#69ECF2"/>
<stop offset="1" stop-color="#55EDF2"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -0,0 +1,58 @@
<svg width="188" height="185" viewBox="0 0 188 185" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_190_2366)">
<path d="M142.901 37.6245C116.415 18.2386 93.9982 41.946 93.9982 41.946C93.9982 41.946 71.586 18.2341 45.0951 37.6245C19.7688 56.1605 22.1252 125.3 93.9982 154.196C165.867 125.3 168.228 56.1605 142.901 37.6245Z" fill="url(#paint0_linear_190_2366)"/>
<path style="mix-blend-mode:multiply" d="M94.0026 154.197C165.871 125.3 168.232 56.161 142.906 37.6249C134.186 31.2439 125.912 29.5306 118.731 30.1062C122.049 29.8948 135.945 30.1242 138.553 48.5838C141.516 69.5751 119.536 116.747 82.6121 121.316C50.7924 125.255 37.3019 103.297 36.8252 102.501C45.4681 122.35 63.451 141.911 94.0071 154.197H94.0026Z" fill="url(#paint1_linear_190_2366)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M56.3055 35.3131C56.3055 35.3131 39.5727 39.9943 34.6352 61.6736C29.6977 83.353 41.7357 105.1 41.7357 105.1C41.7357 105.1 39.2669 79.1529 56.3055 35.3086V35.3131Z" fill="url(#paint2_linear_190_2366)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M82.981 146.417C82.981 146.417 113.312 145.036 139.268 112.798C139.268 112.798 125.215 134.896 95.5766 151.975C95.5766 151.975 87.8915 150.401 82.981 146.417Z" fill="url(#paint3_linear_190_2366)"/>
<path style="mix-blend-mode:multiply" d="M85.9442 42.782C94.3398 55.1304 106.072 59.2045 106.072 59.2045C106.072 59.2045 113.186 29.4039 101.337 36.1716C96.7052 39.0901 94.0026 41.9456 94.0026 41.9456C94.0026 41.9456 81.4744 28.6934 64.0806 30.1099C64.2874 30.1144 77.6116 30.5281 85.9442 42.782Z" fill="url(#paint4_linear_190_2366)"/>
<path style="mix-blend-mode:color-dodge" opacity="0.7" d="M62.8798 35.3131C50.8418 35.3131 36.8296 43.5198 33.4435 67.2407C30.4621 88.1286 41.7177 125.372 79.8059 116.301C117.899 107.231 139.056 48.7722 125.566 40.8352C112.075 32.8983 105.249 54.5641 106.202 69.0485C106.202 69.0485 84.1409 35.3086 62.8753 35.3086L62.8798 35.3131Z" fill="url(#paint5_linear_190_2366)"/>
</g>
<defs>
<filter id="filter0_d_190_2366" x="0.45752" y="0" width="187.084" height="184.197" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="15"/>
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_190_2366"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_190_2366" result="shape"/>
</filter>
<linearGradient id="paint0_linear_190_2366" x1="147.2" y1="123.856" x2="76.6268" y2="64.952" gradientUnits="userSpaceOnUse">
<stop stop-color="#EF4B9F"/>
<stop offset="1" stop-color="#E6332A"/>
</linearGradient>
<linearGradient id="paint1_linear_190_2366" x1="64.2739" y1="52.478" x2="263.21" y2="229.744" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint2_linear_190_2366" x1="25.5651" y1="24.3812" x2="105.955" y2="181.461" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCEA10"/>
<stop offset="0.33" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="0.66" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.89" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="1" stop-color="#FF9500" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint3_linear_190_2366" x1="48.045" y1="193.687" x2="147.56" y2="94.649" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCEA10"/>
<stop offset="0.33" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="0.66" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.89" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="1" stop-color="#FF9500" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint4_linear_190_2366" x1="127.711" y1="55.9308" x2="-41.5908" y2="-10.7528" gradientUnits="userSpaceOnUse">
<stop stop-color="#0C5DA5" stop-opacity="0"/>
<stop offset="0.27" stop-color="#14529D" stop-opacity="0.24"/>
<stop offset="0.87" stop-color="#2A3989" stop-opacity="0.86"/>
<stop offset="1" stop-color="#2F3485"/>
</linearGradient>
<linearGradient id="paint5_linear_190_2366" x1="21.311" y1="3.40793" x2="127.117" y2="120.33" gradientUnits="userSpaceOnUse">
<stop stop-color="#FCEA10"/>
<stop offset="0.33" stop-color="#FDCA0A" stop-opacity="0.63"/>
<stop offset="0.66" stop-color="#FEAD04" stop-opacity="0.29"/>
<stop offset="0.89" stop-color="#FE9B01" stop-opacity="0.08"/>
<stop offset="1" stop-color="#FF9500" stop-opacity="0"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,11 @@
export default function PaymentLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<main className="p-4 pb-220 max-w-[560px] mx-auto relative min-h-dvh">
{children}
</main>
);
}

View File

@ -0,0 +1,24 @@
// import { getTranslations } from "next-intl/server";
import AnimatedInfoScreen from "@/components/widgets/AnimatedInfoScreen/AnimatedInfoScreen";
import LottieAnimation from "@/components/widgets/LottieAnimation/LottieAnimation";
import { ROUTES } from "@/shared/constants/client-routes";
import { ELottieKeys } from "@/shared/constants/lottie";
export default async function PaymentFailed() {
// const t = await getTranslations("Payment.Error");
return (
<AnimatedInfoScreen
lottieAnimation={
<LottieAnimation loadKey={ELottieKeys.loaderCheckMark} />
}
// title={t("title")}
title="Payment failed"
animationTime={0}
animationTexts={[]}
buttonText="Try again"
nextRoute={ROUTES.home()}
/>
);
}

View File

@ -0,0 +1,31 @@
import { NextRequest, NextResponse } from "next/server";
import { createPaymentCheckout } from "@/entities/payment/api";
import { ROUTES } from "@/shared/constants/client-routes";
export async function GET(req: NextRequest) {
const productId = req.nextUrl.searchParams.get("productId");
const placementId = req.nextUrl.searchParams.get("placementId");
const paywallId = req.nextUrl.searchParams.get("paywallId");
const fbPixels = req.nextUrl.searchParams.get("fb_pixels");
const productPrice = req.nextUrl.searchParams.get("price");
const currency = req.nextUrl.searchParams.get("currency");
console.log("PRODUCT ID:", productId);
const data = await createPaymentCheckout({
productId: productId || "",
placementId: placementId || "",
paywallId: paywallId || "",
});
console.log("DATA:", data);
let redirectUrl: URL = new URL(data?.paymentUrl || "", req.nextUrl.origin);
if (!redirectUrl) {
redirectUrl = new URL(`${ROUTES.paymentFailed()}`, origin);
}
if (fbPixels) redirectUrl.searchParams.set("fb_pixels", fbPixels);
if (productPrice) redirectUrl.searchParams.set("price", productPrice);
if (currency) redirectUrl.searchParams.set("currency", currency);
return NextResponse.redirect(redirectUrl, { status: 307 });
}

View File

@ -0,0 +1,31 @@
"use client";
import { ActionButton } from "@/components/ui/ActionButton/ActionButton";
// import { useRouter } from "next/navigation";
// import { useTranslations } from "next-intl";
import Typography from "@/components/ui/Typography/Typography";
// import { ROUTES } from "@/shared/constants/client-routes";
// import styles from "./Button.module.scss";
export default function PaymentSuccessButton() {
// const t = useTranslations("Payment.Success");
// const router = useRouter();
const handleNext = () => {
// router.push(ROUTES.additionalPurchases());
};
return (
<ActionButton
onClick={handleNext}
className="fixed bottom-[calc(0dvh+64px)] left-1/2 -translate-x-1/2 opacity-0 pointer-events-none max-w-[400px] w-[calc(100dvw-32px)] [animation:fadeIn_0.5s_ease-in-out_forwards] [animation-delay:2s]"
>
<Typography color="primary" size="xl" weight="bold">
{/* {t("button")} */}
Done
</Typography>
</ActionButton>
);
}

View File

@ -0,0 +1,24 @@
// import { getTranslations } from "next-intl/server";
import AnimatedInfoScreen from "@/components/widgets/AnimatedInfoScreen/AnimatedInfoScreen";
import LottieAnimation from "@/components/widgets/LottieAnimation/LottieAnimation";
import PaymentSuccessButton from "./Button";
import { ELottieKeys } from "@/shared/constants/lottie";
export default async function PaymentSuccess() {
// const t = await getTranslations("Payment.Success");
return (
<>
<AnimatedInfoScreen
lottieAnimation={
<LottieAnimation loadKey={ELottieKeys.loaderCheckMark} />
}
// title={t("title")}
title="Payment successful"
/>
<PaymentSuccessButton />
</>
);
}

View File

@ -230,3 +230,13 @@
transform: scale(1.05);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}

View File

@ -117,7 +117,7 @@ export function BuilderCanvas() {
const screenTitleMap = useMemo(() => {
return screens.reduce<Record<string, string>>((accumulator, screen) => {
accumulator[screen.id] = screen.title.text || screen.id;
accumulator[screen.id] = screen.title?.text || screen.id;
return accumulator;
}, {});
}, [screens]);
@ -189,7 +189,7 @@ export function BuilderCanvas() {
#{screen.id}
</span>
<span className="max-h-14 overflow-hidden break-words text-lg font-semibold leading-tight text-foreground">
{screen.title.text || "Без названия"}
{screen.title?.text || "Без названия"}
</span>
</div>
</div>

View File

@ -1,17 +1,24 @@
import type { ScreenDefinition, NavigationConditionDefinition } from "@/lib/funnel/types";
import type {
ScreenDefinition,
NavigationConditionDefinition,
} from "@/lib/funnel/types";
export const TEMPLATE_TITLES: Record<ScreenDefinition["template"], string> = {
list: "Список",
form: "Форма",
form: "Форма",
info: "Инфо",
date: "Дата",
coupon: "Купон",
email: "Email",
loaders: "Загрузка",
soulmate: "Портрет партнера",
trialPayment: "Trial Payment",
};
export const OPERATOR_LABELS: Record<Exclude<NavigationConditionDefinition["operator"], undefined>, string> = {
export const OPERATOR_LABELS: Record<
Exclude<NavigationConditionDefinition["operator"], undefined>,
string
> = {
includesAny: "любой из",
includesAll: "все из",
includesExactly: "точное совпадение",

View File

@ -6,7 +6,11 @@ import { Button } from "@/components/ui/button";
import { Trash2 } from "lucide-react";
import { TemplateConfig } from "@/components/admin/builder/templates";
import { ScreenVariantsConfig } from "../forms/ScreenVariantsConfig";
import { useBuilderDispatch, useBuilderSelectedScreen, useBuilderState } from "@/lib/admin/builder/context";
import {
useBuilderDispatch,
useBuilderSelectedScreen,
useBuilderState,
} from "@/lib/admin/builder/context";
import type { BuilderScreen } from "@/lib/admin/builder/types";
import type {
NavigationRuleDefinition,
@ -24,7 +28,9 @@ export function BuilderSidebar() {
const dispatch = useBuilderDispatch();
const selectedScreen = useBuilderSelectedScreen();
const [activeTab, setActiveTab] = useState<"funnel" | "screen">(selectedScreen ? "screen" : "funnel");
const [activeTab, setActiveTab] = useState<"funnel" | "screen">(
selectedScreen ? "screen" : "funnel"
);
const selectedScreenId = selectedScreen?.id ?? null;
useEffect(() => {
@ -37,27 +43,31 @@ export function BuilderSidebar() {
}, [selectedScreenId]);
// ✅ Оптимизированная validation - только критичные поля
const screenIds = useMemo(() => state.screens.map(s => s.id).join(','), [state.screens]);
const screenIds = useMemo(
() => state.screens.map((s) => s.id).join(","),
[state.screens]
);
const validation = useMemo(
() => validateBuilderState(state),
// eslint-disable-next-line react-hooks/exhaustive-deps -- Оптимизация: пересчитываем только при изменении критичных полей
[
state.meta.id,
state.meta.firstScreenId,
screenIds,
state.screens.length,
]
[state.meta.id, state.meta.firstScreenId, screenIds, state.screens.length]
);
const screenValidationIssues = useMemo(() => {
if (!selectedScreenId) {
return [] as ValidationIssues;
}
return validation.issues.filter((issue) => issue.screenId === selectedScreenId);
return validation.issues.filter(
(issue) => issue.screenId === selectedScreenId
);
}, [selectedScreenId, validation]);
const screenOptions = useMemo(
() => state.screens.map((screen) => ({ id: screen.id, title: screen.title.text })),
() =>
state.screens.map((screen) => ({
id: screen.id,
title: screen.title?.text,
})),
[state.screens]
);
@ -86,29 +96,29 @@ export function BuilderSidebar() {
if (newId === currentId) {
return;
}
// Разрешаем пустые ID для полного переименования
if (newId.trim() === "") {
// Просто обновляем на пустое значение, пользователь сможет ввести новое
dispatch({
type: "update-screen",
payload: {
screenId: currentId,
screen: { id: newId }
}
dispatch({
type: "update-screen",
payload: {
screenId: currentId,
screen: { id: newId },
},
});
return;
}
// Обновляем ID экрана
dispatch({
type: "update-screen",
payload: {
screenId: currentId,
screen: { id: newId }
}
dispatch({
type: "update-screen",
payload: {
screenId: currentId,
screen: { id: newId },
},
});
// Если это был первый экран в мета данных, обновляем и там
if (state.meta.firstScreenId === currentId) {
dispatch({ type: "set-meta", payload: { firstScreenId: newId } });
@ -128,15 +138,20 @@ export function BuilderSidebar() {
screenId: screen.id,
navigation: {
defaultNextScreenId:
navigationUpdates.defaultNextScreenId ?? screen.navigation?.defaultNextScreenId,
navigationUpdates.defaultNextScreenId ??
screen.navigation?.defaultNextScreenId,
rules: navigationUpdates.rules ?? screen.navigation?.rules ?? [],
isEndScreen: navigationUpdates.isEndScreen ?? screen.navigation?.isEndScreen,
isEndScreen:
navigationUpdates.isEndScreen ?? screen.navigation?.isEndScreen,
},
},
});
};
const handleDefaultNextChange = (screenId: string, nextScreenId: string | "") => {
const handleDefaultNextChange = (
screenId: string,
nextScreenId: string | ""
) => {
const screen = getScreenById(screenId);
if (!screen) {
return;
@ -186,7 +201,11 @@ export function BuilderSidebar() {
updateRules(screenId, nextRules);
};
const handleRuleOptionToggle = (screenId: string, ruleIndex: number, optionId: string) => {
const handleRuleOptionToggle = (
screenId: string,
ruleIndex: number,
optionId: string
) => {
const screen = getScreenById(screenId);
if (!screen) {
return;
@ -220,7 +239,11 @@ export function BuilderSidebar() {
updateRules(screenId, nextRules);
};
const handleRuleNextScreenChange = (screenId: string, ruleIndex: number, nextScreenId: string) => {
const handleRuleNextScreenChange = (
screenId: string,
ruleIndex: number,
nextScreenId: string
) => {
const screen = getScreenById(screenId);
if (!screen) {
return;
@ -247,7 +270,10 @@ export function BuilderSidebar() {
const nextRules = [
...(screen.navigation?.rules ?? []),
{ nextScreenId: state.screens[0]?.id ?? screen.id, conditions: [defaultCondition] },
{
nextScreenId: state.screens[0]?.id ?? screen.id,
conditions: [defaultCondition],
},
];
updateNavigation(screen, { rules: nextRules });
};
@ -270,7 +296,10 @@ export function BuilderSidebar() {
dispatch({ type: "remove-screen", payload: { screenId } });
};
const handleTemplateUpdate = (screenId: string, updates: Partial<ScreenDefinition>) => {
const handleTemplateUpdate = (
screenId: string,
updates: Partial<ScreenDefinition>
) => {
dispatch({
type: "update-screen",
payload: {
@ -295,7 +324,9 @@ export function BuilderSidebar() {
});
};
const selectedScreenIsListType = selectedScreen ? isListScreen(selectedScreen) : false;
const selectedScreenIsListType = selectedScreen
? isListScreen(selectedScreen)
: false;
return (
<div className="flex h-full flex-col">
@ -347,19 +378,27 @@ export function BuilderSidebar() {
<TextInput
label="Название"
value={state.meta.title ?? ""}
onChange={(event) => handleMetaChange("title", event.target.value)}
onChange={(event) =>
handleMetaChange("title", event.target.value)
}
/>
<TextInput
label="Описание"
value={state.meta.description ?? ""}
onChange={(event) => handleMetaChange("description", event.target.value)}
onChange={(event) =>
handleMetaChange("description", event.target.value)
}
/>
<label className="flex flex-col gap-2">
<span className="text-sm font-medium text-muted-foreground">Первый экран</span>
<span className="text-sm font-medium text-muted-foreground">
Первый экран
</span>
<select
className="rounded-lg border border-border bg-background px-3 py-2 text-sm"
value={state.meta.firstScreenId ?? state.screens[0]?.id ?? ""}
onChange={(event) => handleFirstScreenChange(event.target.value)}
onChange={(event) =>
handleFirstScreenChange(event.target.value)
}
>
{screenOptions.map((screen) => (
<option key={screen.id} value={screen.id}>
@ -370,18 +409,25 @@ export function BuilderSidebar() {
</label>
</Section>
<Section title="Дефолтные тексты" description="Текст кнопок и баннеров">
<Section
title="Дефолтные тексты"
description="Текст кнопок и баннеров"
>
<TextInput
label="Текст кнопки Next/Continue"
placeholder="Next"
value={state.defaultTexts?.nextButton ?? ""}
onChange={(event) => handleDefaultTextsChange("nextButton", event.target.value)}
onChange={(event) =>
handleDefaultTextsChange("nextButton", event.target.value)
}
/>
<TextInput
label="Баннер приватности"
placeholder="Мы не передаем личную информацию..."
value={state.defaultTexts?.privacyBanner ?? ""}
onChange={(event) => handleDefaultTextsChange("privacyBanner", event.target.value)}
onChange={(event) =>
handleDefaultTextsChange("privacyBanner", event.target.value)
}
/>
</Section>
@ -389,13 +435,22 @@ export function BuilderSidebar() {
<div className="flex flex-col gap-3 rounded-lg border border-border/60 bg-muted/20 p-3 text-sm text-muted-foreground">
<div className="flex items-center justify-between">
<span>Всего экранов</span>
<span className="font-semibold text-foreground">{state.screens.length}</span>
<span className="font-semibold text-foreground">
{state.screens.length}
</span>
</div>
<div className="flex flex-col gap-1 text-xs">
{state.screens.map((screen, index) => (
<span key={screen.id} className="flex items-center justify-between">
<span className="truncate">{index + 1}. {screen.title.text}</span>
<span className="uppercase text-muted-foreground/80">{screen.template}</span>
<span
key={screen.id}
className="flex items-center justify-between"
>
<span className="truncate">
{index + 1}. {screen.title?.text}
</span>
<span className="uppercase text-muted-foreground/80">
{screen.template}
</span>
</span>
))}
</div>
@ -406,33 +461,42 @@ export function BuilderSidebar() {
<div className="flex flex-col gap-4">
{/* Валидация всегда вверху, без заголовка */}
<ValidationSummary issues={screenValidationIssues} />
<div className="rounded-lg border border-primary/20 bg-primary/5 p-3">
<div className="flex items-center justify-between text-sm">
<div className="flex items-center gap-2">
<span className="text-muted-foreground">#{selectedScreen.id}</span>
<span className="text-muted-foreground">
#{selectedScreen.id}
</span>
<span className="inline-flex items-center rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary uppercase">
{selectedScreen.template}
</span>
</div>
<span className="text-xs text-muted-foreground">
{state.screens.findIndex((screen) => screen.id === selectedScreen.id) + 1}/{state.screens.length}
{state.screens.findIndex(
(screen) => screen.id === selectedScreen.id
) + 1}
/{state.screens.length}
</span>
</div>
</div>
<Section title="Общие данные">
<TextInput
label="ID экрана"
value={selectedScreen.id}
onChange={(event) => handleScreenIdChange(selectedScreen.id, event.target.value)}
<TextInput
label="ID экрана"
value={selectedScreen.id}
onChange={(event) =>
handleScreenIdChange(selectedScreen.id, event.target.value)
}
/>
</Section>
<Section title="Контент и оформление">
<TemplateConfig
screen={selectedScreen}
onUpdate={(updates) => handleTemplateUpdate(selectedScreen.id, updates)}
onUpdate={(updates) =>
handleTemplateUpdate(selectedScreen.id, updates)
}
/>
</Section>
@ -440,7 +504,9 @@ export function BuilderSidebar() {
<ScreenVariantsConfig
screen={selectedScreen}
allScreens={state.screens}
onChange={(variants) => handleVariantsChange(selectedScreen.id, variants)}
onChange={(variants) =>
handleVariantsChange(selectedScreen.id, variants)
}
/>
</Section>
@ -451,12 +517,16 @@ export function BuilderSidebar() {
type="checkbox"
checked={selectedScreen.navigation?.isEndScreen ?? false}
onChange={(e) => {
updateNavigation(selectedScreen, { isEndScreen: e.target.checked });
updateNavigation(selectedScreen, {
isEndScreen: e.target.checked,
});
}}
className="rounded border-border"
/>
<div className="flex flex-col">
<span className="text-sm font-medium text-foreground">Финальный экран</span>
<span className="text-sm font-medium text-foreground">
Финальный экран
</span>
<span className="text-xs text-muted-foreground">
Этот экран завершает воронку (переход не требуется)
</span>
@ -466,11 +536,18 @@ export function BuilderSidebar() {
{/* ОБЫЧНАЯ НАВИГАЦИЯ - показываем только если НЕ финальный экран */}
{!selectedScreen.navigation?.isEndScreen && (
<label className="flex flex-col gap-2">
<span className="text-sm font-medium text-muted-foreground">Экран по умолчанию</span>
<span className="text-sm font-medium text-muted-foreground">
Экран по умолчанию
</span>
<select
className="rounded-lg border border-border bg-background px-3 py-2 text-sm"
value={selectedScreen.navigation?.defaultNextScreenId ?? ""}
onChange={(event) => handleDefaultNextChange(selectedScreen.id, event.target.value)}
onChange={(event) =>
handleDefaultNextChange(
selectedScreen.id,
event.target.value
)
}
>
<option value=""></option>
{screenOptions
@ -485,114 +562,163 @@ export function BuilderSidebar() {
)}
</Section>
{selectedScreenIsListType && !selectedScreen.navigation?.isEndScreen && (
<Section title="Правила переходов" description="Условная навигация">
<div className="flex flex-col gap-3">
<div className="flex items-center justify-between">
<p className="text-xs text-muted-foreground">
Направляйте пользователей на разные экраны в зависимости от выбора.
</p>
<Button className="h-8 w-8 p-0 flex items-center justify-center" onClick={() => handleAddRule(selectedScreen)}>
<span className="text-lg leading-none">+</span>
</Button>
</div>
{(selectedScreen.navigation?.rules ?? []).length === 0 && (
<div className="rounded-lg border border-border/60 bg-muted/30 p-3 text-center text-xs text-muted-foreground">
Правил пока нет
{selectedScreenIsListType &&
!selectedScreen.navigation?.isEndScreen && (
<Section
title="Правила переходов"
description="Условная навигация"
>
<div className="flex flex-col gap-3">
<div className="flex items-center justify-between">
<p className="text-xs text-muted-foreground">
Направляйте пользователей на разные экраны в зависимости
от выбора.
</p>
<Button
className="h-8 w-8 p-0 flex items-center justify-center"
onClick={() => handleAddRule(selectedScreen)}
>
<span className="text-lg leading-none">+</span>
</Button>
</div>
)}
{(selectedScreen.navigation?.rules ?? []).map((rule, ruleIndex) => (
<div
key={ruleIndex}
className="flex flex-col gap-3 rounded-xl border border-border/80 bg-background/60 p-3"
>
<div className="flex items-center justify-between">
<span className="text-xs font-semibold uppercase text-muted-foreground">Правило {ruleIndex + 1}</span>
<Button
variant="ghost"
className="h-8 px-2 text-destructive hover:bg-destructive/10"
onClick={() => handleRemoveRule(selectedScreen.id, ruleIndex)}
>
<Trash2 className="h-3 w-3 mr-1" />
<span className="text-xs">Удалить</span>
</Button>
{(selectedScreen.navigation?.rules ?? []).length === 0 && (
<div className="rounded-lg border border-border/60 bg-muted/30 p-3 text-center text-xs text-muted-foreground">
Правил пока нет
</div>
<label className="flex flex-col gap-2">
<span className="text-sm font-medium text-muted-foreground">Оператор</span>
<select
className="rounded-lg border border-border bg-background px-3 py-2 text-sm"
value={rule.conditions[0]?.operator ?? "includesAny"}
onChange={(event) =>
handleRuleOperatorChange(
selectedScreen.id,
ruleIndex,
event.target.value as NavigationRuleDefinition["conditions"][0]["operator"]
)
}
>
<option value="includesAny">contains any</option>
<option value="includesAll">contains all</option>
<option value="includesExactly">exact match</option>
</select>
</label>
)}
{selectedScreen.template === "list" ? (
<div className="flex flex-col gap-2">
<span className="text-sm font-medium text-muted-foreground">Варианты ответа</span>
<div className="flex flex-col gap-2 rounded-lg border border-border/60 p-3">
{selectedScreen.list.options.map((option) => {
const condition = rule.conditions[0];
const isChecked = condition.optionIds?.includes(option.id) ?? false;
return (
<label key={option.id} className="flex items-center gap-2 text-sm">
<input
type="checkbox"
checked={isChecked}
onChange={() => handleRuleOptionToggle(selectedScreen.id, ruleIndex, option.id)}
/>
<span>
{option.label}
<span className="text-muted-foreground"> ({option.id})</span>
</span>
</label>
);
})}
{(selectedScreen.navigation?.rules ?? []).map(
(rule, ruleIndex) => (
<div
key={ruleIndex}
className="flex flex-col gap-3 rounded-xl border border-border/80 bg-background/60 p-3"
>
<div className="flex items-center justify-between">
<span className="text-xs font-semibold uppercase text-muted-foreground">
Правило {ruleIndex + 1}
</span>
<Button
variant="ghost"
className="h-8 px-2 text-destructive hover:bg-destructive/10"
onClick={() =>
handleRemoveRule(selectedScreen.id, ruleIndex)
}
>
<Trash2 className="h-3 w-3 mr-1" />
<span className="text-xs">Удалить</span>
</Button>
</div>
</div>
) : (
<div className="flex flex-col gap-2 rounded-lg border border-border/60 bg-muted/20 p-3 text-xs text-muted-foreground">
Навигационные правила с вариантами ответа доступны только для экранов со списком.
</div>
)}
<label className="flex flex-col gap-2">
<span className="text-sm font-medium text-muted-foreground">Следующий экран</span>
<select
className="rounded-lg border border-border bg-background px-3 py-2 text-sm"
value={rule.nextScreenId}
onChange={(event) => handleRuleNextScreenChange(selectedScreen.id, ruleIndex, event.target.value)}
>
{screenOptions
.filter((screen) => screen.id !== selectedScreen.id)
.map((screen) => (
<option key={screen.id} value={screen.id}>
{screen.title}
<label className="flex flex-col gap-2">
<span className="text-sm font-medium text-muted-foreground">
Оператор
</span>
<select
className="rounded-lg border border-border bg-background px-3 py-2 text-sm"
value={
rule.conditions[0]?.operator ?? "includesAny"
}
onChange={(event) =>
handleRuleOperatorChange(
selectedScreen.id,
ruleIndex,
event.target
.value as NavigationRuleDefinition["conditions"][0]["operator"]
)
}
>
<option value="includesAny">contains any</option>
<option value="includesAll">contains all</option>
<option value="includesExactly">
exact match
</option>
))}
</select>
</label>
</div>
))}
</div>
</Section>
)}
</select>
</label>
{selectedScreen.template === "list" ? (
<div className="flex flex-col gap-2">
<span className="text-sm font-medium text-muted-foreground">
Варианты ответа
</span>
<div className="flex flex-col gap-2 rounded-lg border border-border/60 p-3">
{selectedScreen.list.options.map((option) => {
const condition = rule.conditions[0];
const isChecked =
condition.optionIds?.includes(option.id) ??
false;
return (
<label
key={option.id}
className="flex items-center gap-2 text-sm"
>
<input
type="checkbox"
checked={isChecked}
onChange={() =>
handleRuleOptionToggle(
selectedScreen.id,
ruleIndex,
option.id
)
}
/>
<span>
{option.label}
<span className="text-muted-foreground">
{" "}
({option.id})
</span>
</span>
</label>
);
})}
</div>
</div>
) : (
<div className="flex flex-col gap-2 rounded-lg border border-border/60 bg-muted/20 p-3 text-xs text-muted-foreground">
Навигационные правила с вариантами ответа доступны
только для экранов со списком.
</div>
)}
<label className="flex flex-col gap-2">
<span className="text-sm font-medium text-muted-foreground">
Следующий экран
</span>
<select
className="rounded-lg border border-border bg-background px-3 py-2 text-sm"
value={rule.nextScreenId}
onChange={(event) =>
handleRuleNextScreenChange(
selectedScreen.id,
ruleIndex,
event.target.value
)
}
>
{screenOptions
.filter(
(screen) => screen.id !== selectedScreen.id
)
.map((screen) => (
<option key={screen.id} value={screen.id}>
{screen.title}
</option>
))}
</select>
</label>
</div>
)
)}
</div>
</Section>
)}
<Section title="Управление">
<div className="rounded-lg border border-destructive/20 bg-destructive/5 p-4">
<p className="mb-3 text-sm text-muted-foreground">
Удаление экрана нельзя отменить. Все связи с этим экраном будут потеряны.
Удаление экрана нельзя отменить. Все связи с этим экраном
будут потеряны.
</p>
<Button
variant="destructive"
@ -601,7 +727,9 @@ export function BuilderSidebar() {
onClick={() => handleDeleteScreen(selectedScreen.id)}
>
<Trash2 className="h-4 w-4 mr-2" />
{state.screens.length <= 1 ? "Нельзя удалить последний экран" : "Удалить экран"}
{state.screens.length <= 1
? "Нельзя удалить последний экран"
: "Удалить экран"}
</Button>
</div>
</Section>

View File

@ -1,6 +1,9 @@
import { useMemo } from "react";
import { TextInput } from "@/components/ui/TextInput/TextInput";
import { useBuilderDispatch, useBuilderState } from "@/lib/admin/builder/context";
import {
useBuilderDispatch,
useBuilderState,
} from "@/lib/admin/builder/context";
import { Section } from "./Section";
import type { BuilderScreen } from "@/lib/admin/builder/types";
@ -9,7 +12,11 @@ export function FunnelSettingsPanel() {
const dispatch = useBuilderDispatch();
const screenOptions = useMemo(
() => state.screens.map((screen: BuilderScreen) => ({ id: screen.id, title: screen.title.text })),
() =>
state.screens.map((screen: BuilderScreen) => ({
id: screen.id,
title: screen.title?.text,
})),
[state.screens]
);
@ -71,13 +78,17 @@ export function FunnelSettingsPanel() {
label='Кнопка "Next"'
placeholder="Next"
value={state.defaultTexts?.nextButton || ""}
onChange={(e) => handleDefaultTextsChange("nextButton", e.target.value)}
onChange={(e) =>
handleDefaultTextsChange("nextButton", e.target.value)
}
/>
<TextInput
label='Кнопка "Continue"'
placeholder="Continue"
value={state.defaultTexts?.continueButton || ""}
onChange={(e) => handleDefaultTextsChange("continueButton", e.target.value)}
onChange={(e) =>
handleDefaultTextsChange("continueButton", e.target.value)
}
/>
</div>
</Section>

View File

@ -1,7 +1,10 @@
import { useMemo } from "react";
import { Button } from "@/components/ui/button";
import { Trash2 } from "lucide-react";
import { useBuilderDispatch, useBuilderState } from "@/lib/admin/builder/context";
import {
useBuilderDispatch,
useBuilderState,
} from "@/lib/admin/builder/context";
import { Section } from "./Section";
import type { BuilderScreen } from "@/lib/admin/builder/types";
import type { NavigationRuleDefinition } from "@/lib/funnel/types";
@ -10,7 +13,9 @@ interface NavigationPanelProps {
screen: BuilderScreen;
}
function isListScreen(screen: BuilderScreen): screen is BuilderScreen & { template: "list" } {
function isListScreen(
screen: BuilderScreen
): screen is BuilderScreen & { template: "list" } {
return screen.template === "list";
}
@ -19,7 +24,7 @@ export function NavigationPanel({ screen }: NavigationPanelProps) {
const dispatch = useBuilderDispatch();
const screenOptions = useMemo(
() => state.screens.map((s) => ({ id: s.id, title: s.title.text })),
() => state.screens.map((s) => ({ id: s.id, title: s.title?.text })),
[state.screens]
);
@ -38,15 +43,22 @@ export function NavigationPanel({ screen }: NavigationPanelProps) {
screenId: targetScreen.id,
navigation: {
defaultNextScreenId:
navigationUpdates.defaultNextScreenId ?? targetScreen.navigation?.defaultNextScreenId,
rules: navigationUpdates.rules ?? targetScreen.navigation?.rules ?? [],
isEndScreen: navigationUpdates.isEndScreen ?? targetScreen.navigation?.isEndScreen,
navigationUpdates.defaultNextScreenId ??
targetScreen.navigation?.defaultNextScreenId,
rules:
navigationUpdates.rules ?? targetScreen.navigation?.rules ?? [],
isEndScreen:
navigationUpdates.isEndScreen ??
targetScreen.navigation?.isEndScreen,
},
},
});
};
const handleDefaultNextChange = (screenId: string, nextScreenId: string | "") => {
const handleDefaultNextChange = (
screenId: string,
nextScreenId: string | ""
) => {
const targetScreen = getScreenById(screenId);
if (!targetScreen) return;
@ -64,7 +76,9 @@ export function NavigationPanel({ screen }: NavigationPanelProps) {
const handleAddRule = (targetScreen: BuilderScreen) => {
const rules = targetScreen.navigation?.rules ?? [];
const firstScreenOption = screenOptions.find(s => s.id !== targetScreen.id);
const firstScreenOption = screenOptions.find(
(s) => s.id !== targetScreen.id
);
updateRules(targetScreen.id, [
...rules,
{
@ -105,7 +119,9 @@ export function NavigationPanel({ screen }: NavigationPanelProps) {
className="rounded border-border"
/>
<div className="flex flex-col">
<span className="text-sm font-medium text-foreground">Финальный экран</span>
<span className="text-sm font-medium text-foreground">
Финальный экран
</span>
<span className="text-xs text-muted-foreground">
Этот экран завершает воронку (переход не требуется)
</span>
@ -115,11 +131,15 @@ export function NavigationPanel({ screen }: NavigationPanelProps) {
{/* Обычная навигация - показываем только если НЕ финальный экран */}
{!screen.navigation?.isEndScreen && (
<label className="flex flex-col gap-2">
<span className="text-sm font-medium text-muted-foreground">Экран по умолчанию</span>
<span className="text-sm font-medium text-muted-foreground">
Экран по умолчанию
</span>
<select
className="rounded-lg border border-border bg-background px-3 py-2 text-sm"
value={screen.navigation?.defaultNextScreenId ?? ""}
onChange={(e) => handleDefaultNextChange(screen.id, e.target.value)}
onChange={(e) =>
handleDefaultNextChange(screen.id, e.target.value)
}
>
<option value=""></option>
{screenOptions
@ -139,7 +159,8 @@ export function NavigationPanel({ screen }: NavigationPanelProps) {
<div className="flex flex-col gap-3">
<div className="flex items-center justify-between">
<p className="text-xs text-muted-foreground">
Направляйте пользователей на разные экраны в зависимости от выбора.
Направляйте пользователей на разные экраны в зависимости от
выбора.
</p>
<Button
className="h-8 w-8 p-0 flex items-center justify-center"
@ -176,7 +197,10 @@ export function NavigationPanel({ screen }: NavigationPanelProps) {
<div className="text-xs text-muted-foreground">
{/* Здесь должна быть полная логика редактирования правил */}
{/* Для краткости оставляем только структуру */}
<p>Правило {ruleIndex + 1} - редактирование правил сохранено в оригинальном компоненте</p>
<p>
Правило {ruleIndex + 1} - редактирование правил сохранено в
оригинальном компоненте
</p>
</div>
</div>
))}

View File

@ -3,7 +3,10 @@ import { Button } from "@/components/ui/button";
import { Trash2 } from "lucide-react";
import { TemplateConfig } from "@/components/admin/builder/templates";
import { ScreenVariantsConfig } from "../forms/ScreenVariantsConfig";
import { useBuilderDispatch, useBuilderState } from "@/lib/admin/builder/context";
import {
useBuilderDispatch,
useBuilderState,
} from "@/lib/admin/builder/context";
import { Section } from "./Section";
import type { BuilderScreen } from "@/lib/admin/builder/types";
import type { ScreenDefinition } from "@/lib/funnel/types";
@ -46,14 +49,20 @@ export function ScreenSettingsPanel({ screen }: ScreenSettingsPanelProps) {
}
};
const handleTemplateUpdate = (screenId: string, updates: Partial<ScreenDefinition>) => {
const handleTemplateUpdate = (
screenId: string,
updates: Partial<ScreenDefinition>
) => {
dispatch({
type: "update-screen",
payload: { screenId, screen: updates },
});
};
const handleVariantsChange = (screenId: string, variants: BuilderScreen["variants"]) => {
const handleVariantsChange = (
screenId: string,
variants: BuilderScreen["variants"]
) => {
dispatch({
type: "update-screen",
payload: { screenId, screen: { variants } },
@ -70,9 +79,11 @@ export function ScreenSettingsPanel({ screen }: ScreenSettingsPanelProps) {
<div className="flex items-center justify-between px-4 py-3 border-b border-border/60 bg-muted/30">
<div className="flex-1">
<div className="text-sm font-semibold text-foreground truncate">
{screen.title.text || "Без названия"}
{screen.title?.text || "Без названия"}
</div>
<span className="text-xs text-muted-foreground">{screen.template}</span>
<span className="text-xs text-muted-foreground">
{screen.template}
</span>
</div>
<Button
variant="ghost"

View File

@ -9,7 +9,8 @@ import {
Ticket,
Loader,
Heart,
Mail
Mail,
CreditCard
} from "lucide-react";
import { Button } from "@/components/ui/button";
@ -85,6 +86,13 @@ const TEMPLATE_OPTIONS = [
icon: Ticket,
color: "bg-orange-50 text-orange-600 dark:bg-orange-900/20 dark:text-orange-400",
},
{
template: "trialPayment" as const,
title: "Trial Payment",
description: "Страница оплаты с пробным периодом",
icon: CreditCard,
color: "bg-amber-50 text-amber-700 dark:bg-amber-900/20 dark:text-amber-400",
},
] as const;
export function AddScreenDialog({ open, onOpenChange, onAddScreen }: AddScreenDialogProps) {

View File

@ -2,7 +2,10 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useBuilderSelectedScreen, useBuilderState } from "@/lib/admin/builder/context";
import {
useBuilderSelectedScreen,
useBuilderState,
} from "@/lib/admin/builder/context";
import { renderScreen } from "@/lib/funnel/screenRenderer";
import { mergeScreenWithOverrides } from "@/lib/admin/builder/variants";
import { PreviewErrorBoundary } from "@/components/admin/ErrorBoundary";
@ -20,7 +23,9 @@ export function BuilderPreview() {
const selectedScreen = useBuilderSelectedScreen();
const builderState = useBuilderState();
const [selectedIds, setSelectedIds] = useState<string[]>([]);
const [previewVariantIndex, setPreviewVariantIndex] = useState<number | null>(null);
const [previewVariantIndex, setPreviewVariantIndex] = useState<number | null>(
null
);
useEffect(() => {
if (!selectedScreen) {
@ -39,15 +44,20 @@ export function BuilderPreview() {
const handleSelectionChange = useCallback((ids: string[]) => {
setSelectedIds((prev) => {
if (prev.length === ids.length && prev.every((value, index) => value === ids[index])) {
if (
prev.length === ids.length &&
prev.every((value, index) => value === ids[index])
) {
return prev;
}
return ids;
});
}, []);
const variants = useMemo(() => selectedScreen?.variants ?? [], [selectedScreen]);
const variants = useMemo(
() => selectedScreen?.variants ?? [],
[selectedScreen]
);
useEffect(() => {
setPreviewVariantIndex(null);
@ -94,19 +104,33 @@ export function BuilderPreview() {
answers: {}, // Mock empty answers для превью
});
} catch (error) {
console.error('Error rendering preview:', error);
console.error("Error rendering preview:", error);
return (
<div className="flex h-32 items-center justify-center rounded-lg border border-dashed border-border text-sm text-muted-foreground">
Ошибка при отображении превью: {error instanceof Error ? error.message : 'Неизвестная ошибка'}
Ошибка при отображении превью:{" "}
{error instanceof Error ? error.message : "Неизвестная ошибка"}
</div>
);
}
}, [previewScreen, selectedIds, handleSelectionChange, builderState.meta, builderState.defaultTexts, builderState.screens]);
}, [
previewScreen,
selectedIds,
handleSelectionChange,
builderState.meta,
builderState.defaultTexts,
builderState.screens,
]);
const preview = useMemo(() => {
if (!previewScreen) {
return (
<div className="flex items-center justify-center mx-auto" style={{ height: `${PREVIEW_DIMENSIONS.EMPTY_HEIGHT}px`, width: `${PREVIEW_DIMENSIONS.WIDTH}px` }}>
<div
className="flex items-center justify-center mx-auto"
style={{
height: `${PREVIEW_DIMENSIONS.EMPTY_HEIGHT}px`,
width: `${PREVIEW_DIMENSIONS.WIDTH}px`,
}}
>
<div className="flex items-center justify-center rounded-lg border border-dashed border-border bg-muted/30 text-sm text-muted-foreground w-full h-full">
Выберите экран для предпросмотра
</div>
@ -128,9 +152,17 @@ export function BuilderPreview() {
</span>
<select
className="rounded-md border border-border bg-background px-2 py-1 text-xs"
value={previewVariantIndex === null ? "base" : String(previewVariantIndex)}
value={
previewVariantIndex === null
? "base"
: String(previewVariantIndex)
}
onChange={(event) =>
setPreviewVariantIndex(event.target.value === "base" ? null : Number(event.target.value))
setPreviewVariantIndex(
event.target.value === "base"
? null
: Number(event.target.value)
)
}
>
<option value="base">Основной экран</option>
@ -143,7 +175,8 @@ export function BuilderPreview() {
</div>
{previewVariantIndex !== null && (
<div className="mt-2 rounded border border-blue-200 bg-blue-50 px-2 py-1 text-[11px] text-blue-700 dark:border-blue-800 dark:bg-blue-900/20 dark:text-blue-200">
Превью принудительно показывает вариант. В реальной воронке он показывается только при выполнении условий.
Превью принудительно показывает вариант. В реальной воронке
он показывается только при выполнении условий.
</div>
)}
</div>
@ -155,10 +188,10 @@ export function BuilderPreview() {
style={{
height: PREVIEW_HEIGHT,
width: PREVIEW_WIDTH,
overflow: 'hidden', // Hide anything that goes outside
contain: 'layout style paint', // CSS containment
isolation: 'isolate', // Create new stacking context
transform: 'translateZ(0)' // Force new layer
overflow: "hidden", // Hide anything that goes outside
contain: "layout style paint", // CSS containment
isolation: "isolate", // Create new stacking context
transform: "translateZ(0)", // Force new layer
}}
>
{/* Screen Content with scroll - wrapped in Error Boundary */}

View File

@ -98,7 +98,7 @@ export function InfoScreenConfig({ screen, onUpdate }: InfoScreenConfigProps) {
{/* Иконка */}
<CollapsibleSection title="Иконка" defaultExpanded={true}>
<div className="space-y-3">
<div className="grid grid-cols-2 gap-2 text-xs">
<div className="space-y-2 text-xs">
<label className="flex flex-col gap-1 text-muted-foreground">
Тип иконки
<select

View File

@ -62,13 +62,15 @@ export function LoadersScreenConfig({ screen, onUpdate }: LoadersScreenConfigPro
<div className="space-y-4">
<div>
<h4 className="text-sm font-medium text-slate-700 mb-3">Настройки анимации</h4>
<TextInput
label="Длительность анимации (мс)"
type="number"
placeholder="5000"
value={screen.progressbars?.transitionDuration?.toString() || "5000"}
onChange={(e) => updateProgressbars({ transitionDuration: parseInt(e.target.value) || 5000 })}
/>
<div className="space-y-3">
<TextInput
label="Длительность анимации (мс)"
type="number"
placeholder="5000"
value={screen.progressbars?.transitionDuration?.toString() || "5000"}
onChange={(e) => updateProgressbars({ transitionDuration: parseInt(e.target.value) || 5000 })}
/>
</div>
</div>
<div>
@ -100,7 +102,7 @@ export function LoadersScreenConfig({ screen, onUpdate }: LoadersScreenConfigPro
</Button>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-3">
<TextInput
label="Заголовок"
placeholder="Step 1"
@ -115,7 +117,7 @@ export function LoadersScreenConfig({ screen, onUpdate }: LoadersScreenConfigPro
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-3">
<TextInput
label="Текст во время обработки"
placeholder="Processing..."
@ -130,7 +132,7 @@ export function LoadersScreenConfig({ screen, onUpdate }: LoadersScreenConfigPro
/>
</div>
<div className="grid grid-cols-2 gap-3">
<div className="space-y-3">
<TextInput
label="Текст при завершении"
placeholder="Completed!"

View File

@ -2,6 +2,9 @@
import React from "react";
import { TextInput } from "@/components/ui/TextInput/TextInput";
import { Button } from "@/components/ui/button";
import { ImageUpload } from "@/components/admin/builder/forms/ImageUpload";
import { Plus, Trash2 } from "lucide-react";
import type { BuilderScreen } from "@/lib/admin/builder/types";
import type { SoulmatePortraitScreenDefinition } from "@/lib/funnel/types";
@ -11,6 +14,10 @@ interface SoulmatePortraitScreenConfigProps {
}
export function SoulmatePortraitScreenConfig({ screen, onUpdate }: SoulmatePortraitScreenConfigProps) {
type Delivered = NonNullable<SoulmatePortraitScreenDefinition["soulmatePortraitsDelivered"]>;
type Avatar = NonNullable<Delivered["avatars"]>[number];
type DeliveredText = NonNullable<Delivered["text"]>;
const updateDescription = (updates: Partial<SoulmatePortraitScreenDefinition["description"]>) => {
onUpdate({
description: screen.description ? {
@ -20,6 +27,64 @@ export function SoulmatePortraitScreenConfig({ screen, onUpdate }: SoulmatePortr
});
};
const updateDelivered = (
updates: Partial<NonNullable<SoulmatePortraitScreenDefinition["soulmatePortraitsDelivered"]>>
) => {
const base = screen.soulmatePortraitsDelivered ?? {};
onUpdate({
soulmatePortraitsDelivered: {
...base,
...updates,
},
});
};
const updateDeliveredText = (
updates: Partial<DeliveredText>
) => {
const currentText = (screen.soulmatePortraitsDelivered?.text ?? { text: "" }) as DeliveredText;
const nextText = { ...currentText, ...(updates as object) } as DeliveredText;
updateDelivered({ text: nextText });
};
const addAvatar = () => {
const avatars = screen.soulmatePortraitsDelivered?.avatars ?? [];
updateDelivered({ avatars: [...avatars, { src: "", alt: "", fallbackText: "" }] });
};
const removeAvatar = (index: number) => {
const avatars = screen.soulmatePortraitsDelivered?.avatars ?? [];
updateDelivered({ avatars: avatars.filter((_, i) => i !== index) });
};
const updateAvatar = (
index: number,
updates: Partial<Avatar>
) => {
const avatars = screen.soulmatePortraitsDelivered?.avatars ?? [];
const next = avatars.map((a, i) => (i === index ? { ...a, ...updates } : a));
updateDelivered({ avatars: next });
};
const addTextListItem = () => {
const items = screen.textList?.items ?? [];
onUpdate({ textList: { items: [...items, { text: "" }] } });
};
const removeTextListItem = (index: number) => {
const items = screen.textList?.items ?? [];
onUpdate({ textList: { items: items.filter((_, i) => i !== index) } });
};
const updateTextListItem = (
index: number,
updates: Partial<NonNullable<SoulmatePortraitScreenDefinition["textList"]>["items"][number]>
) => {
const items = screen.textList?.items ?? [];
const next = items.map((it, i) => (i === index ? { ...it, ...updates } : it));
onUpdate({ textList: { items: next } });
};
return (
<div className="space-y-4">
<div>
@ -32,6 +97,119 @@ export function SoulmatePortraitScreenConfig({ screen, onUpdate }: SoulmatePortr
/>
</div>
<div>
<h4 className="text-sm font-medium text-slate-700 mb-3">Блок доставленных портретов</h4>
<div className="space-y-3">
<div>
<span className="text-xs font-medium text-muted-foreground mb-2 block">Изображение</span>
<ImageUpload
currentValue={screen.soulmatePortraitsDelivered?.image}
onImageSelect={(url) => updateDelivered({ image: url })}
onImageRemove={() => updateDelivered({ image: undefined })}
funnelId={screen.id}
/>
</div>
<TextInput
label="Текст под изображением"
placeholder="soulmate portraits delivered today"
value={screen.soulmatePortraitsDelivered?.text?.text || ""}
onChange={(e) => updateDeliveredText({ text: e.target.value })}
/>
</div>
</div>
<div>
<div className="flex items-center justify-between mb-3">
<h4 className="text-sm font-medium text-slate-700">Аватары</h4>
<Button type="button" variant="outline" onClick={addAvatar} className="flex items-center gap-2 text-sm px-3 py-1">
<Plus className="w-4 h-4" /> Добавить
</Button>
</div>
<div className="space-y-4">
{(screen.soulmatePortraitsDelivered?.avatars ?? []).map((avatar, index) => (
<div key={index} className="border rounded-lg p-4 space-y-3">
<div className="flex items-center justify-between">
<h5 className="text-sm font-medium text-slate-600">Аватар {index + 1}</h5>
<Button type="button" variant="ghost" onClick={() => removeAvatar(index)} className="text-red-600 hover:text-red-700 text-sm px-2 py-1">
<Trash2 className="w-4 h-4" />
</Button>
</div>
<div className="space-y-3">
<div>
<span className="text-xs font-medium text-muted-foreground mb-2 block">Изображение</span>
<ImageUpload
currentValue={avatar.src}
onImageSelect={(url) => updateAvatar(index, { src: url })}
onImageRemove={() => updateAvatar(index, { src: "" })}
funnelId={screen.id}
/>
</div>
<TextInput
label="Alt"
placeholder="Описание"
value={avatar.alt || ""}
onChange={(e) => updateAvatar(index, { alt: e.target.value })}
/>
<TextInput
label="Fallback текст"
placeholder="Напр. 900+"
value={avatar.fallbackText || ""}
onChange={(e) => updateAvatar(index, { fallbackText: e.target.value })}
/>
</div>
<div className="text-xs text-muted-foreground">Можно указать изображение или fallback текст (или оба).</div>
</div>
))}
</div>
{(screen.soulmatePortraitsDelivered?.avatars ?? []).length === 0 && (
<div className="text-center py-6 text-slate-500">
<p>Нет аватаров</p>
<Button type="button" variant="outline" onClick={addAvatar} className="mt-2 text-sm px-3 py-1">
Добавить первый
</Button>
</div>
)}
</div>
<div>
<div className="flex items-center justify-between mb-3">
<h4 className="text-sm font-medium text-slate-700">Список текстов</h4>
<Button type="button" variant="outline" onClick={addTextListItem} className="flex items-center gap-2 text-sm px-3 py-1">
<Plus className="w-4 h-4" /> Добавить
</Button>
</div>
<div className="space-y-3">
{(screen.textList?.items ?? []).map((item, index) => (
<div key={index} className="space-y-1">
<TextInput
label={`Элемент ${index + 1}`}
placeholder="Текст элемента"
value={item.text || ""}
onChange={(e) => updateTextListItem(index, { text: e.target.value })}
/>
<div className="flex justify-end">
<Button type="button" variant="ghost" onClick={() => removeTextListItem(index)} className="text-red-600 hover:text-red-700 text-sm px-2 py-1">
<Trash2 className="w-4 h-4" />
</Button>
</div>
</div>
))}
</div>
{(screen.textList?.items ?? []).length === 0 && (
<div className="text-center py-6 text-slate-500">
<p>Пока нет элементов</p>
<Button type="button" variant="outline" onClick={addTextListItem} className="mt-2 text-sm px-3 py-1">
Добавить первый
</Button>
</div>
)}
</div>
<div>
<h4 className="text-sm font-medium text-slate-700 mb-3">Информация</h4>
<div className="text-xs text-muted-foreground">

View File

@ -11,6 +11,7 @@ import { ListScreenConfig } from "./ListScreenConfig";
import { EmailScreenConfig } from "./EmailScreenConfig";
import { LoadersScreenConfig } from "./LoadersScreenConfig";
import { SoulmatePortraitScreenConfig } from "./SoulmatePortraitScreenConfig";
import { TrialPaymentScreenConfig } from "./TrialPaymentScreenConfig";
import { TextInput } from "@/components/ui/TextInput/TextInput";
import { TextAreaInput } from "@/components/ui/TextAreaInput/TextAreaInput";
@ -28,6 +29,7 @@ import type {
TypographyVariant,
BottomActionButtonDefinition,
HeaderDefinition,
TrialPaymentScreenDefinition,
} from "@/lib/funnel/types";
const RADIUS_OPTIONS: ("3xl" | "full")[] = ["3xl", "full"];
@ -539,6 +541,12 @@ export function TemplateConfig({ screen, onUpdate }: TemplateConfigProps) {
onUpdate={onUpdate as (updates: Partial<SoulmatePortraitScreenDefinition>) => void}
/>
)}
{template === "trialPayment" && (
<TrialPaymentScreenConfig
screen={screen as BuilderScreen & { template: "trialPayment" }}
onUpdate={onUpdate as (updates: Partial<TrialPaymentScreenDefinition>) => void}
/>
)}
</div>
);
}

View File

@ -0,0 +1,717 @@
"use client";
import React from "react";
import type { BuilderScreen } from "@/lib/admin/builder/types";
import type { TrialPaymentScreenDefinition } from "@/lib/funnel/types";
import { TextInput } from "@/components/ui/TextInput/TextInput";
import { TextAreaInput } from "@/components/ui/TextAreaInput/TextAreaInput";
import { Button } from "@/components/ui/button";
import { Trash } from "lucide-react";
interface TrialPaymentScreenConfigProps {
screen: BuilderScreen & { template: "trialPayment" };
onUpdate: (updates: Partial<TrialPaymentScreenDefinition>) => void;
}
export function TrialPaymentScreenConfig({ screen, onUpdate }: TrialPaymentScreenConfigProps) {
const updateHeaderBlock = (updates: Partial<NonNullable<TrialPaymentScreenDefinition["headerBlock"]>>) => {
onUpdate({ headerBlock: { ...screen.headerBlock, ...updates } });
};
const updateUnlock = (
updates: Partial<NonNullable<TrialPaymentScreenDefinition["unlockYourSketch"]>>
) => {
onUpdate({ unlockYourSketch: { ...screen.unlockYourSketch, ...updates } });
};
const updatePaymentButtons = (
index: number,
field: "text" | "icon" | "primary",
value: string | boolean
) => {
const current = screen.paymentButtons?.buttons ?? [];
const buttons = current.map((b, i) =>
i === index
? {
...b,
...(field === "text" ? { text: String(value) } : {}),
...(field === "icon" ? { icon: String(value) as "pay" | "google" | "card" } : {}),
...(field === "primary" ? { primary: Boolean(value) } : {}),
}
: b
);
onUpdate({ paymentButtons: { buttons } });
};
const updateFooterContacts = (
field: "email" | "address" | "title",
value: { href: string; text: string } | { text: string }
) => {
const next = { ...(screen.footer?.contacts ?? {}) } as NonNullable<TrialPaymentScreenDefinition["footer"]>["contacts"];
if (field === "email") next!.email = value as { href: string; text: string };
if (field === "address") next!.address = value as { text: string };
if (field === "title") next!.title = { text: (value as { text: string }).text };
onUpdate({ footer: { ...screen.footer, contacts: next } });
};
return (
<div className="space-y-6">
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Header Block</h4>
<div className="space-y-3">
<TextInput
label="Текст"
value={screen.headerBlock?.text?.text ?? ""}
onChange={(e) => updateHeaderBlock({ text: { ...(screen.headerBlock?.text ?? {}), text: e.target.value } })}
/>
<TextInput
label="Таймер (сек)"
type="number"
value={String(screen.headerBlock?.timerSeconds ?? 600)}
onChange={(e) => updateHeaderBlock({ timerSeconds: Number(e.target.value) })}
/>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Reviews</h4>
<div className="space-y-3">
<TextInput
label="Title"
value={screen.reviews?.title?.text ?? ""}
onChange={(e) => onUpdate({ reviews: { title: { text: e.target.value }, items: screen.reviews?.items ?? [] } })}
/>
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">Items</span>
<Button
type="button"
className="h-7 px-2 text-xs"
onClick={() => {
const current = screen.reviews?.items ?? [];
const items = [
...current,
{ name: { text: "" }, text: { text: "" }, rating: 5 },
];
onUpdate({ reviews: { ...screen.reviews, items } });
}}
>
Добавить
</Button>
</div>
{(screen.reviews?.items ?? []).map((r, idx) => (
<div key={idx} className="space-y-2 border border-border/60 rounded-md p-3">
<TextInput
label={`Review #${idx + 1} name`}
value={r.name.text}
onChange={(e) => {
const current = screen.reviews?.items ?? [];
const items = current.map((v, i) => (i === idx ? { ...v, name: { text: e.target.value } } : v));
onUpdate({ reviews: { ...screen.reviews, items } });
}}
/>
<TextInput
label="Date"
value={r.date?.text ?? ""}
onChange={(e) => {
const current = screen.reviews?.items ?? [];
const items = current.map((v, i) => (i === idx ? { ...v, date: { text: e.target.value } } : v));
onUpdate({ reviews: { ...screen.reviews, items } });
}}
/>
<TextAreaInput
label="Text (supports **bold**)"
rows={3}
value={r.text.text}
onChange={(e) => {
const current = screen.reviews?.items ?? [];
const items = current.map((v, i) => (i === idx ? { ...v, text: { text: e.target.value } } : v));
onUpdate({ reviews: { ...screen.reviews, items } });
}}
/>
<TextInput
label="Avatar src"
value={r.avatar?.src ?? ""}
onChange={(e) => {
const current = screen.reviews?.items ?? [];
const items = current.map((v, i) => (i === idx ? { ...v, avatar: { src: e.target.value } } : v));
onUpdate({ reviews: { ...screen.reviews, items } });
}}
/>
<TextInput
label="Portrait src"
value={r.portrait?.src ?? ""}
onChange={(e) => {
const current = screen.reviews?.items ?? [];
const items = current.map((v, i) => (i === idx ? { ...v, portrait: { src: e.target.value } } : v));
onUpdate({ reviews: { ...screen.reviews, items } });
}}
/>
<TextInput
label="Photo src"
value={r.photo?.src ?? ""}
onChange={(e) => {
const current = screen.reviews?.items ?? [];
const items = current.map((v, i) => (i === idx ? { ...v, photo: { src: e.target.value } } : v));
onUpdate({ reviews: { ...screen.reviews, items } });
}}
/>
<div className="space-y-2">
<TextInput
label="Rating (1-5)"
type="number"
value={String(r.rating ?? 5)}
onChange={(e) => {
const current = screen.reviews?.items ?? [];
const items = current.map((v, i) => (i === idx ? { ...v, rating: Number(e.target.value) } : v));
onUpdate({ reviews: { ...screen.reviews, items } });
}}
/>
<div className="flex justify-end">
<Button
type="button"
variant="ghost"
className="h-8 w-8 p-0 text-red-500 hover:text-red-600"
onClick={() => {
const current = screen.reviews?.items ?? [];
const items = current.filter((_, i) => i !== idx);
onUpdate({ reviews: { ...screen.reviews, items } });
}}
aria-label="Удалить отзыв"
>
<Trash className="h-4 w-4" />
</Button>
</div>
</div>
</div>
))}
</div>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Common Questions</h4>
<div className="space-y-3">
<TextInput
label="Title"
value={screen.commonQuestions?.title?.text ?? ""}
onChange={(e) => onUpdate({ commonQuestions: { title: { text: e.target.value }, items: screen.commonQuestions?.items ?? [] } })}
/>
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">Items</span>
<Button
type="button"
className="h-7 px-2 text-xs"
onClick={() => {
const current = screen.commonQuestions?.items ?? [];
const items = [...current, { question: "", answer: "" }];
onUpdate({ commonQuestions: { ...screen.commonQuestions, items } });
}}
>
Добавить
</Button>
</div>
{(screen.commonQuestions?.items ?? []).map((q, idx) => (
<div key={idx} className="grid grid-cols-1 gap-2 border border-border/60 rounded-md p-3">
<TextInput
label={`Question #${idx + 1}`}
value={q.question}
onChange={(e) => {
const current = screen.commonQuestions?.items ?? [];
const items = current.map((v, i) => (i === idx ? { ...v, question: e.target.value } : v));
onUpdate({ commonQuestions: { ...screen.commonQuestions, items } });
}}
/>
<TextAreaInput
label="Answer"
rows={2}
value={q.answer}
onChange={(e) => {
const current = screen.commonQuestions?.items ?? [];
const items = current.map((v, i) => (i === idx ? { ...v, answer: e.target.value } : v));
onUpdate({ commonQuestions: { ...screen.commonQuestions, items } });
}}
/>
<div className="flex items-center justify-end">
<Button
type="button"
variant="ghost"
className="h-8 w-8 p-0 text-red-500 hover:text-red-600"
onClick={() => {
const current = screen.commonQuestions?.items ?? [];
const items = current.filter((_, i) => i !== idx);
onUpdate({ commonQuestions: { ...screen.commonQuestions, items } });
}}
aria-label="Удалить вопрос"
>
<Trash className="h-4 w-4" />
</Button>
</div>
</div>
))}
</div>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Progress To See Soulmate</h4>
<div className="space-y-3">
<TextInput
label="Title"
value={screen.progressToSeeSoulmate?.title?.text ?? ""}
onChange={(e) => onUpdate({ progressToSeeSoulmate: { ...screen.progressToSeeSoulmate, title: { text: e.target.value } } })}
/>
<TextInput
label="Progress value (0-100)"
type="number"
value={String(screen.progressToSeeSoulmate?.progress?.value ?? 0)}
onChange={(e) => onUpdate({ progressToSeeSoulmate: { ...screen.progressToSeeSoulmate, progress: { value: Number(e.target.value) } } })}
/>
<TextInput
label="Left text"
value={screen.progressToSeeSoulmate?.leftText?.text ?? ""}
onChange={(e) => onUpdate({ progressToSeeSoulmate: { ...screen.progressToSeeSoulmate, leftText: { text: e.target.value } } })}
/>
<TextInput
label="Right text"
value={screen.progressToSeeSoulmate?.rightText?.text ?? ""}
onChange={(e) => onUpdate({ progressToSeeSoulmate: { ...screen.progressToSeeSoulmate, rightText: { text: e.target.value } } })}
/>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Steps To See Soulmate</h4>
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">Steps</span>
<Button
type="button"
className="h-7 px-2 text-xs"
onClick={() => {
const current = screen.stepsToSeeSoulmate?.steps ?? [];
const steps = [
...current,
{ title: { text: "" }, description: { text: "" }, icon: "questions" as const, isActive: false },
];
onUpdate({ stepsToSeeSoulmate: { ...screen.stepsToSeeSoulmate, steps } });
}}
>
Добавить шаг
</Button>
</div>
{(screen.stepsToSeeSoulmate?.steps ?? []).map((step, idx) => (
<div key={idx} className="grid grid-cols-1 gap-2 border border-border/60 rounded-md p-3">
<div className="space-y-3">
<TextInput
label={`Step #${idx + 1} title`}
value={step.title.text}
onChange={(e) => {
const current = screen.stepsToSeeSoulmate?.steps ?? [];
const steps = current.map((s, i) => (i === idx ? { ...s, title: { text: e.target.value } } : s));
onUpdate({ stepsToSeeSoulmate: { ...screen.stepsToSeeSoulmate, steps } });
}}
/>
<TextInput
label="Icon (questions|profile|sketch|astro|chat)"
value={(step.icon ?? "") as string}
onChange={(e) => {
const icon = e.target.value as "questions" | "profile" | "sketch" | "astro" | "chat";
const current = screen.stepsToSeeSoulmate?.steps ?? [];
const steps = current.map((s, i) => (i === idx ? { ...s, icon } : s));
onUpdate({ stepsToSeeSoulmate: { ...screen.stepsToSeeSoulmate, steps } });
}}
/>
</div>
<TextAreaInput
label="Description"
rows={2}
value={step.description.text}
onChange={(e) => {
const current = screen.stepsToSeeSoulmate?.steps ?? [];
const steps = current.map((s, i) => (i === idx ? { ...s, description: { text: e.target.value } } : s));
onUpdate({ stepsToSeeSoulmate: { ...screen.stepsToSeeSoulmate, steps } });
}}
/>
<div className="flex items-center justify-between">
<label className="text-xs flex items-center gap-2">
<input
type="checkbox"
checked={Boolean(step.isActive)}
onChange={(e) => {
const current = screen.stepsToSeeSoulmate?.steps ?? [];
const steps = current.map((s, i) => (i === idx ? { ...s, isActive: e.target.checked } : s));
onUpdate({ stepsToSeeSoulmate: { ...screen.stepsToSeeSoulmate, steps } });
}}
/>
Active
</label>
<Button
type="button"
variant="ghost"
className="h-8 w-8 p-0 text-red-500 hover:text-red-600"
onClick={() => {
const current = screen.stepsToSeeSoulmate?.steps ?? [];
const steps = current.filter((_, i) => i !== idx);
onUpdate({ stepsToSeeSoulmate: { ...screen.stepsToSeeSoulmate, steps } });
}}
aria-label="Удалить шаг"
>
<Trash className="h-4 w-4" />
</Button>
</div>
</div>
))}
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Money Back Guarantee</h4>
<div className="space-y-3">
<TextInput
label="Title"
value={screen.moneyBackGuarantee?.title?.text ?? ""}
onChange={(e) => onUpdate({ moneyBackGuarantee: { ...screen.moneyBackGuarantee, title: { text: e.target.value } } })}
/>
<TextAreaInput
label="Text"
value={screen.moneyBackGuarantee?.text?.text ?? ""}
onChange={(e) => onUpdate({ moneyBackGuarantee: { ...screen.moneyBackGuarantee, text: { text: e.target.value } } })}
/>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Policy</h4>
<div className="grid grid-cols-1 gap-3">
<TextAreaInput
label="Text"
rows={3}
value={screen.policy?.text?.text ?? ""}
onChange={(e) => onUpdate({ policy: { text: { text: e.target.value } } })}
/>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Users&apos; Portraits</h4>
<div className="space-y-3">
<TextInput
label="Title"
value={screen.usersPortraits?.title?.text ?? ""}
onChange={(e) => onUpdate({ usersPortraits: { ...screen.usersPortraits, title: { text: e.target.value } } })}
/>
<TextInput
label="Button text"
value={screen.usersPortraits?.buttonText ?? ""}
onChange={(e) => onUpdate({ usersPortraits: { ...screen.usersPortraits, buttonText: e.target.value } })}
/>
</div>
<div className="space-y-2 mt-2">
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">Images</span>
<Button
type="button"
className="h-7 px-2 text-xs"
onClick={() => {
const current = screen.usersPortraits?.images ?? [];
const images = [...current, { src: "" }];
onUpdate({ usersPortraits: { ...screen.usersPortraits, images } });
}}
>
Добавить
</Button>
</div>
{(screen.usersPortraits?.images ?? []).map((img, idx) => (
<div key={idx} className="space-y-2">
<TextInput
label={`Image #${idx + 1} src`}
value={img.src}
onChange={(e) => {
const current = screen.usersPortraits?.images ?? [];
const images = current.map((v, i) => (i === idx ? { ...v, src: e.target.value } : v));
onUpdate({ usersPortraits: { ...screen.usersPortraits, images } });
}}
/>
<div className="flex justify-end">
<Button
type="button"
variant="ghost"
className="h-8 w-8 p-0 text-red-500 hover:text-red-600"
onClick={() => {
const current = screen.usersPortraits?.images ?? [];
const images = current.filter((_, i) => i !== idx);
onUpdate({ usersPortraits: { ...screen.usersPortraits, images } });
}}
aria-label="Удалить изображение"
>
<Trash className="h-4 w-4" />
</Button>
</div>
</div>
))}
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Joined Today With Avatars</h4>
<div className="space-y-3">
<TextInput
label="Count"
value={screen.joinedTodayWithAvatars?.count?.text ?? ""}
onChange={(e) => onUpdate({ joinedTodayWithAvatars: { ...screen.joinedTodayWithAvatars, count: { text: e.target.value } } })}
/>
<TextInput
label="Text"
value={screen.joinedTodayWithAvatars?.text?.text ?? ""}
onChange={(e) => onUpdate({ joinedTodayWithAvatars: { ...screen.joinedTodayWithAvatars, text: { text: e.target.value } } })}
/>
</div>
<div className="space-y-2 mt-2">
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">Avatars</span>
<Button
type="button"
className="h-7 px-2 text-xs"
onClick={() => {
const current = screen.joinedTodayWithAvatars?.avatars?.images ?? [];
const images = [...current, { src: "" }];
onUpdate({ joinedTodayWithAvatars: { ...screen.joinedTodayWithAvatars, avatars: { images } } });
}}
>
Добавить
</Button>
</div>
{(screen.joinedTodayWithAvatars?.avatars?.images ?? []).map((img, idx) => (
<div key={idx} className="space-y-2">
<TextInput
label={`Avatar #${idx + 1} src`}
value={img.src}
onChange={(e) => {
const current = screen.joinedTodayWithAvatars?.avatars?.images ?? [];
const images = current.map((v, i) => (i === idx ? { ...v, src: e.target.value } : v));
onUpdate({ joinedTodayWithAvatars: { ...screen.joinedTodayWithAvatars, avatars: { images } } });
}}
/>
<div className="flex justify-end">
<Button
type="button"
variant="ghost"
className="h-8 w-8 p-0 text-red-500 hover:text-red-600"
onClick={() => {
const current = screen.joinedTodayWithAvatars?.avatars?.images ?? [];
const images = current.filter((_, i) => i !== idx);
onUpdate({ joinedTodayWithAvatars: { ...screen.joinedTodayWithAvatars, avatars: { images } } });
}}
aria-label="Удалить аватар"
>
<Trash className="h-4 w-4" />
</Button>
</div>
</div>
))}
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Try For Days</h4>
<div className="space-y-3">
<TextInput
label="Title"
value={screen.tryForDays?.title?.text ?? ""}
onChange={(e) => onUpdate({ tryForDays: { ...screen.tryForDays, title: { text: e.target.value } } })}
/>
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-xs text-muted-foreground">Items</span>
<Button
type="button"
className="h-7 px-2 text-xs"
onClick={() => {
const current = screen.tryForDays?.textList?.items ?? [];
const items = [...current, { text: "" }];
onUpdate({ tryForDays: { ...screen.tryForDays, textList: { items } } });
}}
>
Добавить
</Button>
</div>
{(screen.tryForDays?.textList?.items ?? []).map((it, idx) => (
<div key={idx} className="space-y-2">
<TextAreaInput
label={`Item #${idx + 1}`}
rows={2}
value={it.text}
onChange={(e) => {
const current = screen.tryForDays?.textList?.items ?? [];
const items = current.map((v, i) => (i === idx ? { ...v, text: e.target.value } : v));
onUpdate({ tryForDays: { ...screen.tryForDays, textList: { items } } });
}}
/>
<div className="flex justify-end">
<Button
type="button"
variant="ghost"
className="h-8 w-8 p-0 text-red-500 hover:text-red-600"
onClick={() => {
const current = screen.tryForDays?.textList?.items ?? [];
const items = current.filter((_, i) => i !== idx);
onUpdate({ tryForDays: { ...screen.tryForDays, textList: { items } } });
}}
aria-label="Удалить элемент"
>
<Trash className="h-4 w-4" />
</Button>
</div>
</div>
))}
</div>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Total Price</h4>
<div className="space-y-3">
<TextInput
label="Coupon title"
value={screen.totalPrice?.couponContainer?.title?.text ?? ""}
onChange={(e) => onUpdate({ totalPrice: { couponContainer: { ...(screen.totalPrice?.couponContainer ?? {}), title: { text: e.target.value } }, priceContainer: screen.totalPrice?.priceContainer } })}
/>
<TextInput
label="Coupon button text"
value={screen.totalPrice?.couponContainer?.buttonText ?? ""}
onChange={(e) => onUpdate({ totalPrice: { couponContainer: { ...(screen.totalPrice?.couponContainer ?? {}), buttonText: e.target.value }, priceContainer: screen.totalPrice?.priceContainer } })}
/>
<TextInput
label="Price title"
value={screen.totalPrice?.priceContainer?.title?.text ?? ""}
onChange={(e) => onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), title: { text: e.target.value } } } })}
/>
<TextInput
label="Price"
value={screen.totalPrice?.priceContainer?.price?.text ?? ""}
onChange={(e) => onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), price: { text: e.target.value } } } })}
/>
<TextInput
label="Old price"
value={screen.totalPrice?.priceContainer?.oldPrice?.text ?? ""}
onChange={(e) => onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), oldPrice: { text: e.target.value } } } })}
/>
<TextInput
label="Discount"
value={screen.totalPrice?.priceContainer?.discount?.text ?? ""}
onChange={(e) => onUpdate({ totalPrice: { couponContainer: screen.totalPrice?.couponContainer ?? { title: { text: "" }, buttonText: "" }, priceContainer: { ...(screen.totalPrice?.priceContainer ?? {}), discount: { text: e.target.value } } } })}
/>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Joined Today</h4>
<div className="space-y-3">
<TextInput
label="Count"
value={screen.joinedToday?.count?.text ?? ""}
onChange={(e) => onUpdate({ joinedToday: { ...screen.joinedToday, count: { text: e.target.value } } })}
/>
<TextInput
label="Text"
value={screen.joinedToday?.text?.text ?? ""}
onChange={(e) => onUpdate({ joinedToday: { ...screen.joinedToday, text: { text: e.target.value } } })}
/>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Trusted By Over</h4>
<div className="space-y-3">
<TextInput
label="Text"
value={screen.trustedByOver?.text?.text ?? ""}
onChange={(e) => onUpdate({ trustedByOver: { text: { text: e.target.value } } })}
/>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Finding The One Guide</h4>
<div className="space-y-3">
<TextInput
label="Emoji"
value={screen.findingOneGuide?.header?.emoji?.text ?? ""}
onChange={(e) => onUpdate({ findingOneGuide: { ...screen.findingOneGuide, header: { ...(screen.findingOneGuide?.header ?? {}), emoji: { text: e.target.value } } } })}
/>
<TextInput
label="Title"
value={screen.findingOneGuide?.header?.title?.text ?? ""}
onChange={(e) => onUpdate({ findingOneGuide: { ...screen.findingOneGuide, header: { ...(screen.findingOneGuide?.header ?? {}), title: { text: e.target.value } } } })}
/>
<TextAreaInput
label="Text"
value={screen.findingOneGuide?.text?.text ?? ""}
onChange={(e) => onUpdate({ findingOneGuide: { ...screen.findingOneGuide, text: { text: e.target.value } } })}
/>
<TextInput
label="Blur text"
value={screen.findingOneGuide?.blur?.text?.text ?? ""}
onChange={(e) => onUpdate({ findingOneGuide: { ...screen.findingOneGuide, blur: { ...(screen.findingOneGuide?.blur ?? {}), text: { text: e.target.value }, icon: "lock" } } })}
/>
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Unlock Your Sketch</h4>
<div className="space-y-3">
<TextInput label="Заголовок" value={screen.unlockYourSketch?.title?.text ?? ""} onChange={(e) => updateUnlock({ title: { text: e.target.value } })} />
<TextInput label="Подзаголовок" value={screen.unlockYourSketch?.subtitle?.text ?? ""} onChange={(e) => updateUnlock({ subtitle: { text: e.target.value } })} />
<TextInput label="Изображение" value={screen.unlockYourSketch?.image?.src ?? ""} onChange={(e) => updateUnlock({ image: { src: e.target.value } })} />
<TextInput label="Текст на блюре" value={screen.unlockYourSketch?.blur?.text?.text ?? ""} onChange={(e) => updateUnlock({ blur: { ...(screen.unlockYourSketch?.blur ?? {}), text: { text: e.target.value }, icon: "lock" } as NonNullable<TrialPaymentScreenDefinition["unlockYourSketch"]>["blur"] })} />
<TextInput label="Текст кнопки" value={screen.unlockYourSketch?.buttonText ?? ""} onChange={(e) => updateUnlock({ buttonText: e.target.value })} />
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Payment Buttons</h4>
{(screen.paymentButtons?.buttons ?? []).map((b, i) => (
<div key={i} className="space-y-2 mb-2">
<TextInput label={`Текст #${i + 1}`} value={b.text} onChange={(e) => updatePaymentButtons(i, "text", e.target.value)} />
<TextInput label="Иконка (pay|google|card)" value={("icon" in b ? (b as { icon?: "pay"|"google"|"card" }).icon ?? "" : "")} onChange={(e) => updatePaymentButtons(i, "icon", e.target.value)} />
<label className="text-xs flex items-center gap-2"><input type="checkbox" checked={("primary" in b ? (b as { primary?: boolean }).primary ?? false : false)} onChange={(e) => updatePaymentButtons(i, "primary", e.target.checked)} /> Primary</label>
</div>
))}
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Footer / Contacts</h4>
<div className="space-y-3">
<TextInput label="Email" value={screen.footer?.contacts?.email?.text ?? screen.footer?.contacts?.email?.href ?? ""} onChange={(e) => updateFooterContacts("email", { href: e.target.value, text: e.target.value })} />
<TextAreaInput label="Адрес" value={screen.footer?.contacts?.address?.text ?? ""} onChange={(e) => updateFooterContacts("address", { text: e.target.value })} />
</div>
</div>
<div>
<h4 className="text-sm font-medium text-foreground mb-2">Still Have Questions</h4>
<div className="space-y-3">
<TextInput
label="Title"
value={screen.stillHaveQuestions?.title?.text ?? ""}
onChange={(e) => onUpdate({ stillHaveQuestions: { ...screen.stillHaveQuestions, title: { text: e.target.value } } })}
/>
<TextInput
label="Action button"
value={screen.stillHaveQuestions?.actionButtonText ?? ""}
onChange={(e) => onUpdate({ stillHaveQuestions: { ...screen.stillHaveQuestions, actionButtonText: e.target.value } })}
/>
<TextInput
label="Contact button"
value={screen.stillHaveQuestions?.contactButtonText ?? ""}
onChange={(e) => onUpdate({ stillHaveQuestions: { ...screen.stillHaveQuestions, contactButtonText: e.target.value } })}
/>
</div>
</div>
</div>
);
}

View File

@ -4,3 +4,4 @@ export { CouponScreenConfig } from "./CouponScreenConfig";
export { FormScreenConfig } from "./FormScreenConfig";
export { ListScreenConfig } from "./ListScreenConfig";
export { TemplateConfig } from "./TemplateConfig";
export { TrialPaymentScreenConfig } from "./TrialPaymentScreenConfig";

View File

@ -31,7 +31,11 @@ function estimatePathLength(
const currentScreen = funnel.screens.find((s) => s.id === currentScreenId);
if (!currentScreen) break;
const resolvedScreen = resolveScreenVariant(currentScreen, answers, funnel.screens);
const resolvedScreen = resolveScreenVariant(
currentScreen,
answers,
funnel.screens
);
const nextScreenId = resolveNextScreenId(
resolvedScreen,
answers,
@ -86,13 +90,6 @@ export function FunnelRuntime({ funnel, initialScreenId }: FunnelRuntimeProps) {
createSession();
}, [createSession]);
// useEffect(() => {
// // updateSession({
// // answers: answers,
// // });
// console.log("answers", answers);
// }, [answers]);
useEffect(() => {
registerScreen(currentScreen.id);
}, [currentScreen.id, registerScreen]);
@ -130,10 +127,7 @@ export function FunnelRuntime({ funnel, initialScreenId }: FunnelRuntimeProps) {
};
const handleContinue = () => {
console.log({
[currentScreen.id]: answers[currentScreen.id],
});
if (answers[currentScreen.id]) {
if (answers[currentScreen.id] && currentScreen.template !== "email") {
updateSession({
answers: {
[currentScreen.id]: answers[currentScreen.id],
@ -223,9 +217,6 @@ export function FunnelRuntime({ funnel, initialScreenId }: FunnelRuntimeProps) {
// Auto-advance for single selection without action button
if (shouldAutoAdvance) {
console.log({
[currentScreen.id]: ids,
});
updateSession({
answers: {
[currentScreen.id]: ids,

View File

@ -47,7 +47,7 @@ export function EmailTemplate({
defaultTexts,
}: EmailTemplateProps) {
const { authorization, isLoading, error } = useAuth({
funnelId: funnel.meta.id,
funnelId: funnel?.meta?.id ?? "preview",
});
const [isTouched, setIsTouched] = useState(false);
@ -78,8 +78,10 @@ export function EmailTemplate({
}
try {
await authorization(email);
onContinue();
const token = await authorization(email);
if (token) {
onContinue();
}
} catch (err) {
console.error("Authorization failed:", err);
}

View File

@ -1,110 +1,64 @@
import { Meta, StoryObj } from "@storybook/nextjs-vite";
import { SoulmatePortraitTemplate } from "./SoulmatePortraitTemplate";
import { fn } from "storybook/test";
import { buildSoulmateDefaults } from "@/lib/admin/builder/state/defaults/soulmate";
import type { SoulmatePortraitScreenDefinition } from "@/lib/funnel/types";
// Получаем дефолтные значения из builder
const defaultScreen = buildSoulmateDefaults("soulmate-screen-story") as SoulmatePortraitScreenDefinition;
const defaultScreen: SoulmatePortraitScreenDefinition = {
id: "soulmate-story",
template: "soulmate",
header: { show: false, showBackButton: false },
title: { text: "Soulmate Portrait" },
subtitle: { text: "Готов увидеть, кто твоя настоящая Родственная душа?" },
description: {
text: "Готов увидеть, кто твоя настоящая Родственная душа?",
align: "center",
},
soulmatePortraitsDelivered: {
image: "/soulmate-portrait-delivered-male.jpg",
text: {
text: "soulmate portraits delivered today",
font: "inter",
weight: "medium",
size: "sm",
color: "primary",
},
avatars: [
{ src: "/avatars/male-1.jpg", alt: "Male 1" },
{ src: "/avatars/male-2.jpg", alt: "Male 2" },
{ src: "/avatars/male-3.jpg", alt: "Male 3" },
{ src: "", fallbackText: "900+" },
],
},
textList: {
items: [
{
text: "Всего 2 минуты — и Портрет откроет того, кто связан с тобой судьбой.",
},
{ text: "Поразительная точность 99%." },
{ text: "Тебя ждёт неожиданное открытие." },
{ text: "Осталось лишь осмелиться взглянуть." },
],
},
bottomActionButton: { text: "Continue", showPrivacyTermsConsent: true },
};
/** SoulmatePortraitTemplate - результирующие экраны с портретом партнера */
const meta: Meta<typeof SoulmatePortraitTemplate> = {
title: "Funnel Templates/SoulmatePortraitTemplate",
component: SoulmatePortraitTemplate,
tags: ["autodocs"],
parameters: {
layout: "fullscreen",
},
parameters: { layout: "fullscreen" },
args: {
screen: defaultScreen,
onContinue: fn(),
canGoBack: true,
onBack: fn(),
screenProgress: { current: 10, total: 10 }, // Обычно финальный экран
defaultTexts: {
nextButton: "Next",
},
},
argTypes: {
screen: {
control: { type: "object" },
},
screenProgress: {
control: { type: "object" },
},
onContinue: { action: "continue" },
onBack: { action: "back" },
screenProgress: undefined,
defaultTexts: { nextButton: "Next" },
},
argTypes: {},
};
export default meta;
type Story = StoryObj<typeof meta>;
/** Дефолтный soulmate portrait экран */
export const Default: Story = {};
/** Экран без описания */
export const WithoutDescription: Story = {
args: {
screen: {
...defaultScreen,
description: undefined,
},
},
};
/** Экран с кастомным описанием */
export const CustomDescription: Story = {
args: {
screen: {
...defaultScreen,
description: {
text: "На основе ваших ответов мы создали уникальный **портрет вашей второй половинки**. Этот анализ поможет вам лучше понять, кто может стать идеальным партнером.",
font: "inter",
weight: "regular",
align: "center",
size: "md",
color: "default",
},
},
},
};
/** Экран без header */
export const WithoutHeader: Story = {
args: {
screen: {
...defaultScreen,
header: {
show: false,
},
},
},
};
/** Экран без subtitle */
export const WithoutSubtitle: Story = {
args: {
screen: {
...defaultScreen,
subtitle: undefined, // Просто удаляем subtitle
},
},
};
/** Финальный экран (без прогресса) */
export const FinalScreen: Story = {
args: {
screen: {
...defaultScreen,
header: {
show: true,
showBackButton: false, // На финальном экране обычно нет кнопки назад
showProgress: false, // И нет прогресса
},
},
screenProgress: undefined,
canGoBack: false,
},
};

View File

@ -1,8 +1,15 @@
"use client";
import type { SoulmatePortraitScreenDefinition, DefaultTexts } from "@/lib/funnel/types";
import type {
SoulmatePortraitScreenDefinition,
DefaultTexts,
} from "@/lib/funnel/types";
import { TemplateLayout } from "../layouts/TemplateLayout";
import { createTemplateLayoutProps } from "@/lib/funnel/templateHelpers";
import Typography from "@/components/ui/Typography/Typography";
import { buildTypographyProps } from "@/lib/funnel/mappers";
import SoulmatePortraitsDelivered from "@/components/widgets/SoulmatePortraitsDelivered/SoulmatePortraitsDelivered";
import { cn } from "@/lib/utils";
interface SoulmatePortraitTemplateProps {
screen: SoulmatePortraitScreenDefinition;
@ -21,14 +28,26 @@ export function SoulmatePortraitTemplate({
screenProgress,
defaultTexts,
}: SoulmatePortraitTemplateProps) {
// Скрываем subtitle как ненужный для этого экрана
const screenForLayout: SoulmatePortraitScreenDefinition = {
...screen,
subtitle: undefined,
};
const layoutProps = createTemplateLayoutProps(
screen,
screenForLayout,
{ canGoBack, onBack },
screenProgress,
{
preset: "center",
titleDefaults: { font: "manrope", weight: "bold", align: "center", size: "xl", color: "primary" },
subtitleDefaults: { font: "manrope", weight: "medium", color: "default", align: "left", size: "lg" },
titleDefaults: {
font: "manrope",
weight: "bold",
align: "center",
size: "xl",
className: "leading-[125%] text-primary text-xl",
},
subtitleDefaults: undefined,
actionButton: {
defaultText: defaultTexts?.nextButton || "Continue",
disabled: false,
@ -39,7 +58,91 @@ export function SoulmatePortraitTemplate({
return (
<TemplateLayout {...layoutProps}>
<div className="-mt-[20px]">
<div className="max-w-[560px] mx-auto flex flex-col items-center gap-[30px]">
{screen.soulmatePortraitsDelivered && (
<SoulmatePortraitsDelivered
image={screen.soulmatePortraitsDelivered.image}
textProps={
screen.soulmatePortraitsDelivered.text
? buildTypographyProps(screen.soulmatePortraitsDelivered.text, {
as: "p",
defaults: { font: "inter", size: "sm", color: "primary" },
})
: undefined
}
avatarsProps={
screen.soulmatePortraitsDelivered.avatars
? {
avatars: screen.soulmatePortraitsDelivered.avatars.map(
(a) => ({
imageProps: a.src
? { src: a.src, alt: a.alt ?? "" }
: undefined,
fallbackProps: a.fallbackText
? {
children: (
<Typography
size="xs"
weight="bold"
className="text-[#FF6B9D]"
>
{a.fallbackText}
</Typography>
),
className: "bg-background",
}
: undefined,
className: a.fallbackText ? "w-fit px-1" : undefined,
})
),
}
: undefined
}
/>
)}
<div className="w-full flex flex-col items-center gap-2.5">
{screen.description &&
(() => {
const descProps = buildTypographyProps(screen.description, {
as: "p",
defaults: {
align: "center",
font: "inter",
size: "md",
weight: "bold",
className: "text-[25px] font-bold",
},
});
if (!descProps) return null;
const { children, ...rest } = descProps;
return (
<Typography {...rest} enableMarkup>
{children}
</Typography>
);
})()}
{screen.textList && (
<ul className={cn("list-disc pl-6 w-full")}>
{screen.textList.items.map((item, index) => {
const itemProps = buildTypographyProps(item, {
as: "li",
defaults: { font: "inter", weight: "medium", size: "md" },
});
if (!itemProps) return null;
const { children, ...rest } = itemProps;
return (
<Typography
key={index}
{...rest}
className={cn("list-item text-[17px] leading-[26px]")}
>
{children}
</Typography>
);
})}
</ul>
)}
</div>
</div>
</TemplateLayout>
);

View File

@ -0,0 +1,266 @@
import { Meta, StoryObj } from "@storybook/nextjs-vite";
import { TrialPaymentTemplate } from "./TrialPaymentTemplate";
import { fn } from "storybook/test";
import type { TrialPaymentScreenDefinition } from "@/lib/funnel/types";
const defaultScreen: TrialPaymentScreenDefinition = {
id: "trial-payment-screen-story",
template: "trialPayment",
title: { text: "" },
subtitle: { text: "" },
bottomActionButton: { show: false, showPrivacyTermsConsent: false },
headerBlock: {
timerSeconds: 600,
text: { text: "⚠️ Your sketch expires soon!" },
timer: { text: "" },
},
unlockYourSketch: {
title: { text: "Unlock Your Sketch" },
subtitle: { text: "Just One Click to Reveal Your Match!" },
image: { src: "/trial-payment/portrait-female.jpg" },
blur: { text: { text: "Unlock to reveal your personalized portrait" }, icon: "lock" },
buttonText: "Get Me Soulmate Sketch",
},
joinedToday: {
count: { text: "954" },
text: { text: "Joined today" },
},
trustedByOver: {
text: { text: "Trusted by over 355,000 people." },
},
findingOneGuide: {
header: {
emoji: { text: "❤️" },
title: { text: "Finding the One Guide" },
},
text: {
text:
"You're not just looking for someone — you're. You're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you're. You're not just looking for someone — you're. You're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you're",
},
blur: { text: { text: "Чтобы открыть весь отчёт, нужен полный доступ." }, icon: "lock" },
},
tryForDays: {
title: { text: "Попробуйте в течение 7 дней!" },
textList: {
items: [
{ text: "Receive a hand-drawn sketch of your soulmate, crafted by a trained AI-model." },
{ text: "Reveal the path to your soulmate with the Finding the One guide." },
{ text: "Talk to live experts and get guidance on finding your soulmate." },
{ text: "Start your 7-day trial for just $1.00 — then only $14.50/week for full access." },
{ text: "Cancel anytime—just 24 hours before renewal." },
],
},
},
totalPrice: {
couponContainer: {
title: { text: "Coupon\nCode" },
buttonText: "SOULMATE94",
},
priceContainer: {
title: { text: "Total" },
price: { text: "$1.00" },
oldPrice: { text: "$14.99" },
discount: { text: "94% discount applied" },
},
},
paymentButtons: {
buttons: [
{ text: "Pay", icon: "pay" },
{ text: "Pay", icon: "google" },
{ text: "Credit or debit card", icon: "card", primary: true },
],
},
moneyBackGuarantee: {
title: { text: "30-DAY MONEY-BACK GUARANTEE" },
text: { text: "If you don't receive your soulmate sketch, we'll refund your money!" },
},
policy: {
text: { text: "By clicking Continue, you agree to our Terms of Use & Service and Privacy Policy. You also acknowledge that your 1 week introductory plan to Respontika, billed at $1.00, will automatically renew at $14.50 every 1 week unless canceled before the end of the trial period." },
},
usersPortraits: {
title: { text: "Our Users' Soulmate Portraits" },
images: [
{ src: "/trial-payment/users-portraits/1.jpg" },
{ src: "/trial-payment/users-portraits/2.jpg" },
{ src: "/trial-payment/users-portraits/3.jpg" },
],
buttonText: "Get me soulmate sketch",
},
joinedTodayWithAvatars: {
count: { text: "954" },
text: { text: "people joined today" },
avatars: {
images: [
{ src: "/trial-payment/avatars/1.jpg" },
{ src: "/trial-payment/avatars/2.jpg" },
{ src: "/trial-payment/avatars/3.jpg" },
{ src: "/trial-payment/avatars/4.jpg" },
{ src: "/trial-payment/avatars/5.jpg" },
],
},
},
progressToSeeSoulmate: {
title: { text: "See Your Soulmate Just One Step Away" },
progress: { value: 92 },
leftText: { text: "Step 2 of 5" },
rightText: { text: "99% Complete" },
},
stepsToSeeSoulmate: {
steps: [
{
title: { text: "Questions Answered" },
description: { text: "You've provided all the necessary information about your preferences and personality." },
icon: "questions",
isActive: true,
},
{
title: { text: "Profile Analysis" },
description: { text: "Our advanced system is creating your perfect soulmate profile." },
icon: "profile",
isActive: true,
},
{
title: { text: "Sketch Creation" },
description: { text: "Your personalized soulmate sketch will be created." },
icon: "sketch",
isActive: false,
},
{
title: { text: "Астрологические Идеи" },
description: { text: "Уникальные астрологические рекомендации, усиливающие совместимость." },
icon: "astro",
isActive: false,
},
{
title: { text: "Персонализированный чат с экспертом" },
description: { text: "Персональные советы от экспертов по отношениям." },
icon: "chat",
isActive: false,
},
],
buttonText: "Show Me My Soulmate",
},
reviews: {
title: { text: "Loved and Trusted Worldwide" },
items: [
{
name: { text: "Jennifer Wilson 🇺🇸" },
text: { text: "**“Я увидела свои ошибки… и нашла мужа”**\nПортрет сразу зацепил — было чувство, что я уже где-то его видела. Но настоящий перелом произошёл после гайда: я поняла, почему снова и снова выбирала «не тех». И самое удивительное — вскоре я познакомилась с мужчиной, который оказался точной копией того самого портрета. Сейчас он мой муж, и когда мы сравнили рисунок с его фото, сходство было просто вау." },
avatar: { src: "/trial-payment/reviews/avatars/1.jpg" },
portrait: { src: "/trial-payment/reviews/portraits/1.jpg" },
photo: { src: "/trial-payment/reviews/photos/1.jpg" },
rating: 5,
date: { text: "1 day ago" },
},
{
name: { text: "Amanda Davis 🇨🇦" },
text: { text: "**“Я поняла своего партнёра лучше за один вечер, чем за несколько лет”**\nПрошла тест ради интереса — портрет нас удивил. Но настоящий прорыв случился, когда я прочитала гайд о второй половинке. Там были точные подсказки о том, как мы можем поддерживать друг друга. Цена смешная, а ценность огромная: теперь у нас меньше недопониманий и больше тепла." },
avatar: { src: "/trial-payment/reviews/avatars/2.jpg" },
portrait: { src: "/trial-payment/reviews/portraits/2.jpg" },
photo: { src: "/trial-payment/reviews/photos/2.jpg" },
rating: 5,
date: { text: "4 days ago" },
},
{
name: { text: "Michael Johnson 🇬🇧" },
text: { text: "**“Увидел её лицо — и мурашки по коже”**\nКогда пришёл результат теста и показали портрет, я реально замер. Это была та самая девушка, с которой я начал встречаться пару недель назад. И гайд прямо описал, почему мы тянемся друг к другу. Честно, я не ожидал такого совпадения." },
avatar: { src: "/trial-payment/reviews/avatars/3.jpg" },
portrait: { src: "/trial-payment/reviews/portraits/3.jpg" },
photo: { src: "/trial-payment/reviews/photos/3.jpg" },
rating: 5,
date: { text: "1 week ago" },
},
],
},
commonQuestions: {
title: { text: "Common Questions" },
items: [
{
question: "When will I receive my sketch?",
answer:
"Your personalized soulmate sketch will be delivered within 24-48 hours after completing your order. You'll receive an email notification when it's ready for viewing in your account.",
},
{
question: "How do I cancel my subscription?",
answer:
"You can cancel anytime from your account settings. Make sure to cancel at least 24 hours before the renewal date to avoid being charged.",
},
{
question: "How accurate are the readings?",
answer:
"Our readings are based on a combination of your answers and advanced pattern analysis. While they provide valuable insights, they are intended for guidance and entertainment purposes.",
},
{
question: "Is my data secure and private?",
answer:
"Yes. We follow strict data protection standards. Your data is encrypted and never shared with third parties without your consent.",
},
],
},
stillHaveQuestions: {
title: { text: "Still have questions? We're here to help!" },
actionButtonText: "Get me Soulmate Sketch",
contactButtonText: "Contact Support",
},
footer: {
title: { text: "WIT LAB ©" },
contacts: {
title: { text: "CONTACTS" },
email: { href: "support@witlab.com", text: "support@witlab.com" },
address: { text: "Wit Lab 2108 N ST STE N SACRAMENTO, CA95816, US" },
},
legal: {
title: { text: "LEGAL" },
links: [
{ href: "https://witlab.com/terms", text: "Terms of Service" },
{ href: "https://witlab.com/privacy", text: "Privacy Policy" },
{ href: "https://witlab.com/refund", text: "Refund Policy" },
],
copyright: {
text:
"Copyright © 2025 Wit Lab™. All rights reserved. All trademarks referenced herein are the properties of their respective owners.",
},
},
paymentMethods: {
title: { text: "PAYMENT METHODS" },
methods: [
{ src: "/trial-payment/payment-methods/visa.svg", alt: "visa" },
{ src: "/trial-payment/payment-methods/mastercard.svg", alt: "mastercard" },
{ src: "/trial-payment/payment-methods/discover.svg", alt: "discover" },
{ src: "/trial-payment/payment-methods/apple.svg", alt: "apple" },
{ src: "/trial-payment/payment-methods/google.svg", alt: "google" },
{ src: "/trial-payment/payment-methods/paypal.svg", alt: "paypal" },
],
},
},
};
const meta: Meta<typeof TrialPaymentTemplate> = {
title: "Funnel Templates/TrialPaymentTemplate",
component: TrialPaymentTemplate,
tags: ["autodocs"],
parameters: {
layout: "fullscreen",
},
args: {
screen: defaultScreen,
onContinue: fn(),
canGoBack: true,
onBack: fn(),
screenProgress: { current: 8, total: 10 },
defaultTexts: {
nextButton: "Continue",
continueButton: "Continue",
},
},
argTypes: {
screen: { control: { type: "object" } },
},
};
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};

View File

@ -0,0 +1,993 @@
"use client";
import type {
TrialPaymentScreenDefinition,
DefaultTexts,
FunnelDefinition,
} from "@/lib/funnel/types";
import { TemplateLayout } from "../layouts/TemplateLayout";
import { createTemplateLayoutProps } from "@/lib/funnel/templateHelpers";
import { cn } from "@/lib/utils";
import { useRef } from "react";
import {
Header,
JoinedToday,
TrustedByOver,
JoinedTodayWithAvatars,
} from "@/components/domains/TrialPayment";
import { UnlockYourSketch } from "@/components/domains/TrialPayment/Cards";
import {
FindingOneGuide,
TryForDays,
TotalPrice,
PaymentButtons,
UsersPortraits,
} from "@/components/domains/TrialPayment/Cards";
import { MoneyBackGuarantee, Policy } from "@/components/domains/TrialPayment";
import {
StepsToSeeSoulmate,
Reviews,
CommonQuestions,
StillHaveQuestions,
Footer,
} from "@/components/domains/TrialPayment/Cards";
import ProgressToSeeSoulmate from "@/components/domains/TrialPayment/ProgressToSeeSoulmate/ProgressToSeeSoulmate";
import { buildTypographyProps } from "@/lib/funnel/mappers";
import { usePaymentPlacement } from "@/hooks/payment/usePaymentPlacement";
import { Spinner } from "@/components/ui/spinner";
import { Currency } from "@/shared/types";
import { getFormattedPrice } from "@/shared/utils/price";
import { useRouter } from "next/navigation";
import { ROUTES } from "@/shared/constants/client-routes";
interface TrialPaymentTemplateProps {
funnel: FunnelDefinition;
screen: TrialPaymentScreenDefinition;
onContinue: () => void;
canGoBack: boolean;
onBack: () => void;
screenProgress?: { current: number; total: number };
defaultTexts?: DefaultTexts;
}
export function TrialPaymentTemplate({
funnel,
screen,
canGoBack,
onBack,
screenProgress,
defaultTexts,
}: TrialPaymentTemplateProps) {
const router = useRouter();
// TODO: выбрать корректный paymentId для этого экрана (ключ из backend), временно "main"
const paymentId = "main";
const { placement, isLoading } = usePaymentPlacement({ funnel, paymentId });
const trialInterval = placement?.trialInterval || 7;
const trialPeriod = placement?.trialPeriod;
const variant = placement?.variants?.[0];
const productId = variant?.id || "";
const placementId = placement?.placementId || "";
const paywallId = placement?.paywallId || "";
const trialPrice = variant?.trialPrice || 0;
const price = variant?.price || 0;
const oldPrice = variant?.price || 0;
const billingPeriod = placement?.billingPeriod;
const billingInterval = placement?.billingInterval || 1;
const currency = placement?.currency || Currency.USD;
console.log({ placement });
const handlePayClick = () => {
router.push(
ROUTES.payment({
productId,
placementId,
paywallId,
})
);
};
const paymentSectionRef = useRef<HTMLDivElement | null>(null);
const scrollToPayment = () => {
if (paymentSectionRef.current) {
paymentSectionRef.current.scrollIntoView({
behavior: "smooth",
block: "start",
});
}
};
const formatPeriod = (
period: "DAY" | "WEEK" | "MONTH" | "YEAR" | undefined,
interval: number
) => {
if (!period) return `${interval} days`;
const unit =
period === "DAY"
? interval === 1
? "day"
: "days"
: period === "WEEK"
? interval === 1
? "week"
: "weeks"
: period === "MONTH"
? interval === 1
? "month"
: "months"
: interval === 1
? "year"
: "years";
return `${interval} ${unit}`;
};
const formattedTrialPrice = getFormattedPrice(trialPrice, currency);
const formattedBillingPrice = getFormattedPrice(price, currency);
const trialPeriodText = formatPeriod(trialPeriod, trialInterval);
const billingPeriodText = formatPeriod(billingPeriod, billingInterval);
const computeDiscountPercent = () => {
if (!oldPrice || !trialPrice || oldPrice <= 0) return undefined;
const ratio = 1 - trialPrice / oldPrice;
const percent = Math.max(0, Math.min(100, Math.round(ratio * 100)));
return String(percent);
};
const formatPeriodHyphen = (
period: "DAY" | "WEEK" | "MONTH" | "YEAR" | undefined,
interval: number
) => {
if (!period) return `${interval}-day`;
const unit =
period === "DAY"
? interval === 1
? "day"
: "days"
: period === "WEEK"
? interval === 1
? "week"
: "weeks"
: period === "MONTH"
? interval === 1
? "month"
: "months"
: interval === 1
? "year"
: "years";
return `${interval}-${unit}`;
};
const trialPeriodHyphenText = formatPeriodHyphen(trialPeriod, trialInterval);
const replacePlaceholders = (text: string | undefined) => {
if (!text) return "";
const values: Record<string, string> = {
trialPrice: formattedTrialPrice,
billingPrice: formattedBillingPrice,
oldPrice: getFormattedPrice(oldPrice || 0, currency),
discountPercent: computeDiscountPercent() ?? "",
trialPeriod: trialPeriodText,
billingPeriod: billingPeriodText,
trialPeriodHyphen: trialPeriodHyphenText,
};
let result = text;
for (const [key, value] of Object.entries(values)) {
result = result.replaceAll(`{{${key}}}`, value);
}
return result;
};
// Отключаем общий Header в TemplateLayout для этого экрана
const screenWithoutHeader = {
...screen,
header: {
...(screen.header || {}),
show: false,
showBackButton: false,
showProgress: false,
},
} as typeof screen;
// Убираем title/subtitle из общего Layout для этого экрана
const screenForLayout = {
...screenWithoutHeader,
title: undefined,
subtitle: undefined,
} as typeof screenWithoutHeader;
const layoutProps = createTemplateLayoutProps(
screenForLayout,
{ canGoBack, onBack },
screenProgress,
{
preset: "center",
actionButton:
screen.bottomActionButton?.show === false
? undefined
: {
defaultText: defaultTexts?.nextButton || "Continue",
disabled: false,
onClick: scrollToPayment,
},
}
);
if (isLoading || !placement) {
return (
<div className="w-full min-h-dvh max-w-[560px] mx-auto flex items-center justify-center">
<Spinner className="size-8" />
</div>
);
}
return (
<TemplateLayout
{...layoutProps}
contentProps={{ className: "p-0 pt-0" }}
childrenWrapperProps={{ className: "mt-0" }}
>
<div
className={cn(
"w-full min-h-dvh max-w-[560px] mx-auto bg-trial-payment-secondary",
"px-[17px] py-1.5 pb-[82px]",
"flex flex-col items-center"
)}
>
{/* Header block */}
{screen.headerBlock && (
<Header
className="mt-3"
text={buildTypographyProps(screen.headerBlock.text, {
as: "p",
defaults: { font: "inter", weight: "semiBold", size: "sm" },
})}
timer={buildTypographyProps(screen.headerBlock.timer, {
as: "span",
defaults: { font: "inter", weight: "bold", size: "2xl" },
})}
timerHookProps={{
initialSeconds: screen.headerBlock.timerSeconds ?? 600,
}}
button={{
children: defaultTexts?.continueButton || "Continue",
onClick: scrollToPayment,
}}
/>
)}
{/* UnlockYourSketch section */}
{screen.unlockYourSketch && (
<UnlockYourSketch
title={buildTypographyProps(screen.unlockYourSketch.title, {
as: "h3",
defaults: { font: "inter", weight: "bold", color: "default" },
})}
subtitle={buildTypographyProps(screen.unlockYourSketch.subtitle, {
as: "p",
defaults: {
font: "inter",
weight: "semiBold",
size: "xl",
color: "default",
align: "center",
},
})}
image={
screen.unlockYourSketch.image
? { src: screen.unlockYourSketch.image.src, alt: "portrait" }
: undefined
}
blur={{
text: {
...(buildTypographyProps(screen.unlockYourSketch.blur?.text, {
as: "p",
defaults: {
font: "inter",
weight: "semiBold",
align: "center",
},
}) ?? { as: "p", children: "" }),
className: "text-[#A16207]",
},
icon:
screen.unlockYourSketch.blur?.icon === "lock" ? (
<svg
width="18"
height="21"
viewBox="0 0 18 21"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.625 6.125V8H11.875V6.125C11.875 4.39844 10.4766 3 8.75 3C7.02344 3 5.625 4.39844 5.625 6.125ZM3.125 8V6.125C3.125 3.01953 5.64453 0.5 8.75 0.5C11.8555 0.5 14.375 3.01953 14.375 6.125V8H15C16.3789 8 17.5 9.12109 17.5 10.5V18C17.5 19.3789 16.3789 20.5 15 20.5H2.5C1.12109 20.5 0 19.3789 0 18V10.5C0 9.12109 1.12109 8 2.5 8H3.125Z"
fill="#A16207"
/>
</svg>
) : undefined,
}}
button={
screen.unlockYourSketch.buttonText
? {
children: screen.unlockYourSketch.buttonText,
onClick: scrollToPayment,
}
: undefined
}
/>
)}
{screen.joinedToday && (
<JoinedToday
className="mt-[18px]"
count={buildTypographyProps(screen.joinedToday.count, {
as: "span",
defaults: {
font: "inter",
weight: "bold",
size: "sm",
align: "center",
color: "muted",
},
})}
text={buildTypographyProps(screen.joinedToday.text, {
as: "p",
defaults: {
font: "inter",
size: "sm",
align: "center",
color: "muted",
},
})}
icon={
<svg
width="15"
height="13"
viewBox="0 0 15 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M2.23906 7.96384L7.18008 12.5767C7.38516 12.7681 7.65586 12.8748 7.9375 12.8748C8.21914 12.8748 8.48984 12.7681 8.69492 12.5767L13.6359 7.96384C14.4672 7.19001 14.9375 6.10447 14.9375 4.9697V4.81111C14.9375 2.89978 13.5566 1.27009 11.6727 0.95564C10.4258 0.747827 9.15703 1.15525 8.26562 2.04666L7.9375 2.37478L7.60938 2.04666C6.71797 1.15525 5.44922 0.747827 4.20234 0.95564C2.31836 1.27009 0.9375 2.89978 0.9375 4.81111V4.9697C0.9375 6.10447 1.40781 7.19001 2.23906 7.96384Z"
fill="#1047A2"
/>
</svg>
}
/>
)}
{screen.trustedByOver && (
<TrustedByOver
className="mt-[9px]"
text={buildTypographyProps(screen.trustedByOver.text, {
as: "p",
defaults: {
font: "inter",
size: "sm",
align: "center",
color: "muted",
},
})}
icon={
<svg
width="19"
height="15"
viewBox="0 0 19 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.6875 0.75C5.26766 0.75 5.82406 0.980468 6.2343 1.3907C6.64453 1.80094 6.875 2.35734 6.875 2.9375C6.875 3.51766 6.64453 4.07406 6.2343 4.4843C5.82406 4.89453 5.26766 5.125 4.6875 5.125C4.10734 5.125 3.55094 4.89453 3.1407 4.4843C2.73047 4.07406 2.5 3.51766 2.5 2.9375C2.5 2.35734 2.73047 1.80094 3.1407 1.3907C3.55094 0.980468 4.10734 0.75 4.6875 0.75ZM14.75 0.75C15.3302 0.75 15.8866 0.980468 16.2968 1.3907C16.707 1.80094 16.9375 2.35734 16.9375 2.9375C16.9375 3.51766 16.707 4.07406 16.2968 4.4843C15.8866 4.89453 15.3302 5.125 14.75 5.125C14.1698 5.125 13.6134 4.89453 13.2032 4.4843C12.793 4.07406 12.5625 3.51766 12.5625 2.9375C12.5625 2.35734 12.793 1.80094 13.2032 1.3907C13.6134 0.980468 14.1698 0.75 14.75 0.75ZM0.75 8.91758C0.75 7.30703 2.05703 6 3.66758 6H4.83516C5.26992 6 5.68281 6.0957 6.05469 6.26523C6.01914 6.46211 6.00273 6.66719 6.00273 6.875C6.00273 7.91953 6.46211 8.85742 7.18672 9.5C7.18125 9.5 7.17578 9.5 7.16758 9.5H1.33242C1.0125 9.5 0.75 9.2375 0.75 8.91758ZM11.8324 9.5C11.827 9.5 11.8215 9.5 11.8133 9.5C12.5406 8.85742 12.9973 7.91953 12.9973 6.875C12.9973 6.66719 12.9781 6.46484 12.9453 6.26523C13.3172 6.09297 13.7301 6 14.1648 6H15.3324C16.943 6 18.25 7.30703 18.25 8.91758C18.25 9.24023 17.9875 9.5 17.6676 9.5H11.8324ZM6.875 6.875C6.875 6.17881 7.15156 5.51113 7.64384 5.01884C8.13613 4.52656 8.80381 4.25 9.5 4.25C10.1962 4.25 10.8639 4.52656 11.3562 5.01884C11.8484 5.51113 12.125 6.17881 12.125 6.875C12.125 7.57119 11.8484 8.23887 11.3562 8.73116C10.8639 9.22344 10.1962 9.5 9.5 9.5C8.80381 9.5 8.13613 9.22344 7.64384 8.73116C7.15156 8.23887 6.875 7.57119 6.875 6.875ZM4.25 14.0199C4.25 12.0074 5.88242 10.375 7.89492 10.375H11.1051C13.1176 10.375 14.75 12.0074 14.75 14.0199C14.75 14.4219 14.4246 14.75 14.0199 14.75H4.98008C4.57812 14.75 4.25 14.4246 4.25 14.0199Z"
fill="#1047A2"
/>
</svg>
}
/>
)}
{screen.findingOneGuide && (
<FindingOneGuide
className="mt-[22px]"
header={{
emoji: buildTypographyProps(
screen.findingOneGuide.header?.emoji,
{
as: "span",
defaults: { size: "2xl" },
}
),
title: buildTypographyProps(
screen.findingOneGuide.header?.title,
{
as: "h3",
defaults: { font: "inter", weight: "bold" },
}
),
}}
text={buildTypographyProps(screen.findingOneGuide.text, {
as: "p",
defaults: { font: "inter", size: "sm", color: "muted" },
})}
blur={{
text: {
...(buildTypographyProps(screen.findingOneGuide.blur?.text, {
as: "p",
defaults: {
font: "inter",
weight: "medium",
align: "center",
},
}) ?? { as: "p", children: "" }),
className: "text-[#A16207]",
},
icon:
screen.findingOneGuide.blur?.icon === "lock" ? (
<svg
width="18"
height="21"
viewBox="0 0 18 21"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.625 6.125V8H11.875V6.125C11.875 4.39844 10.4766 3 8.75 3C7.02344 3 5.625 4.39844 5.625 6.125ZM3.125 8V6.125C3.125 3.01953 5.64453 0.5 8.75 0.5C11.8555 0.5 14.375 3.01953 14.375 6.125V8H15C16.3789 8 17.5 9.12109 17.5 10.5V18C17.5 19.3789 16.3789 20.5 15 20.5H2.5C1.12109 20.5 0 19.3789 0 18V10.5C0 9.12109 1.12109 8 2.5 8H3.125Z"
fill="#A16207"
/>
</svg>
) : undefined,
}}
/>
)}
{screen.tryForDays && (
<TryForDays
className="mt-[46px]"
title={buildTypographyProps(
{
...screen.tryForDays.title,
text: replacePlaceholders(screen.tryForDays.title?.text),
},
{
as: "h3",
defaults: { font: "inter", weight: "bold", size: "xl" },
}
)}
textListProps={
screen.tryForDays.textList
? {
listStyleType: "none",
items: screen.tryForDays.textList.items.map(
(it) =>
buildTypographyProps(
{ ...it, text: replacePlaceholders(it.text) },
{ as: "li", defaults: { font: "inter", size: "sm" } }
)!
),
}
: undefined
}
/>
)}
{screen.totalPrice && (
<TotalPrice
className="mt-[46px]"
couponContainer={{
title: buildTypographyProps(
screen.totalPrice.couponContainer.title,
{
as: "h4",
defaults: { font: "inter", weight: "semiBold" },
}
),
button: screen.totalPrice.couponContainer.buttonText
? {
children: screen.totalPrice.couponContainer.buttonText,
onClick: scrollToPayment,
}
: undefined,
}}
priceContainer={
screen.totalPrice.priceContainer
? {
title: buildTypographyProps(
screen.totalPrice.priceContainer.title,
{
as: "h4",
defaults: { font: "inter", weight: "bold", size: "xl" },
}
),
price: buildTypographyProps(
{
...screen.totalPrice.priceContainer.price,
text: replacePlaceholders(
screen.totalPrice.priceContainer.price?.text
),
},
{
as: "span",
defaults: { font: "inter", weight: "black" },
}
),
oldPrice: buildTypographyProps(
{
...screen.totalPrice.priceContainer.oldPrice,
text: replacePlaceholders(
screen.totalPrice.priceContainer.oldPrice?.text
),
},
{
as: "span",
defaults: { font: "inter" },
}
),
discount: buildTypographyProps(
{
...screen.totalPrice.priceContainer.discount,
text: replacePlaceholders(
screen.totalPrice.priceContainer.discount?.text
),
},
{
as: "span",
defaults: { font: "inter", weight: "bold", size: "sm" },
}
),
}
: undefined
}
/>
)}
{screen.paymentButtons && (
<div ref={paymentSectionRef} className="w-full">
<PaymentButtons
className="mt-[46px]"
buttons={screen.paymentButtons.buttons.map((b) => {
const icon =
b.icon === "pay" ? (
<svg
width="19"
height="24"
viewBox="0 0 19 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_49_113)">
<path
d="M15.8141 12.5953C15.8047 10.875 16.5828 9.57656 18.1578 8.62031C17.2766 7.35938 15.9453 6.66563 14.1875 6.52969C12.5234 6.39844 10.7047 7.5 10.0391 7.5C9.33594 7.5 7.72344 6.57656 6.45781 6.57656C3.84219 6.61875 1.0625 8.6625 1.0625 12.8203C1.0625 14.0484 1.2875 15.3172 1.7375 16.6266C2.3375 18.3469 4.50312 22.5656 6.7625 22.4953C7.94375 22.4672 8.77812 21.6563 10.3156 21.6563C11.8062 21.6563 12.5797 22.4953 13.8969 22.4953C16.175 22.4625 18.1344 18.6281 18.7062 16.9031C15.65 15.4641 15.8141 12.6844 15.8141 12.5953ZM13.1609 4.89844C14.4406 3.37969 14.3234 1.99688 14.2859 1.5C13.1562 1.56563 11.8484 2.26875 11.1031 3.13594C10.2828 4.06406 9.8 5.2125 9.90313 6.50625C11.1266 6.6 12.2422 5.97188 13.1609 4.89844Z"
fill="white"
/>
</g>
<defs>
<clipPath id="clip0_49_113">
<path d="M0.875 0H18.875V24H0.875V0Z" fill="white" />
</clipPath>
</defs>
</svg>
) : b.icon === "google" ? (
<svg
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
width="24"
height="24"
viewBox="0 0 48 48"
>
<path
fill="#FFC107"
d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24c0,11.045,8.955,20,20,20c11.045,0,20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"
></path>
<path
fill="#FF3D00"
d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"
></path>
<path
fill="#4CAF50"
d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"
></path>
<path
fill="#1976D2"
d="M43.611,20.083H42V20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"
></path>
</svg>
) : b.icon === "card" ? (
<svg
width="19"
height="16"
viewBox="0 0 19 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_49_125)">
<path
d="M16.0312 2.5C16.3062 2.5 16.5312 2.725 16.5312 3V4H1.53125V3C1.53125 2.725 1.75625 2.5 2.03125 2.5H16.0312ZM16.5312 7V13C16.5312 13.275 16.3062 13.5 16.0312 13.5H2.03125C1.75625 13.5 1.53125 13.275 1.53125 13V7H16.5312ZM2.03125 1C0.928125 1 0.03125 1.89688 0.03125 3V13C0.03125 14.1031 0.928125 15 2.03125 15H16.0312C17.1344 15 18.0312 14.1031 18.0312 13V3C18.0312 1.89688 17.1344 1 16.0312 1H2.03125ZM3.78125 10.5C3.36562 10.5 3.03125 10.8344 3.03125 11.25C3.03125 11.6656 3.36562 12 3.78125 12H5.28125C5.69688 12 6.03125 11.6656 6.03125 11.25C6.03125 10.8344 5.69688 10.5 5.28125 10.5H3.78125ZM7.78125 10.5C7.36562 10.5 7.03125 10.8344 7.03125 11.25C7.03125 11.6656 7.36562 12 7.78125 12H11.2812C11.6969 12 12.0312 11.6656 12.0312 11.25C12.0312 10.8344 11.6969 10.5 11.2812 10.5H7.78125Z"
fill="white"
/>
</g>
<defs>
<clipPath id="clip0_49_125">
<path
d="M0.03125 0H18.0312V16H0.03125V0Z"
fill="white"
/>
</clipPath>
</defs>
</svg>
) : undefined;
const className = b.primary ? "bg-primary" : undefined;
return {
children: b.text,
icon,
className,
onClick: handlePayClick,
};
})}
/>
</div>
)}
{screen.moneyBackGuarantee && (
<MoneyBackGuarantee
className="mt-[17px]"
title={buildTypographyProps(screen.moneyBackGuarantee.title, {
as: "h4",
defaults: { font: "inter", weight: "bold", size: "sm" },
})}
text={buildTypographyProps(screen.moneyBackGuarantee.text, {
as: "p",
defaults: { font: "inter", weight: "medium", size: "xs" },
})}
/>
)}
{screen.policy && (
<Policy
className="mt-4"
text={buildTypographyProps(
screen.policy.text
? {
...screen.policy.text,
text: replacePlaceholders(screen.policy.text.text),
}
: undefined,
{ as: "p", defaults: { font: "inter", size: "xs" } }
)}
/>
)}
{screen.usersPortraits && (
<UsersPortraits
className="mt-12"
title={buildTypographyProps(screen.usersPortraits.title, {
as: "h3",
defaults: {
font: "inter",
weight: "bold",
size: "2xl",
align: "center",
},
})}
imgs={screen.usersPortraits.images?.map((img) => ({
src: img.src,
alt: "user portrait",
}))}
button={
screen.usersPortraits.buttonText
? {
children: screen.usersPortraits.buttonText,
onClick: scrollToPayment,
}
: undefined
}
/>
)}
{screen.joinedTodayWithAvatars && (
<JoinedTodayWithAvatars
className="mt-[22px]"
avatars={
screen.joinedTodayWithAvatars.avatars
? {
avatars: screen.joinedTodayWithAvatars.avatars.images.map(
(img) => ({
imageProps: { src: img.src, alt: "avatar" },
})
),
}
: undefined
}
count={buildTypographyProps(screen.joinedTodayWithAvatars.count, {
as: "span",
defaults: { font: "inter", weight: "bold", size: "sm" },
})}
text={buildTypographyProps(screen.joinedTodayWithAvatars.text, {
as: "p",
defaults: { font: "inter", weight: "semiBold", size: "sm" },
})}
/>
)}
{screen.progressToSeeSoulmate && (
<ProgressToSeeSoulmate
className="mt-12"
title={
buildTypographyProps(screen.progressToSeeSoulmate.title, {
as: "h3",
defaults: { font: "inter", weight: "bold", align: "center" },
}) ?? { as: "h3", children: "" }
}
progress={{
value: screen.progressToSeeSoulmate.progress?.value ?? 0,
}}
progressText={{
leftText: buildTypographyProps(
screen.progressToSeeSoulmate.leftText,
{
as: "span",
defaults: { font: "inter", size: "sm", weight: "medium" },
}
),
rightText: buildTypographyProps(
screen.progressToSeeSoulmate.rightText,
{
as: "span",
defaults: { font: "inter", size: "sm" },
}
),
}}
/>
)}
{screen.stepsToSeeSoulmate && (
<StepsToSeeSoulmate
className="mt-12"
steps={screen.stepsToSeeSoulmate.steps.map((s) => ({
title: buildTypographyProps(s.title, {
as: "h4",
defaults: { font: "inter", weight: "semiBold", size: "sm" },
})!,
description: buildTypographyProps(s.description, {
as: "p",
defaults: { font: "inter", size: "xs" },
})!,
icon:
s.icon === "questions" ? (
<svg
width="12"
height="15"
viewBox="0 0 12 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6 0.75C4.85703 0.75 3.88359 1.48008 3.52539 2.5H2.5C1.53477 2.5 0.75 3.28477 0.75 4.25V13C0.75 13.9652 1.53477 14.75 2.5 14.75H9.5C10.4652 14.75 11.25 13.9652 11.25 13V4.25C11.25 3.28477 10.4652 2.5 9.5 2.5H8.47461C8.11641 1.48008 7.14297 0.75 6 0.75ZM6 2.5C6.23206 2.5 6.45462 2.59219 6.61872 2.75628C6.78281 2.92038 6.875 3.14294 6.875 3.375C6.875 3.60706 6.78281 3.82962 6.61872 3.99372C6.45462 4.15781 6.23206 4.25 6 4.25C5.76794 4.25 5.54538 4.15781 5.38128 3.99372C5.21719 3.82962 5.125 3.60706 5.125 3.375C5.125 3.14294 5.21719 2.92038 5.38128 2.75628C5.54538 2.59219 5.76794 2.5 6 2.5ZM3.64297 7.01992C3.85898 6.41016 4.43867 6 5.08672 6H6.68086C7.63516 6 8.40625 6.77383 8.40625 7.72539C8.40625 8.34336 8.07539 8.91484 7.53945 9.22383L6.65625 9.72969C6.65078 10.0852 6.3582 10.375 6 10.375C5.63633 10.375 5.34375 10.0824 5.34375 9.71875V9.34961C5.34375 9.11445 5.46953 8.89844 5.67461 8.78086L6.88594 8.08633C7.01445 8.0125 7.09375 7.87578 7.09375 7.72813C7.09375 7.49844 6.90781 7.31523 6.68086 7.31523H5.08672C4.99375 7.31523 4.91172 7.37266 4.88164 7.46016L4.8707 7.49297C4.75039 7.83477 4.37305 8.0125 4.03398 7.89219C3.69492 7.77188 3.51445 7.39453 3.63477 7.05547L3.6457 7.02266L3.64297 7.01992ZM5.125 12.125C5.125 11.8929 5.21719 11.6704 5.38128 11.5063C5.54538 11.3422 5.76794 11.25 6 11.25C6.23206 11.25 6.45462 11.3422 6.61872 11.5063C6.78281 11.6704 6.875 11.8929 6.875 12.125C6.875 12.3571 6.78281 12.5796 6.61872 12.7437C6.45462 12.9078 6.23206 13 6 13C5.76794 13 5.54538 12.9078 5.38128 12.7437C5.21719 12.5796 5.125 12.3571 5.125 12.125Z"
fill="white"
/>
</svg>
) : s.icon === "profile" ? (
<svg
width="14"
height="15"
viewBox="0 0 14 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.03125 0.75C5.87617 0.75 6.5625 1.43633 6.5625 2.28125V13.2188C6.5625 14.0637 5.87617 14.75 5.03125 14.75C4.24102 14.75 3.59023 14.1512 3.5082 13.3801C3.36602 13.4184 3.21563 13.4375 3.0625 13.4375C2.09727 13.4375 1.3125 12.6527 1.3125 11.6875C1.3125 11.4852 1.34805 11.2883 1.41094 11.1078C0.585156 10.7961 0 9.99766 0 9.0625C0 8.19023 0.511328 7.43555 1.25234 7.08555C1.01445 6.7875 0.875 6.41016 0.875 6C0.875 5.16055 1.46563 4.46055 2.25312 4.28828C2.20938 4.13789 2.1875 3.97656 2.1875 3.8125C2.1875 2.99492 2.75078 2.30586 3.5082 2.11445C3.59023 1.34883 4.24102 0.75 5.03125 0.75ZM8.96875 0.75C9.75898 0.75 10.407 1.34883 10.4918 2.11445C11.252 2.30586 11.8125 2.99219 11.8125 3.8125C11.8125 3.97656 11.7906 4.13789 11.7469 4.28828C12.5344 4.45781 13.125 5.16055 13.125 6C13.125 6.41016 12.9855 6.7875 12.7477 7.08555C13.4887 7.43555 14 8.19023 14 9.0625C14 9.99766 13.4148 10.7961 12.5891 11.1078C12.652 11.2883 12.6875 11.4852 12.6875 11.6875C12.6875 12.6527 11.9027 13.4375 10.9375 13.4375C10.7844 13.4375 10.634 13.4184 10.4918 13.3801C10.4098 14.1512 9.75898 14.75 8.96875 14.75C8.12383 14.75 7.4375 14.0637 7.4375 13.2188V2.28125C7.4375 1.43633 8.12383 0.75 8.96875 0.75Z"
fill="white"
/>
</svg>
) : s.icon === "sketch" ? (
<svg
width="14"
height="15"
viewBox="0 0 14 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M14 7.75C14 7.77461 14 7.79922 14 7.82383C13.9891 8.82188 13.0813 9.5 12.0832 9.5H9.40625C8.68164 9.5 8.09375 10.0879 8.09375 10.8125C8.09375 10.9055 8.10469 10.9957 8.12109 11.0832C8.17852 11.3621 8.29883 11.6301 8.41641 11.9008C8.5832 12.2781 8.74727 12.6527 8.74727 13.0492C8.74727 13.9187 8.15664 14.709 7.28711 14.7445C7.19141 14.7473 7.0957 14.75 6.99727 14.75C3.13359 14.75 0 11.6164 0 7.75C0 3.88359 3.13359 0.75 7 0.75C10.8664 0.75 14 3.88359 14 7.75ZM3.5 8.625C3.5 8.39294 3.40781 8.17038 3.24372 8.00628C3.07962 7.84219 2.85706 7.75 2.625 7.75C2.39294 7.75 2.17038 7.84219 2.00628 8.00628C1.84219 8.17038 1.75 8.39294 1.75 8.625C1.75 8.85706 1.84219 9.07962 2.00628 9.24372C2.17038 9.40781 2.39294 9.5 2.625 9.5C2.85706 9.5 3.07962 9.40781 3.24372 9.24372C3.40781 9.07962 3.5 8.85706 3.5 8.625ZM3.5 6C3.73206 6 3.95462 5.90781 4.11872 5.74372C4.28281 5.57962 4.375 5.35706 4.375 5.125C4.375 4.89294 4.28281 4.67038 4.11872 4.50628C3.95462 4.34219 3.73206 4.25 3.5 4.25C3.26794 4.25 3.04538 4.34219 2.88128 4.50628C2.71719 4.67038 2.625 4.89294 2.625 5.125C2.625 5.35706 2.71719 5.57962 2.88128 5.74372C3.04538 5.90781 3.26794 6 3.5 6ZM7.875 3.375C7.875 3.14294 7.78281 2.92038 7.61872 2.75628C7.45462 2.59219 7.23206 2.5 7 2.5C6.76794 2.5 6.54538 2.59219 6.38128 2.75628C6.21719 2.92038 6.125 3.14294 6.125 3.375C6.125 3.60706 6.21719 3.82962 6.38128 3.99372C6.54538 4.15781 6.76794 4.25 7 4.25C7.23206 4.25 7.45462 4.15781 7.61872 3.99372C7.78281 3.82962 7.875 3.60706 7.875 3.375ZM10.5 6C10.7321 6 10.9546 5.90781 11.1187 5.74372C11.2828 5.57962 11.375 5.35706 11.375 5.125C11.375 4.89294 11.2828 4.67038 11.1187 4.50628C10.9546 4.34219 10.7321 6 10.5 6Z"
fill="#6B7280"
/>
</svg>
) : s.icon === "astro" ? (
<svg
width="12"
height="13"
viewBox="0 0 12 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.86133 0.625C3.48438 0.625 0.75 3.36758 0.75 6.75C0.75 10.1324 3.48438 12.875 6.86133 12.875C8.51836 12.875 10.0195 12.2133 11.1215 11.1414C11.2582 11.0074 11.2937 10.7996 11.2063 10.6301C11.1187 10.4605 10.9301 10.3648 10.7414 10.3977C10.4734 10.4441 10.2 10.4688 9.91836 10.4688C7.26875 10.4688 5.11953 8.31406 5.11953 5.65625C5.11953 3.85703 6.10391 2.29023 7.56133 1.46445C7.72813 1.36875 7.81289 1.17734 7.77187 0.991406C7.73086 0.805469 7.57227 0.666016 7.38086 0.649609C7.20859 0.635938 7.03633 0.627734 6.86133 0.627734V0.625Z"
fill="#6B7280"
/>
</svg>
) : s.icon === "chat" ? (
<svg
width="18"
height="15"
viewBox="0 0 18 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M5.93755 10.375C9.07935 10.375 11.6251 8.22031 11.6251 5.5625C11.6251 2.90469 9.07935 0.75 5.93755 0.75C2.79576 0.75 0.250055 2.90469 0.250055 5.5625C0.250055 6.61797 0.652008 7.59414 1.33287 8.38984C1.23716 8.64687 1.09498 8.87383 0.944586 9.06523C0.813336 9.23477 0.679352 9.36602 0.580914 9.45625C0.531696 9.5 0.49068 9.53555 0.463336 9.55742C0.449664 9.56836 0.438727 9.57656 0.433258 9.5793L0.427789 9.58477C0.277399 9.69688 0.211774 9.89375 0.27193 10.0715C0.332086 10.2492 0.498883 10.375 0.687555 10.375C1.28365 10.375 1.88521 10.2219 2.3856 10.0332C2.63716 9.9375 2.87232 9.83086 3.0774 9.72148C3.91685 10.1371 4.89302 10.375 5.93755 10.375ZM12.5001 5.5625C12.5001 8.6332 9.79029 10.9465 6.58013 11.2227C7.24459 13.257 9.44849 14.75 12.0626 14.75C13.1071 14.75 14.0833 14.5121 14.9254 14.0965C15.1305 14.2059 15.3629 14.3125 15.6145 14.4082C16.1149 14.5969 16.7165 14.75 17.3126 14.75C17.5012 14.75 17.6708 14.627 17.7282 14.4465C17.7856 14.266 17.7227 14.0691 17.5696 13.957L17.5641 13.9516C17.5586 13.9461 17.5477 13.9406 17.534 13.9297C17.5067 13.9078 17.4657 13.875 17.4165 13.8285C17.318 13.7383 17.184 13.607 17.0528 13.4375C16.9024 13.2461 16.7602 13.0164 16.6645 12.7621C17.3454 11.9691 17.7473 10.993 17.7473 9.93477C17.7473 7.39727 15.4258 5.31641 12.4809 5.13594C12.4919 5.27539 12.4973 5.41758 12.4973 5.55977L12.5001 5.5625Z"
fill="#6B7280"
/>
</svg>
) : null,
isActive: s.isActive ?? false,
}))}
button={
screen.stepsToSeeSoulmate.buttonText
? {
children: screen.stepsToSeeSoulmate.buttonText,
onClick: scrollToPayment,
}
: undefined
}
/>
)}
{screen.reviews && (
<Reviews
className="mt-12"
title={buildTypographyProps(screen.reviews.title, {
as: "h3",
defaults: { font: "inter", weight: "bold", align: "center" },
})}
reviews={screen.reviews.items.map((r) => ({
name: buildTypographyProps(r.name, {
as: "span",
defaults: { font: "inter", weight: "semiBold", size: "sm" },
}),
text: buildTypographyProps(r.text, {
as: "p",
defaults: { font: "inter", size: "sm" },
}),
date: buildTypographyProps(r.date, {
as: "span",
defaults: { font: "inter", size: "xs" },
}),
avatar: r.avatar
? {
imageProps: { src: r.avatar.src, alt: "avatar" },
}
: undefined,
stars: r.rating ? { value: r.rating } : undefined,
portrait: r.portrait
? { src: r.portrait.src, alt: "Portrait" }
: undefined,
photo: r.photo ? { src: r.photo.src, alt: "Photo" } : undefined,
}))}
/>
)}
{screen.commonQuestions && (
<CommonQuestions
className="mt-[31px]"
title={buildTypographyProps(screen.commonQuestions.title, {
as: "h3",
defaults: {
font: "inter",
weight: "bold",
size: "2xl",
align: "center",
},
})}
questions={screen.commonQuestions.items.map((q, index) => ({
value: `q-${index}`,
trigger: { children: q.question },
content: { children: q.answer },
}))}
accordionProps={{ defaultValue: "q-0", type: "single" }}
/>
)}
{screen.stillHaveQuestions && (
<StillHaveQuestions
className="mt-8"
title={buildTypographyProps(screen.stillHaveQuestions.title, {
as: "h3",
defaults: { font: "inter", size: "sm" },
})}
actionButton={
screen.stillHaveQuestions.actionButtonText
? {
children: screen.stillHaveQuestions.actionButtonText,
onClick: scrollToPayment,
}
: undefined
}
contactButton={
screen.stillHaveQuestions.contactButtonText
? {
children: screen.stillHaveQuestions.contactButtonText,
onClick: scrollToPayment,
}
: undefined
}
/>
)}
{screen.footer && (
<Footer
className="mt-[60px]"
title={buildTypographyProps(screen.footer.title, {
as: "h3",
defaults: {
font: "inter",
weight: "bold",
size: "2xl",
align: "center",
},
})}
contacts={
screen.footer.contacts
? {
title: buildTypographyProps(screen.footer.contacts.title, {
as: "h3",
defaults: { font: "inter", weight: "bold" },
}),
email: screen.footer.contacts.email
? {
href: screen.footer.contacts.email.href,
children: screen.footer.contacts.email.text,
}
: undefined,
address: buildTypographyProps(
screen.footer.contacts.address,
{
as: "address",
defaults: { font: "inter", size: "sm" },
}
),
}
: undefined
}
legal={
screen.footer.legal
? {
title: buildTypographyProps(screen.footer.legal.title, {
as: "h3",
defaults: { font: "inter", weight: "bold" },
}),
links:
screen.footer.legal.links?.map((l) => ({
href: l.href,
children: l.text,
})) || [],
copyright: buildTypographyProps(
screen.footer.legal.copyright,
{ as: "p", defaults: { font: "inter", size: "xs" } }
),
}
: undefined
}
paymentMethods={
screen.footer.paymentMethods
? {
title: buildTypographyProps(
screen.footer.paymentMethods.title,
{ as: "h3", defaults: { font: "inter", weight: "bold" } }
),
methods:
screen.footer.paymentMethods.methods?.map((m) => ({
src: m.src,
alt: m.alt,
})) || [],
}
: undefined
}
/>
)}
</div>
</TemplateLayout>
);
}

View File

@ -0,0 +1,3 @@
export { TrialPaymentTemplate } from "./TrialPaymentTemplate";

View File

@ -7,6 +7,7 @@ export { EmailTemplate } from "./EmailTemplate";
export { CouponTemplate } from "./CouponTemplate";
export { LoadersTemplate } from "./LoadersTemplate";
export { SoulmatePortraitTemplate } from "./SoulmatePortraitTemplate";
export { TrialPaymentTemplate } from "./TrialPaymentTemplate/index";
// Layout Templates
export { TemplateLayout } from "./layouts/TemplateLayout";

View File

@ -16,35 +16,57 @@ interface TemplateLayoutProps {
canGoBack: boolean;
onBack: () => void;
screenProgress?: { current: number; total: number };
// Настройки template
titleDefaults?: {
font?: "manrope" | "inter" | "geistSans" | "geistMono";
weight?: "regular" | "medium" | "semiBold" | "bold" | "extraBold" | "black";
align?: "left" | "center" | "right";
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl";
color?: "default" | "primary" | "secondary" | "destructive" | "success" | "card" | "accent" | "muted";
color?:
| "default"
| "primary"
| "secondary"
| "destructive"
| "success"
| "card"
| "accent"
| "muted";
className?: string;
};
subtitleDefaults?: {
font?: "manrope" | "inter" | "geistSans" | "geistMono";
weight?: "regular" | "medium" | "semiBold" | "bold" | "extraBold" | "black";
color?: "default" | "primary" | "secondary" | "destructive" | "success" | "card" | "accent" | "muted";
color?:
| "default"
| "primary"
| "secondary"
| "destructive"
| "success"
| "card"
| "accent"
| "muted";
align?: "left" | "center" | "right";
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl";
className?: string;
};
actionButtonOptions?: {
defaultText: string;
disabled: boolean;
onClick: () => void;
};
// Дополнительные props для BottomActionButton
childrenAboveButton?: React.ReactNode;
childrenUnderButton?: React.ReactNode;
// Дополнительные props для Title
childrenAboveTitle?: React.ReactNode;
// Переопределения стилей LayoutQuestion (контент и обертка контента)
contentProps?: React.ComponentProps<"div">;
childrenWrapperProps?: React.ComponentProps<"div">;
// Контент template
children: React.ReactNode;
}
@ -58,18 +80,30 @@ export function TemplateLayout({
canGoBack,
onBack,
screenProgress,
titleDefaults = { font: "manrope", weight: "bold", align: "left", size: "2xl", color: "default" },
subtitleDefaults = { font: "manrope", weight: "medium", color: "default", align: "left", size: "lg" },
titleDefaults = {
font: "manrope",
weight: "bold",
align: "left",
size: "2xl",
color: "default",
},
subtitleDefaults = {
font: "manrope",
weight: "medium",
color: "default",
align: "left",
size: "lg",
},
actionButtonOptions,
childrenAboveButton,
childrenUnderButton,
childrenAboveTitle,
contentProps,
childrenWrapperProps,
children,
}: TemplateLayoutProps) {
// 🎛️ ЦЕНТРАЛИЗОВАННАЯ ЛОГИКА BOTTOM BUTTON
const {
elementRef: bottomActionButtonRef,
} = useDynamicSize<HTMLDivElement>({
const { elementRef: bottomActionButtonRef } = useDynamicSize<HTMLDivElement>({
defaultHeight: 132,
});
@ -92,20 +126,20 @@ export function TemplateLayout({
: undefined;
// 🎯 Автоматически создаем PrivacyTermsConsent с фиксированными настройками
const shouldShowPrivacyTermsConsent =
'bottomActionButton' in screen &&
const shouldShowPrivacyTermsConsent =
"bottomActionButton" in screen &&
screen.bottomActionButton?.showPrivacyTermsConsent === true;
const autoPrivacyTermsConsent = shouldShowPrivacyTermsConsent ? (
<PrivacyTermsConsent
<PrivacyTermsConsent
className="mt-5"
privacyPolicy={{
href: "/privacy",
children: "Privacy Policy"
children: "Privacy Policy",
}}
termsOfUse={{
href: "/terms",
children: "Terms of use"
children: "Terms of use",
}}
/>
) : null;
@ -121,13 +155,18 @@ export function TemplateLayout({
// 🎨 ЦЕНТРАЛИЗОВАННЫЙ РЕНДЕРИНГ
return (
<div className="w-full">
<LayoutQuestion {...layoutQuestionProps} childrenAboveTitle={childrenAboveTitle}>
<LayoutQuestion
{...layoutQuestionProps}
childrenAboveTitle={childrenAboveTitle}
contentProps={contentProps}
childrenWrapperProps={childrenWrapperProps}
>
{children}
</LayoutQuestion>
{bottomActionButtonProps && (
<BottomActionButton
{...bottomActionButtonProps}
<BottomActionButton
{...bottomActionButtonProps}
ref={bottomActionButtonRef}
childrenAboveButton={childrenAboveButton}
childrenUnderButton={finalChildrenUnderButton}

View File

@ -2,7 +2,9 @@
import { cn } from "@/lib/utils";
import { Header } from "@/components/layout/Header/Header";
import Typography, { TypographyProps } from "@/components/ui/Typography/Typography";
import Typography, {
TypographyProps,
} from "@/components/ui/Typography/Typography";
export interface LayoutQuestionProps
extends Omit<React.ComponentProps<"section">, "title" | "content"> {
@ -55,7 +57,7 @@ function LayoutQuestion({
weight="bold"
{...title}
align={title.align ?? "left"}
className={cn(title.className, "w-full text-[25px] leading-[38px]")}
className={cn("w-full text-[25px] leading-[38px]", title.className)}
/>
)}
@ -66,8 +68,8 @@ function LayoutQuestion({
{...subtitle}
align={subtitle.align ?? "left"}
className={cn(
subtitle.className,
"w-full mt-2.5 text-[17px] leading-[26px]"
"w-full mt-2.5 text-[17px] leading-[26px]",
subtitle.className
)}
/>
)}
@ -83,4 +85,4 @@ function LayoutQuestion({
);
}
export { LayoutQuestion };
export { LayoutQuestion };

View File

@ -0,0 +1,82 @@
.list {
position: relative;
width: 100%;
margin-top: 16px;
display: flex;
flex-direction: column;
align-items: center;
/* gap: 32px; */
font-size: 20px;
/* color: #1A6697; */
color: #acacac;
line-height: 25px;
text-align: center;
overflow: hidden;
}
.list > .item {
transition: margin-top 0.5s ease-in-out;
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
display: block;
background: var(--background);
/* padding: 16px 0; */
overflow: hidden;
opacity: 0;
animation: list-item ease-in-out forwards;
}
.list > .item > .line {
display: block;
height: 100%;
width: 64px;
background: linear-gradient(
to right,
#acacac 0%,
#333333 50%,
#acacac 100%
);
top: 0;
left: 50%;
position: absolute;
mix-blend-mode: color-burn;
filter: blur(3px);
animation: line-move cubic-bezier(0.65, 0, 0.46, 1.02) infinite;
}
.list > .item > .text {
position: relative;
color: #000;
z-index: 1;
}
@keyframes line-move {
0% {
left: -64px;
}
100% {
left: 100%;
}
}
@keyframes list-item {
0% {
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
opacity: 0;
}
}

View File

@ -0,0 +1,61 @@
"use client";
import { useEffect, useRef, useState } from "react";
import styles from "./GPTAnimationText.module.css";
interface GPTAnimationTextProps {
points: Array<string>;
totalAnimationTime: number;
}
function GPTAnimationText({
points,
totalAnimationTime,
}: GPTAnimationTextProps) {
const listRef = useRef<Array<HTMLParagraphElement | null>>([]);
const [listHeight, setListHeight] = useState(0);
useEffect(() => {
let maxHeight = 0;
listRef.current.forEach(item => {
if (item?.offsetHeight && item.offsetHeight > maxHeight) {
maxHeight = item.offsetHeight;
}
});
setListHeight(maxHeight);
}, [listRef]);
return (
<div
className={styles.list}
style={{
height: `${listHeight}px`,
}}
>
{points.map((element, index) => (
<p
key={element}
className={styles.item}
ref={el => {
listRef.current[index] = el;
}}
style={{
animationDuration: `${totalAnimationTime / points.length}ms`,
animationDelay: `${index * (totalAnimationTime / points.length)}ms`,
}}
>
{element}
<span
className={styles.line}
style={{
animationDuration: `${totalAnimationTime / points.length}ms`,
}}
/>
</p>
))}
</div>
);
}
export default GPTAnimationText;

View File

@ -0,0 +1,53 @@
import Link from "next/link";
import GPTAnimationText from "@/components/ui/GPTAnimationText/GPTAnimationText";
import Typography from "@/components/ui/Typography/Typography";
import { ActionButton } from "@/components/ui/ActionButton/ActionButton";
interface AnimatedInfoScreenProps {
lottieAnimation: React.ReactNode;
title: string;
animationTime?: number;
animationTexts?: string[];
buttonText?: string;
nextRoute?: string;
}
export default async function AnimatedInfoScreen({
lottieAnimation,
title,
animationTime,
animationTexts,
buttonText,
nextRoute,
}: AnimatedInfoScreenProps) {
return (
<div className="w-full flex flex-col items-center px-7">
{lottieAnimation}
<Typography
as="h1"
weight="bold"
className="mt-8 mb-[50px] text-[27px] leading-[40px] text-center"
>
{title}
</Typography>
{!!animationTexts?.length && animationTime && (
<GPTAnimationText
points={animationTexts}
totalAnimationTime={animationTime}
/>
)}
{nextRoute && buttonText && (
<ActionButton
asChild
className="w-full mt-[126px] sticky bottom-[calc(0dvh+16px)] opacity-0 [animation:fadeIn_0.5s_ease-in-out_forwards] pointer-events-none"
style={
animationTime ? { animationDelay: `${animationTime}ms` } : undefined
}
>
<Link href={nextRoute}>{buttonText}</Link>
</ActionButton>
)}
</div>
);
}

View File

@ -0,0 +1,46 @@
"use client";
import {
DotLottieReact,
DotLottieReactProps,
} from "@lottiefiles/dotlottie-react";
import clsx from "clsx";
import { useLottie } from "@/hooks/lottie/useLottie";
import { ELottieKeys } from "@/shared/constants/lottie";
interface LottieAnimationProps {
loadKey: ELottieKeys;
width?: number | string;
height?: number | string;
className?: string;
animationProps?: DotLottieReactProps;
}
export default function LottieAnimation({
loadKey,
width = 80,
height = 80,
className,
animationProps,
}: LottieAnimationProps) {
const { animationData } = useLottie({
loadKey,
});
return (
<div style={{ width: width, height: height }} className={className}>
{animationData && (
<DotLottieReact
style={{ width: width, height: height }}
data={animationData}
autoplay
width={width}
height={height}
{...animationProps}
className={clsx(animationProps?.className, "ym-hide-content")}
/>
)}
</div>
);
}

View File

@ -0,0 +1,33 @@
"use server";
import { http } from "@/shared/api/httpClient";
import { API_ROUTES } from "@/shared/constants/api-routes";
import { ActionResponse } from "@/shared/types";
import {
SingleCheckoutRequest,
SingleCheckoutResponse,
SingleCheckoutResponseSchema,
} from "./types";
export async function performSingleCheckout(
payload: SingleCheckoutRequest
): Promise<ActionResponse<SingleCheckoutResponse>> {
try {
const response = await http.post<SingleCheckoutResponse>(
API_ROUTES.paymentSingleCheckout(),
payload,
{
schema: SingleCheckoutResponseSchema,
revalidate: 0,
}
);
return { data: response, error: null };
} catch (error) {
console.error("Failed to perform single checkout:", error);
const errorMessage =
error instanceof Error ? error.message : "Something went wrong.";
return { data: null, error: errorMessage };
}
}

View File

@ -0,0 +1,31 @@
import { http } from "@/shared/api/httpClient";
import { API_ROUTES } from "@/shared/constants/api-routes";
import {
CheckoutRequest,
CheckoutResponse,
CheckoutResponseSchema,
SingleCheckoutRequest,
SingleCheckoutResponse,
SingleCheckoutResponseSchema,
} from "./types";
export async function createPaymentCheckout(payload: CheckoutRequest) {
return http.post<CheckoutResponse>(API_ROUTES.paymentCheckout(), payload, {
schema: CheckoutResponseSchema,
revalidate: 0,
});
}
export async function createSinglePaymentCheckout(
payload: SingleCheckoutRequest
) {
return http.post<SingleCheckoutResponse>(
API_ROUTES.paymentSingleCheckout(),
payload,
{
schema: SingleCheckoutResponseSchema,
revalidate: 0,
}
);
}

View File

@ -0,0 +1,52 @@
import { z } from "zod";
export const CheckoutRequestSchema = z.object({
productId: z.string(),
placementId: z.string(),
paywallId: z.string(),
});
export type CheckoutRequest = z.infer<typeof CheckoutRequestSchema>;
export const CheckoutResponseSchema = z.object({
status: z.string(),
invoiceId: z.string(),
paymentUrl: z.string().url(),
});
export type CheckoutResponse = z.infer<typeof CheckoutResponseSchema>;
export const PaymentInfoSchema = z.object({
productId: z.string(),
key: z.string(),
isAutoTopUp: z.boolean().optional(),
});
export type PaymentInfo = z.infer<typeof PaymentInfoSchema>;
export const SingleCheckoutRequestSchema = z.object({
paymentInfo: PaymentInfoSchema,
return_url: z.string().optional(),
pageUrl: z.string().optional(),
});
export type SingleCheckoutRequest = z.infer<typeof SingleCheckoutRequestSchema>;
export const SingleCheckoutSuccessSchema = z.object({
payment: z.object({
status: z.string(),
invoiceId: z.string(),
paymentUrl: z.string().url().optional(),
}),
});
export type SingleCheckoutSuccess = z.infer<typeof SingleCheckoutSuccessSchema>;
export const SingleCheckoutErrorSchema = z.object({
status: z.string(),
message: z.string(),
});
export type SingleCheckoutError = z.infer<typeof SingleCheckoutErrorSchema>;
export const SingleCheckoutResponseSchema = z.union([
SingleCheckoutSuccessSchema,
SingleCheckoutErrorSchema,
]);
export type SingleCheckoutResponse = z.infer<
typeof SingleCheckoutResponseSchema
>;

View File

@ -0,0 +1,14 @@
"use server";
import { http } from "@/shared/api/httpClient";
import { API_ROUTES } from "@/shared/constants/api-routes";
import { FunnelRequest, FunnelResponse, FunnelResponseSchema } from "./types";
export const getFunnel = async (payload: FunnelRequest) => {
return http.post<FunnelResponse>(API_ROUTES.funnel(), payload, {
tags: ["funnel"],
schema: FunnelResponseSchema,
revalidate: 0,
});
};

View File

@ -0,0 +1,41 @@
import { cache } from "react";
import { getFunnel } from "./api";
import type { FunnelRequest } from "./types";
export const loadFunnel = cache((payload: FunnelRequest) => getFunnel(payload));
export const loadFunnelData = cache((payload: FunnelRequest) =>
loadFunnel(payload).then(d => d.data)
);
export const loadFunnelStatus = cache((payload: FunnelRequest) =>
loadFunnel(payload).then(d => d.status)
);
export const loadFunnelCurrency = cache((payload: FunnelRequest) =>
loadFunnelData(payload).then(d => d.currency)
);
export const loadFunnelLocale = cache((payload: FunnelRequest) =>
loadFunnelData(payload).then(d => d.locale)
);
export const loadFunnelPayment = cache((payload: FunnelRequest) =>
loadFunnelData(payload).then(d => d.payment)
);
export const loadFunnelPaymentById = cache(
(payload: FunnelRequest, paymentId: string) =>
loadFunnelData(payload).then(d => d.payment[paymentId])
);
// export const loadFunnelProducts = cache(
// (payload: FunnelRequest, paymentId: string) =>
// loadFunnelPaymentById(payload, paymentId).then(d => d?.variants ?? [])
// );
// export const loadFunnelProperties = cache(
// (payload: FunnelRequest, paymentId: string) =>
// loadFunnelPaymentById(payload, paymentId).then(d => d?.properties ?? [])
// );

View File

@ -0,0 +1,70 @@
import { z } from "zod";
import { Currency } from "@/shared/types";
// Request schemas
export const FunnelRequestSchema = z.object({
// funnel: z.enum(ELocalesPlacement),
funnel: z.string(),
});
// Response schemas
export const FunnelPaymentPropertySchema = z.object({
key: z.string(),
value: z.union([z.string(), z.number()]),
});
export const FunnelPaymentVariantSchema = z.object({
id: z.string(),
key: z.string(),
type: z.string(),
price: z.number(),
oldPrice: z.number().optional(),
trialPrice: z.number().optional(),
});
export const FunnelPaymentPlacementSchema = z.object({
price: z.number().optional(),
currency: z.enum(Currency).optional(),
billingPeriod: z.enum(["DAY", "WEEK", "MONTH", "YEAR"]).optional(),
billingInterval: z.number().optional(),
trialPeriod: z.enum(["DAY", "WEEK", "MONTH", "YEAR"]).optional(),
trialInterval: z.number().optional(),
placementId: z.string().optional(),
paywallId: z.string().optional(),
properties: z.array(FunnelPaymentPropertySchema).optional(),
variants: z.array(FunnelPaymentVariantSchema).optional(),
paymentUrl: z.string().optional(),
type: z.string().optional(),
});
export const FunnelSchema = z.object({
currency: z.enum(Currency),
// funnel: z.enum(ELocalesPlacement),
funnel: z.string(),
locale: z.string(),
payment: z.record(
z.string(),
z.union([
FunnelPaymentPlacementSchema.nullable(),
z.array(FunnelPaymentPlacementSchema),
])
),
});
export const FunnelResponseSchema = z.object({
status: z.union([z.literal("success"), z.string()]),
data: FunnelSchema,
});
// Type exports
export type FunnelRequest = z.infer<typeof FunnelRequestSchema>;
export type IFunnelPaymentProperty = z.infer<
typeof FunnelPaymentPropertySchema
>;
export type IFunnelPaymentVariant = z.infer<typeof FunnelPaymentVariantSchema>;
export type IFunnelPaymentPlacement = z.infer<
typeof FunnelPaymentPlacementSchema
>;
export type IFunnel = z.infer<typeof FunnelSchema>;
export type FunnelResponse = z.infer<typeof FunnelResponseSchema>;

View File

@ -39,7 +39,7 @@ export const CreateAuthorizeResponseSchema = z.object({
token: z.string(),
userId: z.string().optional(),
generatingVideo: z.boolean().optional(),
videoId: z.string().optional(),
videoId: z.string().nullable().optional(),
authCode: z.string().optional(),
});

View File

@ -39,8 +39,8 @@ export const useAuth = ({ funnelId }: IUseAuthProps) => {
timezone,
locale,
email,
// source: funnelId,
source: "aura.compatibility.v2",
source: funnelId,
// source: "aura.compatibility.v2",
// profile: {
// name: username || "",
// gender: EGender[gender as keyof typeof EGender] || null,
@ -60,22 +60,10 @@ export const useAuth = ({ funnelId }: IUseAuthProps) => {
sign: true,
signDate: new Date().toISOString(),
// feature: feature.includes("black") ? "ios" : feature,
feature: "stripe"
});
},
[
// birthPlace,
// birthdate,
// gender,
// locale,
// partnerBirthPlace,
// partnerBirthdate,
// partnerGender,
// partnerName,
// username,
// birthtime,
// checked,
// dateOfCheck,
]
[funnelId]
);
const authorization = useCallback(
@ -106,6 +94,7 @@ export const useAuth = ({ funnelId }: IUseAuthProps) => {
// authCode,
} = await createAuthorization(payload);
await setAuthTokenToCookie(token);
return token;
// const { user: userMe } = await api.getMe({ token });
// const userId = userIdFromApi || userMe?._id;
// if (userId?.length) {

View File

@ -0,0 +1,88 @@
"use client";
import { useCallback, useEffect, useMemo, useState } from "react";
import { Data } from "@lottiefiles/dotlottie-react";
import indexedDB, { EObjectStores } from "@/shared/utils/indexedDB";
import { ELottieKeys, lottieUrls } from "@/shared/constants/lottie";
interface IUseLottieProps {
preloadKey?: ELottieKeys;
loadKey?: ELottieKeys;
}
export const useLottie = ({ preloadKey, loadKey }: IUseLottieProps) => {
const [animationData, setAnimationData] = useState<Data>();
const [isError, setIsError] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const getAnimationDataFromLottie = async (key: ELottieKeys) => {
try {
const animation = await fetch(lottieUrls[key]);
if (!animation.ok) {
throw new Error(`HTTP error! status: ${animation.status}`);
}
const arrayBuffer = await animation.arrayBuffer();
return arrayBuffer;
} catch (error) {
console.error("Error loading animation:", error);
setIsError(true);
return null;
}
};
const preload = useCallback(async (key: ELottieKeys) => {
console.log("preload", key);
const arrayBuffer = await getAnimationDataFromLottie(key);
indexedDB.set(EObjectStores.Lottie, key, arrayBuffer);
}, []);
const load = useCallback(async (key: ELottieKeys) => {
setIsLoading(true);
setIsError(false);
try {
const animationFromDB = await indexedDB.get<ArrayBuffer>(
EObjectStores.Lottie,
key
);
if (animationFromDB) {
setAnimationData(animationFromDB);
setIsLoading(false);
return;
}
const arrayBuffer = await getAnimationDataFromLottie(key);
if (!arrayBuffer) {
setIsLoading(false);
return;
}
setAnimationData(arrayBuffer);
await indexedDB.set<ArrayBuffer>(EObjectStores.Lottie, key, arrayBuffer);
} catch (error) {
console.error("Error in load process:", error);
setIsError(true);
} finally {
setIsLoading(false);
}
}, []);
useEffect(() => {
if (preloadKey) {
preload(preloadKey);
}
if (loadKey) {
load(loadKey);
}
}, [load, loadKey, preload, preloadKey]);
return useMemo(
() => ({
animationData,
isError,
isLoading,
}),
[animationData, isError, isLoading]
);
};

View File

@ -0,0 +1,68 @@
"use client";
import { useEffect, useMemo, useState } from "react";
import type { FunnelDefinition } from "@/lib/funnel/types";
import { loadFunnelPaymentById } from "@/entities/session/funnel/loaders";
import type { IFunnelPaymentPlacement } from "@/entities/session/funnel/types";
interface UsePaymentPlacementArgs {
funnel: FunnelDefinition;
paymentId: string;
}
interface UsePaymentPlacementResult {
placement: IFunnelPaymentPlacement | null;
isLoading: boolean;
error: string | null;
}
export function usePaymentPlacement({
funnel,
paymentId,
}: UsePaymentPlacementArgs): UsePaymentPlacementResult {
const [placement, setPlacement] = useState<IFunnelPaymentPlacement | null>(
null
);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const funnelKey = useMemo(() => funnel?.meta?.id ?? "", [funnel]);
useEffect(() => {
let isMounted = true;
if (!funnelKey || !paymentId) return;
(async () => {
try {
setIsLoading(true);
setError(null);
const data = await loadFunnelPaymentById(
{ funnel: funnelKey },
paymentId
);
// Normalize union: record value can be IFunnelPaymentPlacement or IFunnelPaymentPlacement[] or null
const normalized: IFunnelPaymentPlacement | null = Array.isArray(data)
? data[0] ?? null
: data ?? null;
if (!isMounted) return;
setPlacement(normalized);
} catch (e) {
if (!isMounted) return;
const message =
e instanceof Error ? e.message : "Failed to load payment placement";
setError(message);
} finally {
if (isMounted) setIsLoading(false);
}
})();
return () => {
isMounted = false;
};
}, [funnelKey, paymentId]);
return { placement, isLoading, error };
}

View File

@ -54,6 +54,7 @@ export const useSession = ({ funnelId }: IUseSessionProps) => {
try {
const utm = parseQueryParams();
const sessionParams = {
feature: "stripe",
locale,
timezone,
// source: funnelId,
@ -102,7 +103,10 @@ export const useSession = ({ funnelId }: IUseSessionProps) => {
}
const result = await updateSessionApi({
sessionId: _sessionId,
data,
data: {
feature: "stripe",
...data,
},
});
return result;
} catch (error) {

View File

@ -29,3 +29,4 @@ export { buildCouponDefaults } from "./coupon";
export { buildEmailDefaults } from "./email";
export { buildLoadersDefaults } from "./loaders";
export { buildSoulmateDefaults } from "./soulmate";
export { buildTrialPaymentDefaults } from "./trialPayment";

View File

@ -1,11 +1,11 @@
import type { BuilderScreen } from "@/lib/admin/builder/types";
import {
buildDefaultHeader,
import {
buildDefaultHeader,
buildDefaultTitle,
buildDefaultSubtitle,
buildDefaultBottomActionButton,
buildDefaultNavigation,
buildDefaultDescription
buildDefaultDescription,
} from "./blocks";
export function buildSoulmateDefaults(id: string): BuilderScreen {
@ -19,12 +19,39 @@ export function buildSoulmateDefaults(id: string): BuilderScreen {
title: buildDefaultTitle(),
subtitle: buildDefaultSubtitle(),
bottomActionButton: buildDefaultBottomActionButton({
text: "Получить полный анализ",
text: "Continue",
showPrivacyTermsConsent: true,
}),
description: buildDefaultDescription({
text: "Ваш персональный портрет почти готов.",
text: "Готов увидеть, кто твоя настоящая Родственная душа?",
align: "center",
}),
soulmatePortraitsDelivered: {
image: "/soulmate-portrait-delivered-male.jpg",
text: {
text: "soulmate portraits delivered today",
font: "inter",
weight: "medium",
size: "sm",
color: "primary",
},
avatars: [
{ src: "/avatars/male-1.jpg", alt: "Male 1" },
{ src: "/avatars/male-2.jpg", alt: "Male 2" },
{ src: "/avatars/male-3.jpg", alt: "Male 3" },
{ src: "", fallbackText: "900+" },
],
},
textList: {
items: [
{
text: "Всего 2 минуты — и Портрет откроет того, кто связан с тобой судьбой.",
},
{ text: "Поразительная точность 99%." },
{ text: "Тебя ждёт неожиданное открытие." },
{ text: "Осталось лишь осмелиться взглянуть." },
],
},
navigation: buildDefaultNavigation(),
} as BuilderScreen;
}

View File

@ -0,0 +1,208 @@
import { nanoid } from "nanoid";
import type { TrialPaymentScreenDefinition } from "@/lib/funnel/types";
import {
buildDefaultBottomActionButton,
buildDefaultNavigation,
buildDefaultTitle,
buildDefaultSubtitle,
buildDefaultImage,
} from "./blocks";
export function buildTrialPaymentDefaults(id?: string): TrialPaymentScreenDefinition {
return {
id: id || `trial-${nanoid(6)}`,
template: "trialPayment",
header: { show: false },
title: buildDefaultTitle({ show: false }),
subtitle: buildDefaultSubtitle({ show: false }),
bottomActionButton: buildDefaultBottomActionButton({ show: false }),
navigation: buildDefaultNavigation(),
headerBlock: {
text: { text: "⚠️ Your sketch expires soon!" },
timer: { text: "" },
timerSeconds: 600,
},
unlockYourSketch: {
title: { text: "Unlock Your Sketch" },
subtitle: { text: "Just One Click to Reveal Your Match!" },
image: buildDefaultImage({ src: "/trial-payment/portrait-female.jpg" }),
blur: { text: { text: "Unlock to reveal your personalized portrait" }, icon: "lock" },
buttonText: "Get Me Soulmate Sketch",
},
joinedToday: { count: { text: "954" }, text: { text: "Joined today" } },
trustedByOver: { text: { text: "Trusted by over 355,000 people." } },
findingOneGuide: {
header: { emoji: { text: "❤️" }, title: { text: "Finding the One Guide" } },
text: { text: "You're not just looking for someone — you're. You're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you're. You're not just looking for someone — you're. You're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you'reYou're not just looking for someone — you're" },
blur: { text: { text: "Чтобы открыть весь отчёт, нужен полный доступ." }, icon: "lock" },
},
tryForDays: {
title: { text: "Попробуйте в течение 7 дней!" },
textList: {
items: [
{ text: "Receive a hand-drawn sketch of your soulmate." },
{ text: "Reveal the path with the guide." },
{ text: "Talk to live experts and get guidance." },
{ text: "Start your 7-day trial for just $1.00." },
{ text: "Cancel anytime—just 24 hours before renewal." },
],
},
},
totalPrice: {
couponContainer: { title: { text: "Coupon\nCode" }, buttonText: "SOULMATE94" },
priceContainer: {
title: { text: "Total" },
price: { text: "$1.00" },
oldPrice: { text: "$14.99" },
discount: { text: "94% discount applied" },
},
},
paymentButtons: {
buttons: [
{ text: "Pay", icon: "pay" },
{ text: "Pay", icon: "google" },
{ text: "Credit or debit card", icon: "card", primary: true },
],
},
moneyBackGuarantee: {
title: { text: "30-DAY MONEY-BACK GUARANTEE" },
text: { text: "If you don't receive your soulmate sketch, we'll refund your money!" },
},
policy: {
text: { text: "By clicking Continue, you agree to our Terms of Use & Service and Privacy Policy. You also acknowledge that your 1 week introductory plan to Respontika, billed at $1.00, will automatically renew at $14.50 every 1 week unless canceled before the end of the trial period." },
},
usersPortraits: {
title: { text: "Our Users' Soulmate Portraits" },
images: [
{ src: "/trial-payment/users-portraits/1.jpg" },
{ src: "/trial-payment/users-portraits/2.jpg" },
{ src: "/trial-payment/users-portraits/3.jpg" },
],
buttonText: "Get me soulmate sketch",
},
joinedTodayWithAvatars: {
count: { text: "954" },
text: { text: "people joined today" },
avatars: {
images: [
{ src: "/trial-payment/avatars/1.jpg" },
{ src: "/trial-payment/avatars/2.jpg" },
{ src: "/trial-payment/avatars/3.jpg" },
{ src: "/trial-payment/avatars/4.jpg" },
{ src: "/trial-payment/avatars/5.jpg" },
],
},
},
progressToSeeSoulmate: {
title: { text: "See Your Soulmate Just One Step Away" },
progress: { value: 92 },
leftText: { text: "Step 2 of 5" },
rightText: { text: "99% Complete" },
},
stepsToSeeSoulmate: {
steps: [
{ title: { text: "Questions Answered" }, description: { text: "You've provided all the necessary information." }, icon: "questions", isActive: true },
{ title: { text: "Profile Analysis" }, description: { text: "Creating your perfect soulmate profile." }, icon: "profile", isActive: true },
{ title: { text: "Sketch Creation" }, description: { text: "Your personalized soulmate sketch will be created." }, icon: "sketch" },
{ title: { text: "Астрологические Идеи" }, description: { text: "Уникальные астрологические рекомендации." }, icon: "astro" },
{ title: { text: "Персонализированный чат с экспертом" }, description: { text: "Персональные советы." }, icon: "chat" },
],
buttonText: "Show Me My Soulmate",
},
reviews: {
title: { text: "Loved and Trusted Worldwide" },
items: [
{
name: { text: "Jennifer Wilson 🇺🇸" },
text: { text: "**“Я увидела свои ошибки… и нашла мужа”**\nПортрет сразу зацепил — было чувство, что я уже где-то его видела. Но настоящий перелом произошёл после гайда: я поняла, почему снова и снова выбирала «не тех». И самое удивительное — вскоре я познакомилась с мужчиной, который оказался точной копией того самого портрета. Сейчас он мой муж, и когда мы сравнили рисунок с его фото, сходство было просто вау." },
avatar: { src: "/trial-payment/reviews/avatars/1.jpg" },
portrait: { src: "/trial-payment/reviews/portraits/1.jpg" },
photo: { src: "/trial-payment/reviews/photos/1.jpg" },
rating: 5,
date: { text: "1 day ago" },
},
{
name: { text: "Amanda Davis 🇨🇦" },
text: { text: "**“Я поняла своего партнёра лучше за один вечер, чем за несколько лет”**\nПрошла тест ради интереса — портрет нас удивил. Но настоящий прорыв случился, когда я прочитала гайд о второй половинке. Там были точные подсказки о том, как мы можем поддерживать друг друга. Цена смешная, а ценность огромная: теперь у нас меньше недопониманий и больше тепла." },
avatar: { src: "/trial-payment/reviews/avatars/2.jpg" },
portrait: { src: "/trial-payment/reviews/portraits/2.jpg" },
photo: { src: "/trial-payment/reviews/photos/2.jpg" },
rating: 5,
date: { text: "4 days ago" },
},
{
name: { text: "Michael Johnson 🇬🇧" },
text: { text: "**“Увидел её лицо — и мурашки по коже”**\nКогда пришёл результат теста и показали портрет, я реально замер. Это была та самая девушка, с которой я начал встречаться пару недель назад. И гайд прямо описал, почему мы тянемся друг к другу. Честно, я не ожидал такого совпадения." },
avatar: { src: "/trial-payment/reviews/avatars/3.jpg" },
portrait: { src: "/trial-payment/reviews/portraits/3.jpg" },
photo: { src: "/trial-payment/reviews/photos/3.jpg" },
rating: 5,
date: { text: "1 week ago" },
},
],
},
stillHaveQuestions: {
title: { text: "Still have questions? We're here to help!" },
actionButtonText: "Get me Soulmate Sketch",
contactButtonText: "Contact Support",
},
commonQuestions: {
title: { text: "Common Questions" },
items: [
{
question: "When will I receive my sketch?",
answer:
"Your personalized soulmate sketch will be delivered within 24-48 hours after completing your order. You'll receive an email notification when it's ready for viewing in your account.",
},
{
question: "How do I cancel my subscription?",
answer:
"You can cancel anytime from your account settings. Make sure to cancel at least 24 hours before the renewal date to avoid being charged.",
},
{
question: "How accurate are the readings?",
answer:
"Our readings are based on a combination of your answers and advanced pattern analysis. While they provide valuable insights, they are intended for guidance and entertainment purposes.",
},
{
question: "Is my data secure and private?",
answer:
"Yes. We follow strict data protection standards. Your data is encrypted and never shared with third parties without your consent.",
},
],
},
footer: {
title: { text: "WIT LAB ©" },
contacts: {
title: { text: "CONTACTS" },
email: { href: "support@witlab.com", text: "support@witlab.com" },
address: { text: "Wit Lab 2108 N ST STE N SACRAMENTO, CA95816, US" },
},
legal: {
title: { text: "LEGAL" },
links: [
{ href: "https://witlab.com/terms", text: "Terms of Service" },
{ href: "https://witlab.com/privacy", text: "Privacy Policy" },
{ href: "https://witlab.com/refund", text: "Refund Policy" },
],
copyright: {
text:
"Copyright © 2025 Wit Lab™. All rights reserved. All trademarks referenced herein are the properties of their respective owners.",
},
},
paymentMethods: {
title: { text: "PAYMENT METHODS" },
methods: [
{ src: "/trial-payment/payment-methods/visa.svg", alt: "visa" },
{ src: "/trial-payment/payment-methods/mastercard.svg", alt: "mastercard" },
{ src: "/trial-payment/payment-methods/discover.svg", alt: "discover" },
{ src: "/trial-payment/payment-methods/apple.svg", alt: "apple" },
{ src: "/trial-payment/payment-methods/google.svg", alt: "google" },
{ src: "/trial-payment/payment-methods/paypal.svg", alt: "paypal" },
],
},
},
};
}

View File

@ -9,6 +9,7 @@ import { buildCouponDefaults } from "./defaults/coupon";
import { buildEmailDefaults } from "./defaults/email";
import { buildLoadersDefaults } from "./defaults/loaders";
import { buildSoulmateDefaults } from "./defaults/soulmate";
import { buildTrialPaymentDefaults } from "./defaults/trialPayment";
/**
* Marks the state as dirty if it has changed
@ -57,6 +58,8 @@ export function createScreenByTemplate(
return buildLoadersDefaults(id);
case "soulmate":
return buildSoulmateDefaults(id);
case "trialPayment":
return buildTrialPaymentDefaults(id);
default:
throw new Error(`Unknown template: ${template}`);
}

View File

@ -1,6 +1,7 @@
import type { TypographyProps } from "@/components/ui/Typography/Typography";
import type { MainButtonProps } from "@/components/ui/MainButton/MainButton";
import { hasTextMarkup } from "@/lib/text-markup";
import { cn } from "@/lib/utils";
import type {
HeaderDefinition,
@ -24,7 +25,9 @@ type TypographyAs =
| "h4"
| "h5"
| "h6"
| "div";
| "div"
| "li"
| "address";
interface TypographyDefaults {
font?: TypographyVariant["font"];
@ -32,6 +35,7 @@ interface TypographyDefaults {
size?: TypographyVariant["size"];
align?: TypographyVariant["align"];
color?: TypographyVariant["color"];
className?: string;
}
interface BuildTypographyOptions<T extends TypographyAs> {
@ -67,7 +71,7 @@ export function buildTypographyProps<T extends TypographyAs>(
size: variant.size ?? defaults?.size,
align: variant.align ?? defaults?.align,
color: variant.color ?? defaults?.color,
className: variant.className,
className: cn(defaults?.className, variant.className),
enableMarkup: hasTextMarkup(variant.text || ""), // 🎨 АВТОМАТИЧЕСКИ включаем разметку если обнаружена
} as TypographyProps<T>;
}

View File

@ -11,6 +11,7 @@ import {
EmailTemplate,
LoadersTemplate,
SoulmatePortraitTemplate,
TrialPaymentTemplate,
} from "@/components/funnel/templates";
import type {
ListScreenDefinition,
@ -21,6 +22,7 @@ import type {
EmailScreenDefinition,
LoadersScreenDefinition,
SoulmatePortraitScreenDefinition,
TrialPaymentScreenDefinition,
ScreenDefinition,
DefaultTexts,
FunnelDefinition,
@ -297,6 +299,29 @@ const TEMPLATE_REGISTRY: Record<
/>
);
},
trialPayment: ({
screen,
onContinue,
canGoBack,
onBack,
screenProgress,
defaultTexts,
funnel,
}) => {
const trialPaymentScreen = screen as TrialPaymentScreenDefinition;
return (
<TrialPaymentTemplate
screen={trialPaymentScreen}
onContinue={onContinue}
canGoBack={canGoBack}
onBack={onBack}
screenProgress={screenProgress}
defaultTexts={defaultTexts}
funnel={funnel}
/>
);
},
};
export function renderScreen(props: ScreenRenderProps): JSX.Element {

View File

@ -326,13 +326,177 @@ export interface SoulmatePortraitScreenDefinition {
header?: HeaderDefinition;
title: TitleDefinition;
subtitle?: SubtitleDefinition;
description?: TypographyVariant; // 🎯 Настраиваемый текст описания
description?: TypographyVariant; // Настраиваемый текст описания
soulmatePortraitsDelivered?: {
image?: string;
text?: TypographyVariant;
avatars?: Array<{
src: string;
alt?: string;
fallbackText?: string;
}>;
};
textList?: {
items: TypographyVariant[];
};
bottomActionButton?: BottomActionButtonDefinition;
navigation?: NavigationDefinition;
variants?: ScreenVariantDefinition<SoulmatePortraitScreenDefinition>[];
}
export type ScreenDefinition = InfoScreenDefinition | DateScreenDefinition | CouponScreenDefinition | FormScreenDefinition | ListScreenDefinition | EmailScreenDefinition | LoadersScreenDefinition | SoulmatePortraitScreenDefinition;
// TrialPayment Screen Definition (лендинг оплаты с большим количеством секций)
export interface TrialPaymentScreenDefinition {
id: string;
template: "trialPayment";
header?: HeaderDefinition;
// В TrialPayment заголовок и подзаголовок используются опционально сверху экрана
title?: TitleDefinition;
subtitle?: SubtitleDefinition;
// Глобальная нижняя кнопка экрана
bottomActionButton?: BottomActionButtonDefinition;
navigation?: NavigationDefinition;
variants?: ScreenVariantDefinition<TrialPaymentScreenDefinition>[];
// Минимальные секции для первого шага миграции
headerBlock?: {
text?: TypographyVariant;
timer?: TypographyVariant;
timerSeconds?: number;
};
unlockYourSketch?: {
title?: TypographyVariant;
subtitle?: TypographyVariant;
image?: ImageDefinition;
blur?: {
text?: TypographyVariant;
icon?: "lock";
};
buttonText?: string;
};
joinedToday?: {
count?: TypographyVariant;
text?: TypographyVariant;
};
trustedByOver?: {
text?: TypographyVariant;
};
findingOneGuide?: {
header?: {
emoji?: TypographyVariant;
title?: TypographyVariant;
};
text?: TypographyVariant;
blur?: {
text?: TypographyVariant;
icon?: "lock";
};
};
tryForDays?: {
title?: TypographyVariant;
textList?: {
items: TypographyVariant[];
};
};
totalPrice?: {
couponContainer: {
title?: TypographyVariant;
buttonText?: string;
};
priceContainer?: {
title?: TypographyVariant;
price?: TypographyVariant;
oldPrice?: TypographyVariant;
discount?: TypographyVariant;
};
};
paymentButtons?: {
buttons: Array<{
text: string;
icon?: "pay" | "google" | "card";
primary?: boolean;
}>;
};
moneyBackGuarantee?: {
title?: TypographyVariant;
text?: TypographyVariant;
};
policy?: {
text?: TypographyVariant;
};
usersPortraits?: {
title?: TypographyVariant;
images?: ImageDefinition[];
buttonText?: string;
};
joinedTodayWithAvatars?: {
count?: TypographyVariant;
text?: TypographyVariant;
avatars?: {
// minimal: только пути
images: ImageDefinition[];
};
};
progressToSeeSoulmate?: {
title?: TypographyVariant;
progress?: {
value: number;
};
leftText?: TypographyVariant;
rightText?: TypographyVariant;
};
stepsToSeeSoulmate?: {
steps: Array<{
title: TypographyVariant;
description: TypographyVariant;
icon?: "questions" | "profile" | "sketch" | "astro" | "chat";
isActive?: boolean;
}>;
buttonText?: string;
};
reviews?: {
title?: TypographyVariant;
items: Array<{
// минимальный набор, дальше расширим
name: TypographyVariant;
text: TypographyVariant;
avatar?: ImageDefinition;
rating?: number;
date?: TypographyVariant;
portrait?: ImageDefinition;
photo?: ImageDefinition;
}>;
};
commonQuestions?: {
title?: TypographyVariant;
items: Array<{
question: string;
answer: string;
}>;
};
stillHaveQuestions?: {
title?: TypographyVariant;
actionButtonText?: string;
contactButtonText?: string;
};
footer?: {
title?: TypographyVariant;
contacts?: {
title?: TypographyVariant;
email?: { href: string; text: string };
address?: TypographyVariant;
};
legal?: {
title?: TypographyVariant;
links?: Array<{ href: string; text: string }>;
copyright?: TypographyVariant;
};
paymentMethods?: {
title?: TypographyVariant;
methods?: Array<{ src: string; alt: string }>;
};
};
}
export type ScreenDefinition = InfoScreenDefinition | DateScreenDefinition | CouponScreenDefinition | FormScreenDefinition | ListScreenDefinition | EmailScreenDefinition | LoadersScreenDefinition | SoulmatePortraitScreenDefinition | TrialPaymentScreenDefinition;
export interface FunnelMetaDefinition {
id: string;

View File

@ -1,29 +1,29 @@
import mongoose, { Schema, Document, Model } from 'mongoose';
import type { FunnelDefinition } from '@/lib/funnel/types';
import mongoose, { Schema, Document, Model } from "mongoose";
import type { FunnelDefinition } from "@/lib/funnel/types";
// Extend FunnelDefinition with MongoDB specific fields
export interface IFunnel extends Document {
// Основные данные воронки
funnelData: FunnelDefinition;
// Метаданные для админки
name: string; // Человеко-читаемое имя для каталога
description?: string;
status: 'draft' | 'published' | 'archived';
status: "draft" | "published" | "archived";
// Система версий и истории
version: number;
parentFunnelId?: string; // Для создания копий
// Timestamps
createdAt: Date;
updatedAt: Date;
publishedAt?: Date;
// Пользовательские данные
createdBy?: string; // User ID in future
lastModifiedBy?: string;
// Статистика использования
usage: {
totalViews: number;
@ -33,231 +33,290 @@ export interface IFunnel extends Document {
}
// Вложенные схемы для валидации структуры данных воронки
const TypographyVariantSchema = new Schema({
text: {
type: String,
// НЕ required - позволяет { show: false } без текста
validate: {
validator: function(v: string | undefined): boolean {
// Если текст указан, он не может быть пустым
if (v === undefined || v === null) return true;
return v.trim().length > 0;
const TypographyVariantSchema = new Schema(
{
text: {
type: String,
// НЕ required — позволяет { show: false } без текста, но если указан — не пустой
validate: {
validator: function (v: string | undefined): boolean {
if (v === undefined || v === null) return true;
return v.trim().length > 0;
},
message: "Text field cannot be empty if provided",
},
message: 'Text field cannot be empty if provided'
}
},
show: { type: Boolean, default: true }, // поддержка флага видимости
font: {
type: String,
enum: ["manrope", "inter", "geistSans", "geistMono"],
default: "manrope",
},
weight: {
type: String,
enum: ["regular", "medium", "semiBold", "bold", "extraBold", "black"],
default: "regular",
},
size: {
type: String,
enum: ["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl"],
default: "md",
},
align: {
type: String,
enum: ["center", "left", "right"],
default: "center",
},
color: {
type: String,
enum: [
"default",
"primary",
"secondary",
"destructive",
"success",
"card",
"accent",
"muted",
],
default: "default",
},
className: String,
},
show: { type: Boolean, default: true }, // Добавляем поддержку show флага
font: {
type: String,
enum: ['manrope', 'inter', 'geistSans', 'geistMono'],
default: 'manrope'
},
weight: {
type: String,
enum: ['regular', 'medium', 'semiBold', 'bold', 'extraBold', 'black'],
default: 'regular'
},
size: {
type: String,
enum: ['xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl', '4xl'],
default: 'md'
},
align: {
type: String,
enum: ['center', 'left', 'right'],
default: 'center'
},
color: {
type: String,
enum: ['default', 'primary', 'secondary', 'destructive', 'success', 'card', 'accent', 'muted'],
default: 'default'
},
className: String
}, { _id: false });
{ _id: false }
);
const HeaderDefinitionSchema = new Schema({
progress: {
current: Number,
total: Number,
value: Number,
label: String,
className: String
const HeaderDefinitionSchema = new Schema(
{
progress: {
current: Number,
total: Number,
value: Number,
label: String,
className: String,
},
showBackButton: { type: Boolean, default: true },
show: { type: Boolean, default: true },
},
showBackButton: { type: Boolean, default: true },
show: { type: Boolean, default: true }
}, { _id: false });
{ _id: false }
);
const ListOptionDefinitionSchema = new Schema({
id: { type: String, required: true },
label: { type: String, required: true },
description: String,
emoji: String,
value: String,
disabled: { type: Boolean, default: false }
}, { _id: false });
const NavigationConditionSchema = new Schema({
screenId: { type: String, required: true },
conditionType: { type: String, enum: ['options', 'values'], default: 'options' },
operator: {
type: String,
enum: ['includesAny', 'includesAll', 'includesExactly', 'equals'],
default: 'includesAny'
const ListOptionDefinitionSchema = new Schema(
{
id: { type: String, required: true },
label: { type: String, required: true },
description: String,
emoji: String,
value: String,
disabled: { type: Boolean, default: false },
},
optionIds: [{ type: String }],
values: [{ type: String }],
}, { _id: false });
{ _id: false }
);
const NavigationRuleSchema = new Schema({
conditions: [NavigationConditionSchema],
nextScreenId: { type: String, required: true }
}, { _id: false });
const NavigationDefinitionSchema = new Schema({
rules: [NavigationRuleSchema],
defaultNextScreenId: String,
isEndScreen: { type: Boolean, default: false },
}, { _id: false });
const BottomActionButtonSchema = new Schema({
show: { type: Boolean, default: true },
text: String,
cornerRadius: {
type: String,
enum: ['3xl', 'full'],
default: '3xl'
const NavigationConditionSchema = new Schema(
{
screenId: { type: String, required: true },
conditionType: {
type: String,
enum: ["options", "values"],
default: "options",
},
operator: {
type: String,
enum: ["includesAny", "includesAll", "includesExactly", "equals"],
default: "includesAny",
},
optionIds: [{ type: String }],
values: [{ type: String }],
},
showPrivacyTermsConsent: { type: Boolean, default: false },
}, { _id: false });
{ _id: false }
);
const NavigationRuleSchema = new Schema(
{
conditions: [NavigationConditionSchema],
nextScreenId: { type: String, required: true },
},
{ _id: false }
);
const NavigationDefinitionSchema = new Schema(
{
rules: [NavigationRuleSchema],
defaultNextScreenId: String,
isEndScreen: { type: Boolean, default: false },
},
{ _id: false }
);
const BottomActionButtonSchema = new Schema(
{
show: { type: Boolean, default: true },
text: String,
cornerRadius: {
type: String,
enum: ["3xl", "full"],
default: "3xl",
},
showPrivacyTermsConsent: { type: Boolean, default: false },
},
{ _id: false }
);
// Схемы для различных типов экранов (используем Mixed для гибкости)
const ScreenDefinitionSchema = new Schema({
id: { type: String, required: true },
template: {
type: String,
enum: ['info', 'date', 'coupon', 'form', 'list', 'email', 'loaders', 'soulmate'],
required: true
},
header: HeaderDefinitionSchema,
title: { type: TypographyVariantSchema, required: true },
subtitle: TypographyVariantSchema,
bottomActionButton: BottomActionButtonSchema,
navigation: NavigationDefinitionSchema,
// Специфичные для template поля (используем Mixed для максимальной гибкости)
description: TypographyVariantSchema, // info, soulmate
icon: Schema.Types.Mixed, // info
variables: [Schema.Types.Mixed], // info - динамические переменные для подстановки в текст
dateInput: Schema.Types.Mixed, // date
infoMessage: Schema.Types.Mixed, // date
coupon: Schema.Types.Mixed, // coupon
copiedMessage: String, // coupon
fields: [Schema.Types.Mixed], // form
validationMessages: Schema.Types.Mixed, // form
list: { // list
selectionType: {
type: String,
enum: ['single', 'multi']
const ScreenDefinitionSchema = new Schema(
{
id: { type: String, required: true },
template: {
type: String,
enum: [
"info",
"date",
"coupon",
"form",
"list",
"email",
"loaders",
"soulmate",
"trialPayment",
],
required: true,
},
options: [ListOptionDefinitionSchema]
},
emailInput: Schema.Types.Mixed, // email
image: Schema.Types.Mixed, // email, soulmate
// loaders
progressbars: Schema.Types.Mixed, // preferred key used by runtime/templates
variants: [Schema.Types.Mixed] // variants для всех типов
}, { _id: false });
header: HeaderDefinitionSchema,
title: { type: TypographyVariantSchema, required: true },
subtitle: TypographyVariantSchema,
bottomActionButton: BottomActionButtonSchema,
navigation: NavigationDefinitionSchema,
const FunnelMetaSchema = new Schema({
id: { type: String, required: true },
version: String,
title: String,
description: String,
firstScreenId: String
}, { _id: false });
const DefaultTextsSchema = new Schema({
nextButton: { type: String, default: 'Next' },
privacyBanner: { type: String },
}, { _id: false });
const FunnelDataSchema = new Schema({
meta: { type: FunnelMetaSchema, required: true },
defaultTexts: DefaultTextsSchema,
screens: [ScreenDefinitionSchema]
}, { _id: false });
const FunnelSchema = new Schema<IFunnel>({
// Основные данные воронки
funnelData: {
type: FunnelDataSchema,
required: true,
validate: {
validator: function(v: FunnelDefinition): boolean {
// Базовая валидация структуры
return Boolean(v?.meta && v.meta.id && Array.isArray(v.screens));
// Специфичные для template поля (используем Mixed для максимальной гибкости)
description: TypographyVariantSchema, // info, soulmate
icon: Schema.Types.Mixed, // info
variables: [Schema.Types.Mixed], // info - динамические переменные для подстановки в текст
dateInput: Schema.Types.Mixed, // date
infoMessage: Schema.Types.Mixed, // date
coupon: Schema.Types.Mixed, // coupon
copiedMessage: String, // coupon
fields: [Schema.Types.Mixed], // form
validationMessages: Schema.Types.Mixed, // form
list: {
// list
selectionType: {
type: String,
enum: ["single", "multi"],
},
message: 'Invalid funnel data structure'
}
options: [ListOptionDefinitionSchema],
},
emailInput: Schema.Types.Mixed, // email
image: Schema.Types.Mixed, // email, soulmate
// loaders
progressbars: Schema.Types.Mixed, // preferred key used by runtime/templates
variants: [Schema.Types.Mixed], // variants для всех типов
},
// Метаданные для админки
name: {
type: String,
required: true,
trim: true,
maxlength: 200
{ _id: false, strict: false }
);
const FunnelMetaSchema = new Schema(
{
id: { type: String, required: true },
version: String,
title: String,
description: String,
firstScreenId: String,
},
description: {
type: String,
trim: true,
maxlength: 1000
{ _id: false }
);
const DefaultTextsSchema = new Schema(
{
nextButton: { type: String, default: "Next" },
privacyBanner: { type: String },
},
status: {
type: String,
enum: ['draft', 'published', 'archived'],
default: 'draft',
required: true
{ _id: false }
);
const FunnelDataSchema = new Schema(
{
meta: { type: FunnelMetaSchema, required: true },
defaultTexts: DefaultTextsSchema,
screens: [ScreenDefinitionSchema],
},
// Система версий
version: {
type: Number,
default: 1,
min: 1
{ _id: false }
);
const FunnelSchema = new Schema<IFunnel>(
{
// Основные данные воронки
funnelData: {
type: FunnelDataSchema,
required: true,
validate: {
validator: function (v: FunnelDefinition): boolean {
// Базовая валидация структуры
return Boolean(v?.meta && v.meta.id && Array.isArray(v.screens));
},
message: "Invalid funnel data structure",
},
},
// Метаданные для админки
name: {
type: String,
required: true,
trim: true,
maxlength: 200,
},
description: {
type: String,
trim: true,
maxlength: 1000,
},
status: {
type: String,
enum: ["draft", "published", "archived"],
default: "draft",
required: true,
},
// Система версий
version: {
type: Number,
default: 1,
min: 1,
},
parentFunnelId: {
type: Schema.Types.ObjectId,
ref: "Funnel",
},
// Пользовательские данные
createdBy: String, // В будущем можно заменить на ObjectId ref на User
lastModifiedBy: String,
// Статистика
usage: {
totalViews: { type: Number, default: 0, min: 0 },
totalCompletions: { type: Number, default: 0, min: 0 },
lastUsed: Date,
},
// Timestamps
publishedAt: Date,
},
parentFunnelId: {
type: Schema.Types.ObjectId,
ref: 'Funnel'
},
// Пользовательские данные
createdBy: String, // В будущем можно заменить на ObjectId ref на User
lastModifiedBy: String,
// Статистика
usage: {
totalViews: { type: Number, default: 0, min: 0 },
totalCompletions: { type: Number, default: 0, min: 0 },
lastUsed: Date
},
// Timestamps
publishedAt: Date
}, {
timestamps: true, // Автоматически добавляет createdAt и updatedAt
collection: 'funnels'
});
{
timestamps: true, // Автоматически добавляет createdAt и updatedAt
collection: "funnels",
}
);
// Индексы для производительности
FunnelSchema.index({ 'funnelData.meta.id': 1 }); // Для поиска по ID воронки
FunnelSchema.index({ "funnelData.meta.id": 1 }); // Для поиска по ID воронки
FunnelSchema.index({ status: 1, updatedAt: -1 }); // Для каталога воронок
FunnelSchema.index({ name: 'text', description: 'text' }); // Для поиска по тексту
FunnelSchema.index({ name: "text", description: "text" }); // Для поиска по тексту
FunnelSchema.index({ createdBy: 1 }); // Для фильтра по автору
FunnelSchema.index({ 'usage.lastUsed': -1 }); // Для сортировки по использованию
FunnelSchema.index({ "usage.lastUsed": -1 }); // Для сортировки по использованию
// Методы модели
FunnelSchema.methods.toPublicJSON = function(this: IFunnel) {
FunnelSchema.methods.toPublicJSON = function (this: IFunnel) {
return {
_id: this._id,
name: this.name,
@ -268,14 +327,17 @@ FunnelSchema.methods.toPublicJSON = function(this: IFunnel) {
updatedAt: this.updatedAt,
publishedAt: this.publishedAt,
usage: this.usage,
funnelData: this.funnelData
funnelData: this.funnelData,
};
};
FunnelSchema.methods.incrementUsage = function(this: IFunnel, type: 'view' | 'completion') {
if (type === 'view') {
FunnelSchema.methods.incrementUsage = function (
this: IFunnel,
type: "view" | "completion"
) {
if (type === "view") {
this.usage.totalViews += 1;
} else if (type === 'completion') {
} else if (type === "completion") {
this.usage.totalCompletions += 1;
}
this.usage.lastUsed = new Date();
@ -283,35 +345,50 @@ FunnelSchema.methods.incrementUsage = function(this: IFunnel, type: 'view' | 'co
};
// Статические методы
FunnelSchema.statics.findPublished = function() {
return this.find({ status: 'published' }).sort({ publishedAt: -1 });
FunnelSchema.statics.findPublished = function () {
return this.find({ status: "published" }).sort({ publishedAt: -1 });
};
FunnelSchema.statics.findByFunnelId = function(funnelId: string) {
return this.findOne({ 'funnelData.meta.id': funnelId });
FunnelSchema.statics.findByFunnelId = function (funnelId: string) {
return this.findOne({ "funnelData.meta.id": funnelId });
};
// Pre-save хуки
FunnelSchema.pre('save', function(next) {
FunnelSchema.pre("save", function (next) {
// Автоматически устанавливаем publishedAt при первой публикации
if (this.status === 'published' && !this.publishedAt) {
if (this.status === "published" && !this.publishedAt) {
this.publishedAt = new Date();
}
// Валидация: firstScreenId должен существовать в screens
if (this.funnelData.meta.firstScreenId) {
const firstScreenExists = this.funnelData.screens.some(
screen => screen.id === this.funnelData.meta.firstScreenId
(screen) => screen.id === this.funnelData.meta.firstScreenId
);
if (!firstScreenExists) {
return next(new Error('firstScreenId must reference an existing screen'));
return next(new Error("firstScreenId must reference an existing screen"));
}
}
next();
});
// Экспорт модели с проверкой на существование
const FunnelModel: Model<IFunnel> = mongoose.models.Funnel || mongoose.model<IFunnel>('Funnel', FunnelSchema);
// В dev окружении пересоздаём модель, чтобы подтянуть изменения схемы (enums и т.п.)
if (
process.env.NODE_ENV !== "production" &&
typeof mongoose.models.Funnel !== "undefined"
) {
try {
(
mongoose as unknown as { deleteModel: (name: string) => void }
).deleteModel("Funnel");
} catch {
// no-op
}
}
const FunnelModel: Model<IFunnel> =
mongoose.models.Funnel || mongoose.model<IFunnel>("Funnel", FunnelSchema);
export default FunnelModel;

View File

@ -106,6 +106,7 @@ class HttpClient {
// ignore
}
}
if (accessToken) headers.set("Authorization", `Bearer ${accessToken}`);
headers.set("Content-Type", "application/json");

View File

@ -9,7 +9,7 @@ export function getClientAccessToken(): string | undefined {
if (typeof window === "undefined") return undefined;
const cookies = document.cookie.split(";");
const accessTokenCookie = cookies.find(cookie =>
const accessTokenCookie = cookies.find((cookie) =>
cookie.trim().startsWith("accessToken=")
);

View File

@ -11,4 +11,7 @@ const createRoute = (
export const API_ROUTES = {
session: (id?: string) => createRoute(["session", id], ROOT_ROUTE_V2),
authorization: () => createRoute(["users", "auth"]),
paymentCheckout: () => createRoute(["payment", "checkout"], ROOT_ROUTE_V2),
paymentSingleCheckout: () => createRoute(["payment", "checkout"]),
funnel: () => createRoute(["session", "funnel"], ROOT_ROUTE_V2),
};

View File

@ -0,0 +1,22 @@
const ROOT_ROUTE = "/";
const createRoute = (
segments: Array<string | undefined>,
queryParams?: Record<string, string>
): string => {
const url = ROOT_ROUTE + segments.filter(Boolean).join("/");
if (queryParams) {
return url + "?" + new URLSearchParams(queryParams).toString();
}
return url;
};
export const ROUTES = {
home: () => createRoute([]),
// Payment
payment: (queryParams?: Record<string, string>) =>
createRoute(["payment"], queryParams),
paymentSuccess: () => createRoute(["payment", "success"]),
paymentFailed: () => createRoute(["payment", "failed"]),
};

View File

@ -0,0 +1,8 @@
import { Currency } from "@/shared/types";
export const symbolByCurrency: Record<string, string> = {
[Currency.USD]: "$",
[Currency.EUR]: "€",
[Currency.USD.toLowerCase()]: "$",
[Currency.EUR.toLowerCase()]: "€",
};

View File

@ -0,0 +1,77 @@
export enum ELottieKeys {
goal = "goal",
magnifyingGlassAndPlanet = "magnifyingGlassAndPlanet",
scalesNeutral = "scalesNeutral",
scalesHead = "scalesHead",
scalesHeart = "scalesHeart",
compass = "compass",
handWithStars = "handWithStars",
key = "key",
cloudAndStars = "cloudAndStars",
darts = "darts",
umbrella = "umbrella",
hourglass = "hourglass",
lightBulb = "lightBulb",
sun = "sun",
handSymbols = "handSymbols",
scalesNeutralPalmistry = "scalesNeutralPalmistry",
scalesHeadPalmistry = "scalesHeadPalmistry",
scalesHeartPalmistry = "scalesHeartPalmistry",
letScan = "letScan",
letScanDark = "letScanDark",
scannedPhoto = "scannedPhoto",
loaderCheckMark = "loaderCheckMark",
loaderCheckMark2 = "loaderCheckMark2",
confetti = "confetti",
}
export const lottieUrls = {
[ELottieKeys.goal]:
"https://lottie.host/a86e1531-7028-4688-a836-ea9d71dafa3b/Pe5G1g9s9L.lottie",
[ELottieKeys.magnifyingGlassAndPlanet]:
"https://lottie.host/beaa1dc6-cd60-4bbe-a222-c039b04c630f/ZktoTHROIW.lottie",
[ELottieKeys.scalesNeutral]:
"https://lottie.host/ddd2cb46-d62f-4808-a10d-1dd5ce8d42d2/6hgUBBGjaJ.lottie",
[ELottieKeys.scalesHead]:
"https://lottie.host/19fe41d7-d26f-431c-b063-8e123ce3d57a/HiucMMidQT.lottie",
[ELottieKeys.scalesHeart]:
"https://lottie.host/9eb3f7a1-83c2-495a-9342-c234bfebc40c/0T90l2xSWl.lottie",
[ELottieKeys.compass]:
"https://lottie.host/15b235d7-b8c9-487f-8d65-73143afc9ecc/czTjX9Lwp1.lottie",
[ELottieKeys.handWithStars]:
"https://lottie.host/25105d46-cc0a-4f76-9ad0-5e64e3eb0e52/OenfEsMruV.lottie",
[ELottieKeys.key]:
"https://lottie.host/a80ec293-6f3d-4d21-a19e-9dfb40b86a14/clQys1OEAL.lottie",
[ELottieKeys.cloudAndStars]:
"https://lottie.host/6010e02c-da90-4089-982c-177f3b5dbc05/fXkYv6hGPc.lottie",
[ELottieKeys.darts]:
"https://lottie.host/c3856d09-bfe9-44de-8712-f935f5deed67/rtD0j4YfnN.lottie",
[ELottieKeys.umbrella]:
"https://lottie.host/e353e80c-fd4a-4eca-a930-d9bf923466e0/G4sxbtkhIA.lottie",
[ELottieKeys.hourglass]:
"https://lottie.host/c1b52c33-1a3c-4759-9c5d-090ed2a62c77/IqHW4RCqVH.lottie",
[ELottieKeys.lightBulb]:
"https://lottie.host/07e33753-d13c-4469-ad33-26e57017b0ec/qMVfYwwLqs.lottie",
[ELottieKeys.sun]:
"https://lottie.host/8ae9682d-93d3-4988-8745-e7134daed217/lZG1RZgqaP.lottie",
[ELottieKeys.handSymbols]:
"https://lottie.host/ae56bb19-96e6-4147-ac94-6c9a5a24bd9d/bDBUSdzN5e.lottie",
[ELottieKeys.scalesNeutralPalmistry]:
"https://lottie.host/9027e5a7-d5e8-4e60-b097-ba4bf099b433/UsCKDjKVUr.lottie",
[ELottieKeys.scalesHeadPalmistry]:
"https://lottie.host/d16336c4-2622-48f8-b361-8d9d50b3c8a6/wWSM7JMCHu.lottie",
[ELottieKeys.scalesHeartPalmistry]:
"https://lottie.host/fa931c2d-07f5-4c57-a4bb-8302b411ecca/zy9ag3MyMe.lottie",
[ELottieKeys.letScan]:
"https://lottie.host/77c3c34b-4c1e-4cab-87f4-40d7534fea3d/wMg1wqtSS6.lottie", //"https://lottie.host/f87184ec-aa5e-4cf4-82a5-9ab5e60c22d5/qpgweCSCtn.lottie",
[ELottieKeys.letScanDark]:
"https://lottie.host/71623941-9182-4d58-8a1d-cb05cc5732ad/fEXKgPZQYq.lottie", //"https://lottie.host/c890243e-c61a-4e76-8b93-e8d24b25dd97/leetT4srXt.lottie",
[ELottieKeys.scannedPhoto]:
"https://lottie.host/0570b1a3-2441-486e-909b-bc2a6ceb692b/KAHTUVUb8C.lottie",
[ELottieKeys.loaderCheckMark]:
"https://lottie.host/c29ba802-17b4-4ddb-a733-5385b91394f2/qnFaLSA5p3.lottie",
[ELottieKeys.loaderCheckMark2]:
"https://lottie.host/6e249251-0469-43b2-9582-822e8f701ce2/sjRwaq20Dr.lottie",
[ELottieKeys.confetti]:
"https://lottie.host/ee592a75-4a56-4d3b-b671-b0695715a021/NYbdrg8EEb.lottie",
};

11
src/shared/types/index.ts Normal file
View File

@ -0,0 +1,11 @@
export type ActionResponse<T> = {
data: T | null;
error: string | null;
};
export enum Currency {
USD = "USD",
EUR = "EUR",
usd = "usd",
eur = "eur",
}

View File

@ -0,0 +1,52 @@
"use client";
import { IDBPDatabase, openDB } from "idb";
export enum EObjectStores {
Lottie = "lottie",
}
const objectStores: EObjectStores[] = [EObjectStores.Lottie];
let dbPromise: Promise<IDBPDatabase> | null = null;
function getDB() {
if (typeof window === "undefined") {
throw new Error("IndexedDB is unavailable on the server.");
}
if (!dbPromise) {
dbPromise = openDB("wit-store", 1, {
upgrade(db) {
db.createObjectStore("lottie");
},
});
}
return dbPromise;
}
async function get<T>(
store: EObjectStores,
key: string
): Promise<T | undefined> {
return (await getDB()).get(store, key);
}
async function set<T>(store: EObjectStores, key: string, val: T) {
return (await getDB()).put(store, val, key);
}
async function del(store: EObjectStores, key: string) {
return (await getDB()).delete(store, key);
}
async function clear() {
return Promise.all(objectStores.map(async s => (await getDB()).clear(s)));
}
async function keys() {
return Promise.all(
objectStores.map(async s => ({
objectStore: s,
keys: await (await getDB()).getAllKeys(s),
}))
);
}
const indexedDBService = { get, set, del, clear, keys };
export default indexedDBService;

18
src/shared/utils/price.ts Normal file
View File

@ -0,0 +1,18 @@
import { Currency } from "@/shared/types";
import { symbolByCurrency } from "../constants/currency";
const addCurrency = (price: number | string, currency: Currency) => {
const symbol = symbolByCurrency[currency];
if ([Currency.EUR].includes(currency)) {
return `${price} ${symbol}`;
}
return `${symbol}${price}`;
};
export const getFormattedPrice = (
price: number,
currency: Currency,
precision = 2
) => {
return addCurrency((price / 100).toFixed(precision), currency);
};