feat: Admin Wizard
feat: Wallet admin wizard feat: Notifications admin wizard feat: Twillio admin wizard feat: Commissions admin wizard feat: OperatorInfor admin wizard feat: Locales admin wizard feat: wizard admin route fix: better margin for admin wizard sidebar feat: allow FormRenderer to receive a field xs size feat: add a few flags on notifications, to reuse component parts as desired fix: wrong gql fix: missnig prop fix: eslint fix: radio styles feat: configure bitgo wallet for single cryptocurrency on wizard fix: eslint feat: set up infura wallet on wizard feat: exchange account config on wizard fix: choose wallet choose exchange DRY fix: layout fix: rebase wizard to use commissions new changes fix: typo fix: eslint fix: horizontal radios feat: radio interacts with mailgun enabled/disabled state fix: use yup to validate wizard steps fix: eslint feat: add xl size for button feat: admin wizard splash feat: use fullsize modal for wizard fix: eslint feat: Footer styles feat: wizard footer styles fix: wallet step styles fix: zeplin spec fix: zeplin styles feat: blockcypher link fix: xs can only be used on item feat: minimize wizard footer on click away feat: read blockcypher config fix: all set title styles do not match fix: no need to wrap the wrapper feat: allow to override Setup table width for wizard fix: wrapper class for wizard steps fix: layout elements for mailgun step feat: use yup to validate wizard steps style: eslint feat: operator info components open by default on wizard fix: all set table is too wide feat: full example modal feat: check if wallet has valid config feat: check if twilio has valid config
This commit is contained in:
parent
d4494dad6f
commit
1f5b84340e
34 changed files with 2096 additions and 67 deletions
BIN
new-lamassu-admin/public/fullexample.commissions.png
Normal file
BIN
new-lamassu-admin/public/fullexample.commissions.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
BIN
new-lamassu-admin/public/fullexample.locale.png
Normal file
BIN
new-lamassu-admin/public/fullexample.locale.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
new-lamassu-admin/public/fullexample.twilio.png
Normal file
BIN
new-lamassu-admin/public/fullexample.twilio.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 160 KiB |
204
new-lamassu-admin/public/wizard-background.svg
Normal file
204
new-lamassu-admin/public/wizard-background.svg
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="1440px" height="800px" viewBox="0 0 1440 800" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>welcome-page-background</title>
|
||||
<defs>
|
||||
<polygon id="path-1" points="0.620193054 0 101.619191 0 101.619191 100.386818 0.620193054 100.386818"></polygon>
|
||||
<polygon id="path-3" points="0.0225128411 0.23984597 99.9173917 0.23984597 99.9173917 98.7927534 0.0225128411 98.7927534"></polygon>
|
||||
<polygon id="path-5" points="0.80162247 0.532954466 522.863527 0.532954466 522.863527 522.05824 0.80162247 522.05824"></polygon>
|
||||
<polygon id="path-7" points="0 0.00538631356 325.54897 0.00538631356 325.54897 316.901388 0 316.901388"></polygon>
|
||||
<polygon id="path-9" points="0 0.0986886392 325.54897 0.0986886392 325.54897 317.206983 0 317.206983"></polygon>
|
||||
</defs>
|
||||
<g id="welcome-page-background" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Group-103" transform="translate(156.000000, -89.000000)">
|
||||
<path d="M229.204348,254 C229.204348,278.853349 209.057697,299 184.204348,299 C159.350998,299 139.204348,278.853349 139.204348,254 C139.204348,229.147776 159.350998,209 184.204348,209 C209.057697,209 229.204348,229.147776 229.204348,254" id="Fill-1" fill="#CCD8FF"></path>
|
||||
<path d="M228.204348,248 C228.204348,272.852224 208.056572,293 183.204348,293 C158.350998,293 138.204348,272.852224 138.204348,248 C138.204348,223.147776 158.350998,203 183.204348,203 C208.056572,203 228.204348,223.147776 228.204348,248" id="Fill-3" fill="#EBEFFF"></path>
|
||||
<path d="M205.545763,271.344802 C208.506388,268.344798 210.87851,264.771048 212.466337,260.875909 C214.051901,256.977376 214.801111,252.763112 214.750183,248.580534 C214.614375,240.187087 210.948677,232.043734 204.975368,226.385485 C201.992108,223.554097 198.4758,221.321352 194.676558,219.85926 C190.880711,218.383589 186.810984,217.712521 182.779736,217.770235 C174.700263,217.893585 166.789419,221.241005 161.146577,226.949047 C155.436962,232.58919 152.081361,240.500554 151.959134,248.580534 C151.894625,252.613734 152.572535,256.683147 154.047189,260.480964 C155.510525,264.279912 157.744575,267.79708 160.577313,270.780109 C166.238263,276.752957 174.384508,280.419502 182.779736,280.550774 C186.962637,280.601698 191.178358,279.852546 195.076062,278.265973 C198.971501,276.678268 202.545528,274.305198 205.545763,271.344802 M205.545763,271.344802 C199.613196,277.3516 191.273424,280.87782 182.779736,280.996644 C178.537984,281.055489 174.258885,280.341418 170.267248,278.784268 C166.273347,277.241829 162.577093,274.886866 159.448971,271.909495 C153.179146,265.958149 149.340293,257.388164 149.206748,248.580534 C149.154688,244.188601 149.949168,239.766113 151.610558,235.671804 C153.278739,231.58089 155.773088,227.827207 158.879707,224.682352 C162.02141,221.571447 165.778778,219.080685 169.868876,217.411502 C173.961239,215.740055 178.387463,214.952427 182.779736,215.002219 C191.590311,215.128964 200.163221,218.976574 206.115027,225.245914 C209.09376,228.374926 211.448905,232.072026 212.991463,236.067881 C214.547602,240.05921 215.26286,244.33911 215.200614,248.580534 C215.08065,257.074697 211.553025,265.412694 205.545763,271.344802" id="Fill-5" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-7" fill="#CCD8FF" points="178.603997 259 173.204348 253.599236 188.803583 238 194.204348 243.39965"></polygon>
|
||||
<polygon id="Fill-9" fill="#DDE4FB" points="178.724114 259 174.204348 254.480493 188.684582 240 193.204348 244.520596"></polygon>
|
||||
<path d="M267.204348,207.999438 C267.204348,233.405315 246.609663,254 221.203786,254 C195.799033,254 175.204348,233.405315 175.204348,207.999438 C175.204348,182.593562 195.799033,162 221.203786,162 C246.609663,162 267.204348,182.593562 267.204348,207.999438" id="Fill-11" fill="#CCD8FF"></path>
|
||||
<path d="M266.204348,201.999438 C266.204348,227.405315 245.609663,248 220.204909,248 C194.799033,248 174.204348,227.405315 174.204348,201.999438 C174.204348,176.594685 194.799033,156 220.204909,156 C245.609663,156 266.204348,176.594685 266.204348,201.999438" id="Fill-13" fill="#EBEFFF"></path>
|
||||
<path d="M244.398769,225.197623 C247.407939,222.150475 249.817745,218.521956 251.430642,214.564592 C253.041294,210.606105 253.805653,206.324385 253.755144,202.075213 C253.620456,193.548811 249.900803,185.271568 243.834199,179.51734 C240.803703,176.637421 237.229963,174.365809 233.370009,172.877588 C229.511178,171.374778 225.375112,170.690151 221.274964,170.746268 C213.060075,170.867481 205.01467,174.271532 199.274686,180.075142 C193.469603,185.812535 190.057489,193.858576 189.937392,202.075213 C189.873415,206.176237 190.565939,210.313175 192.066596,214.172896 C193.556028,218.034861 195.830023,221.607263 198.710116,224.637576 C204.466936,230.704936 212.746924,234.424364 221.274964,234.554555 C225.525515,234.603938 229.80637,233.841871 233.765096,232.229071 C237.722699,230.616271 241.351437,228.205489 244.398769,225.197623 M244.398769,225.197623 C238.373694,231.299775 229.901775,234.878911 221.274964,234.996756 C216.968292,235.055118 212.623459,234.326721 208.571574,232.744224 C204.516322,231.175195 200.765241,228.782371 197.591077,225.757669 C191.229281,219.712756 187.339022,211.012392 187.206578,202.075213 C187.15607,197.618409 187.96308,193.131301 189.650056,188.977528 C191.342644,184.827122 193.873669,181.019029 197.026507,177.828222 C200.21414,174.671084 204.02583,172.143579 208.175365,170.449971 C212.327144,168.752996 216.819012,167.952769 221.274964,168.002152 C230.214926,168.126731 238.919184,172.025734 244.964462,178.387146 C247.989345,181.56224 250.382315,185.314217 251.951438,189.369224 C253.532908,193.421987 254.262472,197.76768 254.200739,202.075213 C254.082887,210.701504 250.500167,219.172912 244.398769,225.197623" id="Fill-15" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-17" fill="#CCD8FF" points="215.604006 214 210.204348 208.600342 225.803601 193 231.204348 198.399658"></polygon>
|
||||
<polygon id="Fill-19" fill="#DDE4FB" points="215.961564 214 211.204348 209.242784 226.446013 194 231.204348 198.757216"></polygon>
|
||||
<path d="M222.204348,194.5 C222.204348,222.943392 199.146866,246 170.704911,246 C142.26183,246 119.204348,222.943392 119.204348,194.5 C119.204348,166.056608 142.26183,143 170.704911,143 C199.146866,143 222.204348,166.056608 222.204348,194.5" id="Fill-21" fill="#CCD8FF"></path>
|
||||
<path d="M168.704348,137 C197.14774,137 220.204348,160.057482 220.204348,188.499437 C220.204348,216.942518 197.14774,240 168.704348,240 C140.260956,240 117.204348,216.942518 117.204348,188.499437 C117.204348,160.057482 140.260956,137 168.704348,137" id="Fill-23" fill="#EBEFFF"></path>
|
||||
<polygon id="Fill-25" fill="#CCD8FF" points="181.204348 193.82838 175.032728 200 157.204348 182.17162 163.375967 176"></polygon>
|
||||
<polygon id="Fill-27" fill="#DDE4FB" points="182.204348 193.766262 176.970348 199 160.204348 182.233738 165.437245 177"></polygon>
|
||||
<path d="M194.37424,214.026713 C197.706715,210.608764 200.377816,206.537466 202.165974,202.097178 C203.953017,197.654633 204.808126,192.849869 204.759136,188.077829 C204.628865,178.504411 200.533696,169.189398 193.814188,162.697663 C190.457218,159.448975 186.496773,156.880717 182.210094,155.193746 C177.92787,153.492106 173.330547,152.704477 168.769966,152.760897 C159.633218,152.87938 150.671276,156.712584 144.280229,163.259611 C137.821262,169.733291 134.031171,178.815852 133.915375,188.077829 C133.85191,192.702048 134.635759,197.361247 136.313688,201.703363 C137.980482,206.047736 140.514632,210.062614 143.72129,213.463638 C150.129039,220.274712 159.322573,224.426127 168.769966,224.553637 C173.478632,224.603287 178.2207,223.736669 182.603133,221.924445 C186.984452,220.111093 191.001682,217.40404 194.37424,214.026713 M194.37424,214.026713 C187.700383,220.87164 178.316454,224.883132 168.769966,224.997102 C164.004516,225.055779 159.201209,224.224142 154.726363,222.442385 C150.248176,220.674168 146.108469,217.984042 142.611208,214.589789 C135.59887,207.806925 131.333347,198.065374 131.206417,188.077829 C131.155199,183.098162 132.054845,178.0869 133.917602,173.448012 C135.784812,168.813638 138.57505,164.561794 142.051156,160.999408 C145.562892,157.472003 149.760496,154.646468 154.333324,152.752998 C158.907266,150.856144 163.856431,149.952288 168.769966,150.001938 C178.627099,150.122678 188.241506,154.455767 194.935405,161.561356 C198.284581,165.107944 200.938982,169.303367 202.682602,173.842955 C204.440697,178.379158 205.262403,183.247112 205.201165,188.077829 C205.087595,197.752805 201.12715,207.263032 194.37424,214.026713" id="Fill-29" fill="#CCD8FF"></path>
|
||||
<path d="M266.204348,240.5 C266.204348,270.599732 241.80408,295 211.704348,295 C181.604616,295 157.204348,270.599732 157.204348,240.5 C157.204348,210.400268 181.604616,186 211.704348,186 C241.80408,186 266.204348,210.400268 266.204348,240.5" id="Fill-31" fill="#CCD8FF"></path>
|
||||
<path d="M265.204348,234.5 C265.204348,264.599732 240.80408,289 210.704348,289 C180.604616,289 156.204348,264.599732 156.204348,234.5 C156.204348,204.400268 180.604616,180 210.704348,180 C240.80408,180 265.204348,204.400268 265.204348,234.5" id="Fill-33" fill="#EBEFFF"></path>
|
||||
<path d="M236.788402,261.441702 C240.306252,257.838502 243.126293,253.544277 245.014812,248.861913 C246.902223,244.176182 247.809376,239.110164 247.759533,234.074435 C247.633262,223.97493 243.322344,214.137926 236.230153,207.273673 C232.689042,203.838742 228.504395,201.121759 223.97638,199.333621 C219.450581,197.529777 214.590276,196.689554 209.766523,196.744522 C200.102402,196.860067 190.61661,200.910862 183.854495,207.831204 C177.021491,214.677508 173.014065,224.285667 172.901086,234.074435 C172.837951,238.962088 173.675323,243.885638 175.455294,248.469285 C177.221973,253.057419 179.906881,257.294432 183.298462,260.881927 C190.078299,268.06589 199.793372,272.43191 209.766523,272.556429 C214.738699,272.605788 219.740782,271.685917 224.366268,269.77326 C228.990647,267.860602 233.229568,265.004517 236.788402,261.441702 M236.788402,261.441702 C229.743839,268.659319 219.837146,272.886236 209.766523,272.997293 C204.737857,273.055627 199.673747,272.171654 194.956327,270.288163 C190.236691,268.419255 185.877037,265.579997 182.193041,262.001476 C174.81065,254.847801 170.331371,244.585637 170.206209,234.074435 C170.156365,228.833417 171.107824,223.560989 173.070554,218.681188 C175.037715,213.804753 177.97738,209.333285 181.637008,205.585374 C185.333188,201.874482 189.752655,198.900609 194.566439,196.907182 C199.382439,194.909268 204.591649,193.953501 209.766523,194.001738 C220.148392,194.120648 230.28215,198.667276 237.345542,206.144027 C240.880008,209.876233 243.683433,214.292733 245.528754,219.073816 C247.386259,223.851533 248.261292,228.981493 248.201479,234.074435 C248.089608,244.276022 243.914929,254.307097 236.788402,261.441702" id="Fill-35" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-37" fill="#CCD8FF" points="204.89042 248 198.204348 241.313928 217.518276 222 224.204348 228.686072"></polygon>
|
||||
<polygon id="Fill-39" fill="#DDE4FB" points="203.914098 248 198.204348 242.28998 216.494597 224 222.204348 229.708886"></polygon>
|
||||
<path d="M277.204348,57 C277.204348,84.6141928 254.595559,107 226.703783,107 C198.814266,107 176.204348,84.6141928 176.204348,57 C176.204348,29.3858072 198.814266,7 226.703783,7 C254.595559,7 277.204348,29.3858072 277.204348,57" id="Fill-41" fill="#CCD8FF"></path>
|
||||
<g id="Group-45" transform="translate(175.204348, 0.000000)">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<g id="Clip-44"></g>
|
||||
<path d="M101.619191,50.1931844 C101.619191,77.9143178 79.0097498,100.386818 51.1196919,100.386818 C23.229634,100.386818 0.620193054,77.9143178 0.620193054,50.1931844 C0.620193054,22.4720511 23.229634,-0.000448888889 51.1196919,-0.000448888889 C79.0097498,-0.000448888889 101.619191,22.4720511 101.619191,50.1931844" id="Fill-43" fill="#EBEFFF" mask="url(#mask-2)"></path>
|
||||
</g>
|
||||
<path d="M93.2043478,281.5 C93.2043478,307.1816 72.3848256,328 46.7043478,328 C21.0227479,328 0.204347826,307.1816 0.204347826,281.5 C0.204347826,255.8184 21.0227479,235 46.7043478,235 C72.3848256,235 93.2043478,255.8184 93.2043478,281.5" id="Fill-52" fill="#CCD8FF"></path>
|
||||
<path d="M84.0671984,303.176414 C68.7818604,323.811564 39.6630839,328.148189 19.0279337,312.862851 C-1.60721653,297.577513 -5.94384073,268.458736 9.34149728,247.823586 C24.6257133,227.188436 53.7456118,222.851811 74.380762,238.137149 C95.0159122,253.422487 99.3525364,282.541264 84.0671984,303.176414" id="Fill-54" fill="#EBEFFF"></path>
|
||||
<polygon id="Fill-56" fill="#CCD8FF" points="35.3802117 282 34.2043478 274.309201 57.0273588 271 58.2043478 278.690799"></polygon>
|
||||
<polygon id="Fill-58" fill="#DDE4FB" points="35.2257594 282 34.2043478 275.229471 56.1829362 272 57.2043478 278.769404"></polygon>
|
||||
<path d="M70.2525697,299.051431 C73.3058447,295.957796 75.7543212,292.274039 77.3918948,288.256931 C79.0272158,284.23757 79.8032049,279.891615 79.7525234,275.577194 C79.6184992,266.920196 75.8455478,258.514338 69.6860639,252.669429 C66.6091376,249.744722 62.981473,247.436039 59.059856,245.92357 C55.1427441,244.397587 50.9418161,243.699351 46.7769283,243.75566 C38.4336376,243.877289 30.259285,247.333556 24.4286673,253.23027 C18.5316006,259.058286 15.0672428,267.23215 14.9467336,275.577194 C14.8825372,279.742959 15.5875722,283.944762 17.1125204,287.862765 C18.6273322,291.785274 20.9361532,295.413848 23.8632877,298.489464 C29.7107992,304.649706 38.1182864,308.422431 46.7769283,308.554195 C51.0916079,308.602621 55.4389489,307.827805 59.45855,306.190329 C63.4747723,304.552853 67.1587496,302.10565 70.2525697,299.051431 M70.2525697,299.051431 C64.1347572,305.245458 55.5335543,308.879663 46.7769283,308.996786 C42.4036835,309.055348 37.9943985,308.314317 33.8824446,306.707249 C29.7671119,305.113694 25.9603729,302.68451 22.7392861,299.613398 C16.2847236,293.476807 12.3394553,284.64525 12.2065573,275.577194 C12.1558759,271.055554 12.9769151,266.502381 14.6888214,262.287064 C16.4052328,258.076251 18.9742185,254.212305 22.1739065,250.975644 C25.4073821,247.771642 29.2738126,245.206189 33.4848769,243.487628 C37.6970674,241.765688 42.2550179,240.952581 46.7769283,241.002134 C55.8489055,241.12714 64.6821167,245.081182 70.8190756,251.535359 C73.8903706,254.757379 76.3208271,258.565016 77.9133504,262.681229 C79.5205151,266.794063 80.2627166,271.204211 80.2007726,275.577194 C80.0825159,284.334422 76.4469675,292.933984 70.2525697,299.051431" id="Fill-60" fill="#CCD8FF"></path>
|
||||
<path d="M408.204348,185.5 C408.204348,211.181229 387.609434,232 362.204348,232 C336.799261,232 316.204348,211.181229 316.204348,185.5 C316.204348,159.818771 336.799261,139 362.204348,139 C387.609434,139 408.204348,159.818771 408.204348,185.5" id="Fill-62" fill="#CCD8FF"></path>
|
||||
<path d="M399.067041,207.176225 C383.782506,227.811912 354.66362,232.147767 334.02786,216.863286 C313.39323,201.577676 309.05623,172.458893 324.340765,151.823206 C339.62643,131.188648 368.745315,126.851664 389.381075,142.137274 C410.015706,157.421755 414.352706,186.540538 399.067041,207.176225" id="Fill-64" fill="#EBEFFF"></path>
|
||||
<polygon id="Fill-66" fill="#CCD8FF" points="350.381185 186 349.204348 178.30871 372.027511 175 373.204348 182.69129"></polygon>
|
||||
<polygon id="Fill-68" fill="#DDE4FB" points="351.182248 186 350.204348 179.230299 371.227546 176 372.204348 182.769701"></polygon>
|
||||
<path d="M385.252853,203.051745 C388.306547,199.958563 390.75381,196.274855 392.390608,192.259045 C394.02514,188.239836 394.801599,183.894193 394.75059,179.580286 C394.615702,170.925271 390.842224,162.523014 384.682694,156.680085 C381.606329,153.755787 377.979075,151.449219 374.060507,149.938331 C370.143073,148.412709 365.943393,147.716772 361.78112,147.772311 C353.440703,147.895856 345.271447,151.350609 339.442903,157.244543 C333.549749,163.069337 330.085722,171.239236 329.964435,179.580286 C329.898691,183.744577 330.602605,187.945139 332.128319,191.862338 C333.64043,195.782938 335.948271,199.409974 338.873878,202.486154 C344.718291,208.645316 353.124451,212.4197 361.78112,212.55118 C366.095285,212.601052 370.441188,211.825773 374.460639,210.189073 C378.475556,208.552373 382.159485,206.105257 385.252853,203.051745 M385.252853,203.051745 C379.136396,209.24491 370.537537,212.878747 361.78112,212.996626 C357.409145,213.056699 352.999765,212.315423 348.886232,210.709326 C344.771566,209.11683 340.964083,206.687849 337.742628,203.617336 C331.287249,197.481978 327.340344,188.650144 327.206589,179.580286 C327.155581,175.057825 327.975113,170.504761 329.686724,166.288331 C331.405135,162.077569 333.973684,158.212508 337.173602,154.974245 C340.407526,151.771118 344.276219,149.20499 348.486101,147.486681 C352.699383,145.764973 357.258387,144.95229 361.78112,145.002162 C370.853788,145.127974 379.687285,149.083711 385.824145,155.538702 C388.894842,158.761097 391.323969,162.569486 392.91656,166.685038 C394.522753,170.798324 395.264073,175.208574 395.200597,179.580286 C395.082711,188.337312 391.446389,196.935655 385.252853,203.051745" id="Fill-70" fill="#CCD8FF"></path>
|
||||
<path d="M415.204348,171.5 C415.204348,198.28583 393.49043,220 366.70491,220 C339.918266,220 318.204348,198.28583 318.204348,171.5 C318.204348,144.71417 339.918266,123 366.70491,123 C393.49043,123 415.204348,144.71417 415.204348,171.5" id="Fill-72" fill="#CCD8FF"></path>
|
||||
<path d="M403.674044,194.366501 C387.730722,215.889089 357.360294,220.413003 335.837651,204.46972 C314.315008,188.527562 309.792207,158.156087 325.733282,136.633499 C341.676605,115.110911 372.048157,110.586997 393.570799,126.53028 C415.093442,142.472438 419.617367,172.843913 403.674044,194.366501" id="Fill-74" fill="#EBEFFF"></path>
|
||||
<polygon id="Fill-76" fill="#CCD8FF" points="353.430202 172 352.204348 163.610213 375.978493 160 377.204348 168.389787"></polygon>
|
||||
<polygon id="Fill-78" fill="#DDE4FB" points="353.270227 173 352.204348 165.552182 375.139611 162 376.204348 169.446629"></polygon>
|
||||
<path d="M389.814082,189.612327 C393.006514,186.380604 395.564756,182.529438 397.27854,178.33238 C398.987803,174.131931 399.802868,169.58898 399.751997,165.078809 C399.618602,156.03021 395.683455,147.235943 389.245458,141.114997 C386.029286,138.052829 382.236577,135.63384 378.135252,134.046803 C374.036187,132.446201 369.638679,131.710331 365.279608,131.76685 C356.543377,131.886669 347.982368,135.503848 341.876728,141.677921 C335.70213,147.781911 332.07673,156.343322 331.956901,165.078809 C331.892464,169.439771 332.63518,173.836905 334.235917,177.936751 C335.82309,182.038859 338.243415,185.832375 341.308104,189.048274 C347.431831,195.485722 356.227977,199.421665 365.279608,199.551658 C369.790162,199.601394 374.333499,198.787529 378.533175,197.07502 C382.731721,195.362512 386.582084,192.804488 389.814082,189.612327 M389.814082,189.612327 C383.420174,196.084818 374.429589,199.879465 365.279608,199.997023 C360.710269,200.054672 356.104756,199.275848 351.811252,197.593859 C347.514355,195.925436 343.540772,193.384367 340.181031,190.176381 C333.446853,183.76267 329.337614,174.541124 329.20648,165.078809 C329.155609,160.360651 330.014762,155.610843 331.802027,151.21484 C333.594944,146.821097 336.275276,142.791334 339.612407,139.414923 C342.985714,136.072423 347.020342,133.395711 351.412198,131.601816 C355.806314,129.80453 360.559918,128.953363 365.279608,129.001968 C374.746119,129.126309 383.96958,133.243111 390.383836,139.976716 C393.593225,143.338432 396.13451,147.312808 397.800815,151.609338 C399.482946,155.904739 400.264097,160.50986 400.200791,165.078809 C400.084354,174.229143 396.287123,183.217833 389.814082,189.612327" id="Fill-80" fill="#CCD8FF"></path>
|
||||
<g id="Group-84" transform="translate(321.204348, 103.000000)">
|
||||
<mask id="mask-4" fill="white">
|
||||
<use xlink:href="#path-3"></use>
|
||||
</mask>
|
||||
<g id="Clip-83"></g>
|
||||
<path d="M99.9175048,49.5162997 C99.9175048,76.7312191 77.5544069,98.7927534 49.9688772,98.7927534 C22.3833475,98.7927534 0.0225128411,76.7312191 0.0225128411,49.5162997 C0.0225128411,22.3013803 22.3833475,0.23984597 49.9688772,0.23984597 C77.5544069,0.23984597 99.9175048,22.3013803 99.9175048,49.5162997" id="Fill-82" fill="#CCD8FF" mask="url(#mask-4)"></path>
|
||||
</g>
|
||||
<path d="M410.477409,176.962082 C394.205793,198.928168 363.208983,203.544993 341.242862,187.273403 C319.275621,171.002935 314.659909,140.00393 330.930404,118.038966 C347.202019,96.0717589 378.199951,91.4549336 400.166072,107.726523 C422.133313,123.998113 426.749025,154.995996 410.477409,176.962082" id="Fill-85" fill="#EBEFFF"></path>
|
||||
<polygon id="Fill-87" fill="#CCD8FF" points="359.429201 154 358.204348 145.6092 381.978394 142 383.204348 150.389678"></polygon>
|
||||
<polygon id="Fill-89" fill="#DDE4FB" points="359.269774 154 358.204348 146.553596 381.137805 143 382.204348 150.446404"></polygon>
|
||||
<path d="M395.667007,171.465831 C398.906259,168.186399 401.501922,164.280232 403.239839,160.020895 C404.976635,155.759315 405.805228,151.151293 405.754773,146.573543 C405.622467,137.391135 401.634228,128.464353 395.101903,122.247448 C391.839105,119.136192 387.989899,116.678581 383.825626,115.065212 C379.665838,113.437269 375.201074,112.687204 370.77331,112.743263 C361.903207,112.860986 353.205774,116.535071 347.004214,122.805792 C340.732016,129.004759 337.052118,137.7017 336.933267,146.573543 C336.870477,151.003299 337.626191,155.46781 339.254226,159.629605 C340.868807,163.793643 343.327679,167.643751 346.440232,170.906365 C352.659732,177.43832 361.589261,181.427455 370.77331,181.555269 C375.35132,181.6046 379.959602,180.777174 384.221423,179.038235 C388.481001,177.301537 392.387389,174.7049 395.667007,171.465831 M395.667007,171.465831 C389.17841,178.031421 380.056029,181.882651 370.77331,181.997011 C366.139239,182.055312 361.467046,181.261521 357.112163,179.552853 C352.756158,177.859882 348.727555,175.280062 345.322359,172.025296 C338.496271,165.518007 334.335361,156.166301 334.206419,146.573543 C334.155963,141.790618 335.027164,136.9763 336.840204,132.519637 C338.657729,128.067458 341.374485,123.983024 344.758377,120.560082 C348.178149,117.171897 352.265057,114.458658 356.717487,112.640115 C361.171039,110.818208 365.990115,109.952663 370.77331,110.001994 C380.368854,110.123081 389.723331,114.291603 396.230989,121.118427 C399.48706,124.524551 402.067025,128.554047 403.760093,132.912048 C405.466615,137.266684 406.262693,141.939734 406.201025,146.573543 C406.085538,155.856857 402.231847,164.977601 395.667007,171.465831" id="Fill-91" fill="#CCD8FF"></path>
|
||||
<path d="M404.204348,160.5 C404.204348,187.838382 382.041859,210 354.703787,210 C327.365715,210 305.204348,187.838382 305.204348,160.5 C305.204348,133.161618 327.365715,111 354.703787,111 C382.041859,111 404.204348,133.161618 404.204348,160.5" id="Fill-93" fill="#CCD8FF"></path>
|
||||
<path d="M393.477781,184.962097 C377.206243,206.928166 346.209579,211.544988 324.242441,195.27341 C302.276425,179.002954 297.659614,148.003972 313.930031,126.037903 C330.20157,104.071834 361.200477,99.455012 383.166493,115.72659 C405.132509,131.998167 409.74932,162.996028 393.477781,184.962097" id="Fill-95" fill="#EBEFFF"></path>
|
||||
<polygon id="Fill-97" fill="#CCD8FF" points="342.430248 162 341.204348 153.6092 364.978448 150 366.204348 158.389678"></polygon>
|
||||
<polygon id="Fill-99" fill="#DDE4FB" points="342.27089 162 341.204348 154.553596 364.138922 151 365.204348 158.446404"></polygon>
|
||||
<path d="M378.665993,179.465831 C381.906368,176.18752 384.502032,172.281353 386.239949,168.020895 C387.975624,163.759315 388.804219,159.151293 388.753763,154.574664 C388.622578,145.392256 384.634338,136.464353 378.102011,130.247448 C374.839211,127.136192 370.988883,124.678581 366.82573,123.065212 C362.66482,121.437269 358.201175,120.687204 353.77341,120.743263 C344.902183,120.862107 336.205868,124.535071 330.003185,130.805792 C323.732106,137.004759 320.052207,145.702821 319.933355,154.574664 C319.869445,159.003298 320.62628,163.46781 322.253195,167.629605 C323.867776,171.793642 326.326649,175.643751 329.440324,178.906365 C335.659826,185.439442 344.589358,189.428576 353.77341,189.55639 C358.3503,189.6046 362.959705,188.777174 367.221527,187.039356 C371.481107,185.301537 375.387496,182.7049 378.665993,179.465831 M378.665993,179.465831 C372.178516,186.032542 363.05501,189.882651 353.77341,189.997011 C349.138216,190.055312 344.467143,189.261521 340.112258,187.553974 C335.755131,185.859882 331.727648,183.281183 328.32133,180.025296 C321.495239,173.518007 317.335449,164.167422 317.206507,154.574664 C317.15493,149.790618 318.027252,144.977422 319.839171,140.520758 C321.656697,136.067458 324.374575,131.983024 327.758469,128.560082 C331.17712,125.173018 335.265151,122.458658 339.717583,120.640115 C344.171136,118.818208 348.989092,117.952663 353.77341,118.001994 C363.368957,118.124202 372.723437,122.292724 379.231097,129.118427 C382.487169,132.525672 385.066014,136.555168 386.760203,140.912048 C388.466726,145.267806 389.261683,149.939734 389.201136,154.574664 C389.084528,163.856857 385.231957,172.978723 378.665993,179.465831" id="Fill-101" fill="#CCD8FF"></path>
|
||||
</g>
|
||||
<g id="Group-227" transform="translate(877.000000, 162.000000)">
|
||||
<g id="Group-5" transform="translate(290.000000, 287.000000)" fill="#BCC7EB">
|
||||
<polygon id="Fill-1" points="154.396031 13.4109453 194.481169 0.534068468 40.9980706 154.185792 0.91132549 167.062669"></polygon>
|
||||
<polygon id="Fill-3" points="0.91132549 167.062669 368.998071 535.553816 409.083208 522.676939 40.9980706 154.185792"></polygon>
|
||||
</g>
|
||||
<polygon id="Fill-6" fill="#6A7187" points="421.861628 332.45245 345 406.954054 351.015146 413 441 326"></polygon>
|
||||
<polygon id="Fill-7" fill="#A3AFD1" points="328.704117 422 422 332.134023 304.792566 216 210 302.656133"></polygon>
|
||||
<g id="Group-226">
|
||||
<path d="M441.040903,325.419702 L323.705647,209.142381 C319.333014,210.486423 304.514022,215.573204 304.514022,215.573204 L421.849279,331.850525 L441.040903,325.419702 Z" id="Fill-8" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-10" fill="#EBEFFF" points="441.040903 325.419702 444.204905 322.149628 323.650928 202.656896 320.486926 205.92697"></polygon>
|
||||
<polygon id="Fill-12" fill="#DDE4FB" points="306.180032 209.08788 323.649641 202.657057 227.08801 295.903992 209.618402 302.334815"></polygon>
|
||||
<polygon id="Fill-14" fill="#EBEFFF" points="326.869006 205.872468 323.650285 202.657057 227.088654 295.903992 230.307375 299.119404"></polygon>
|
||||
<path d="M347.642953,415.396242 L227.088976,295.905117 C222.716343,297.249159 209.617758,302.335941 209.617758,302.335941 L328.451329,421.827065 L347.642953,415.396242 Z" id="Fill-16" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-18" fill="#EBEFFF" points="347.642953 415.396242 350.806956 412.127776 230.252979 292.635044 227.088976 295.905117"></polygon>
|
||||
<polygon id="Fill-20" fill="#CCD8FF" points="425.736206 360.456434 371.595709 414.542872 255.564035 298.628893 309.702923 244.544062"></polygon>
|
||||
<polygon id="Fill-22" fill="#EBEFFF" points="426.020741 360.385695 371.878635 414.472133 255.846961 298.558154 309.987458 244.473323"></polygon>
|
||||
<polygon id="Stroke-24" stroke="#CCD8FF" stroke-width="2.835" points="371.879278 396.557628 273.779099 298.558314 309.988101 262.386542 408.088281 360.385855"></polygon>
|
||||
<path d="M354.229744,329.471924 C354.229744,322.13757 348.276719,316.190667 340.933207,316.190667 C333.591304,316.190667 327.639889,322.13757 327.639889,329.471924 C327.639889,336.806278 333.591304,342.754789 340.933207,342.754789 C348.276719,342.754789 354.229744,336.806278 354.229744,329.471924" id="Fill-26" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-28" fill="#CCD8FF" points="426.069505 360.38891 371.929008 414.475348 255.897334 298.561369 310.036221 244.476539"></polygon>
|
||||
<polygon id="Fill-30" fill="#EBEFFF" points="426.353879 360.318171 372.211772 414.404609 256.180098 298.49063 310.320595 244.4058"></polygon>
|
||||
<polygon id="Stroke-32" stroke="#CCD8FF" stroke-width="2.835" points="372.212416 396.490104 274.112237 298.490791 310.321239 262.319018 408.421418 360.318332"></polygon>
|
||||
<path d="M354.562881,329.4044 C354.562881,322.070047 348.609857,316.123143 341.266345,316.123143 C333.924442,316.123143 327.971417,322.070047 327.971417,329.4044 C327.971417,336.738754 333.924442,342.687266 341.266345,342.687266 C348.609857,342.687266 354.562881,336.738754 354.562881,329.4044" id="Fill-34" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-36" fill="#CCD8FF" points="426.399424 360.316724 372.258927 414.403162 256.227252 298.489183 310.36614 244.404353"></polygon>
|
||||
<polygon id="Fill-38" fill="#EBEFFF" points="426.683958 360.245985 372.541852 414.332423 256.510178 298.418444 310.650675 244.333614"></polygon>
|
||||
<polygon id="Stroke-40" stroke="#CCD8FF" stroke-width="2.835" points="372.542496 396.417757 274.442317 298.418444 310.651319 262.246672 408.751498 360.245985"></polygon>
|
||||
<path d="M354.892961,329.332214 C354.892961,321.997861 348.939937,316.050957 341.596425,316.050957 C334.254522,316.050957 328.301497,321.997861 328.301497,329.332214 C328.301497,336.666568 334.254522,342.61508 341.596425,342.61508 C348.939937,342.61508 354.892961,336.666568 354.892961,329.332214" id="Fill-42" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-44" fill="#CCD8FF" points="426.726446 360.250647 372.584339 414.335478 256.554275 298.421499 310.693162 244.336668"></polygon>
|
||||
<polygon id="Fill-46" fill="#EBEFFF" points="427.01082 360.180069 372.868713 414.264899 256.837039 298.35092 310.977536 244.26609"></polygon>
|
||||
<polygon id="Stroke-48" stroke="#CCD8FF" stroke-width="2.835" points="372.869357 396.350395 274.769178 298.351081 310.97818 262.179309 409.078359 360.178622"></polygon>
|
||||
<path d="M355.218374,329.264691 C355.218374,321.930337 349.266959,315.983433 341.923447,315.983433 C334.581544,315.983433 328.628519,321.930337 328.628519,329.264691 C328.628519,336.600652 334.581544,342.547556 341.923447,342.547556 C349.266959,342.547556 355.218374,336.600652 355.218374,329.264691" id="Fill-50" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-52" fill="#CCD8FF" points="427.059583 360.183285 372.917477 414.268115 256.887412 298.355743 311.0263 244.269306"></polygon>
|
||||
<polygon id="Fill-54" fill="#EBEFFF" points="427.344118 360.112545 373.202012 414.197376 257.170338 298.285004 311.310835 244.198566"></polygon>
|
||||
<polygon id="Stroke-56" stroke="#CCD8FF" stroke-width="2.835" points="373.202656 396.284318 275.102476 298.285004 311.311478 262.111624 409.411658 360.112545"></polygon>
|
||||
<path d="M355.551511,329.198775 C355.551511,321.864421 349.600096,315.91591 342.256584,315.91591 C334.914682,315.91591 328.961657,321.864421 328.961657,329.198775 C328.961657,336.533129 334.914682,342.480032 342.256584,342.480032 C349.600096,342.480032 355.551511,336.533129 355.551511,329.198775" id="Fill-58" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-60" fill="#CCD8FF" points="429.178145 354.684931 378.566976 412.080027 255.433194 303.725481 306.044363 246.330384"></polygon>
|
||||
<polygon id="Fill-62" fill="#EBEFFF" points="429.456404 354.596989 378.845234 411.992086 255.711452 303.635931 306.322622 246.240835"></polygon>
|
||||
<polygon id="Stroke-64" stroke="#CCD8FF" stroke-width="2.835" points="377.711601 394.114558 273.606898 302.504267 307.456577 264.118684 411.559671 355.728975"></polygon>
|
||||
<path d="M355.853266,328.277238 C355.389771,320.955746 349.071421,315.397907 341.744003,315.860926 C334.416584,316.323945 328.851416,322.635798 329.316521,329.955683 C329.780017,337.277175 336.096757,342.836621 343.424175,342.371995 C350.753203,341.908975 356.316762,335.59873 355.853266,328.277238" id="Fill-66" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-68" fill="#CCD8FF" points="427.733744 360.037144 373.591638 414.123582 257.559964 298.209603 311.700461 244.124773"></polygon>
|
||||
<polygon id="Fill-70" fill="#EBEFFF" points="428.01667 359.966566 373.876173 414.053004 257.844499 298.139025 311.983386 244.054195"></polygon>
|
||||
<polygon id="Stroke-72" stroke="#CCD8FF" stroke-width="2.835" points="373.875207 396.138338 275.775028 298.139025 311.98403 261.967252 410.084209 359.966566"></polygon>
|
||||
<path d="M356.225672,329.052795 C356.225672,321.718441 350.272648,315.771538 342.929136,315.771538 C335.587233,315.771538 329.635818,321.718441 329.635818,329.052795 C329.635818,336.387149 335.587233,342.33566 342.929136,342.33566 C350.272648,342.33566 356.225672,336.387149 356.225672,329.052795" id="Fill-74" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-76" fill="#CCD8FF" points="428.067043 359.971228 373.924937 414.056058 257.893262 298.142079 312.033759 244.057249"></polygon>
|
||||
<polygon id="Fill-78" fill="#EBEFFF" points="428.349968 359.90065 374.207862 413.98548 258.177797 298.071501 312.316685 243.986671"></polygon>
|
||||
<polygon id="Stroke-80" stroke="#CCD8FF" stroke-width="2.835" points="374.208506 396.070814 276.108327 298.071501 312.317329 261.899729 410.417508 359.899042"></polygon>
|
||||
<path d="M356.558971,328.986879 C356.558971,321.650918 350.605947,315.704014 343.262435,315.704014 C335.920532,315.704014 329.969117,321.650918 329.969117,328.986879 C329.969117,336.321233 335.920532,342.268137 343.262435,342.268137 C350.605947,342.268137 356.558971,336.321233 356.558971,328.986879" id="Fill-82" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-84" fill="#CCD8FF" points="428.395513 359.897434 374.255016 413.983872 258.223342 298.069893 312.363839 243.985063"></polygon>
|
||||
<polygon id="Fill-86" fill="#EBEFFF" points="428.679887 359.826856 374.537781 413.913294 258.507716 297.999315 312.646604 243.914485"></polygon>
|
||||
<polygon id="Stroke-88" stroke="#CCD8FF" stroke-width="2.835" points="374.538425 395.998628 276.438246 297.999315 312.647248 261.827543 410.747427 359.826856"></polygon>
|
||||
<path d="M356.88889,328.913086 C356.88889,321.578732 350.935865,315.631828 343.592353,315.631828 C336.250451,315.631828 330.299036,321.578732 330.299036,328.913086 C330.299036,336.247439 336.250451,342.195951 343.592353,342.195951 C350.935865,342.195951 356.88889,336.247439 356.88889,328.913086" id="Fill-90" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-92" fill="#CCD8FF" points="425.660888 367.190309 366.957854 416.297682 261.605896 290.618499 320.30893 241.511126"></polygon>
|
||||
<polygon id="Fill-94" fill="#EBEFFF" points="425.949929 367.144651 367.246895 416.252024 261.894937 290.57284 320.597971 241.465467"></polygon>
|
||||
<polygon id="Stroke-96" stroke="#CCD8FF" stroke-width="2.835" points="368.828736 398.408579 279.757069 292.152089 319.017418 259.309875 408.089085 365.564758"></polygon>
|
||||
<path d="M357.165539,330.029316 C357.812502,322.722293 352.408269,316.275393 345.095335,315.629095 C337.780792,314.982797 331.325647,320.381473 330.678684,327.688496 C330.031721,334.995519 335.435953,341.442419 342.750497,342.088717 C350.06504,342.735014 356.520185,337.336338 357.165539,330.029316" id="Fill-98" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-100" fill="#CCD8FF" points="429.055512 359.763995 374.915015 413.850433 258.883341 297.936454 313.022229 243.850016"></polygon>
|
||||
<polygon id="Fill-102" fill="#EBEFFF" points="429.340047 359.693417 375.197941 413.779854 259.166267 297.865875 313.306764 243.779438"></polygon>
|
||||
<polygon id="Stroke-104" stroke="#CCD8FF" stroke-width="2.835" points="375.198584 395.865189 277.098405 297.865875 313.307407 261.694103 411.407587 359.693417"></polygon>
|
||||
<path d="M357.54905,328.779646 C357.54905,321.445292 351.596025,315.496781 344.252513,315.496781 C336.91061,315.496781 330.959195,321.445292 330.959195,328.779646 C330.959195,336.114 336.91061,342.060903 344.252513,342.060903 C351.596025,342.060903 357.54905,336.114 357.54905,328.779646" id="Fill-106" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-108" fill="#CCD8FF" points="429.385592 359.691809 375.245095 413.776639 259.213421 297.86266 313.352308 243.77783"></polygon>
|
||||
<polygon id="Fill-110" fill="#EBEFFF" points="429.670127 359.62107 375.528021 413.7059 259.496346 297.793529 313.636843 243.707091"></polygon>
|
||||
<polygon id="Stroke-112" stroke="#CCD8FF" stroke-width="2.835" points="375.528664 395.791395 277.428485 297.793689 313.637487 261.620309 411.737666 359.619623"></polygon>
|
||||
<path d="M357.879129,328.707299 C357.879129,321.372945 351.926105,315.424434 344.582593,315.424434 C337.24069,315.424434 331.289275,321.372945 331.289275,328.707299 C331.289275,336.041653 337.24069,341.988557 344.582593,341.988557 C351.926105,341.988557 357.879129,336.041653 357.879129,328.707299" id="Fill-114" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-116" fill="#CCD8FF" points="429.712453 359.618015 375.571956 413.704453 259.540282 297.790474 313.67917 243.705644"></polygon>
|
||||
<polygon id="Fill-118" fill="#EBEFFF" points="429.996988 359.547276 375.854882 413.633714 259.823208 297.719735 313.963705 243.634905"></polygon>
|
||||
<polygon id="Stroke-120" stroke="#CCD8FF" stroke-width="2.835" points="375.855525 395.719209 277.755346 297.719896 313.964348 261.548123 412.064528 359.547437"></polygon>
|
||||
<path d="M358.205991,328.633506 C358.205991,321.299152 352.252966,315.352248 344.909454,315.352248 C337.567551,315.352248 331.614527,321.299152 331.614527,328.633506 C331.614527,335.967859 337.567551,341.916371 344.909454,341.916371 C352.252966,341.916371 358.205991,335.967859 358.205991,328.633506" id="Fill-122" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-124" fill="#CCD8FF" points="430.045752 359.552099 375.903645 413.636929 259.873581 297.724558 314.012468 243.63812"></polygon>
|
||||
<polygon id="Fill-126" fill="#EBEFFF" points="430.330126 359.48136 376.188019 413.56619 260.156345 297.653819 314.296842 243.567381"></polygon>
|
||||
<polygon id="Stroke-128" stroke="#CCD8FF" stroke-width="2.835" points="376.188663 395.651686 278.088484 297.65398 314.297486 261.4806 412.397665 359.479913"></polygon>
|
||||
<path d="M358.539128,328.56759 C358.539128,321.233236 352.586104,315.284724 345.242592,315.284724 C337.900689,315.284724 331.947665,321.233236 331.947665,328.56759 C331.947665,335.901943 337.900689,341.848847 345.242592,341.848847 C352.586104,341.848847 358.539128,335.901943 358.539128,328.56759" id="Fill-130" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-132" fill="#CCD8FF" points="430.375671 359.478306 376.233564 413.564743 260.2035 297.650764 314.342387 243.565934"></polygon>
|
||||
<polygon id="Fill-134" fill="#EBEFFF" points="430.660205 359.407566 376.518099 413.494004 260.486425 297.580025 314.626922 243.495195"></polygon>
|
||||
<polygon id="Stroke-136" stroke="#CCD8FF" stroke-width="2.835" points="376.518743 395.5795 278.418564 297.580186 314.627566 261.408414 412.727745 359.407727"></polygon>
|
||||
<path d="M358.867599,328.493796 C358.867599,321.159442 352.916184,315.212538 345.572672,315.212538 C338.230769,315.212538 332.277744,321.159442 332.277744,328.493796 C332.277744,335.82815 338.230769,341.776661 345.572672,341.776661 C352.916184,341.776661 358.867599,335.82815 358.867599,328.493796" id="Fill-138" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-140" fill="#CCD8FF" points="430.702693 359.41239 376.560586 413.49722 260.530522 297.584848 314.669409 243.498411"></polygon>
|
||||
<polygon id="Fill-142" fill="#EBEFFF" points="430.987067 359.341651 376.84496 413.426481 260.813286 297.514109 314.953783 243.427672"></polygon>
|
||||
<polygon id="Stroke-144" stroke="#CCD8FF" stroke-width="2.835" points="376.845604 395.511976 278.745425 297.51427 314.954427 261.34089 413.054606 359.341811"></polygon>
|
||||
<path d="M359.194621,328.42788 C359.194621,321.093526 353.243206,315.145015 345.899694,315.145015 C338.557791,315.145015 332.604766,321.093526 332.604766,328.42788 C332.604766,335.762234 338.557791,341.709137 345.899694,341.709137 C353.243206,341.709137 359.194621,335.762234 359.194621,328.42788" id="Fill-146" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-148" fill="#CCD8FF" points="431.03583 359.344866 376.893724 413.431304 260.86205 297.517325 315.002547 243.432495"></polygon>
|
||||
<polygon id="Fill-150" fill="#EBEFFF" points="431.318756 359.274127 377.178259 413.360565 261.146585 297.446586 315.287082 243.361756"></polygon>
|
||||
<polygon id="Stroke-152" stroke="#CCD8FF" stroke-width="2.835" points="377.178903 395.44606 279.078723 297.446747 315.287725 261.274974 413.387905 359.274288"></polygon>
|
||||
<path d="M359.527758,328.360356 C359.527758,321.026003 353.576343,315.077491 346.232831,315.077491 C338.890929,315.077491 332.937904,321.026003 332.937904,328.360356 C332.937904,335.69471 338.890929,341.643221 346.232831,341.643221 C353.576343,341.643221 359.527758,335.69471 359.527758,328.360356" id="Fill-154" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-156" fill="#CCD8FF" points="431.36591 359.27268 377.223804 413.35751 261.19213 297.445139 315.332627 243.358701"></polygon>
|
||||
<polygon id="Fill-158" fill="#EBEFFF" points="431.648836 359.201941 377.508339 413.286771 261.476665 297.3744 315.617162 243.287962"></polygon>
|
||||
<polygon id="Stroke-160" stroke="#CCD8FF" stroke-width="2.835" points="377.508821 395.373713 279.408642 297.3744 315.617644 261.20102 413.717824 359.201941"></polygon>
|
||||
<path d="M359.857838,328.28817 C359.857838,320.953817 353.906423,315.005305 346.562911,315.005305 C339.221008,315.005305 333.267984,320.953817 333.267984,328.28817 C333.267984,335.622524 339.221008,341.569428 346.562911,341.569428 C353.906423,341.569428 359.857838,335.622524 359.857838,328.28817" id="Fill-162" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-172" fill="#CCD8FF" points="342.560271 170.137188 288.418164 116.052358 404.451448 0.138379092 458.593554 54.2232092"></polygon>
|
||||
<polygon id="Fill-174" fill="#EBEFFF" points="342.631082 170.420627 288.488976 116.335797 404.52226 0.421817621 458.664366 54.5066477"></polygon>
|
||||
<polygon id="Stroke-176" stroke="#CCD8FF" stroke-width="2.835" points="306.421919 116.334832 404.522099 18.3355185 440.731101 54.5072908 342.630921 152.506604"></polygon>
|
||||
<path d="M373.576671,98.7034442 C380.918574,98.7034442 386.873208,92.7565406 386.873208,85.4221868 C386.873208,78.0862253 380.918574,72.1409294 373.576671,72.1409294 C366.234768,72.1409294 360.281744,78.0862253 360.281744,85.4221868 C360.281744,92.7565406 366.234768,98.7034442 373.576671,98.7034442" id="Fill-178" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-180" fill="#CCD8FF" points="113.614576 667.707175 39.2733855 686.014121 0.000160936051 526.851249 74.3429604 508.545911"></polygon>
|
||||
<polygon id="Fill-182" fill="#EBEFFF" points="113.892834 667.793509 39.5516439 686.100455 0.278419368 526.937583 74.6212188 508.632245"></polygon>
|
||||
<polygon id="Stroke-184" stroke="#CCD8FF" stroke-width="2.835" points="48.8284807 670.768729 15.625764 536.205363 65.3453476 523.962684 98.5480643 658.52605"></polygon>
|
||||
<path d="M68.4644492,604.237363 C72.26254,597.959272 70.2476207,589.795342 63.9646772,585.999548 C57.6801244,582.205363 49.5077918,584.21821 45.7080916,590.494694 C41.9100008,596.771177 43.9249202,604.938322 50.2078636,608.732508 C56.4924164,612.526694 64.6663584,610.513846 68.4644492,604.237363" id="Fill-186" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-197" fill="#CCD8FF" points="612.302611 238.37835 541.722496 268.026052 478.115741 116.916178 548.697465 87.2684761"></polygon>
|
||||
<polygon id="Fill-199" fill="#EBEFFF" points="612.591813 238.41999 542.011698 268.067692 478.404943 116.957818 548.986667 87.3101157"></polygon>
|
||||
<polygon id="Stroke-201" stroke="#CCD8FF" stroke-width="2.835" points="548.784692 251.480991 495.00952 123.724652 542.213673 103.896816 595.988845 231.653156"></polygon>
|
||||
<path d="M557.809181,182.706071 C560.583719,175.913514 557.319935,168.162765 550.521997,165.39108 C543.722448,162.621003 535.962112,165.879823 533.189184,172.672379 C530.416256,179.463329 533.67843,187.215686 540.476368,189.985763 C547.275916,192.757448 555.034643,189.498628 557.809181,182.706071" id="Fill-203" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-205" fill="#CCD8FF" points="288.693848 712.234677 227.089137 666.812165 324.535916 534.920806 386.139017 580.34171"></polygon>
|
||||
<polygon id="Fill-207" fill="#EBEFFF" points="288.805537 712.503164 227.202436 667.08226 324.647605 535.190901 386.252316 580.611805"></polygon>
|
||||
<polygon id="Stroke-209" stroke="#CCD8FF" stroke-width="2.835" points="244.933082 664.4117 327.319466 552.904442 368.520704 583.280435 286.134321 694.790908"></polygon>
|
||||
<path d="M308.707211,636.98022 C315.968646,635.88698 320.96732,629.120147 319.872954,621.867786 C318.780199,614.613817 312.0064,609.620283 304.744966,610.713523 C297.483531,611.806763 292.484857,618.573597 293.580832,625.827565 C294.673588,633.079926 301.447386,638.07346 308.707211,636.98022" id="Fill-211" fill="#CCD8FF"></path>
|
||||
<polygon id="Fill-213" fill="#CCD8FF" points="477.226408 790.23831 405.711255 817.564486 347.088689 664.457841 418.600623 637.131666"></polygon>
|
||||
<polygon id="Fill-215" fill="#EBEFFF" points="477.514001 790.288471 405.998848 817.614646 347.376282 664.508002 418.888216 637.181826"></polygon>
|
||||
<g id="Group-223" transform="translate(329.918905, 286.740873)">
|
||||
<mask id="mask-6" fill="white">
|
||||
<use xlink:href="#path-5"></use>
|
||||
</mask>
|
||||
<g id="Clip-222"></g>
|
||||
<polygon id="Fill-221" fill="#DDE4FB" mask="url(#mask-6)" points="522.863688 368.588254 369.235743 522.05824 0.80162247 154.00294 154.429567 0.532954466"></polygon>
|
||||
</g>
|
||||
<polygon id="Fill-224" fill="#BCC7EB" points="441.040903 325.419702 445.749891 325.83449 358.636816 412.857996 350.807278 412.128097"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Group-7" transform="translate(40.000000, 572.000000)">
|
||||
<g id="Group-3" transform="translate(0.207729, 0.000000)">
|
||||
<mask id="mask-8" fill="white">
|
||||
<use xlink:href="#path-7"></use>
|
||||
</mask>
|
||||
<g id="Clip-2"></g>
|
||||
<path d="M324.29049,64.5305141 L266.17857,99.2065236 L228.516424,89.6598214 L238.051117,51.9502402 L288.008735,22.1402264 L296.161962,27.8615687 L296.161962,17.275308 C295.010741,16.3499394 293.829395,15.4730475 292.63191,14.6198554 L292.325276,14.388244 L292.315593,14.3936303 C267.762306,-2.93737224 234.347844,-5.30519568 206.875619,11.0885883 C173.227685,31.1655334 160.03383,72.4871765 174.17556,107.776148 L7.18394257,274.978094 C-2.39486271,284.567887 -2.39486271,300.117097 7.18394257,309.709044 C16.7627479,319.298836 32.2934739,319.298836 41.8712033,309.709044 L205.878253,145.494348 C229.967824,160.332564 261.261672,161.606966 287.239461,146.105156 C316.173844,128.839866 329.977738,95.8680864 324.29049,64.5305141" id="Fill-1" fill="#CCD8FF" mask="url(#mask-8)"></path>
|
||||
</g>
|
||||
<g id="Group-6" transform="translate(0.207729, 10.000000)">
|
||||
<mask id="mask-10" fill="white">
|
||||
<use xlink:href="#path-9"></use>
|
||||
</mask>
|
||||
<g id="Clip-5"></g>
|
||||
<path d="M324.29049,64.6713249 L266.17857,99.3699758 L228.516424,89.8170402 L238.051117,52.0828368 L296.161962,17.3852639 C271.34185,-2.54740729 235.810002,-6.0831361 206.875619,11.1934266 C173.227685,31.2845588 160.03383,72.6331825 174.17556,107.945196 L7.18394257,275.255237 C-2.39486271,284.852369 -2.39486271,300.411732 7.18394257,310.009942 C16.7627479,319.605996 32.2934739,319.605996 41.8712033,310.009942 L205.878253,145.688023 C229.9689,160.535928 261.261672,161.811162 287.239461,146.29923 C316.173844,129.022667 329.977738,96.0293588 324.29049,64.6713249" id="Fill-4" fill="#EBEFFF" mask="url(#mask-10)"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 48 KiB |
|
|
@ -43,16 +43,20 @@ const styles = {
|
|||
flex: 1,
|
||||
padding: [[0, 24]]
|
||||
},
|
||||
content: ({ small }) => ({
|
||||
content: ({ small, xl }) => ({
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flex: 1,
|
||||
padding: small ? [[0, 16]] : [[0, 32]]
|
||||
padding: xl ? [[0, 60 + 28]] : small ? [[0, 16]] : [[0, 32]]
|
||||
}),
|
||||
button: ({ small }) => ({
|
||||
padding: 0,
|
||||
margin: small ? [[12, 12, 'auto', 'auto']] : [[16, 16, 'auto', 'auto']]
|
||||
button: ({ small, xl }) => ({
|
||||
padding: [[0, 0, xl ? 26 : 0, 0]],
|
||||
margin: xl
|
||||
? [[0, 0, 'auto', 'auto']]
|
||||
: small
|
||||
? [[12, 12, 'auto', 'auto']]
|
||||
: [[16, 16, 'auto', 'auto']]
|
||||
}),
|
||||
header: {
|
||||
display: 'flex'
|
||||
|
|
@ -70,6 +74,7 @@ const Modal = ({
|
|||
infoPanelHeight,
|
||||
title,
|
||||
small,
|
||||
xl,
|
||||
infoPanel,
|
||||
handleClose,
|
||||
children,
|
||||
|
|
@ -79,14 +84,9 @@ const Modal = ({
|
|||
closeOnBackdropClick,
|
||||
...props
|
||||
}) => {
|
||||
const classes = useStyles({
|
||||
width,
|
||||
height,
|
||||
small,
|
||||
infoPanelHeight
|
||||
})
|
||||
const classes = useStyles({ width, height, small, infoPanelHeight, xl })
|
||||
const TitleCase = small ? H4 : H1
|
||||
const closeSize = small ? 16 : 20
|
||||
const closeSize = xl ? 28 : small ? 16 : 20
|
||||
|
||||
const innerClose = (evt, reason) => {
|
||||
if (!closeOnBackdropClick && reason === 'backdropClick') return
|
||||
|
|
|
|||
|
|
@ -25,13 +25,13 @@ const BooleanCell = ({ name }) => {
|
|||
}
|
||||
|
||||
const BooleanPropertiesTable = memo(
|
||||
({ title, disabled, data, elements, save }) => {
|
||||
({ title, disabled, data, elements, save, forcedEditing = false }) => {
|
||||
const initialValues = _.fromPairs(elements.map(it => [it.name, '']))
|
||||
const schemaValidation = _.fromPairs(
|
||||
elements.map(it => [it.name, Yup.boolean().required()])
|
||||
)
|
||||
|
||||
const [editing, setEditing] = useState(false)
|
||||
const [editing, setEditing] = useState(forcedEditing)
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ import {
|
|||
spacer
|
||||
} from 'src/styling/variables'
|
||||
|
||||
const { h3 } = typographyStyles
|
||||
const { h1, h3 } = typographyStyles
|
||||
|
||||
const pickSize = size => {
|
||||
switch (size) {
|
||||
case 'xl':
|
||||
return spacer * 7.625
|
||||
case 'sm':
|
||||
return spacer * 4
|
||||
case 'lg':
|
||||
|
|
@ -28,10 +30,11 @@ export default {
|
|||
},
|
||||
button: ({ size }) => {
|
||||
const height = pickSize(size)
|
||||
const shadowSize = height / 12
|
||||
const shadowSize = size === 'xl' ? 3 : height / 12
|
||||
const padding = size === 'xl' ? 20 : height / 2
|
||||
|
||||
return {
|
||||
extend: h3,
|
||||
extend: size === 'xl' ? h1 : h3,
|
||||
border: 'none',
|
||||
color: white,
|
||||
cursor: 'pointer',
|
||||
|
|
@ -51,7 +54,7 @@ export default {
|
|||
},
|
||||
shadowSize,
|
||||
height,
|
||||
padding: `0 ${height / 2}px`,
|
||||
padding: `0 ${padding}px`,
|
||||
borderRadius: height / 4,
|
||||
boxShadow: `0 ${shadowSize}px ${secondaryColorDark}`,
|
||||
'&:hover': {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,17 @@ const SAVE_CONFIG = gql`
|
|||
|
||||
const FIELDS_WIDTH = 130
|
||||
|
||||
const Notifications = ({ name: SCREEN_KEY }) => {
|
||||
// TODO: what about 'onlySetup' 'onlyFiat'?
|
||||
const Notifications = ({
|
||||
name: SCREEN_KEY,
|
||||
displaySetup = true,
|
||||
displayTransactionAlerts = true,
|
||||
displayFiatAlerts = true,
|
||||
displayCryptoAlerts = true,
|
||||
displayOverrides = true,
|
||||
displayTitle = true,
|
||||
wizard = false
|
||||
}) => {
|
||||
const [section, setSection] = useState(null)
|
||||
const [error, setError] = useState(null)
|
||||
const [editingKey, setEditingKey] = useState(null)
|
||||
|
|
@ -92,27 +102,38 @@ const Notifications = ({ name: SCREEN_KEY }) => {
|
|||
|
||||
return (
|
||||
<NotificationsCtx.Provider value={contextValue}>
|
||||
<TitleSection title="Notifications" />
|
||||
|
||||
<Section title="Setup" error={error && !section}>
|
||||
<Setup forceDisable={!!editingKey} />
|
||||
</Section>
|
||||
|
||||
<Section title="Transaction alerts" error={error && section === 'tx'}>
|
||||
<TransactionAlerts section="tx" fieldWidth={FIELDS_WIDTH} />
|
||||
</Section>
|
||||
|
||||
<Section title="Fiat balance alerts" error={error && section === 'fiat'}>
|
||||
<FiatBalanceAlerts section="fiat" max={500} fieldWidth={50} />
|
||||
<FiatBalanceOverrides section="fiat" />
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
title="Crypto balance alerts"
|
||||
error={error && section === 'crypto'}>
|
||||
<CryptoBalanceAlerts section="crypto" fieldWidth={FIELDS_WIDTH} />
|
||||
<CryptoBalanceOverrides section="crypto" fieldWidth={FIELDS_WIDTH} />
|
||||
</Section>
|
||||
{displayTitle && <TitleSection title="Notifications" />}
|
||||
{displaySetup && (
|
||||
<Section title="Setup" error={error && !section}>
|
||||
<Setup forceDisable={!!editingKey} wizard={wizard} />
|
||||
</Section>
|
||||
)}
|
||||
{displayTransactionAlerts && (
|
||||
<Section title="Transaction alerts" error={error && section === 'tx'}>
|
||||
<TransactionAlerts section="tx" fieldWidth={FIELDS_WIDTH} />
|
||||
</Section>
|
||||
)}
|
||||
{displayFiatAlerts && (
|
||||
<Section
|
||||
title="Fiat balance alerts"
|
||||
error={error && section === 'fiat'}>
|
||||
<FiatBalanceAlerts section="fiat" max={500} fieldWidth={50} />
|
||||
{displayOverrides && <FiatBalanceOverrides section="fiat" />}
|
||||
</Section>
|
||||
)}
|
||||
{displayCryptoAlerts && (
|
||||
<Section
|
||||
title="Crypto balance alerts"
|
||||
error={error && section === 'crypto'}>
|
||||
<CryptoBalanceAlerts section="crypto" fieldWidth={FIELDS_WIDTH} />
|
||||
{displayOverrides && (
|
||||
<CryptoBalanceOverrides
|
||||
section="crypto"
|
||||
fieldWidth={FIELDS_WIDTH}
|
||||
/>
|
||||
)}
|
||||
</Section>
|
||||
)}
|
||||
</NotificationsCtx.Provider>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,16 +66,20 @@ const Row = ({ namespace, forceDisable }) => {
|
|||
const useStyles = makeStyles({
|
||||
mainTable: {
|
||||
width
|
||||
},
|
||||
wizardTable: {
|
||||
width: 930
|
||||
}
|
||||
})
|
||||
const Setup = ({ forceDisable }) => {
|
||||
const Setup = ({ wizard, forceDisable }) => {
|
||||
const widthAdjust = wizard ? 20 : 0
|
||||
const classes = useStyles()
|
||||
return (
|
||||
<Table className={classes.mainTable}>
|
||||
<Table className={wizard ? classes.wizardTable : classes.mainTable}>
|
||||
<THead>
|
||||
<Th width={channelSize}>Channel</Th>
|
||||
<Th width={channelSize - widthAdjust}>Channel</Th>
|
||||
{Object.keys(sizes).map(it => (
|
||||
<Th key={it} width={sizes[it]} textAlign="center">
|
||||
<Th key={it} width={sizes[it] - widthAdjust} textAlign="center">
|
||||
{startCase(it)}
|
||||
</Th>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -103,10 +103,10 @@ const styles = R.merge(globalStyles, contactInfoStyles)
|
|||
|
||||
const contactUseStyles = makeStyles(styles)
|
||||
|
||||
const ContactInfo = () => {
|
||||
const ContactInfo = ({ wizard }) => {
|
||||
const classes = contactUseStyles()
|
||||
|
||||
const [editing, setEditing] = useState(false)
|
||||
const [editing, setEditing] = useState(wizard || false)
|
||||
const [error, setError] = useState(null)
|
||||
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||
|
|
@ -298,14 +298,16 @@ const ContactInfo = () => {
|
|||
</Form>
|
||||
</Formik>
|
||||
</div>
|
||||
<div className={classnames(classes.section, classes.infoMessage)}>
|
||||
<WarningIcon />
|
||||
<Label1>
|
||||
Sharing your information with your customers through your machines
|
||||
allows them to contact you in case there's a problem with a machine in
|
||||
your network or a transaction.
|
||||
</Label1>
|
||||
</div>
|
||||
{!wizard && (
|
||||
<div className={classnames(classes.section, classes.infoMessage)}>
|
||||
<WarningIcon />
|
||||
<Label1>
|
||||
Sharing your information with your customers through your machines
|
||||
allows them to contact you in case there's a problem with a machine
|
||||
in your network or a transaction.
|
||||
</Label1>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ const innerRoutes = [
|
|||
}
|
||||
]
|
||||
|
||||
const Routes = () => (
|
||||
const Routes = ({ wizard }) => (
|
||||
<Switch>
|
||||
<Redirect
|
||||
exact
|
||||
|
|
@ -64,13 +64,13 @@ const Routes = () => (
|
|||
<Route exact path="/" />
|
||||
{innerRoutes.map(({ route, component: Page, key }) => (
|
||||
<Route path={route} key={key}>
|
||||
<Page name={key} />
|
||||
<Page name={key} wizard={wizard} />
|
||||
</Route>
|
||||
))}
|
||||
</Switch>
|
||||
)
|
||||
|
||||
const OperatorInfo = () => {
|
||||
const OperatorInfo = ({ wizard = false }) => {
|
||||
const classes = useStyles()
|
||||
const history = useHistory()
|
||||
const location = useLocation()
|
||||
|
|
@ -90,7 +90,7 @@ const OperatorInfo = () => {
|
|||
onClick={onClick}
|
||||
/>
|
||||
<div className={classes.content}>
|
||||
<Routes />
|
||||
<Routes wizard={wizard} />
|
||||
</div>
|
||||
</Grid>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const SAVE_CONFIG = gql`
|
|||
}
|
||||
`
|
||||
|
||||
const ReceiptPrinting = memo(() => {
|
||||
const ReceiptPrinting = memo(({ wizard }) => {
|
||||
const classes = useStyles()
|
||||
|
||||
const { data } = useQuery(GET_CONFIG)
|
||||
|
|
@ -70,6 +70,7 @@ const ReceiptPrinting = memo(() => {
|
|||
<Label2>{receiptPrintingConfig.active ? 'Yes' : 'No'}</Label2>
|
||||
</div>
|
||||
<BooleanPropertiesTable
|
||||
editing={wizard}
|
||||
title={'Visible on the receipt (optionals)'}
|
||||
data={receiptPrintingConfig}
|
||||
elements={[
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ const FormRenderer = ({
|
|||
elements,
|
||||
value,
|
||||
save,
|
||||
buttonLabel = 'Save changes'
|
||||
buttonLabel = 'Save changes',
|
||||
xs = 12
|
||||
}) => {
|
||||
const classes = useStyles()
|
||||
|
||||
|
|
@ -46,7 +47,7 @@ const FormRenderer = ({
|
|||
<Form className={classes.form}>
|
||||
<Grid container spacing={3} className={classes.grid}>
|
||||
{elements.map(({ component, code, display, inputProps }) => (
|
||||
<Grid item xs={12} key={code}>
|
||||
<Grid item xs={xs} key={code}>
|
||||
<FastField
|
||||
component={component}
|
||||
{...inputProps}
|
||||
|
|
|
|||
42
new-lamassu-admin/src/pages/Services/schemas/singlebitgo.js
Normal file
42
new-lamassu-admin/src/pages/Services/schemas/singlebitgo.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import {
|
||||
TextInput,
|
||||
SecretInput,
|
||||
Autocomplete
|
||||
} from 'src/components/inputs/formik'
|
||||
|
||||
import bitgo from './bitgo'
|
||||
|
||||
export default code => ({
|
||||
code: 'bitgo',
|
||||
name: 'BitGo',
|
||||
title: 'BitGo (Wallet)',
|
||||
elements: [
|
||||
{
|
||||
code: 'token',
|
||||
display: 'API Token',
|
||||
component: TextInput,
|
||||
face: true,
|
||||
long: true
|
||||
},
|
||||
{
|
||||
code: 'environment',
|
||||
display: 'Environment',
|
||||
component: Autocomplete,
|
||||
inputProps: {
|
||||
options: ['prod', 'test']
|
||||
},
|
||||
face: true
|
||||
},
|
||||
{
|
||||
code: `${code.toLowerCase()}WalletId`,
|
||||
display: `${code.toUpperCase()} Wallet ID`,
|
||||
component: TextInput
|
||||
},
|
||||
{
|
||||
code: `${code.toLowerCase()}WalletPassphrase`,
|
||||
display: `${code.toUpperCase()} Wallet Passphrase`,
|
||||
component: SecretInput
|
||||
}
|
||||
],
|
||||
validationSchema: bitgo.validationSchema
|
||||
})
|
||||
|
|
@ -13,7 +13,8 @@ const WalletSchema = Yup.object().shape({
|
|||
zeroConf: Yup.string().required('Required')
|
||||
})
|
||||
|
||||
const getElements = (cryptoCurrencies, accounts) => {
|
||||
const getElements = (cryptoCurrencies, accounts, wizard = false) => {
|
||||
const widthAdjust = wizard ? 11 : 0
|
||||
const viewCryptoCurrency = it =>
|
||||
R.compose(
|
||||
R.prop(['display']),
|
||||
|
|
@ -36,7 +37,7 @@ const getElements = (cryptoCurrencies, accounts) => {
|
|||
{
|
||||
name: 'id',
|
||||
header: 'Cryptocurrency',
|
||||
width: 180,
|
||||
width: 180 - widthAdjust,
|
||||
view: viewCryptoCurrency,
|
||||
size: 'sm',
|
||||
editable: false
|
||||
|
|
@ -46,7 +47,7 @@ const getElements = (cryptoCurrencies, accounts) => {
|
|||
size: 'sm',
|
||||
stripe: true,
|
||||
view: getDisplayName('ticker'),
|
||||
width: 190,
|
||||
width: 190 - widthAdjust,
|
||||
input: Autocomplete,
|
||||
inputProps: {
|
||||
options: getOptions('ticker'),
|
||||
|
|
@ -60,7 +61,7 @@ const getElements = (cryptoCurrencies, accounts) => {
|
|||
size: 'sm',
|
||||
stripe: true,
|
||||
view: getDisplayName('wallet'),
|
||||
width: 190,
|
||||
width: 190 - widthAdjust,
|
||||
input: Autocomplete,
|
||||
inputProps: {
|
||||
options: getOptions('wallet'),
|
||||
|
|
@ -74,7 +75,7 @@ const getElements = (cryptoCurrencies, accounts) => {
|
|||
size: 'sm',
|
||||
stripe: true,
|
||||
view: getDisplayName('exchange'),
|
||||
width: 190,
|
||||
width: 190 - widthAdjust,
|
||||
input: Autocomplete,
|
||||
inputProps: {
|
||||
options: getOptions('exchange'),
|
||||
|
|
@ -89,7 +90,7 @@ const getElements = (cryptoCurrencies, accounts) => {
|
|||
stripe: true,
|
||||
view: getDisplayName('zeroConf'),
|
||||
input: Autocomplete,
|
||||
width: 190,
|
||||
width: 190 - widthAdjust,
|
||||
inputProps: {
|
||||
options: getOptions('zeroConf'),
|
||||
valueProp: 'code',
|
||||
|
|
|
|||
33
new-lamassu-admin/src/pages/Wizard/Radio.styles.js
Normal file
33
new-lamassu-admin/src/pages/Wizard/Radio.styles.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { spacer, primaryColor } from 'src/styling/variables'
|
||||
|
||||
const LABEL_WIDTH = 150
|
||||
|
||||
export default {
|
||||
radioGroup: {
|
||||
flexDirection: 'row',
|
||||
width: 600
|
||||
},
|
||||
radioLabel: {
|
||||
width: LABEL_WIDTH,
|
||||
height: 48
|
||||
},
|
||||
mdForm: {
|
||||
width: 385
|
||||
},
|
||||
infoMessage: {
|
||||
display: 'flex',
|
||||
marginBottom: 20,
|
||||
'& > p': {
|
||||
width: 330,
|
||||
marginTop: 4,
|
||||
marginLeft: 16
|
||||
}
|
||||
},
|
||||
actionButton: {
|
||||
marginBottom: spacer * 4
|
||||
},
|
||||
actionButtonLink: {
|
||||
textDecoration: 'none',
|
||||
color: primaryColor
|
||||
}
|
||||
}
|
||||
263
new-lamassu-admin/src/pages/Wizard/Wizard.js
Normal file
263
new-lamassu-admin/src/pages/Wizard/Wizard.js
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
import { useQuery } from '@apollo/react-hooks'
|
||||
import { makeStyles, Dialog, DialogContent } from '@material-ui/core'
|
||||
import classnames from 'classnames'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useReducer, useEffect } from 'react'
|
||||
import { Switch, Route, useRouteMatch } from 'react-router-dom'
|
||||
|
||||
import Twilio from 'src/pages/Wizard/components/Twilio'
|
||||
import { backgroundColor } from 'src/styling/variables'
|
||||
import { fromNamespace, namespaces } from 'src/utils/config'
|
||||
|
||||
import { schema as CommissionsSchema } from '../Commissions/helper'
|
||||
import { LocaleSchema } from '../Locales/helper'
|
||||
import twilio from '../Services/schemas/twilio'
|
||||
import { WalletSchema } from '../Wallet/helper'
|
||||
|
||||
import Commissions from './components/Commissions'
|
||||
import Footer from './components/Footer'
|
||||
import Locales from './components/Locales'
|
||||
import N from './components/Notifications'
|
||||
import WizardOperatorInfo from './components/OperatorInfo'
|
||||
import Wallet from './components/Wallet'
|
||||
import Welcome from './components/Welcome'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
wrapper: {
|
||||
display: 'flex',
|
||||
flexGrow: 1,
|
||||
padding: '1rem 0',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between'
|
||||
},
|
||||
welcomeBackground: {
|
||||
background: 'url(/wizard-background.svg) no-repeat center center fixed',
|
||||
backgroundSize: 'cover',
|
||||
backgroundColor: backgroundColor
|
||||
}
|
||||
})
|
||||
|
||||
const GET_GLOBAL_CONFIG = gql`
|
||||
query getData {
|
||||
config
|
||||
accounts
|
||||
}
|
||||
`
|
||||
const validateSteps = ({ config, accounts }) => {
|
||||
const steps = [
|
||||
{
|
||||
namespace: 'welcome',
|
||||
isComplete: true
|
||||
},
|
||||
{
|
||||
namespace: namespaces.WALLETS,
|
||||
tag: 'Wallet settings',
|
||||
p: (
|
||||
<>
|
||||
Your wallet settings are the first step for this wizard. We'll start
|
||||
by setting one of cryptocurrency to get you up and running, but you
|
||||
can later setup as many cryptocurrencies as you want.
|
||||
</>
|
||||
),
|
||||
isComplete: (() => {
|
||||
const wallets = fromNamespace(namespaces.WALLETS)(config)
|
||||
const wizardCoin = Object.keys(wallets).find(
|
||||
k => k.endsWith('_wizard') && wallets[k]
|
||||
)
|
||||
const coinCode = wizardCoin && wizardCoin.replace('_wizard', '')
|
||||
const wallet = coinCode && fromNamespace(coinCode)(wallets)
|
||||
return wallet && WalletSchema.isValidSync(wallet)
|
||||
})()
|
||||
},
|
||||
{
|
||||
namespace: namespaces.LOCALE,
|
||||
tag: 'Locales',
|
||||
p: (
|
||||
<>
|
||||
From the Locales page, you can define some important default settings
|
||||
of your machines. These values will be the default values of all
|
||||
machines you'll later add to your network. Default settings keep you
|
||||
from having to enther the same values everytime you add a new machine.
|
||||
Once a machine is added, you may override some of these values in the
|
||||
overrides section.
|
||||
</>
|
||||
),
|
||||
isComplete: LocaleSchema.isValidSync(
|
||||
fromNamespace(namespaces.LOCALE)(config)
|
||||
)
|
||||
},
|
||||
{
|
||||
namespace: 'twilio',
|
||||
tag: 'Twilio (SMS service)',
|
||||
p: (
|
||||
<>
|
||||
Twilio is used for SMS operator notifications, phone number collection
|
||||
for compliance, and 1-confirmation redemptions on cash-out
|
||||
transactions.
|
||||
<br />
|
||||
You'll need to configure Twilio if you're offering cash-out or any
|
||||
compliance options
|
||||
</>
|
||||
),
|
||||
isComplete: twilio.validationSchema.isValidSync(accounts?.twilio) || R.isEmpty(accounts?.twilio)
|
||||
},
|
||||
{
|
||||
namespace: namespaces.COMMISSIONS,
|
||||
tag: 'Commissions',
|
||||
p: (
|
||||
<>
|
||||
From the Commissions page, you can define all the commissions of your
|
||||
machines. The values set here will be default values of all machines
|
||||
you'll later add to your network. Default settings keep you from
|
||||
having to enter the same values everytime you add a new machine. Once
|
||||
a machine is added, you may override these values per machine and per
|
||||
cryptocurrency in the overrides section.
|
||||
</>
|
||||
),
|
||||
isComplete: CommissionsSchema.isValidSync(
|
||||
fromNamespace(namespaces.COMMISSIONS)(config)
|
||||
)
|
||||
},
|
||||
{
|
||||
namespace: namespaces.NOTIFICATIONS,
|
||||
tag: 'Notifications',
|
||||
p: (
|
||||
<>
|
||||
Your notification settings will allow customize what notifications you
|
||||
get and where. You can later override all default balance alerts setup
|
||||
here.
|
||||
</>
|
||||
),
|
||||
isComplete: true
|
||||
},
|
||||
{
|
||||
namespace: namespaces.OPERATOR_INFO,
|
||||
tag: 'Operator info',
|
||||
p: <></>,
|
||||
isComplete: true
|
||||
}
|
||||
]
|
||||
|
||||
return steps
|
||||
}
|
||||
|
||||
const findStepByName = namespace =>
|
||||
R.findIndex(R.propEq('namespace', namespace))
|
||||
|
||||
const getNextIndex = namespace => R.compose(R.add(1), findStepByName(namespace))
|
||||
|
||||
const initialState = {
|
||||
steps: [],
|
||||
current: 'welcome',
|
||||
next: namespaces.WALLETS
|
||||
}
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'wizard/INIT':
|
||||
return { ...state, steps: validateSteps(action.payload) }
|
||||
case 'wizard/VALIDATE_STEP':
|
||||
return { ...state, steps: validateSteps(action.payload) }
|
||||
case 'wizard/SET_STEP':
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const nextIndex = getNextIndex(action.payload)(state.steps)
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const current = findStepByName(action.payload)(state.steps)
|
||||
|
||||
return {
|
||||
...state,
|
||||
...state.steps[current],
|
||||
current: action.payload,
|
||||
next:
|
||||
state.steps[current].isComplete && state.steps[nextIndex]
|
||||
? state.steps[nextIndex].namespace
|
||||
: null
|
||||
}
|
||||
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
function Wizard() {
|
||||
const { path } = useRouteMatch()
|
||||
|
||||
const { data } = useQuery(GET_GLOBAL_CONFIG)
|
||||
const [state, dispatch] = useReducer(reducer, initialState)
|
||||
const { steps } = state
|
||||
|
||||
useEffect(() => {
|
||||
data &&
|
||||
dispatch({
|
||||
type: 'wizard/INIT',
|
||||
payload: { config: data.config, accounts: data.accounts }
|
||||
})
|
||||
}, [data])
|
||||
|
||||
const classes = useStyles()
|
||||
|
||||
if (!steps || !steps.length) return <div></div>
|
||||
|
||||
return (
|
||||
<Dialog fullScreen open={true}>
|
||||
<DialogContent
|
||||
className={classnames(
|
||||
classes.wrapper,
|
||||
state.current === 'welcome' && classes.welcomeBackground
|
||||
)}>
|
||||
<Switch>
|
||||
<Route exact path={path}>
|
||||
<Welcome
|
||||
// {...{ state, dispatch }}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path={`${path}/${namespaces.WALLETS}`}>
|
||||
<Wallet
|
||||
{...{ state }}
|
||||
namespace={namespaces.WALLETS}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
</Route>
|
||||
|
||||
<Route path={`${path}/${namespaces.LOCALE}`}>
|
||||
<Locales
|
||||
{...{ state }}
|
||||
namespace={namespaces.LOCALE}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={`${path}/${namespaces.COMMISSIONS}`}>
|
||||
<Commissions
|
||||
{...{ state }}
|
||||
namespace={namespaces.COMMISSIONS}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={`${path}/twilio`}>
|
||||
<Twilio {...{ state }} namespace={'twilio'} dispatch={dispatch} />
|
||||
</Route>
|
||||
<Route path={`${path}/${namespaces.NOTIFICATIONS}`}>
|
||||
<N
|
||||
{...{ state }}
|
||||
namespace={namespaces.NOTIFICATIONS}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
</Route>
|
||||
<Route path={`${path}/${namespaces.OPERATOR_INFO}`}>
|
||||
<WizardOperatorInfo
|
||||
{...{ state }}
|
||||
namespace={namespaces.OPERATOR_INFO}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
</Route>
|
||||
</Switch>
|
||||
</DialogContent>
|
||||
{state.current !== 'welcome' && <Footer {...state} path={path}></Footer>}
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default Wizard
|
||||
71
new-lamassu-admin/src/pages/Wizard/components/AllSet.js
Normal file
71
new-lamassu-admin/src/pages/Wizard/components/AllSet.js
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { useQuery } from '@apollo/react-hooks'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import { NamespacedTable as EditableTable } from 'src/components/editableTable'
|
||||
import { P, H4 } from 'src/components/typography'
|
||||
import { getElements } from 'src/pages/Wallet/helper'
|
||||
import { fromNamespace } from 'src/utils/config'
|
||||
|
||||
const GET_INFO = gql`
|
||||
query getData {
|
||||
config
|
||||
accounts
|
||||
accountsConfig {
|
||||
code
|
||||
display
|
||||
class
|
||||
cryptos
|
||||
}
|
||||
cryptoCurrencies {
|
||||
code
|
||||
display
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const Wallet = ({ dispatch, namespace }) => {
|
||||
const { data } = useQuery(GET_INFO)
|
||||
const [dispatched, setDispatched] = useState(false)
|
||||
|
||||
const config = data?.config && fromNamespace('wallets')(data.config)
|
||||
|
||||
const wizardCoin =
|
||||
config && Object.keys(config).find(k => k.endsWith('_wizard') && config[k])
|
||||
const coinCode = wizardCoin && wizardCoin.replace('_wizard', '')
|
||||
|
||||
const accountsConfig = data?.accountsConfig
|
||||
const cryptoCurrencies =
|
||||
data?.cryptoCurrencies?.filter(({ code }) => code === coinCode) ?? []
|
||||
|
||||
useEffect(() => {
|
||||
if (dispatched || !data?.config) return
|
||||
|
||||
dispatch({
|
||||
type: 'wizard/VALIDATE_STEP',
|
||||
payload: { config: data.config, accounts: data.accounts }
|
||||
})
|
||||
setDispatched(true)
|
||||
}, [data, dispatch, dispatched])
|
||||
|
||||
return (
|
||||
<>
|
||||
<H4>All set</H4>
|
||||
<P>
|
||||
This are your wallet settings. You can later edit these and add
|
||||
additional coins.
|
||||
</P>
|
||||
<EditableTable
|
||||
rowSize="lg"
|
||||
titleLg
|
||||
name="All set"
|
||||
namespaces={R.map(R.path(['code']))(cryptoCurrencies)}
|
||||
data={config}
|
||||
elements={getElements(cryptoCurrencies, accountsConfig, true)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Wallet
|
||||
105
new-lamassu-admin/src/pages/Wizard/components/Blockcypher.js
Normal file
105
new-lamassu-admin/src/pages/Wizard/components/Blockcypher.js
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { useMutation, useQuery } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import gql from 'graphql-tag'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
import { Link } from 'src/components/buttons'
|
||||
import { RadioGroup } from 'src/components/inputs'
|
||||
import { P, H4 } from 'src/components/typography'
|
||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||
import schema from 'src/pages/Services/schemas'
|
||||
import styles from 'src/pages/Wizard/Radio.styles'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
...styles,
|
||||
radioGroup: styles.radioGroup,
|
||||
radioLabel: {
|
||||
...styles.radioLabel,
|
||||
width: 200
|
||||
}
|
||||
})
|
||||
|
||||
const GET_CONFIG = gql`
|
||||
{
|
||||
accounts
|
||||
}
|
||||
`
|
||||
const SAVE_ACCOUNTS = gql`
|
||||
mutation Save($accounts: JSONObject) {
|
||||
saveAccounts(accounts: $accounts)
|
||||
}
|
||||
`
|
||||
|
||||
const options = [
|
||||
{
|
||||
code: 'enable',
|
||||
display: 'I will enable cash-out'
|
||||
},
|
||||
{
|
||||
code: 'disable',
|
||||
display: "I won't enable cash-out"
|
||||
}
|
||||
]
|
||||
|
||||
const Blockcypher = () => {
|
||||
const { data } = useQuery(GET_CONFIG)
|
||||
const accounts = data?.accounts ?? []
|
||||
|
||||
const classes = useStyles()
|
||||
const [saveConfig] = useMutation(SAVE_ACCOUNTS)
|
||||
const [currentCode, setCurrentCode] = useState('disable')
|
||||
|
||||
useEffect(() => {
|
||||
if (!accounts?.blockcypher) return
|
||||
schema.blockcypher.validationSchema.isValidSync(accounts.blockcypher) &&
|
||||
setCurrentCode('enable')
|
||||
}, [accounts])
|
||||
|
||||
const handleRadio = cashoutOrNot => {
|
||||
setCurrentCode(cashoutOrNot)
|
||||
cashoutOrNot === 'disable' && save({})
|
||||
}
|
||||
|
||||
const save = blockcypher => {
|
||||
const accounts = { blockcypher }
|
||||
return saveConfig({ variables: { accounts } })
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<H4>Blockcypher</H4>
|
||||
<P>
|
||||
If you are enabling cash-out services,{' '}
|
||||
<Link>
|
||||
<a
|
||||
className={classes.actionButtonLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://support.lamassu.is/hc/en-us/articles/115001209472-Blockcypher">
|
||||
create a Blockcypher account.
|
||||
</a>
|
||||
</Link>
|
||||
</P>
|
||||
<RadioGroup
|
||||
labelClassName={classes.radioLabel}
|
||||
className={classes.radioGroup}
|
||||
options={options}
|
||||
value={currentCode}
|
||||
onChange={event => handleRadio(event.target.value)}
|
||||
/>
|
||||
<div className={classes.mdForm}>
|
||||
{currentCode === 'enable' && (
|
||||
<FormRenderer
|
||||
value={accounts.blockcypher}
|
||||
save={save}
|
||||
elements={schema.blockcypher.elements}
|
||||
validationSchema={schema.blockcypher.validationSchema}
|
||||
buttonLabel={'Save'}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Blockcypher
|
||||
76
new-lamassu-admin/src/pages/Wizard/components/ChooseCoin.js
Normal file
76
new-lamassu-admin/src/pages/Wizard/components/ChooseCoin.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import gql from 'graphql-tag'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
import { RadioGroup } from 'src/components/inputs'
|
||||
import { H4 } from 'src/components/typography'
|
||||
import styles from 'src/pages/Wizard/Radio.styles'
|
||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const GET_CONFIG = gql`
|
||||
{
|
||||
config
|
||||
cryptoCurrencies {
|
||||
code
|
||||
display
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const SAVE_CONFIG = gql`
|
||||
mutation Save($config: JSONObject) {
|
||||
saveConfig(config: $config)
|
||||
}
|
||||
`
|
||||
|
||||
const WalletCoin = () => {
|
||||
const classes = useStyles()
|
||||
|
||||
const { data } = useQuery(GET_CONFIG)
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG)
|
||||
const [currentCode, setCurrentCode] = useState(null)
|
||||
const cryptoCurrencies = data?.cryptoCurrencies ?? []
|
||||
|
||||
const wallets = data?.config && fromNamespace('wallets')(data.config)
|
||||
const wizardCoin =
|
||||
wallets &&
|
||||
Object.keys(wallets).find(k => k.endsWith('_wizard') && wallets[k])
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentCode(wizardCoin && wizardCoin.replace('_wizard', ''))
|
||||
}, [wizardCoin])
|
||||
|
||||
const save = code => {
|
||||
setCurrentCode(code)
|
||||
|
||||
const keys = {}
|
||||
for (const { code: c } of cryptoCurrencies) {
|
||||
keys[`${c}_wizard`] = c === code
|
||||
}
|
||||
|
||||
keys[`${code}_zeroConf`] = 'no-zero-conf'
|
||||
keys[`${code}_ticker`] = 'kraken'
|
||||
|
||||
const config = toNamespace(namespaces.WALLETS)(keys)
|
||||
return saveConfig({ variables: { config } })
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<H4>Choose your first cryptocurrency</H4>
|
||||
|
||||
<RadioGroup
|
||||
labelClassName={classes.radioLabel}
|
||||
className={classes.radioGroup}
|
||||
options={cryptoCurrencies}
|
||||
value={currentCode}
|
||||
onChange={event => save(event.target.value)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WalletCoin
|
||||
162
new-lamassu-admin/src/pages/Wizard/components/ChooseExchange.js
Normal file
162
new-lamassu-admin/src/pages/Wizard/components/ChooseExchange.js
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState, useEffect, useCallback } from 'react'
|
||||
|
||||
import { ActionButton } from 'src/components/buttons'
|
||||
import { RadioGroup } from 'src/components/inputs'
|
||||
import { H4, Info3 } from 'src/components/typography'
|
||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||
import schema from 'src/pages/Services/schemas'
|
||||
import styles from 'src/pages/Wizard/Radio.styles'
|
||||
import { ReactComponent as InverseLinkIcon } from 'src/styling/icons/action/external link/white.svg'
|
||||
import { ReactComponent as LinkIcon } from 'src/styling/icons/action/external link/zodiac.svg'
|
||||
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
||||
|
||||
import { getItems } from './getItems'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const GET_CONFIG = gql`
|
||||
{
|
||||
config
|
||||
accounts
|
||||
accountsConfig {
|
||||
code
|
||||
display
|
||||
class
|
||||
cryptos
|
||||
}
|
||||
cryptoCurrencies {
|
||||
code
|
||||
display
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const SAVE_CONFIG = gql`
|
||||
mutation Save($config: JSONObject) {
|
||||
saveConfig(config: $config)
|
||||
}
|
||||
`
|
||||
const SAVE_ACCOUNTS = gql`
|
||||
mutation Save($accounts: JSONObject) {
|
||||
saveAccounts(accounts: $accounts)
|
||||
}
|
||||
`
|
||||
const ChooseExchange = () => {
|
||||
const classes = useStyles()
|
||||
const { data } = useQuery(GET_CONFIG)
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG)
|
||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS)
|
||||
const [currentCode, setCurrentCode] = useState(null)
|
||||
const [currentName, setCurrentName] = useState(null)
|
||||
const accounts = data?.accounts ?? []
|
||||
const accountsConfig = data?.accountsConfig ?? []
|
||||
|
||||
const walletConfigs =
|
||||
data?.config && fromNamespace(namespaces.WALLETS)(data.config)
|
||||
const wizardCoin =
|
||||
walletConfigs &&
|
||||
Object.keys(walletConfigs).find(
|
||||
k => k.endsWith('_wizard') && walletConfigs[k]
|
||||
)
|
||||
|
||||
const coinCode = wizardCoin && wizardCoin.replace('_wizard', '')
|
||||
const exchanges = getItems(accountsConfig, accounts, 'exchange', coinCode)
|
||||
|
||||
const setCurrName = useCallback(
|
||||
currentCode => {
|
||||
const ex =
|
||||
currentCode &&
|
||||
R.union(exchanges.filled, exchanges.unfilled).find(
|
||||
({ code }) => code === currentCode
|
||||
)
|
||||
setCurrentName(ex && ex.display)
|
||||
},
|
||||
[exchanges]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (currentCode) return
|
||||
const exchange =
|
||||
walletConfigs && coinCode && fromNamespace(coinCode)(walletConfigs)
|
||||
setCurrentCode(exchange && exchange.exchange)
|
||||
setCurrName(exchange && exchange.exchange)
|
||||
}, [walletConfigs, coinCode, currentCode, setCurrName])
|
||||
|
||||
const save = exchange => {
|
||||
setCurrentCode(exchange)
|
||||
setCurrName(exchange)
|
||||
const config = toNamespace(`wallets_${coinCode}`)({
|
||||
active: true,
|
||||
exchange
|
||||
})
|
||||
return saveConfig({ variables: { config } })
|
||||
}
|
||||
|
||||
const saveExchange = name => exchange => {
|
||||
const accounts = { [name]: exchange }
|
||||
return saveAccounts({ variables: { accounts } })
|
||||
}
|
||||
|
||||
const supportArticles = {
|
||||
kraken:
|
||||
'https://support.lamassu.is/hc/en-us/articles/115001206891-Kraken-trading',
|
||||
itbit:
|
||||
'https://support.lamassu.is/hc/en-us/articles/360026195032-itBit-trading',
|
||||
bitstamp:
|
||||
'https://support.lamassu.is/hc/en-us/articles/115001206911-Bitstamp-trading'
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.mdForm}>
|
||||
<H4>Choose your exchange</H4>
|
||||
<RadioGroup
|
||||
labelClassName={classes.radioLabel}
|
||||
className={classes.radioGroup}
|
||||
options={R.union(exchanges.filled, exchanges.unfilled)}
|
||||
value={currentCode}
|
||||
onChange={event => save(event.target.value)}
|
||||
/>
|
||||
{['kraken', 'itbit', 'bitstamp'].includes(currentCode) && (
|
||||
<>
|
||||
<div className={classes.infoMessage}>
|
||||
<WarningIcon />
|
||||
<Info3>
|
||||
Make sure you set up {currentName} to enter the necessary
|
||||
information below. Please follow the instructions on our support
|
||||
page if you haven’t.
|
||||
</Info3>
|
||||
</div>
|
||||
<ActionButton
|
||||
className={classes.actionButton}
|
||||
color="primary"
|
||||
Icon={LinkIcon}
|
||||
InverseIcon={InverseLinkIcon}>
|
||||
<a
|
||||
className={classes.actionButtonLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={supportArticles[currentCode]}>
|
||||
{currentName} trading
|
||||
</a>
|
||||
</ActionButton>
|
||||
|
||||
<H4 noMargin>Enter exchange information</H4>
|
||||
<FormRenderer
|
||||
value={accounts[currentCode]}
|
||||
save={saveExchange(currentCode)}
|
||||
elements={schema[currentCode].elements}
|
||||
validationSchema={schema[currentCode].validationSchema}
|
||||
buttonLabel={'Confirm'}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ChooseExchange
|
||||
160
new-lamassu-admin/src/pages/Wizard/components/ChooseWallet.js
Normal file
160
new-lamassu-admin/src/pages/Wizard/components/ChooseWallet.js
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
import { ActionButton } from 'src/components/buttons'
|
||||
import { RadioGroup } from 'src/components/inputs'
|
||||
import { H4, Info3 } from 'src/components/typography'
|
||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||
import schema from 'src/pages/Services/schemas'
|
||||
import bitgo from 'src/pages/Services/schemas/singlebitgo'
|
||||
import styles from 'src/pages/Wizard/Radio.styles'
|
||||
import { ReactComponent as InverseLinkIcon } from 'src/styling/icons/action/external link/white.svg'
|
||||
import { ReactComponent as LinkIcon } from 'src/styling/icons/action/external link/zodiac.svg'
|
||||
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
||||
|
||||
import { getItems } from './getItems'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const GET_CONFIG = gql`
|
||||
{
|
||||
config
|
||||
accounts
|
||||
accountsConfig {
|
||||
code
|
||||
display
|
||||
class
|
||||
cryptos
|
||||
}
|
||||
cryptoCurrencies {
|
||||
code
|
||||
display
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const SAVE_CONFIG = gql`
|
||||
mutation Save($config: JSONObject) {
|
||||
saveConfig(config: $config)
|
||||
}
|
||||
`
|
||||
const SAVE_ACCOUNTS = gql`
|
||||
mutation Save($accounts: JSONObject) {
|
||||
saveAccounts(accounts: $accounts)
|
||||
}
|
||||
`
|
||||
const WalletCoin = () => {
|
||||
const classes = useStyles()
|
||||
const { data } = useQuery(GET_CONFIG)
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG)
|
||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS)
|
||||
const [currentCode, setCurrentCode] = useState(null)
|
||||
const accounts = data?.accounts ?? []
|
||||
const accountsConfig = data?.accountsConfig ?? []
|
||||
|
||||
const walletConfigs =
|
||||
data?.config && fromNamespace(namespaces.WALLETS)(data.config)
|
||||
const wizardCoin =
|
||||
walletConfigs &&
|
||||
Object.keys(walletConfigs).find(
|
||||
k => k.endsWith('_wizard') && walletConfigs[k]
|
||||
)
|
||||
|
||||
const coinCode = wizardCoin && wizardCoin.replace('_wizard', '')
|
||||
const wallets = getItems(accountsConfig, accounts, 'wallet', coinCode)
|
||||
|
||||
useEffect(() => {
|
||||
if (currentCode) return
|
||||
const wallet =
|
||||
walletConfigs && coinCode && fromNamespace(coinCode)(walletConfigs)
|
||||
setCurrentCode(wallet && wallet.wallet)
|
||||
}, [walletConfigs, coinCode, currentCode])
|
||||
|
||||
const save = wallet => {
|
||||
setCurrentCode(wallet)
|
||||
const config = toNamespace(`wallets_${coinCode}`)({
|
||||
active: true,
|
||||
wallet
|
||||
})
|
||||
return saveConfig({ variables: { config } })
|
||||
}
|
||||
|
||||
const saveWallet = name => wallet => {
|
||||
const accounts = { [name]: wallet }
|
||||
return saveAccounts({ variables: { accounts } })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.mdForm}>
|
||||
<H4>Choose your wallet</H4>
|
||||
<RadioGroup
|
||||
labelClassName={classes.radioLabel}
|
||||
className={classes.radioGroup}
|
||||
options={R.union(wallets.filled, wallets.unfilled)}
|
||||
value={currentCode}
|
||||
onChange={event => save(event.target.value)}
|
||||
/>
|
||||
{currentCode === 'bitgo' && (
|
||||
<>
|
||||
<div className={classes.infoMessage}>
|
||||
<WarningIcon />
|
||||
<Info3>
|
||||
Make sure you set up a BitGo wallet to enter the necessary
|
||||
information below. Please follow the instructions on our support
|
||||
page if you haven’t.
|
||||
</Info3>
|
||||
</div>
|
||||
<ActionButton
|
||||
className={classes.actionButton}
|
||||
color="primary"
|
||||
Icon={LinkIcon}
|
||||
InverseIcon={InverseLinkIcon}>
|
||||
<a
|
||||
className={classes.actionButtonLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://support.lamassu.is/hc/en-us/articles/360024455592-Setting-up-BitGo">
|
||||
Support article
|
||||
</a>
|
||||
</ActionButton>
|
||||
<H4 noMargin>Enter wallet information</H4>
|
||||
<FormRenderer
|
||||
value={accounts.bitgo}
|
||||
save={saveWallet(currentCode)}
|
||||
elements={bitgo(coinCode).elements}
|
||||
validationSchema={bitgo(coinCode).validationSchema}
|
||||
buttonLabel={'Confirm'}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{currentCode === 'bitcoind' && (
|
||||
<div className={classes.infoMessage}>
|
||||
<WarningIcon />
|
||||
<Info3>
|
||||
To setup bitcoind please read our instructions from our support
|
||||
article. We’ll later verify if your wallet is correctly set up for
|
||||
you to progress
|
||||
</Info3>
|
||||
</div>
|
||||
)}
|
||||
{currentCode === 'infura' && (
|
||||
<>
|
||||
<H4>Enter wallet information</H4>
|
||||
<FormRenderer
|
||||
value={accounts.infura}
|
||||
save={saveWallet(currentCode)}
|
||||
elements={schema.infura.elements}
|
||||
validationSchema={schema.infura.validationSchema}
|
||||
buttonLabel={'Confirm'}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WalletCoin
|
||||
80
new-lamassu-admin/src/pages/Wizard/components/Commissions.js
Normal file
80
new-lamassu-admin/src/pages/Wizard/components/Commissions.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
import { Table as EditableTable } from 'src/components/editableTable'
|
||||
import Section from 'src/components/layout/Section'
|
||||
import TitleSection from 'src/components/layout/TitleSection'
|
||||
import styles from 'src/pages/AddMachine/styles'
|
||||
import { mainFields, defaults, schema } from 'src/pages/Commissions/helper'
|
||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const GET_DATA = gql`
|
||||
query getData {
|
||||
config,
|
||||
accounts
|
||||
}
|
||||
`
|
||||
const SAVE_CONFIG = gql`
|
||||
mutation Save($config: JSONObject) {
|
||||
saveConfig(config: $config)
|
||||
}
|
||||
`
|
||||
|
||||
function Commissions({ dispatch, namespace }) {
|
||||
const classes = useStyles()
|
||||
const { data, refetch } = useQuery(GET_DATA)
|
||||
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||
refetchQueries: () => ['getData']
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
||||
}, [dispatch, namespace])
|
||||
|
||||
const config = data?.config && fromNamespace(namespace)(data.config)
|
||||
const values = config && !R.isEmpty(config) ? config : defaults
|
||||
|
||||
const save = it => {
|
||||
const config = toNamespace(namespace)(it[namespace][0])
|
||||
return saveConfig({ variables: { config } })
|
||||
.then(() => refetch())
|
||||
.then(({ data }) => {
|
||||
return dispatch({
|
||||
type: 'wizard/VALIDATE_STEP',
|
||||
payload: { accounts: data.accounts, config: data.config }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const currency = R.path(['fiatCurrency'])(
|
||||
fromNamespace(namespaces.LOCALE)(data?.config)
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<TitleSection title="Commissions" />
|
||||
<Section>
|
||||
<EditableTable
|
||||
title="Default setup"
|
||||
rowSize="lg"
|
||||
titleLg
|
||||
name="commissions"
|
||||
enableEdit
|
||||
initialValues={values}
|
||||
save={save}
|
||||
validationSchema={schema}
|
||||
data={R.of(values)}
|
||||
elements={mainFields(currency)}
|
||||
/>
|
||||
</Section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Commissions
|
||||
151
new-lamassu-admin/src/pages/Wizard/components/Footer.js
Normal file
151
new-lamassu-admin/src/pages/Wizard/components/Footer.js
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import Modal from 'src/components/Modal'
|
||||
|
||||
import { makeStyles, Drawer, Grid } from '@material-ui/core'
|
||||
import ClickAwayListener from '@material-ui/core/ClickAwayListener'
|
||||
import classnames from 'classnames'
|
||||
import * as R from 'ramda'
|
||||
import React, { useState } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
|
||||
import Stepper from 'src/components/Stepper'
|
||||
import { Button, Link } from 'src/components/buttons'
|
||||
import { P, H2, Info2 } from 'src/components/typography'
|
||||
import { spacer } from 'src/styling/variables'
|
||||
import { URI } from 'src/utils/apollo'
|
||||
|
||||
const getStepperProps = (current, steps) => ({
|
||||
steps: R.length(steps),
|
||||
currentStep: R.compose(
|
||||
R.add(1),
|
||||
R.findIndex(R.propEq('namespace', current))
|
||||
)(steps)
|
||||
})
|
||||
|
||||
const useStyles = makeStyles(() => ({
|
||||
drawer: {
|
||||
borderTop: 'none',
|
||||
boxShadow: '0 0 4px 0 rgba(0, 0, 0, 0.08)'
|
||||
},
|
||||
wrapper: {
|
||||
padding: '32px 0',
|
||||
flexGrow: 1,
|
||||
height: 264
|
||||
},
|
||||
smallWrapper: {
|
||||
height: 84
|
||||
},
|
||||
subtitle: {
|
||||
marginTop: spacer,
|
||||
marginBottom: 6,
|
||||
lineHeight: 1.25,
|
||||
display: 'inline'
|
||||
},
|
||||
modal: {
|
||||
background: 'none',
|
||||
boxShadow: 'none',
|
||||
},
|
||||
}))
|
||||
|
||||
function Footer({ next, current, steps: collection, path, tag, p }) {
|
||||
const classes = useStyles()
|
||||
const history = useHistory()
|
||||
const [open, setOpen] = useState(true)
|
||||
const [fullExample, setFullExample] = useState(false)
|
||||
|
||||
const handleClick = () => history.push(`${path}/${next}`)
|
||||
const handleClickAway = () => setOpen(false)
|
||||
const { currentStep, steps } = getStepperProps(current, collection)
|
||||
|
||||
const wrapperClassNames = {
|
||||
[classes.wrapper]: true,
|
||||
[classes.smallWrapper]: !open
|
||||
}
|
||||
|
||||
return (
|
||||
<ClickAwayListener onClickAway={handleClickAway}>
|
||||
<Drawer
|
||||
onClick={() => setOpen(true)}
|
||||
anchor={'bottom'}
|
||||
open={true}
|
||||
variant={'persistent'}
|
||||
classes={{ paperAnchorDockedBottom: classes.drawer }}>
|
||||
<div className={classnames(wrapperClassNames)}>
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justify="center"
|
||||
alignItems="baseline">
|
||||
<Grid
|
||||
item
|
||||
xs={5}
|
||||
container
|
||||
direction="column"
|
||||
justify="flex-start"
|
||||
alignItems="flex-start">
|
||||
<H2 noMargin>Setup Lamassu Admin</H2>
|
||||
{open && <Info2 className={classes.subtitle}>{tag}</Info2>}
|
||||
{open && <P>{p}</P>}
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={4}
|
||||
container
|
||||
direction="column"
|
||||
justify="flex-start"
|
||||
alignItems="flex-end"
|
||||
spacing={5}>
|
||||
<Grid item xs={12}>
|
||||
{steps && currentStep && (
|
||||
<Stepper {...{ currentStep, steps }}></Stepper>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{open && (
|
||||
<Grid
|
||||
container
|
||||
direction="row"
|
||||
justify="center"
|
||||
alignItems="baseline">
|
||||
<Grid
|
||||
item
|
||||
xs={5}
|
||||
container
|
||||
direction="column"
|
||||
justify="flex-start"
|
||||
alignItems="flex-start">
|
||||
<Link onClick={() => { setFullExample(true) }}>See full example</Link>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={4}
|
||||
container
|
||||
direction="column"
|
||||
justify="flex-start"
|
||||
alignItems="flex-end"
|
||||
spacing={5}>
|
||||
<Grid item>
|
||||
<Button size="lg" disabled={!next} onClick={handleClick}>
|
||||
Continue
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</div>
|
||||
<Modal
|
||||
closeOnEscape={true}
|
||||
closeOnBackdropClick={true}
|
||||
className={classes.modal}
|
||||
xl={true}
|
||||
width={1152 + 120 + 56}
|
||||
handleClose={() => { setFullExample(false) }}
|
||||
open={fullExample}>
|
||||
<img width={1152} src={`/fullexample.${current}.png`} alt={`${current} configuration example`} />
|
||||
</Modal>
|
||||
</Drawer>
|
||||
</ClickAwayListener>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer
|
||||
101
new-lamassu-admin/src/pages/Wizard/components/Locales.js
Normal file
101
new-lamassu-admin/src/pages/Wizard/components/Locales.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import { useQuery, useMutation } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import gql from 'graphql-tag'
|
||||
import * as R from 'ramda'
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
import { Table as EditableTable } from 'src/components/editableTable'
|
||||
import Section from 'src/components/layout/Section'
|
||||
import TitleSection from 'src/components/layout/TitleSection'
|
||||
import styles from 'src/pages/AddMachine/styles'
|
||||
import {
|
||||
mainFields,
|
||||
localeDefaults as defaults,
|
||||
LocaleSchema as schema
|
||||
} from 'src/pages/Locales/helper'
|
||||
import { fromNamespace, toNamespace } from 'src/utils/config'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const GET_DATA = gql`
|
||||
query getData {
|
||||
config
|
||||
accounts
|
||||
currencies {
|
||||
code
|
||||
display
|
||||
}
|
||||
countries {
|
||||
code
|
||||
display
|
||||
}
|
||||
cryptoCurrencies {
|
||||
code
|
||||
display
|
||||
}
|
||||
languages {
|
||||
code
|
||||
display
|
||||
}
|
||||
machines {
|
||||
name
|
||||
deviceId
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const SAVE_CONFIG = gql`
|
||||
mutation Save($config: JSONObject) {
|
||||
saveConfig(config: $config)
|
||||
}
|
||||
`
|
||||
|
||||
function Locales({ dispatch, namespace }) {
|
||||
const classes = useStyles()
|
||||
const { data, refetch } = useQuery(GET_DATA)
|
||||
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG, {
|
||||
refetchQueries: () => ['getData']
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
||||
}, [dispatch, namespace])
|
||||
|
||||
const config = data?.config && fromNamespace(namespace)(data.config)
|
||||
const locale = config && !R.isEmpty(config) ? config : defaults
|
||||
|
||||
const save = it => {
|
||||
const config = toNamespace(namespace)(it[namespace][0])
|
||||
return saveConfig({ variables: { config } })
|
||||
.then(() => refetch())
|
||||
.then(({ data }) => {
|
||||
return dispatch({
|
||||
type: 'wizard/VALIDATE_STEP',
|
||||
payload: { accounts: data.accounts, config: data.config }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<TitleSection title="Locales" />
|
||||
<Section>
|
||||
<EditableTable
|
||||
title="Default settings"
|
||||
rowSize="lg"
|
||||
titleLg
|
||||
name="locale"
|
||||
enableEdit
|
||||
initialValues={locale}
|
||||
save={save}
|
||||
validationSchema={schema}
|
||||
data={R.of(locale)}
|
||||
elements={mainFields(data)}
|
||||
/>
|
||||
</Section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Locales
|
||||
138
new-lamassu-admin/src/pages/Wizard/components/Mailgun.js
Normal file
138
new-lamassu-admin/src/pages/Wizard/components/Mailgun.js
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
import { useMutation, useQuery } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import gql from 'graphql-tag'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
import { ActionButton } from 'src/components/buttons'
|
||||
import { RadioGroup } from 'src/components/inputs'
|
||||
import { H4, Info3 } from 'src/components/typography'
|
||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||
import schema from 'src/pages/Services/schemas'
|
||||
import styles from 'src/pages/Wizard/Radio.styles'
|
||||
import { ReactComponent as InverseLinkIcon } from 'src/styling/icons/action/external link/white.svg'
|
||||
import { ReactComponent as LinkIcon } from 'src/styling/icons/action/external link/zodiac.svg'
|
||||
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||
import { fromNamespace, toNamespace, namespaces } from 'src/utils/config'
|
||||
|
||||
const useStyles = makeStyles({
|
||||
...styles,
|
||||
radioGroup: {
|
||||
...styles.radioGroup,
|
||||
width: 768
|
||||
},
|
||||
radioLabel: {
|
||||
...styles.radioLabel,
|
||||
width: 300
|
||||
}
|
||||
})
|
||||
|
||||
const GET_CONFIG = gql`
|
||||
{
|
||||
config
|
||||
accounts
|
||||
}
|
||||
`
|
||||
|
||||
const SAVE_CONFIG = gql`
|
||||
mutation Save($config: JSONObject) {
|
||||
saveConfig(config: $config)
|
||||
}
|
||||
`
|
||||
const SAVE_ACCOUNTS = gql`
|
||||
mutation Save($accounts: JSONObject) {
|
||||
saveAccounts(accounts: $accounts)
|
||||
}
|
||||
`
|
||||
|
||||
const options = [
|
||||
{
|
||||
code: 'enabled',
|
||||
display: 'Yes, send notifications to my email'
|
||||
},
|
||||
{
|
||||
code: 'disabled',
|
||||
display: "No, don't send email notifications"
|
||||
}
|
||||
]
|
||||
|
||||
const Mailgun = () => {
|
||||
const classes = useStyles()
|
||||
const { data } = useQuery(GET_CONFIG)
|
||||
const [saveConfig] = useMutation(SAVE_CONFIG)
|
||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS)
|
||||
const [emailActive, setEmailActive] = useState(false)
|
||||
const accounts = data?.accounts ?? []
|
||||
|
||||
const emailConfig =
|
||||
data?.config &&
|
||||
fromNamespace(namespaces.NOTIFICATIONS + '_email')(data.config)
|
||||
|
||||
useEffect(() => {
|
||||
if (emailActive) return
|
||||
emailConfig && setEmailActive(emailConfig?.active ? 'enabled' : 'disabled')
|
||||
}, [emailActive, emailConfig])
|
||||
|
||||
const handleRadio = enabled => {
|
||||
setEmailActive(enabled)
|
||||
save(enabled === 'enabled')
|
||||
}
|
||||
|
||||
const save = active => {
|
||||
const config = toNamespace(`notifications_email`)({ active })
|
||||
return saveConfig({ variables: { config } })
|
||||
}
|
||||
|
||||
const saveAccount = mailgun => {
|
||||
const accounts = { mailgun }
|
||||
return saveAccounts({ variables: { accounts } })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.mdForm}>
|
||||
<H4>Do you want to get notifications via email?</H4>
|
||||
<RadioGroup
|
||||
labelClassName={classes.radioLabel}
|
||||
className={classes.radioGroup}
|
||||
options={options}
|
||||
value={emailActive}
|
||||
onChange={event => handleRadio(event.target.value)}
|
||||
/>
|
||||
|
||||
<div className={classes.infoMessage}>
|
||||
<WarningIcon />
|
||||
<Info3>
|
||||
To get email notifications, you’ll need to setup Mailgun. Check out
|
||||
our article on how to set it up.
|
||||
</Info3>
|
||||
</div>
|
||||
<ActionButton
|
||||
className={classes.actionButton}
|
||||
color="primary"
|
||||
Icon={LinkIcon}
|
||||
InverseIcon={InverseLinkIcon}>
|
||||
<a
|
||||
className={classes.actionButtonLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://support.lamassu.is/hc/en-us/articles/115001203991-Email-notifications-with-Mailgun">
|
||||
Email notifications with Mailgun
|
||||
</a>
|
||||
</ActionButton>
|
||||
|
||||
{emailActive === 'enabled' && (
|
||||
<>
|
||||
<H4>Mailgun credentials</H4>
|
||||
<FormRenderer
|
||||
value={accounts.mailgun}
|
||||
save={saveAccount}
|
||||
elements={schema.mailgun.elements}
|
||||
validationSchema={schema.mailgun.validationSchema}
|
||||
buttonLabel={'Save'}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Mailgun
|
||||
109
new-lamassu-admin/src/pages/Wizard/components/Notifications.js
Normal file
109
new-lamassu-admin/src/pages/Wizard/components/Notifications.js
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import { makeStyles } from '@material-ui/core'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
import Sidebar from 'src/components/layout/Sidebar'
|
||||
import TitleSection from 'src/components/layout/TitleSection'
|
||||
import addMachineStyles from 'src/pages/AddMachine/styles'
|
||||
import Notifications from 'src/pages/Notifications/Notifications'
|
||||
import { namespaces } from 'src/utils/config'
|
||||
|
||||
import Mailgun from './Mailgun'
|
||||
|
||||
const styles = {
|
||||
...addMachineStyles,
|
||||
grid: {
|
||||
flex: 1,
|
||||
height: '100%'
|
||||
},
|
||||
content: {
|
||||
marginLeft: 48,
|
||||
paddingTop: 15
|
||||
}
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const EMAIL = 'Email'
|
||||
const SETUP_CHANNELS = 'Setup channels'
|
||||
const TRANSACTION_ALERTS = 'Transaction alerts'
|
||||
const FIAT_BALANCE_ALERTS = 'Fiat balance alerts'
|
||||
const CRYPTO_BALANCE_ALERTS = 'Crypto balance alerts'
|
||||
|
||||
const pages = [
|
||||
EMAIL,
|
||||
SETUP_CHANNELS,
|
||||
TRANSACTION_ALERTS,
|
||||
FIAT_BALANCE_ALERTS,
|
||||
CRYPTO_BALANCE_ALERTS
|
||||
]
|
||||
|
||||
const N = ({ dispatch, namespace }) => {
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
||||
}, [dispatch, namespace])
|
||||
const [selected, setSelected] = useState(EMAIL)
|
||||
const classes = useStyles()
|
||||
|
||||
const isSelected = it => selected === it
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<TitleSection title="Notifications"></TitleSection>
|
||||
<Grid container className={classes.grid}>
|
||||
<Sidebar
|
||||
data={pages}
|
||||
isSelected={isSelected}
|
||||
displayName={it => it}
|
||||
onClick={it => setSelected(it)}
|
||||
/>
|
||||
<div className={classes.content}>
|
||||
{isSelected(EMAIL) && <Mailgun />}
|
||||
{isSelected(SETUP_CHANNELS) && (
|
||||
<Notifications
|
||||
name={namespaces.NOTIFICATIONS}
|
||||
wizard={true}
|
||||
displayCryptoAlerts={false}
|
||||
displayOverrides={false}
|
||||
displayTitle={false}
|
||||
displayTransactionAlerts={false}
|
||||
displayFiatAlerts={false}
|
||||
/>
|
||||
)}
|
||||
{isSelected(TRANSACTION_ALERTS) && (
|
||||
<Notifications
|
||||
name={namespaces.NOTIFICATIONS}
|
||||
displayCryptoAlerts={false}
|
||||
displayOverrides={false}
|
||||
displayTitle={false}
|
||||
displaySetup={false}
|
||||
displayFiatAlerts={false}
|
||||
/>
|
||||
)}
|
||||
{isSelected(FIAT_BALANCE_ALERTS) && (
|
||||
<Notifications
|
||||
name={namespaces.NOTIFICATIONS}
|
||||
displayCryptoAlerts={false}
|
||||
displayOverrides={false}
|
||||
displayTitle={false}
|
||||
displayTransactionAlerts={false}
|
||||
displaySetup={false}
|
||||
/>
|
||||
)}
|
||||
{isSelected(CRYPTO_BALANCE_ALERTS) && (
|
||||
<Notifications
|
||||
name={namespaces.NOTIFICATIONS}
|
||||
displaySetup={false}
|
||||
displayOverrides={false}
|
||||
displayTitle={false}
|
||||
displayTransactionAlerts={false}
|
||||
displayFiatAlerts={false}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default N
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { makeStyles } from '@material-ui/core'
|
||||
import React, { useEffect } from 'react'
|
||||
|
||||
import styles from 'src/pages/AddMachine/styles'
|
||||
import OperatorInfo from 'src/pages/OperatorInfo/OperatorInfo'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
function WizardOperatorInfo({ dispatch, namespace }) {
|
||||
const classes = useStyles()
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
||||
}, [dispatch, namespace])
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<OperatorInfo wizard={true}></OperatorInfo>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default WizardOperatorInfo
|
||||
143
new-lamassu-admin/src/pages/Wizard/components/Twilio.js
Normal file
143
new-lamassu-admin/src/pages/Wizard/components/Twilio.js
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
import { useMutation, useQuery } from '@apollo/react-hooks'
|
||||
import { makeStyles } from '@material-ui/core'
|
||||
import classnames from 'classnames'
|
||||
import gql from 'graphql-tag'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import { H1, Label1, H4, P } from 'src/components/typography'
|
||||
import addMachineStyles from 'src/pages/AddMachine/styles'
|
||||
import {
|
||||
styles as globalStyles,
|
||||
contactInfoStyles
|
||||
} from 'src/pages/OperatorInfo/OperatorInfo.styles'
|
||||
import FormRenderer from 'src/pages/Services/FormRenderer'
|
||||
import twilio from 'src/pages/Services/schemas/twilio'
|
||||
import { ReactComponent as WarningIcon } from 'src/styling/icons/warning-icon/comet.svg'
|
||||
import { RadioGroup } from 'src/components/inputs'
|
||||
import styles from 'src/pages/Wizard/Radio.styles'
|
||||
import Tooltip from 'src/components/Tooltip'
|
||||
import { IconButton } from 'src/components/buttons'
|
||||
import { ReactComponent as HelpIcon } from 'src/styling/icons/action/help/zodiac.svg'
|
||||
|
||||
const GET_CONFIG = gql`
|
||||
{
|
||||
config
|
||||
accounts
|
||||
}
|
||||
`
|
||||
|
||||
const SAVE_ACCOUNTS = gql`
|
||||
mutation Save($accounts: JSONObject) {
|
||||
saveAccounts(accounts: $accounts)
|
||||
}
|
||||
`
|
||||
|
||||
const useStyles = makeStyles({
|
||||
...styles,
|
||||
...globalStyles,
|
||||
...contactInfoStyles,
|
||||
...addMachineStyles,
|
||||
content: {
|
||||
width: 820,
|
||||
flex: 0
|
||||
},
|
||||
radioLabel: {
|
||||
...styles.radioLabel,
|
||||
width: 280
|
||||
}
|
||||
})
|
||||
|
||||
const options = [
|
||||
{
|
||||
code: 'enable',
|
||||
display: 'Yes, I will add two-way machines'
|
||||
},
|
||||
{
|
||||
code: 'disable',
|
||||
display: "No, not for now"
|
||||
}
|
||||
]
|
||||
|
||||
function Twilio({ dispatch, namespace }) {
|
||||
const { data, refetch } = useQuery(GET_CONFIG)
|
||||
const [saveAccounts] = useMutation(SAVE_ACCOUNTS)
|
||||
const accounts = data?.accounts ?? []
|
||||
const config = data?.config ?? []
|
||||
|
||||
const [enable, setEnable] = useState('disable')
|
||||
|
||||
useEffect(() => {
|
||||
if (!accounts?.twilio) return
|
||||
twilio.validationSchema.isValidSync(accounts.twilio) && setEnable('enable')
|
||||
}, [accounts])
|
||||
|
||||
const handleRadio = enableOrNot => {
|
||||
setEnable(enableOrNot)
|
||||
enableOrNot === 'disable' && save({})
|
||||
enableOrNot === 'enable' && save({ enable: true, ...accounts?.twilio })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
||||
}, [dispatch, namespace])
|
||||
const classes = useStyles()
|
||||
|
||||
const save = twilio => {
|
||||
const accounts = { twilio }
|
||||
return saveAccounts({ variables: { accounts } })
|
||||
.then(() => refetch())
|
||||
.then(({ data }) => {
|
||||
return dispatch({
|
||||
type: 'wizard/VALIDATE_STEP',
|
||||
payload: { accounts: data.accounts, config: data.config }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.content}>
|
||||
<H1>Twilio (SMS service)</H1>
|
||||
<H4>
|
||||
Will you setup a two way machine?
|
||||
<Tooltip width={304} enableClick Button={IconButton} Icon={HelpIcon}>
|
||||
<P>
|
||||
Two-way machines allow your customers not only to buy (cash-in) but also sell cryptocurrencies (cash-out).</P>
|
||||
<P>
|
||||
To get your admin up and running, you’ll only need an SMS service for cash-out transactions. If you’re using one-way machines, select “No” to skip this step for now. You can later set it up within the Lamassu Admin.</P>
|
||||
</Tooltip>
|
||||
|
||||
</H4>
|
||||
|
||||
<RadioGroup
|
||||
labelClassName={classes.radioLabel}
|
||||
className={classes.radioGroup}
|
||||
options={options}
|
||||
value={enable}
|
||||
onChange={event => handleRadio(event.target.value)}
|
||||
/>
|
||||
|
||||
|
||||
<div className={classnames(classes.section, classes.infoMessage)}>
|
||||
<WarningIcon />
|
||||
<Label1>
|
||||
Before configuring Twilio, create an account and phone number to use
|
||||
the Admin.
|
||||
</Label1>
|
||||
</div>
|
||||
{enable === 'enable' && (
|
||||
<FormRenderer
|
||||
xs={6}
|
||||
save={save}
|
||||
value={accounts.twilio}
|
||||
elements={twilio.elements}
|
||||
validationSchema={twilio.validationSchema}
|
||||
buttonLabel={'Save'}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Twilio
|
||||
57
new-lamassu-admin/src/pages/Wizard/components/Wallet.js
Normal file
57
new-lamassu-admin/src/pages/Wizard/components/Wallet.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import { makeStyles } from '@material-ui/core'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
|
||||
import Sidebar from 'src/components/layout/Sidebar'
|
||||
import TitleSection from 'src/components/layout/TitleSection'
|
||||
import styles from 'src/pages/AddMachine/styles'
|
||||
import ChooseCoin from 'src/pages/Wizard/components/ChooseCoin'
|
||||
import ChooseWallet from 'src/pages/Wizard/components/ChooseWallet'
|
||||
|
||||
import AllSet from './AllSet'
|
||||
import Blockcypher from './Blockcypher'
|
||||
import ChooseExchange from './ChooseExchange'
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
const COIN = 'Choose cryptocurrency'
|
||||
const WALLET = 'Choose wallet'
|
||||
const EXCHANGE = 'Exchange'
|
||||
const CASHOUT_OR_NOT = 'Blockcypher'
|
||||
const ALL_SET = 'All set'
|
||||
|
||||
const pages = [COIN, WALLET, EXCHANGE, CASHOUT_OR_NOT, ALL_SET]
|
||||
|
||||
const Wallet = ({ dispatch, namespace }) => {
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'wizard/SET_STEP', payload: namespace })
|
||||
}, [dispatch, namespace])
|
||||
const [selected, setSelected] = useState(COIN)
|
||||
const classes = useStyles()
|
||||
|
||||
const isSelected = it => selected === it
|
||||
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.headerDiv}>
|
||||
<TitleSection title="Wallet settings"></TitleSection>
|
||||
</div>
|
||||
<div className={classes.contentDiv}>
|
||||
<Sidebar
|
||||
data={pages}
|
||||
isSelected={isSelected}
|
||||
displayName={it => it}
|
||||
onClick={it => setSelected(it)}
|
||||
/>
|
||||
<div className={classes.contentWrapper}>
|
||||
{isSelected(COIN) && <ChooseCoin />}
|
||||
{isSelected(WALLET) && <ChooseWallet />}
|
||||
{isSelected(EXCHANGE) && <ChooseExchange />}
|
||||
{isSelected(CASHOUT_OR_NOT) && <Blockcypher />}
|
||||
{isSelected(ALL_SET) && <AllSet dispatch={dispatch} />}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Wallet
|
||||
53
new-lamassu-admin/src/pages/Wizard/components/Welcome.js
Normal file
53
new-lamassu-admin/src/pages/Wizard/components/Welcome.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
import { makeStyles } from '@material-ui/core'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useHistory } from 'react-router-dom'
|
||||
|
||||
import { Button } from 'src/components/buttons'
|
||||
import { H1, P } from 'src/components/typography'
|
||||
import { comet } from 'src/styling/variables'
|
||||
|
||||
const styles = {
|
||||
welcome: {
|
||||
textAlign: 'center',
|
||||
paddingTop: 256
|
||||
},
|
||||
title: {
|
||||
lineHeight: 1,
|
||||
fontSize: 48
|
||||
},
|
||||
getStarted: {
|
||||
fontSize: 24,
|
||||
fontWeight: 500,
|
||||
marginBottom: 54,
|
||||
color: comet
|
||||
}
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(styles)
|
||||
|
||||
function Welcome({ dispatch }) {
|
||||
const classes = useStyles()
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({ type: 'wizard/SET_STEP', payload: 'welcome' })
|
||||
}, [dispatch])
|
||||
|
||||
const history = useHistory()
|
||||
const handleClick = () => history.push('/wizard/wallets')
|
||||
|
||||
return (
|
||||
<div className={classes.welcome}>
|
||||
<H1 className={classes.title}>Welcome to Lamassu Admin</H1>
|
||||
<P className={classes.getStarted}>
|
||||
To get started, we’ve put together wizard that will
|
||||
<br />
|
||||
help set up you need before start adding machines.
|
||||
</P>
|
||||
<Button size={'xl'} onClick={handleClick}>
|
||||
Get started
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Welcome
|
||||
21
new-lamassu-admin/src/pages/Wizard/components/getItems.js
Normal file
21
new-lamassu-admin/src/pages/Wizard/components/getItems.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import * as R from 'ramda'
|
||||
|
||||
import schema from 'src/pages/Services/schemas'
|
||||
const contains = crypto => R.compose(R.contains(crypto), R.prop('cryptos'))
|
||||
const sameClass = type => R.propEq('class', type)
|
||||
const filterConfig = (crypto, type) =>
|
||||
R.filter(it => sameClass(type)(it) && contains(crypto)(it))
|
||||
export const getItems = (accountsConfig, accounts, type, crypto) => {
|
||||
const fConfig = filterConfig(crypto, type)(accountsConfig)
|
||||
const find = code => accounts && accounts[code]
|
||||
|
||||
const [filled, unfilled] = R.partition(({ code }) => {
|
||||
const account = find(code)
|
||||
if (!schema[code]) return true
|
||||
|
||||
const { validationSchema } = schema[code]
|
||||
return validationSchema.isValidSync(account)
|
||||
})(fConfig)
|
||||
|
||||
return { filled, unfilled }
|
||||
}
|
||||
3
new-lamassu-admin/src/pages/Wizard/index.js
Normal file
3
new-lamassu-admin/src/pages/Wizard/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import Wizard from './Wizard'
|
||||
|
||||
export default Wizard
|
||||
|
|
@ -18,6 +18,7 @@ import Services from 'src/pages/Services/Services'
|
|||
import Transactions from 'src/pages/Transactions/Transactions'
|
||||
import Triggers from 'src/pages/Triggers'
|
||||
import WalletSettings from 'src/pages/Wallet/Wallet'
|
||||
import Wizard from 'src/pages/Wizard'
|
||||
import { namespaces } from 'src/utils/config'
|
||||
|
||||
const tree = [
|
||||
|
|
@ -157,6 +158,7 @@ const Routes = () => (
|
|||
<Switch>
|
||||
<Route exact path="/" />
|
||||
<Route path="/register" component={AuthRegister} />
|
||||
<Route path="/wizard" component={Wizard}></Route>
|
||||
{flattened.map(({ route, component: Page, key }) => (
|
||||
<Route path={route} key={key}>
|
||||
<Page name={key} />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue