diff --git a/apps/builder/next.config.js b/apps/builder/next.config.js index 3b24d76bc6..6a7f2fe6cd 100644 --- a/apps/builder/next.config.js +++ b/apps/builder/next.config.js @@ -1,11 +1,7 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ const { withSentryConfig } = require('@sentry/nextjs') const path = require('path') -const withTM = require('next-transpile-modules')([ - 'utils', - 'models', - 'emails', - 'bot-engine', -]) +const withTM = require('next-transpile-modules')(['utils', 'models', 'emails']) /** @type {import('next').NextConfig} */ const nextConfig = withTM({ diff --git a/apps/builder/package.json b/apps/builder/package.json index d1faf75b00..b96f688fa2 100644 --- a/apps/builder/package.json +++ b/apps/builder/package.json @@ -17,13 +17,6 @@ "@chakra-ui/css-reset": "2.0.11", "@chakra-ui/react": "2.5.0", "@chakra-ui/theme-tools": "^2.0.16", - "@codemirror/lang-css": "6.0.1", - "@codemirror/lang-html": "6.4.1", - "@codemirror/lang-javascript": "6.1.2", - "@codemirror/lang-json": "6.0.1", - "@codemirror/lint": "6.1.0", - "@codemirror/state": "6.2.0", - "@codemirror/theme-one-dark": "^6.1.0", "@dnd-kit/core": "6.0.7", "@dnd-kit/sortable": "7.0.2", "@dnd-kit/utilities": "3.2.1", @@ -52,12 +45,17 @@ "@udecode/plate-ui-toolbar": "19.2.0", "@use-gesture/react": "^10.2.24", "aws-sdk": "2.1304.0", - "bot-engine": "workspace:*", "browser-image-compression": "2.0.0", "canvas-confetti": "1.6.0", "chakra-react-select": "^4.4.3", "codemirror": "6.0.1", "@paralleldrive/cuid2": "2.0.1", + "@typebot.io/js": "workspace:*", + "@typebot.io/react": "workspace:*", + "@uiw/codemirror-extensions-langs": "^4.19.7", + "@uiw/codemirror-theme-github": "^4.19.7", + "@uiw/codemirror-theme-tokyo-night": "^4.19.7", + "@uiw/react-codemirror": "^4.19.7", "deep-object-diff": "1.1.9", "dequal": "2.0.3", "emails": "workspace:*", @@ -94,7 +92,6 @@ "swr": "2.0.3", "tinycolor2": "1.5.2", "trpc-openapi": "1.1.2", - "typebot-js": "workspace:*", "use-debounce": "9.0.3" }, "devDependencies": { diff --git a/apps/builder/public/bots/onboarding-dark.json b/apps/builder/public/bots/onboarding-dark.json index edd6b376f8..314b6f51a5 100644 --- a/apps/builder/public/bots/onboarding-dark.json +++ b/apps/builder/public/bots/onboarding-dark.json @@ -6,21 +6,22 @@ "folderId": null, "groups": [ { - "id": "cl1265zct0000mb1a6bir36w7", + "id": "clcueadzi00043b6s1r8wnql8", + "title": "Start", "blocks": [ { "id": "cl1265zct0001mb1afel460do", "type": "start", "label": "Start", - "groupId": "cl1265zct0000mb1a6bir36w7", - "outgoingEdgeId": "cl1266kt100082e6d1wks5dtp" + "groupId": "clcueadzi00043b6s1r8wnql8", + "outgoingEdgeId": "clcuefdfv000r3b6sqzv3prz3" } ], - "title": "Start", "graphCoordinates": { "x": 0, "y": 0 } }, { - "id": "cl1266bah00032e6dgdnj4vgz", + "id": "clcueadzi00063b6sch7b1f32", + "title": "Name", "blocks": [ { "id": "cl1266bam00042e6dm0gn22vy", @@ -40,47 +41,39 @@ ], "logicalOperator": "AND" }, - "outgoingEdgeId": "cl12bk3j6000c2e69bak89ja9" + "outgoingEdgeId": "clcueadzi000l3b6smlf218u7" } ], - "groupId": "cl1266bah00032e6dgdnj4vgz", - "outgoingEdgeId": "cl12bnfyd000g2e69g7lr3czq" - } - ], - "title": "Group #1", - "graphCoordinates": { "x": 266, "y": 162 } - }, - { - "id": "cl1267q1z000d2e6d949f2ge4", - "blocks": [ + "groupId": "clcueadzi00063b6sch7b1f32" + }, { "id": "cl1267q2c000e2e6dynjeg83n", "type": "text", - "groupId": "cl1267q1z000d2e6d949f2ge4", "content": { "html": "
Welcome πŸ‘‹
", "richText": [ { "type": "p", "children": [{ "text": "Welcome πŸ‘‹" }] } ], "plainText": "Welcome πŸ‘‹" - } + }, + "groupId": "clcueadzi00063b6sch7b1f32" }, { "id": "cl1267y1u000f2e6d4rlglv6g", "type": "text", - "groupId": "cl1267q1z000d2e6d949f2ge4", "content": { "html": "
What's your name?
", "richText": [ { "type": "p", "children": [{ "text": "What's your name?" }] } ], "plainText": "What's your name?" - } + }, + "groupId": "clcueadzi00063b6sch7b1f32" }, { "id": "cl126820m000g2e6dfleq78bt", "type": "text input", - "groupId": "cl1267q1z000d2e6d949f2ge4", + "groupId": "clcueadzi00063b6sch7b1f32", "options": { "isLong": false, "labels": { @@ -88,41 +81,31 @@ "placeholder": "Type your answer..." }, "variableId": "cl126f4hf000i2e6d8zvzc3t1" - } - }, - { - "id": "cl1289y1s00142e6dvbkpvbje", - "type": "Code", - "groupId": "cl1267q1z000d2e6d949f2ge4", - "options": { - "name": "Store Name in DB", - "content": "postMessage({from: \"typebot\", action: \"storeName\", content: {{Name}}}, \"*\")" }, - "outgoingEdgeId": "cl12bk56s000d2e69oll3nqxm" + "outgoingEdgeId": "clcuecvjo000q3b6s42ouw3zz" } ], - "title": "Group #3", - "graphCoordinates": { "x": 269, "y": 381 } + "graphCoordinates": { "x": 328.22861564828236, "y": -1.7421511890097776 } }, { - "id": "cl126ixoq000p2e6dfbz9sype", + "id": "clcueadzi00073b6sqz8n5vxp", + "title": "Company", "blocks": [ { "id": "cl1266v6f000a2e6db7wj3ux7", "type": "text", - "groupId": "cl126ixoq000p2e6dfbz9sype", "content": { "html": "
Welcome {{Name}} πŸ‘‹
", "richText": [ { "type": "p", "children": [{ "text": "Welcome {{Name}} πŸ‘‹" }] } ], "plainText": "Welcome {{Name}} πŸ‘‹" - } + }, + "groupId": "clcueadzi00073b6sqz8n5vxp" }, { "id": "cl126hb9m000l2e6d5qk3mohn", "type": "text", - "groupId": "cl126ixoq000p2e6dfbz9sype", "content": { "html": "
I'm super pumped that you've decided to try out Typebot 😍
", "richText": [ @@ -136,12 +119,12 @@ } ], "plainText": "I'm super pumped that you've decided to try out Typebot 😍" - } + }, + "groupId": "clcueadzi00073b6sqz8n5vxp" }, { "id": "cl126hpw1000m2e6dneousygl", "type": "text", - "groupId": "cl126ixoq000p2e6dfbz9sype", "content": { "html": "
You are small steps away from meaningful, hyper-personalized experience for your users
", "richText": [ @@ -155,12 +138,12 @@ } ], "plainText": "You are small steps away from meaningful, hyper-personalized experience for your users" - } + }, + "groupId": "clcueadzi00073b6sqz8n5vxp" }, { "id": "cl126guhd000k2e6d6ypkex9z", "type": "text", - "groupId": "cl126ixoq000p2e6dfbz9sype", "content": { "html": "
Let's get you set up for your Typebot journey.
", "richText": [ @@ -172,12 +155,12 @@ } ], "plainText": "Let's get you set up for your Typebot journey." - } + }, + "groupId": "clcueadzi00073b6sqz8n5vxp" }, { "id": "cl126ixp9000q2e6dslh0zypi", "type": "text", - "groupId": "cl126ixoq000p2e6dfbz9sype", "content": { "html": "
Do you work for a specific company?
", "richText": [ @@ -187,7 +170,8 @@ } ], "plainText": "Do you work for a specific company?" - } + }, + "groupId": "clcueadzi00073b6sqz8n5vxp" }, { "id": "cl126jb2q000r2e6dgqlnxnt8", @@ -197,31 +181,23 @@ "id": "cl126jb2q000s2e6dm60yq5p2", "type": 0, "blockId": "cl126jb2q000r2e6dgqlnxnt8", - "content": "Yes", - "outgoingEdgeId": "cl126jsoo000x2e6ditu7dgf8" + "content": "Yes" }, { "id": "cl126jc5a000t2e6dqv91w7j6", "type": 0, "blockId": "cl126jb2q000r2e6dgqlnxnt8", "content": "No", - "outgoingEdgeId": "cl126l5tx00122e6dmisci6h5" + "outgoingEdgeId": "clcueadzi000f3b6sb7lxyeta" } ], - "groupId": "cl126ixoq000p2e6dfbz9sype", + "groupId": "clcueadzi00073b6sqz8n5vxp", "options": { "buttonLabel": "Send", "isMultipleChoice": false } - } - ], - "title": "Group #5", - "graphCoordinates": { "x": 614, "y": 244 } - }, - { - "id": "cl126jioj000u2e6dqssno3hv", - "blocks": [ + }, { "id": "cl126jioz000v2e6dwrk1f2cb", "type": "text input", - "groupId": "cl126jioj000u2e6dqssno3hv", + "groupId": "clcueadzi00073b6sqz8n5vxp", "options": { "isLong": false, "labels": { @@ -229,43 +205,34 @@ "placeholder": "Type the company name..." }, "variableId": "cl126jqww000w2e6dq9yv4ifq" - } - }, - { - "id": "cl12890kw00132e6dp9v5dexm", - "type": "Code", - "groupId": "cl126jioj000u2e6dqssno3hv", - "options": { - "name": "Store company in DB", - "content": "postMessage({from: \"typebot\", action: \"storeCompany\", content: {{Company}}}, \"*\")" }, - "outgoingEdgeId": "cl128ag8i00162e6dufv3tgo0" + "outgoingEdgeId": "clcueb0cl000p3b6sisrc741n" } ], - "title": "Group #6", - "graphCoordinates": { "x": 969, "y": 308 } + "graphCoordinates": { "x": 679.828061917379, "y": 3.351428911218571 } }, { - "id": "cl126krbp00102e6dnjelmfa1", + "id": "clcueadzi00093b6s82ivles8", + "title": "Bot category", "blocks": [ { "id": "cl126krck00112e6d1m6ctxpn", "type": "text", - "groupId": "cl126krbp00102e6dnjelmfa1", "content": { - "html": "
What type of forms are you planning to build with Typebot?
", + "html": "
What type of bots are you planning to build with Typebot?
", "richText": [ { "type": "p", "children": [ { - "text": "What type of forms are you planning to build with Typebot?" + "text": "What type of bots are you planning to build with Typebot?" } ] } ], - "plainText": "What type of forms are you planning to build with Typebot?" - } + "plainText": "What type of bots are you planning to build with Typebot?" + }, + "groupId": "clcueadzi00093b6s82ivles8" }, { "id": "cl126lb8v00142e6duv5qe08l", @@ -320,7 +287,7 @@ "content": "Other" } ], - "groupId": "cl126krbp00102e6dnjelmfa1", + "groupId": "clcueadzi00093b6s82ivles8", "options": { "variableId": "cl126mo3t001b2e6dvyi16bkd", "buttonLabel": "Send", @@ -328,26 +295,40 @@ } }, { - "id": "cl128ain900172e6d1osj4u90", - "type": "Code", - "groupId": "cl126krbp00102e6dnjelmfa1", - "options": { - "name": "Store categories in DB", - "content": "postMessage({from: \"typebot\", action: \"storeCategories\", content: {{Categories}}}, \"*\")" - }, - "outgoingEdgeId": "cl128azam00182e6dct61k7v5" + "id": "cl1278gyk002w2e6d744eb87n", + "type": "Condition", + "items": [ + { + "id": "cl1278gyk002x2e6dwmpzs3nf", + "type": 1, + "blockId": "cl1278gyk002w2e6d744eb87n", + "content": { + "comparisons": [ + { + "id": "cl1278irq002y2e6dv4965diw", + "value": "Other", + "variableId": "cl126mo3t001b2e6dvyi16bkd", + "comparisonOperator": "Contains" + } + ], + "logicalOperator": "AND" + }, + "outgoingEdgeId": "clcueadzi000g3b6sdb6o0xet" + } + ], + "groupId": "clcueadzi00093b6s82ivles8", + "outgoingEdgeId": "clcueadzi000h3b6shpxplygo" } ], - "title": "Group #6", - "graphCoordinates": { "x": 1218, "y": 510 } + "graphCoordinates": { "x": 1030.2081982319628, "y": -0.2818258211374715 } }, { - "id": "cl126p75m001j2e6d73qmes0m", + "id": "clcueadzi000a3b6spk404zpz", + "title": "Bye", "blocks": [ { "id": "cl126p76d001k2e6dbhnf2ysq", "type": "text", - "groupId": "cl126p75m001j2e6d73qmes0m", "content": { "html": "
Thank you for answering those questions!
", "richText": [ @@ -359,29 +340,20 @@ } ], "plainText": "Thank you for answering those questions!" - } - }, - { - "id": "cl128375600112e6d4l0jtuyf", - "type": "Code", - "groupId": "cl126p75m001j2e6d73qmes0m", - "options": { - "name": "Shoot confettis", - "content": "postMessage({from: \"typebot\", action: \"shootConfettis\"}, \"*\")" - } + }, + "groupId": "clcueadzi000a3b6spk404zpz" }, { "id": "cl126rfy6001t2e6d21gcb6b0", "type": "image", - "groupId": "cl126p75m001j2e6d73qmes0m", "content": { "url": "https://media4.giphy.com/media/l0amJzVHIAfl7jMDos/giphy.gif?cid=fe3852a3i4c33635xdtj3nesr9uq4zteujaab6b0jr42gpxx&rid=giphy.gif&ct=g" - } + }, + "groupId": "clcueadzi000a3b6spk404zpz" }, { "id": "cl126txta001y2e6dtxrbsnek", "type": "text", - "groupId": "cl126p75m001j2e6d73qmes0m", "content": { "html": "
You can reach out to me using the contact bubble on the bottom right corner πŸ€“
", "richText": [ @@ -395,11 +367,11 @@ } ], "plainText": "You can reach out to me using the contact bubble on the bottom right corner πŸ€“" - } + }, + "groupId": "clcueadzi000a3b6spk404zpz" }, { "id": "cl12buyly00172e6991bz38ch", - "groupId": "cl126p75m001j2e6d73qmes0m", "type": "text", "content": { "html": "
Let's create your first typebot...
", @@ -410,91 +382,41 @@ } ], "plainText": "Let's create your first typebot..." - } - }, - { - "id": "cl12bwpi800182e69kcivnp1s", - "groupId": "cl126p75m001j2e6d73qmes0m", - "type": "Code", - "options": { - "name": "Go to typebot creation", - "content": "setTimeout(() => {window.location.href = \"https://app.typebot.io/typebots/create?isFirstBot=true\"}, 4000)" - } + }, + "groupId": "clcueadzi000a3b6spk404zpz" } ], - "title": "Group #7", - "graphCoordinates": { "x": 1612, "y": 1103 } + "graphCoordinates": { "x": 1585.6402200792238, "y": 219.28927860860924 } }, { - "id": "cl126pv6w001n2e6dp0qkvthu", + "id": "clcueadzi000b3b6sv6936vs4", + "title": "Other category", "blocks": [ { "id": "cl127yxym000b2e6d9hksxo6h", "type": "text", - "groupId": "cl126pv6w001n2e6dp0qkvthu", "content": { "html": "
What else?
", "richText": [ { "type": "p", "children": [{ "text": "What else?" }] } ], "plainText": "What else?" - } + }, + "groupId": "clcueadzi000b3b6sv6936vs4" }, { "id": "cl126pv7n001o2e6dajltc4qz", "type": "text input", - "groupId": "cl126pv6w001n2e6dp0qkvthu", + "groupId": "clcueadzi000b3b6sv6936vs4", "options": { "isLong": false, "labels": { "button": "Send", "placeholder": "Type your answer" }, "variableId": "cl126q38p001q2e6d0hj23f6b" - } - }, - { - "id": "cl128b34o00192e6dqjxs3cxf", - "type": "Code", - "groupId": "cl126pv6w001n2e6dp0qkvthu", - "options": { - "name": "Store Other categories in DB", - "content": "postMessage({from: \"typebot\", action: \"storeOtherCategories\", content: {{Other categories}}}, \"*\")" }, - "outgoingEdgeId": "cl128c0fu001a2e6droq69g6z" + "outgoingEdgeId": "clcuehrt2000s3b6skmk7rhje" } ], - "title": "Group #8", - "graphCoordinates": { "x": 1943, "y": 895 } - }, - { - "id": "cl1278gx9002v2e6d4kf3v89s", - "blocks": [ - { - "id": "cl1278gyk002w2e6d744eb87n", - "type": "Condition", - "items": [ - { - "id": "cl1278gyk002x2e6dwmpzs3nf", - "type": 1, - "blockId": "cl1278gyk002w2e6d744eb87n", - "content": { - "comparisons": [ - { - "id": "cl1278irq002y2e6dv4965diw", - "value": "Other", - "variableId": "cl126mo3t001b2e6dvyi16bkd", - "comparisonOperator": "Contains" - } - ], - "logicalOperator": "AND" - }, - "outgoingEdgeId": "cl1278r3b002z2e6d6d6rk9dh" - } - ], - "groupId": "cl1278gx9002v2e6d4kf3v89s", - "outgoingEdgeId": "cl1278trd00312e6dxmzhcmmn" - } - ], - "title": "Group #13", - "graphCoordinates": { "x": 1585, "y": 792 } + "graphCoordinates": { "x": 1369.6844213687823, "y": -7.90789096298402 } } ], "variables": [ @@ -505,99 +427,71 @@ ], "edges": [ { - "id": "cl1266kt100082e6d1wks5dtp", - "to": { "groupId": "cl1266bah00032e6dgdnj4vgz" }, - "from": { - "blockId": "cl1265zct0001mb1afel460do", - "groupId": "cl1265zct0000mb1a6bir36w7" - } - }, - { - "id": "cl126jsoo000x2e6ditu7dgf8", - "to": { "groupId": "cl126jioj000u2e6dqssno3hv" }, - "from": { - "itemId": "cl126jb2q000s2e6dm60yq5p2", - "blockId": "cl126jb2q000r2e6dgqlnxnt8", - "groupId": "cl126ixoq000p2e6dfbz9sype" - } - }, - { - "id": "cl126l5tx00122e6dmisci6h5", - "to": { "groupId": "cl126krbp00102e6dnjelmfa1" }, + "id": "clcueadzi000f3b6sb7lxyeta", + "to": { "groupId": "clcueadzi00093b6s82ivles8" }, "from": { "itemId": "cl126jc5a000t2e6dqv91w7j6", "blockId": "cl126jb2q000r2e6dgqlnxnt8", - "groupId": "cl126ixoq000p2e6dfbz9sype" + "groupId": "clcueadzi00073b6sqz8n5vxp" } }, { - "id": "cl1278r3b002z2e6d6d6rk9dh", - "to": { "groupId": "cl126pv6w001n2e6dp0qkvthu" }, + "id": "clcueadzi000g3b6sdb6o0xet", + "to": { "groupId": "clcueadzi000b3b6sv6936vs4" }, "from": { "itemId": "cl1278gyk002x2e6dwmpzs3nf", "blockId": "cl1278gyk002w2e6d744eb87n", - "groupId": "cl1278gx9002v2e6d4kf3v89s" + "groupId": "clcueadzi00093b6s82ivles8" } }, { - "id": "cl1278trd00312e6dxmzhcmmn", - "to": { "groupId": "cl126p75m001j2e6d73qmes0m" }, + "id": "clcueadzi000h3b6shpxplygo", + "to": { "groupId": "clcueadzi000a3b6spk404zpz" }, "from": { "blockId": "cl1278gyk002w2e6d744eb87n", - "groupId": "cl1278gx9002v2e6d4kf3v89s" + "groupId": "clcueadzi00093b6s82ivles8" } }, { - "id": "cl128ag8i00162e6dufv3tgo0", - "to": { "groupId": "cl126krbp00102e6dnjelmfa1" }, + "id": "clcueadzi000l3b6smlf218u7", + "to": { "groupId": "clcueadzi00073b6sqz8n5vxp" }, "from": { - "blockId": "cl12890kw00132e6dp9v5dexm", - "groupId": "cl126jioj000u2e6dqssno3hv" - } - }, - { - "id": "cl128azam00182e6dct61k7v5", - "to": { "groupId": "cl1278gx9002v2e6d4kf3v89s" }, - "from": { - "blockId": "cl128ain900172e6d1osj4u90", - "groupId": "cl126krbp00102e6dnjelmfa1" + "itemId": "cl1266bam00052e6dn1sdjnax", + "blockId": "cl1266bam00042e6dm0gn22vy", + "groupId": "clcueadzi00063b6sch7b1f32" } }, { - "id": "cl128c0fu001a2e6droq69g6z", - "to": { "groupId": "cl126p75m001j2e6d73qmes0m" }, "from": { - "blockId": "cl128b34o00192e6dqjxs3cxf", - "groupId": "cl126pv6w001n2e6dp0qkvthu" - } + "groupId": "clcueadzi00073b6sqz8n5vxp", + "blockId": "cl126jioz000v2e6dwrk1f2cb" + }, + "to": { "groupId": "clcueadzi00093b6s82ivles8" }, + "id": "clcueb0cl000p3b6sisrc741n" }, { "from": { - "groupId": "cl1266bah00032e6dgdnj4vgz", - "blockId": "cl1266bam00042e6dm0gn22vy", - "itemId": "cl1266bam00052e6dn1sdjnax" + "groupId": "clcueadzi00063b6sch7b1f32", + "blockId": "cl126820m000g2e6dfleq78bt" }, - "to": { "groupId": "cl126ixoq000p2e6dfbz9sype" }, - "id": "cl12bk3j6000c2e69bak89ja9" + "to": { "groupId": "clcueadzi00073b6sqz8n5vxp" }, + "id": "clcuecvjo000q3b6s42ouw3zz" }, { "from": { - "groupId": "cl1267q1z000d2e6d949f2ge4", - "blockId": "cl1289y1s00142e6dvbkpvbje" - }, - "to": { - "groupId": "cl126ixoq000p2e6dfbz9sype", - "blockId": "cl126hb9m000l2e6d5qk3mohn" + "groupId": "clcueadzi00043b6s1r8wnql8", + "blockId": "cl1265zct0001mb1afel460do" }, - "id": "cl12bk56s000d2e69oll3nqxm" + "to": { "groupId": "clcueadzi00063b6sch7b1f32" }, + "id": "clcuefdfv000r3b6sqzv3prz3" }, { "from": { - "groupId": "cl1266bah00032e6dgdnj4vgz", - "blockId": "cl1266bam00042e6dm0gn22vy" + "groupId": "clcueadzi000b3b6sv6936vs4", + "blockId": "cl126pv7n001o2e6dajltc4qz" }, - "to": { "groupId": "cl1267q1z000d2e6d949f2ge4" }, - "id": "cl12bnfyd000g2e69g7lr3czq" + "to": { "groupId": "clcueadzi000a3b6spk404zpz" }, + "id": "clcuehrt2000s3b6skmk7rhje" } ], "theme": { @@ -632,6 +526,10 @@ }, "typingEmulation": { "speed": 300, "enabled": true, "maxDelay": 1.5 } }, - "publicId": "typebot-onboarding", - "customDomain": null + "publicId": null, + "customDomain": null, + "workspaceId": "proWorkspace", + "resultsTablePreferences": null, + "isArchived": false, + "isClosed": false } diff --git a/apps/builder/public/bots/onboarding.json b/apps/builder/public/bots/onboarding.json index 72a10ad068..05f9917f84 100644 --- a/apps/builder/public/bots/onboarding.json +++ b/apps/builder/public/bots/onboarding.json @@ -6,21 +6,22 @@ "folderId": null, "groups": [ { - "id": "cl1265zct0000mb1a6bir36w7", + "id": "clcueadzi00043b6s1r8wnql8", + "title": "Start", "blocks": [ { "id": "cl1265zct0001mb1afel460do", "type": "start", "label": "Start", - "groupId": "cl1265zct0000mb1a6bir36w7", - "outgoingEdgeId": "cl1266kt100082e6d1wks5dtp" + "groupId": "clcueadzi00043b6s1r8wnql8", + "outgoingEdgeId": "clcuefdfv000r3b6sqzv3prz3" } ], - "title": "Start", "graphCoordinates": { "x": 0, "y": 0 } }, { - "id": "cl1266bah00032e6dgdnj4vgz", + "id": "clcueadzi00063b6sch7b1f32", + "title": "Name", "blocks": [ { "id": "cl1266bam00042e6dm0gn22vy", @@ -40,47 +41,39 @@ ], "logicalOperator": "AND" }, - "outgoingEdgeId": "cl12bk3j6000c2e69bak89ja9" + "outgoingEdgeId": "clcueadzi000l3b6smlf218u7" } ], - "groupId": "cl1266bah00032e6dgdnj4vgz", - "outgoingEdgeId": "cl12bnfyd000g2e69g7lr3czq" - } - ], - "title": "Group #1", - "graphCoordinates": { "x": 266, "y": 162 } - }, - { - "id": "cl1267q1z000d2e6d949f2ge4", - "blocks": [ + "groupId": "clcueadzi00063b6sch7b1f32" + }, { "id": "cl1267q2c000e2e6dynjeg83n", "type": "text", - "groupId": "cl1267q1z000d2e6d949f2ge4", "content": { "html": "
Welcome πŸ‘‹
", "richText": [ { "type": "p", "children": [{ "text": "Welcome πŸ‘‹" }] } ], "plainText": "Welcome πŸ‘‹" - } + }, + "groupId": "clcueadzi00063b6sch7b1f32" }, { "id": "cl1267y1u000f2e6d4rlglv6g", "type": "text", - "groupId": "cl1267q1z000d2e6d949f2ge4", "content": { "html": "
What's your name?
", "richText": [ { "type": "p", "children": [{ "text": "What's your name?" }] } ], "plainText": "What's your name?" - } + }, + "groupId": "clcueadzi00063b6sch7b1f32" }, { "id": "cl126820m000g2e6dfleq78bt", "type": "text input", - "groupId": "cl1267q1z000d2e6d949f2ge4", + "groupId": "clcueadzi00063b6sch7b1f32", "options": { "isLong": false, "labels": { @@ -88,41 +81,31 @@ "placeholder": "Type your answer..." }, "variableId": "cl126f4hf000i2e6d8zvzc3t1" - } - }, - { - "id": "cl1289y1s00142e6dvbkpvbje", - "type": "Code", - "groupId": "cl1267q1z000d2e6d949f2ge4", - "options": { - "name": "Store Name in DB", - "content": "postMessage({from: \"typebot\", action: \"storeName\", content: {{Name}}}, \"*\")" }, - "outgoingEdgeId": "cl12bk56s000d2e69oll3nqxm" + "outgoingEdgeId": "clcuecvjo000q3b6s42ouw3zz" } ], - "title": "Group #3", - "graphCoordinates": { "x": 269, "y": 381 } + "graphCoordinates": { "x": 328.22861564828236, "y": -1.7421511890097776 } }, { - "id": "cl126ixoq000p2e6dfbz9sype", + "id": "clcueadzi00073b6sqz8n5vxp", + "title": "Company", "blocks": [ { "id": "cl1266v6f000a2e6db7wj3ux7", "type": "text", - "groupId": "cl126ixoq000p2e6dfbz9sype", "content": { "html": "
Welcome {{Name}} πŸ‘‹
", "richText": [ { "type": "p", "children": [{ "text": "Welcome {{Name}} πŸ‘‹" }] } ], "plainText": "Welcome {{Name}} πŸ‘‹" - } + }, + "groupId": "clcueadzi00073b6sqz8n5vxp" }, { "id": "cl126hb9m000l2e6d5qk3mohn", "type": "text", - "groupId": "cl126ixoq000p2e6dfbz9sype", "content": { "html": "
I'm super pumped that you've decided to try out Typebot 😍
", "richText": [ @@ -136,12 +119,12 @@ } ], "plainText": "I'm super pumped that you've decided to try out Typebot 😍" - } + }, + "groupId": "clcueadzi00073b6sqz8n5vxp" }, { "id": "cl126hpw1000m2e6dneousygl", "type": "text", - "groupId": "cl126ixoq000p2e6dfbz9sype", "content": { "html": "
You are small steps away from meaningful, hyper-personalized experience for your users
", "richText": [ @@ -155,12 +138,12 @@ } ], "plainText": "You are small steps away from meaningful, hyper-personalized experience for your users" - } + }, + "groupId": "clcueadzi00073b6sqz8n5vxp" }, { "id": "cl126guhd000k2e6d6ypkex9z", "type": "text", - "groupId": "cl126ixoq000p2e6dfbz9sype", "content": { "html": "
Let's get you set up for your Typebot journey.
", "richText": [ @@ -172,12 +155,12 @@ } ], "plainText": "Let's get you set up for your Typebot journey." - } + }, + "groupId": "clcueadzi00073b6sqz8n5vxp" }, { "id": "cl126ixp9000q2e6dslh0zypi", "type": "text", - "groupId": "cl126ixoq000p2e6dfbz9sype", "content": { "html": "
Do you work for a specific company?
", "richText": [ @@ -187,7 +170,8 @@ } ], "plainText": "Do you work for a specific company?" - } + }, + "groupId": "clcueadzi00073b6sqz8n5vxp" }, { "id": "cl126jb2q000r2e6dgqlnxnt8", @@ -197,31 +181,23 @@ "id": "cl126jb2q000s2e6dm60yq5p2", "type": 0, "blockId": "cl126jb2q000r2e6dgqlnxnt8", - "content": "Yes", - "outgoingEdgeId": "cl126jsoo000x2e6ditu7dgf8" + "content": "Yes" }, { "id": "cl126jc5a000t2e6dqv91w7j6", "type": 0, "blockId": "cl126jb2q000r2e6dgqlnxnt8", "content": "No", - "outgoingEdgeId": "cl126l5tx00122e6dmisci6h5" + "outgoingEdgeId": "clcueadzi000f3b6sb7lxyeta" } ], - "groupId": "cl126ixoq000p2e6dfbz9sype", + "groupId": "clcueadzi00073b6sqz8n5vxp", "options": { "buttonLabel": "Send", "isMultipleChoice": false } - } - ], - "title": "Group #5", - "graphCoordinates": { "x": 614, "y": 244 } - }, - { - "id": "cl126jioj000u2e6dqssno3hv", - "blocks": [ + }, { "id": "cl126jioz000v2e6dwrk1f2cb", "type": "text input", - "groupId": "cl126jioj000u2e6dqssno3hv", + "groupId": "clcueadzi00073b6sqz8n5vxp", "options": { "isLong": false, "labels": { @@ -229,43 +205,34 @@ "placeholder": "Type the company name..." }, "variableId": "cl126jqww000w2e6dq9yv4ifq" - } - }, - { - "id": "cl12890kw00132e6dp9v5dexm", - "type": "Code", - "groupId": "cl126jioj000u2e6dqssno3hv", - "options": { - "name": "Store company in DB", - "content": "postMessage({from: \"typebot\", action: \"storeCompany\", content: {{Company}}}, \"*\")" }, - "outgoingEdgeId": "cl128ag8i00162e6dufv3tgo0" + "outgoingEdgeId": "clcueb0cl000p3b6sisrc741n" } ], - "title": "Group #6", - "graphCoordinates": { "x": 969, "y": 308 } + "graphCoordinates": { "x": 679.828061917379, "y": 3.351428911218571 } }, { - "id": "cl126krbp00102e6dnjelmfa1", + "id": "clcueadzi00093b6s82ivles8", + "title": "Bot category", "blocks": [ { "id": "cl126krck00112e6d1m6ctxpn", "type": "text", - "groupId": "cl126krbp00102e6dnjelmfa1", "content": { - "html": "
What type of forms are you planning to build with Typebot?
", + "html": "
What type of bots are you planning to build with Typebot?
", "richText": [ { "type": "p", "children": [ { - "text": "What type of forms are you planning to build with Typebot?" + "text": "What type of bots are you planning to build with Typebot?" } ] } ], - "plainText": "What type of forms are you planning to build with Typebot?" - } + "plainText": "What type of bots are you planning to build with Typebot?" + }, + "groupId": "clcueadzi00093b6s82ivles8" }, { "id": "cl126lb8v00142e6duv5qe08l", @@ -320,7 +287,7 @@ "content": "Other" } ], - "groupId": "cl126krbp00102e6dnjelmfa1", + "groupId": "clcueadzi00093b6s82ivles8", "options": { "variableId": "cl126mo3t001b2e6dvyi16bkd", "buttonLabel": "Send", @@ -328,26 +295,40 @@ } }, { - "id": "cl128ain900172e6d1osj4u90", - "type": "Code", - "groupId": "cl126krbp00102e6dnjelmfa1", - "options": { - "name": "Store categories in DB", - "content": "postMessage({from: \"typebot\", action: \"storeCategories\", content: {{Categories}}}, \"*\")" - }, - "outgoingEdgeId": "cl128azam00182e6dct61k7v5" + "id": "cl1278gyk002w2e6d744eb87n", + "type": "Condition", + "items": [ + { + "id": "cl1278gyk002x2e6dwmpzs3nf", + "type": 1, + "blockId": "cl1278gyk002w2e6d744eb87n", + "content": { + "comparisons": [ + { + "id": "cl1278irq002y2e6dv4965diw", + "value": "Other", + "variableId": "cl126mo3t001b2e6dvyi16bkd", + "comparisonOperator": "Contains" + } + ], + "logicalOperator": "AND" + }, + "outgoingEdgeId": "clcueadzi000g3b6sdb6o0xet" + } + ], + "groupId": "clcueadzi00093b6s82ivles8", + "outgoingEdgeId": "clcueadzi000h3b6shpxplygo" } ], - "title": "Group #6", - "graphCoordinates": { "x": 1218, "y": 510 } + "graphCoordinates": { "x": 1030.2081982319628, "y": -0.2818258211374715 } }, { - "id": "cl126p75m001j2e6d73qmes0m", + "id": "clcueadzi000a3b6spk404zpz", + "title": "Bye", "blocks": [ { "id": "cl126p76d001k2e6dbhnf2ysq", "type": "text", - "groupId": "cl126p75m001j2e6d73qmes0m", "content": { "html": "
Thank you for answering those questions!
", "richText": [ @@ -359,29 +340,20 @@ } ], "plainText": "Thank you for answering those questions!" - } - }, - { - "id": "cl128375600112e6d4l0jtuyf", - "type": "Code", - "groupId": "cl126p75m001j2e6d73qmes0m", - "options": { - "name": "Shoot confettis", - "content": "postMessage({from: \"typebot\", action: \"shootConfettis\"}, \"*\")" - } + }, + "groupId": "clcueadzi000a3b6spk404zpz" }, { "id": "cl126rfy6001t2e6d21gcb6b0", "type": "image", - "groupId": "cl126p75m001j2e6d73qmes0m", "content": { "url": "https://media4.giphy.com/media/l0amJzVHIAfl7jMDos/giphy.gif?cid=fe3852a3i4c33635xdtj3nesr9uq4zteujaab6b0jr42gpxx&rid=giphy.gif&ct=g" - } + }, + "groupId": "clcueadzi000a3b6spk404zpz" }, { "id": "cl126txta001y2e6dtxrbsnek", "type": "text", - "groupId": "cl126p75m001j2e6d73qmes0m", "content": { "html": "
You can reach out to me using the contact bubble on the bottom right corner πŸ€“
", "richText": [ @@ -395,11 +367,11 @@ } ], "plainText": "You can reach out to me using the contact bubble on the bottom right corner πŸ€“" - } + }, + "groupId": "clcueadzi000a3b6spk404zpz" }, { "id": "cl12buyly00172e6991bz38ch", - "groupId": "cl126p75m001j2e6d73qmes0m", "type": "text", "content": { "html": "
Let's create your first typebot...
", @@ -410,91 +382,41 @@ } ], "plainText": "Let's create your first typebot..." - } - }, - { - "id": "cl12bwpi800182e69kcivnp1s", - "groupId": "cl126p75m001j2e6d73qmes0m", - "type": "Code", - "options": { - "name": "Go to typebot creation", - "content": "setTimeout(() => {window.location.href = \"https://app.typebot.io/typebots/create?isFirstBot=true\"}, 4000)" - } + }, + "groupId": "clcueadzi000a3b6spk404zpz" } ], - "title": "Group #7", - "graphCoordinates": { "x": 1612, "y": 1103 } + "graphCoordinates": { "x": 1585.6402200792238, "y": 219.28927860860924 } }, { - "id": "cl126pv6w001n2e6dp0qkvthu", + "id": "clcueadzi000b3b6sv6936vs4", + "title": "Other category", "blocks": [ { "id": "cl127yxym000b2e6d9hksxo6h", "type": "text", - "groupId": "cl126pv6w001n2e6dp0qkvthu", "content": { "html": "
What else?
", "richText": [ { "type": "p", "children": [{ "text": "What else?" }] } ], "plainText": "What else?" - } + }, + "groupId": "clcueadzi000b3b6sv6936vs4" }, { "id": "cl126pv7n001o2e6dajltc4qz", "type": "text input", - "groupId": "cl126pv6w001n2e6dp0qkvthu", + "groupId": "clcueadzi000b3b6sv6936vs4", "options": { "isLong": false, "labels": { "button": "Send", "placeholder": "Type your answer" }, "variableId": "cl126q38p001q2e6d0hj23f6b" - } - }, - { - "id": "cl128b34o00192e6dqjxs3cxf", - "type": "Code", - "groupId": "cl126pv6w001n2e6dp0qkvthu", - "options": { - "name": "Store Other categories in DB", - "content": "postMessage({from: \"typebot\", action: \"storeOtherCategories\", content: {{Other categories}}}, \"*\")" }, - "outgoingEdgeId": "cl128c0fu001a2e6droq69g6z" + "outgoingEdgeId": "clcuehrt2000s3b6skmk7rhje" } ], - "title": "Group #8", - "graphCoordinates": { "x": 1943, "y": 895 } - }, - { - "id": "cl1278gx9002v2e6d4kf3v89s", - "blocks": [ - { - "id": "cl1278gyk002w2e6d744eb87n", - "type": "Condition", - "items": [ - { - "id": "cl1278gyk002x2e6dwmpzs3nf", - "type": 1, - "blockId": "cl1278gyk002w2e6d744eb87n", - "content": { - "comparisons": [ - { - "id": "cl1278irq002y2e6dv4965diw", - "value": "Other", - "variableId": "cl126mo3t001b2e6dvyi16bkd", - "comparisonOperator": "Contains" - } - ], - "logicalOperator": "AND" - }, - "outgoingEdgeId": "cl1278r3b002z2e6d6d6rk9dh" - } - ], - "groupId": "cl1278gx9002v2e6d4kf3v89s", - "outgoingEdgeId": "cl1278trd00312e6dxmzhcmmn" - } - ], - "title": "Group #13", - "graphCoordinates": { "x": 1585, "y": 792 } + "graphCoordinates": { "x": 1369.6844213687823, "y": -7.90789096298402 } } ], "variables": [ @@ -505,99 +427,71 @@ ], "edges": [ { - "id": "cl1266kt100082e6d1wks5dtp", - "to": { "groupId": "cl1266bah00032e6dgdnj4vgz" }, - "from": { - "blockId": "cl1265zct0001mb1afel460do", - "groupId": "cl1265zct0000mb1a6bir36w7" - } - }, - { - "id": "cl126jsoo000x2e6ditu7dgf8", - "to": { "groupId": "cl126jioj000u2e6dqssno3hv" }, - "from": { - "itemId": "cl126jb2q000s2e6dm60yq5p2", - "blockId": "cl126jb2q000r2e6dgqlnxnt8", - "groupId": "cl126ixoq000p2e6dfbz9sype" - } - }, - { - "id": "cl126l5tx00122e6dmisci6h5", - "to": { "groupId": "cl126krbp00102e6dnjelmfa1" }, + "id": "clcueadzi000f3b6sb7lxyeta", + "to": { "groupId": "clcueadzi00093b6s82ivles8" }, "from": { "itemId": "cl126jc5a000t2e6dqv91w7j6", "blockId": "cl126jb2q000r2e6dgqlnxnt8", - "groupId": "cl126ixoq000p2e6dfbz9sype" + "groupId": "clcueadzi00073b6sqz8n5vxp" } }, { - "id": "cl1278r3b002z2e6d6d6rk9dh", - "to": { "groupId": "cl126pv6w001n2e6dp0qkvthu" }, + "id": "clcueadzi000g3b6sdb6o0xet", + "to": { "groupId": "clcueadzi000b3b6sv6936vs4" }, "from": { "itemId": "cl1278gyk002x2e6dwmpzs3nf", "blockId": "cl1278gyk002w2e6d744eb87n", - "groupId": "cl1278gx9002v2e6d4kf3v89s" + "groupId": "clcueadzi00093b6s82ivles8" } }, { - "id": "cl1278trd00312e6dxmzhcmmn", - "to": { "groupId": "cl126p75m001j2e6d73qmes0m" }, + "id": "clcueadzi000h3b6shpxplygo", + "to": { "groupId": "clcueadzi000a3b6spk404zpz" }, "from": { "blockId": "cl1278gyk002w2e6d744eb87n", - "groupId": "cl1278gx9002v2e6d4kf3v89s" - } - }, - { - "id": "cl128ag8i00162e6dufv3tgo0", - "to": { "groupId": "cl126krbp00102e6dnjelmfa1" }, - "from": { - "blockId": "cl12890kw00132e6dp9v5dexm", - "groupId": "cl126jioj000u2e6dqssno3hv" + "groupId": "clcueadzi00093b6s82ivles8" } }, { - "id": "cl128azam00182e6dct61k7v5", - "to": { "groupId": "cl1278gx9002v2e6d4kf3v89s" }, + "id": "clcueadzi000l3b6smlf218u7", + "to": { "groupId": "clcueadzi00073b6sqz8n5vxp" }, "from": { - "blockId": "cl128ain900172e6d1osj4u90", - "groupId": "cl126krbp00102e6dnjelmfa1" + "itemId": "cl1266bam00052e6dn1sdjnax", + "blockId": "cl1266bam00042e6dm0gn22vy", + "groupId": "clcueadzi00063b6sch7b1f32" } }, { - "id": "cl128c0fu001a2e6droq69g6z", - "to": { "groupId": "cl126p75m001j2e6d73qmes0m" }, "from": { - "blockId": "cl128b34o00192e6dqjxs3cxf", - "groupId": "cl126pv6w001n2e6dp0qkvthu" - } + "groupId": "clcueadzi00073b6sqz8n5vxp", + "blockId": "cl126jioz000v2e6dwrk1f2cb" + }, + "to": { "groupId": "clcueadzi00093b6s82ivles8" }, + "id": "clcueb0cl000p3b6sisrc741n" }, { "from": { - "groupId": "cl1266bah00032e6dgdnj4vgz", - "blockId": "cl1266bam00042e6dm0gn22vy", - "itemId": "cl1266bam00052e6dn1sdjnax" + "groupId": "clcueadzi00063b6sch7b1f32", + "blockId": "cl126820m000g2e6dfleq78bt" }, - "to": { "groupId": "cl126ixoq000p2e6dfbz9sype" }, - "id": "cl12bk3j6000c2e69bak89ja9" + "to": { "groupId": "clcueadzi00073b6sqz8n5vxp" }, + "id": "clcuecvjo000q3b6s42ouw3zz" }, { "from": { - "groupId": "cl1267q1z000d2e6d949f2ge4", - "blockId": "cl1289y1s00142e6dvbkpvbje" - }, - "to": { - "groupId": "cl126ixoq000p2e6dfbz9sype", - "blockId": "cl126hb9m000l2e6d5qk3mohn" + "groupId": "clcueadzi00043b6s1r8wnql8", + "blockId": "cl1265zct0001mb1afel460do" }, - "id": "cl12bk56s000d2e69oll3nqxm" + "to": { "groupId": "clcueadzi00063b6sch7b1f32" }, + "id": "clcuefdfv000r3b6sqzv3prz3" }, { "from": { - "groupId": "cl1266bah00032e6dgdnj4vgz", - "blockId": "cl1266bam00042e6dm0gn22vy" + "groupId": "clcueadzi000b3b6sv6936vs4", + "blockId": "cl126pv7n001o2e6dajltc4qz" }, - "to": { "groupId": "cl1267q1z000d2e6d949f2ge4" }, - "id": "cl12bnfyd000g2e69g7lr3czq" + "to": { "groupId": "clcueadzi000a3b6spk404zpz" }, + "id": "clcuehrt2000s3b6skmk7rhje" } ], "theme": { @@ -608,12 +502,12 @@ "placeholderColor": "#9095A0" }, "buttons": { "color": "#FFFFFF", "backgroundColor": "#0042DA" }, - "hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" }, - "guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" }, "hostAvatar": { - "isEnabled": true, - "url": "https://s3.eu-west-3.amazonaws.com/typebot/typebots/ckzp7a2za005809lczf2knzix/273013187_1315820332248257_6244778509534754615_n.jpg" - } + "url": "https://github.com/avatars/u/16015833?v=4", + "isEnabled": true + }, + "hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" }, + "guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" } }, "general": { "font": "Open Sans", @@ -631,6 +525,10 @@ }, "typingEmulation": { "speed": 300, "enabled": true, "maxDelay": 1.5 } }, - "publicId": "typebot-onboarding", - "customDomain": null + "publicId": null, + "customDomain": null, + "workspaceId": "proWorkspace", + "resultsTablePreferences": null, + "isArchived": false, + "isClosed": false } diff --git a/apps/builder/public/templates/faq.json b/apps/builder/public/templates/faq.json index 0a0160b036..8922485a1d 100644 --- a/apps/builder/public/templates/faq.json +++ b/apps/builder/public/templates/faq.json @@ -557,7 +557,7 @@ }, "settings": { "general": { - "isBrandingEnabled": false, + "isBrandingEnabled": true, "isInputPrefillEnabled": true, "isHideQueryParamsEnabled": true, "isNewResultOnRefreshEnabled": false diff --git a/apps/builder/public/templates/lead-gen.json b/apps/builder/public/templates/lead-gen.json index 555a596764..82993dd34e 100644 --- a/apps/builder/public/templates/lead-gen.json +++ b/apps/builder/public/templates/lead-gen.json @@ -337,7 +337,10 @@ }, "buttons": { "color": "#FFFFFF", "backgroundColor": "#0042DA" }, "hostBubbles": { "color": "#303235", "backgroundColor": "#F7F8FF" }, - "guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" } + "guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" }, + "hostAvatar": { + "isEnabled": true + } }, "general": { "font": "Open Sans", diff --git a/apps/builder/src/assets/styles/codeMirror.css b/apps/builder/src/assets/styles/codeMirror.css deleted file mode 100644 index e88793c6fd..0000000000 --- a/apps/builder/src/assets/styles/codeMirror.css +++ /dev/null @@ -1,9 +0,0 @@ -.cm-editor { - outline: 0px solid transparent !important; - height: 100%; - border-radius: 0.25rem; -} - -.cm-scroller { - border-radius: 5px; -} diff --git a/apps/builder/src/components/CodeEditor.tsx b/apps/builder/src/components/CodeEditor.tsx index 2b2da9f969..4f79f790ee 100644 --- a/apps/builder/src/components/CodeEditor.tsx +++ b/apps/builder/src/components/CodeEditor.tsx @@ -1,29 +1,26 @@ import { - Box, BoxProps, + Fade, HStack, - useColorMode, useColorModeValue, + useDisclosure, } from '@chakra-ui/react' -import { EditorView, basicSetup } from 'codemirror' -import { EditorState } from '@codemirror/state' -import { json, jsonParseLinter } from '@codemirror/lang-json' -import { css } from '@codemirror/lang-css' -import { javascript } from '@codemirror/lang-javascript' -import { html } from '@codemirror/lang-html' -import { oneDark } from '@codemirror/theme-one-dark' -import { useEffect, useRef, useState } from 'react' +import { useRef, useState } from 'react' import { useDebouncedCallback } from 'use-debounce' -import { linter, LintSource } from '@codemirror/lint' import { VariablesButton } from '@/features/variables' import { Variable } from 'models' import { env } from 'utils' - -const linterExtension = linter(jsonParseLinter() as unknown as LintSource) +import CodeMirror, { ReactCodeMirrorRef } from '@uiw/react-codemirror' +import { tokyoNight } from '@uiw/codemirror-theme-tokyo-night' +import { githubLight } from '@uiw/codemirror-theme-github' +import { LanguageName, loadLanguage } from '@uiw/codemirror-extensions-langs' +import { isDefined } from '@udecode/plate-common' +import { CopyButton } from './CopyButton' type Props = { - value: string - lang?: 'css' | 'json' | 'js' | 'html' + value?: string + defaultValue?: string + lang: LanguageName isReadOnly?: boolean debounceTimeout?: number withVariableButton?: boolean @@ -31,7 +28,7 @@ type Props = { onChange?: (value: string) => void } export const CodeEditor = ({ - value, + defaultValue, lang, onChange, height = '250px', @@ -40,91 +37,25 @@ export const CodeEditor = ({ debounceTimeout = 1000, ...props }: Props & Omit) => { - const isDark = useColorMode().colorMode === 'dark' - const editorContainer = useRef(null) - const editorView = useRef(null) - const [, setPlainTextValue] = useState(value) + const theme = useColorModeValue(githubLight, tokyoNight) + const codeEditor = useRef(null) const [carretPosition, setCarretPosition] = useState(0) const isVariableButtonDisplayed = withVariableButton && !isReadOnly + const [value, _setValue] = useState(defaultValue ?? '') + const { onOpen, onClose, isOpen } = useDisclosure() - const debounced = useDebouncedCallback( + const setValue = useDebouncedCallback( (value) => { - setPlainTextValue(value) + _setValue(value) onChange && onChange(value) }, env('E2E_TEST') === 'true' ? 0 : debounceTimeout ) - useEffect( - () => () => { - debounced.flush() - }, - [debounced] - ) - - useEffect(() => { - if (!editorView.current || !isReadOnly) return - editorView.current.dispatch({ - changes: { - from: 0, - to: editorView.current.state.doc.length, - insert: value, - }, - }) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [value]) - - useEffect(() => { - const editor = initEditor(value) - return () => { - editor?.destroy() - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - - const initEditor = (value: string) => { - if (!editorContainer.current) return - const updateListenerExtension = EditorView.updateListener.of((update) => { - if (update.docChanged && onChange) - debounced(update.state.doc.toJSON().join('\n')) - }) - const extensions = [ - updateListenerExtension, - basicSetup, - EditorState.readOnly.of(isReadOnly), - ] - if (isDark) extensions.push(oneDark) - if (lang === 'json') { - extensions.push(json()) - extensions.push(linterExtension) - } - if (lang === 'css') extensions.push(css()) - if (lang === 'js') extensions.push(javascript()) - if (lang === 'html') extensions.push(html()) - extensions.push( - EditorView.theme({ - '&': { maxHeight: '500px' }, - '.cm-gutter,.cm-content': { minHeight: isReadOnly ? '0' : height }, - '.cm-scroller': { overflow: 'auto' }, - }) - ) - const editor = new EditorView({ - state: EditorState.create({ - extensions, - }), - parent: editorContainer.current, - }) - editor.dispatch({ - changes: { from: 0, insert: value }, - }) - editorView.current = editor - return editor - } - const handleVariableSelected = (variable?: Pick) => { - editorView.current?.focus() + codeEditor.current?.view?.focus() const insert = `{{${variable?.name}}}` - editorView.current?.dispatch({ + codeEditor.current?.view?.dispatch({ changes: { from: carretPosition, insert, @@ -133,9 +64,10 @@ export const CodeEditor = ({ }) } - const handleKeyUp = () => { - if (!editorContainer.current) return - setCarretPosition(editorView.current?.state.selection.main.from ?? 0) + const handleChange = (newValue: string) => { + if (isDefined(props.value)) return + setValue(newValue) + setCarretPosition(codeEditor.current?.state?.selection.main.head ?? 0) } return ( @@ -143,19 +75,61 @@ export const CodeEditor = ({ align="flex-end" spacing={0} borderWidth={'1px'} - borderRadius="md" - bg={useColorModeValue('#FCFCFC', '#282C34')} + rounded="md" + bg={useColorModeValue('white', '#1A1B26')} + width="full" + h="full" + pos="relative" + onMouseEnter={onOpen} + onMouseLeave={onClose} + sx={{ + '& .cm-editor': { + maxH: '70vh', + outline: '0px solid transparent !important', + rounded: 'md', + }, + '& .cm-scroller': { + rounded: 'md', + overflow: 'auto', + }, + '& .cm-gutter,.cm-content': { + minH: isReadOnly ? '0' : height, + }, + '& .ΝΌ1 .cm-scroller': { + fontSize: '14px', + fontFamily: + 'JetBrainsMono, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace', + }, + }} > - {isVariableButtonDisplayed && ( )} + {isReadOnly && ( + + + + )} ) } diff --git a/apps/builder/src/components/ColorPicker.tsx b/apps/builder/src/components/ColorPicker.tsx index 0fa336fd03..cc06b0c6ae 100644 --- a/apps/builder/src/components/ColorPicker.tsx +++ b/apps/builder/src/components/ColorPicker.tsx @@ -29,12 +29,12 @@ const colorsSelection: `#${string}`[] = [ ] type Props = { - initialColor: string + initialColor?: string onColorChange: (color: string) => void } export const ColorPicker = ({ initialColor, onColorChange }: Props) => { - const [color, setColor] = useState(initialColor) + const [color, setColor] = useState(initialColor ?? '') useEffect(() => { onColorChange(color) diff --git a/apps/builder/src/components/Seo.tsx b/apps/builder/src/components/Seo.tsx index 0a2d769065..599547a7bd 100644 --- a/apps/builder/src/components/Seo.tsx +++ b/apps/builder/src/components/Seo.tsx @@ -2,7 +2,6 @@ import Head from 'next/head' export const Seo = ({ title, - currentUrl = 'https://app.typebot.io', description = 'Create and publish conversational forms that collect 4 times more answers and feel native to your product', imagePreviewUrl = 'https://app.typebot.io/site-preview.png', }: { @@ -20,9 +19,6 @@ export const Seo = ({ - - - diff --git a/apps/builder/src/components/SupportBubble.tsx b/apps/builder/src/components/SupportBubble.tsx index 55bd4b2c4b..faa2b8f2a2 100644 --- a/apps/builder/src/components/SupportBubble.tsx +++ b/apps/builder/src/components/SupportBubble.tsx @@ -1,43 +1,35 @@ import { useTypebot } from '@/features/editor' import { useUser } from '@/features/account' import { useWorkspace } from '@/features/workspace' -import React, { useEffect, useState } from 'react' -import { initBubble } from 'typebot-js' -import { isCloudProdInstance } from '@/utils/helpers' +import React from 'react' +import { Bubble } from '@typebot.io/react' import { planToReadable } from '@/features/billing' +import { isCloudProdInstance } from '@/utils/helpers' export const SupportBubble = () => { const { typebot } = useTypebot() const { user } = useUser() const { workspace } = useWorkspace() - const [localTypebotId, setLocalTypebotId] = useState(typebot?.id) - const [localUserId, setLocalUserId] = useState(user?.id) - useEffect(() => { - if ( - isCloudProdInstance && - (localTypebotId !== typebot?.id || localUserId !== user?.id) - ) { - setLocalTypebotId(typebot?.id) - setLocalUserId(user?.id) - initBubble({ - url: `https://viewer.typebot.io/typebot-support`, - backgroundColor: '#ffffff', - button: { - color: '#0042DA', - }, - hiddenVariables: { - 'User ID': user?.id, - 'First name': user?.name?.split(' ')[0] ?? undefined, - Email: user?.email ?? undefined, - 'Typebot ID': typebot?.id, - 'Avatar URL': user?.image ?? undefined, - Plan: planToReadable(workspace?.plan), - }, - }) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [user, typebot]) + if (!isCloudProdInstance) return null - return <> + return ( + + ) } diff --git a/apps/builder/src/components/inputs/SmartNumberInput.tsx b/apps/builder/src/components/inputs/SmartNumberInput.tsx index 9a37b5316c..3afd7f6506 100644 --- a/apps/builder/src/components/inputs/SmartNumberInput.tsx +++ b/apps/builder/src/components/inputs/SmartNumberInput.tsx @@ -93,6 +93,7 @@ export const SmartNumberInput = ({ as={HStack} isRequired={isRequired} justifyContent="space-between" + width={label ? 'full' : 'auto'} > {label && ( diff --git a/apps/builder/src/features/auth/constants.ts b/apps/builder/src/features/auth/constants.ts index 68ad70e92f..c183e2afd7 100644 --- a/apps/builder/src/features/auth/constants.ts +++ b/apps/builder/src/features/auth/constants.ts @@ -5,12 +5,12 @@ export const mockedUser: User = { name: 'John Doe', email: 'user@email.com', company: null, - createdAt: new Date(), + createdAt: new Date('2022-01-01'), emailVerified: null, graphNavigation: 'TRACKPAD', preferredAppAppearance: null, image: 'https://github.com/avatars/u/16015833?v=4', - lastActivityAt: new Date(), + lastActivityAt: new Date('2022-01-01'), onboardingCategories: [], - updatedAt: new Date(), + updatedAt: new Date('2022-01-01'), } diff --git a/apps/builder/src/features/blocks/bubbles/audio/audio.spec.ts b/apps/builder/src/features/blocks/bubbles/audio/audio.spec.ts index 840ed19ce4..421d134aef 100644 --- a/apps/builder/src/features/blocks/bubbles/audio/audio.spec.ts +++ b/apps/builder/src/features/blocks/bubbles/audio/audio.spec.ts @@ -4,7 +4,6 @@ import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { BubbleBlockType, defaultAudioBubbleContent } from 'models' import { createId } from '@paralleldrive/cuid2' import { getTestAsset } from '@/test/utils/playwright' -import { typebotViewer } from 'utils/playwright/testHelpers' const audioSampleUrl = 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3' @@ -34,7 +33,7 @@ test('should work as expected', async ({ page }) => { RegExp(`/public/typebots/${typebotId}/blocks`, 'gm') ) await page.getByRole('button', { name: 'Preview', exact: true }).click() - await expect(typebotViewer(page).locator('audio')).toHaveAttribute( + await expect(page.locator('audio')).toHaveAttribute( 'src', RegExp(`/public/typebots/${typebotId}/blocks`, 'gm') ) diff --git a/apps/builder/src/features/blocks/bubbles/embed/embed.spec.ts b/apps/builder/src/features/blocks/bubbles/embed/embed.spec.ts index 580a7225cf..65df373201 100644 --- a/apps/builder/src/features/blocks/bubbles/embed/embed.spec.ts +++ b/apps/builder/src/features/blocks/bubbles/embed/embed.spec.ts @@ -3,7 +3,6 @@ import { BubbleBlockType, defaultEmbedBubbleContent } from 'models' import { createId } from '@paralleldrive/cuid2' import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' -import { typebotViewer } from 'utils/playwright/testHelpers' const pdfSrc = 'https://www.orimi.com/pdf-test.pdf' const siteSrc = 'https://app.cal.com/baptistearno/15min' @@ -47,9 +46,10 @@ test.describe.parallel('Embed bubble block', () => { await page.goto(`/typebots/${typebotId}/edit`) await page.click('text=Preview') - await expect( - typebotViewer(page).locator('iframe#embed-bubble-content') - ).toHaveAttribute('src', siteSrc) + await expect(page.locator('iframe#embed-bubble-content')).toHaveAttribute( + 'src', + siteSrc + ) }) }) }) diff --git a/apps/builder/src/features/blocks/bubbles/image/image.spec.ts b/apps/builder/src/features/blocks/bubbles/image/image.spec.ts index 119fd089b0..3df5c5cfee 100644 --- a/apps/builder/src/features/blocks/bubbles/image/image.spec.ts +++ b/apps/builder/src/features/blocks/bubbles/image/image.spec.ts @@ -3,7 +3,6 @@ import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { BubbleBlockType, defaultImageBubbleContent } from 'models' import { createId } from '@paralleldrive/cuid2' -import { typebotViewer } from 'utils/playwright/testHelpers' import { getTestAsset } from '@/test/utils/playwright' const unsplashImageSrc = @@ -117,10 +116,7 @@ test.describe.parallel('Image bubble block', () => { await page.goto(`/typebots/${typebotId}/edit`) await page.click('text=Preview') - await expect(typebotViewer(page).locator('img')).toHaveAttribute( - 'src', - unsplashImageSrc - ) + await expect(page.locator('img')).toHaveAttribute('src', unsplashImageSrc) }) }) }) diff --git a/apps/builder/src/features/blocks/bubbles/textBubble/textBubble.spec.ts b/apps/builder/src/features/blocks/bubbles/textBubble/textBubble.spec.ts index 2db4ba0aa7..92877a7d51 100644 --- a/apps/builder/src/features/blocks/bubbles/textBubble/textBubble.spec.ts +++ b/apps/builder/src/features/blocks/bubbles/textBubble/textBubble.spec.ts @@ -3,7 +3,6 @@ import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { BubbleBlockType, defaultTextBubbleContent } from 'models' import { createId } from '@paralleldrive/cuid2' -import { typebotViewer } from 'utils/playwright/testHelpers' test.describe('Text bubble block', () => { test('rich text features should work', async ({ page }) => { @@ -51,17 +50,17 @@ test.describe('Text bubble block', () => { await page.getByRole('menuitem', { name: 'Create test' }).click() await page.click('text=Preview') + await expect(page.locator('span.slate-bold >> nth=0')).toHaveText( + 'Bold text' + ) + await expect(page.locator('span.slate-italic >> nth=0')).toHaveText( + 'Italic text' + ) + await expect(page.locator('span.slate-underline >> nth=0')).toHaveText( + 'Underlined text' + ) await expect( - typebotViewer(page).locator('span.slate-bold >> nth=0') - ).toHaveText('Bold text') - await expect( - typebotViewer(page).locator('span.slate-italic >> nth=0') - ).toHaveText('Italic text') - await expect( - typebotViewer(page).locator('span.slate-underline >> nth=0') - ).toHaveText('Underlined text') - await expect( - typebotViewer(page).locator('a[href="https://github.com"]') + page.locator('typebot-standard').locator('a[href="https://github.com"]') ).toHaveText('My super link') }) }) diff --git a/apps/builder/src/features/blocks/bubbles/video/video.spec.ts b/apps/builder/src/features/blocks/bubbles/video/video.spec.ts index f0a95def01..52b070af66 100644 --- a/apps/builder/src/features/blocks/bubbles/video/video.spec.ts +++ b/apps/builder/src/features/blocks/bubbles/video/video.spec.ts @@ -7,7 +7,6 @@ import { VideoBubbleContentType, } from 'models' import { createId } from '@paralleldrive/cuid2' -import { typebotViewer } from 'utils/playwright/testHelpers' const videoSrc = 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerEscapes.mp4' @@ -57,9 +56,10 @@ test.describe.parallel('Video bubble block', () => { await page.goto(`/typebots/${typebotId}/edit`) await page.click('text=Preview') - await expect( - typebotViewer(page).locator('video > source') - ).toHaveAttribute('src', videoSrc) + await expect(page.locator('video > source').nth(1)).toHaveAttribute( + 'src', + videoSrc + ) }) test('should display youtube video correctly', async ({ page }) => { @@ -80,7 +80,7 @@ test.describe.parallel('Video bubble block', () => { await page.goto(`/typebots/${typebotId}/edit`) await page.click('text=Preview') - await expect(typebotViewer(page).locator('iframe')).toHaveAttribute( + await expect(page.locator('iframe').nth(1)).toHaveAttribute( 'src', 'https://www.youtube.com/embed/dQw4w9WgXcQ' ) @@ -104,7 +104,7 @@ test.describe.parallel('Video bubble block', () => { await page.goto(`/typebots/${typebotId}/edit`) await page.click('text=Preview') - await expect(typebotViewer(page).locator('iframe')).toHaveAttribute( + await expect(page.locator('iframe').nth(1)).toHaveAttribute( 'src', 'https://player.vimeo.com/video/649301125' ) diff --git a/apps/builder/src/features/blocks/inputs/buttons/buttons.spec.ts b/apps/builder/src/features/blocks/inputs/buttons/buttons.spec.ts index 3ec721fedd..dcad1c7431 100644 --- a/apps/builder/src/features/blocks/inputs/buttons/buttons.spec.ts +++ b/apps/builder/src/features/blocks/inputs/buttons/buttons.spec.ts @@ -6,7 +6,6 @@ import { import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { defaultChoiceInputOptions, InputBlockType, ItemType } from 'models' import { createId } from '@paralleldrive/cuid2' -import { typebotViewer } from 'utils/playwright/testHelpers' import { getTestAsset } from '@/test/utils/playwright' test.describe.parallel('Buttons input block', () => { @@ -42,10 +41,10 @@ test.describe.parallel('Buttons input block', () => { await expect(page.locator('text=Item 2')).toBeHidden() await page.click('text=Preview') - const item3Button = typebotViewer(page).locator('button >> text=Item 3') + const item3Button = page.locator('button >> text=Item 3') await item3Button.click() await expect(item3Button).toBeHidden() - await expect(typebotViewer(page).locator('text=Item 3')).toBeVisible() + await expect(page.getByTestId('guest-bubble')).toHaveText('Item 3') await page.click('button[aria-label="Close"]') await page.click('[data-testid="block2-icon"]') @@ -64,13 +63,11 @@ test.describe.parallel('Buttons input block', () => { await page.click('text=Preview') - await typebotViewer(page).locator('button >> text="Item 3"').click() - await typebotViewer(page).locator('button >> text="Item 1"').click() - await typebotViewer(page).locator('text=Go').click() + await page.locator('button >> text="Item 3"').click() + await page.locator('button >> text="Item 1"').click() + await page.locator('text=Go').click() - await expect( - typebotViewer(page).locator('text="Item 3, Item 1"') - ).toBeVisible() + await expect(page.locator('text="Item 3, Item 1"')).toBeVisible() }) }) @@ -85,18 +82,18 @@ test('Variable buttons should work', async ({ page }) => { await page.goto(`/typebots/${typebotId}/edit`) await page.click('text=Preview') - await typebotViewer(page).locator('text=Variable item').click() - await expect(typebotViewer(page).locator('text=Variable item')).toBeVisible() - await expect(typebotViewer(page).locator('text=Ok great!')).toBeVisible() + await page.getByRole('button', { name: 'Variable item' }).click() + await expect(page.getByTestId('guest-bubble')).toHaveText('Variable item') + await expect(page.locator('text=Ok great!')).toBeVisible() await page.click('text="Item 1"') await page.fill('input[value="Item 1"]', '{{Item 2}}') await page.click('[data-testid="block1-icon"]') await page.click('text=Multiple choice?') await page.click('text="Restart"') - await typebotViewer(page).locator('text="Variable item" >> nth=0').click() - await typebotViewer(page).locator('text="Variable item" >> nth=1').click() - await typebotViewer(page).locator('text="Send"').click() + await page.getByTestId('button').first().click() + await page.getByTestId('button').nth(1).click() + await page.locator('text="Send"').click() await expect( - typebotViewer(page).locator('text="Variable item, Variable item"') + page.locator('text="Variable item, Variable item"') ).toBeVisible() }) diff --git a/apps/builder/src/features/blocks/inputs/date/date.spec.ts b/apps/builder/src/features/blocks/inputs/date/date.spec.ts index 0301a42486..12d8564a7d 100644 --- a/apps/builder/src/features/blocks/inputs/date/date.spec.ts +++ b/apps/builder/src/features/blocks/inputs/date/date.spec.ts @@ -2,7 +2,6 @@ import test, { expect } from '@playwright/test' import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { defaultDateInputOptions, InputBlockType } from 'models' -import { typebotViewer } from 'utils/playwright/testHelpers' import { createId } from '@paralleldrive/cuid2' test.describe('Date input block', () => { @@ -21,15 +20,14 @@ test.describe('Date input block', () => { await page.goto(`/typebots/${typebotId}/edit`) await page.click('text=Preview') - await expect( - typebotViewer(page).locator('[data-testid="from-date"]') - ).toHaveAttribute('type', 'date') - await expect(typebotViewer(page).locator(`button`)).toBeDisabled() - await typebotViewer(page) - .locator('[data-testid="from-date"]') - .fill('2021-01-01') - await typebotViewer(page).locator(`button`).click() - await expect(typebotViewer(page).locator('text="01/01/2021"')).toBeVisible() + await expect(page.locator('[data-testid="from-date"]')).toHaveAttribute( + 'type', + 'date' + ) + await expect(page.getByRole('button', { name: 'Send' })).toBeDisabled() + await page.locator('[data-testid="from-date"]').fill('2021-01-01') + await page.getByRole('button', { name: 'Send' }).click() + await expect(page.locator('text="01/01/2021"')).toBeVisible() await page.click(`text=Pick a date...`) await page.click('text=Is range?') @@ -39,23 +37,19 @@ test.describe('Date input block', () => { await page.fill('#button', 'Go') await page.click('text=Restart') + await expect(page.locator(`[data-testid="from-date"]`)).toHaveAttribute( + 'type', + 'datetime-local' + ) + await expect(page.locator(`[data-testid="to-date"]`)).toHaveAttribute( + 'type', + 'datetime-local' + ) + await page.locator('[data-testid="from-date"]').fill('2021-01-01T11:00') + await page.locator('[data-testid="to-date"]').fill('2022-01-01T09:00') + await page.getByRole('button', { name: 'Go' }).click() await expect( - typebotViewer(page).locator(`[data-testid="from-date"]`) - ).toHaveAttribute('type', 'datetime-local') - await expect( - typebotViewer(page).locator(`[data-testid="to-date"]`) - ).toHaveAttribute('type', 'datetime-local') - await typebotViewer(page) - .locator('[data-testid="from-date"]') - .fill('2021-01-01T11:00') - await typebotViewer(page) - .locator('[data-testid="to-date"]') - .fill('2022-01-01T09:00') - await typebotViewer(page).locator(`button`).click() - await expect( - typebotViewer(page).locator( - 'text="01/01/2021, 11:00 AM to 01/01/2022, 09:00 AM"' - ) + page.locator('text="01/01/2021, 11:00 AM to 01/01/2022, 09:00 AM"') ).toBeVisible() }) }) diff --git a/apps/builder/src/features/blocks/inputs/emailInput/emailInput.spec.ts b/apps/builder/src/features/blocks/inputs/emailInput/emailInput.spec.ts index 01b2966c75..6feecadc49 100644 --- a/apps/builder/src/features/blocks/inputs/emailInput/emailInput.spec.ts +++ b/apps/builder/src/features/blocks/inputs/emailInput/emailInput.spec.ts @@ -2,7 +2,6 @@ import test, { expect } from '@playwright/test' import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { defaultEmailInputOptions, InputBlockType } from 'models' -import { typebotViewer } from 'utils/playwright/testHelpers' import { createId } from '@paralleldrive/cuid2' test.describe('Email input block', () => { @@ -22,11 +21,11 @@ test.describe('Email input block', () => { await page.click('text=Preview') await expect( - typebotViewer(page).locator( + page.locator( `input[placeholder="${defaultEmailInputOptions.labels.placeholder}"]` ) ).toHaveAttribute('type', 'email') - await expect(typebotViewer(page).locator(`button`)).toBeDisabled() + await expect(page.getByRole('button', { name: 'Send' })).toBeDisabled() await page.click(`text=${defaultEmailInputOptions.labels.placeholder}`) await page.fill( @@ -41,19 +40,13 @@ test.describe('Email input block', () => { ) await page.click('text=Restart') - await typebotViewer(page) - .locator(`input[placeholder="Your email..."]`) - .fill('test@test') - await typebotViewer(page).locator('text=Go').click() - await expect( - typebotViewer(page).locator('text=Try again bro') - ).toBeVisible() - await typebotViewer(page) + await page.locator(`input[placeholder="Your email..."]`).fill('test@test') + await page.getByRole('button', { name: 'Go' }).click() + await expect(page.locator('text=Try again bro')).toBeVisible() + await page .locator(`input[placeholder="Your email..."]`) .fill('test@test.com') - await typebotViewer(page).locator('text=Go').click() - await expect( - typebotViewer(page).locator('text=test@test.com') - ).toBeVisible() + await page.getByRole('button', { name: 'Go' }).click() + await expect(page.locator('text=test@test.com')).toBeVisible() }) }) diff --git a/apps/builder/src/features/blocks/inputs/fileUpload/components/FileInputSettings.tsx b/apps/builder/src/features/blocks/inputs/fileUpload/components/FileInputSettings.tsx index fcedcf13ef..ced9e732ee 100644 --- a/apps/builder/src/features/blocks/inputs/fileUpload/components/FileInputSettings.tsx +++ b/apps/builder/src/features/blocks/inputs/fileUpload/components/FileInputSettings.tsx @@ -63,7 +63,7 @@ export const FileInputSettings = ({ options, onOptionsChange }: Props) => { diff --git a/apps/builder/src/features/blocks/inputs/fileUpload/fileUpload.spec.ts b/apps/builder/src/features/blocks/inputs/fileUpload/fileUpload.spec.ts index de6f095481..11a3258c9f 100644 --- a/apps/builder/src/features/blocks/inputs/fileUpload/fileUpload.spec.ts +++ b/apps/builder/src/features/blocks/inputs/fileUpload/fileUpload.spec.ts @@ -2,7 +2,6 @@ import test, { expect } from '@playwright/test' import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { defaultFileInputOptions, InputBlockType } from 'models' -import { typebotViewer } from 'utils/playwright/testHelpers' import { createId } from '@paralleldrive/cuid2' import { freeWorkspaceId } from 'utils/playwright/databaseSetup' import { getTestAsset } from '@/test/utils/playwright' @@ -24,14 +23,12 @@ test('options should work', async ({ page }) => { await page.goto(`/typebots/${typebotId}/edit`) await page.click('text=Preview') - await expect( - typebotViewer(page).locator(`text=Click to upload`) - ).toBeVisible() - await expect(typebotViewer(page).locator(`text="Skip"`)).toBeHidden() - await typebotViewer(page) + await expect(page.locator(`text=Click to upload`)).toBeVisible() + await expect(page.locator(`text="Skip"`)).toBeHidden() + await page .locator(`input[type="file"]`) .setInputFiles([getTestAsset('avatar.jpg')]) - await expect(typebotViewer(page).locator(`text=File uploaded`)).toBeVisible() + await expect(page.locator(`text=File uploaded`)).toBeVisible() await page.click('text="Collect file"') await page.click('text="Required?"') await page.click('text="Allow multiple files?"') @@ -41,20 +38,18 @@ test('options should work', async ({ page }) => { await page.fill('[value="Skip"]', 'Pass') await page.fill('input[value="10"]', '20') await page.click('text="Restart"') - await expect(typebotViewer(page).locator(`text="Pass"`)).toBeVisible() - await expect(typebotViewer(page).locator(`text="Upload now!!"`)).toBeVisible() - await typebotViewer(page) + await expect(page.locator(`text="Pass"`)).toBeVisible() + await expect(page.locator(`text="Upload now!!"`)).toBeVisible() + await page .locator(`input[type="file"]`) .setInputFiles([ getTestAsset('avatar.jpg'), getTestAsset('avatar.jpg'), getTestAsset('avatar.jpg'), ]) - await expect(typebotViewer(page).locator(`text="3"`)).toBeVisible() - await typebotViewer(page).locator('text="Go"').click() - await expect( - typebotViewer(page).locator(`text="3 files uploaded"`) - ).toBeVisible() + await expect(page.locator(`text="3"`)).toBeVisible() + await page.locator('text="Go"').click() + await expect(page.locator(`text="3 files uploaded"`)).toBeVisible() }) test.describe('Free workspace', () => { diff --git a/apps/builder/src/features/blocks/inputs/number/number.spec.ts b/apps/builder/src/features/blocks/inputs/number/number.spec.ts index 7580848135..db0af544f4 100644 --- a/apps/builder/src/features/blocks/inputs/number/number.spec.ts +++ b/apps/builder/src/features/blocks/inputs/number/number.spec.ts @@ -2,7 +2,6 @@ import test, { expect } from '@playwright/test' import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { defaultNumberInputOptions, InputBlockType } from 'models' -import { typebotViewer } from 'utils/playwright/testHelpers' import { createId } from '@paralleldrive/cuid2' test.describe('Number input block', () => { @@ -22,11 +21,11 @@ test.describe('Number input block', () => { await page.click('text=Preview') await expect( - typebotViewer(page).locator( + page.locator( `input[placeholder="${defaultNumberInputOptions.labels.placeholder}"]` ) ).toHaveAttribute('type', 'number') - await expect(typebotViewer(page).locator(`button`)).toBeDisabled() + await expect(page.getByRole('button', { name: 'Send' })).toBeDisabled() await page.click(`text=${defaultNumberInputOptions.labels.placeholder}`) await page.fill('#placeholder', 'Your number...') @@ -37,15 +36,13 @@ test.describe('Number input block', () => { await page.fill('[role="spinbutton"] >> nth=2', '10') await page.click('text=Restart') - const input = typebotViewer(page).locator( - `input[placeholder="Your number..."]` - ) + const input = page.locator(`input[placeholder="Your number..."]`) await input.fill('-1') await input.press('Enter') await input.fill('150') await input.press('Enter') await input.fill('50') await input.press('Enter') - await expect(typebotViewer(page).locator('text=50')).toBeVisible() + await expect(page.locator('text=50')).toBeVisible() }) }) diff --git a/apps/builder/src/features/blocks/inputs/payment/payment.spec.ts b/apps/builder/src/features/blocks/inputs/payment/payment.spec.ts index df1cfa097b..cc69d2f7dc 100644 --- a/apps/builder/src/features/blocks/inputs/payment/payment.spec.ts +++ b/apps/builder/src/features/blocks/inputs/payment/payment.spec.ts @@ -3,7 +3,6 @@ import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { defaultPaymentInputOptions, InputBlockType } from 'models' import { createId } from '@paralleldrive/cuid2' -import { typebotViewer } from 'utils/playwright/testHelpers' import { stripePaymentForm } from '@/test/utils/selectorUtils' test.describe('Payment input block', () => { @@ -59,9 +58,9 @@ test.describe('Payment input block', () => { .locator(`[placeholder="MM / YY"]`) .fill('12 / 25') await stripePaymentForm(page).locator(`[placeholder="CVC"]`).fill('240') - await typebotViewer(page).locator(`text="Pay 30€"`).click() + await page.locator(`text="Pay 30€"`).click() await expect( - typebotViewer(page).locator(`text="Your card has been declined."`) + page.locator(`text="Your card has been declined."`) ).toBeVisible() await stripePaymentForm(page) .locator(`[placeholder="1234 1234 1234 1234"]`) @@ -69,7 +68,7 @@ test.describe('Payment input block', () => { const zipInput = stripePaymentForm(page).getByPlaceholder('90210') const isZipInputVisible = await zipInput.isVisible() if (isZipInputVisible) await zipInput.fill('12345') - await typebotViewer(page).locator(`text="Pay 30€"`).click() - await expect(typebotViewer(page).locator(`text="Success"`)).toBeVisible() + await page.locator(`text="Pay 30€"`).click() + await expect(page.locator(`text="Success"`)).toBeVisible() }) }) diff --git a/apps/builder/src/features/blocks/inputs/phone/phone.spec.ts b/apps/builder/src/features/blocks/inputs/phone/phone.spec.ts index 774feaa5bd..c0530968c4 100644 --- a/apps/builder/src/features/blocks/inputs/phone/phone.spec.ts +++ b/apps/builder/src/features/blocks/inputs/phone/phone.spec.ts @@ -2,7 +2,6 @@ import test, { expect } from '@playwright/test' import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { defaultPhoneInputOptions, InputBlockType } from 'models' -import { typebotViewer } from 'utils/playwright/testHelpers' import { createId } from '@paralleldrive/cuid2' test.describe('Phone input block', () => { @@ -22,11 +21,11 @@ test.describe('Phone input block', () => { await page.click('text=Preview') await expect( - typebotViewer(page).locator( + page.locator( `input[placeholder="${defaultPhoneInputOptions.labels.placeholder}"]` ) ).toHaveAttribute('type', 'tel') - await expect(typebotViewer(page).locator(`button`)).toBeDisabled() + await expect(page.getByRole('button', { name: 'Send' })).toBeDisabled() await page.click(`text=${defaultPhoneInputOptions.labels.placeholder}`) await page.fill('#placeholder', '+33 XX XX XX XX') @@ -37,21 +36,14 @@ test.describe('Phone input block', () => { ) await page.click('text=Restart') - await typebotViewer(page) - .locator(`input[placeholder="+33 XX XX XX XX"]`) - .fill('+33 6 73') - await expect(typebotViewer(page).locator(`img`)).toHaveAttribute( - 'alt', - 'France' - ) - await typebotViewer(page).locator('button >> text="Go"').click() - await expect( - typebotViewer(page).locator('text=Try again bro') - ).toBeVisible() - await typebotViewer(page) + await page.locator(`input[placeholder="+33 XX XX XX XX"]`).type('+33 6 73') + await expect(page.getByRole('combobox')).toHaveText(/πŸ‡«πŸ‡·.+/) + await page.locator('button >> text="Go"').click() + await expect(page.locator('text=Try again bro')).toBeVisible() + await page .locator(`input[placeholder="+33 XX XX XX XX"]`) .fill('+33 6 73 54 45 67') - await typebotViewer(page).locator('button >> text="Go"').click() - await expect(typebotViewer(page).locator('text=+33673544567')).toBeVisible() + await page.locator('button >> text="Go"').click() + await expect(page.locator('text=+33 6 73 54 45 67')).toBeVisible() }) }) diff --git a/apps/builder/src/features/blocks/inputs/rating/rating.spec.ts b/apps/builder/src/features/blocks/inputs/rating/rating.spec.ts index 1520afb437..5ccbc09b43 100644 --- a/apps/builder/src/features/blocks/inputs/rating/rating.spec.ts +++ b/apps/builder/src/features/blocks/inputs/rating/rating.spec.ts @@ -2,7 +2,6 @@ import test, { expect } from '@playwright/test' import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { defaultRatingInputOptions, InputBlockType } from 'models' -import { typebotViewer } from 'utils/playwright/testHelpers' import { createId } from '@paralleldrive/cuid2' const boxSvg = ` { await page.goto(`/typebots/${typebotId}/edit`) await page.click('text=Preview') - await expect(typebotViewer(page).locator(`text=Send`)).toBeHidden() - await typebotViewer(page).locator(`text=8`).click() - await typebotViewer(page).locator(`text=Send`).click() - await expect(typebotViewer(page).locator(`text=8`)).toBeVisible() + await expect(page.locator(`text=Send`)).toBeHidden() + await page.locator(`text=8`).click() + await page.locator(`text=Send`).click() + await expect(page.locator(`text=8`)).toBeVisible() await page.click('text=Rate from 0 to 10') await page.click('text="10"') await page.click('text="5"') @@ -48,14 +47,10 @@ test('options should work', async ({ page }) => { await page.fill('[placeholder="Not likely at all"]', 'Not likely at all') await page.fill('[placeholder="Extremely likely"]', 'Extremely likely') await page.click('text="Restart"') - await expect(typebotViewer(page).locator(`text=8`)).toBeHidden() - await expect(typebotViewer(page).locator(`text=4`)).toBeHidden() - await expect( - typebotViewer(page).locator(`text=Not likely at all`) - ).toBeVisible() - await expect( - typebotViewer(page).locator(`text=Extremely likely`) - ).toBeVisible() - await typebotViewer(page).locator(`svg >> nth=4`).click() - await expect(typebotViewer(page).locator(`text=5`)).toBeVisible() + await expect(page.locator(`text=8`)).toBeHidden() + await expect(page.locator(`text=4`)).toBeHidden() + await expect(page.locator(`text=Not likely at all`)).toBeVisible() + await expect(page.locator(`text=Extremely likely`)).toBeVisible() + await page.locator(`svg >> nth=4`).click() + await expect(page.locator(`text=5`)).toBeVisible() }) diff --git a/apps/builder/src/features/blocks/inputs/textInput/textInput.spec.ts b/apps/builder/src/features/blocks/inputs/textInput/textInput.spec.ts index 575f9b3d8f..a3019539a8 100644 --- a/apps/builder/src/features/blocks/inputs/textInput/textInput.spec.ts +++ b/apps/builder/src/features/blocks/inputs/textInput/textInput.spec.ts @@ -2,7 +2,6 @@ import test, { expect } from '@playwright/test' import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { defaultTextInputOptions, InputBlockType } from 'models' -import { typebotViewer } from 'utils/playwright/testHelpers' import { createId } from '@paralleldrive/cuid2' test.describe.parallel('Text input block', () => { @@ -22,11 +21,11 @@ test.describe.parallel('Text input block', () => { await page.click('text=Preview') await expect( - typebotViewer(page).locator( + page.locator( `input[placeholder="${defaultTextInputOptions.labels.placeholder}"]` ) ).toHaveAttribute('type', 'text') - await expect(typebotViewer(page).locator(`button`)).toBeDisabled() + await expect(page.getByRole('button', { name: 'Send' })).toBeDisabled() await page.click(`text=${defaultTextInputOptions.labels.placeholder}`) await page.fill('#placeholder', 'Your name...') @@ -35,8 +34,8 @@ test.describe.parallel('Text input block', () => { await page.click('text=Restart') await expect( - typebotViewer(page).locator(`textarea[placeholder="Your name..."]`) + page.locator(`textarea[placeholder="Your name..."]`) ).toBeVisible() - await expect(typebotViewer(page).locator(`text=Go`)).toBeVisible() + await expect(page.getByRole('button', { name: 'Go' })).toBeVisible() }) }) diff --git a/apps/builder/src/features/blocks/inputs/url/url.spec.ts b/apps/builder/src/features/blocks/inputs/url/url.spec.ts index 229ed7f84b..2d79f025c6 100644 --- a/apps/builder/src/features/blocks/inputs/url/url.spec.ts +++ b/apps/builder/src/features/blocks/inputs/url/url.spec.ts @@ -2,7 +2,6 @@ import test, { expect } from '@playwright/test' import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { defaultUrlInputOptions, InputBlockType } from 'models' -import { typebotViewer } from 'utils/playwright/testHelpers' import { createId } from '@paralleldrive/cuid2' test.describe('Url input block', () => { @@ -22,11 +21,13 @@ test.describe('Url input block', () => { await page.click('text=Preview') await expect( - typebotViewer(page).locator( + page.locator( `input[placeholder="${defaultUrlInputOptions.labels.placeholder}"]` ) ).toHaveAttribute('type', 'url') - await expect(typebotViewer(page).locator(`button`)).toBeDisabled() + await expect( + page.locator('typebot-standard').locator(`button`) + ).toBeDisabled() await page.click(`text=${defaultUrlInputOptions.labels.placeholder}`) await page.fill('#placeholder', 'Your URL...') @@ -38,19 +39,15 @@ test.describe('Url input block', () => { ) await page.click('text=Restart') - await typebotViewer(page) + await page .locator(`input[placeholder="Your URL..."]`) .fill('https://https://test') - await typebotViewer(page).locator('button >> text="Go"').click() - await expect( - typebotViewer(page).locator('text=Try again bro') - ).toBeVisible() - await typebotViewer(page) + await page.locator('button >> text="Go"').click() + await expect(page.locator('text=Try again bro')).toBeVisible() + await page .locator(`input[placeholder="Your URL..."]`) .fill('https://website.com') - await typebotViewer(page).locator('button >> text="Go"').click() - await expect( - typebotViewer(page).locator('text=https://website.com') - ).toBeVisible() + await page.locator('button >> text="Go"').click() + await expect(page.locator('text=https://website.com')).toBeVisible() }) }) diff --git a/apps/builder/src/features/blocks/integrations/chatwoot/chatwoot.spec.ts b/apps/builder/src/features/blocks/integrations/chatwoot/chatwoot.spec.ts index c64af89441..adaed434f9 100644 --- a/apps/builder/src/features/blocks/integrations/chatwoot/chatwoot.spec.ts +++ b/apps/builder/src/features/blocks/integrations/chatwoot/chatwoot.spec.ts @@ -36,7 +36,7 @@ test.describe('Chatwoot block', () => { await page.getByLabel('Phone number').fill('+33654347543') await page.getByRole('button', { name: 'Preview', exact: true }).click() await expect( - page.getByText("Chatwoot won't open in preview mode").nth(0) + page.getByText('Chatwoot block is not supported in preview').nth(0) ).toBeVisible() }) }) diff --git a/apps/builder/src/features/blocks/integrations/googleSheets/googleSheets.spec.ts b/apps/builder/src/features/blocks/integrations/googleSheets/googleSheets.spec.ts index 817fd73769..62ec180075 100644 --- a/apps/builder/src/features/blocks/integrations/googleSheets/googleSheets.spec.ts +++ b/apps/builder/src/features/blocks/integrations/googleSheets/googleSheets.spec.ts @@ -1,6 +1,5 @@ import test, { expect, Page } from '@playwright/test' import { importTypebotInDatabase } from 'utils/playwright/databaseActions' -import { typebotViewer } from 'utils/playwright/testHelpers' import { createId } from '@paralleldrive/cuid2' import { getTestAsset } from '@/test/utils/playwright' @@ -33,25 +32,17 @@ test.describe.parallel('Google sheets integration', () => { ) await page.click('text=Preview') - await typebotViewer(page) + await page + .locator('typebot-standard') .locator('input[placeholder="Type your email..."]') .fill('georges@gmail.com') - await Promise.all([ - page.waitForResponse( - (resp) => - resp - .request() - .url() - .includes( - '/api/integrations/google-sheets/spreadsheets/1k_pIDw3YHl9tlZusbBVSBRY0PeRPd2H6t4Nj7rwnOtM/sheets/0' - ) && - resp.status() === 200 && - resp.request().method() === 'POST' - ), - typebotViewer(page) - .locator('input[placeholder="Type your email..."]') - .press('Enter'), - ]) + await page + .locator('typebot-standard') + .locator('input[placeholder="Type your email..."]') + .press('Enter') + await expect( + page.getByText('Succesfully inserted row in CRM > Sheet1').nth(0) + ).toBeVisible() }) test('Update row should work', async ({ page }) => { @@ -82,25 +73,17 @@ test.describe.parallel('Google sheets integration', () => { ) await page.click('text=Preview') - await typebotViewer(page) + await page + .locator('typebot-standard') .locator('input[placeholder="Type your email..."]') .fill('test@test.com') - await Promise.all([ - page.waitForResponse( - (resp) => - resp - .request() - .url() - .includes( - '/api/integrations/google-sheets/spreadsheets/1k_pIDw3YHl9tlZusbBVSBRY0PeRPd2H6t4Nj7rwnOtM/sheets/0' - ) && - resp.status() === 200 && - resp.request().method() === 'POST' - ), - typebotViewer(page) - .locator('input[placeholder="Type your email..."]') - .press('Enter'), - ]) + await page + .locator('typebot-standard') + .locator('input[placeholder="Type your email..."]') + .press('Enter') + await expect( + page.getByText('Succesfully updated row in CRM > Sheet1').nth(0) + ).toBeVisible() }) test('Get row should work', async ({ page }) => { @@ -143,15 +126,17 @@ test.describe.parallel('Google sheets integration', () => { await createNewVar(page, 'Last name') await page.click('text=Preview') - await typebotViewer(page) + await page + .locator('typebot-standard') .locator('input[placeholder="Type your email..."]') .fill('test2@test.com') - await typebotViewer(page) + await page + .locator('typebot-standard') .locator('input[placeholder="Type your email..."]') .press('Enter') - await expect(typebotViewer(page).locator('text=Your name is:')).toHaveText( - /John|Fred|Georges/ - ) + await expect( + page.locator('typebot-standard').locator('text=Your name is:') + ).toHaveText(/John|Fred|Georges/) }) }) diff --git a/apps/builder/src/features/blocks/integrations/sendEmail/components/SendEmailSettings/SendEmailSettings.tsx b/apps/builder/src/features/blocks/integrations/sendEmail/components/SendEmailSettings/SendEmailSettings.tsx index fd03db10ce..8173201f1f 100644 --- a/apps/builder/src/features/blocks/integrations/sendEmail/components/SendEmailSettings/SendEmailSettings.tsx +++ b/apps/builder/src/features/blocks/integrations/sendEmail/components/SendEmailSettings/SendEmailSettings.tsx @@ -181,7 +181,7 @@ export const SendEmailSettings = ({ options, onOptionsChange }: Props) => { {options.isBodyCode ? ( diff --git a/apps/builder/src/features/blocks/integrations/sendEmail/queries/sendEmail.spec.ts b/apps/builder/src/features/blocks/integrations/sendEmail/queries/sendEmail.spec.ts index 96ab8cc9af..122e6b2837 100644 --- a/apps/builder/src/features/blocks/integrations/sendEmail/queries/sendEmail.spec.ts +++ b/apps/builder/src/features/blocks/integrations/sendEmail/queries/sendEmail.spec.ts @@ -1,6 +1,5 @@ import test, { expect } from '@playwright/test' import { importTypebotInDatabase } from 'utils/playwright/databaseActions' -import { typebotViewer } from 'utils/playwright/testHelpers' import { createId } from '@paralleldrive/cuid2' import { getTestAsset } from '@/test/utils/playwright' @@ -64,7 +63,7 @@ test.describe('Send email block', () => { await page.fill('[data-testid="body-input"]', 'Here is my email') await page.click('text=Preview') - await typebotViewer(page).locator('text=Go').click() + await page.locator('typebot-standard').locator('text=Go').click() await expect( page.locator('text=Emails are not sent in preview mode >> nth=0') ).toBeVisible() diff --git a/apps/builder/src/features/blocks/integrations/webhook/components/WebhookSettings/WebhookSettings.tsx b/apps/builder/src/features/blocks/integrations/webhook/components/WebhookSettings/WebhookSettings.tsx index 34764ee3f0..0278b1721f 100644 --- a/apps/builder/src/features/blocks/integrations/webhook/components/WebhookSettings/WebhookSettings.tsx +++ b/apps/builder/src/features/blocks/integrations/webhook/components/WebhookSettings/WebhookSettings.tsx @@ -223,7 +223,7 @@ export const WebhookSettings = ({ /> {(options.isCustomBody ?? true) && ( )} {testResponse && ( - + )} {(testResponse || options?.responseVariableMapping.length > 0) && ( diff --git a/apps/builder/src/features/blocks/logic/condition/condition.spec.ts b/apps/builder/src/features/blocks/logic/condition/condition.spec.ts index 924220fe69..d61ff16c61 100644 --- a/apps/builder/src/features/blocks/logic/condition/condition.spec.ts +++ b/apps/builder/src/features/blocks/logic/condition/condition.spec.ts @@ -1,5 +1,4 @@ import test, { expect } from '@playwright/test' -import { typebotViewer } from 'utils/playwright/testHelpers' import { importTypebotInDatabase } from 'utils/playwright/databaseActions' import { createId } from '@paralleldrive/cuid2' import { getTestAsset } from '@/test/utils/playwright' @@ -51,30 +50,33 @@ test.describe('Condition block', () => { await page.fill('input[placeholder="Type a value..."]', '20') await page.click('text=Preview') - await typebotViewer(page) + await page + .locator('typebot-standard') .locator('input[placeholder="Type a number..."]') .fill('15') - await typebotViewer(page).locator('text=Send').click() + await page.locator('typebot-standard').locator('text=Send').click() await expect( - typebotViewer(page).locator('text=You are younger than 20') + page.locator('typebot-standard').getByText('You are younger than 20') ).toBeVisible() await page.click('text=Restart') - await typebotViewer(page) + await page + .locator('typebot-standard') .locator('input[placeholder="Type a number..."]') .fill('45') - await typebotViewer(page).locator('text=Send').click() + await page.locator('typebot-standard').locator('text=Send').click() await expect( - typebotViewer(page).locator('text=You are older than 20') + page.locator('typebot-standard').getByText('You are older than 20') ).toBeVisible() await page.click('text=Restart') - await typebotViewer(page) + await page + .locator('typebot-standard') .locator('input[placeholder="Type a number..."]') .fill('90') - await typebotViewer(page).locator('text=Send').click() + await page.locator('typebot-standard').locator('text=Send').click() await expect( - typebotViewer(page).locator('text=You are older than 80') + page.locator('typebot-standard').getByText('You are older than 80') ).toBeVisible() }) }) diff --git a/apps/builder/src/features/blocks/logic/redirect/redirect.spec.ts b/apps/builder/src/features/blocks/logic/redirect/redirect.spec.ts index ec88fc6884..6c509003d9 100644 --- a/apps/builder/src/features/blocks/logic/redirect/redirect.spec.ts +++ b/apps/builder/src/features/blocks/logic/redirect/redirect.spec.ts @@ -1,5 +1,4 @@ import test, { expect } from '@playwright/test' -import { typebotViewer } from 'utils/playwright/testHelpers' import { importTypebotInDatabase } from 'utils/playwright/databaseActions' import { createId } from '@paralleldrive/cuid2' import { getTestAsset } from '@/test/utils/playwright' @@ -20,7 +19,7 @@ test.describe('Redirect block', () => { await page.fill('input[placeholder="Type a URL..."]', 'google.com') await page.click('text=Preview') - await typebotViewer(page).locator('text=Go to URL').click() + await page.locator('typebot-standard').locator('text=Go to URL').click() await expect(page).toHaveURL('https://www.google.com') await page.goBack() @@ -30,7 +29,7 @@ test.describe('Redirect block', () => { await page.click('text=Preview') const [newPage] = await Promise.all([ context.waitForEvent('page'), - typebotViewer(page).locator('text=Go to URL').click(), + page.locator('typebot-standard').locator('text=Go to URL').click(), ]) await newPage.waitForLoadState() await expect(newPage).toHaveURL('https://www.google.com') diff --git a/apps/builder/src/features/blocks/logic/script/components/ScriptSettings.tsx b/apps/builder/src/features/blocks/logic/script/components/ScriptSettings.tsx index c7b755dd99..2a282feb6e 100644 --- a/apps/builder/src/features/blocks/logic/script/components/ScriptSettings.tsx +++ b/apps/builder/src/features/blocks/logic/script/components/ScriptSettings.tsx @@ -41,8 +41,8 @@ export const ScriptSettings = ({ options, onOptionsChange }: Props) => { Code: diff --git a/apps/builder/src/features/blocks/logic/script/script.spec.ts b/apps/builder/src/features/blocks/logic/script/script.spec.ts index f35720f2bc..3bea66b741 100644 --- a/apps/builder/src/features/blocks/logic/script/script.spec.ts +++ b/apps/builder/src/features/blocks/logic/script/script.spec.ts @@ -1,5 +1,4 @@ import test, { expect } from '@playwright/test' -import { typebotViewer } from 'utils/playwright/testHelpers' import { importTypebotInDatabase } from 'utils/playwright/databaseActions' import { createId } from '@paralleldrive/cuid2' import { getTestAsset } from '@/test/utils/playwright' @@ -20,7 +19,7 @@ test.describe('Script block', () => { ) await page.click('text=Preview') - await typebotViewer(page).locator('text=Trigger code').click() + await page.getByRole('button', { name: 'Trigger code' }).click() await expect(page).toHaveURL('https://www.google.com') }) }) diff --git a/apps/builder/src/features/blocks/logic/setVariable/components/SetVariableSettings.tsx b/apps/builder/src/features/blocks/logic/setVariable/components/SetVariableSettings.tsx index a6b30f3fac..7e26df570b 100644 --- a/apps/builder/src/features/blocks/logic/setVariable/components/SetVariableSettings.tsx +++ b/apps/builder/src/features/blocks/logic/setVariable/components/SetVariableSettings.tsx @@ -51,9 +51,9 @@ export const SetVariableSettings = ({ options, onOptionsChange }: Props) => { {options.isCode ?? false ? ( ) : (
- ` -} diff --git a/apps/builder/src/features/publish/components/embeds/codeSnippets/Container/EmbedSettings.tsx b/apps/builder/src/features/publish/components/embeds/codeSnippets/Container/EmbedSettings.tsx deleted file mode 100644 index fe2cd5f693..0000000000 --- a/apps/builder/src/features/publish/components/embeds/codeSnippets/Container/EmbedSettings.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import { - StackProps, - Stack, - Flex, - Heading, - FormControl, - FormLabel, - Switch, - Input, - HStack, - Text, -} from '@chakra-ui/react' -import { DropdownList } from '@/components/DropdownList' -import { useState, useEffect } from 'react' - -type StandardEmbedWindowSettingsProps = { - onUpdateWindowSettings: (windowSettings: { - heightLabel: string - widthLabel: string - }) => void -} -export const StandardEmbedWindowSettings = ({ - onUpdateWindowSettings, - ...props -}: StandardEmbedWindowSettingsProps & StackProps) => { - const [fullscreen, setFullscreen] = useState(false) - const [inputValues, setInputValues] = useState({ - widthValue: '100', - widthType: '%', - heightValue: '600', - heightType: 'px', - }) - - useEffect(() => { - onUpdateWindowSettings({ - widthLabel: fullscreen - ? '100%' - : inputValues.widthValue + inputValues.widthType, - heightLabel: fullscreen - ? '100vh' - : inputValues.heightValue + inputValues.heightType, - }) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [inputValues, fullscreen]) - - const handleWidthTypeSelect = (widthType: string) => - setInputValues({ ...inputValues, widthType }) - const handleHeightTypeSelect = (heightType: string) => - setInputValues({ ...inputValues, heightType }) - - return ( - - - - Window settings - - - - Set to fullscreen? - - setFullscreen(!fullscreen)} - isChecked={fullscreen} - /> - - - - {!fullscreen && ( - <> - - Width - - - setInputValues({ - ...inputValues, - widthValue: e.target.value, - }) - } - w="70px" - value={inputValues.widthValue} - /> - - items={['px', '%']} - onItemSelect={handleWidthTypeSelect} - currentItem={inputValues.widthType} - /> - - - - Height - - - setInputValues({ - ...inputValues, - heightValue: e.target.value, - }) - } - w="70px" - value={inputValues.heightValue} - /> - - items={['px', '%']} - onItemSelect={handleHeightTypeSelect} - currentItem={inputValues.heightType} - /> - - - - )} - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/codeSnippets/Popup/EmbedCode.tsx b/apps/builder/src/features/publish/components/embeds/codeSnippets/Popup/EmbedCode.tsx deleted file mode 100644 index 291eff2adb..0000000000 --- a/apps/builder/src/features/publish/components/embeds/codeSnippets/Popup/EmbedCode.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { FlexProps } from '@chakra-ui/react' -import { useTypebot } from '@/features/editor' -import parserHtml from 'prettier/parser-html' -import prettier from 'prettier/standalone' -import { PopupParams } from 'typebot-js' -import { env, getViewerUrl } from 'utils' -import { parseInitPopupCode, typebotJsHtml } from '../params' -import { CodeEditor } from '@/components/CodeEditor' - -type PopupEmbedCodeProps = { - delay?: number - withStarterVariables?: boolean - onCopied?: () => void -} - -export const PopupEmbedCode = ({ delay }: PopupEmbedCodeProps & FlexProps) => { - const { typebot } = useTypebot() - const snippet = prettier.format( - createSnippet({ - url: `${env('VIEWER_INTERNAL_URL') ?? getViewerUrl()}/${ - typebot?.publicId - }`, - delay, - }), - { - parser: 'html', - plugins: [parserHtml], - } - ) - return -} - -const createSnippet = (params: PopupParams): string => { - const jsCode = parseInitPopupCode(params) - return `${typebotJsHtml} - ` -} diff --git a/apps/builder/src/features/publish/components/embeds/codeSnippets/Popup/EmbedSettings.tsx b/apps/builder/src/features/publish/components/embeds/codeSnippets/Popup/EmbedSettings.tsx deleted file mode 100644 index 2cb2b024e4..0000000000 --- a/apps/builder/src/features/publish/components/embeds/codeSnippets/Popup/EmbedSettings.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { - StackProps, - Stack, - Flex, - Heading, - NumberInput, - NumberInputField, - Switch, - HStack, - NumberIncrementStepper, - NumberDecrementStepper, -} from '@chakra-ui/react' -import { useState, useEffect } from 'react' -import { PopupParams } from 'typebot-js' - -type PopupEmbedSettingsProps = { - onUpdateSettings: (windowSettings: Pick) => void -} -export const PopupEmbedSettings = ({ - onUpdateSettings, - ...props -}: PopupEmbedSettingsProps & StackProps) => { - const [isEnabled, setIsEnabled] = useState(false) - const [inputValue, setInputValue] = useState(0) - - useEffect(() => { - onUpdateSettings({ - delay: isEnabled ? inputValue * 1000 : undefined, - }) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [inputValue, isEnabled]) - - return ( - - - - Popup settings - - - - - -

Appearance delay

- setIsEnabled(e.target.checked)} - /> -
- - {isEnabled && ( - setInputValue(val)} - value={inputValue} - min={0} - > - - - - - - - )} -
-
- ) -} diff --git a/apps/builder/src/features/publish/components/embeds/codeSnippets/ReactCode.tsx b/apps/builder/src/features/publish/components/embeds/codeSnippets/ReactCode.tsx deleted file mode 100644 index b310af5edc..0000000000 --- a/apps/builder/src/features/publish/components/embeds/codeSnippets/ReactCode.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { FlexProps } from '@chakra-ui/react' -import React from 'react' -import { BubbleParams, IframeParams, PopupParams } from 'typebot-js' -import { - parseInitBubbleCode, - parseInitContainerCode, - parseInitPopupCode, -} from './params' -import parserBabel from 'prettier/parser-babel' -import prettier from 'prettier/standalone' -import { useTypebot } from '@/features/editor' -import { env, getViewerUrl } from 'utils' -import { CodeEditor } from '@/components/CodeEditor' - -type StandardReactDivProps = { widthLabel: string; heightLabel: string } -export const StandardReactDiv = ({ - widthLabel, - heightLabel, -}: StandardReactDivProps) => { - const { typebot } = useTypebot() - const snippet = prettier.format( - parseContainerSnippet({ - url: `${env('VIEWER_INTERNAL_URL') ?? getViewerUrl()}/${ - typebot?.publicId - }`, - heightLabel, - widthLabel, - }), - { - parser: 'babel', - plugins: [parserBabel], - } - ) - return -} - -type SnippetProps = IframeParams & - Pick - -const parseContainerSnippet = ({ - url, - customDomain, - backgroundColor, - hiddenVariables, - ...embedProps -}: SnippetProps): string => { - const jsCode = parseInitContainerCode({ - url, - customDomain, - backgroundColor, - hiddenVariables, - }) - return `import Typebot from "typebot-js"; - - const Component = () => { - useEffect(()=> { - ${jsCode} - }, []) - - return
- }` -} - -type PopupEmbedCodeProps = { - delay?: number - withStarterVariables?: boolean -} - -export const PopupReactCode = ({ delay }: PopupEmbedCodeProps & FlexProps) => { - const { typebot } = useTypebot() - const snippet = prettier.format( - parsePopupSnippet({ - url: `${env('VIEWER_INTERNAL_URL') ?? getViewerUrl()}/${ - typebot?.publicId - }`, - delay, - }), - { - parser: 'babel', - plugins: [parserBabel], - } - ) - return -} - -const parsePopupSnippet = ({ - url, - customDomain, - backgroundColor, - hiddenVariables, - delay, -}: PopupParams): string => { - const jsCode = parseInitPopupCode({ - url, - customDomain, - backgroundColor, - hiddenVariables, - delay, - }) - return `import Typebot from "typebot-js"; - - const Component = () => { - useEffect(()=> { - ${jsCode} - }, []) - - return <>; - }` -} - -type ChatEmbedCodeProps = { - withStarterVariables?: boolean -} & Pick - -export const ChatReactCode = ({ - proactiveMessage, - button, -}: ChatEmbedCodeProps & FlexProps) => { - const { typebot } = useTypebot() - const snippet = prettier.format( - parseBubbleSnippet({ - url: `${env('VIEWER_INTERNAL_URL') ?? getViewerUrl()}/${ - typebot?.publicId - }`, - button, - proactiveMessage, - }), - { - parser: 'babel', - plugins: [parserBabel], - } - ) - return -} - -const parseBubbleSnippet = ({ - url, - customDomain, - backgroundColor, - hiddenVariables, - proactiveMessage, - button, -}: BubbleParams): string => { - const jsCode = parseInitBubbleCode({ - url, - customDomain, - backgroundColor, - hiddenVariables, - proactiveMessage, - button, - }) - return `import Typebot from "typebot-js"; - - const Component = () => { - useEffect(()=> { - ${jsCode} - }, []) - - return <> - }` -} diff --git a/apps/builder/src/features/publish/components/embeds/codeSnippets/params.ts b/apps/builder/src/features/publish/components/embeds/codeSnippets/params.ts deleted file mode 100644 index 7cdc429fc3..0000000000 --- a/apps/builder/src/features/publish/components/embeds/codeSnippets/params.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { - BubbleParams, - ButtonParams, - IframeParams, - PopupParams, - ProactiveMessageParams, -} from 'typebot-js' -import parserBabel from 'prettier/parser-babel' -import prettier from 'prettier/standalone' -import { isDefined } from 'utils' - -const parseStringParam = (fieldName: string, fieldValue?: string) => - fieldValue ? `${fieldName}: "${fieldValue}",` : `` - -const parseNonStringParam = ( - fieldName: string, - fieldValue?: number | boolean -) => (isDefined(fieldValue) ? `${fieldName}: ${fieldValue},` : ``) - -const parseCustomDomain = (domain?: string): string => - parseStringParam('customDomain', domain) - -const parseHiddenVariables = ( - variables: { [key: string]: string | undefined } | undefined -): string => (variables ? `hiddenVariables: ${JSON.stringify(variables)},` : ``) - -const parseBackgroundColor = (bgColor?: string): string => - parseStringParam('backgroundColor', bgColor) - -const parseDelay = (delay?: number) => parseNonStringParam('delay', delay) - -const parseButton = (button?: ButtonParams): string => { - if (!button) return '' - const iconUrlString = parseStringParam('iconUrl', button.iconUrl) - const buttonColorstring = parseStringParam('color', button.color) - const buttonIconColorString = parseStringParam('iconColor', button.iconColor) - return `button: {${iconUrlString}${buttonColorstring}${buttonIconColorString}},` -} - -const parseProactiveMessage = ( - proactiveMessage?: ProactiveMessageParams -): string => { - if (!proactiveMessage) return `` - const { avatarUrl, textContent, delay } = proactiveMessage - const avatarUrlString = parseStringParam('avatarUrl', avatarUrl) - const textContentString = parseStringParam('textContent', textContent) - const delayString = parseNonStringParam('delay', delay) - return `proactiveMessage: {${avatarUrlString}${textContentString}${delayString}},` -} - -const parseIframeParams = ({ - customDomain, - hiddenVariables, - backgroundColor, -}: Pick< - IframeParams, - 'customDomain' | 'hiddenVariables' | 'backgroundColor' ->) => ({ - customDomainString: parseCustomDomain(customDomain), - hiddenVariablesString: parseHiddenVariables(hiddenVariables), - bgColorString: parseBackgroundColor(backgroundColor), -}) - -const parsePopupParams = ({ delay }: Pick) => ({ - delayString: parseDelay(delay), -}) - -const parseBubbleParams = ({ - button, - proactiveMessage, -}: Pick) => ({ - proactiveMessageString: parseProactiveMessage(proactiveMessage), - buttonString: parseButton(button), -}) - -export const parseInitContainerCode = ({ - url, - customDomain, - backgroundColor, - hiddenVariables, -}: IframeParams) => { - const { customDomainString, hiddenVariablesString, bgColorString } = - parseIframeParams({ - customDomain, - hiddenVariables, - backgroundColor, - }) - return prettier.format( - `Typebot.initContainer("typebot-container", { - url: "${url}",${bgColorString}${customDomainString}${hiddenVariablesString} - });`, - { parser: 'babel', plugins: [parserBabel] } - ) -} - -export const parseInitPopupCode = ({ - url, - customDomain, - hiddenVariables, - backgroundColor, - delay, -}: PopupParams) => { - const { customDomainString, hiddenVariablesString, bgColorString } = - parseIframeParams({ - customDomain, - hiddenVariables, - backgroundColor, - }) - const { delayString } = parsePopupParams({ delay }) - return prettier.format( - `var typebotCommands = Typebot.initPopup({url: "${url}",${delayString}${bgColorString}${customDomainString}${hiddenVariablesString}});`, - { parser: 'babel', plugins: [parserBabel] } - ) -} - -export const parseInitBubbleCode = ({ - url, - customDomain, - hiddenVariables, - backgroundColor, - button, - proactiveMessage, -}: BubbleParams) => { - const { customDomainString, hiddenVariablesString, bgColorString } = - parseIframeParams({ - customDomain, - hiddenVariables, - backgroundColor, - }) - const { buttonString, proactiveMessageString } = parseBubbleParams({ - button, - proactiveMessage, - }) - return prettier.format( - `var typebotCommands = Typebot.initBubble({url: "${url}",${bgColorString}${customDomainString}${hiddenVariablesString}${proactiveMessageString}${buttonString}});`, - { parser: 'babel', plugins: [parserBabel] } - ) -} - -export const typebotJsHtml = `` diff --git a/apps/builder/src/features/publish/components/embeds/modals/ChooseEmbedTypeList.tsx b/apps/builder/src/features/publish/components/embeds/modals/ChooseEmbedTypeList.tsx deleted file mode 100644 index 63bb8400fc..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/ChooseEmbedTypeList.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { HStack, Button, Text, Stack } from '@chakra-ui/react' - -type ChooseEmbedTypeListProps = { - onSelectEmbedType: (type: 'standard' | 'popup' | 'bubble') => void - disabledTypes?: ('standard' | 'popup' | 'bubble')[] -} - -export const ChooseEmbedTypeList = ({ - onSelectEmbedType, - disabledTypes = [], -}: ChooseEmbedTypeListProps) => { - return ( - - onSelectEmbedType('standard')} - whiteSpace={'normal'} - spacing="6" - isDisabled={disabledTypes.includes('standard')} - > - - - - Standard - - Embed in a container on your site - - - onSelectEmbedType('popup')} - whiteSpace={'normal'} - spacing="6" - isDisabled={disabledTypes.includes('popup')} - > - - - - Popup - - - Embed in a popup window on top of your website - - - - onSelectEmbedType('bubble')} - whiteSpace={'normal'} - spacing="6" - isDisabled={disabledTypes.includes('bubble')} - > - - - - Bubble - - - Embed in a chat bubble on the corner of your site - - - - - ) -} - -const StandardEmbedSvg = () => ( - - - - - - - - - - - - - - - - -) - -const PopupEmbedSvg = () => ( - - - - - - - - - - - -) - -const BubbleEmbedSvg = () => ( - - - - -) diff --git a/apps/builder/src/features/publish/components/embeds/modals/GtmModal/GtmInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/GtmInstructions.tsx deleted file mode 100644 index 23130fa8ce..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/GtmModal/GtmInstructions.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { CodeEditor } from '@/components/CodeEditor' -import { OrderedList, ListItem, Tag } from '@chakra-ui/react' -import { useState } from 'react' -import { BubbleParams } from 'typebot-js' -import { env, getViewerUrl } from 'utils' -import { ChatEmbedCode } from '../../codeSnippets/Chat/EmbedCode' -import { ChatEmbedSettings } from '../../codeSnippets/Chat/EmbedSettings' -import { StandardEmbedWindowSettings } from '../../codeSnippets/Container/EmbedSettings' -import { - parseInitContainerCode, - typebotJsHtml, -} from '../../codeSnippets/params' -import { PopupEmbedCode } from '../../codeSnippets/Popup/EmbedCode' -import { PopupEmbedSettings } from '../../codeSnippets/Popup/EmbedSettings' -import { ModalProps } from '../../EmbedButton' - -type GtmInstructionsProps = { - type: 'standard' | 'popup' | 'bubble' - publicId: string -} - -export const GtmInstructions = ({ type, publicId }: GtmInstructionsProps) => { - switch (type) { - case 'standard': { - return - } - case 'popup': { - return - } - case 'bubble': { - return - } - } -} - -const StandardInstructions = ({ publicId }: Pick) => { - const [windowSizes, setWindowSizes] = useState({ - height: '100%', - width: '100%', - }) - - const jsCode = parseInitContainerCode({ - url: `${env('VIEWER_INTERNAL_URL') ?? getViewerUrl()}/${publicId}`, - }) - const headCode = `${typebotJsHtml} - ` - - const elementCode = `
` - return ( - - - On your GTM account dashboard, click on Add a new tag - - - Choose Custom HTML tag type - - - Paste the code below: - - - - On your webpage, you need to have an element on which the typebot will - go. It needs to have the id typebot-container: - - setWindowSizes({ - height: sizes.heightLabel, - width: sizes.widthLabel, - }) - } - /> - - - - ) -} - -const PopupInstructions = () => { - const [inputValue, setInputValue] = useState() - - return ( - - - On your GTM account dashboard, click on Add a new tag - - - Choose Custom HTML tag type - - - Paste the code below: - setInputValue(settings.delay)} - /> - - - - ) -} - -const BubbleInstructions = () => { - const [inputValues, setInputValues] = useState< - Pick - >({ - proactiveMessage: undefined, - button: { - color: '', - iconUrl: '', - }, - }) - - return ( - - - On your GTM account dashboard, click on Add a new tag - - - Choose Custom HTML tag type - - - Paste the code below: - setInputValues({ ...settings })} - /> - - - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/modals/GtmModal/GtmModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/GtmModal.tsx new file mode 100644 index 0000000000..e71a0afbe0 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/GtmModal.tsx @@ -0,0 +1,30 @@ +import React, { useState } from 'react' +import { ModalProps } from '../../EmbedButton' +import { EmbedModal } from '../../EmbedModal' +import { isDefined } from '@udecode/plate-common' +import { GtmInstructions } from './instructions/GtmInstructions' + +export const GtmModal = ({ + isOpen, + onClose, + isPublished, + publicId, +}: ModalProps) => { + const [selectedEmbedType, setSelectedEmbedType] = useState< + 'standard' | 'popup' | 'bubble' | undefined + >() + return ( + + {isDefined(selectedEmbedType) && ( + + )} + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/GtmModal/index.ts b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/index.ts new file mode 100644 index 0000000000..6169150565 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/index.ts @@ -0,0 +1 @@ +export * from './GtmModal' diff --git a/apps/builder/src/features/publish/components/embeds/modals/GtmModal/index.tsx b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/index.tsx deleted file mode 100644 index b57f5bbc92..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/GtmModal/index.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalCloseButton, - ModalBody, - ModalFooter, - IconButton, - Heading, - HStack, -} from '@chakra-ui/react' -import { ChevronLeftIcon } from '@/components/icons' -import React, { useState } from 'react' -import { ModalProps } from '../../EmbedButton' -import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList' -import { capitalize } from 'utils' -import { GtmInstructions } from './GtmInstructions' -import { AlertInfo } from '@/components/AlertInfo' - -export const GtmModal = ({ - isOpen, - onClose, - isPublished, - publicId, -}: ModalProps) => { - const [chosenEmbedType, setChosenEmbedType] = useState< - 'standard' | 'popup' | 'bubble' | undefined - >() - return ( - - - - - - {chosenEmbedType && ( - } - aria-label="back" - variant="ghost" - colorScheme="gray" - mr={2} - onClick={() => setChosenEmbedType(undefined)} - /> - )} - - Javascript {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`} - - - - - - {!isPublished && ( - You need to publish your bot first. - )} - {!chosenEmbedType ? ( - - ) : ( - - )} - - - - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmBubbleInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmBubbleInstructions.tsx new file mode 100644 index 0000000000..0498208676 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmBubbleInstructions.tsx @@ -0,0 +1,45 @@ +import { useTypebot } from '@/features/editor' +import { OrderedList, ListItem, Stack, Text, Code } from '@chakra-ui/react' +import { BubbleProps } from '@typebot.io/js' +import { useState } from 'react' +import { BubbleSettings } from '../../../settings/BubbleSettings/BubbleSettings' +import { parseDefaultBubbleTheme } from '../../Javascript/instructions/JavascriptBubbleInstructions' +import { JavascriptBubbleSnippet } from '../../Javascript/JavascriptBubbleSnippet' + +export const GtmBubbleInstructions = () => { + const { typebot } = useTypebot() + const [theme, setTheme] = useState( + parseDefaultBubbleTheme(typebot) + ) + const [previewMessage, setPreviewMessage] = + useState() + + return ( + + + On your GTM account dashboard, click on Add a new tag + + + Choose Custom HTML tag type + + + + + Paste the code below: + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmInstructions.tsx new file mode 100644 index 0000000000..133ab8edd9 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmInstructions.tsx @@ -0,0 +1,22 @@ +import { GtmBubbleInstructions } from './GtmBubbleInstructions' +import { GtmPopupInstructions } from './GtmPopupInstructions' +import { GtmStandardInstructions } from './GtmStandardInstructions' + +type GtmInstructionsProps = { + type: 'standard' | 'popup' | 'bubble' + publicId: string +} + +export const GtmInstructions = ({ type, publicId }: GtmInstructionsProps) => { + switch (type) { + case 'standard': { + return + } + case 'popup': { + return + } + case 'bubble': { + return + } + } +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmPopupInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmPopupInstructions.tsx new file mode 100644 index 0000000000..81843517c1 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmPopupInstructions.tsx @@ -0,0 +1,30 @@ +import { OrderedList, ListItem, Code, Stack, Text } from '@chakra-ui/react' +import { useState } from 'react' +import { PopupSettings } from '../../../settings/PopupSettings' +import { JavascriptPopupSnippet } from '../../Javascript/JavascriptPopupSnippet' + +export const GtmPopupInstructions = () => { + const [inputValue, setInputValue] = useState() + + return ( + + + On your GTM account dashboard, click on Add a new tag + + + Choose Custom HTML tag type + + + + + setInputValue(settings.autoShowDelay) + } + /> + Paste the code below: + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmStandardInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmStandardInstructions.tsx new file mode 100644 index 0000000000..09173b7005 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/GtmModal/instructions/GtmStandardInstructions.tsx @@ -0,0 +1,62 @@ +import { CodeEditor } from '@/components/CodeEditor' +import { OrderedList, ListItem, Code, Stack, Text } from '@chakra-ui/react' +import { Typebot } from 'models' +import { useState } from 'react' +import { StandardSettings } from '../../../settings/StandardSettings' +import { + parseStandardElementCode, + parseStandardHeadCode, +} from '../../Javascript/JavascriptStandardSnippet' + +export const GtmStandardInstructions = ({ + publicId, +}: Pick) => { + const [windowSizes, setWindowSizes] = useState<{ + height: string + width?: string + }>({ + height: '100%', + width: '100%', + }) + + const headCode = parseStandardHeadCode(publicId) + + const elementCode = parseStandardElementCode( + windowSizes.width, + windowSizes.height + ) + + return ( + + + On your GTM account dashboard, click on Add a new tag + + + Choose Custom HTML tag type + + + + Paste the code below: + + + + + + + setWindowSizes({ + height: sizes.heightLabel, + width: sizes.widthLabel, + }) + } + /> + + On your web page, you need to have an element on which the typebot + will go: + + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/IframeModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/IframeModal/IframeModal.tsx similarity index 65% rename from apps/builder/src/features/publish/components/embeds/modals/IframeModal.tsx rename to apps/builder/src/features/publish/components/embeds/modals/IframeModal/IframeModal.tsx index 8f6b3d5cf8..ebdb127f7c 100644 --- a/apps/builder/src/features/publish/components/embeds/modals/IframeModal.tsx +++ b/apps/builder/src/features/publish/components/embeds/modals/IframeModal/IframeModal.tsx @@ -11,12 +11,15 @@ import { Text, } from '@chakra-ui/react' import { useState } from 'react' -import { StandardEmbedWindowSettings } from '../codeSnippets/Container/EmbedSettings' -import { IframeEmbedCode } from '../codeSnippets/Iframe/EmbedCode' -import { ModalProps } from '../EmbedButton' +import { ModalProps } from '../../EmbedButton' +import { StandardSettings } from '../../settings/StandardSettings' +import { IframeSnippet } from './IframeSnippet' export const IframeModal = ({ isPublished, isOpen, onClose }: ModalProps) => { - const [inputValues, setInputValues] = useState({ + const [inputValues, setInputValues] = useState<{ + heightLabel: string + widthLabel?: string + }>({ heightLabel: '100%', widthLabel: '100%', }) @@ -27,17 +30,21 @@ export const IframeModal = ({ isPublished, isOpen, onClose }: ModalProps) => { Iframe - + {!isPublished && ( You need to publish your bot first. )} - Paste this anywhere in your HTML code: - setInputValues({ ...settings }) } /> - + Paste this anywhere in your HTML code: + + diff --git a/apps/builder/src/features/publish/components/embeds/codeSnippets/Iframe/EmbedCode.tsx b/apps/builder/src/features/publish/components/embeds/modals/IframeModal/IframeSnippet.tsx similarity index 55% rename from apps/builder/src/features/publish/components/embeds/codeSnippets/Iframe/EmbedCode.tsx rename to apps/builder/src/features/publish/components/embeds/modals/IframeModal/IframeSnippet.tsx index 2e30757b29..f289ac99c6 100644 --- a/apps/builder/src/features/publish/components/embeds/codeSnippets/Iframe/EmbedCode.tsx +++ b/apps/builder/src/features/publish/components/embeds/modals/IframeModal/IframeSnippet.tsx @@ -2,21 +2,24 @@ import { FlexProps } from '@chakra-ui/react' import { useTypebot } from '@/features/editor' import { env, getViewerUrl } from 'utils' import { CodeEditor } from '@/components/CodeEditor' +import prettier from 'prettier/standalone' +import parserHtml from 'prettier/parser-html' type Props = { widthLabel: string heightLabel: string onCopied?: () => void -} -export const IframeEmbedCode = ({ - widthLabel, - heightLabel, -}: Props & FlexProps) => { +} & FlexProps + +export const IframeSnippet = ({ widthLabel, heightLabel }: Props) => { const { typebot } = useTypebot() const src = `${env('VIEWER_INTERNAL_URL') ?? getViewerUrl()}/${ typebot?.publicId }` - const code = `` + const code = prettier.format( + ``, + { parser: 'html', plugins: [parserHtml] } + ) return } diff --git a/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptBubbleSnippet.tsx b/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptBubbleSnippet.tsx new file mode 100644 index 0000000000..fe840bf05f --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptBubbleSnippet.tsx @@ -0,0 +1,36 @@ +import prettier from 'prettier/standalone' +import parserHtml from 'prettier/parser-html' +import { parseInitBubbleCode } from '../../snippetParsers' +import { useTypebot } from '@/features/editor' +import { CodeEditor } from '@/components/CodeEditor' +import { BubbleProps } from '@typebot.io/js' +import { isCloudProdInstance } from '@/utils/helpers' +import { env, getViewerUrl } from 'utils' + +type Props = Pick + +export const JavascriptBubbleSnippet = ({ theme, previewMessage }: Props) => { + const { typebot } = useTypebot() + + const snippet = prettier.format( + ``, + { + parser: 'html', + plugins: [parserHtml], + } + ) + + return +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptInstructions.tsx deleted file mode 100644 index e45e4c9358..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptInstructions.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import { Stack, Tag, Text } from '@chakra-ui/react' -import { useState } from 'react' -import { BubbleParams } from 'typebot-js' -import { ChatEmbedCode } from '../../codeSnippets/Chat/EmbedCode' -import { ChatEmbedSettings } from '../../codeSnippets/Chat/EmbedSettings' -import { ContainerEmbedCode } from '../../codeSnippets/Container/EmbedCode' -import { StandardEmbedWindowSettings } from '../../codeSnippets/Container/EmbedSettings' -import { PopupEmbedCode } from '../../codeSnippets/Popup/EmbedCode' -import { PopupEmbedSettings } from '../../codeSnippets/Popup/EmbedSettings' - -type JavascriptInstructionsProps = { - type: 'standard' | 'popup' | 'bubble' -} - -export const JavascriptInstructions = ({ - type, -}: JavascriptInstructionsProps) => { - switch (type) { - case 'standard': { - return - } - case 'popup': { - return - } - case 'bubble': { - return - } - } -} - -const StandardInstructions = () => { - const [inputValues, setInputValues] = useState({ - heightLabel: '100%', - widthLabel: '100%', - }) - - return ( - - - Paste this anywhere in the body - - setInputValues({ ...settings })} - /> - - - ) -} - -const PopupInstructions = () => { - const [inputValue, setInputValue] = useState() - - return ( - - - Paste this anywhere in the body - - setInputValue(settings.delay)} - /> - - - ) -} - -const BubbleInstructions = () => { - const [inputValues, setInputValues] = useState< - Pick - >({ - proactiveMessage: undefined, - button: { - color: '', - iconUrl: '', - }, - }) - - return ( - - - Paste this anywhere in the body - - setInputValues({ ...settings })} - /> - - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptModal.tsx index 3c3d0a1ab4..3f3dba2271 100644 --- a/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptModal.tsx +++ b/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptModal.tsx @@ -1,69 +1,29 @@ -import { - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalCloseButton, - ModalBody, - ModalFooter, - IconButton, - Heading, - HStack, -} from '@chakra-ui/react' -import { ChevronLeftIcon } from '@/components/icons' import React, { useState } from 'react' import { ModalProps } from '../../EmbedButton' -import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList' -import { capitalize } from 'utils' -import { JavascriptInstructions } from './JavascriptInstructions' -import { AlertInfo } from '@/components/AlertInfo' +import { EmbedModal } from '../../EmbedModal' +import { isDefined } from '@udecode/plate-common' +import { JavascriptInstructions } from './instructions/JavascriptInstructions' export const JavascriptModal = ({ isOpen, onClose, isPublished, }: ModalProps) => { - const [chosenEmbedType, setChosenEmbedType] = useState< + const [selectedEmbedType, setSelectedEmbedType] = useState< 'standard' | 'popup' | 'bubble' | undefined >() return ( - - - - - - {chosenEmbedType && ( - } - aria-label="back" - variant="ghost" - colorScheme="gray" - mr={2} - onClick={() => setChosenEmbedType(undefined)} - /> - )} - - Javascript {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`} - - - - - - {!isPublished && ( - You need to publish your bot first. - )} - {!chosenEmbedType ? ( - - ) : ( - - )} - - - - + {isDefined(selectedEmbedType) && ( + + )} + ) } diff --git a/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptPopupSnippet.tsx b/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptPopupSnippet.tsx new file mode 100644 index 0000000000..ddc50a736d --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptPopupSnippet.tsx @@ -0,0 +1,33 @@ +import { useTypebot } from '@/features/editor' +import parserHtml from 'prettier/parser-html' +import prettier from 'prettier/standalone' +import { parseInitPopupCode } from '../../snippetParsers' +import { CodeEditor } from '@/components/CodeEditor' +import { PopupProps } from '@typebot.io/js' +import { isCloudProdInstance } from '@/utils/helpers' +import { env, getViewerUrl } from 'utils' + +type Props = Pick + +export const JavascriptPopupSnippet = ({ autoShowDelay }: Props) => { + const { typebot } = useTypebot() + const snippet = prettier.format( + createSnippet({ + typebot: typebot?.publicId ?? '', + apiHost: isCloudProdInstance + ? undefined + : env('VIEWER_INTERNAL_URL') ?? getViewerUrl(), + autoShowDelay, + }), + { + parser: 'html', + plugins: [parserHtml], + } + ) + return +} + +const createSnippet = (params: PopupProps): string => { + const jsCode = parseInitPopupCode(params) + return `` +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptStandardSnippet.tsx b/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptStandardSnippet.tsx new file mode 100644 index 0000000000..dd3b7aaa97 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Javascript/JavascriptStandardSnippet.tsx @@ -0,0 +1,51 @@ +import parserHtml from 'prettier/parser-html' +import prettier from 'prettier/standalone' +import { parseInitStandardCode } from '../../snippetParsers' +import { useTypebot } from '@/features/editor' +import { CodeEditor } from '@/components/CodeEditor' +import { isCloudProdInstance } from '@/utils/helpers' +import { env, getViewerUrl } from 'utils' + +type Props = { + widthLabel?: string + heightLabel?: string +} + +export const JavascriptStandardSnippet = ({ + widthLabel, + heightLabel, +}: Props) => { + const { typebot } = useTypebot() + + const snippet = prettier.format( + `${parseStandardHeadCode(typebot?.publicId)} + ${parseStandardElementCode(widthLabel, heightLabel)}`, + { + parser: 'html', + plugins: [parserHtml], + } + ) + + return +} + +export const parseStandardHeadCode = (publicId?: string | null) => + prettier.format( + ``, + { parser: 'html', plugins: [parserHtml] } + ) + +export const parseStandardElementCode = (width?: string, height?: string) => { + if (!width && !height) return '' + return prettier.format( + ``, + { parser: 'html', plugins: [parserHtml] } + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptBubbleInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptBubbleInstructions.tsx new file mode 100644 index 0000000000..38050f8fb3 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptBubbleInstructions.tsx @@ -0,0 +1,43 @@ +import { useTypebot } from '@/features/editor' +import { Stack, Code, Text } from '@chakra-ui/react' +import { BubbleProps } from '@typebot.io/js' +import { Typebot } from 'models' +import { useState } from 'react' +import { BubbleSettings } from '../../../settings/BubbleSettings/BubbleSettings' +import { JavascriptBubbleSnippet } from '../JavascriptBubbleSnippet' + +export const parseDefaultBubbleTheme = (typebot?: Typebot) => ({ + button: { + backgroundColor: typebot?.theme.chat.buttons.backgroundColor, + iconColor: typebot?.theme.chat.buttons.color, + }, + previewMessage: { + backgroundColor: typebot?.theme.general.background.content ?? 'white', + textColor: 'black', + }, +}) + +export const JavascriptBubbleInstructions = () => { + const { typebot } = useTypebot() + const [theme, setTheme] = useState( + parseDefaultBubbleTheme(typebot) + ) + const [previewMessage, setPreviewMessage] = + useState() + + return ( + + + + Paste this anywhere in the {''}: + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptInstructions.tsx new file mode 100644 index 0000000000..cb6b643c41 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptInstructions.tsx @@ -0,0 +1,23 @@ +import { JavascriptBubbleInstructions } from './JavascriptBubbleInstructions' +import { JavascriptPopupInstructions } from './JavascriptPopupInstructions' +import { JavascriptStandardInstructions } from './JavascriptStandardInstructions' + +type JavascriptInstructionsProps = { + type: 'standard' | 'popup' | 'bubble' +} + +export const JavascriptInstructions = ({ + type, +}: JavascriptInstructionsProps) => { + switch (type) { + case 'standard': { + return + } + case 'popup': { + return + } + case 'bubble': { + return + } + } +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptPopupInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptPopupInstructions.tsx new file mode 100644 index 0000000000..1f53888c35 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptPopupInstructions.tsx @@ -0,0 +1,20 @@ +import { Stack, Code, Text } from '@chakra-ui/react' +import { useState } from 'react' +import { PopupSettings } from '../../../settings/PopupSettings' +import { JavascriptPopupSnippet } from '../JavascriptPopupSnippet' + +export const JavascriptPopupInstructions = () => { + const [inputValue, setInputValue] = useState() + + return ( + + setInputValue(settings.autoShowDelay)} + /> + + Paste this anywhere in the {''}: + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptStandardInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptStandardInstructions.tsx new file mode 100644 index 0000000000..9ea57f80f4 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/Javascript/instructions/JavascriptStandardInstructions.tsx @@ -0,0 +1,26 @@ +import { Stack, Code, Text } from '@chakra-ui/react' +import { useState } from 'react' +import { StandardSettings } from '../../../settings/StandardSettings' +import { JavascriptStandardSnippet } from '../JavascriptStandardSnippet' + +export const JavascriptStandardInstructions = () => { + const [inputValues, setInputValues] = useState<{ + heightLabel: string + widthLabel?: string + }>({ + heightLabel: '100%', + widthLabel: '100%', + }) + + return ( + + setInputValues({ ...settings })} + /> + + Paste this anywhere in the {''}: + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/NotionModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/NotionModal.tsx index 54e463b3e9..60bc5681e0 100644 --- a/apps/builder/src/features/publish/components/embeds/modals/NotionModal.tsx +++ b/apps/builder/src/features/publish/components/embeds/modals/NotionModal.tsx @@ -10,11 +10,13 @@ import { ModalBody, OrderedList, ListItem, - Tag, + Code, InputGroup, Input, InputRightElement, ModalFooter, + Text, + Stack, } from '@chakra-ui/react' import { env, getViewerUrl } from 'utils' import { ModalProps } from '../EmbedButton' @@ -37,28 +39,30 @@ export const NotionModal = ({ {!isPublished && ( You need to publish your bot first. )} - + - Type /embed + Type /embed - Paste your typebot URL - - - - + Paste your typebot URL + + - - + + + + + diff --git a/apps/builder/src/features/publish/components/embeds/modals/OtherModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/OtherModal.tsx new file mode 100644 index 0000000000..04aa663cfb --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/OtherModal.tsx @@ -0,0 +1,25 @@ +import React, { useState } from 'react' +import { isDefined } from '@udecode/plate-common' +import { EmbedModal } from '../EmbedModal' +import { JavascriptInstructions } from './Javascript/instructions/JavascriptInstructions' +import { ModalProps } from '../EmbedButton' + +export const OtherModal = ({ isOpen, onClose, isPublished }: ModalProps) => { + const [selectedEmbedType, setSelectedEmbedType] = useState< + 'standard' | 'popup' | 'bubble' | undefined + >() + return ( + + {isDefined(selectedEmbedType) && ( + + )} + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/React/InstallReactPackageSnippet.tsx b/apps/builder/src/features/publish/components/embeds/modals/React/InstallReactPackageSnippet.tsx new file mode 100644 index 0000000000..cba15fd044 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/React/InstallReactPackageSnippet.tsx @@ -0,0 +1,11 @@ +import { CodeEditor } from '@/components/CodeEditor' + +export const InstallReactPackageSnippet = () => { + return ( + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/React/ReactBubbleSnippet.tsx b/apps/builder/src/features/publish/components/embeds/modals/React/ReactBubbleSnippet.tsx new file mode 100644 index 0000000000..d69cf08c64 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/React/ReactBubbleSnippet.tsx @@ -0,0 +1,31 @@ +import { CodeEditor } from '@/components/CodeEditor' +import { useTypebot } from '@/features/editor' +import { BubbleProps } from '@typebot.io/js' +import parserBabel from 'prettier/parser-babel' +import prettier from 'prettier/standalone' +import { parseReactBubbleProps } from '../../snippetParsers' + +export const ReactBubbleSnippet = ({ + theme, + previewMessage, +}: Pick) => { + const { typebot } = useTypebot() + + const snippet = prettier.format( + `import { Bubble } from "@typebot.io/react"; + + const App = () => { + return + }`, + { + parser: 'babel', + plugins: [parserBabel], + } + ) + + return +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/React/ReactInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/React/ReactInstructions.tsx deleted file mode 100644 index 2b95bb68e5..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/React/ReactInstructions.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import { CodeEditor } from '@/components/CodeEditor' -import { Stack, Text } from '@chakra-ui/react' -import { useState } from 'react' -import { BubbleParams } from 'typebot-js' -import { ChatEmbedSettings } from '../../codeSnippets/Chat/EmbedSettings' -import { StandardEmbedWindowSettings } from '../../codeSnippets/Container/EmbedSettings' -import { PopupEmbedSettings } from '../../codeSnippets/Popup/EmbedSettings' -import { - StandardReactDiv, - PopupReactCode, - ChatReactCode, -} from '../../codeSnippets/ReactCode' - -type Props = { - type: 'standard' | 'popup' | 'bubble' -} - -export const ReactInstructions = ({ type }: Props) => { - switch (type) { - case 'standard': { - return - } - case 'popup': { - return - } - case 'bubble': { - return - } - } -} - -const StandardInstructions = () => { - const [inputValues, setInputValues] = useState({ - heightLabel: '100%', - widthLabel: '100%', - }) - - return ( - - - setInputValues({ ...settings })} - /> - Insert the typebot container - - - ) -} - -const PopupInstructions = () => { - const [inputValue, setInputValue] = useState() - - return ( - - - setInputValue(settings.delay)} - /> - Initialize the typebot - - - ) -} - -const BubbleInstructions = () => { - const [inputValues, setInputValues] = useState< - Pick - >({ - proactiveMessage: undefined, - button: { - color: '', - iconUrl: '', - }, - }) - - return ( - - - setInputValues({ ...settings })} - /> - Initialize the typebot - - - ) -} - -const InstallPackageInstruction = () => { - return ( - - Install the package: - - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/modals/React/ReactModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/React/ReactModal.tsx index e0ee6f6e01..e2c7f916d3 100644 --- a/apps/builder/src/features/publish/components/embeds/modals/React/ReactModal.tsx +++ b/apps/builder/src/features/publish/components/embeds/modals/React/ReactModal.tsx @@ -1,65 +1,25 @@ -import { - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalCloseButton, - ModalBody, - ModalFooter, - IconButton, - Heading, - HStack, -} from '@chakra-ui/react' -import { ChevronLeftIcon } from '@/components/icons' import React, { useState } from 'react' import { ModalProps } from '../../EmbedButton' -import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList' -import { capitalize } from 'utils' -import { ReactInstructions } from './ReactInstructions' -import { AlertInfo } from '@/components/AlertInfo' +import { EmbedModal } from '../../EmbedModal' +import { isDefined } from '@udecode/plate-common' +import { ReactInstructions } from './instructions/ReactInstructions' export const ReactModal = ({ isOpen, onClose, isPublished }: ModalProps) => { - const [chosenEmbedType, setChosenEmbedType] = useState< + const [selectedEmbedType, setSelectedEmbedType] = useState< 'standard' | 'popup' | 'bubble' | undefined >() return ( - - - - - - {chosenEmbedType && ( - } - aria-label="back" - variant="ghost" - colorScheme="gray" - mr={2} - onClick={() => setChosenEmbedType(undefined)} - /> - )} - - React {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`} - - - - - - {!isPublished && ( - You need to publish your bot first. - )} - {!chosenEmbedType ? ( - - ) : ( - - )} - - - - + {isDefined(selectedEmbedType) && ( + + )} + ) } diff --git a/apps/builder/src/features/publish/components/embeds/modals/React/ReactPopupSnippet.tsx b/apps/builder/src/features/publish/components/embeds/modals/React/ReactPopupSnippet.tsx new file mode 100644 index 0000000000..b822a133ed --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/React/ReactPopupSnippet.tsx @@ -0,0 +1,29 @@ +import { CodeEditor } from '@/components/CodeEditor' +import { useTypebot } from '@/features/editor' +import { PopupProps } from '@typebot.io/js' +import parserBabel from 'prettier/parser-babel' +import prettier from 'prettier/standalone' +import { parseReactPopupProps } from '../../snippetParsers' + +export const ReactPopupSnippet = ({ + autoShowDelay, +}: Pick) => { + const { typebot } = useTypebot() + + const snippet = prettier.format( + `import { Popup } from "@typebot.io/react"; + + const App = () => { + return ; + }`, + { + parser: 'babel', + plugins: [parserBabel], + } + ) + + return +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/React/ReactStandardSnippet.tsx b/apps/builder/src/features/publish/components/embeds/modals/React/ReactStandardSnippet.tsx new file mode 100644 index 0000000000..2e7f7d35e5 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/React/ReactStandardSnippet.tsx @@ -0,0 +1,28 @@ +import { CodeEditor } from '@/components/CodeEditor' +import { useTypebot } from '@/features/editor' +import parserBabel from 'prettier/parser-babel' +import prettier from 'prettier/standalone' +import { parseReactBotProps } from '../../snippetParsers' + +type ReactStandardSnippetProps = { widthLabel?: string; heightLabel: string } + +export const ReactStandardSnippet = ({ + widthLabel, + heightLabel, +}: ReactStandardSnippetProps) => { + const { typebot } = useTypebot() + const snippet = prettier.format( + `import { Standard } from "@typebot.io/react"; + + const App = () => { + return + }`, + { + parser: 'babel', + plugins: [parserBabel], + } + ) + return +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactBubbleInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactBubbleInstructions.tsx new file mode 100644 index 0000000000..e554a9e351 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactBubbleInstructions.tsx @@ -0,0 +1,42 @@ +import { useTypebot } from '@/features/editor' +import { ListItem, OrderedList, Stack, Text } from '@chakra-ui/react' +import { BubbleProps } from '@typebot.io/js' +import { useState } from 'react' +import { BubbleSettings } from '../../../settings/BubbleSettings/BubbleSettings' +import { InstallReactPackageSnippet } from '../InstallReactPackageSnippet' +import { ReactBubbleSnippet } from '../ReactBubbleSnippet' +import { parseDefaultBubbleTheme } from '../../Javascript/instructions/JavascriptBubbleInstructions' + +export const ReactBubbleInstructions = () => { + const { typebot } = useTypebot() + const [theme, setTheme] = useState( + parseDefaultBubbleTheme(typebot) + ) + const [previewMessage, setPreviewMessage] = + useState() + + return ( + + + + Install the packages + + + + + + + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactInstructions.tsx new file mode 100644 index 0000000000..32154e25ac --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactInstructions.tsx @@ -0,0 +1,21 @@ +import { ReactBubbleInstructions } from './ReactBubbleInstructions' +import { ReactPopupInstructions } from './ReactPopupInstructions' +import { ReactStandardInstructions } from './ReactStandardInstructions' + +type Props = { + type: 'standard' | 'popup' | 'bubble' +} + +export const ReactInstructions = ({ type }: Props) => { + switch (type) { + case 'standard': { + return + } + case 'popup': { + return + } + case 'bubble': { + return + } + } +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactPopupInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactPopupInstructions.tsx new file mode 100644 index 0000000000..b6273aa57b --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactPopupInstructions.tsx @@ -0,0 +1,30 @@ +import { ListItem, OrderedList, Stack, Text } from '@chakra-ui/react' +import { useState } from 'react' +import { PopupSettings } from '../../../settings/PopupSettings' +import { InstallReactPackageSnippet } from '../InstallReactPackageSnippet' +import { ReactPopupSnippet } from '../ReactPopupSnippet' + +export const ReactPopupInstructions = () => { + const [inputValue, setInputValue] = useState() + + return ( + + + + Install the packages + + + + + + + setInputValue(settings.autoShowDelay) + } + /> + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactStandardInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactStandardInstructions.tsx new file mode 100644 index 0000000000..7abecab43e --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/React/instructions/ReactStandardInstructions.tsx @@ -0,0 +1,36 @@ +import { ListItem, OrderedList, Stack, Text } from '@chakra-ui/react' +import { useState } from 'react' +import { StandardSettings } from '../../../settings/StandardSettings' +import { InstallReactPackageSnippet } from '../InstallReactPackageSnippet' +import { ReactStandardSnippet } from '../ReactStandardSnippet' + +export const ReactStandardInstructions = () => { + const [inputValues, setInputValues] = useState<{ + widthLabel?: string + heightLabel: string + }>({ + heightLabel: '100%', + widthLabel: '100%', + }) + + return ( + + + + Install the packages + + + + + + + setInputValues({ ...settings }) + } + /> + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/ShopifyInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/ShopifyInstructions.tsx deleted file mode 100644 index 80a72788ed..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/ShopifyInstructions.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import { OrderedList, ListItem, Tag } from '@chakra-ui/react' -import { useState } from 'react' -import { BubbleParams } from 'typebot-js' -import { ModalProps } from '../../EmbedButton' -import parserHtml from 'prettier/parser-html' -import prettier from 'prettier/standalone' -import { env, getViewerUrl } from 'utils' -import { CodeEditor } from '@/components/CodeEditor' -import { ChatEmbedCode } from '../../codeSnippets/Chat/EmbedCode' -import { ChatEmbedSettings } from '../../codeSnippets/Chat/EmbedSettings' -import { StandardEmbedWindowSettings } from '../../codeSnippets/Container/EmbedSettings' -import { - parseInitContainerCode, - typebotJsHtml, -} from '../../codeSnippets/params' -import { PopupEmbedCode } from '../../codeSnippets/Popup/EmbedCode' -import { PopupEmbedSettings } from '../../codeSnippets/Popup/EmbedSettings' - -type ShopifyInstructionsProps = { - type: 'standard' | 'popup' | 'bubble' - publicId: string -} - -export const ShopifyInstructions = ({ - type, - publicId, -}: ShopifyInstructionsProps) => { - switch (type) { - case 'standard': { - return - } - case 'popup': { - return - } - case 'bubble': { - return - } - } -} - -const StandardInstructions = ({ publicId }: Pick) => { - const [windowSizes, setWindowSizes] = useState({ - height: '100%', - width: '100%', - }) - - const jsCode = parseInitContainerCode({ - url: `${env('VIEWER_INTERNAL_URL') ?? getViewerUrl()}/${publicId}`, - }) - const headCode = prettier.format( - `${typebotJsHtml}`, - { - parser: 'html', - plugins: [parserHtml], - } - ) - - const elementCode = prettier.format( - `
`, - { - parser: 'html', - plugins: [parserHtml], - } - ) - - return ( - - - On your shop dashboard in the Themes page, click on{' '} - Actions {'>'} Edit code - - - In Layout {'>'} theme.liquid file, paste this code just - before the closing head tag: - - - - Then, you can place an element on which the typebot will go in any file - in the body tags. It needs to have the id{' '} - typebot-container: - - setWindowSizes({ - height: sizes.heightLabel, - width: sizes.widthLabel, - }) - } - /> - - - - ) -} - -const PopupInstructions = () => { - const [inputValue, setInputValue] = useState() - - return ( - - - On your shop dashboard in the Themes page, click on{' '} - Actions {'>'} Edit code - - - In Layout {'>'} theme.liquid file, paste this code just - before the closing head tag: - setInputValue(settings.delay)} - /> - - - - ) -} - -const BubbleInstructions = () => { - const [inputValues, setInputValues] = useState< - Pick - >({ - proactiveMessage: undefined, - button: { - color: '', - iconUrl: '', - }, - }) - - return ( - - - On your shop dashboard in the Themes page, click on{' '} - Actions {'>'} Edit code - - - In Layout {'>'} theme.liquid file, paste this code just - before the closing head tag: - setInputValues({ ...settings })} - /> - - - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/ShopifyModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/ShopifyModal.tsx new file mode 100644 index 0000000000..bd6945ee38 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/ShopifyModal.tsx @@ -0,0 +1,30 @@ +import React, { useState } from 'react' +import { ModalProps } from '../../EmbedButton' +import { EmbedModal } from '../../EmbedModal' +import { isDefined } from '@udecode/plate-common' +import { ShopifyInstructions } from './instructions/ShopifyInstructions' + +export const ShopifyModal = ({ + isOpen, + onClose, + isPublished, + publicId, +}: ModalProps) => { + const [selectedEmbedType, setSelectedEmbedType] = useState< + 'standard' | 'popup' | 'bubble' | undefined + >() + return ( + + {isDefined(selectedEmbedType) && ( + + )} + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/index.ts b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/index.ts new file mode 100644 index 0000000000..6255586271 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/index.ts @@ -0,0 +1 @@ +export * from './ShopifyModal' diff --git a/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/index.tsx b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/index.tsx deleted file mode 100644 index c34666937d..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/index.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalCloseButton, - ModalBody, - ModalFooter, - IconButton, - Heading, - HStack, -} from '@chakra-ui/react' -import { ChevronLeftIcon } from '@/components/icons' -import React, { useState } from 'react' -import { ModalProps } from '../../EmbedButton' -import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList' -import { capitalize } from 'utils' -import { ShopifyInstructions } from './ShopifyInstructions' -import { AlertInfo } from '@/components/AlertInfo' - -export const ShopifyModal = ({ - isOpen, - onClose, - isPublished, - publicId, -}: ModalProps) => { - const [chosenEmbedType, setChosenEmbedType] = useState< - 'standard' | 'popup' | 'bubble' | undefined - >() - return ( - - - - - - {chosenEmbedType && ( - } - aria-label="back" - variant="ghost" - colorScheme="gray" - mr={2} - onClick={() => setChosenEmbedType(undefined)} - /> - )} - - Shopify {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`} - - - - - - {!isPublished && ( - You need to publish your bot first. - )} - {!chosenEmbedType ? ( - - ) : ( - - )} - - - - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyBubbleInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyBubbleInstructions.tsx new file mode 100644 index 0000000000..f6ef3187a3 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyBubbleInstructions.tsx @@ -0,0 +1,47 @@ +import { useTypebot } from '@/features/editor' +import { OrderedList, ListItem, Stack, Text, Code } from '@chakra-ui/react' +import { BubbleProps } from '@typebot.io/js' +import { useState } from 'react' +import { BubbleSettings } from '../../../settings/BubbleSettings/BubbleSettings' +import { parseDefaultBubbleTheme } from '../../Javascript/instructions/JavascriptBubbleInstructions' +import { JavascriptBubbleSnippet } from '../../Javascript/JavascriptBubbleSnippet' + +export const ShopifyBubbleInstructions = () => { + const { typebot } = useTypebot() + + const [theme, setTheme] = useState( + parseDefaultBubbleTheme(typebot) + ) + const [previewMessage, setPreviewMessage] = + useState() + + return ( + + + On your shop dashboard in the Themes page, click on{' '} + Actions {'>'} Edit code + + + + + + In Layout {'>'} theme.liquid file, paste this code just + before the closing {''} tag: + + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyInstructions.tsx new file mode 100644 index 0000000000..fe4a9d527e --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyInstructions.tsx @@ -0,0 +1,25 @@ +import { ShopifyBubbleInstructions } from './ShopifyBubbleInstructions' +import { ShopifyPopupInstructions } from './ShopifyPopupInstructions' +import { ShopifyStandardInstructions } from './ShopifyStandardInstructions' + +type ShopifyInstructionsProps = { + type: 'standard' | 'popup' | 'bubble' + publicId: string +} + +export const ShopifyInstructions = ({ + type, + publicId, +}: ShopifyInstructionsProps) => { + switch (type) { + case 'standard': { + return + } + case 'popup': { + return + } + case 'bubble': { + return + } + } +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyPopupInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyPopupInstructions.tsx new file mode 100644 index 0000000000..e633910d5e --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyPopupInstructions.tsx @@ -0,0 +1,31 @@ +import { OrderedList, ListItem, Stack, Text, Code } from '@chakra-ui/react' +import { useState } from 'react' +import { PopupSettings } from '../../../settings/PopupSettings' +import { JavascriptPopupSnippet } from '../../Javascript/JavascriptPopupSnippet' + +export const ShopifyPopupInstructions = () => { + const [inputValue, setInputValue] = useState() + + return ( + + + On your shop dashboard in the Themes page, click on{' '} + Actions {'>'} Edit code + + + + + setInputValue(settings.autoShowDelay) + } + /> + + In Layout {'>'} theme.liquid file, paste this code just + before the closing {''} tag: + + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyStandardInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyStandardInstructions.tsx new file mode 100644 index 0000000000..d612dd5599 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/ShopifyModal/instructions/ShopifyStandardInstructions.tsx @@ -0,0 +1,65 @@ +import { CodeEditor } from '@/components/CodeEditor' +import { OrderedList, ListItem, Stack, Text, Code } from '@chakra-ui/react' +import { useState } from 'react' +import { StandardSettings } from '../../../settings/StandardSettings' +import { + parseStandardElementCode, + parseStandardHeadCode, +} from '../../Javascript/JavascriptStandardSnippet' + +type Props = { + publicId: string +} + +export const ShopifyStandardInstructions = ({ publicId }: Props) => { + const [windowSizes, setWindowSizes] = useState<{ + width?: string + height: string + }>({ + height: '100%', + width: '100%', + }) + + const headCode = parseStandardHeadCode(publicId) + + const elementCode = parseStandardElementCode( + windowSizes.width, + windowSizes.height + ) + + return ( + + + On your shop dashboard in the Themes page, click on{' '} + Actions {'>'} Edit code + + + + + In Layout {'>'} theme.liquid file, paste this code just + before the closing {''} tag: + + + + + + + + + setWindowSizes({ + height: sizes.heightLabel, + width: sizes.widthLabel, + }) + } + /> + + Place an element on which the typebot will go in any file in the{' '} + {''}: + + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/WebflowInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/WebflowInstructions.tsx deleted file mode 100644 index ccf7e4877a..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/WebflowInstructions.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { OrderedList, ListItem, Tag } from '@chakra-ui/react' -import { useState } from 'react' -import { BubbleParams } from 'typebot-js' -import { ChatEmbedCode } from '../../codeSnippets/Chat/EmbedCode' -import { ChatEmbedSettings } from '../../codeSnippets/Chat/EmbedSettings' -import { ContainerEmbedCode } from '../../codeSnippets/Container/EmbedCode' -import { PopupEmbedCode } from '../../codeSnippets/Popup/EmbedCode' -import { PopupEmbedSettings } from '../../codeSnippets/Popup/EmbedSettings' - -type WebflowInstructionsProps = { - type: 'standard' | 'popup' | 'bubble' -} - -export const WebflowInstructions = ({ type }: WebflowInstructionsProps) => { - switch (type) { - case 'standard': { - return - } - case 'popup': { - return - } - case 'bubble': { - return - } - default: - return <> - } -} - -const StandardInstructions = () => ( - - - Press A to open the Add elements panel - - - Add an embed element from the components - section and paste this code: - - - -) - -const PopupInstructions = () => { - const [inputValue, setInputValue] = useState() - - return ( - - - Press A to open the Add elements panel - - - Add an embed element from the components - section and paste this code: - setInputValue(settings.delay)} - my={4} - /> - - - - ) -} - -const BubbleInstructions = () => { - const [inputValues, setInputValues] = useState< - Pick - >({ - proactiveMessage: undefined, - button: { - color: '', - iconUrl: '', - }, - }) - - return ( - - - Press A to open the Add elements panel - - - Add an embed element from the components - section and paste this code: - setInputValues({ ...settings })} - my={4} - /> - - - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/WebflowModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/WebflowModal.tsx new file mode 100644 index 0000000000..9e78b0392c --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/WebflowModal.tsx @@ -0,0 +1,26 @@ +import React, { useState } from 'react' +import { ModalProps } from '../../EmbedButton' +import { EmbedModal } from '../../EmbedModal' +import { isDefined } from '@udecode/plate-common' +import { WebflowInstructions } from './instructions/WebflowInstructions' + +export const WebflowModal = ({ isOpen, onClose, isPublished }: ModalProps) => { + const [selectedEmbedType, setSelectedEmbedType] = useState< + 'standard' | 'popup' | 'bubble' | undefined + >() + + return ( + + {isDefined(selectedEmbedType) && ( + + )} + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/index.ts b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/index.ts new file mode 100644 index 0000000000..7e48139569 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/index.ts @@ -0,0 +1 @@ +export * from './WebflowModal' diff --git a/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/index.tsx b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/index.tsx deleted file mode 100644 index 870de78766..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/index.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalCloseButton, - ModalBody, - ModalFooter, - IconButton, - Heading, - HStack, -} from '@chakra-ui/react' -import { ChevronLeftIcon } from '@/components/icons' -import React, { useState } from 'react' -import { ModalProps } from '../../EmbedButton' -import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList' -import { capitalize } from 'utils' -import { WebflowInstructions } from './WebflowInstructions' -import { AlertInfo } from '@/components/AlertInfo' - -export const WebflowModal = ({ isOpen, onClose, isPublished }: ModalProps) => { - const [chosenEmbedType, setChosenEmbedType] = useState< - 'standard' | 'popup' | 'bubble' | undefined - >() - return ( - - - - - - {chosenEmbedType && ( - } - aria-label="back" - variant="ghost" - colorScheme="gray" - mr={2} - onClick={() => setChosenEmbedType(undefined)} - /> - )} - - Webflow {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`} - - - - - - {!isPublished && ( - You need to publish your bot first. - )} - {!chosenEmbedType ? ( - - ) : ( - - )} - - - - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowBubbleInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowBubbleInstructions.tsx new file mode 100644 index 0000000000..e6da812c7a --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowBubbleInstructions.tsx @@ -0,0 +1,46 @@ +import { useTypebot } from '@/features/editor' +import { OrderedList, ListItem, Code, Stack, Text } from '@chakra-ui/react' +import { BubbleProps } from '@typebot.io/js' +import { useState } from 'react' +import { BubbleSettings } from '../../../settings/BubbleSettings/BubbleSettings' +import { parseDefaultBubbleTheme } from '../../Javascript/instructions/JavascriptBubbleInstructions' +import { JavascriptBubbleSnippet } from '../../Javascript/JavascriptBubbleSnippet' + +export const WebflowBubbleInstructions = () => { + const { typebot } = useTypebot() + + const [theme, setTheme] = useState( + parseDefaultBubbleTheme(typebot) + ) + const [previewMessage, setPreviewMessage] = + useState() + + return ( + + + Press A to open the Add elements panel + + + + + + Add an embed element from the components{' '} + section and paste this code: + + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowInstructions.tsx new file mode 100644 index 0000000000..d5411a7d7f --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowInstructions.tsx @@ -0,0 +1,21 @@ +import { WebflowStandardInstructions } from './WebflowStandardInstructions' +import { WebflowPopupInstructions } from './WebflowPopupInstructions' +import { WebflowBubbleInstructions } from './WebflowBubbleInstructions' + +type WebflowInstructionsProps = { + type: 'standard' | 'popup' | 'bubble' +} + +export const WebflowInstructions = ({ type }: WebflowInstructionsProps) => { + switch (type) { + case 'standard': { + return + } + case 'popup': { + return + } + case 'bubble': { + return + } + } +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowPopupInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowPopupInstructions.tsx new file mode 100644 index 0000000000..ee09192cbd --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowPopupInstructions.tsx @@ -0,0 +1,30 @@ +import { OrderedList, ListItem, Code, Stack, Text } from '@chakra-ui/react' +import { useState } from 'react' +import { PopupSettings } from '../../../settings/PopupSettings' +import { JavascriptPopupSnippet } from '../../Javascript/JavascriptPopupSnippet' + +export const WebflowPopupInstructions = () => { + const [inputValue, setInputValue] = useState() + + return ( + + + Press A to open the Add elements panel + + + + + setInputValue(settings.autoShowDelay) + } + /> + + Add an embed element from the components{' '} + section and paste this code: + + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowStandardInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowStandardInstructions.tsx new file mode 100644 index 0000000000..e1ad7c66df --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WebflowModal/instructions/WebflowStandardInstructions.tsx @@ -0,0 +1,19 @@ +import { OrderedList, ListItem, Code, Stack, Text } from '@chakra-ui/react' +import { JavascriptStandardSnippet } from '../../Javascript/JavascriptStandardSnippet' + +export const WebflowStandardInstructions = () => ( + + + Press A to open the Add elements panel + + + + + Add an embed element from the components{' '} + section and paste this code: + + + + + +) diff --git a/apps/builder/src/features/publish/components/embeds/modals/WixModal/WixInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WixModal/WixInstructions.tsx deleted file mode 100644 index 839265de54..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/WixModal/WixInstructions.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { ListItem, OrderedList, Tag } from '@chakra-ui/react' -import { useState } from 'react' -import { BubbleParams } from 'typebot-js' -import { ChatEmbedCode } from '../../codeSnippets/Chat/EmbedCode' -import { ChatEmbedSettings } from '../../codeSnippets/Chat/EmbedSettings' -import { ContainerEmbedCode } from '../../codeSnippets/Container/EmbedCode' -import { PopupEmbedCode } from '../../codeSnippets/Popup/EmbedCode' -import { PopupEmbedSettings } from '../../codeSnippets/Popup/EmbedSettings' - -type WixInstructionsProps = { - type: 'standard' | 'popup' | 'bubble' -} - -export const WixInstructions = ({ type }: WixInstructionsProps) => { - switch (type) { - case 'standard': { - return - } - case 'popup': { - return - } - case 'bubble': { - return - } - } -} - -const StandardInstructions = () => { - return ( - - - In the Wix Website Editor: - - Add {'>'} Embed {'>'} Embed a Widget - - - - Click on Enter code and paste this code: - - - - ) -} - -const PopupInstructions = () => { - const [inputValue, setInputValue] = useState() - - return ( - <> - - - Go to Settings in your dashboard on Wix - - - Click on Custom Code under Advanced - - - Click + Add Custom Code at the top right. - - - Paste this snippet in the code box: - setInputValue(settings.delay)} - my={4} - /> - - - - - ) -} - -const BubbleInstructions = () => { - const [inputValues, setInputValues] = useState< - Pick - >({ - proactiveMessage: undefined, - button: { - color: '', - iconUrl: '', - }, - }) - - return ( - - - Go to Settings in your dashboard on Wix - - - Click on Custom Code under Advanced - - - Click + Add Custom Code at the top right. - - - Paste this snippet in the code box:{' '} - setInputValues({ ...settings })} - /> - - - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WixModal/WixModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/WixModal/WixModal.tsx new file mode 100644 index 0000000000..fa590f4ac6 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WixModal/WixModal.tsx @@ -0,0 +1,26 @@ +import React, { useState } from 'react' +import { ModalProps } from '../../EmbedButton' +import { EmbedModal } from '../../EmbedModal' +import { isDefined } from '@udecode/plate-common' +import { WixInstructions } from './instructions/WixInstructions' + +export const WixModal = ({ isOpen, onClose, isPublished }: ModalProps) => { + const [selectedEmbedType, setSelectedEmbedType] = useState< + 'standard' | 'popup' | 'bubble' | undefined + >() + + return ( + + {isDefined(selectedEmbedType) && ( + + )} + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WixModal/index.ts b/apps/builder/src/features/publish/components/embeds/modals/WixModal/index.ts new file mode 100644 index 0000000000..98f88b10ac --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WixModal/index.ts @@ -0,0 +1 @@ +export * from './WixModal' diff --git a/apps/builder/src/features/publish/components/embeds/modals/WixModal/index.tsx b/apps/builder/src/features/publish/components/embeds/modals/WixModal/index.tsx deleted file mode 100644 index 50da9a4a26..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/WixModal/index.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - ModalCloseButton, - ModalBody, - ModalFooter, - IconButton, - Heading, - HStack, -} from '@chakra-ui/react' -import { ChevronLeftIcon } from '@/components/icons' -import React, { useState } from 'react' -import { ModalProps } from '../../EmbedButton' -import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList' -import { WixInstructions } from './WixInstructions' -import { capitalize } from 'utils' -import { AlertInfo } from '@/components/AlertInfo' - -export const WixModal = ({ isOpen, onClose, isPublished }: ModalProps) => { - const [chosenEmbedType, setChosenEmbedType] = useState< - 'standard' | 'popup' | 'bubble' | undefined - >() - return ( - - - - - - {chosenEmbedType && ( - } - aria-label="back" - variant="ghost" - colorScheme="gray" - mr={2} - onClick={() => setChosenEmbedType(undefined)} - /> - )} - - Wix {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`} - - - - - - {!isPublished && ( - You need to publish your bot first. - )} - {!chosenEmbedType ? ( - - ) : ( - - )} - - - - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixBubbleInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixBubbleInstructions.tsx new file mode 100644 index 0000000000..bbc6e3d61e --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixBubbleInstructions.tsx @@ -0,0 +1,49 @@ +import { useTypebot } from '@/features/editor' +import { OrderedList, ListItem, Code, Stack, Text } from '@chakra-ui/react' +import { BubbleProps } from '@typebot.io/js' +import { useState } from 'react' +import { BubbleSettings } from '../../../settings/BubbleSettings/BubbleSettings' +import { parseDefaultBubbleTheme } from '../../Javascript/instructions/JavascriptBubbleInstructions' +import { JavascriptBubbleSnippet } from '../../Javascript/JavascriptBubbleSnippet' + +export const WixBubbleInstructions = () => { + const { typebot } = useTypebot() + + const [theme, setTheme] = useState( + parseDefaultBubbleTheme(typebot) + ) + const [previewMessage, setPreviewMessage] = + useState() + + return ( + + + Go to Settings in your dashboard on Wix + + + Click on Custom Code under Advanced + + + Click + Add Custom Code at the top right. + + + + + Paste this snippet in the code box: + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixInstructions.tsx new file mode 100644 index 0000000000..6e7874ae5a --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixInstructions.tsx @@ -0,0 +1,21 @@ +import { WixBubbleInstructions } from './WixBubbleInstructions' +import { WixPopupInstructions } from './WixPopupInstructions' +import { WixStandardInstructions } from './WixStandardInstuctions' + +type WixInstructionsProps = { + type: 'standard' | 'popup' | 'bubble' +} + +export const WixInstructions = ({ type }: WixInstructionsProps) => { + switch (type) { + case 'standard': { + return + } + case 'popup': { + return + } + case 'bubble': { + return + } + } +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixPopupInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixPopupInstructions.tsx new file mode 100644 index 0000000000..31e92d00b2 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixPopupInstructions.tsx @@ -0,0 +1,33 @@ +import { OrderedList, ListItem, Code, Stack, Text } from '@chakra-ui/react' +import { useState } from 'react' +import { PopupSettings } from '../../../settings/PopupSettings' +import { JavascriptPopupSnippet } from '../../Javascript/JavascriptPopupSnippet' + +export const WixPopupInstructions = () => { + const [inputValue, setInputValue] = useState() + + return ( + + + Go to Settings in your dashboard on Wix + + + Click on Custom Code under Advanced + + + Click + Add Custom Code at the top right. + + + + + setInputValue(settings.autoShowDelay) + } + /> + Paste this snippet in the code box: + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixStandardInstuctions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixStandardInstuctions.tsx new file mode 100644 index 0000000000..7f7660cff2 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WixModal/instructions/WixStandardInstuctions.tsx @@ -0,0 +1,23 @@ +import { OrderedList, ListItem, Code, Stack, Text } from '@chakra-ui/react' +import { JavascriptStandardSnippet } from '../../Javascript/JavascriptStandardSnippet' + +export const WixStandardInstructions = () => { + return ( + + + In the Wix Website Editor: + + Add {'>'} Embed {'>'} Embed a Widget + + + + + + Click on Enter code and paste this code: + + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WordpressModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal.tsx deleted file mode 100644 index f3ed48fd38..0000000000 --- a/apps/builder/src/features/publish/components/embeds/modals/WordpressModal.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { - Modal, - ModalOverlay, - ModalContent, - ModalHeader, - Heading, - ModalCloseButton, - ModalBody, - OrderedList, - ListItem, - InputGroup, - Input, - InputRightElement, - ModalFooter, - Link, - useColorModeValue, -} from '@chakra-ui/react' -import { ExternalLinkIcon } from '@/components/icons' -import { env, getViewerUrl } from 'utils' -import { ModalProps } from '../EmbedButton' -import { AlertInfo } from '@/components/AlertInfo' -import { CopyButton } from '@/components/CopyButton' - -export const WordpressModal = ({ - publicId, - isPublished, - isOpen, - onClose, -}: ModalProps): JSX.Element => { - return ( - - - - - WordPress - - - - {!isPublished && ( - You need to publish your bot first. - )} - - - Install{' '} - - the official Typebot WordPress plugin - - - - - Copy your typebot URL - - - - - - - - Complete the setup in your Wordpress interface - - - - - - ) -} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/WordpressModal.tsx b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/WordpressModal.tsx new file mode 100644 index 0000000000..74b445ad76 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/WordpressModal.tsx @@ -0,0 +1,30 @@ +import { isDefined } from 'utils' +import { ModalProps } from '../../EmbedButton' +import { useState } from 'react' +import { EmbedModal } from '../../EmbedModal' +import { WordpressInstructions } from './instructions/WordpressInstructions' + +export const WordpressModal = ({ + isOpen, + onClose, + isPublished, + publicId, +}: ModalProps) => { + const [selectedEmbedType, setSelectedEmbedType] = useState< + 'standard' | 'popup' | 'bubble' | undefined + >() + return ( + + {isDefined(selectedEmbedType) && ( + + )} + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/index.ts b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/index.ts new file mode 100644 index 0000000000..062eae6d13 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/index.ts @@ -0,0 +1 @@ +export * from './WordpressModal' diff --git a/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressBubbleInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressBubbleInstructions.tsx new file mode 100644 index 0000000000..1c2e0f85f9 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressBubbleInstructions.tsx @@ -0,0 +1,74 @@ +import { CodeEditor } from '@/components/CodeEditor' +import { ExternalLinkIcon } from '@/components/icons' +import { useTypebot } from '@/features/editor' +import { + OrderedList, + ListItem, + useColorModeValue, + Link, + Stack, + Text, +} from '@chakra-ui/react' +import { BubbleProps } from '@typebot.io/js' +import { useState } from 'react' +import { BubbleSettings } from '../../../settings/BubbleSettings/BubbleSettings' +import { parseInitBubbleCode } from '../../../snippetParsers' +import { parseDefaultBubbleTheme } from '../../Javascript/instructions/JavascriptBubbleInstructions' + +type Props = { + publicId: string +} +export const WordpressBubbleInstructions = ({ publicId }: Props) => { + const { typebot } = useTypebot() + + const [theme, setTheme] = useState( + parseDefaultBubbleTheme(typebot) + ) + const [previewMessage, setPreviewMessage] = + useState() + + const initCode = parseInitBubbleCode({ + typebot: publicId, + theme: { + ...theme, + chatWindow: { + backgroundColor: typebot?.theme.general.background.content ?? '#fff', + }, + }, + previewMessage, + }) + + return ( + + + Install{' '} + + the official Typebot WordPress plugin + + + + + + + + You can now place the following code snippet in the Typebot panel in + your WordPress admin: + + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressInstructions.tsx new file mode 100644 index 0000000000..0397f26516 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressInstructions.tsx @@ -0,0 +1,22 @@ +import { WordpressBubbleInstructions } from './WordpressBubbleInstructions' +import { WordpressPopupInstructions } from './WordpressPopupInstructions' +import { WordpressStandardInstructions } from './WordpressStandardInstructions' + +type Props = { + publicId: string + type: 'standard' | 'popup' | 'bubble' +} + +export const WordpressInstructions = ({ publicId, type }: Props) => { + switch (type) { + case 'standard': { + return + } + case 'popup': { + return + } + case 'bubble': { + return + } + } +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressPopupInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressPopupInstructions.tsx new file mode 100644 index 0000000000..017b407fe1 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressPopupInstructions.tsx @@ -0,0 +1,55 @@ +import { CodeEditor } from '@/components/CodeEditor' +import { ExternalLinkIcon } from '@/components/icons' +import { + OrderedList, + ListItem, + useColorModeValue, + Link, + Stack, + Text, +} from '@chakra-ui/react' +import { useState } from 'react' +import { PopupSettings } from '../../../settings/PopupSettings' +import { parseInitPopupCode } from '../../../snippetParsers/popup' + +type Props = { + publicId: string +} +export const WordpressPopupInstructions = ({ publicId }: Props) => { + const [autoShowDelay, setAutoShowDelay] = useState() + + const initCode = parseInitPopupCode({ + typebot: publicId, + autoShowDelay, + }) + + return ( + + + Install{' '} + + the official Typebot WordPress plugin + + + + + + + setAutoShowDelay(settings.autoShowDelay) + } + /> + + You can now place the following code snippet in the Typebot panel in + your WordPress admin: + + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressStandardInstructions.tsx b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressStandardInstructions.tsx new file mode 100644 index 0000000000..019db1c23c --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/modals/WordpressModal/instructions/WordpressStandardInstructions.tsx @@ -0,0 +1,80 @@ +import { CodeEditor } from '@/components/CodeEditor' +import { ExternalLinkIcon } from '@/components/icons' +import { + OrderedList, + ListItem, + useColorModeValue, + Link, + Stack, + Text, + Code, +} from '@chakra-ui/react' +import { useState } from 'react' +import { StandardSettings } from '../../../settings/StandardSettings' + +type Props = { + publicId: string +} + +export const WordpressStandardInstructions = ({ publicId }: Props) => { + const [windowSizes, setWindowSizes] = useState<{ + width?: string + height: string + }>({ + height: '100%', + width: '100%', + }) + + const elementCode = parseWordpressShortcode({ ...windowSizes, publicId }) + + return ( + + + Install{' '} + + the official Typebot WordPress plugin + + + + + + + setWindowSizes({ + height: sizes.heightLabel, + width: sizes.widthLabel, + }) + } + /> + + You can now place the following shortcode anywhere on your site: + + + + Note: Your page templating system probably has a{' '} + Shortcode element (if not, use a text element). + + + + + ) +} + +const parseWordpressShortcode = ({ + width, + height, + publicId, +}: { + width?: string + height?: string + publicId: string +}) => { + return `[typebot typebot="${publicId}"${width ? ` width="${width}"` : ''}${ + height ? ` height="${height}"` : '' + }] +` +} diff --git a/apps/builder/src/features/publish/components/embeds/modals/index.tsx b/apps/builder/src/features/publish/components/embeds/modals/index.tsx index d2393557c0..6b7f51cb49 100644 --- a/apps/builder/src/features/publish/components/embeds/modals/index.tsx +++ b/apps/builder/src/features/publish/components/embeds/modals/index.tsx @@ -1,4 +1,4 @@ -export * from './IframeModal' +export * from './IframeModal/IframeModal' export * from './NotionModal' export * from './WordpressModal' export * from './WixModal' diff --git a/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/BubbleSettings.tsx b/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/BubbleSettings.tsx new file mode 100644 index 0000000000..c04a0d0ef0 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/BubbleSettings.tsx @@ -0,0 +1,96 @@ +import { Stack, Heading, HStack, Flex, Text, Image } from '@chakra-ui/react' +import { BubbleProps } from '@typebot.io/js' +import { isDefined } from 'utils' +import { PreviewMessageSettings } from './PreviewMessageSettings' +import { ThemeSettings } from './ThemeSettings' + +type Props = { + defaultPreviewMessageAvatar: string + theme: BubbleProps['theme'] + previewMessage: BubbleProps['previewMessage'] + onThemeChange: (theme: BubbleProps['theme']) => void + onPreviewMessageChange: ( + previewMessage: BubbleProps['previewMessage'] + ) => void +} + +export const BubbleSettings = ({ + defaultPreviewMessageAvatar, + theme, + previewMessage, + onThemeChange, + onPreviewMessageChange, +}: Props) => { + const updatePreviewMessage = ( + previewMessage: BubbleProps['previewMessage'] + ) => { + onPreviewMessageChange(previewMessage) + } + + const updateTheme = (theme: BubbleProps['theme']) => { + onThemeChange(theme) + } + + return ( + + Chat bubble settings + + + + Preview: + + {isDefined(previewMessage) && ( + + {previewMessage.avatarUrl && ( + Preview message avatar + )} + + {previewMessage.message} + + + )} + + + + + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/ButtonThemeSettings.tsx b/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/ButtonThemeSettings.tsx new file mode 100644 index 0000000000..b37c1a8a75 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/ButtonThemeSettings.tsx @@ -0,0 +1,65 @@ +import { ColorPicker } from '@/components/ColorPicker' +import { Heading, HStack, Input, Stack, Text } from '@chakra-ui/react' +import { ButtonTheme } from '@typebot.io/js/dist/features/bubble/types' +import React from 'react' + +type Props = { + buttonTheme: ButtonTheme | undefined + onChange: (newButtonTheme?: ButtonTheme) => void +} + +export const ButtonThemeSettings = ({ buttonTheme, onChange }: Props) => { + const updateBackgroundColor = (backgroundColor: string) => { + onChange({ + ...buttonTheme, + backgroundColor, + }) + } + + const updateIconColor = (iconColor: string) => { + onChange({ + ...buttonTheme, + iconColor, + }) + } + + const updateCustomIconSrc = (customIconSrc: string) => { + onChange({ + ...buttonTheme, + customIconSrc, + }) + } + + return ( + + Button + + + Background color + + + + Icon color + + + + Custom icon + updateCustomIconSrc(e.target.value)} + minW="0" + w="300px" + size="sm" + /> + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/PreviewMessageSettings.tsx b/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/PreviewMessageSettings.tsx new file mode 100644 index 0000000000..e3aa154841 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/PreviewMessageSettings.tsx @@ -0,0 +1,121 @@ +import { SmartNumberInput } from '@/components/inputs' +import { FormLabel, HStack, Input, Stack, Switch, Text } from '@chakra-ui/react' +import { PreviewMessageParams } from '@typebot.io/js/dist/features/bubble/types' +import { useState } from 'react' +import { isDefined } from 'utils' + +type Props = { + defaultAvatar: string + onChange: (newPreviewMessage?: PreviewMessageParams) => void +} + +export const PreviewMessageSettings = ({ defaultAvatar, onChange }: Props) => { + const [isPreviewMessageEnabled, setIsPreviewMessageEnabled] = useState(false) + const [previewMessage, setPreviewMessage] = useState() + const [autoShowDelay, setAutoShowDelay] = useState(10) + + const [isAutoShowEnabled, setIsAutoShowEnabled] = useState(false) + + const updatePreviewMessage = (previewMessage: PreviewMessageParams) => { + setPreviewMessage(previewMessage) + onChange(previewMessage) + } + + const updateAutoShowDelay = (autoShowDelay?: number) => { + setAutoShowDelay(autoShowDelay ?? 0) + updatePreviewMessage({ + ...previewMessage, + message: previewMessage?.message ?? '', + autoShowDelay, + }) + } + + const updateAvatarUrl = (avatarUrl: string) => { + updatePreviewMessage({ + ...previewMessage, + message: previewMessage?.message ?? '', + avatarUrl, + }) + } + + const updateMessage = (message: string) => { + updatePreviewMessage({ ...previewMessage, message }) + } + + const updatePreviewMessageCheck = (isChecked: boolean) => { + setIsPreviewMessageEnabled(isChecked) + const newPreviewMessage = { + autoShowDelay: isAutoShowEnabled ? autoShowDelay : undefined, + message: previewMessage?.message ?? 'I have a question for you!', + avatarUrl: previewMessage?.avatarUrl ?? defaultAvatar, + } + if (isChecked) setPreviewMessage(newPreviewMessage) + onChange(isChecked ? newPreviewMessage : undefined) + } + + const updateAutoShowDelayCheck = (isChecked: boolean) => { + setIsAutoShowEnabled(isChecked) + updatePreviewMessage({ + ...previewMessage, + message: previewMessage?.message ?? '', + + autoShowDelay: isChecked ? autoShowDelay : undefined, + }) + } + + return ( + + + + Preview message + + updatePreviewMessageCheck(e.target.checked)} + /> + + {isPreviewMessageEnabled && ( + + + Avatar URL + updateAvatarUrl(e.target.value)} + value={previewMessage?.avatarUrl} + placeholder={'Paste image link (.png, .jpg)'} + /> + + + Message + updateMessage(e.target.value)} + value={previewMessage?.message} + /> + + + Auto show + updateAutoShowDelayCheck(e.target.checked)} + /> + {isAutoShowEnabled && ( + <> + After + + isDefined(val) && updateAutoShowDelay(val) + } + withVariableButton={false} + /> + seconds + + )} + + + )} + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/PreviewMessageThemeSettings.tsx b/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/PreviewMessageThemeSettings.tsx new file mode 100644 index 0000000000..a86a425cbb --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/PreviewMessageThemeSettings.tsx @@ -0,0 +1,80 @@ +import { ColorPicker } from '@/components/ColorPicker' +import { Heading, HStack, Stack, Text } from '@chakra-ui/react' +import { PreviewMessageTheme } from '@typebot.io/js/dist/features/bubble/types' +import React from 'react' + +type Props = { + previewMessageTheme?: PreviewMessageTheme + onChange: (newPreviewMessageTheme?: PreviewMessageTheme) => void +} + +export const PreviewMessageThemeSettings = ({ + previewMessageTheme, + onChange, +}: Props) => { + const updateBackgroundColor = (backgroundColor: string) => { + onChange({ + ...previewMessageTheme, + backgroundColor, + }) + } + + const updateTextColor = (textColor: string) => { + onChange({ + ...previewMessageTheme, + textColor, + }) + } + + const updateCloseButtonBackgroundColor = ( + closeButtonBackgroundColor: string + ) => { + onChange({ + ...previewMessageTheme, + closeButtonBackgroundColor, + }) + } + + const updateCloseButtonIconColor = (closeButtonIconColor: string) => { + onChange({ + ...previewMessageTheme, + closeButtonIconColor, + }) + } + + return ( + + Preview message + + + Background color + + + + Text color + + + + Close button background + + + + Close icon color + + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/ThemeSettings.tsx b/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/ThemeSettings.tsx new file mode 100644 index 0000000000..51617d8990 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/settings/BubbleSettings/ThemeSettings.tsx @@ -0,0 +1,68 @@ +import { + Accordion, + AccordionButton, + AccordionIcon, + AccordionItem, + AccordionPanel, + HStack, + Stack, + Text, +} from '@chakra-ui/react' +import { BubbleProps } from '@typebot.io/js' +import { + ButtonTheme, + PreviewMessageTheme, +} from '@typebot.io/js/dist/features/bubble/types' +import { ButtonThemeSettings } from './ButtonThemeSettings' +import { PreviewMessageThemeSettings } from './PreviewMessageThemeSettings' + +type Props = { + isPreviewMessageEnabled: boolean + theme: BubbleProps['theme'] + onChange: (newBubbleTheme: BubbleProps['theme']) => void +} + +export const ThemeSettings = ({ + isPreviewMessageEnabled, + theme, + onChange, +}: Props) => { + const updateButtonTheme = (button?: ButtonTheme) => { + onChange({ + ...theme, + button, + }) + } + + const updatePreviewMessageTheme = (previewMessage?: PreviewMessageTheme) => { + onChange({ + ...theme, + previewMessage, + }) + } + + return ( + + + + + Theme + + + + + + {isPreviewMessageEnabled ? ( + + ) : null} + + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/settings/PopupSettings.tsx b/apps/builder/src/features/publish/components/embeds/settings/PopupSettings.tsx new file mode 100644 index 0000000000..ccdfc9c2b9 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/settings/PopupSettings.tsx @@ -0,0 +1,54 @@ +import { SmartNumberInput } from '@/components/inputs' +import { + StackProps, + Stack, + Heading, + Switch, + HStack, + Text, +} from '@chakra-ui/react' +import { PopupProps } from '@typebot.io/js' +import { useState, useEffect } from 'react' +import { isDefined } from 'utils' + +type Props = { + onUpdateSettings: (windowSettings: Pick) => void +} & StackProps + +export const PopupSettings = ({ onUpdateSettings, ...props }: Props) => { + const [isEnabled, setIsEnabled] = useState(false) + const [inputValue, setInputValue] = useState(5) + + useEffect(() => { + onUpdateSettings({ + autoShowDelay: isEnabled ? inputValue * 1000 : undefined, + }) + }, [inputValue, isEnabled, onUpdateSettings]) + + return ( + + Popup settings + + +

Auto show

+ setIsEnabled(e.target.checked)} + /> + {isEnabled && ( + <> + isDefined(val) && setInputValue(val)} + withVariableButton={false} + /> + seconds + + )} +
+
+ ) +} diff --git a/apps/builder/src/features/publish/components/embeds/settings/StandardSettings.tsx b/apps/builder/src/features/publish/components/embeds/settings/StandardSettings.tsx new file mode 100644 index 0000000000..e6e1f26640 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/settings/StandardSettings.tsx @@ -0,0 +1,107 @@ +import { + StackProps, + Stack, + Flex, + Heading, + Input, + HStack, + Text, +} from '@chakra-ui/react' +import { DropdownList } from '@/components/DropdownList' +import { useState, useEffect } from 'react' +import { SwitchWithLabel } from '@/components/SwitchWithLabel' + +type Props = { + onUpdateWindowSettings: (windowSettings: { + heightLabel: string + widthLabel?: string + }) => void +} & StackProps + +export const StandardSettings = ({ + onUpdateWindowSettings, + ...props +}: Props) => { + const [isFullscreenChecked, setIsFullscreenChecked] = useState(false) + const [inputValues, setInputValues] = useState({ + widthValue: '100', + widthType: '%', + heightValue: '600', + heightType: 'px', + }) + + useEffect(() => { + onUpdateWindowSettings({ + widthLabel: isFullscreenChecked + ? undefined + : inputValues.widthValue + inputValues.widthType, + heightLabel: isFullscreenChecked + ? '100vh' + : inputValues.heightValue + inputValues.heightType, + }) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [inputValues, isFullscreenChecked]) + + const handleWidthTypeSelect = (widthType: string) => + setInputValues({ ...inputValues, widthType }) + const handleHeightTypeSelect = (heightType: string) => + setInputValues({ ...inputValues, heightType }) + + return ( + + Window settings + + + setIsFullscreenChecked(!isFullscreenChecked)} + /> + {!isFullscreenChecked && ( + <> + + Width + + + setInputValues({ + ...inputValues, + widthValue: e.target.value, + }) + } + w="70px" + value={inputValues.widthValue} + /> + + items={['px', '%']} + onItemSelect={handleWidthTypeSelect} + currentItem={inputValues.widthType} + /> + + + + Height + + + setInputValues({ + ...inputValues, + heightValue: e.target.value, + }) + } + w="70px" + value={inputValues.heightValue} + /> + + items={['px', '%']} + onItemSelect={handleHeightTypeSelect} + currentItem={inputValues.heightType} + /> + + + + )} + + + ) +} diff --git a/apps/builder/src/features/publish/components/embeds/snippetParsers/bubble.ts b/apps/builder/src/features/publish/components/embeds/snippetParsers/bubble.ts new file mode 100644 index 0000000000..a616acabb1 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/snippetParsers/bubble.ts @@ -0,0 +1,162 @@ +import { BubbleProps } from '@typebot.io/js' +import parserBabel from 'prettier/parser-babel' +import prettier from 'prettier/standalone' +import { + parseStringParam, + parseBotProps, + parseNumberOrBoolParam, + parseReactBotProps, + typebotImportUrl, +} from './shared' + +const parseButtonTheme = ( + button: NonNullable['button'] +): string => { + if (!button) return '' + const { backgroundColor, iconColor, customIconSrc } = button + const backgroundColorLine = parseStringParam( + 'backgroundColor', + backgroundColor + ) + const iconColorLine = parseStringParam('iconColor', iconColor) + const customIconLine = parseStringParam('customIconSrc', customIconSrc) + const line = `button: {${backgroundColorLine}${iconColorLine}${customIconLine}},` + if (line === 'button: {},') return '' + return line +} + +const parsePreviewMessageTheme = ( + previewMessage: NonNullable['previewMessage'] +): string => { + if (!previewMessage) return '' + const { + backgroundColor, + closeButtonBackgroundColor, + closeButtonIconColor, + textColor, + } = previewMessage + const backgroundColorLine = parseStringParam( + 'backgroundColor', + backgroundColor + ) + const closeButtonBackgroundColorLine = parseStringParam( + 'closeButtonBackgroundColor', + closeButtonBackgroundColor + ) + const closeButtonIconColorLine = parseStringParam( + 'closeButtonIconColor', + closeButtonIconColor + ) + const textColorLine = parseStringParam('textColor', textColor) + const line = `previewMessage: {${backgroundColorLine}${textColorLine}${closeButtonBackgroundColorLine}${closeButtonIconColorLine}},` + if (line === 'previewMessage: {},') return '' + return line +} + +const parseChatWindowTheme = ( + chatWindow: NonNullable['chatWindow'] +) => { + if (!chatWindow) return '' + const backgroundColorLine = parseStringParam( + 'backgroundColor', + chatWindow.backgroundColor + ) + const line = `chatWindow: {${backgroundColorLine}},` + if (line === 'chatWindow: {},') return '' + return line +} + +const parseBubbleTheme = (theme: BubbleProps['theme']): string => { + if (!theme) return '' + const { button, previewMessage } = theme + const buttonThemeLine = parseButtonTheme(button) + const previewMessageThemeLine = parsePreviewMessageTheme(previewMessage) + const chatWindowThemeLine = parseChatWindowTheme(theme.chatWindow) + const line = `theme: {${buttonThemeLine}${previewMessageThemeLine}${chatWindowThemeLine}},` + if (line === 'theme: {},') return '' + return line +} + +const parsePreviewMessage = ( + previewMessage: BubbleProps['previewMessage'] +): string => { + if (!previewMessage) return '' + const { message, autoShowDelay, avatarUrl } = previewMessage + const messageLine = parseStringParam('message', message) + const autoShowDelayLine = parseNumberOrBoolParam( + 'autoShowDelay', + autoShowDelay + ) + const avatarUrlLine = parseStringParam('avatarUrl', avatarUrl) + const line = `previewMessage: {${messageLine}${autoShowDelayLine}${avatarUrlLine}},` + if (line === 'previewMessage: {},') return '' + return line +} + +const parseBubbleProps = ({ + previewMessage, + theme, +}: Pick) => { + const previewMessageLine = parsePreviewMessage(previewMessage) + const themeLine = parseBubbleTheme(theme) + return `${previewMessageLine}${themeLine}` +} + +export const parseInitBubbleCode = ({ + typebot, + apiHost, + previewMessage, + theme, +}: BubbleProps) => { + const botProps = parseBotProps({ typebot, apiHost }) + const bubbleProps = parseBubbleProps({ previewMessage, theme }) + + return prettier.format( + `import Typebot from '${typebotImportUrl}' + + Typebot.initBubble({${botProps}${bubbleProps}});`, + { + parser: 'babel', + plugins: [parserBabel], + } + ) +} + +const parseReactBubbleTheme = (theme: BubbleProps['theme']): string => { + if (!theme) return '' + const { button, previewMessage } = theme + const buttonThemeLine = parseButtonTheme(button) + const previewMessageThemeLine = parsePreviewMessageTheme(previewMessage) + const line = `theme={{${buttonThemeLine}${previewMessageThemeLine}}}` + if (line === 'theme={{}}') return '' + return line +} + +const parseReactPreviewMessage = ( + previewMessage: BubbleProps['previewMessage'] +): string => { + if (!previewMessage) return '' + const { message, autoShowDelay, avatarUrl } = previewMessage + const messageLine = parseStringParam('message', message) + const autoShowDelayLine = parseNumberOrBoolParam( + 'autoShowDelay', + autoShowDelay + ) + const avatarUrlLine = parseStringParam('avatarUrl', avatarUrl) + const line = `previewMessage={{${messageLine}${autoShowDelayLine}${avatarUrlLine}}}` + if (line === 'previewMessage={{}}') return '' + return line +} + +export const parseReactBubbleProps = ({ + typebot, + apiHost, + previewMessage, + theme, +}: BubbleProps) => { + const botProps = parseReactBotProps({ typebot, apiHost }) + const previewMessageProp = parseReactPreviewMessage(previewMessage) + const themeProp = parseReactBubbleTheme(theme) + + return `${botProps} ${previewMessageProp} ${themeProp}` +} diff --git a/apps/builder/src/features/publish/components/embeds/snippetParsers/index.ts b/apps/builder/src/features/publish/components/embeds/snippetParsers/index.ts new file mode 100644 index 0000000000..7c8eff3163 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/snippetParsers/index.ts @@ -0,0 +1,4 @@ +export * from './bubble' +export * from './popup' +export * from './shared' +export * from './standard' diff --git a/apps/builder/src/features/publish/components/embeds/snippetParsers/popup.ts b/apps/builder/src/features/publish/components/embeds/snippetParsers/popup.ts new file mode 100644 index 0000000000..40b0e9f97b --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/snippetParsers/popup.ts @@ -0,0 +1,77 @@ +import { PopupProps } from '@typebot.io/js' +import parserBabel from 'prettier/parser-babel' +import prettier from 'prettier/standalone' +import { + parseBotProps, + parseNumberOrBoolParam, + parseReactBotProps, + parseReactNumberOrBoolParam, + parseReactStringParam, + parseStringParam, + typebotImportUrl, +} from './shared' + +const parsePopupTheme = (theme: PopupProps['theme']): string => { + if (!theme) return '' + const { width } = theme + const widthLine = parseStringParam('width', width) + const line = `theme: {${widthLine}},` + if (line === 'theme: {}') return '' + return line +} + +const parsePopupProps = ({ + autoShowDelay, + theme, +}: Pick) => { + const autoShowDelayLine = parseNumberOrBoolParam( + 'autoShowDelay', + autoShowDelay + ) + const themeLine = parsePopupTheme(theme) + return `${autoShowDelayLine}${themeLine}` +} + +export const parseInitPopupCode = ({ + typebot, + apiHost, + theme, + autoShowDelay, +}: PopupProps) => { + const botProps = parseBotProps({ typebot, apiHost }) + const bubbleProps = parsePopupProps({ theme, autoShowDelay }) + + return prettier.format( + `import Typebot from '${typebotImportUrl}' + + Typebot.initPopup({${botProps}${bubbleProps}});`, + { + parser: 'babel', + plugins: [parserBabel], + } + ) +} + +const parseReactThemeProp = (theme: PopupProps['theme']): string => { + if (!theme) return '' + const { width } = theme + const widthProp = parseReactStringParam('width', width) + if (widthProp === 'theme={{}}') return '' + return widthProp +} + +export const parseReactPopupProps = ({ + typebot, + apiHost, + theme, + autoShowDelay, +}: PopupProps) => { + const botProps = parseReactBotProps({ typebot, apiHost }) + const autoShowDelayProp = parseReactNumberOrBoolParam( + 'autoShowDelay', + autoShowDelay + ) + const themeProp = parseReactThemeProp(theme) + + return `${botProps} ${autoShowDelayProp} ${themeProp}` +} diff --git a/apps/builder/src/features/publish/components/embeds/snippetParsers/shared.ts b/apps/builder/src/features/publish/components/embeds/snippetParsers/shared.ts new file mode 100644 index 0000000000..e7d412c0af --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/snippetParsers/shared.ts @@ -0,0 +1,32 @@ +import { BotProps } from '@typebot.io/js' +import { isDefined } from 'utils' + +export const parseStringParam = (fieldName: string, fieldValue?: string) => + fieldValue ? `${fieldName}: "${fieldValue}",` : `` + +export const parseNumberOrBoolParam = ( + fieldName: string, + fieldValue?: number | boolean +) => (isDefined(fieldValue) ? `${fieldName}: ${fieldValue},` : ``) + +export const parseBotProps = ({ typebot, apiHost }: BotProps) => { + const typebotLine = parseStringParam('typebot', typebot as string) + const apiHostLine = parseStringParam('apiHost', apiHost) + return `${typebotLine}${apiHostLine}` +} + +export const parseReactStringParam = (fieldName: string, fieldValue?: string) => + fieldValue ? `${fieldName}="${fieldValue}"` : `` + +export const parseReactNumberOrBoolParam = ( + fieldName: string, + fieldValue?: number | boolean +) => (isDefined(fieldValue) ? `${fieldName}={${fieldValue}}` : ``) + +export const parseReactBotProps = ({ typebot, apiHost }: BotProps) => { + const typebotLine = parseReactStringParam('typebot', typebot as string) + const apiHostLine = parseReactStringParam('apiHost', apiHost) + return `${typebotLine} ${apiHostLine}` +} + +export const typebotImportUrl = `https://cdn.jsdelivr.net/npm/@typebot.io/js@0.0.9/dist/web.js` diff --git a/apps/builder/src/features/publish/components/embeds/snippetParsers/standard.ts b/apps/builder/src/features/publish/components/embeds/snippetParsers/standard.ts new file mode 100644 index 0000000000..d52e2b9f37 --- /dev/null +++ b/apps/builder/src/features/publish/components/embeds/snippetParsers/standard.ts @@ -0,0 +1,21 @@ +import { BotProps } from '@typebot.io/js' +import parserBabel from 'prettier/parser-babel' +import prettier from 'prettier/standalone' +import { parseBotProps, typebotImportUrl } from './shared' + +export const parseInitStandardCode = ({ + typebot, + apiHost, +}: Pick) => { + const botProps = parseBotProps({ typebot, apiHost }) + + return prettier.format( + `import Typebot from '${typebotImportUrl}' + + Typebot.initStandard({${botProps}});`, + { + parser: 'babel', + plugins: [parserBabel], + } + ) +} diff --git a/apps/builder/src/features/publish/utils.ts b/apps/builder/src/features/publish/utils.ts index f827494fd7..d43574bf30 100644 --- a/apps/builder/src/features/publish/utils.ts +++ b/apps/builder/src/features/publish/utils.ts @@ -10,6 +10,7 @@ export const parsePublicTypebotToTypebot = ( existingTypebot: Typebot ): Typebot => ({ id: typebot.typebotId, + version: typebot.version, groups: typebot.groups, edges: typebot.edges, name: existingTypebot.name, @@ -32,6 +33,7 @@ export const parseTypebotToPublicTypebot = ( typebot: Typebot ): PublicTypebot => ({ id: createId(), + version: typebot.version, typebotId: typebot.id, groups: typebot.groups, edges: typebot.edges, diff --git a/apps/builder/src/features/settings/components/SettingsPage.tsx b/apps/builder/src/features/settings/components/SettingsPage.tsx index d787456309..9ee1dbd4cd 100644 --- a/apps/builder/src/features/settings/components/SettingsPage.tsx +++ b/apps/builder/src/features/settings/components/SettingsPage.tsx @@ -1,19 +1,12 @@ import { Seo } from '@/components/Seo' import { TypebotHeader, useTypebot } from '@/features/editor' import { Flex } from '@chakra-ui/react' -import { TypebotViewer } from 'bot-engine' -import { useMemo } from 'react' +import { Standard } from '@typebot.io/react' import { getViewerUrl } from 'utils' import { SettingsSideMenu } from './SettingsSideMenu' -import { parseTypebotToPublicTypebot } from '@/features/publish' export const SettingsPage = () => { const { typebot } = useTypebot() - const publicTypebot = useMemo( - () => (typebot ? parseTypebotToPublicTypebot(typebot) : undefined), - // eslint-disable-next-line react-hooks/exhaustive-deps - [typebot?.settings] - ) return ( @@ -21,10 +14,8 @@ export const SettingsPage = () => { - - {publicTypebot && ( - - )} + + {typebot && } diff --git a/apps/builder/src/features/settings/settings.spec.ts b/apps/builder/src/features/settings/settings.spec.ts index d7de15f7e4..c06a572aae 100644 --- a/apps/builder/src/features/settings/settings.spec.ts +++ b/apps/builder/src/features/settings/settings.spec.ts @@ -4,7 +4,6 @@ import { createId } from '@paralleldrive/cuid2' import { defaultTextInputOptions } from 'models' import { importTypebotInDatabase } from 'utils/playwright/databaseActions' import { freeWorkspaceId } from 'utils/playwright/databaseSetup' -import { typebotViewer } from 'utils/playwright/testHelpers' test.describe.parallel('Settings page', () => { test.describe('General', () => { @@ -15,12 +14,10 @@ test.describe.parallel('Settings page', () => { }) await page.goto(`/typebots/${typebotId}/settings`) await expect( - typebotViewer(page).locator('a:has-text("Made with Typebot")') + page.locator('a:has-text("Made with Typebot")') ).toHaveAttribute('href', 'https://www.typebot.io/?utm_source=litebadge') await page.click('text="Typebot.io branding"') - await expect( - typebotViewer(page).locator('a:has-text("Made with Typebot")') - ).toBeHidden() + await expect(page.locator('a:has-text("Made with Typebot")')).toBeHidden() await page.click('text="Remember session"') await expect( @@ -32,13 +29,13 @@ test.describe.parallel('Settings page', () => { page.locator('input[type="checkbox"] >> nth=-1') ).toHaveAttribute('checked', '') - await expect( - typebotViewer(page).locator('input[value="Baptiste"]') - ).toBeVisible() + await expect(page.getByPlaceholder('Type your answer...')).toHaveValue( + 'Baptiste' + ) await page.click('text=Prefill input') await page.click('text=Theme') await expect( - typebotViewer(page).locator( + page.locator( `input[placeholder="${defaultTextInputOptions.labels.placeholder}"]` ) ).toHaveValue('') @@ -53,7 +50,7 @@ test.describe.parallel('Settings page', () => { }) await page.goto(`/typebots/${typebotId}/settings`) await expect( - typebotViewer(page).locator('a:has-text("Made with Typebot")') + page.locator('a:has-text("Made with Typebot")') ).toHaveAttribute('href', 'https://www.typebot.io/?utm_source=litebadge') await page.click('button:has-text("Typing emulation")') await page.fill('[data-testid="speed"] input', '350') @@ -74,7 +71,7 @@ test.describe.parallel('Settings page', () => { }) await page.goto(`/typebots/${typebotId}/settings`) await expect( - typebotViewer(page).locator( + page.locator( `input[placeholder="${defaultTextInputOptions.labels.placeholder}"]` ) ).toHaveValue('Baptiste') @@ -120,9 +117,7 @@ test.describe.parallel('Settings page', () => { workspaceId: freeWorkspaceId, }) await page.goto(`/typebots/${typebotId}/settings`) - await expect( - typebotViewer(page).locator('text="What\'s your name?"') - ).toBeVisible() + await expect(page.locator('text="What\'s your name?"')).toBeVisible() await expect( page.locator('[data-testid="starter-lock-tag"]') ).toBeVisible() diff --git a/apps/builder/src/features/templates/components/TemplatesModal.tsx b/apps/builder/src/features/templates/components/TemplatesModal.tsx index e913608f26..2fadfcc91a 100644 --- a/apps/builder/src/features/templates/components/TemplatesModal.tsx +++ b/apps/builder/src/features/templates/components/TemplatesModal.tsx @@ -12,14 +12,13 @@ import { Tooltip, } from '@chakra-ui/react' import { ExternalLinkIcon } from '@/components/icons' -import { TypebotViewer } from 'bot-engine' +import { Standard } from '@typebot.io/react' import { Typebot } from 'models' import React, { useCallback, useEffect, useState } from 'react' -import { getViewerUrl, sendRequest } from 'utils' import { templates } from '../data' import { TemplateProps } from '../types' import { useToast } from '@/hooks/useToast' -import { parseTypebotToPublicTypebot } from '@/features/publish' +import { sendRequest } from 'utils' type Props = { isOpen: boolean @@ -68,18 +67,20 @@ export const TemplatesModal = ({ isOpen, onClose, onTypebotChoose }: Props) => { > - + {selectedTemplate.emoji}{' '} {selectedTemplate.name} {typebot && ( - )} diff --git a/apps/builder/src/features/templates/components/TemplatesPage.tsx b/apps/builder/src/features/templates/components/TemplatesPage.tsx index 9ee3fbc0e1..b78eef6f26 100644 --- a/apps/builder/src/features/templates/components/TemplatesPage.tsx +++ b/apps/builder/src/features/templates/components/TemplatesPage.tsx @@ -4,7 +4,7 @@ import { VStack } from '@chakra-ui/react' import { CreateNewTypebotButtons } from './CreateNewTypebotButtons' export const TemplatesPage = () => ( - + diff --git a/apps/builder/src/features/templates/templates.spec.ts b/apps/builder/src/features/templates/templates.spec.ts index 7308eb8f31..0c932dabe3 100644 --- a/apps/builder/src/features/templates/templates.spec.ts +++ b/apps/builder/src/features/templates/templates.spec.ts @@ -1,6 +1,5 @@ import { getTestAsset } from '@/test/utils/playwright' import test, { expect } from '@playwright/test' -import { typebotViewer } from 'utils/playwright/testHelpers' test.describe.parallel('Templates page', () => { test('From scratch should create a blank typebot', async ({ page }) => { @@ -26,9 +25,7 @@ test.describe.parallel('Templates page', () => { await page.goto('/typebots/create') await page.click('text=Start from a template') await page.click('text=Customer Support') - await expect( - typebotViewer(page).locator('text=How can I help you?') - ).toBeVisible() + await expect(page.locator('text=How can I help you?')).toBeVisible() await page.click('text=Use this template') await expect(page).toHaveURL(new RegExp(`/edit`)) }) diff --git a/apps/builder/src/features/theme/components/CustomCssSettings/CustomCssSettings.tsx b/apps/builder/src/features/theme/components/CustomCssSettings/CustomCssSettings.tsx index a9640765e5..c67cd6fac0 100644 --- a/apps/builder/src/features/theme/components/CustomCssSettings/CustomCssSettings.tsx +++ b/apps/builder/src/features/theme/components/CustomCssSettings/CustomCssSettings.tsx @@ -9,7 +9,7 @@ type Props = { export const CustomCssSettings = ({ customCss, onCustomCssChange }: Props) => { return ( diff --git a/apps/builder/src/features/theme/components/ThemePage.tsx b/apps/builder/src/features/theme/components/ThemePage.tsx index 0b05b684a5..e9fc760ca7 100644 --- a/apps/builder/src/features/theme/components/ThemePage.tsx +++ b/apps/builder/src/features/theme/components/ThemePage.tsx @@ -1,14 +1,11 @@ import { Seo } from '@/components/Seo' import { TypebotHeader, useTypebot } from '@/features/editor' import { Flex } from '@chakra-ui/react' -import { TypebotViewer } from 'bot-engine' -import { getViewerUrl } from 'utils' +import { Standard } from '@typebot.io/react' import { ThemeSideMenu } from './ThemeSideMenu' -import { parseTypebotToPublicTypebot } from '@/features/publish' export const ThemePage = () => { const { typebot } = useTypebot() - const publicTypebot = typebot && parseTypebotToPublicTypebot(typebot) return ( @@ -16,9 +13,15 @@ export const ThemePage = () => { - - {publicTypebot && ( - + + {typebot && ( + )} diff --git a/apps/builder/src/features/theme/components/ThemeSideMenu.tsx b/apps/builder/src/features/theme/components/ThemeSideMenu.tsx index 092721faac..db3e2d6fa6 100644 --- a/apps/builder/src/features/theme/components/ThemeSideMenu.tsx +++ b/apps/builder/src/features/theme/components/ThemeSideMenu.tsx @@ -38,6 +38,7 @@ export const ThemeSideMenu = () => { spacing={10} overflowY="scroll" pb="20" + position="relative" > Customize the theme diff --git a/apps/builder/src/features/theme/theme.spec.ts b/apps/builder/src/features/theme/theme.spec.ts index 5fcb0cc652..2a1dd56b45 100644 --- a/apps/builder/src/features/theme/theme.spec.ts +++ b/apps/builder/src/features/theme/theme.spec.ts @@ -2,7 +2,6 @@ import { getTestAsset } from '@/test/utils/playwright' import test, { expect } from '@playwright/test' import { createId } from '@paralleldrive/cuid2' import { importTypebotInDatabase } from 'utils/playwright/databaseActions' -import { typebotViewer } from 'utils/playwright/testHelpers' const hostAvatarUrl = 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1760&q=80' @@ -13,30 +12,29 @@ test.describe.parallel('Theme page', () => { test.describe('General', () => { test('should reflect change in real-time', async ({ page }) => { const typebotId = createId() - const chatContainer = typebotViewer(page).locator( - '[data-testid="container"]' - ) await importTypebotInDatabase(getTestAsset('typebots/theme.json'), { id: typebotId, }) await page.goto(`/typebots/${typebotId}/theme`) - await expect( - typebotViewer(page).locator('button >> text="Go"') - ).toBeVisible() + await expect(page.locator('button >> text="Go"')).toBeVisible() // Font await page.fill('input[type="text"]', 'Roboto Slab') - await expect(chatContainer).toHaveCSS('font-family', '"Roboto Slab"') + await expect(page.locator('.typebot-container')).toHaveCSS( + 'font-family', + /"Roboto Slab"/ + ) // BG color - await expect(chatContainer).toHaveCSS( + await expect(page.locator('.typebot-container')).toHaveCSS( 'background-color', 'rgba(0, 0, 0, 0)' ) await page.click('text=Color') - await page.click('[aria-label="Pick a color"]') + await page.waitForTimeout(100) + await page.getByRole('button', { name: 'Pick a color' }).click() await page.fill('[aria-label="Color value"] >> nth=-1', '#2a9d8f') - await expect(chatContainer).toHaveCSS( + await expect(page.locator('.typebot-container')).toHaveCSS( 'background-color', 'rgb(42, 157, 143)' ) @@ -55,30 +53,26 @@ test.describe.parallel('Theme page', () => { } await page.goto(`/typebots/${typebotId}/theme`) - await expect( - typebotViewer(page).locator('button >> text="Go"') - ).toBeVisible() + await expect(page.locator('button >> text="Go"')).toBeVisible() await page.click('button:has-text("Chat")') // Host avatar - await expect( - typebotViewer(page).locator('[data-testid="default-avatar"]') - ).toBeVisible() + await expect(page.locator('[data-testid="default-avatar"]')).toBeVisible() await page.click('[data-testid="default-avatar"]') await page.click('button:has-text("Embed link")') await page.fill( 'input[placeholder="Paste the image link..."]', hostAvatarUrl ) - await typebotViewer(page).locator('button >> text="Go"').click() + await page.locator('button >> text="Go"').click() - await expect(typebotViewer(page).locator('img')).toHaveAttribute( + await expect(page.locator('.typebot-container img')).toHaveAttribute( 'src', hostAvatarUrl ) await page.click('text=Bot avatar') - await expect(typebotViewer(page).locator('img')).toBeHidden() + await expect(page.locator('.typebot-container img')).toBeHidden() // Host bubbles await page.click( @@ -89,9 +83,7 @@ test.describe.parallel('Theme page', () => { '[data-testid="host-bubbles-theme"] >> [aria-label="Pick a color"] >> nth=1' ) await page.fill('input[value="#303235"]', '#ffffff') - const hostBubble = typebotViewer(page).locator( - '[data-testid="host-bubble"] >> nth=-1' - ) + const hostBubble = page.locator('[data-testid="host-bubble"] >> nth=-1') await expect(hostBubble).toHaveCSS( 'background-color', 'rgb(42, 157, 143)' @@ -107,7 +99,7 @@ test.describe.parallel('Theme page', () => { '[data-testid="buttons-theme"] >> [aria-label="Pick a color"] >> nth=1' ) await page.fill('input[value="#FFFFFF"]', '#e9c46a') - const button = typebotViewer(page).locator('[data-testid="button"]') + const button = page.getByRole('button', { name: 'Go' }) await expect(button).toHaveCSS('background-color', 'rgb(114, 9, 183)') await expect(button).toHaveCSS('color', 'rgb(233, 196, 106)') @@ -120,10 +112,8 @@ test.describe.parallel('Theme page', () => { '[data-testid="guest-bubbles-theme"] >> [aria-label="Pick a color"] >> nth=1' ) await page.fill('input[value="#FFFFFF"]', '#264653') - await typebotViewer(page).locator('button >> text="Go"').click() - const guestBubble = typebotViewer(page).locator( - '[data-testid="guest-bubble"] >> nth=-1' - ) + await page.locator('button >> text="Go"').click() + const guestBubble = page.locator('[data-testid="guest-bubble"] >> nth=-1') await expect(guestBubble).toHaveCSS( 'background-color', 'rgb(216, 243, 220)' @@ -133,21 +123,20 @@ test.describe.parallel('Theme page', () => { // Guest avatar await page.click('text=User avatar') await expect( - typebotViewer(page).locator('[data-testid="default-avatar"] >> nth=-1') + page.locator('[data-testid="default-avatar"] >> nth=-1') ).toBeVisible() await page.click('[data-testid="default-avatar"]') await page.click('button:has-text("Embed link")') - await page.fill( - 'input[placeholder="Paste the image link..."]', - guestAvatarUrl - ) - - typebotViewer(page).locator('button >> text="Go"').click() - await expect(typebotViewer(page).locator('img')).toHaveAttribute( + await page + .locator('input[placeholder="Paste the image link..."]') + .fill(guestAvatarUrl) + await page.locator('button >> text="Go"').click() + await expect(page.locator('.typebot-container img')).toHaveAttribute( 'src', guestAvatarUrl ) + await page.waitForTimeout(1000) // Input await page.click( '[data-testid="inputs-theme"] >> [aria-label="Pick a color"] >> nth=0' @@ -157,7 +146,7 @@ test.describe.parallel('Theme page', () => { '[data-testid="inputs-theme"] >> [aria-label="Pick a color"] >> nth=1' ) await page.fill('input[value="#303235"]', '#023e8a') - const input = typebotViewer(page).locator('.typebot-input') + const input = page.locator('.typebot-input') await expect(input).toHaveCSS('background-color', 'rgb(255, 232, 214)') await expect(input).toHaveCSS('color', 'rgb(2, 62, 138)') }) @@ -170,17 +159,16 @@ test.describe.parallel('Theme page', () => { id: typebotId, }) await page.goto(`/typebots/${typebotId}/theme`) - await expect( - typebotViewer(page).locator('button >> text="Go"') - ).toBeVisible() + await expect(page.locator('button >> text="Go"')).toBeVisible() await page.click('button:has-text("Custom CSS")') await page.fill( 'div[role="textbox"]', '.typebot-button {background-color: green}' ) - await expect( - typebotViewer(page).locator('[data-testid="button"]') - ).toHaveCSS('background-color', 'rgb(0, 128, 0)') + await expect(page.getByRole('button', { name: 'Go' })).toHaveCSS( + 'background-color', + 'rgb(0, 128, 0)' + ) }) }) }) diff --git a/apps/builder/src/lib/googleSheets.ts b/apps/builder/src/lib/googleSheets.ts index 163696b9dd..b9e6922350 100644 --- a/apps/builder/src/lib/googleSheets.ts +++ b/apps/builder/src/lib/googleSheets.ts @@ -27,7 +27,7 @@ export const getAuthenticatedGoogleClient = async ( ) as GoogleSheetsCredentialsData oauth2Client.setCredentials(data) - oauth2Client.on('tokens', updateTokens(credentialsId, data)) + oauth2Client.on('tokens', updateTokens(credentials.id, data)) return { client: oauth2Client, credentials } } diff --git a/apps/builder/src/pages/_app.tsx b/apps/builder/src/pages/_app.tsx index 77a0391138..b9e4036347 100644 --- a/apps/builder/src/pages/_app.tsx +++ b/apps/builder/src/pages/_app.tsx @@ -7,7 +7,6 @@ import { useRouterProgressBar } from '@/lib/routerProgressBar' import '@/assets/styles/routerProgressBar.css' import '@/assets/styles/plate.css' import '@/assets/styles/submissionsTable.css' -import '@/assets/styles/codeMirror.css' import '@/assets/styles/custom.css' import { UserProvider } from '@/features/account' import { TypebotProvider } from '@/features/editor' diff --git a/apps/builder/src/pages/api/typebots/[typebotId].ts b/apps/builder/src/pages/api/typebots/[typebotId].ts index e8a011ecfa..c0dcbf7bb1 100644 --- a/apps/builder/src/pages/api/typebots/[typebotId].ts +++ b/apps/builder/src/pages/api/typebots/[typebotId].ts @@ -92,9 +92,13 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { const updates = { ...omit(data, 'id', 'createdAt', 'updatedAt'), + version: '3', theme: data.theme ?? undefined, settings: data.settings ?? undefined, resultsTablePreferences: data.resultsTablePreferences ?? undefined, + groups: data.groups ?? [], + variables: data.variables ?? [], + edges: data.edges ?? [], } satisfies Prisma.TypebotUpdateInput const updatedTypebot = await prisma.typebot.update({ diff --git a/apps/builder/src/test/assets/typebots/integrations/googleSheets.json b/apps/builder/src/test/assets/typebots/integrations/googleSheets.json index da14d9db13..9a0ed9431d 100644 --- a/apps/builder/src/test/assets/typebots/integrations/googleSheets.json +++ b/apps/builder/src/test/assets/typebots/integrations/googleSheets.json @@ -30,7 +30,8 @@ "type": "email input", "options": { "labels": { "button": "Send", "placeholder": "Type your email..." }, - "variableId": "qyLW6xD1AyLeedso2tHmhw" + "variableId": "qyLW6xD1AyLeedso2tHmhw", + "retryMessageContent": "This email doesn't seem to be valid. Can you type it again?" }, "outgoingEdgeId": "4yg9V76fdDntpDEw6H3tvU" } diff --git a/apps/builder/src/test/assets/typebots/integrations/googleSheetsGet.json b/apps/builder/src/test/assets/typebots/integrations/googleSheetsGet.json index de8664221f..9404f7598a 100644 --- a/apps/builder/src/test/assets/typebots/integrations/googleSheetsGet.json +++ b/apps/builder/src/test/assets/typebots/integrations/googleSheetsGet.json @@ -30,7 +30,8 @@ "type": "email input", "options": { "labels": { "button": "Send", "placeholder": "Type your email..." }, - "variableId": "qyLW6xD1AyLeedso2tHmhw" + "variableId": "qyLW6xD1AyLeedso2tHmhw", + "retryMessageContent": "This email doesn't seem to be valid. Can you type it again?" }, "outgoingEdgeId": "4yg9V76fdDntpDEw6H3tvU" } diff --git a/apps/builder/src/test/utils/selectorUtils.ts b/apps/builder/src/test/utils/selectorUtils.ts index 5c276ce81e..924fdfde8d 100644 --- a/apps/builder/src/test/utils/selectorUtils.ts +++ b/apps/builder/src/test/utils/selectorUtils.ts @@ -4,6 +4,4 @@ export const deleteButtonInConfirmDialog = (page: Page) => page.locator('section[role="alertdialog"] button:has-text("Delete")') export const stripePaymentForm = (page: Page) => - page - .frameLocator('#typebot-iframe') - .frameLocator('[title="Secure payment input frame"]') + page.frameLocator('[title="Secure payment input frame"]') diff --git a/apps/docs/docs/editor/blocks/logic/set-variable.md b/apps/docs/docs/editor/blocks/logic/set-variable.md index 195662148d..c39c054b54 100644 --- a/apps/docs/docs/editor/blocks/logic/set-variable.md +++ b/apps/docs/docs/editor/blocks/logic/set-variable.md @@ -93,7 +93,7 @@ window.location.href :::caution It will not give you the parent URL if you embed the bot on your site. -A more bulletproof option is to pass the URL as a hidden variable in the embed code options. You can find an example [here](/embed/html-javascript#additional-configuration). +A more bulletproof option is to pass the URL as a prefilled variable in the embed code options. You can find an example [here](/embed/html-javascript#additional-configuration). ::: ## Extract a cookie diff --git a/apps/docs/docs/editor/variables.mdx b/apps/docs/docs/editor/variables.mdx index 3acaa7f986..450eb230ad 100644 --- a/apps/docs/docs/editor/variables.mdx +++ b/apps/docs/docs/editor/variables.mdx @@ -34,12 +34,12 @@ Then the variables will be prefilled as following: - Email => test@test.com - First name => John -Prefilling variables using the embed library is even easier. You need to add an object named `hiddenVariables` that contains a dictionary of your values. For example: +Prefilling variables using the embed library is even easier. You need to add an object named `prefilledVariables` that contains a dictionary of your values. For example: ```js Typebot.initBubble({ - url: `https://viewer.typebot.io/typebot-support`, - hiddenVariables: { + typebot: `my-bot`, + prefilledVariables: { Email: 'test@test.com', 'First name': 'John', }, diff --git a/apps/docs/docs/embed/html-javascript.md b/apps/docs/docs/embed/html-javascript.md index 61d2f52215..bd864ebeb1 100644 --- a/apps/docs/docs/embed/html-javascript.md +++ b/apps/docs/docs/embed/html-javascript.md @@ -12,10 +12,10 @@ There, you can change the container dimensions. Here is a code example: ```html -
+ ``` @@ -32,8 +32,8 @@ Here is an example: ``` @@ -71,9 +71,8 @@ Here is an example: ```html ``` @@ -122,12 +121,12 @@ You can bind these commands on a button element, for example: ## Additional configuration -You can add hidden variable values in your embed code by adding the `hiddenVariables` option. Here is an example: +You can prefill the bot variable values in your embed code by adding the `prefilledVariables` option. Here is an example: ```js -Typebot.initContainer('typebot-container', { - url: 'https://viewer.typebot.io/my-typebot', - hiddenVariables: { +Typebot.initStandard({ + typebot: 'my-typebot', + prefilledVariables: { 'Current URL': 'https://my-site/account', 'User name': 'John Doe', }, diff --git a/apps/docs/docs/embed/wordpress.md b/apps/docs/docs/embed/wordpress.md index 85af87b93d..2f262045e4 100644 --- a/apps/docs/docs/embed/wordpress.md +++ b/apps/docs/docs/embed/wordpress.md @@ -8,78 +8,19 @@ Typebot has a native [WordPress plug-in](https://wordpress.org/plugins/typebot/) Of course, before using it, you need to create and publish your first typebot. -WP plugin preview +WP plugin preview -You can either choose to configurate an "easy setup" or "advanced setup". - -## Easy setup - -### Container - -When choosing "container" and click on "Save" you can then use a typebot shortcode in your page builder. [Here is a complete tutorial on how to insert a shortcode](https://www.wpbeginner.com/wp-tutorials/how-to-add-a-shortcode-in-wordpress/). - -Here is how it looks like: - -```text -[typebot width="100%" height="500px" background-color="#F7F8FF"] -``` - -`width`, `height`, `background-color` and `url` are optionnal. - -You should use `url` parameter only if you need to embed different typebots as containers on your website. - -If your typebot appears to have a small height like this: -WP plugin preview - -you need to set a fixed `height` in pixel (`500px` or `600px` is usually a great number). - -### Popup & Bubble - -Fields are self explanatory. To open the popup or the bubble when a button in your site is clicked you can use the [Javascript commands](/embed/html-javascript#open-or-close-a-popup). - -#### Pages to include separated by a comma - -With this field, you can tell the plugin to include the typebot only on specific pages. - -Example: - -- `/my-page,/other-page/*`: the typebot will appear on these pages: `/my-page`, `/other-page`, `/other-page/sub/path`, `/other-page/other/path` - -## Advanced setup - -This config allows you to directly paste the code from "HTML & Javascript" instructions in the Share page. So that you can set your own logic if needed. - -Here is an example for a bubble config: - -```html - - - -``` +The code snippet to paste is easily configurable in the Share tab of your bot after clicking on the "Wordpress" button. ## Personalize user experience -You can leverage the [hidden variables](/editor/variables#hidden-variables) and inject your user information directly into your typebot so that the experience is entirely customized to your user. +You can leverage the [prefilled variables](/editor/variables#prefilled-variables) and inject your user information directly into your typebot so that the experience is entirely customized to your user. Here are the available variables from WordPress, make sure to create them in your typebot's variables dropdown: WP predefined variables -You can use these variables anywhere on your typebot. For more informations, check out the [Hidden variables doc](https://docs.typebot.io/editor/variables/hidden-variables) +You can use these variables anywhere on your typebot. For more informations, check out the [Prefilled variables doc](https://docs.typebot.io/editor/variables#prefilled-variables) ## Your typebot isn't showing? diff --git a/apps/docs/static/img/embeddings/wordpress-preview.png b/apps/docs/static/img/embeddings/wordpress-preview.png index f40baa6e1a..29ddea58c2 100644 Binary files a/apps/docs/static/img/embeddings/wordpress-preview.png and b/apps/docs/static/img/embeddings/wordpress-preview.png differ diff --git a/apps/viewer/src/components/TypebotPage.tsx b/apps/viewer/src/components/TypebotPage.tsx deleted file mode 100644 index b60ea115ff..0000000000 --- a/apps/viewer/src/components/TypebotPage.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { TypebotViewer } from 'bot-engine' -import { AnswerInput, PublicTypebot, Typebot, VariableWithValue } from 'models' -import { useRouter } from 'next/router' -import React, { useEffect, useState } from 'react' -import { - injectCustomHeadCode, - isDefined, - isNotDefined, - isNotEmpty, -} from 'utils' -import { SEO } from './Seo' -import { ErrorPage } from './ErrorPage' -import { createResultQuery, updateResultQuery } from '@/features/results' -import { upsertAnswerQuery } from '@/features/answers' -import { gtmBodyElement } from '@/lib/google-tag-manager' -import { - getExistingResultFromSession, - setResultInSession, -} from '@/utils/sessionStorage' - -export type TypebotPageProps = { - publishedTypebot: Omit & { - typebot: Pick - } - url: string - isIE: boolean - customHeadCode: string | null -} - -export const TypebotPage = ({ - publishedTypebot, - isIE, - url, - customHeadCode, -}: TypebotPageProps) => { - const { asPath, push } = useRouter() - const [showTypebot, setShowTypebot] = useState(false) - const [predefinedVariables, setPredefinedVariables] = useState<{ - [key: string]: string - }>() - const [error, setError] = useState( - isIE ? new Error('Internet explorer is not supported') : undefined - ) - const [resultId, setResultId] = useState() - const [variableUpdateQueue, setVariableUpdateQueue] = useState< - VariableWithValue[][] - >([]) - const [chatStarted, setChatStarted] = useState(false) - - useEffect(() => { - setShowTypebot(true) - const urlParams = new URLSearchParams(location.search) - clearQueryParams() - const predefinedVariables: { [key: string]: string } = {} - urlParams.forEach((value, key) => { - predefinedVariables[key] = value - }) - setPredefinedVariables(predefinedVariables) - initializeResult().then() - if (isDefined(customHeadCode)) injectCustomHeadCode(customHeadCode) - const gtmId = publishedTypebot.settings.metadata.googleTagManagerId - if (isNotEmpty(gtmId)) document.body.prepend(gtmBodyElement(gtmId)) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - - const clearQueryParams = () => { - const hasQueryParams = asPath.includes('?') - if ( - hasQueryParams && - publishedTypebot.settings.general.isHideQueryParamsEnabled !== false - ) - push(asPath.split('?')[0], undefined, { shallow: true }) - } - - const initializeResult = async () => { - const resultIdFromSession = getExistingResultFromSession() - if (resultIdFromSession) setResultId(resultIdFromSession) - else { - const { error, data } = await createResultQuery( - publishedTypebot.typebotId - ) - if (error) return setError(error) - if (data?.hasReachedLimit) - return setError(new Error('This bot is now closed.')) - if (data?.result) { - setResultId(data.result.id) - if ( - publishedTypebot.settings.general.isNewResultOnRefreshEnabled !== true - ) - setResultInSession(data.result.id) - } - } - } - - useEffect(() => { - if (!resultId || variableUpdateQueue.length === 0) return - Promise.all(variableUpdateQueue.map(sendNewVariables(resultId))).then() - setVariableUpdateQueue([]) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [resultId]) - - const handleNewVariables = async (variables: VariableWithValue[]) => { - if (!resultId) - return setVariableUpdateQueue([...variableUpdateQueue, variables]) - await sendNewVariables(resultId)(variables) - } - - const sendNewVariables = - (resultId: string) => async (variables: VariableWithValue[]) => { - if (publishedTypebot.settings.general.isResultSavingEnabled === false) - return - const { error } = await updateResultQuery(resultId, { variables }) - if (error) setError(error) - } - - const handleNewAnswer = async ( - answer: AnswerInput & { uploadedFiles: boolean } - ) => { - if (!resultId) return setError(new Error('Error: result was not created')) - if (publishedTypebot.settings.general.isResultSavingEnabled !== false) { - const { error } = await upsertAnswerQuery({ ...answer, resultId }) - if (error) setError(error) - } - if (chatStarted) return - updateResultQuery(resultId, { - hasStarted: true, - }).then(({ error }) => (error ? setError(error) : setChatStarted(true))) - } - - const handleCompleted = async () => { - if (publishedTypebot.settings.general.isResultSavingEnabled === false) - return - if (!resultId) return setError(new Error('Error: result was not created')) - const { error } = await updateResultQuery(resultId, { isCompleted: true }) - if (error) setError(error) - } - - if (error) { - return - } - return ( -
- - {showTypebot && ( - - )} -
- ) -} diff --git a/apps/viewer/src/components/TypebotPageV2.tsx b/apps/viewer/src/components/TypebotPageV2.tsx index fd376311f2..4addce3111 100644 --- a/apps/viewer/src/components/TypebotPageV2.tsx +++ b/apps/viewer/src/components/TypebotPageV2.tsx @@ -1,50 +1,161 @@ -import { Standard } from '@typebot.io/react' -import { BackgroundType, Typebot } from 'models' +import { TypebotViewer } from 'bot-engine' +import { AnswerInput, PublicTypebot, Typebot, VariableWithValue } from 'models' import { useRouter } from 'next/router' +import React, { useEffect, useState } from 'react' +import { + injectCustomHeadCode, + isDefined, + isNotDefined, + isNotEmpty, +} from 'utils' import { SEO } from './Seo' +import { ErrorPage } from './ErrorPage' +import { createResultQuery, updateResultQuery } from '@/features/results' +import { upsertAnswerQuery } from '@/features/answers' +import { gtmBodyElement } from '@/lib/google-tag-manager' +import { + getExistingResultFromSession, + setResultInSession, +} from '@/utils/sessionStorage' export type TypebotPageProps = { + publishedTypebot: Omit & { + typebot: Pick + } url: string - typebot?: Pick + isIE: boolean + customHeadCode: string | null } -export const TypebotPage = ({ url, typebot }: TypebotPageProps) => { - const { asPath, push, query } = useRouter() +export const TypebotPageV2 = ({ + publishedTypebot, + isIE, + url, + customHeadCode, +}: TypebotPageProps) => { + const { asPath, push } = useRouter() + const [showTypebot, setShowTypebot] = useState(false) + const [predefinedVariables, setPredefinedVariables] = useState<{ + [key: string]: string + }>() + const [error, setError] = useState( + isIE ? new Error('Internet explorer is not supported') : undefined + ) + const [resultId, setResultId] = useState() + const [variableUpdateQueue, setVariableUpdateQueue] = useState< + VariableWithValue[][] + >([]) + const [chatStarted, setChatStarted] = useState(false) - const background = typebot?.theme.general.background + useEffect(() => { + setShowTypebot(true) + const urlParams = new URLSearchParams(location.search) + clearQueryParams() + const predefinedVariables: { [key: string]: string } = {} + urlParams.forEach((value, key) => { + predefinedVariables[key] = value + }) + setPredefinedVariables(predefinedVariables) + initializeResult().then() + if (isDefined(customHeadCode)) injectCustomHeadCode(customHeadCode) + const gtmId = publishedTypebot.settings.metadata.googleTagManagerId + if (isNotEmpty(gtmId)) document.body.prepend(gtmBodyElement(gtmId)) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) - const clearQueryParamsIfNecessary = () => { + const clearQueryParams = () => { const hasQueryParams = asPath.includes('?') if ( - !hasQueryParams || - !(typebot?.settings.general.isHideQueryParamsEnabled ?? true) + hasQueryParams && + publishedTypebot.settings.general.isHideQueryParamsEnabled !== false ) + push(asPath.split('?')[0], undefined, { shallow: true }) + } + + const initializeResult = async () => { + const resultIdFromSession = getExistingResultFromSession() + if (resultIdFromSession) setResultId(resultIdFromSession) + else { + const { error, data } = await createResultQuery( + publishedTypebot.typebotId + ) + if (error) return setError(error) + if (data?.hasReachedLimit) + return setError(new Error('This bot is now closed.')) + if (data?.result) { + setResultId(data.result.id) + if ( + publishedTypebot.settings.general.isNewResultOnRefreshEnabled !== true + ) + setResultInSession(data.result.id) + } + } + } + + useEffect(() => { + if (!resultId || variableUpdateQueue.length === 0) return + Promise.all(variableUpdateQueue.map(sendNewVariables(resultId))).then() + setVariableUpdateQueue([]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [resultId]) + + const handleNewVariables = async (variables: VariableWithValue[]) => { + if (!resultId) + return setVariableUpdateQueue([...variableUpdateQueue, variables]) + await sendNewVariables(resultId)(variables) + } + + const sendNewVariables = + (resultId: string) => async (variables: VariableWithValue[]) => { + if (publishedTypebot.settings.general.isResultSavingEnabled === false) + return + const { error } = await updateResultQuery(resultId, { variables }) + if (error) setError(error) + } + + const handleNewAnswer = async ( + answer: AnswerInput & { uploadedFiles: boolean } + ) => { + if (!resultId) return setError(new Error('Error: result was not created')) + if (publishedTypebot.settings.general.isResultSavingEnabled !== false) { + const { error } = await upsertAnswerQuery({ ...answer, resultId }) + if (error) setError(error) + } + if (chatStarted) return + updateResultQuery(resultId, { + hasStarted: true, + }).then(({ error }) => (error ? setError(error) : setChatStarted(true))) + } + + const handleCompleted = async () => { + if (publishedTypebot.settings.general.isResultSavingEnabled === false) return - push(asPath.split('?')[0], undefined, { shallow: true }) + if (!resultId) return setError(new Error('Error: result was not created')) + const { error } = await updateResultQuery(resultId, { isCompleted: true }) + if (error) setError(error) } + if (error) { + return + } return ( -
- {typebot && ( - + + {showTypebot && ( + )} -
) } diff --git a/apps/viewer/src/components/TypebotPageV3.tsx b/apps/viewer/src/components/TypebotPageV3.tsx new file mode 100644 index 0000000000..67255c0ee1 --- /dev/null +++ b/apps/viewer/src/components/TypebotPageV3.tsx @@ -0,0 +1,50 @@ +import { Standard } from '@typebot.io/react' +import { BackgroundType, Typebot } from 'models' +import { useRouter } from 'next/router' +import { SEO } from './Seo' + +export type TypebotPageProps = { + url: string + typebot?: Pick +} + +export const TypebotPageV3 = ({ url, typebot }: TypebotPageProps) => { + const { asPath, push, query } = useRouter() + + const background = typebot?.theme.general.background + + const clearQueryParamsIfNecessary = () => { + const hasQueryParams = asPath.includes('?') + if ( + !hasQueryParams || + !(typebot?.settings.general.isHideQueryParamsEnabled ?? true) + ) + return + push(asPath.split('?')[0], undefined, { shallow: true }) + } + + return ( +
+ {typebot && ( + + )} + +
+ ) +} diff --git a/apps/viewer/src/features/blocks/inputs/fileUpload/fileUpload.spec.ts b/apps/viewer/src/features/blocks/inputs/fileUpload/fileUpload.spec.ts index db8cdd1a15..c9c5f24514 100644 --- a/apps/viewer/src/features/blocks/inputs/fileUpload/fileUpload.spec.ts +++ b/apps/viewer/src/features/blocks/inputs/fileUpload/fileUpload.spec.ts @@ -8,7 +8,6 @@ import { importTypebotInDatabase, injectFakeResults, } from 'utils/playwright/databaseActions' -import { typebotViewer } from 'utils/playwright/testHelpers' import { getTestAsset } from '@/test/utils/playwright' import { Plan } from 'db' @@ -21,18 +20,16 @@ test('should work as expected', async ({ page, browser }) => { publicId: `${typebotId}-public`, }) await page.goto(`/${typebotId}-public`) - await typebotViewer(page) + await page .locator(`input[type="file"]`) .setInputFiles([ getTestAsset('typebots/api.json'), getTestAsset('typebots/fileUpload.json'), getTestAsset('typebots/hugeGroup.json'), ]) - await expect(typebotViewer(page).locator(`text="3"`)).toBeVisible() - await typebotViewer(page).locator('text="Upload 3 files"').click() - await expect( - typebotViewer(page).locator(`text="3 files uploaded"`) - ).toBeVisible() + await expect(page.locator(`text="3"`)).toBeVisible() + await page.locator('text="Upload 3 files"').click() + await expect(page.locator(`text="3 files uploaded"`)).toBeVisible() await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) await expect(page.getByRole('link', { name: 'api.json' })).toHaveAttribute( 'href', @@ -100,18 +97,16 @@ test.describe('Storage limit is reached', () => { page, }) => { await page.goto(`/${typebotId}-public`) - await typebotViewer(page) + await page .locator(`input[type="file"]`) .setInputFiles([ getTestAsset('typebots/api.json'), getTestAsset('typebots/fileUpload.json'), getTestAsset('typebots/hugeGroup.json'), ]) - await expect(typebotViewer(page).locator(`text="3"`)).toBeVisible() - await typebotViewer(page).locator('text="Upload 3 files"').click() - await expect( - typebotViewer(page).locator(`text="3 files uploaded"`) - ).toBeVisible() + await expect(page.locator(`text="3"`)).toBeVisible() + await page.locator('text="Upload 3 files"').click() + await expect(page.locator(`text="3 files uploaded"`)).toBeVisible() await page.evaluate(() => window.localStorage.setItem('workspaceId', 'starterWorkspace') ) diff --git a/apps/viewer/src/features/blocks/inputs/fileUpload/fileUploadV2.spec.ts b/apps/viewer/src/features/blocks/inputs/fileUpload/fileUploadV2.spec.ts deleted file mode 100644 index 914c282979..0000000000 --- a/apps/viewer/src/features/blocks/inputs/fileUpload/fileUploadV2.spec.ts +++ /dev/null @@ -1,117 +0,0 @@ -import test, { expect } from '@playwright/test' -import { createId } from '@paralleldrive/cuid2' -import { parse } from 'papaparse' -import { readFileSync } from 'fs' -import { isDefined } from 'utils' -import { - createWorkspaces, - importTypebotInDatabase, - injectFakeResults, -} from 'utils/playwright/databaseActions' -import { getTestAsset } from '@/test/utils/playwright' -import { Plan } from 'db' - -const THREE_GIGABYTES = 3 * 1024 * 1024 * 1024 - -test('should work as expected', async ({ page, browser }) => { - const typebotId = createId() - await importTypebotInDatabase(getTestAsset('typebots/fileUpload.json'), { - id: typebotId, - publicId: `${typebotId}-public`, - }) - await page.goto(`/next/${typebotId}-public`) - await page - .locator(`input[type="file"]`) - .setInputFiles([ - getTestAsset('typebots/api.json'), - getTestAsset('typebots/fileUpload.json'), - getTestAsset('typebots/hugeGroup.json'), - ]) - await expect(page.locator(`text="3"`)).toBeVisible() - await page.locator('text="Upload 3 files"').click() - await expect(page.locator(`text="3 files uploaded"`)).toBeVisible() - await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) - await expect(page.getByRole('link', { name: 'api.json' })).toHaveAttribute( - 'href', - /.+\/api\.json/ - ) - await expect( - page.getByRole('link', { name: 'fileUpload.json' }) - ).toHaveAttribute('href', /.+\/fileUpload\.json/) - await expect( - page.getByRole('link', { name: 'hugeGroup.json' }) - ).toHaveAttribute('href', /.+\/hugeGroup\.json/) - - await page.click('[data-testid="checkbox"] >> nth=0') - const [download] = await Promise.all([ - page.waitForEvent('download'), - page.getByRole('button', { name: 'Export' }).click(), - ]) - const downloadPath = await download.path() - expect(downloadPath).toBeDefined() - const file = readFileSync(downloadPath as string).toString() - const { data } = parse(file) - expect(data).toHaveLength(2) - expect((data[1] as unknown[])[1]).toContain(process.env.S3_ENDPOINT) - - const urls = ( - await Promise.all( - [ - page.getByRole('link', { name: 'api.json' }), - page.getByRole('link', { name: 'fileUpload.json' }), - page.getByRole('link', { name: 'hugeGroup.json' }), - ].map((elem) => elem.getAttribute('href')) - ) - ).filter(isDefined) - - const page2 = await browser.newPage() - await page2.goto(urls[0]) - await expect(page2.locator('pre')).toBeVisible() - - page.getByRole('button', { name: 'Delete' }).click() - await page.locator('button >> text="Delete"').click() - await expect(page.locator('text="api.json"')).toBeHidden() - await page2.goto(urls[0]) - await expect(page2.locator('pre')).toBeHidden() -}) - -test.describe('Storage limit is reached', () => { - const typebotId = createId() - const workspaceId = createId() - - test.beforeAll(async () => { - await createWorkspaces([{ id: workspaceId, plan: Plan.STARTER }]) - await importTypebotInDatabase(getTestAsset('typebots/fileUpload.json'), { - id: typebotId, - publicId: `${typebotId}-public`, - workspaceId, - }) - await injectFakeResults({ - typebotId, - count: 20, - fakeStorage: THREE_GIGABYTES, - }) - }) - - test("shouldn't upload anything if limit has been reached", async ({ - page, - }) => { - await page.goto(`/next/${typebotId}-public`) - await page - .locator(`input[type="file"]`) - .setInputFiles([ - getTestAsset('typebots/api.json'), - getTestAsset('typebots/fileUpload.json'), - getTestAsset('typebots/hugeGroup.json'), - ]) - await expect(page.locator(`text="3"`)).toBeVisible() - await page.locator('text="Upload 3 files"').click() - await expect(page.locator(`text="3 files uploaded"`)).toBeVisible() - await page.evaluate(() => - window.localStorage.setItem('workspaceId', 'starterWorkspace') - ) - await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) - await expect(page.locator('text="150%"')).toBeVisible() - await expect(page.locator('text="api.json"')).toBeHidden() - }) -}) diff --git a/apps/viewer/src/features/blocks/integrations/chatwoot/chatwoot.spec.ts b/apps/viewer/src/features/blocks/integrations/chatwoot/chatwoot.spec.ts index 26df441d78..be21ca94dc 100644 --- a/apps/viewer/src/features/blocks/integrations/chatwoot/chatwoot.spec.ts +++ b/apps/viewer/src/features/blocks/integrations/chatwoot/chatwoot.spec.ts @@ -3,7 +3,6 @@ import { createId } from '@paralleldrive/cuid2' import { createTypebots } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' import { defaultChatwootOptions, IntegrationBlockType } from 'models' -import { typebotViewer } from 'utils/playwright/testHelpers' const typebotId = createId() @@ -26,6 +25,6 @@ test('should work as expected', async ({ page }) => { }, ]) await page.goto(`/${typebotId}-public`) - await typebotViewer(page).getByRole('button', { name: 'Go' }).click() + await page.getByRole('button', { name: 'Go' }).click() await expect(page.locator('#chatwoot_live_chat_widget')).toBeVisible() }) diff --git a/apps/viewer/src/features/blocks/integrations/chatwoot/chatwootV2.spec.ts b/apps/viewer/src/features/blocks/integrations/chatwoot/chatwootV2.spec.ts deleted file mode 100644 index fee2996c13..0000000000 --- a/apps/viewer/src/features/blocks/integrations/chatwoot/chatwootV2.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import test, { expect } from '@playwright/test' -import { createId } from '@paralleldrive/cuid2' -import { createTypebots } from 'utils/playwright/databaseActions' -import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' -import { defaultChatwootOptions, IntegrationBlockType } from 'models' - -const typebotId = createId() - -const chatwootTestWebsiteToken = 'tueXiiqEmrWUCZ4NUyoR7nhE' - -test('should work as expected', async ({ page }) => { - await createTypebots([ - { - id: typebotId, - ...parseDefaultGroupWithBlock( - { - type: IntegrationBlockType.CHATWOOT, - options: { - ...defaultChatwootOptions, - websiteToken: chatwootTestWebsiteToken, - }, - }, - { withGoButton: true } - ), - }, - ]) - await page.goto(`/next/${typebotId}-public`) - await page.getByRole('button', { name: 'Go' }).click() - await expect(page.locator('#chatwoot_live_chat_widget')).toBeVisible() -}) diff --git a/apps/viewer/src/features/blocks/integrations/sendEmail/sendEmail.spec.ts b/apps/viewer/src/features/blocks/integrations/sendEmail/sendEmail.spec.ts index b7be76a757..33d41618aa 100644 --- a/apps/viewer/src/features/blocks/integrations/sendEmail/sendEmail.spec.ts +++ b/apps/viewer/src/features/blocks/integrations/sendEmail/sendEmail.spec.ts @@ -3,7 +3,6 @@ import { createSmtpCredentials } from '../../../../test/utils/databaseActions' import { createId } from '@paralleldrive/cuid2' import { SmtpCredentialsData } from 'models' import { importTypebotInDatabase } from 'utils/playwright/databaseActions' -import { typebotViewer } from 'utils/playwright/testHelpers' import { getTestAsset } from '@/test/utils/playwright' export const mockSmtpCredentials: SmtpCredentialsData = { @@ -33,23 +32,8 @@ test('should send an email', async ({ page }) => { publicId: `${typebotId}-public`, }) await page.goto(`/${typebotId}-public`) - const [response] = await Promise.all([ - page.waitForResponse((resp) => - resp.request().url().includes(`integrations/email`) - ), - typebotViewer(page).locator('text=Send email').click(), - ]) - const { previewUrl } = await response.json() - await page.goto(previewUrl) - await expect(page.locator('text="Hey!"')).toBeVisible() - await expect( - page.locator(`text="${mockSmtpCredentials.from.name}"`) - ).toBeVisible() - await expect(page.locator('text="" >> nth=0')).toBeVisible() - await expect(page.locator('text="" >> nth=0')).toBeVisible() - await expect( - page.locator('text="" >> nth=0') - ).toBeVisible() + await page.locator('text=Send email').click() + await expect(page.getByText('Email sent!')).toBeVisible() await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) await page.click('text="See logs"') await expect(page.locator('text="Email successfully sent"')).toBeVisible() diff --git a/apps/viewer/src/features/blocks/integrations/sendEmail/sendEmailV2.spec.ts b/apps/viewer/src/features/blocks/integrations/sendEmail/sendEmailV2.spec.ts deleted file mode 100644 index be3fbdccb6..0000000000 --- a/apps/viewer/src/features/blocks/integrations/sendEmail/sendEmailV2.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import test, { expect } from '@playwright/test' -import { createSmtpCredentials } from '../../../../test/utils/databaseActions' -import { createId } from '@paralleldrive/cuid2' -import { SmtpCredentialsData } from 'models' -import { importTypebotInDatabase } from 'utils/playwright/databaseActions' -import { getTestAsset } from '@/test/utils/playwright' - -const mockSmtpCredentials: SmtpCredentialsData = { - from: { - email: 'sunny.cremin66@ethereal.email', - name: 'Sunny Cremin', - }, - host: 'smtp.ethereal.email', - port: 587, - username: 'sunny.cremin66@ethereal.email', - password: 'yJDHkf2bYbNydaRvTq', -} - -test.beforeAll(async () => { - try { - const credentialsId = 'send-email-credentials' - await createSmtpCredentials(credentialsId, mockSmtpCredentials) - } catch (err) { - console.error(err) - } -}) - -test('should send an email', async ({ page }) => { - const typebotId = createId() - await importTypebotInDatabase(getTestAsset('typebots/sendEmail.json'), { - id: typebotId, - publicId: `${typebotId}-public`, - }) - await page.goto(`/next/${typebotId}-public`) - await page.locator('text=Send email').click() - await expect(page.getByText('Email sent!')).toBeVisible() - await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) - await page.click('text="See logs"') - await expect(page.locator('text="Email successfully sent"')).toBeVisible() -}) diff --git a/apps/viewer/src/features/blocks/integrations/webhook/webhook.spec.ts b/apps/viewer/src/features/blocks/integrations/webhook/webhook.spec.ts index b19d6f8253..01c2867347 100644 --- a/apps/viewer/src/features/blocks/integrations/webhook/webhook.spec.ts +++ b/apps/viewer/src/features/blocks/integrations/webhook/webhook.spec.ts @@ -1,190 +1,67 @@ import test, { expect } from '@playwright/test' import { createId } from '@paralleldrive/cuid2' -import { HttpMethod, Typebot } from 'models' +import { HttpMethod } from 'models' import { createWebhook, importTypebotInDatabase, } from 'utils/playwright/databaseActions' -import { typebotViewer } from 'utils/playwright/testHelpers' -import { apiToken } from 'utils/playwright/databaseSetup' import { getTestAsset } from '@/test/utils/playwright' const typebotId = createId() -test.describe('Bot', () => { - test.beforeEach(async () => { - await importTypebotInDatabase(getTestAsset('typebots/webhook.json'), { - id: typebotId, - publicId: `${typebotId}-public`, - }) - - try { - await createWebhook(typebotId, { - id: 'failing-webhook', - url: 'http://localhost:3001/api/mock/fail', - method: HttpMethod.POST, - }) - - await createWebhook(typebotId, { - id: 'partial-body-webhook', - url: 'http://localhost:3000/api/mock/webhook-easy-config', - method: HttpMethod.POST, - body: `{ - "name": "{{Name}}", - "age": {{Age}}, - "gender": "{{Gender}}" - }`, - }) - - await createWebhook(typebotId, { - id: 'full-body-webhook', - url: 'http://localhost:3000/api/mock/webhook-easy-config', - method: HttpMethod.POST, - body: `{{Full body}}`, - }) - } catch (err) { - // Webhooks already created - } - }) - - test('should execute webhooks properly', async ({ page }) => { - await page.goto(`/${typebotId}-public`) - await typebotViewer(page).locator('text=Send failing webhook').click() - await typebotViewer(page) - .locator('[placeholder="Type a name..."]') - .fill('John') - await typebotViewer(page).locator('text="Send"').click() - await typebotViewer(page) - .locator('[placeholder="Type an age..."]') - .fill('30') - await typebotViewer(page).locator('text="Send"').click() - await typebotViewer(page).locator('text="Male"').click() - await expect( - typebotViewer(page).getByText('{"name":"John","age":25,"gender":"male"}') - ).toBeVisible() - await expect( - typebotViewer(page).getByText('{"name":"John","age":30,"gender":"Male"}') - ).toBeVisible() - await page.goto(`http://localhost:3000/typebots/${typebotId}/results`) - await page.click('text="See logs"') - await expect( - page.locator('text="Webhook successfuly executed." >> nth=1') - ).toBeVisible() - await expect(page.locator('text="Webhook returned an error"')).toBeVisible() - }) -}) - -test.describe('API', () => { - const typebotId = 'webhook-flow' - - test.beforeAll(async () => { - try { - await importTypebotInDatabase(getTestAsset('typebots/api.json'), { - id: typebotId, - }) - await createWebhook(typebotId) - } catch (err) { - console.log(err) - } +test.beforeEach(async () => { + await importTypebotInDatabase(getTestAsset('typebots/webhook.json'), { + id: typebotId, + publicId: `${typebotId}-public`, }) - test('can list typebots', async ({ request }) => { - expect((await request.get(`/api/typebots`)).status()).toBe(401) - const response = await request.get(`/api/typebots`, { - headers: { Authorization: `Bearer ${apiToken}` }, + try { + await createWebhook(typebotId, { + id: 'failing-webhook', + url: 'http://localhost:3001/api/mock/fail', + method: HttpMethod.POST, }) - const { typebots } = (await response.json()) as { typebots: Typebot[] } - expect(typebots.length).toBeGreaterThanOrEqual(1) - expect(typebots.find((typebot) => typebot.id === typebotId)).toMatchObject({ - id: typebotId, - name: 'My typebot', - }) - }) - - test('can get webhook blocks', async ({ request }) => { - expect( - (await request.get(`/api/typebots/${typebotId}/webhookBlocks`)).status() - ).toBe(401) - const response = await request.get( - `/api/typebots/${typebotId}/webhookBlocks`, - { - headers: { Authorization: `Bearer ${apiToken}` }, - } - ) - const { blocks } = await response.json() - expect(blocks).toHaveLength(1) - expect(blocks[0]).toEqual({ - blockId: 'webhookBlock', - name: 'Webhook > webhookBlock', - }) - }) - test('can subscribe webhook', async ({ request }) => { - expect( - ( - await request.post( - `/api/typebots/${typebotId}/blocks/webhookBlock/subscribeWebhook`, - { data: { url: 'https://test.com' } } - ) - ).status() - ).toBe(401) - const response = await request.post( - `/api/typebots/${typebotId}/blocks/webhookBlock/subscribeWebhook`, - { - headers: { - Authorization: `Bearer ${apiToken}`, - }, - data: { url: 'https://test.com' }, - } - ) - const body = await response.json() - expect(body).toEqual({ - message: 'success', + await createWebhook(typebotId, { + id: 'partial-body-webhook', + url: 'http://localhost:3000/api/mock/webhook-easy-config', + method: HttpMethod.POST, + body: `{ + "name": "{{Name}}", + "age": {{Age}}, + "gender": "{{Gender}}" + }`, }) - }) - test('can unsubscribe webhook', async ({ request }) => { - expect( - ( - await request.post( - `/api/typebots/${typebotId}/blocks/webhookBlock/unsubscribeWebhook` - ) - ).status() - ).toBe(401) - const response = await request.post( - `/api/typebots/${typebotId}/blocks/webhookBlock/unsubscribeWebhook`, - { - headers: { Authorization: `Bearer ${apiToken}` }, - } - ) - const body = await response.json() - expect(body).toEqual({ - message: 'success', + await createWebhook(typebotId, { + id: 'full-body-webhook', + url: 'http://localhost:3000/api/mock/webhook-easy-config', + method: HttpMethod.POST, + body: `{{Full body}}`, }) - }) + } catch (err) { + console.log(err) + } +}) - test('can get a sample result', async ({ request }) => { - expect( - ( - await request.get( - `/api/typebots/${typebotId}/blocks/webhookBlock/sampleResult` - ) - ).status() - ).toBe(401) - const response = await request.get( - `/api/typebots/${typebotId}/blocks/webhookBlock/sampleResult`, - { - headers: { Authorization: `Bearer ${apiToken}` }, - } - ) - const data = await response.json() - expect(data).toMatchObject({ - message: 'This is a sample result, it has been generated ⬇️', - Welcome: 'Hi!', - Email: 'test@email.com', - Name: 'answer value', - Services: 'Website dev, Content Marketing, Social Media, UI / UX Design', - 'Additional information': 'answer value', - }) - }) +test('should execute webhooks properly', async ({ page }) => { + await page.goto(`/${typebotId}-public`) + await page.locator('text=Send failing webhook').click() + await page.locator('[placeholder="Type a name..."]').fill('John') + await page.locator('text="Send"').click() + await page.locator('[placeholder="Type an age..."]').fill('30') + await page.locator('text="Send"').click() + await page.locator('text="Male"').click() + await expect( + page.getByText('{"name":"John","age":25,"gender":"male"}') + ).toBeVisible() + await expect( + page.getByText('{"name":"John","age":30,"gender":"Male"}') + ).toBeVisible() + await page.goto(`http://localhost:3000/typebots/${typebotId}/results`) + await page.click('text="See logs"') + await expect( + page.locator('text="Webhook successfuly executed." >> nth=1') + ).toBeVisible() + await expect(page.locator('text="Webhook returned an error"')).toBeVisible() }) diff --git a/apps/viewer/src/features/blocks/integrations/webhook/webhookV2.spec.ts b/apps/viewer/src/features/blocks/integrations/webhook/webhookV2.spec.ts deleted file mode 100644 index bb78bb73ee..0000000000 --- a/apps/viewer/src/features/blocks/integrations/webhook/webhookV2.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import test, { expect } from '@playwright/test' -import { createId } from '@paralleldrive/cuid2' -import { HttpMethod } from 'models' -import { - createWebhook, - importTypebotInDatabase, -} from 'utils/playwright/databaseActions' -import { getTestAsset } from '@/test/utils/playwright' - -const typebotId = createId() - -test.beforeEach(async () => { - await importTypebotInDatabase(getTestAsset('typebots/webhook.json'), { - id: typebotId, - publicId: `${typebotId}-public`, - }) - - try { - await createWebhook(typebotId, { - id: 'failing-webhook', - url: 'http://localhost:3001/api/mock/fail', - method: HttpMethod.POST, - }) - - await createWebhook(typebotId, { - id: 'partial-body-webhook', - url: 'http://localhost:3000/api/mock/webhook-easy-config', - method: HttpMethod.POST, - body: `{ - "name": "{{Name}}", - "age": {{Age}}, - "gender": "{{Gender}}" - }`, - }) - - await createWebhook(typebotId, { - id: 'full-body-webhook', - url: 'http://localhost:3000/api/mock/webhook-easy-config', - method: HttpMethod.POST, - body: `{{Full body}}`, - }) - } catch (err) { - // Webhooks already created - } -}) - -test('should execute webhooks properly', async ({ page }) => { - await page.goto(`/next/${typebotId}-public`) - await page.locator('text=Send failing webhook').click() - await page.locator('[placeholder="Type a name..."]').fill('John') - await page.locator('text="Send"').click() - await page.locator('[placeholder="Type an age..."]').fill('30') - await page.locator('text="Send"').click() - await page.locator('text="Male"').click() - await expect( - page.getByText('{"name":"John","age":25,"gender":"male"}') - ).toBeVisible() - await expect( - page.getByText('{"name":"John","age":30,"gender":"Male"}') - ).toBeVisible() - await page.goto(`http://localhost:3000/typebots/${typebotId}/results`) - await page.click('text="See logs"') - await expect( - page.locator('text="Webhook successfuly executed." >> nth=1') - ).toBeVisible() - await expect(page.locator('text="Webhook returned an error"')).toBeVisible() -}) diff --git a/apps/viewer/src/features/blocks/logic/typebotLink/typebotLink.spec.ts b/apps/viewer/src/features/blocks/logic/typebotLink/typebotLink.spec.ts index 0e12de7833..d9179bd6e9 100644 --- a/apps/viewer/src/features/blocks/logic/typebotLink/typebotLink.spec.ts +++ b/apps/viewer/src/features/blocks/logic/typebotLink/typebotLink.spec.ts @@ -1,9 +1,8 @@ import { getTestAsset } from '@/test/utils/playwright' import test, { expect } from '@playwright/test' import { importTypebotInDatabase } from 'utils/playwright/databaseActions' -import { typebotViewer } from 'utils/playwright/testHelpers' -const typebotId = 'cl0ibhi7s0018n21aarlmg0cm1' +const typebotId = 'cl0ibhi7s0018n21aarlmg0cm' const linkedTypebotId = 'cl0ibhv8d0130n21aw8doxhj5' test.beforeAll(async () => { @@ -23,16 +22,9 @@ test.beforeAll(async () => { test('should work as expected', async ({ page }) => { await page.goto(`/${typebotId}-public`) - await typebotViewer(page).locator('input').fill('Hello there!') - await Promise.all([ - page.waitForResponse( - (resp) => - resp.request().url().includes(`/api/typebots/t/results`) && - resp.status() === 200 && - resp.request().method() === 'PUT' - ), - typebotViewer(page).locator('input').press('Enter'), - ]) + await page.locator('input').fill('Hello there!') + await page.locator('input').press('Enter') + await expect(page.getByText('Cheers!')).toBeVisible() await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) await expect(page.locator('text=Hello there!')).toBeVisible() }) diff --git a/apps/viewer/src/features/blocks/logic/typebotLink/typebotLinkV2.spec.ts b/apps/viewer/src/features/blocks/logic/typebotLink/typebotLinkV2.spec.ts deleted file mode 100644 index 1bd0197de6..0000000000 --- a/apps/viewer/src/features/blocks/logic/typebotLink/typebotLinkV2.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { getTestAsset } from '@/test/utils/playwright' -import test, { expect } from '@playwright/test' -import { importTypebotInDatabase } from 'utils/playwright/databaseActions' - -const typebotId = 'cl0ibhi7s0018n21aarlmg0cm' -const linkedTypebotId = 'cl0ibhv8d0130n21aw8doxhj5' - -test.beforeAll(async () => { - try { - await importTypebotInDatabase( - getTestAsset('typebots/linkTypebots/1.json'), - { id: typebotId, publicId: `${typebotId}-public` } - ) - await importTypebotInDatabase( - getTestAsset('typebots/linkTypebots/2.json'), - { id: linkedTypebotId, publicId: `${linkedTypebotId}-public` } - ) - } catch (err) { - console.error(err) - } -}) - -test('should work as expected', async ({ page }) => { - await page.goto(`/next/${typebotId}-public`) - await page.locator('input').fill('Hello there!') - await page.locator('input').press('Enter') - await expect(page.getByText('Cheers!')).toBeVisible() - await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) - await expect(page.locator('text=Hello there!')).toBeVisible() -}) diff --git a/apps/viewer/src/features/results/results.spec.ts b/apps/viewer/src/features/results/results.spec.ts index 16f9876eb3..299008e0f2 100644 --- a/apps/viewer/src/features/results/results.spec.ts +++ b/apps/viewer/src/features/results/results.spec.ts @@ -1,12 +1,7 @@ import { getTestAsset } from '@/test/utils/playwright' import test, { expect } from '@playwright/test' import { createId } from '@paralleldrive/cuid2' -import { - importTypebotInDatabase, - injectFakeResults, -} from 'utils/playwright/databaseActions' -import { apiToken } from 'utils/playwright/databaseSetup' -import { typebotViewer } from 'utils/playwright/testHelpers' +import { importTypebotInDatabase } from 'utils/playwright/databaseActions' test('Big groups should work as expected', async ({ page }) => { const typebotId = createId() @@ -15,11 +10,11 @@ test('Big groups should work as expected', async ({ page }) => { publicId: `${typebotId}-public`, }) await page.goto(`/${typebotId}-public`) - await typebotViewer(page).locator('input').fill('Baptiste') - await typebotViewer(page).locator('input').press('Enter') - await typebotViewer(page).locator('input').fill('26') - await typebotViewer(page).locator('input').press('Enter') - await typebotViewer(page).locator('button >> text=Yes').click() + await page.locator('input').fill('Baptiste') + await page.locator('input').press('Enter') + await page.locator('input').fill('26') + await page.locator('input').press('Enter') + await page.locator('button >> text=Yes').click() await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) await expect(page.locator('text="Baptiste"')).toBeVisible() await expect(page.locator('text="26"')).toBeVisible() @@ -30,22 +25,3 @@ test('Big groups should work as expected', async ({ page }) => { await expect(page.locator('text="26" >> nth=1')).toBeVisible() await expect(page.locator('text="Yes" >> nth=1')).toBeVisible() }) - -test('can list results with API', async ({ request }) => { - const typebotId = createId() - await importTypebotInDatabase(getTestAsset('typebots/api.json'), { - id: typebotId, - }) - await injectFakeResults({ typebotId, count: 20 }) - expect( - (await request.get(`/api/typebots/${typebotId}/results`)).status() - ).toBe(401) - const response = await request.get( - `/api/typebots/${typebotId}/results?limit=10`, - { - headers: { Authorization: `Bearer ${apiToken}` }, - } - ) - const { results } = await response.json() - expect(results).toHaveLength(10) -}) diff --git a/apps/viewer/src/features/results/resultsV2.spec.ts b/apps/viewer/src/features/results/resultsV2.spec.ts deleted file mode 100644 index 6e573494ab..0000000000 --- a/apps/viewer/src/features/results/resultsV2.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { getTestAsset } from '@/test/utils/playwright' -import test, { expect } from '@playwright/test' -import { createId } from '@paralleldrive/cuid2' -import { importTypebotInDatabase } from 'utils/playwright/databaseActions' - -test('Big groups should work as expected', async ({ page }) => { - const typebotId = createId() - await importTypebotInDatabase(getTestAsset('typebots/hugeGroup.json'), { - id: typebotId, - publicId: `${typebotId}-public`, - }) - await page.goto(`/next/${typebotId}-public`) - await page.locator('input').fill('Baptiste') - await page.locator('input').press('Enter') - await page.locator('input').fill('26') - await page.locator('input').press('Enter') - await page.locator('button >> text=Yes').click() - await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) - await expect(page.locator('text="Baptiste"')).toBeVisible() - await expect(page.locator('text="26"')).toBeVisible() - await expect(page.locator('text="Yes"')).toBeVisible() - await page.hover('tbody > tr') - await page.click('button >> text="Open"') - await expect(page.locator('text="Baptiste" >> nth=1')).toBeVisible() - await expect(page.locator('text="26" >> nth=1')).toBeVisible() - await expect(page.locator('text="Yes" >> nth=1')).toBeVisible() -}) diff --git a/apps/viewer/src/features/settings/settings.spec.ts b/apps/viewer/src/features/settings/settings.spec.ts index ed65d00565..32a964afa5 100644 --- a/apps/viewer/src/features/settings/settings.spec.ts +++ b/apps/viewer/src/features/settings/settings.spec.ts @@ -8,9 +8,8 @@ import { } from 'models' import { createTypebots, updateTypebot } from 'utils/playwright/databaseActions' import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' -import { typebotViewer } from 'utils/playwright/testHelpers' -test('Result should be in storage by default', async ({ page }) => { +test('Result should be overwritten on page refresh', async ({ page }) => { const typebotId = createId() await createTypebots([ { @@ -21,18 +20,21 @@ test('Result should be in storage by default', async ({ page }) => { }), }, ]) - await Promise.all([ + + const [, response] = await Promise.all([ page.goto(`/${typebotId}-public`), - page.waitForResponse( - (resp) => - resp.request().url().includes(`/api/typebots/${typebotId}/results`) && - resp.status() === 200 && - resp.request().method() === 'POST' - ), + page.waitForResponse(/sendMessage/), ]) - await page.reload() - const resultId = await page.evaluate(() => sessionStorage.getItem('resultId')) + const { resultId } = await response.json() expect(resultId).toBeDefined() + await expect(page.getByRole('textbox')).toBeVisible() + + const [, secondResponse] = await Promise.all([ + page.reload(), + page.waitForResponse(/sendMessage/), + ]) + const { resultId: secondResultId } = await secondResponse.json() + expect(secondResultId).toBe(resultId) }) test.describe('Create result on page refresh enabled', () => { @@ -54,28 +56,20 @@ test.describe('Create result on page refresh enabled', () => { }), }, ]) - await Promise.all([ + const [, response] = await Promise.all([ page.goto(`/${typebotId}-public`), - page.waitForResponse( - (resp) => - resp.request().url().includes(`/api/typebots/${typebotId}/results`) && - resp.status() === 200 && - resp.request().method() === 'POST' - ), + page.waitForResponse(/sendMessage/), ]) - await Promise.all([ + const { resultId } = await response.json() + expect(resultId).toBeDefined() + + await expect(page.getByRole('textbox')).toBeVisible() + const [, secondResponse] = await Promise.all([ page.reload(), - page.waitForResponse( - (resp) => - resp.request().url().includes(`/api/typebots/${typebotId}/results`) && - resp.status() === 200 && - resp.request().method() === 'POST' - ), + page.waitForResponse(/sendMessage/), ]) - const resultId = await page.evaluate(() => - sessionStorage.getItem('resultId') - ) - expect(resultId).toBe(null) + const { resultId: secondResultId } = await secondResponse.json() + expect(secondResultId).not.toBe(resultId) }) }) @@ -125,14 +119,12 @@ test('Show close message', async ({ page }) => { test('Should correctly parse metadata', async ({ page }) => { const typebotId = createId() - const googleTagManagerId = 'GTM-M72NXKB' const customMetadata: Metadata = { description: 'My custom description', title: 'Custom title', favIconUrl: 'https://www.baptistearno.com/favicon.png', imageUrl: 'https://www.baptistearno.com/images/site-preview.png', customHeadCode: '', - googleTagManagerId, } await createTypebots([ { @@ -148,11 +140,6 @@ test('Should correctly parse metadata', async ({ page }) => { }, ]) await page.goto(`/${typebotId}-public`) - await expect( - typebotViewer(page).locator( - `input[placeholder="${defaultTextInputOptions.labels.placeholder}"]` - ) - ).toBeVisible() expect( await page.evaluate(`document.querySelector('title').textContent`) ).toBe(customMetadata.title) @@ -177,6 +164,11 @@ test('Should correctly parse metadata', async ({ page }) => { ).getAttribute('href') ) ).toBe(customMetadata.favIconUrl) + await expect( + page.locator( + `input[placeholder="${defaultTextInputOptions.labels.placeholder}"]` + ) + ).toBeVisible() expect( await page.evaluate( () => @@ -184,12 +176,4 @@ test('Should correctly parse metadata', async ({ page }) => { .content ) ).toBe('John Doe') - expect( - await page.evaluate( - (googleTagManagerId) => - document.querySelector( - `iframe[src="https://www.googletagmanager.com/ns.html?id=${googleTagManagerId}"]` - ) as HTMLMetaElement - ) - ).toBeDefined() }) diff --git a/apps/viewer/src/features/settings/settingsV2.spec.ts b/apps/viewer/src/features/settings/settingsV2.spec.ts deleted file mode 100644 index b482a956e4..0000000000 --- a/apps/viewer/src/features/settings/settingsV2.spec.ts +++ /dev/null @@ -1,179 +0,0 @@ -import test, { expect } from '@playwright/test' -import { createId } from '@paralleldrive/cuid2' -import { - defaultSettings, - defaultTextInputOptions, - InputBlockType, - Metadata, -} from 'models' -import { createTypebots, updateTypebot } from 'utils/playwright/databaseActions' -import { parseDefaultGroupWithBlock } from 'utils/playwright/databaseHelpers' - -test('Result should be overwritten on page refresh', async ({ page }) => { - const typebotId = createId() - await createTypebots([ - { - id: typebotId, - ...parseDefaultGroupWithBlock({ - type: InputBlockType.TEXT, - options: defaultTextInputOptions, - }), - }, - ]) - - const [, response] = await Promise.all([ - page.goto(`/next/${typebotId}-public`), - page.waitForResponse(/sendMessage/), - ]) - const { resultId } = await response.json() - expect(resultId).toBeDefined() - await expect(page.getByRole('textbox')).toBeVisible() - - const [, secondResponse] = await Promise.all([ - page.reload(), - page.waitForResponse(/sendMessage/), - ]) - const { resultId: secondResultId } = await secondResponse.json() - expect(secondResultId).toBe(resultId) -}) - -test.describe('Create result on page refresh enabled', () => { - test('should work', async ({ page }) => { - const typebotId = createId() - await createTypebots([ - { - id: typebotId, - settings: { - ...defaultSettings, - general: { - ...defaultSettings.general, - isNewResultOnRefreshEnabled: true, - }, - }, - ...parseDefaultGroupWithBlock({ - type: InputBlockType.TEXT, - options: defaultTextInputOptions, - }), - }, - ]) - const [, response] = await Promise.all([ - page.goto(`/next/${typebotId}-public`), - page.waitForResponse(/sendMessage/), - ]) - const { resultId } = await response.json() - expect(resultId).toBeDefined() - - await expect(page.getByRole('textbox')).toBeVisible() - const [, secondResponse] = await Promise.all([ - page.reload(), - page.waitForResponse(/sendMessage/), - ]) - const { resultId: secondResultId } = await secondResponse.json() - expect(secondResultId).not.toBe(resultId) - }) -}) - -test('Hide query params', async ({ page }) => { - const typebotId = createId() - await createTypebots([ - { - id: typebotId, - ...parseDefaultGroupWithBlock({ - type: InputBlockType.TEXT, - options: defaultTextInputOptions, - }), - }, - ]) - await page.goto(`/next/${typebotId}-public?Name=John`) - await page.waitForTimeout(1000) - expect(page.url()).toEqual(`http://localhost:3001/next/${typebotId}-public`) - await updateTypebot({ - id: typebotId, - settings: { - ...defaultSettings, - general: { ...defaultSettings.general, isHideQueryParamsEnabled: false }, - }, - }) - await page.goto(`/next/${typebotId}-public?Name=John`) - await page.waitForTimeout(1000) - expect(page.url()).toEqual( - `http://localhost:3001/next/${typebotId}-public?Name=John` - ) -}) - -test('Show close message', async ({ page }) => { - const typebotId = createId() - await createTypebots([ - { - id: typebotId, - ...parseDefaultGroupWithBlock({ - type: InputBlockType.TEXT, - options: defaultTextInputOptions, - }), - isClosed: true, - }, - ]) - await page.goto(`/next/${typebotId}-public`) - await expect(page.locator('text=This bot is now closed')).toBeVisible() -}) - -test('Should correctly parse metadata', async ({ page }) => { - const typebotId = createId() - const customMetadata: Metadata = { - description: 'My custom description', - title: 'Custom title', - favIconUrl: 'https://www.baptistearno.com/favicon.png', - imageUrl: 'https://www.baptistearno.com/images/site-preview.png', - customHeadCode: '', - } - await createTypebots([ - { - id: typebotId, - settings: { - ...defaultSettings, - metadata: customMetadata, - }, - ...parseDefaultGroupWithBlock({ - type: InputBlockType.TEXT, - options: defaultTextInputOptions, - }), - }, - ]) - await page.goto(`/next/${typebotId}-public`) - expect( - await page.evaluate(`document.querySelector('title').textContent`) - ).toBe(customMetadata.title) - expect( - await page.evaluate( - () => - (document.querySelector('meta[name="description"]') as HTMLMetaElement) - .content - ) - ).toBe(customMetadata.description) - expect( - await page.evaluate( - () => - (document.querySelector('meta[property="og:image"]') as HTMLMetaElement) - .content - ) - ).toBe(customMetadata.imageUrl) - expect( - await page.evaluate(() => - ( - document.querySelector('link[rel="icon"]') as HTMLLinkElement - ).getAttribute('href') - ) - ).toBe(customMetadata.favIconUrl) - await expect( - page.locator( - `input[placeholder="${defaultTextInputOptions.labels.placeholder}"]` - ) - ).toBeVisible() - expect( - await page.evaluate( - () => - (document.querySelector('meta[name="author"]') as HTMLMetaElement) - .content - ) - ).toBe('John Doe') -}) diff --git a/apps/viewer/src/features/usage/usage.spec.ts b/apps/viewer/src/features/usage/usage.spec.ts index f01e2a548f..7993d28f68 100644 --- a/apps/viewer/src/features/usage/usage.spec.ts +++ b/apps/viewer/src/features/usage/usage.spec.ts @@ -8,7 +8,6 @@ import { importTypebotInDatabase, injectFakeResults, } from 'utils/playwright/databaseActions' -import { typebotViewer } from 'utils/playwright/testHelpers' test('should not start if chat limit is reached', async ({ page, context }) => { await test.step('Free plan', async () => { @@ -38,9 +37,7 @@ test('should not start if chat limit is reached', async ({ page, context }) => { }) await injectFakeResults({ typebotId, count: 3000 }) await page.goto(`/${typebotId}-public`) - await expect( - typebotViewer(page).locator('text="Hey there, upload please"') - ).toBeVisible() + await expect(page.locator('text="Hey there, upload please"')).toBeVisible() }) await test.step('Custom plan', async () => { @@ -63,9 +60,7 @@ test('should not start if chat limit is reached', async ({ page, context }) => { }) const page = await context.newPage() await page.goto(`/${typebotId}-public`) - await expect( - typebotViewer(page).locator('text="Hey there, upload please"') - ).toBeVisible() + await expect(page.locator('text="Hey there, upload please"')).toBeVisible() await injectFakeResults({ typebotId, count: 2000 }) await page.goto(`/${typebotId}-public`) await expect(page.locator('text="This bot is now closed."')).toBeVisible() diff --git a/apps/viewer/src/features/usage/usageV2.spec.ts b/apps/viewer/src/features/usage/usageV2.spec.ts deleted file mode 100644 index c8e39af231..0000000000 --- a/apps/viewer/src/features/usage/usageV2.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { getTestAsset } from '@/test/utils/playwright' -import test, { expect } from '@playwright/test' -import { createId } from '@paralleldrive/cuid2' -import { Plan } from 'db' -import { defaultSettings } from 'models' -import { - createWorkspaces, - importTypebotInDatabase, - injectFakeResults, -} from 'utils/playwright/databaseActions' - -test('should not start if chat limit is reached', async ({ page, context }) => { - await test.step('Free plan', async () => { - const workspaceId = createId() - const typebotId = createId() - await createWorkspaces([{ id: workspaceId, plan: Plan.FREE }]) - await importTypebotInDatabase(getTestAsset('typebots/fileUpload.json'), { - id: typebotId, - publicId: `${typebotId}-public`, - workspaceId, - }) - await injectFakeResults({ typebotId, count: 400 }) - await page.goto(`/next/${typebotId}-public`) - await expect(page.locator('text="This bot is now closed."')).toBeVisible() - await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) - await expect(page.locator('text="133%"')).toBeVisible() - }) - - await test.step('Lifetime plan', async () => { - const workspaceId = createId() - const typebotId = createId() - await createWorkspaces([{ id: workspaceId, plan: Plan.LIFETIME }]) - await importTypebotInDatabase(getTestAsset('typebots/fileUpload.json'), { - id: typebotId, - publicId: `${typebotId}-public`, - workspaceId, - }) - await injectFakeResults({ typebotId, count: 3000 }) - await page.goto(`/next/${typebotId}-public`) - await expect(page.locator('text="Hey there, upload please"')).toBeVisible() - }) - - await test.step('Custom plan', async () => { - const workspaceId = createId() - const typebotId = createId() - await createWorkspaces([ - { id: workspaceId, plan: Plan.CUSTOM, customChatsLimit: 1000 }, - ]) - await importTypebotInDatabase(getTestAsset('typebots/fileUpload.json'), { - id: typebotId, - publicId: `${typebotId}-public`, - workspaceId, - settings: { - ...defaultSettings, - general: { - ...defaultSettings.general, - isNewResultOnRefreshEnabled: true, - }, - }, - }) - const page = await context.newPage() - await page.goto(`/next/${typebotId}-public`) - await expect(page.locator('text="Hey there, upload please"')).toBeVisible() - await injectFakeResults({ typebotId, count: 2000 }) - await page.goto(`/next/${typebotId}-public`) - await expect(page.locator('text="This bot is now closed."')).toBeVisible() - await page.goto(`${process.env.NEXTAUTH_URL}/typebots/${typebotId}/results`) - await expect(page.locator('text="200%"')).toBeVisible() - }) -}) diff --git a/apps/viewer/src/features/variables/variables.spec.ts b/apps/viewer/src/features/variables/variables.spec.ts index 9372df3576..d91de16285 100644 --- a/apps/viewer/src/features/variables/variables.spec.ts +++ b/apps/viewer/src/features/variables/variables.spec.ts @@ -2,7 +2,6 @@ import { getTestAsset } from '@/test/utils/playwright' import test, { expect } from '@playwright/test' import { createId } from '@paralleldrive/cuid2' import { importTypebotInDatabase } from 'utils/playwright/databaseActions' -import { typebotViewer } from 'utils/playwright/testHelpers' test('should correctly be injected', async ({ page }) => { const typebotId = createId() @@ -11,12 +10,10 @@ test('should correctly be injected', async ({ page }) => { { id: typebotId, publicId: `${typebotId}-public` } ) await page.goto(`/${typebotId}-public`) - await expect(typebotViewer(page).locator('text="Your name is"')).toBeVisible() + await expect(page.locator('text="Your name is"')).toBeVisible() await page.goto(`/${typebotId}-public?Name=Baptiste&Email=email@test.com`) - await expect( - typebotViewer(page).locator('text="Your name is Baptiste"') - ).toBeVisible() - await expect( - typebotViewer(page).locator('input[value="email@test.com"]') - ).toBeVisible() + await expect(page.locator('text="Your name is Baptiste"')).toBeVisible() + await expect(page.getByPlaceholder('Type your email...')).toHaveValue( + 'email@test.com' + ) }) diff --git a/apps/viewer/src/features/variables/variablesV2.spec.ts b/apps/viewer/src/features/variables/variablesV2.spec.ts deleted file mode 100644 index b0a4fe2359..0000000000 --- a/apps/viewer/src/features/variables/variablesV2.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { getTestAsset } from '@/test/utils/playwright' -import test, { expect } from '@playwright/test' -import { createId } from '@paralleldrive/cuid2' -import { importTypebotInDatabase } from 'utils/playwright/databaseActions' - -test('should correctly be injected', async ({ page }) => { - const typebotId = createId() - await importTypebotInDatabase( - getTestAsset('typebots/predefinedVariables.json'), - { id: typebotId, publicId: `${typebotId}-public` } - ) - await page.goto(`/next/${typebotId}-public`) - await expect(page.locator('text="Your name is"')).toBeVisible() - await page.goto( - `/next/${typebotId}-public?Name=Baptiste&Email=email@test.com` - ) - await expect(page.locator('text="Your name is Baptiste"')).toBeVisible() - await expect(page.getByPlaceholder('Type your email...')).toHaveValue( - 'email@test.com' - ) -}) diff --git a/apps/viewer/src/pages/[[...publicId]].tsx b/apps/viewer/src/pages/[[...publicId]].tsx index 57b1d1f385..0390df7511 100644 --- a/apps/viewer/src/pages/[[...publicId]].tsx +++ b/apps/viewer/src/pages/[[...publicId]].tsx @@ -3,8 +3,9 @@ import { ErrorPage } from '@/components/ErrorPage' import { NotFoundPage } from '@/components/NotFoundPage' import { GetServerSideProps, GetServerSidePropsContext } from 'next' import { env, getViewerUrl, isDefined, isNotDefined, omit } from 'utils' -import { TypebotPage, TypebotPageProps } from '../components/TypebotPage' import prisma from '../lib/prisma' +import { TypebotPageProps, TypebotPageV2 } from '@/components/TypebotPageV2' +import { TypebotPageV3 } from '@/components/TypebotPageV3' export const getServerSideProps: GetServerSideProps = async ( context: GetServerSidePropsContext @@ -76,7 +77,14 @@ const getTypebotFromCustomDomain = async ( const publishedTypebot = await prisma.publicTypebot.findFirst({ where: { typebot: { customDomain } }, include: { - typebot: { select: { name: true, isClosed: true, isArchived: true } }, + typebot: { + select: { + name: true, + isClosed: true, + isArchived: true, + publicId: true, + }, + }, }, }) if (isNotDefined(publishedTypebot)) return null @@ -99,7 +107,19 @@ const App = ({ publishedTypebot, ...props }: TypebotPageProps) => { return if (publishedTypebot.typebot.isClosed) return - return + return publishedTypebot.version === '3' ? ( + + ) : ( + + ) } export default App diff --git a/apps/viewer/src/pages/next/[[...publicId]].tsx b/apps/viewer/src/pages/next/[[...publicId]].tsx deleted file mode 100644 index 2aa0c53d5d..0000000000 --- a/apps/viewer/src/pages/next/[[...publicId]].tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { IncomingMessage } from 'http' -import { GetServerSideProps, GetServerSidePropsContext } from 'next' -import { env, getViewerUrl, isNotDefined } from 'utils' -import prisma from '@/lib/prisma' -import { TypebotPage, TypebotPageProps } from '@/components/TypebotPageV2' - -export const getServerSideProps: GetServerSideProps = async ( - context: GetServerSidePropsContext -) => { - const { host, forwardedHost } = getHost(context.req) - const pathname = context.resolvedUrl.split('?')[0] - try { - if (!host) return { props: {} } - const viewerUrls = (getViewerUrl({ returnAll: true }) ?? '').split(',') - const isMatchingViewerUrl = - env('E2E_TEST') === 'true' - ? true - : viewerUrls.some( - (url) => - host.split(':')[0].includes(url.split('//')[1].split(':')[0]) || - (forwardedHost && - forwardedHost - .split(':')[0] - .includes(url.split('//')[1].split(':')[0])) - ) - const typebot = isMatchingViewerUrl - ? await getTypebotFromPublicId(context.query.publicId?.toString()) - : null - return { - props: { - typebot, - url: `https://${forwardedHost ?? host}${pathname}`, - }, - } - } catch (err) { - console.error(err) - } - return { - props: {}, - url: `https://${forwardedHost ?? host}${pathname}`, - } -} - -const getTypebotFromPublicId = async ( - publicId?: string -): Promise => { - const typebot = (await prisma.typebot.findUnique({ - where: { publicId: publicId ?? '' }, - select: { - theme: true, - name: true, - settings: true, - publicId: true, - }, - })) as TypebotPageProps['typebot'] | null - if (isNotDefined(typebot)) return null - return typebot -} - -const getHost = ( - req?: IncomingMessage -): { host?: string; forwardedHost?: string } => ({ - host: req?.headers ? req.headers.host : window.location.host, - forwardedHost: req?.headers['x-forwarded-host'] as string | undefined, -}) - -const App = ({ typebot, url }: TypebotPageProps) => ( - -) - -export default App diff --git a/packages/db/mysql/schema.prisma b/packages/db/mysql/schema.prisma index 45d94f355a..da1fbaccff 100644 --- a/packages/db/mysql/schema.prisma +++ b/packages/db/mysql/schema.prisma @@ -170,6 +170,7 @@ model DashboardFolder { model Typebot { id String @id @default(cuid()) + version String? @db.VarChar(10) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt icon String? @db.VarChar(1000) @@ -226,6 +227,7 @@ model CollaboratorsOnTypebots { model PublicTypebot { id String @id @default(cuid()) + version String? @db.VarChar(10) createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt typebotId String @unique diff --git a/packages/db/package.json b/packages/db/package.json index 43e1cf95dc..aee6021707 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -9,7 +9,7 @@ "db:generate": "tsx scripts/db-generate.ts", "db:push": "tsx scripts/db-push.ts", "migrate:deploy": "tsx scripts/migrate-deploy.ts", - "migrate:dev": "tsx scripts/migrate-dev.ts", + "migrate:dev": "prisma migrate dev --create-only --schema postgresql/schema.prisma", "db:migrate": "pnpm migrate:deploy" }, "dependencies": { diff --git a/packages/db/postgresql/migrations/20230220085522_add_version_fields/migration.sql b/packages/db/postgresql/migrations/20230220085522_add_version_fields/migration.sql new file mode 100644 index 0000000000..7e8d5515cb --- /dev/null +++ b/packages/db/postgresql/migrations/20230220085522_add_version_fields/migration.sql @@ -0,0 +1,11 @@ +-- AlterTable +ALTER TABLE + "PublicTypebot" +ADD + COLUMN "version" TEXT; + +-- AlterTable +ALTER TABLE + "Typebot" +ADD + COLUMN "version" TEXT; \ No newline at end of file diff --git a/packages/db/postgresql/schema.prisma b/packages/db/postgresql/schema.prisma index 9dd5a1c79b..54ce631349 100644 --- a/packages/db/postgresql/schema.prisma +++ b/packages/db/postgresql/schema.prisma @@ -154,6 +154,7 @@ model DashboardFolder { model Typebot { id String @id @default(cuid()) + version String? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt icon String? @@ -207,6 +208,7 @@ model CollaboratorsOnTypebots { model PublicTypebot { id String @id @default(cuid()) + version String? createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt typebotId String @unique diff --git a/packages/models/features/publicTypebot.ts b/packages/models/features/publicTypebot.ts index 48d1fdda0e..e7da986768 100644 --- a/packages/models/features/publicTypebot.ts +++ b/packages/models/features/publicTypebot.ts @@ -13,6 +13,7 @@ import { schemaForType } from './utils' export const publicTypebotSchema = schemaForType()( z.object({ id: z.string(), + version: z.enum(['3']).nullable(), createdAt: z.date(), updatedAt: z.date(), typebotId: z.string(), diff --git a/packages/models/features/typebot/typebot.ts b/packages/models/features/typebot/typebot.ts index 0cb3a64a85..bcb2c945b2 100644 --- a/packages/models/features/typebot/typebot.ts +++ b/packages/models/features/typebot/typebot.ts @@ -41,7 +41,7 @@ const resultsTablePreferencesSchema = z.object({ export const typebotSchema = schemaForType()( z.object({ - version: z.enum(['2']).optional(), + version: z.enum(['3']).nullable(), id: z.string(), name: z.string(), groups: z.array(groupSchema), diff --git a/packages/utils/playwright/databaseActions.ts b/packages/utils/playwright/databaseActions.ts index 1c02b08bd2..297175d590 100644 --- a/packages/utils/playwright/databaseActions.ts +++ b/packages/utils/playwright/databaseActions.ts @@ -78,6 +78,7 @@ export const importTypebotInDatabase = async ( ...JSON.parse(readFileSync(path).toString()), workspaceId: proWorkspaceId, ...updates, + version: '3', } await prisma.typebot.create({ data: parseCreateTypebot(typebot), diff --git a/packages/utils/playwright/databaseHelpers.ts b/packages/utils/playwright/databaseHelpers.ts index 7f6e4c79a4..966c1b805b 100644 --- a/packages/utils/playwright/databaseHelpers.ts +++ b/packages/utils/playwright/databaseHelpers.ts @@ -16,6 +16,7 @@ export const parseTestTypebot = ( partialTypebot: Partial ): Typebot => ({ id: createId(), + version: '3', workspaceId: proWorkspaceId, folderId: null, name: 'My typebot', @@ -62,6 +63,7 @@ export const parseTypebotToPublicTypebot = ( typebot: Typebot ): Omit => ({ id, + version: typebot.version, groups: typebot.groups, typebotId: typebot.id, theme: typebot.theme, diff --git a/packages/utils/typebotConversions.ts b/packages/utils/typebotConversions.ts deleted file mode 100644 index 5fe4a445a1..0000000000 --- a/packages/utils/typebotConversions.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Typebot } from 'models' - -export const convertTypebotToV2 = (typebot: any): Typebot => { - const newTypebot = JSON.parse( - JSON.stringify(typebot) - .replace(/\"blocks\":/g, '"groups":') - .replace(/\"steps\":/g, '"blocks":') - .replace(/\"blockId\":/g, '"groupId":') - .replace(/\"blockId\":/g, '"blockId":') - ) - return { - version: '2', - ...newTypebot, - } -} diff --git a/packages/wordpress/trunk/README.txt b/packages/wordpress/trunk/README.txt index d48a9e43f9..aef81f8c77 100644 --- a/packages/wordpress/trunk/README.txt +++ b/packages/wordpress/trunk/README.txt @@ -5,7 +5,7 @@ Requires at least: 5.0 Tested up to: 6.0 License: GPL 2.0 License URI: http://www.gnu.org/licenses/gpl-2.0.txt -Stable Tag: 2.1.11 +Stable Tag: 3.0.0 Build beautiful conversational forms @@ -26,6 +26,9 @@ This plugin relies on Typebot which is a tool that allows you to create conversa 3. Activate your Typebot with the "Typebot" admin button located in the sidebar == Changelog == += 3.0.0 = +* Complete rework of the plugin. You are now required to generate a code snippet on https://app.typebot.io + = 2.1.9 = * Fix standard embed when window is already loaded diff --git a/packages/wordpress/trunk/admin/class-typebot-admin.php b/packages/wordpress/trunk/admin/class-typebot-admin.php index 4c20896723..c5edb10f8b 100644 --- a/packages/wordpress/trunk/admin/class-typebot-admin.php +++ b/packages/wordpress/trunk/admin/class-typebot-admin.php @@ -5,26 +5,6 @@ class Typebot_Admin { - private $version; - - public function __construct($version) - { - $this->version = $version; - } - - public function enqueue_styles($hook) - { - if ($hook === 'toplevel_page_typebot/settings') { - wp_enqueue_style( - 'bulma', - plugin_dir_url(__FILE__) . 'css/bulma.min.css', - [], - $this->version, - 'all' - ); - } - } - public function my_admin_menu() { add_menu_page( @@ -45,47 +25,6 @@ public function typebot_settings_callback() public function register_typebot_settings() { - register_setting('typebot', 'url', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'embed_type', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'popup_delay', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'bubble_delay', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'chat_delay', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'avatar', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'text_content', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'button_color', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'chat_included_pages', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'popup_included_pages', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'chat_icon', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'custom_code', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'config_type', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); - register_setting('typebot', 'dont_show_callout_twice', [ - 'sanitize_callback' => 'sanitize_text_field', - ]); + register_setting('typebot', 'init_snippet'); } } diff --git a/packages/wordpress/trunk/admin/css/bulma.min.css b/packages/wordpress/trunk/admin/css/bulma.min.css deleted file mode 100644 index fc33352a7a..0000000000 --- a/packages/wordpress/trunk/admin/css/bulma.min.css +++ /dev/null @@ -1 +0,0 @@ -/*! bulma.io v0.9.1 | MIT License | github.com/jgthms/bulma */@-webkit-keyframes spinAround{from{transform:rotate(0)}to{transform:rotate(359deg)}}@keyframes spinAround{from{transform:rotate(0)}to{transform:rotate(359deg)}}.breadcrumb,.button,.file,.is-unselectable,.modal-close,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.tabs{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.navbar-link:not(.is-arrowless)::after,.select:not(.is-multiple):not(.is-loading)::after{border:3px solid transparent;border-radius:2px;border-right:0;border-top:0;content:" ";display:block;height:.625em;margin-top:-.4375em;pointer-events:none;position:absolute;top:50%;transform:rotate(-45deg);transform-origin:center;width:.625em}.block:not(:last-child),.box:not(:last-child),.breadcrumb:not(:last-child),.content:not(:last-child),.highlight:not(:last-child),.level:not(:last-child),.message:not(:last-child),.notification:not(:last-child),.pagination:not(:last-child),.progress:not(:last-child),.subtitle:not(:last-child),.table-container:not(:last-child),.table:not(:last-child),.tabs:not(:last-child),.title:not(:last-child){margin-bottom:1.5rem}.modal-close{-moz-appearance:none;-webkit-appearance:none;background-color:rgba(10,10,10,.2);border:none;border-radius:290486px;cursor:pointer;pointer-events:auto;display:inline-block;flex-grow:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:0;position:relative;vertical-align:top;width:20px}.modal-close::after,.modal-close::before{background-color:#fff;content:"";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.modal-close::before{height:2px;width:50%}.modal-close::after{height:50%;width:2px}.modal-close:focus,.modal-close:hover{background-color:rgba(10,10,10,.3)}.modal-close:active{background-color:rgba(10,10,10,.4)}.is-small.modal-close{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.is-medium.modal-close{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.is-large.modal-close{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.button.is-loading::after,.control.is-loading::after,.loader,.select.is-loading::after{-webkit-animation:spinAround .5s infinite linear;animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em}.hero-video,.image.is-16by9 .has-ratio,.image.is-16by9 img,.image.is-1by1 .has-ratio,.image.is-1by1 img,.image.is-1by2 .has-ratio,.image.is-1by2 img,.image.is-1by3 .has-ratio,.image.is-1by3 img,.image.is-2by1 .has-ratio,.image.is-2by1 img,.image.is-2by3 .has-ratio,.image.is-2by3 img,.image.is-3by1 .has-ratio,.image.is-3by1 img,.image.is-3by2 .has-ratio,.image.is-3by2 img,.image.is-3by4 .has-ratio,.image.is-3by4 img,.image.is-3by5 .has-ratio,.image.is-3by5 img,.image.is-4by3 .has-ratio,.image.is-4by3 img,.image.is-4by5 .has-ratio,.image.is-4by5 img,.image.is-5by3 .has-ratio,.image.is-5by3 img,.image.is-5by4 .has-ratio,.image.is-5by4 img,.image.is-9by16 .has-ratio,.image.is-9by16 img,.image.is-square .has-ratio,.image.is-square img,.is-overlay,.modal,.modal-background{bottom:0;left:0;position:absolute;right:0;top:0}.button,.file-cta,.file-name,.input,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.select select,.textarea{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:4px;box-shadow:none;display:inline-flex;font-size:1rem;height:2.5em;justify-content:flex-start;line-height:1.5;padding-bottom:calc(.5em - 1px);padding-left:calc(.75em - 1px);padding-right:calc(.75em - 1px);padding-top:calc(.5em - 1px);position:relative;vertical-align:top}.button:active,.button:focus,.file-cta:active,.file-cta:focus,.file-name:active,.file-name:focus,.input:active,.input:focus,.is-active.button,.is-active.file-cta,.is-active.file-name,.is-active.input,.is-active.pagination-ellipsis,.is-active.pagination-link,.is-active.pagination-next,.is-active.pagination-previous,.is-active.textarea,.is-focused.button,.is-focused.file-cta,.is-focused.file-name,.is-focused.input,.is-focused.pagination-ellipsis,.is-focused.pagination-link,.is-focused.pagination-next,.is-focused.pagination-previous,.is-focused.textarea,.pagination-ellipsis:active,.pagination-ellipsis:focus,.pagination-link:active,.pagination-link:focus,.pagination-next:active,.pagination-next:focus,.pagination-previous:active,.pagination-previous:focus,.select select.is-active,.select select.is-focused,.select select:active,.select select:focus,.textarea:active,.textarea:focus{outline:0}.button[disabled],.file-cta[disabled],.file-name[disabled],.input[disabled],.pagination-ellipsis[disabled],.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled],.select fieldset[disabled] select,.select select[disabled],.textarea[disabled],fieldset[disabled] .button,fieldset[disabled] .file-cta,fieldset[disabled] .file-name,fieldset[disabled] .input,fieldset[disabled] .pagination-ellipsis,fieldset[disabled] .pagination-link,fieldset[disabled] .pagination-next,fieldset[disabled] .pagination-previous,fieldset[disabled] .select select,fieldset[disabled] .textarea{cursor:not-allowed}/*! minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */blockquote,body,dd,dl,dt,fieldset,figure,h1,h2,h3,h4,h5,h6,hr,html,iframe,legend,li,ol,p,pre,textarea,ul{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,::after,::before{box-sizing:inherit}img,video{height:auto;max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}td:not([align]),th:not([align]){text-align:inherit}html{background-color:#fff;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:hidden;overflow-y:scroll;text-rendering:optimizeLegibility;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}article,aside,figure,footer,header,hgroup,section{display:block}body,button,input,optgroup,select,textarea{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:monospace}body{color:#4a4a4a;font-size:1em;font-weight:400;line-height:1.5}a{color:#3273dc;cursor:pointer;text-decoration:none}a strong{color:currentColor}a:hover{color:#363636}code{background-color:#f5f5f5;color:#da1039;font-size:.875em;font-weight:400;padding:.25em .5em .25em}hr{background-color:#f5f5f5;border:none;display:block;height:2px;margin:1.5rem 0}img{height:auto;max-width:100%}input[type=checkbox],input[type=radio]{vertical-align:baseline}small{font-size:.875em}span{font-style:inherit;font-weight:inherit}strong{color:#363636;font-weight:700}fieldset{border:none}pre{-webkit-overflow-scrolling:touch;background-color:#f5f5f5;color:#4a4a4a;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}table td,table th{vertical-align:top}table td:not([align]),table th:not([align]){text-align:inherit}table th{color:#363636}.box{background-color:#fff;border-radius:6px;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);color:#4a4a4a;display:block;padding:1.25rem}a.box:focus,a.box:hover{box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px #3273dc}a.box:active{box-shadow:inset 0 1px 2px rgba(10,10,10,.2),0 0 0 1px #3273dc}.button{background-color:#fff;border-color:#dbdbdb;border-width:1px;color:#363636;cursor:pointer;justify-content:center;padding-bottom:calc(.5em - 1px);padding-left:1em;padding-right:1em;padding-top:calc(.5em - 1px);text-align:center;white-space:nowrap}.button strong{color:inherit}.button .icon,.button .icon.is-large,.button .icon.is-medium,.button .icon.is-small{height:1.5em;width:1.5em}.button .icon:first-child:not(:last-child){margin-left:calc(-.5em - 1px);margin-right:.25em}.button .icon:last-child:not(:first-child){margin-left:.25em;margin-right:calc(-.5em - 1px)}.button .icon:first-child:last-child{margin-left:calc(-.5em - 1px);margin-right:calc(-.5em - 1px)}.button.is-hovered,.button:hover{border-color:#b5b5b5;color:#363636}.button.is-focused,.button:focus{border-color:#3273dc;color:#363636}.button.is-focused:not(:active),.button:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-active,.button:active{border-color:#4a4a4a;color:#363636}.button.is-text{background-color:transparent;border-color:transparent;color:#4a4a4a;text-decoration:underline}.button.is-text.is-focused,.button.is-text.is-hovered,.button.is-text:focus,.button.is-text:hover{background-color:#f5f5f5;color:#363636}.button.is-text.is-active,.button.is-text:active{background-color:#e8e8e8;color:#363636}.button.is-text[disabled],fieldset[disabled] .button.is-text{background-color:transparent;border-color:transparent;box-shadow:none}.button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}.button.is-white.is-hovered,.button.is-white:hover{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.button.is-white.is-focused,.button.is-white:focus{border-color:transparent;color:#0a0a0a}.button.is-white.is-focused:not(:active),.button.is-white:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.button.is-white.is-active,.button.is-white:active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.button.is-white[disabled],fieldset[disabled] .button.is-white{background-color:#fff;border-color:transparent;box-shadow:none}.button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-hovered,.button.is-white.is-inverted:hover{background-color:#000}.button.is-white.is-inverted[disabled],fieldset[disabled] .button.is-white.is-inverted{background-color:#0a0a0a;border-color:transparent;box-shadow:none;color:#fff}.button.is-white.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-white.is-outlined.is-focused,.button.is-white.is-outlined.is-hovered,.button.is-white.is-outlined:focus,.button.is-white.is-outlined:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.button.is-white.is-outlined.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-outlined.is-loading.is-focused::after,.button.is-white.is-outlined.is-loading.is-hovered::after,.button.is-white.is-outlined.is-loading:focus::after,.button.is-white.is-outlined.is-loading:hover::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined[disabled],fieldset[disabled] .button.is-white.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-white.is-inverted.is-outlined.is-focused,.button.is-white.is-inverted.is-outlined.is-hovered,.button.is-white.is-inverted.is-outlined:focus,.button.is-white.is-inverted.is-outlined:hover{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-white.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-white.is-inverted.is-outlined.is-loading:focus::after,.button.is-white.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}.button.is-black.is-hovered,.button.is-black:hover{background-color:#040404;border-color:transparent;color:#fff}.button.is-black.is-focused,.button.is-black:focus{border-color:transparent;color:#fff}.button.is-black.is-focused:not(:active),.button.is-black:focus:not(:active){box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.button.is-black.is-active,.button.is-black:active{background-color:#000;border-color:transparent;color:#fff}.button.is-black[disabled],fieldset[disabled] .button.is-black{background-color:#0a0a0a;border-color:transparent;box-shadow:none}.button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-hovered,.button.is-black.is-inverted:hover{background-color:#f2f2f2}.button.is-black.is-inverted[disabled],fieldset[disabled] .button.is-black.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#0a0a0a}.button.is-black.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-black.is-outlined.is-focused,.button.is-black.is-outlined.is-hovered,.button.is-black.is-outlined:focus,.button.is-black.is-outlined:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.button.is-black.is-outlined.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-outlined.is-loading.is-focused::after,.button.is-black.is-outlined.is-loading.is-hovered::after,.button.is-black.is-outlined.is-loading:focus::after,.button.is-black.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined[disabled],fieldset[disabled] .button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-black.is-inverted.is-outlined.is-focused,.button.is-black.is-inverted.is-outlined.is-hovered,.button.is-black.is-inverted.is-outlined:focus,.button.is-black.is-inverted.is-outlined:hover{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-black.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-black.is-inverted.is-outlined.is-loading:focus::after,.button.is-black.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-light{background-color:#f5f5f5;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light.is-hovered,.button.is-light:hover{background-color:#eee;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light.is-focused,.button.is-light:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light.is-focused:not(:active),.button.is-light:focus:not(:active){box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.button.is-light.is-active,.button.is-light:active{background-color:#e8e8e8;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-light[disabled],fieldset[disabled] .button.is-light{background-color:#f5f5f5;border-color:transparent;box-shadow:none}.button.is-light.is-inverted{background-color:rgba(0,0,0,.7);color:#f5f5f5}.button.is-light.is-inverted.is-hovered,.button.is-light.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-light.is-inverted[disabled],fieldset[disabled] .button.is-light.is-inverted{background-color:rgba(0,0,0,.7);border-color:transparent;box-shadow:none;color:#f5f5f5}.button.is-light.is-loading::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-light.is-outlined.is-focused,.button.is-light.is-outlined.is-hovered,.button.is-light.is-outlined:focus,.button.is-light.is-outlined:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:rgba(0,0,0,.7)}.button.is-light.is-outlined.is-loading::after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-outlined.is-loading.is-focused::after,.button.is-light.is-outlined.is-loading.is-hovered::after,.button.is-light.is-outlined.is-loading:focus::after,.button.is-light.is-outlined.is-loading:hover::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-light.is-outlined[disabled],fieldset[disabled] .button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-light.is-inverted.is-outlined.is-focused,.button.is-light.is-inverted.is-outlined.is-hovered,.button.is-light.is-inverted.is-outlined:focus,.button.is-light.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#f5f5f5}.button.is-light.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-light.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-light.is-inverted.is-outlined.is-loading:focus::after,.button.is-light.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);box-shadow:none;color:rgba(0,0,0,.7)}.button.is-dark{background-color:#363636;border-color:transparent;color:#fff}.button.is-dark.is-hovered,.button.is-dark:hover{background-color:#2f2f2f;border-color:transparent;color:#fff}.button.is-dark.is-focused,.button.is-dark:focus{border-color:transparent;color:#fff}.button.is-dark.is-focused:not(:active),.button.is-dark:focus:not(:active){box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.button.is-dark.is-active,.button.is-dark:active{background-color:#292929;border-color:transparent;color:#fff}.button.is-dark[disabled],fieldset[disabled] .button.is-dark{background-color:#363636;border-color:transparent;box-shadow:none}.button.is-dark.is-inverted{background-color:#fff;color:#363636}.button.is-dark.is-inverted.is-hovered,.button.is-dark.is-inverted:hover{background-color:#f2f2f2}.button.is-dark.is-inverted[disabled],fieldset[disabled] .button.is-dark.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#363636}.button.is-dark.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-dark.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-dark.is-outlined.is-focused,.button.is-dark.is-outlined.is-hovered,.button.is-dark.is-outlined:focus,.button.is-dark.is-outlined:hover{background-color:#363636;border-color:#363636;color:#fff}.button.is-dark.is-outlined.is-loading::after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-outlined.is-loading.is-focused::after,.button.is-dark.is-outlined.is-loading.is-hovered::after,.button.is-dark.is-outlined.is-loading:focus::after,.button.is-dark.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-dark.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-outlined{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-dark.is-inverted.is-outlined.is-focused,.button.is-dark.is-inverted.is-outlined.is-hovered,.button.is-dark.is-inverted.is-outlined:focus,.button.is-dark.is-inverted.is-outlined:hover{background-color:#fff;color:#363636}.button.is-dark.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-dark.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-dark.is-inverted.is-outlined.is-loading:focus::after,.button.is-dark.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-primary{background-color:#00d1b2;border-color:transparent;color:#fff}.button.is-primary.is-hovered,.button.is-primary:hover{background-color:#00c4a7;border-color:transparent;color:#fff}.button.is-primary.is-focused,.button.is-primary:focus{border-color:transparent;color:#fff}.button.is-primary.is-focused:not(:active),.button.is-primary:focus:not(:active){box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.button.is-primary.is-active,.button.is-primary:active{background-color:#00b89c;border-color:transparent;color:#fff}.button.is-primary[disabled],fieldset[disabled] .button.is-primary{background-color:#00d1b2;border-color:transparent;box-shadow:none}.button.is-primary.is-inverted{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-hovered,.button.is-primary.is-inverted:hover{background-color:#f2f2f2}.button.is-primary.is-inverted[disabled],fieldset[disabled] .button.is-primary.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#00d1b2}.button.is-primary.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;color:#00d1b2}.button.is-primary.is-outlined.is-focused,.button.is-primary.is-outlined.is-hovered,.button.is-primary.is-outlined:focus,.button.is-primary.is-outlined:hover{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.button.is-primary.is-outlined.is-loading::after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-outlined.is-loading.is-focused::after,.button.is-primary.is-outlined.is-loading.is-hovered::after,.button.is-primary.is-outlined.is-loading:focus::after,.button.is-primary.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined[disabled],fieldset[disabled] .button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;box-shadow:none;color:#00d1b2}.button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined.is-focused,.button.is-primary.is-inverted.is-outlined.is-hovered,.button.is-primary.is-inverted.is-outlined:focus,.button.is-primary.is-inverted.is-outlined:hover{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-primary.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-primary.is-inverted.is-outlined.is-loading:focus::after,.button.is-primary.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-primary.is-light{background-color:#ebfffc;color:#00947e}.button.is-primary.is-light.is-hovered,.button.is-primary.is-light:hover{background-color:#defffa;border-color:transparent;color:#00947e}.button.is-primary.is-light.is-active,.button.is-primary.is-light:active{background-color:#d1fff8;border-color:transparent;color:#00947e}.button.is-link{background-color:#3273dc;border-color:transparent;color:#fff}.button.is-link.is-hovered,.button.is-link:hover{background-color:#276cda;border-color:transparent;color:#fff}.button.is-link.is-focused,.button.is-link:focus{border-color:transparent;color:#fff}.button.is-link.is-focused:not(:active),.button.is-link:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-link.is-active,.button.is-link:active{background-color:#2366d1;border-color:transparent;color:#fff}.button.is-link[disabled],fieldset[disabled] .button.is-link{background-color:#3273dc;border-color:transparent;box-shadow:none}.button.is-link.is-inverted{background-color:#fff;color:#3273dc}.button.is-link.is-inverted.is-hovered,.button.is-link.is-inverted:hover{background-color:#f2f2f2}.button.is-link.is-inverted[disabled],fieldset[disabled] .button.is-link.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#3273dc}.button.is-link.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined{background-color:transparent;border-color:#3273dc;color:#3273dc}.button.is-link.is-outlined.is-focused,.button.is-link.is-outlined.is-hovered,.button.is-link.is-outlined:focus,.button.is-link.is-outlined:hover{background-color:#3273dc;border-color:#3273dc;color:#fff}.button.is-link.is-outlined.is-loading::after{border-color:transparent transparent #3273dc #3273dc!important}.button.is-link.is-outlined.is-loading.is-focused::after,.button.is-link.is-outlined.is-loading.is-hovered::after,.button.is-link.is-outlined.is-loading:focus::after,.button.is-link.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined[disabled],fieldset[disabled] .button.is-link.is-outlined{background-color:transparent;border-color:#3273dc;box-shadow:none;color:#3273dc}.button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-link.is-inverted.is-outlined.is-focused,.button.is-link.is-inverted.is-outlined.is-hovered,.button.is-link.is-inverted.is-outlined:focus,.button.is-link.is-inverted.is-outlined:hover{background-color:#fff;color:#3273dc}.button.is-link.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-link.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-link.is-inverted.is-outlined.is-loading:focus::after,.button.is-link.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #3273dc #3273dc!important}.button.is-link.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-link.is-light{background-color:#eef3fc;color:#2160c4}.button.is-link.is-light.is-hovered,.button.is-link.is-light:hover{background-color:#e3ecfa;border-color:transparent;color:#2160c4}.button.is-link.is-light.is-active,.button.is-link.is-light:active{background-color:#d8e4f8;border-color:transparent;color:#2160c4}.button.is-info{background-color:#3298dc;border-color:transparent;color:#fff}.button.is-info.is-hovered,.button.is-info:hover{background-color:#2793da;border-color:transparent;color:#fff}.button.is-info.is-focused,.button.is-info:focus{border-color:transparent;color:#fff}.button.is-info.is-focused:not(:active),.button.is-info:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,152,220,.25)}.button.is-info.is-active,.button.is-info:active{background-color:#238cd1;border-color:transparent;color:#fff}.button.is-info[disabled],fieldset[disabled] .button.is-info{background-color:#3298dc;border-color:transparent;box-shadow:none}.button.is-info.is-inverted{background-color:#fff;color:#3298dc}.button.is-info.is-inverted.is-hovered,.button.is-info.is-inverted:hover{background-color:#f2f2f2}.button.is-info.is-inverted[disabled],fieldset[disabled] .button.is-info.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#3298dc}.button.is-info.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined{background-color:transparent;border-color:#3298dc;color:#3298dc}.button.is-info.is-outlined.is-focused,.button.is-info.is-outlined.is-hovered,.button.is-info.is-outlined:focus,.button.is-info.is-outlined:hover{background-color:#3298dc;border-color:#3298dc;color:#fff}.button.is-info.is-outlined.is-loading::after{border-color:transparent transparent #3298dc #3298dc!important}.button.is-info.is-outlined.is-loading.is-focused::after,.button.is-info.is-outlined.is-loading.is-hovered::after,.button.is-info.is-outlined.is-loading:focus::after,.button.is-info.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined[disabled],fieldset[disabled] .button.is-info.is-outlined{background-color:transparent;border-color:#3298dc;box-shadow:none;color:#3298dc}.button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined.is-focused,.button.is-info.is-inverted.is-outlined.is-hovered,.button.is-info.is-inverted.is-outlined:focus,.button.is-info.is-inverted.is-outlined:hover{background-color:#fff;color:#3298dc}.button.is-info.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-info.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-info.is-inverted.is-outlined.is-loading:focus::after,.button.is-info.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #3298dc #3298dc!important}.button.is-info.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-info.is-light{background-color:#eef6fc;color:#1d72aa}.button.is-info.is-light.is-hovered,.button.is-info.is-light:hover{background-color:#e3f1fa;border-color:transparent;color:#1d72aa}.button.is-info.is-light.is-active,.button.is-info.is-light:active{background-color:#d8ebf8;border-color:transparent;color:#1d72aa}.button.is-success{background-color:#48c774;border-color:transparent;color:#fff}.button.is-success.is-hovered,.button.is-success:hover{background-color:#3ec46d;border-color:transparent;color:#fff}.button.is-success.is-focused,.button.is-success:focus{border-color:transparent;color:#fff}.button.is-success.is-focused:not(:active),.button.is-success:focus:not(:active){box-shadow:0 0 0 .125em rgba(72,199,116,.25)}.button.is-success.is-active,.button.is-success:active{background-color:#3abb67;border-color:transparent;color:#fff}.button.is-success[disabled],fieldset[disabled] .button.is-success{background-color:#48c774;border-color:transparent;box-shadow:none}.button.is-success.is-inverted{background-color:#fff;color:#48c774}.button.is-success.is-inverted.is-hovered,.button.is-success.is-inverted:hover{background-color:#f2f2f2}.button.is-success.is-inverted[disabled],fieldset[disabled] .button.is-success.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#48c774}.button.is-success.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined{background-color:transparent;border-color:#48c774;color:#48c774}.button.is-success.is-outlined.is-focused,.button.is-success.is-outlined.is-hovered,.button.is-success.is-outlined:focus,.button.is-success.is-outlined:hover{background-color:#48c774;border-color:#48c774;color:#fff}.button.is-success.is-outlined.is-loading::after{border-color:transparent transparent #48c774 #48c774!important}.button.is-success.is-outlined.is-loading.is-focused::after,.button.is-success.is-outlined.is-loading.is-hovered::after,.button.is-success.is-outlined.is-loading:focus::after,.button.is-success.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined[disabled],fieldset[disabled] .button.is-success.is-outlined{background-color:transparent;border-color:#48c774;box-shadow:none;color:#48c774}.button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined.is-focused,.button.is-success.is-inverted.is-outlined.is-hovered,.button.is-success.is-inverted.is-outlined:focus,.button.is-success.is-inverted.is-outlined:hover{background-color:#fff;color:#48c774}.button.is-success.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-success.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-success.is-inverted.is-outlined.is-loading:focus::after,.button.is-success.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #48c774 #48c774!important}.button.is-success.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-success.is-light{background-color:#effaf3;color:#257942}.button.is-success.is-light.is-hovered,.button.is-success.is-light:hover{background-color:#e6f7ec;border-color:transparent;color:#257942}.button.is-success.is-light.is-active,.button.is-success.is-light:active{background-color:#dcf4e4;border-color:transparent;color:#257942}.button.is-warning{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-hovered,.button.is-warning:hover{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused,.button.is-warning:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused:not(:active),.button.is-warning:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.button.is-warning.is-active,.button.is-warning:active{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning[disabled],fieldset[disabled] .button.is-warning{background-color:#ffdd57;border-color:transparent;box-shadow:none}.button.is-warning.is-inverted{background-color:rgba(0,0,0,.7);color:#ffdd57}.button.is-warning.is-inverted.is-hovered,.button.is-warning.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-warning.is-inverted[disabled],fieldset[disabled] .button.is-warning.is-inverted{background-color:rgba(0,0,0,.7);border-color:transparent;box-shadow:none;color:#ffdd57}.button.is-warning.is-loading::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;color:#ffdd57}.button.is-warning.is-outlined.is-focused,.button.is-warning.is-outlined.is-hovered,.button.is-warning.is-outlined:focus,.button.is-warning.is-outlined:hover{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.button.is-warning.is-outlined.is-loading::after{border-color:transparent transparent #ffdd57 #ffdd57!important}.button.is-warning.is-outlined.is-loading.is-focused::after,.button.is-warning.is-outlined.is-loading.is-hovered::after,.button.is-warning.is-outlined.is-loading:focus::after,.button.is-warning.is-outlined.is-loading:hover::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;box-shadow:none;color:#ffdd57}.button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-warning.is-inverted.is-outlined.is-focused,.button.is-warning.is-inverted.is-outlined.is-hovered,.button.is-warning.is-inverted.is-outlined:focus,.button.is-warning.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#ffdd57}.button.is-warning.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-warning.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-warning.is-inverted.is-outlined.is-loading:focus::after,.button.is-warning.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #ffdd57 #ffdd57!important}.button.is-warning.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);box-shadow:none;color:rgba(0,0,0,.7)}.button.is-warning.is-light{background-color:#fffbeb;color:#947600}.button.is-warning.is-light.is-hovered,.button.is-warning.is-light:hover{background-color:#fff8de;border-color:transparent;color:#947600}.button.is-warning.is-light.is-active,.button.is-warning.is-light:active{background-color:#fff6d1;border-color:transparent;color:#947600}.button.is-danger{background-color:#f14668;border-color:transparent;color:#fff}.button.is-danger.is-hovered,.button.is-danger:hover{background-color:#f03a5f;border-color:transparent;color:#fff}.button.is-danger.is-focused,.button.is-danger:focus{border-color:transparent;color:#fff}.button.is-danger.is-focused:not(:active),.button.is-danger:focus:not(:active){box-shadow:0 0 0 .125em rgba(241,70,104,.25)}.button.is-danger.is-active,.button.is-danger:active{background-color:#ef2e55;border-color:transparent;color:#fff}.button.is-danger[disabled],fieldset[disabled] .button.is-danger{background-color:#f14668;border-color:transparent;box-shadow:none}.button.is-danger.is-inverted{background-color:#fff;color:#f14668}.button.is-danger.is-inverted.is-hovered,.button.is-danger.is-inverted:hover{background-color:#f2f2f2}.button.is-danger.is-inverted[disabled],fieldset[disabled] .button.is-danger.is-inverted{background-color:#fff;border-color:transparent;box-shadow:none;color:#f14668}.button.is-danger.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined{background-color:transparent;border-color:#f14668;color:#f14668}.button.is-danger.is-outlined.is-focused,.button.is-danger.is-outlined.is-hovered,.button.is-danger.is-outlined:focus,.button.is-danger.is-outlined:hover{background-color:#f14668;border-color:#f14668;color:#fff}.button.is-danger.is-outlined.is-loading::after{border-color:transparent transparent #f14668 #f14668!important}.button.is-danger.is-outlined.is-loading.is-focused::after,.button.is-danger.is-outlined.is-loading.is-hovered::after,.button.is-danger.is-outlined.is-loading:focus::after,.button.is-danger.is-outlined.is-loading:hover::after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-outlined{background-color:transparent;border-color:#f14668;box-shadow:none;color:#f14668}.button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined.is-focused,.button.is-danger.is-inverted.is-outlined.is-hovered,.button.is-danger.is-inverted.is-outlined:focus,.button.is-danger.is-inverted.is-outlined:hover{background-color:#fff;color:#f14668}.button.is-danger.is-inverted.is-outlined.is-loading.is-focused::after,.button.is-danger.is-inverted.is-outlined.is-loading.is-hovered::after,.button.is-danger.is-inverted.is-outlined.is-loading:focus::after,.button.is-danger.is-inverted.is-outlined.is-loading:hover::after{border-color:transparent transparent #f14668 #f14668!important}.button.is-danger.is-inverted.is-outlined[disabled],fieldset[disabled] .button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-danger.is-light{background-color:#feecf0;color:#cc0f35}.button.is-danger.is-light.is-hovered,.button.is-danger.is-light:hover{background-color:#fde0e6;border-color:transparent;color:#cc0f35}.button.is-danger.is-light.is-active,.button.is-danger.is-light:active{background-color:#fcd4dc;border-color:transparent;color:#cc0f35}.button.is-small{border-radius:2px;font-size:.75rem}.button.is-normal{font-size:1rem}.button.is-medium{font-size:1.25rem}.button.is-large{font-size:1.5rem}.button[disabled],fieldset[disabled] .button{background-color:#fff;border-color:#dbdbdb;box-shadow:none;opacity:.5}.button.is-fullwidth{display:flex;width:100%}.button.is-loading{color:transparent!important;pointer-events:none}.button.is-loading::after{position:absolute;left:calc(50% - (1em / 2));top:calc(50% - (1em / 2));position:absolute!important}.button.is-static{background-color:#f5f5f5;border-color:#dbdbdb;color:#7a7a7a;box-shadow:none;pointer-events:none}.button.is-rounded{border-radius:290486px;padding-left:calc(1em + .25em);padding-right:calc(1em + .25em)}.buttons{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.buttons .button{margin-bottom:.5rem}.buttons .button:not(:last-child):not(.is-fullwidth){margin-right:.5rem}.buttons:last-child{margin-bottom:-.5rem}.buttons:not(:last-child){margin-bottom:1rem}.buttons.are-small .button:not(.is-normal):not(.is-medium):not(.is-large){border-radius:2px;font-size:.75rem}.buttons.are-medium .button:not(.is-small):not(.is-normal):not(.is-large){font-size:1.25rem}.buttons.are-large .button:not(.is-small):not(.is-normal):not(.is-medium){font-size:1.5rem}.buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.buttons.has-addons .button:last-child{margin-right:0}.buttons.has-addons .button.is-hovered,.buttons.has-addons .button:hover{z-index:2}.buttons.has-addons .button.is-active,.buttons.has-addons .button.is-focused,.buttons.has-addons .button.is-selected,.buttons.has-addons .button:active,.buttons.has-addons .button:focus{z-index:3}.buttons.has-addons .button.is-active:hover,.buttons.has-addons .button.is-focused:hover,.buttons.has-addons .button.is-selected:hover,.buttons.has-addons .button:active:hover,.buttons.has-addons .button:focus:hover{z-index:4}.buttons.has-addons .button.is-expanded{flex-grow:1;flex-shrink:1}.buttons.is-centered{justify-content:center}.buttons.is-centered:not(.has-addons) .button:not(.is-fullwidth){margin-left:.25rem;margin-right:.25rem}.buttons.is-right{justify-content:flex-end}.buttons.is-right:not(.has-addons) .button:not(.is-fullwidth){margin-left:.25rem;margin-right:.25rem}.container{flex-grow:1;margin:0 auto;position:relative;width:auto}.container.is-fluid{max-width:none!important;padding-left:32px;padding-right:32px;width:100%}@media screen and (min-width:1024px){.container{max-width:960px}}@media screen and (max-width:1215px){.container.is-widescreen:not(.is-max-desktop){max-width:1152px}}@media screen and (max-width:1407px){.container.is-fullhd:not(.is-max-desktop):not(.is-max-widescreen){max-width:1344px}}@media screen and (min-width:1216px){.container:not(.is-max-desktop){max-width:1152px}}@media screen and (min-width:1408px){.container:not(.is-max-desktop):not(.is-max-widescreen){max-width:1344px}}.content li+li{margin-top:.25em}.content blockquote:not(:last-child),.content dl:not(:last-child),.content ol:not(:last-child),.content p:not(:last-child),.content pre:not(:last-child),.content table:not(:last-child),.content ul:not(:last-child){margin-bottom:1em}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#363636;font-weight:600;line-height:1.125}.content h1{font-size:2em;margin-bottom:.5em}.content h1:not(:first-child){margin-top:1em}.content h2{font-size:1.75em;margin-bottom:.5714em}.content h2:not(:first-child){margin-top:1.1428em}.content h3{font-size:1.5em;margin-bottom:.6666em}.content h3:not(:first-child){margin-top:1.3333em}.content h4{font-size:1.25em;margin-bottom:.8em}.content h5{font-size:1.125em;margin-bottom:.8888em}.content h6{font-size:1em;margin-bottom:1em}.content blockquote{background-color:#f5f5f5;border-left:5px solid #dbdbdb;padding:1.25em 1.5em}.content ol{list-style-position:outside;margin-left:2em;margin-top:1em}.content ol:not([type]){list-style-type:decimal}.content ol:not([type]).is-lower-alpha{list-style-type:lower-alpha}.content ol:not([type]).is-lower-roman{list-style-type:lower-roman}.content ol:not([type]).is-upper-alpha{list-style-type:upper-alpha}.content ol:not([type]).is-upper-roman{list-style-type:upper-roman}.content ul{list-style:disc outside;margin-left:2em;margin-top:1em}.content ul ul{list-style-type:circle;margin-top:.5em}.content ul ul ul{list-style-type:square}.content dd{margin-left:2em}.content figure{margin-left:2em;margin-right:2em;text-align:center}.content figure:not(:first-child){margin-top:2em}.content figure:not(:last-child){margin-bottom:2em}.content figure img{display:inline-block}.content figure figcaption{font-style:italic}.content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:1.25em 1.5em;white-space:pre;word-wrap:normal}.content sub,.content sup{font-size:75%}.content table{width:100%}.content table td,.content table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.content table th{color:#363636}.content table th:not([align]){text-align:inherit}.content table thead td,.content table thead th{border-width:0 0 2px;color:#363636}.content table tfoot td,.content table tfoot th{border-width:2px 0 0;color:#363636}.content table tbody tr:last-child td,.content table tbody tr:last-child th{border-bottom-width:0}.content .tabs li+li{margin-top:0}.content.is-small{font-size:.75rem}.content.is-medium{font-size:1.25rem}.content.is-large{font-size:1.5rem}.icon{align-items:center;display:inline-flex;justify-content:center;height:1.5rem;width:1.5rem}.icon.is-small{height:1rem;width:1rem}.icon.is-medium{height:2rem;width:2rem}.icon.is-large{height:3rem;width:3rem}.image{display:block;position:relative}.image img{display:block;height:auto;width:100%}.image img.is-rounded{border-radius:290486px}.image.is-fullwidth{width:100%}.image.is-16by9 .has-ratio,.image.is-16by9 img,.image.is-1by1 .has-ratio,.image.is-1by1 img,.image.is-1by2 .has-ratio,.image.is-1by2 img,.image.is-1by3 .has-ratio,.image.is-1by3 img,.image.is-2by1 .has-ratio,.image.is-2by1 img,.image.is-2by3 .has-ratio,.image.is-2by3 img,.image.is-3by1 .has-ratio,.image.is-3by1 img,.image.is-3by2 .has-ratio,.image.is-3by2 img,.image.is-3by4 .has-ratio,.image.is-3by4 img,.image.is-3by5 .has-ratio,.image.is-3by5 img,.image.is-4by3 .has-ratio,.image.is-4by3 img,.image.is-4by5 .has-ratio,.image.is-4by5 img,.image.is-5by3 .has-ratio,.image.is-5by3 img,.image.is-5by4 .has-ratio,.image.is-5by4 img,.image.is-9by16 .has-ratio,.image.is-9by16 img,.image.is-square .has-ratio,.image.is-square img{height:100%;width:100%}.image.is-1by1,.image.is-square{padding-top:100%}.image.is-5by4{padding-top:80%}.image.is-4by3{padding-top:75%}.image.is-3by2{padding-top:66.6666%}.image.is-5by3{padding-top:60%}.image.is-16by9{padding-top:56.25%}.image.is-2by1{padding-top:50%}.image.is-3by1{padding-top:33.3333%}.image.is-4by5{padding-top:125%}.image.is-3by4{padding-top:133.3333%}.image.is-2by3{padding-top:150%}.image.is-3by5{padding-top:166.6666%}.image.is-9by16{padding-top:177.7777%}.image.is-1by2{padding-top:200%}.image.is-1by3{padding-top:300%}.image.is-16x16{height:16px;width:16px}.image.is-24x24{height:24px;width:24px}.image.is-32x32{height:32px;width:32px}.image.is-48x48{height:48px;width:48px}.image.is-64x64{height:64px;width:64px}.image.is-96x96{height:96px;width:96px}.image.is-128x128{height:128px;width:128px}.notification{background-color:#f5f5f5;border-radius:4px;position:relative;padding:1.25rem 2.5rem 1.25rem 1.5rem}.notification a:not(.button):not(.dropdown-item){color:currentColor;text-decoration:underline}.notification strong{color:currentColor}.notification code,.notification pre{background:#fff}.notification pre code{background:0 0}.notification>.delete{right:.5rem;position:absolute;top:.5rem}.notification .content,.notification .subtitle,.notification .title{color:currentColor}.notification.is-white{background-color:#fff;color:#0a0a0a}.notification.is-black{background-color:#0a0a0a;color:#fff}.notification.is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.notification.is-dark{background-color:#363636;color:#fff}.notification.is-primary{background-color:#00d1b2;color:#fff}.notification.is-primary.is-light{background-color:#ebfffc;color:#00947e}.notification.is-link{background-color:#3273dc;color:#fff}.notification.is-link.is-light{background-color:#eef3fc;color:#2160c4}.notification.is-info{background-color:#3298dc;color:#fff}.notification.is-info.is-light{background-color:#eef6fc;color:#1d72aa}.notification.is-success{background-color:#48c774;color:#fff}.notification.is-success.is-light{background-color:#effaf3;color:#257942}.notification.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.notification.is-warning.is-light{background-color:#fffbeb;color:#947600}.notification.is-danger{background-color:#f14668;color:#fff}.notification.is-danger.is-light{background-color:#feecf0;color:#cc0f35}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:290486px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}.progress::-webkit-progress-bar{background-color:#ededed}.progress::-webkit-progress-value{background-color:#4a4a4a}.progress::-moz-progress-bar{background-color:#4a4a4a}.progress::-ms-fill{background-color:#4a4a4a;border:none}.progress.is-white::-webkit-progress-value{background-color:#fff}.progress.is-white::-moz-progress-bar{background-color:#fff}.progress.is-white::-ms-fill{background-color:#fff}.progress.is-white:indeterminate{background-image:linear-gradient(to right,#fff 30%,#ededed 30%)}.progress.is-black::-webkit-progress-value{background-color:#0a0a0a}.progress.is-black::-moz-progress-bar{background-color:#0a0a0a}.progress.is-black::-ms-fill{background-color:#0a0a0a}.progress.is-black:indeterminate{background-image:linear-gradient(to right,#0a0a0a 30%,#ededed 30%)}.progress.is-light::-webkit-progress-value{background-color:#f5f5f5}.progress.is-light::-moz-progress-bar{background-color:#f5f5f5}.progress.is-light::-ms-fill{background-color:#f5f5f5}.progress.is-light:indeterminate{background-image:linear-gradient(to right,#f5f5f5 30%,#ededed 30%)}.progress.is-dark::-webkit-progress-value{background-color:#363636}.progress.is-dark::-moz-progress-bar{background-color:#363636}.progress.is-dark::-ms-fill{background-color:#363636}.progress.is-dark:indeterminate{background-image:linear-gradient(to right,#363636 30%,#ededed 30%)}.progress.is-primary::-webkit-progress-value{background-color:#00d1b2}.progress.is-primary::-moz-progress-bar{background-color:#00d1b2}.progress.is-primary::-ms-fill{background-color:#00d1b2}.progress.is-primary:indeterminate{background-image:linear-gradient(to right,#00d1b2 30%,#ededed 30%)}.progress.is-link::-webkit-progress-value{background-color:#3273dc}.progress.is-link::-moz-progress-bar{background-color:#3273dc}.progress.is-link::-ms-fill{background-color:#3273dc}.progress.is-link:indeterminate{background-image:linear-gradient(to right,#3273dc 30%,#ededed 30%)}.progress.is-info::-webkit-progress-value{background-color:#3298dc}.progress.is-info::-moz-progress-bar{background-color:#3298dc}.progress.is-info::-ms-fill{background-color:#3298dc}.progress.is-info:indeterminate{background-image:linear-gradient(to right,#3298dc 30%,#ededed 30%)}.progress.is-success::-webkit-progress-value{background-color:#48c774}.progress.is-success::-moz-progress-bar{background-color:#48c774}.progress.is-success::-ms-fill{background-color:#48c774}.progress.is-success:indeterminate{background-image:linear-gradient(to right,#48c774 30%,#ededed 30%)}.progress.is-warning::-webkit-progress-value{background-color:#ffdd57}.progress.is-warning::-moz-progress-bar{background-color:#ffdd57}.progress.is-warning::-ms-fill{background-color:#ffdd57}.progress.is-warning:indeterminate{background-image:linear-gradient(to right,#ffdd57 30%,#ededed 30%)}.progress.is-danger::-webkit-progress-value{background-color:#f14668}.progress.is-danger::-moz-progress-bar{background-color:#f14668}.progress.is-danger::-ms-fill{background-color:#f14668}.progress.is-danger:indeterminate{background-image:linear-gradient(to right,#f14668 30%,#ededed 30%)}.progress:indeterminate{-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:moveIndeterminate;animation-name:moveIndeterminate;-webkit-animation-timing-function:linear;animation-timing-function:linear;background-color:#ededed;background-image:linear-gradient(to right,#4a4a4a 30%,#ededed 30%);background-position:top left;background-repeat:no-repeat;background-size:150% 150%}.progress:indeterminate::-webkit-progress-bar{background-color:transparent}.progress:indeterminate::-moz-progress-bar{background-color:transparent}.progress:indeterminate::-ms-fill{animation-name:none}.progress.is-small{height:.75rem}.progress.is-medium{height:1.25rem}.progress.is-large{height:1.5rem}@-webkit-keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}@keyframes moveIndeterminate{from{background-position:200% 0}to{background-position:-200% 0}}.table{background-color:#fff;color:#363636}.table td,.table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.table td.is-white,.table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}.table td.is-black,.table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.table td.is-light,.table th.is-light{background-color:#f5f5f5;border-color:#f5f5f5;color:rgba(0,0,0,.7)}.table td.is-dark,.table th.is-dark{background-color:#363636;border-color:#363636;color:#fff}.table td.is-primary,.table th.is-primary{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.table td.is-link,.table th.is-link{background-color:#3273dc;border-color:#3273dc;color:#fff}.table td.is-info,.table th.is-info{background-color:#3298dc;border-color:#3298dc;color:#fff}.table td.is-success,.table th.is-success{background-color:#48c774;border-color:#48c774;color:#fff}.table td.is-warning,.table th.is-warning{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.table td.is-danger,.table th.is-danger{background-color:#f14668;border-color:#f14668;color:#fff}.table td.is-narrow,.table th.is-narrow{white-space:nowrap;width:1%}.table td.is-selected,.table th.is-selected{background-color:#00d1b2;color:#fff}.table td.is-selected a,.table td.is-selected strong,.table th.is-selected a,.table th.is-selected strong{color:currentColor}.table td.is-vcentered,.table th.is-vcentered{vertical-align:middle}.table th{color:#363636}.table th:not([align]){text-align:inherit}.table tr.is-selected{background-color:#00d1b2;color:#fff}.table tr.is-selected a,.table tr.is-selected strong{color:currentColor}.table tr.is-selected td,.table tr.is-selected th{border-color:#fff;color:currentColor}.table thead{background-color:transparent}.table thead td,.table thead th{border-width:0 0 2px;color:#363636}.table tfoot{background-color:transparent}.table tfoot td,.table tfoot th{border-width:2px 0 0;color:#363636}.table tbody{background-color:transparent}.table tbody tr:last-child td,.table tbody tr:last-child th{border-bottom-width:0}.table.is-bordered td,.table.is-bordered th{border-width:1px}.table.is-bordered tr:last-child td,.table.is-bordered tr:last-child th{border-bottom-width:1px}.table.is-fullwidth{width:100%}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover:nth-child(even){background-color:#f5f5f5}.table.is-narrow td,.table.is-narrow th{padding:.25em .5em}.table.is-striped tbody tr:not(.is-selected):nth-child(even){background-color:#fafafa}.table-container{-webkit-overflow-scrolling:touch;overflow:auto;overflow-y:hidden;max-width:100%}.tags{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.tags .tag{margin-bottom:.5rem}.tags .tag:not(:last-child){margin-right:.5rem}.tags:last-child{margin-bottom:-.5rem}.tags:not(:last-child){margin-bottom:1rem}.tags.are-medium .tag:not(.is-normal):not(.is-large){font-size:1rem}.tags.are-large .tag:not(.is-normal):not(.is-medium){font-size:1.25rem}.tags.is-centered{justify-content:center}.tags.is-centered .tag{margin-right:.25rem;margin-left:.25rem}.tags.is-right{justify-content:flex-end}.tags.is-right .tag:not(:first-child){margin-left:.5rem}.tags.is-right .tag:not(:last-child){margin-right:0}.tags.has-addons .tag{margin-right:0}.tags.has-addons .tag:not(:first-child){margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.tags.has-addons .tag:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.tag:not(body){align-items:center;background-color:#f5f5f5;border-radius:4px;color:#4a4a4a;display:inline-flex;font-size:.75rem;height:2em;justify-content:center;line-height:1.5;padding-left:.75em;padding-right:.75em;white-space:nowrap}.tag:not(body) .delete{margin-left:.25rem;margin-right:-.375rem}.tag:not(body).is-white{background-color:#fff;color:#0a0a0a}.tag:not(body).is-black{background-color:#0a0a0a;color:#fff}.tag:not(body).is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.tag:not(body).is-dark{background-color:#363636;color:#fff}.tag:not(body).is-primary{background-color:#00d1b2;color:#fff}.tag:not(body).is-primary.is-light{background-color:#ebfffc;color:#00947e}.tag:not(body).is-link{background-color:#3273dc;color:#fff}.tag:not(body).is-link.is-light{background-color:#eef3fc;color:#2160c4}.tag:not(body).is-info{background-color:#3298dc;color:#fff}.tag:not(body).is-info.is-light{background-color:#eef6fc;color:#1d72aa}.tag:not(body).is-success{background-color:#48c774;color:#fff}.tag:not(body).is-success.is-light{background-color:#effaf3;color:#257942}.tag:not(body).is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.tag:not(body).is-warning.is-light{background-color:#fffbeb;color:#947600}.tag:not(body).is-danger{background-color:#f14668;color:#fff}.tag:not(body).is-danger.is-light{background-color:#feecf0;color:#cc0f35}.tag:not(body).is-normal{font-size:.75rem}.tag:not(body).is-medium{font-size:1rem}.tag:not(body).is-large{font-size:1.25rem}.tag:not(body) .icon:first-child:not(:last-child){margin-left:-.375em;margin-right:.1875em}.tag:not(body) .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:-.375em}.tag:not(body) .icon:first-child:last-child{margin-left:-.375em;margin-right:-.375em}.tag:not(body).is-delete{margin-left:1px;padding:0;position:relative;width:2em}.tag:not(body).is-delete::after,.tag:not(body).is-delete::before{background-color:currentColor;content:"";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.tag:not(body).is-delete::before{height:1px;width:50%}.tag:not(body).is-delete::after{height:50%;width:1px}.tag:not(body).is-delete:focus,.tag:not(body).is-delete:hover{background-color:#e8e8e8}.tag:not(body).is-delete:active{background-color:#dbdbdb}.tag:not(body).is-rounded{border-radius:290486px}a.tag:hover{text-decoration:underline}.subtitle,.title{word-break:break-word}.subtitle em,.subtitle span,.title em,.title span{font-weight:inherit}.subtitle sub,.title sub{font-size:.75em}.subtitle sup,.title sup{font-size:.75em}.subtitle .tag,.title .tag{vertical-align:middle}.title{color:#363636;font-size:2rem;font-weight:600;line-height:1.125}.title strong{color:inherit;font-weight:inherit}.title+.highlight{margin-top:-.75rem}.title:not(.is-spaced)+.subtitle{margin-top:-1.25rem}.title.is-1{font-size:3rem}.title.is-2{font-size:2.5rem}.title.is-3{font-size:2rem}.title.is-4{font-size:1.5rem}.title.is-5{font-size:1.25rem}.title.is-6{font-size:1rem}.title.is-7{font-size:.75rem}.subtitle{color:#4a4a4a;font-size:1.25rem;font-weight:400;line-height:1.25}.subtitle strong{color:#363636;font-weight:600}.subtitle:not(.is-spaced)+.title{margin-top:-1.25rem}.subtitle.is-1{font-size:3rem}.subtitle.is-2{font-size:2.5rem}.subtitle.is-3{font-size:2rem}.subtitle.is-4{font-size:1.5rem}.subtitle.is-5{font-size:1.25rem}.subtitle.is-6{font-size:1rem}.subtitle.is-7{font-size:.75rem}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.highlight{font-weight:400;max-width:100%;overflow:hidden;padding:0}.highlight pre{overflow:auto;max-width:100%}.number{align-items:center;background-color:#f5f5f5;border-radius:290486px;display:inline-flex;font-size:1.25rem;height:2em;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:.25rem .5rem;text-align:center;vertical-align:top}.input,.select select,.textarea{background-color:#fff;border-color:#dbdbdb;border-radius:4px;color:#363636}.input::-moz-placeholder,.select select::-moz-placeholder,.textarea::-moz-placeholder{color:rgba(54,54,54,.3)}.input::-webkit-input-placeholder,.select select::-webkit-input-placeholder,.textarea::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.input:-moz-placeholder,.select select:-moz-placeholder,.textarea:-moz-placeholder{color:rgba(54,54,54,.3)}.input:-ms-input-placeholder,.select select:-ms-input-placeholder,.textarea:-ms-input-placeholder{color:rgba(54,54,54,.3)}.input:hover,.is-hovered.input,.is-hovered.textarea,.select select.is-hovered,.select select:hover,.textarea:hover{border-color:#b5b5b5}.input:active,.input:focus,.is-active.input,.is-active.textarea,.is-focused.input,.is-focused.textarea,.select select.is-active,.select select.is-focused,.select select:active,.select select:focus,.textarea:active,.textarea:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input[disabled],.select fieldset[disabled] select,.select select[disabled],.textarea[disabled],fieldset[disabled] .input,fieldset[disabled] .select select,fieldset[disabled] .textarea{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.input[disabled]::-moz-placeholder,.select fieldset[disabled] select::-moz-placeholder,.select select[disabled]::-moz-placeholder,.textarea[disabled]::-moz-placeholder,fieldset[disabled] .input::-moz-placeholder,fieldset[disabled] .select select::-moz-placeholder,fieldset[disabled] .textarea::-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]::-webkit-input-placeholder,.select fieldset[disabled] select::-webkit-input-placeholder,.select select[disabled]::-webkit-input-placeholder,.textarea[disabled]::-webkit-input-placeholder,fieldset[disabled] .input::-webkit-input-placeholder,fieldset[disabled] .select select::-webkit-input-placeholder,fieldset[disabled] .textarea::-webkit-input-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-moz-placeholder,.select fieldset[disabled] select:-moz-placeholder,.select select[disabled]:-moz-placeholder,.textarea[disabled]:-moz-placeholder,fieldset[disabled] .input:-moz-placeholder,fieldset[disabled] .select select:-moz-placeholder,fieldset[disabled] .textarea:-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-ms-input-placeholder,.select fieldset[disabled] select:-ms-input-placeholder,.select select[disabled]:-ms-input-placeholder,.textarea[disabled]:-ms-input-placeholder,fieldset[disabled] .input:-ms-input-placeholder,fieldset[disabled] .select select:-ms-input-placeholder,fieldset[disabled] .textarea:-ms-input-placeholder{color:rgba(122,122,122,.3)}.input,.textarea{box-shadow:inset 0 .0625em .125em rgba(10,10,10,.05);max-width:100%;width:100%}.input[readonly],.textarea[readonly]{box-shadow:none}.is-white.input,.is-white.textarea{border-color:#fff}.is-white.input:active,.is-white.input:focus,.is-white.is-active.input,.is-white.is-active.textarea,.is-white.is-focused.input,.is-white.is-focused.textarea,.is-white.textarea:active,.is-white.textarea:focus{box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.is-black.input,.is-black.textarea{border-color:#0a0a0a}.is-black.input:active,.is-black.input:focus,.is-black.is-active.input,.is-black.is-active.textarea,.is-black.is-focused.input,.is-black.is-focused.textarea,.is-black.textarea:active,.is-black.textarea:focus{box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.is-light.input,.is-light.textarea{border-color:#f5f5f5}.is-light.input:active,.is-light.input:focus,.is-light.is-active.input,.is-light.is-active.textarea,.is-light.is-focused.input,.is-light.is-focused.textarea,.is-light.textarea:active,.is-light.textarea:focus{box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.is-dark.input,.is-dark.textarea{border-color:#363636}.is-dark.input:active,.is-dark.input:focus,.is-dark.is-active.input,.is-dark.is-active.textarea,.is-dark.is-focused.input,.is-dark.is-focused.textarea,.is-dark.textarea:active,.is-dark.textarea:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.is-primary.input,.is-primary.textarea{border-color:#00d1b2}.is-primary.input:active,.is-primary.input:focus,.is-primary.is-active.input,.is-primary.is-active.textarea,.is-primary.is-focused.input,.is-primary.is-focused.textarea,.is-primary.textarea:active,.is-primary.textarea:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.is-link.input,.is-link.textarea{border-color:#3273dc}.is-link.input:active,.is-link.input:focus,.is-link.is-active.input,.is-link.is-active.textarea,.is-link.is-focused.input,.is-link.is-focused.textarea,.is-link.textarea:active,.is-link.textarea:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.is-info.input,.is-info.textarea{border-color:#3298dc}.is-info.input:active,.is-info.input:focus,.is-info.is-active.input,.is-info.is-active.textarea,.is-info.is-focused.input,.is-info.is-focused.textarea,.is-info.textarea:active,.is-info.textarea:focus{box-shadow:0 0 0 .125em rgba(50,152,220,.25)}.is-success.input,.is-success.textarea{border-color:#48c774}.is-success.input:active,.is-success.input:focus,.is-success.is-active.input,.is-success.is-active.textarea,.is-success.is-focused.input,.is-success.is-focused.textarea,.is-success.textarea:active,.is-success.textarea:focus{box-shadow:0 0 0 .125em rgba(72,199,116,.25)}.is-warning.input,.is-warning.textarea{border-color:#ffdd57}.is-warning.input:active,.is-warning.input:focus,.is-warning.is-active.input,.is-warning.is-active.textarea,.is-warning.is-focused.input,.is-warning.is-focused.textarea,.is-warning.textarea:active,.is-warning.textarea:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.is-danger.input,.is-danger.textarea{border-color:#f14668}.is-danger.input:active,.is-danger.input:focus,.is-danger.is-active.input,.is-danger.is-active.textarea,.is-danger.is-focused.input,.is-danger.is-focused.textarea,.is-danger.textarea:active,.is-danger.textarea:focus{box-shadow:0 0 0 .125em rgba(241,70,104,.25)}.is-small.input,.is-small.textarea{border-radius:2px;font-size:.75rem}.is-medium.input,.is-medium.textarea{font-size:1.25rem}.is-large.input,.is-large.textarea{font-size:1.5rem}.is-fullwidth.input,.is-fullwidth.textarea{display:block;width:100%}.is-inline.input,.is-inline.textarea{display:inline;width:auto}.input.is-rounded{border-radius:290486px;padding-left:calc(calc(.75em - 1px) + .375em);padding-right:calc(calc(.75em - 1px) + .375em)}.input.is-static{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.textarea{display:block;max-width:100%;min-width:100%;padding:calc(.75em - 1px);resize:vertical}.textarea:not([rows]){max-height:40em;min-height:8em}.textarea[rows]{height:initial}.textarea.has-fixed-size{resize:none}.checkbox,.radio{cursor:pointer;display:inline-block;line-height:1.25;position:relative}.checkbox input,.radio input{cursor:pointer}.checkbox:hover,.radio:hover{color:#363636}.checkbox input[disabled],.checkbox[disabled],.radio input[disabled],.radio[disabled],fieldset[disabled] .checkbox,fieldset[disabled] .radio{color:#7a7a7a;cursor:not-allowed}.radio+.radio{margin-left:.5em}.select{display:inline-block;max-width:100%;position:relative;vertical-align:top}.select:not(.is-multiple){height:2.5em}.select:not(.is-multiple):not(.is-loading)::after{border-color:#3273dc;right:1.125em;z-index:4}.select.is-rounded select{border-radius:290486px;padding-left:1em}.select select{cursor:pointer;display:block;font-size:1em;max-width:100%;outline:0}.select select::-ms-expand{display:none}.select select[disabled]:hover,fieldset[disabled] .select select:hover{border-color:#f5f5f5}.select select:not([multiple]){padding-right:2.5em}.select select[multiple]{height:auto;padding:0}.select select[multiple] option{padding:.5em 1em}.select:not(.is-multiple):not(.is-loading):hover::after{border-color:#363636}.select.is-white:not(:hover)::after{border-color:#fff}.select.is-white select{border-color:#fff}.select.is-white select.is-hovered,.select.is-white select:hover{border-color:#f2f2f2}.select.is-white select.is-active,.select.is-white select.is-focused,.select.is-white select:active,.select.is-white select:focus{box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.select.is-black:not(:hover)::after{border-color:#0a0a0a}.select.is-black select{border-color:#0a0a0a}.select.is-black select.is-hovered,.select.is-black select:hover{border-color:#000}.select.is-black select.is-active,.select.is-black select.is-focused,.select.is-black select:active,.select.is-black select:focus{box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.select.is-light:not(:hover)::after{border-color:#f5f5f5}.select.is-light select{border-color:#f5f5f5}.select.is-light select.is-hovered,.select.is-light select:hover{border-color:#e8e8e8}.select.is-light select.is-active,.select.is-light select.is-focused,.select.is-light select:active,.select.is-light select:focus{box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.select.is-dark:not(:hover)::after{border-color:#363636}.select.is-dark select{border-color:#363636}.select.is-dark select.is-hovered,.select.is-dark select:hover{border-color:#292929}.select.is-dark select.is-active,.select.is-dark select.is-focused,.select.is-dark select:active,.select.is-dark select:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.select.is-primary:not(:hover)::after{border-color:#00d1b2}.select.is-primary select{border-color:#00d1b2}.select.is-primary select.is-hovered,.select.is-primary select:hover{border-color:#00b89c}.select.is-primary select.is-active,.select.is-primary select.is-focused,.select.is-primary select:active,.select.is-primary select:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.select.is-link:not(:hover)::after{border-color:#3273dc}.select.is-link select{border-color:#3273dc}.select.is-link select.is-hovered,.select.is-link select:hover{border-color:#2366d1}.select.is-link select.is-active,.select.is-link select.is-focused,.select.is-link select:active,.select.is-link select:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select.is-info:not(:hover)::after{border-color:#3298dc}.select.is-info select{border-color:#3298dc}.select.is-info select.is-hovered,.select.is-info select:hover{border-color:#238cd1}.select.is-info select.is-active,.select.is-info select.is-focused,.select.is-info select:active,.select.is-info select:focus{box-shadow:0 0 0 .125em rgba(50,152,220,.25)}.select.is-success:not(:hover)::after{border-color:#48c774}.select.is-success select{border-color:#48c774}.select.is-success select.is-hovered,.select.is-success select:hover{border-color:#3abb67}.select.is-success select.is-active,.select.is-success select.is-focused,.select.is-success select:active,.select.is-success select:focus{box-shadow:0 0 0 .125em rgba(72,199,116,.25)}.select.is-warning:not(:hover)::after{border-color:#ffdd57}.select.is-warning select{border-color:#ffdd57}.select.is-warning select.is-hovered,.select.is-warning select:hover{border-color:#ffd83d}.select.is-warning select.is-active,.select.is-warning select.is-focused,.select.is-warning select:active,.select.is-warning select:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.select.is-danger:not(:hover)::after{border-color:#f14668}.select.is-danger select{border-color:#f14668}.select.is-danger select.is-hovered,.select.is-danger select:hover{border-color:#ef2e55}.select.is-danger select.is-active,.select.is-danger select.is-focused,.select.is-danger select:active,.select.is-danger select:focus{box-shadow:0 0 0 .125em rgba(241,70,104,.25)}.select.is-small{border-radius:2px;font-size:.75rem}.select.is-medium{font-size:1.25rem}.select.is-large{font-size:1.5rem}.select.is-disabled::after{border-color:#7a7a7a}.select.is-fullwidth{width:100%}.select.is-fullwidth select{width:100%}.select.is-loading::after{margin-top:0;position:absolute;right:.625em;top:.625em;transform:none}.select.is-loading.is-small:after{font-size:.75rem}.select.is-loading.is-medium:after{font-size:1.25rem}.select.is-loading.is-large:after{font-size:1.5rem}.file{align-items:stretch;display:flex;justify-content:flex-start;position:relative}.file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}.file.is-white.is-hovered .file-cta,.file.is-white:hover .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.file.is-white.is-focused .file-cta,.file.is-white:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,255,255,.25);color:#0a0a0a}.file.is-white.is-active .file-cta,.file.is-white:active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}.file.is-black.is-hovered .file-cta,.file.is-black:hover .file-cta{background-color:#040404;border-color:transparent;color:#fff}.file.is-black.is-focused .file-cta,.file.is-black:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(10,10,10,.25);color:#fff}.file.is-black.is-active .file-cta,.file.is-black:active .file-cta{background-color:#000;border-color:transparent;color:#fff}.file.is-light .file-cta{background-color:#f5f5f5;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-light.is-hovered .file-cta,.file.is-light:hover .file-cta{background-color:#eee;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-light.is-focused .file-cta,.file.is-light:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(245,245,245,.25);color:rgba(0,0,0,.7)}.file.is-light.is-active .file-cta,.file.is-light:active .file-cta{background-color:#e8e8e8;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-dark .file-cta{background-color:#363636;border-color:transparent;color:#fff}.file.is-dark.is-hovered .file-cta,.file.is-dark:hover .file-cta{background-color:#2f2f2f;border-color:transparent;color:#fff}.file.is-dark.is-focused .file-cta,.file.is-dark:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(54,54,54,.25);color:#fff}.file.is-dark.is-active .file-cta,.file.is-dark:active .file-cta{background-color:#292929;border-color:transparent;color:#fff}.file.is-primary .file-cta{background-color:#00d1b2;border-color:transparent;color:#fff}.file.is-primary.is-hovered .file-cta,.file.is-primary:hover .file-cta{background-color:#00c4a7;border-color:transparent;color:#fff}.file.is-primary.is-focused .file-cta,.file.is-primary:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(0,209,178,.25);color:#fff}.file.is-primary.is-active .file-cta,.file.is-primary:active .file-cta{background-color:#00b89c;border-color:transparent;color:#fff}.file.is-link .file-cta{background-color:#3273dc;border-color:transparent;color:#fff}.file.is-link.is-hovered .file-cta,.file.is-link:hover .file-cta{background-color:#276cda;border-color:transparent;color:#fff}.file.is-link.is-focused .file-cta,.file.is-link:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(50,115,220,.25);color:#fff}.file.is-link.is-active .file-cta,.file.is-link:active .file-cta{background-color:#2366d1;border-color:transparent;color:#fff}.file.is-info .file-cta{background-color:#3298dc;border-color:transparent;color:#fff}.file.is-info.is-hovered .file-cta,.file.is-info:hover .file-cta{background-color:#2793da;border-color:transparent;color:#fff}.file.is-info.is-focused .file-cta,.file.is-info:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(50,152,220,.25);color:#fff}.file.is-info.is-active .file-cta,.file.is-info:active .file-cta{background-color:#238cd1;border-color:transparent;color:#fff}.file.is-success .file-cta{background-color:#48c774;border-color:transparent;color:#fff}.file.is-success.is-hovered .file-cta,.file.is-success:hover .file-cta{background-color:#3ec46d;border-color:transparent;color:#fff}.file.is-success.is-focused .file-cta,.file.is-success:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(72,199,116,.25);color:#fff}.file.is-success.is-active .file-cta,.file.is-success:active .file-cta{background-color:#3abb67;border-color:transparent;color:#fff}.file.is-warning .file-cta{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-hovered .file-cta,.file.is-warning:hover .file-cta{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-focused .file-cta,.file.is-warning:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,221,87,.25);color:rgba(0,0,0,.7)}.file.is-warning.is-active .file-cta,.file.is-warning:active .file-cta{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-danger .file-cta{background-color:#f14668;border-color:transparent;color:#fff}.file.is-danger.is-hovered .file-cta,.file.is-danger:hover .file-cta{background-color:#f03a5f;border-color:transparent;color:#fff}.file.is-danger.is-focused .file-cta,.file.is-danger:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(241,70,104,.25);color:#fff}.file.is-danger.is-active .file-cta,.file.is-danger:active .file-cta{background-color:#ef2e55;border-color:transparent;color:#fff}.file.is-small{font-size:.75rem}.file.is-medium{font-size:1.25rem}.file.is-medium .file-icon .fa{font-size:21px}.file.is-large{font-size:1.5rem}.file.is-large .file-icon .fa{font-size:28px}.file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}.file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}.file.has-name.is-empty .file-cta{border-radius:4px}.file.has-name.is-empty .file-name{display:none}.file.is-boxed .file-label{flex-direction:column}.file.is-boxed .file-cta{flex-direction:column;height:auto;padding:1em 3em}.file.is-boxed .file-name{border-width:0 1px 1px}.file.is-boxed .file-icon{height:1.5em;width:1.5em}.file.is-boxed .file-icon .fa{font-size:21px}.file.is-boxed.is-small .file-icon .fa{font-size:14px}.file.is-boxed.is-medium .file-icon .fa{font-size:28px}.file.is-boxed.is-large .file-icon .fa{font-size:35px}.file.is-boxed.has-name .file-cta{border-radius:4px 4px 0 0}.file.is-boxed.has-name .file-name{border-radius:0 0 4px 4px;border-width:0 1px 1px}.file.is-centered{justify-content:center}.file.is-fullwidth .file-label{width:100%}.file.is-fullwidth .file-name{flex-grow:1;max-width:none}.file.is-right{justify-content:flex-end}.file.is-right .file-cta{border-radius:0 4px 4px 0}.file.is-right .file-name{border-radius:4px 0 0 4px;border-width:1px 0 1px 1px;order:-1}.file-label{align-items:stretch;display:flex;cursor:pointer;justify-content:flex-start;overflow:hidden;position:relative}.file-label:hover .file-cta{background-color:#eee;color:#363636}.file-label:hover .file-name{border-color:#d5d5d5}.file-label:active .file-cta{background-color:#e8e8e8;color:#363636}.file-label:active .file-name{border-color:#cfcfcf}.file-input{height:100%;left:0;opacity:0;outline:0;position:absolute;top:0;width:100%}.file-cta,.file-name{border-color:#dbdbdb;border-radius:4px;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}.file-cta{background-color:#f5f5f5;color:#4a4a4a}.file-name{border-color:#dbdbdb;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:inherit;text-overflow:ellipsis}.file-icon{align-items:center;display:flex;height:1em;justify-content:center;margin-right:.5em;width:1em}.file-icon .fa{font-size:14px}.label{color:#363636;display:block;font-size:1rem;font-weight:700}.label:not(:last-child){margin-bottom:.5em}.label.is-small{font-size:.75rem}.label.is-medium{font-size:1.25rem}.label.is-large{font-size:1.5rem}.help{display:block;font-size:.75rem;margin-top:.25rem}.help.is-white{color:#fff}.help.is-black{color:#0a0a0a}.help.is-light{color:#f5f5f5}.help.is-dark{color:#363636}.help.is-primary{color:#00d1b2}.help.is-link{color:#3273dc}.help.is-info{color:#3298dc}.help.is-success{color:#48c774}.help.is-warning{color:#ffdd57}.help.is-danger{color:#f14668}.field:not(:last-child){margin-bottom:.75rem}.field.has-addons{display:flex;justify-content:flex-start}.field.has-addons .control:not(:last-child){margin-right:-1px}.field.has-addons .control:not(:first-child):not(:last-child) .button,.field.has-addons .control:not(:first-child):not(:last-child) .input,.field.has-addons .control:not(:first-child):not(:last-child) .select select{border-radius:0}.field.has-addons .control:first-child:not(:only-child) .button,.field.has-addons .control:first-child:not(:only-child) .input,.field.has-addons .control:first-child:not(:only-child) .select select{border-bottom-right-radius:0;border-top-right-radius:0}.field.has-addons .control:last-child:not(:only-child) .button,.field.has-addons .control:last-child:not(:only-child) .input,.field.has-addons .control:last-child:not(:only-child) .select select{border-bottom-left-radius:0;border-top-left-radius:0}.field.has-addons .control .button:not([disabled]).is-hovered,.field.has-addons .control .button:not([disabled]):hover,.field.has-addons .control .input:not([disabled]).is-hovered,.field.has-addons .control .input:not([disabled]):hover,.field.has-addons .control .select select:not([disabled]).is-hovered,.field.has-addons .control .select select:not([disabled]):hover{z-index:2}.field.has-addons .control .button:not([disabled]).is-active,.field.has-addons .control .button:not([disabled]).is-focused,.field.has-addons .control .button:not([disabled]):active,.field.has-addons .control .button:not([disabled]):focus,.field.has-addons .control .input:not([disabled]).is-active,.field.has-addons .control .input:not([disabled]).is-focused,.field.has-addons .control .input:not([disabled]):active,.field.has-addons .control .input:not([disabled]):focus,.field.has-addons .control .select select:not([disabled]).is-active,.field.has-addons .control .select select:not([disabled]).is-focused,.field.has-addons .control .select select:not([disabled]):active,.field.has-addons .control .select select:not([disabled]):focus{z-index:3}.field.has-addons .control .button:not([disabled]).is-active:hover,.field.has-addons .control .button:not([disabled]).is-focused:hover,.field.has-addons .control .button:not([disabled]):active:hover,.field.has-addons .control .button:not([disabled]):focus:hover,.field.has-addons .control .input:not([disabled]).is-active:hover,.field.has-addons .control .input:not([disabled]).is-focused:hover,.field.has-addons .control .input:not([disabled]):active:hover,.field.has-addons .control .input:not([disabled]):focus:hover,.field.has-addons .control .select select:not([disabled]).is-active:hover,.field.has-addons .control .select select:not([disabled]).is-focused:hover,.field.has-addons .control .select select:not([disabled]):active:hover,.field.has-addons .control .select select:not([disabled]):focus:hover{z-index:4}.field.has-addons .control.is-expanded{flex-grow:1;flex-shrink:1}.field.has-addons.has-addons-centered{justify-content:center}.field.has-addons.has-addons-right{justify-content:flex-end}.field.has-addons.has-addons-fullwidth .control{flex-grow:1;flex-shrink:0}.field.is-grouped{display:flex;justify-content:flex-start}.field.is-grouped>.control{flex-shrink:0}.field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:.75rem}.field.is-grouped>.control.is-expanded{flex-grow:1;flex-shrink:1}.field.is-grouped.is-grouped-centered{justify-content:center}.field.is-grouped.is-grouped-right{justify-content:flex-end}.field.is-grouped.is-grouped-multiline{flex-wrap:wrap}.field.is-grouped.is-grouped-multiline>.control:last-child,.field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:.75rem}.field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-.75rem}.field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media screen and (min-width:769px),print{.field.is-horizontal{display:flex}}.field-label .label{font-size:inherit}@media screen and (max-width:768px){.field-label{margin-bottom:.5rem}}@media screen and (min-width:769px),print{.field-label{flex-basis:0;flex-grow:1;flex-shrink:0;margin-right:1.5rem;text-align:right}.field-label.is-small{font-size:.75rem;padding-top:.375em}.field-label.is-normal{padding-top:.375em}.field-label.is-medium{font-size:1.25rem;padding-top:.375em}.field-label.is-large{font-size:1.5rem;padding-top:.375em}}.field-body .field .field{margin-bottom:0}@media screen and (min-width:769px),print{.field-body{display:flex;flex-basis:0;flex-grow:5;flex-shrink:1}.field-body .field{margin-bottom:0}.field-body>.field{flex-shrink:1}.field-body>.field:not(.is-narrow){flex-grow:1}.field-body>.field:not(:last-child){margin-right:.75rem}}.control{box-sizing:border-box;clear:both;font-size:1rem;position:relative;text-align:inherit}.control.has-icons-left .input:focus~.icon,.control.has-icons-left .select:focus~.icon,.control.has-icons-right .input:focus~.icon,.control.has-icons-right .select:focus~.icon{color:#4a4a4a}.control.has-icons-left .input.is-small~.icon,.control.has-icons-left .select.is-small~.icon,.control.has-icons-right .input.is-small~.icon,.control.has-icons-right .select.is-small~.icon{font-size:.75rem}.control.has-icons-left .input.is-medium~.icon,.control.has-icons-left .select.is-medium~.icon,.control.has-icons-right .input.is-medium~.icon,.control.has-icons-right .select.is-medium~.icon{font-size:1.25rem}.control.has-icons-left .input.is-large~.icon,.control.has-icons-left .select.is-large~.icon,.control.has-icons-right .input.is-large~.icon,.control.has-icons-right .select.is-large~.icon{font-size:1.5rem}.control.has-icons-left .icon,.control.has-icons-right .icon{color:#dbdbdb;height:2.5em;pointer-events:none;position:absolute;top:0;width:2.5em;z-index:4}.control.has-icons-left .input,.control.has-icons-left .select select{padding-left:2.5em}.control.has-icons-left .icon.is-left{left:0}.control.has-icons-right .input,.control.has-icons-right .select select{padding-right:2.5em}.control.has-icons-right .icon.is-right{right:0}.control.is-loading::after{position:absolute!important;right:.625em;top:.625em;z-index:4}.control.is-loading.is-small:after{font-size:.75rem}.control.is-loading.is-medium:after{font-size:1.25rem}.control.is-loading.is-large:after{font-size:1.5rem}.breadcrumb{font-size:1rem;white-space:nowrap}.breadcrumb a{align-items:center;color:#3273dc;display:flex;justify-content:center;padding:0 .75em}.breadcrumb a:hover{color:#363636}.breadcrumb li{align-items:center;display:flex}.breadcrumb li:first-child a{padding-left:0}.breadcrumb li.is-active a{color:#363636;cursor:default;pointer-events:none}.breadcrumb li+li::before{color:#b5b5b5;content:"\0002f"}.breadcrumb ol,.breadcrumb ul{align-items:flex-start;display:flex;flex-wrap:wrap;justify-content:flex-start}.breadcrumb .icon:first-child{margin-right:.5em}.breadcrumb .icon:last-child{margin-left:.5em}.breadcrumb.is-centered ol,.breadcrumb.is-centered ul{justify-content:center}.breadcrumb.is-right ol,.breadcrumb.is-right ul{justify-content:flex-end}.breadcrumb.is-small{font-size:.75rem}.breadcrumb.is-medium{font-size:1.25rem}.breadcrumb.is-large{font-size:1.5rem}.breadcrumb.has-arrow-separator li+li::before{content:"\02192"}.breadcrumb.has-bullet-separator li+li::before{content:"\02022"}.breadcrumb.has-dot-separator li+li::before{content:"\000b7"}.breadcrumb.has-succeeds-separator li+li::before{content:"\0227B"}.card{background-color:#fff;border-radius:.25rem;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);color:#4a4a4a;max-width:100%;overflow:hidden;position:relative}.card-header{background-color:transparent;align-items:stretch;box-shadow:0 .125em .25em rgba(10,10,10,.1);display:flex}.card-header-title{align-items:center;color:#363636;display:flex;flex-grow:1;font-weight:700;padding:.75rem 1rem}.card-header-title.is-centered{justify-content:center}.card-header-icon{align-items:center;cursor:pointer;display:flex;justify-content:center;padding:.75rem 1rem}.card-image{display:block;position:relative}.card-content{background-color:transparent;padding:1.5rem}.card-footer{background-color:transparent;border-top:1px solid #ededed;align-items:stretch;display:flex}.card-footer-item{align-items:center;display:flex;flex-basis:0;flex-grow:1;flex-shrink:0;justify-content:center;padding:.75rem}.card-footer-item:not(:last-child){border-right:1px solid #ededed}.card .media:not(:last-child){margin-bottom:1.5rem}.dropdown{display:inline-flex;position:relative;vertical-align:top}.dropdown.is-active .dropdown-menu,.dropdown.is-hoverable:hover .dropdown-menu{display:block}.dropdown.is-right .dropdown-menu{left:auto;right:0}.dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:initial;top:auto}.dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}.dropdown-content{background-color:#fff;border-radius:4px;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);padding-bottom:.5rem;padding-top:.5rem}.dropdown-item{color:#4a4a4a;display:block;font-size:.875rem;line-height:1.5;padding:.375rem 1rem;position:relative}a.dropdown-item,button.dropdown-item{padding-right:3rem;text-align:inherit;white-space:nowrap;width:100%}a.dropdown-item:hover,button.dropdown-item:hover{background-color:#f5f5f5;color:#0a0a0a}a.dropdown-item.is-active,button.dropdown-item.is-active{background-color:#3273dc;color:#fff}.dropdown-divider{background-color:#ededed;border:none;display:block;height:1px;margin:.5rem 0}.level{align-items:center;justify-content:space-between}.level code{border-radius:4px}.level img{display:inline-block;vertical-align:top}.level.is-mobile{display:flex}.level.is-mobile .level-left,.level.is-mobile .level-right{display:flex}.level.is-mobile .level-left+.level-right{margin-top:0}.level.is-mobile .level-item:not(:last-child){margin-bottom:0;margin-right:.75rem}.level.is-mobile .level-item:not(.is-narrow){flex-grow:1}@media screen and (min-width:769px),print{.level{display:flex}.level>.level-item:not(.is-narrow){flex-grow:1}}.level-item{align-items:center;display:flex;flex-basis:auto;flex-grow:0;flex-shrink:0;justify-content:center}.level-item .subtitle,.level-item .title{margin-bottom:0}@media screen and (max-width:768px){.level-item:not(:last-child){margin-bottom:.75rem}}.level-left,.level-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.level-left .level-item.is-flexible,.level-right .level-item.is-flexible{flex-grow:1}@media screen and (min-width:769px),print{.level-left .level-item:not(:last-child),.level-right .level-item:not(:last-child){margin-right:.75rem}}.level-left{align-items:center;justify-content:flex-start}@media screen and (max-width:768px){.level-left+.level-right{margin-top:1.5rem}}@media screen and (min-width:769px),print{.level-left{display:flex}}.level-right{align-items:center;justify-content:flex-end}@media screen and (min-width:769px),print{.level-right{display:flex}}.media{align-items:flex-start;display:flex;text-align:inherit}.media .content:not(:last-child){margin-bottom:.75rem}.media .media{border-top:1px solid rgba(219,219,219,.5);display:flex;padding-top:.75rem}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:.5rem}.media .media .media{padding-top:.5rem}.media .media .media+.media{margin-top:.5rem}.media+.media{border-top:1px solid rgba(219,219,219,.5);margin-top:1rem;padding-top:1rem}.media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}.media-left,.media-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.media-left{margin-right:1rem}.media-right{margin-left:1rem}.media-content{flex-basis:auto;flex-grow:1;flex-shrink:1;text-align:inherit}@media screen and (max-width:768px){.media-content{overflow-x:auto}}.menu{font-size:1rem}.menu.is-small{font-size:.75rem}.menu.is-medium{font-size:1.25rem}.menu.is-large{font-size:1.5rem}.menu-list{line-height:1.25}.menu-list a{border-radius:2px;color:#4a4a4a;display:block;padding:.5em .75em}.menu-list a:hover{background-color:#f5f5f5;color:#363636}.menu-list a.is-active{background-color:#3273dc;color:#fff}.menu-list li ul{border-left:1px solid #dbdbdb;margin:.75em;padding-left:.75em}.menu-label{color:#7a7a7a;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}.menu-label:not(:first-child){margin-top:1em}.menu-label:not(:last-child){margin-bottom:1em}.message{background-color:#f5f5f5;border-radius:4px;font-size:1rem}.message strong{color:currentColor}.message a:not(.button):not(.tag):not(.dropdown-item){color:currentColor;text-decoration:underline}.message.is-small{font-size:.75rem}.message.is-medium{font-size:1.25rem}.message.is-large{font-size:1.5rem}.message.is-white{background-color:#fff}.message.is-white .message-header{background-color:#fff;color:#0a0a0a}.message.is-white .message-body{border-color:#fff}.message.is-black{background-color:#fafafa}.message.is-black .message-header{background-color:#0a0a0a;color:#fff}.message.is-black .message-body{border-color:#0a0a0a}.message.is-light{background-color:#fafafa}.message.is-light .message-header{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.message.is-light .message-body{border-color:#f5f5f5}.message.is-dark{background-color:#fafafa}.message.is-dark .message-header{background-color:#363636;color:#fff}.message.is-dark .message-body{border-color:#363636}.message.is-primary{background-color:#ebfffc}.message.is-primary .message-header{background-color:#00d1b2;color:#fff}.message.is-primary .message-body{border-color:#00d1b2;color:#00947e}.message.is-link{background-color:#eef3fc}.message.is-link .message-header{background-color:#3273dc;color:#fff}.message.is-link .message-body{border-color:#3273dc;color:#2160c4}.message.is-info{background-color:#eef6fc}.message.is-info .message-header{background-color:#3298dc;color:#fff}.message.is-info .message-body{border-color:#3298dc;color:#1d72aa}.message.is-success{background-color:#effaf3}.message.is-success .message-header{background-color:#48c774;color:#fff}.message.is-success .message-body{border-color:#48c774;color:#257942}.message.is-warning{background-color:#fffbeb}.message.is-warning .message-header{background-color:#ffdd57;color:rgba(0,0,0,.7)}.message.is-warning .message-body{border-color:#ffdd57;color:#947600}.message.is-danger{background-color:#feecf0}.message.is-danger .message-header{background-color:#f14668;color:#fff}.message.is-danger .message-body{border-color:#f14668;color:#cc0f35}.message-header{align-items:center;background-color:#4a4a4a;border-radius:4px 4px 0 0;color:#fff;display:flex;font-weight:700;justify-content:space-between;line-height:1.25;padding:.75em 1em;position:relative}.message-header .delete{flex-grow:0;flex-shrink:0;margin-left:.75em}.message-header+.message-body{border-width:0;border-top-left-radius:0;border-top-right-radius:0}.message-body{border-color:#dbdbdb;border-radius:4px;border-style:solid;border-width:0 0 0 4px;color:#4a4a4a;padding:1.25em 1.5em}.message-body code,.message-body pre{background-color:#fff}.message-body pre code{background-color:transparent}.modal{align-items:center;display:none;flex-direction:column;justify-content:center;overflow:hidden;position:fixed;z-index:40}.modal.is-active{display:flex}.modal-background{background-color:rgba(10,10,10,.86)}.modal-card,.modal-content{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media screen and (min-width:769px){.modal-card,.modal-content{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{background:0 0;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal-card{display:flex;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden;-ms-overflow-y:visible}.modal-card-foot,.modal-card-head{align-items:center;background-color:#f5f5f5;display:flex;flex-shrink:0;justify-content:flex-start;padding:20px;position:relative}.modal-card-head{border-bottom:1px solid #dbdbdb;border-top-left-radius:6px;border-top-right-radius:6px}.modal-card-title{color:#363636;flex-grow:1;flex-shrink:0;font-size:1.5rem;line-height:1}.modal-card-foot{border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:1px solid #dbdbdb}.modal-card-foot .button:not(:last-child){margin-right:.5em}.modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;flex-grow:1;flex-shrink:1;overflow:auto;padding:20px}.navbar{background-color:#fff;min-height:3.25rem;position:relative;z-index:30}.navbar.is-white{background-color:#fff;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link,.navbar.is-white .navbar-brand>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link.is-active,.navbar.is-white .navbar-brand .navbar-link:focus,.navbar.is-white .navbar-brand .navbar-link:hover,.navbar.is-white .navbar-brand>a.navbar-item.is-active,.navbar.is-white .navbar-brand>a.navbar-item:focus,.navbar.is-white .navbar-brand>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-burger{color:#0a0a0a}@media screen and (min-width:1024px){.navbar.is-white .navbar-end .navbar-link,.navbar.is-white .navbar-end>.navbar-item,.navbar.is-white .navbar-start .navbar-link,.navbar.is-white .navbar-start>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link.is-active,.navbar.is-white .navbar-end .navbar-link:focus,.navbar.is-white .navbar-end .navbar-link:hover,.navbar.is-white .navbar-end>a.navbar-item.is-active,.navbar.is-white .navbar-end>a.navbar-item:focus,.navbar.is-white .navbar-end>a.navbar-item:hover,.navbar.is-white .navbar-start .navbar-link.is-active,.navbar.is-white .navbar-start .navbar-link:focus,.navbar.is-white .navbar-start .navbar-link:hover,.navbar.is-white .navbar-start>a.navbar-item.is-active,.navbar.is-white .navbar-start>a.navbar-item:focus,.navbar.is-white .navbar-start>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link::after,.navbar.is-white .navbar-start .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-white .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-white .navbar-item.has-dropdown:hover .navbar-link{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}.navbar.is-black{background-color:#0a0a0a;color:#fff}.navbar.is-black .navbar-brand .navbar-link,.navbar.is-black .navbar-brand>.navbar-item{color:#fff}.navbar.is-black .navbar-brand .navbar-link.is-active,.navbar.is-black .navbar-brand .navbar-link:focus,.navbar.is-black .navbar-brand .navbar-link:hover,.navbar.is-black .navbar-brand>a.navbar-item.is-active,.navbar.is-black .navbar-brand>a.navbar-item:focus,.navbar.is-black .navbar-brand>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-black .navbar-end .navbar-link,.navbar.is-black .navbar-end>.navbar-item,.navbar.is-black .navbar-start .navbar-link,.navbar.is-black .navbar-start>.navbar-item{color:#fff}.navbar.is-black .navbar-end .navbar-link.is-active,.navbar.is-black .navbar-end .navbar-link:focus,.navbar.is-black .navbar-end .navbar-link:hover,.navbar.is-black .navbar-end>a.navbar-item.is-active,.navbar.is-black .navbar-end>a.navbar-item:focus,.navbar.is-black .navbar-end>a.navbar-item:hover,.navbar.is-black .navbar-start .navbar-link.is-active,.navbar.is-black .navbar-start .navbar-link:focus,.navbar.is-black .navbar-start .navbar-link:hover,.navbar.is-black .navbar-start>a.navbar-item.is-active,.navbar.is-black .navbar-start>a.navbar-item:focus,.navbar.is-black .navbar-start>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-end .navbar-link::after,.navbar.is-black .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-black .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-black .navbar-item.has-dropdown:hover .navbar-link{background-color:#000;color:#fff}.navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}.navbar.is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-brand .navbar-link,.navbar.is-light .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-light .navbar-brand .navbar-link.is-active,.navbar.is-light .navbar-brand .navbar-link:focus,.navbar.is-light .navbar-brand .navbar-link:hover,.navbar.is-light .navbar-brand>a.navbar-item.is-active,.navbar.is-light .navbar-brand>a.navbar-item:focus,.navbar.is-light .navbar-brand>a.navbar-item:hover{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-light .navbar-burger{color:rgba(0,0,0,.7)}@media screen and (min-width:1024px){.navbar.is-light .navbar-end .navbar-link,.navbar.is-light .navbar-end>.navbar-item,.navbar.is-light .navbar-start .navbar-link,.navbar.is-light .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-light .navbar-end .navbar-link.is-active,.navbar.is-light .navbar-end .navbar-link:focus,.navbar.is-light .navbar-end .navbar-link:hover,.navbar.is-light .navbar-end>a.navbar-item.is-active,.navbar.is-light .navbar-end>a.navbar-item:focus,.navbar.is-light .navbar-end>a.navbar-item:hover,.navbar.is-light .navbar-start .navbar-link.is-active,.navbar.is-light .navbar-start .navbar-link:focus,.navbar.is-light .navbar-start .navbar-link:hover,.navbar.is-light .navbar-start>a.navbar-item.is-active,.navbar.is-light .navbar-start>a.navbar-item:focus,.navbar.is-light .navbar-start>a.navbar-item:hover{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-end .navbar-link::after,.navbar.is-light .navbar-start .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-light .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-light .navbar-item.has-dropdown:hover .navbar-link{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:rgba(0,0,0,.7)}}.navbar.is-dark{background-color:#363636;color:#fff}.navbar.is-dark .navbar-brand .navbar-link,.navbar.is-dark .navbar-brand>.navbar-item{color:#fff}.navbar.is-dark .navbar-brand .navbar-link.is-active,.navbar.is-dark .navbar-brand .navbar-link:focus,.navbar.is-dark .navbar-brand .navbar-link:hover,.navbar.is-dark .navbar-brand>a.navbar-item.is-active,.navbar.is-dark .navbar-brand>a.navbar-item:focus,.navbar.is-dark .navbar-brand>a.navbar-item:hover{background-color:#292929;color:#fff}.navbar.is-dark .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-dark .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-dark .navbar-end .navbar-link,.navbar.is-dark .navbar-end>.navbar-item,.navbar.is-dark .navbar-start .navbar-link,.navbar.is-dark .navbar-start>.navbar-item{color:#fff}.navbar.is-dark .navbar-end .navbar-link.is-active,.navbar.is-dark .navbar-end .navbar-link:focus,.navbar.is-dark .navbar-end .navbar-link:hover,.navbar.is-dark .navbar-end>a.navbar-item.is-active,.navbar.is-dark .navbar-end>a.navbar-item:focus,.navbar.is-dark .navbar-end>a.navbar-item:hover,.navbar.is-dark .navbar-start .navbar-link.is-active,.navbar.is-dark .navbar-start .navbar-link:focus,.navbar.is-dark .navbar-start .navbar-link:hover,.navbar.is-dark .navbar-start>a.navbar-item.is-active,.navbar.is-dark .navbar-start>a.navbar-item:focus,.navbar.is-dark .navbar-start>a.navbar-item:hover{background-color:#292929;color:#fff}.navbar.is-dark .navbar-end .navbar-link::after,.navbar.is-dark .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link{background-color:#292929;color:#fff}.navbar.is-dark .navbar-dropdown a.navbar-item.is-active{background-color:#363636;color:#fff}}.navbar.is-primary{background-color:#00d1b2;color:#fff}.navbar.is-primary .navbar-brand .navbar-link,.navbar.is-primary .navbar-brand>.navbar-item{color:#fff}.navbar.is-primary .navbar-brand .navbar-link.is-active,.navbar.is-primary .navbar-brand .navbar-link:focus,.navbar.is-primary .navbar-brand .navbar-link:hover,.navbar.is-primary .navbar-brand>a.navbar-item.is-active,.navbar.is-primary .navbar-brand>a.navbar-item:focus,.navbar.is-primary .navbar-brand>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-primary .navbar-end .navbar-link,.navbar.is-primary .navbar-end>.navbar-item,.navbar.is-primary .navbar-start .navbar-link,.navbar.is-primary .navbar-start>.navbar-item{color:#fff}.navbar.is-primary .navbar-end .navbar-link.is-active,.navbar.is-primary .navbar-end .navbar-link:focus,.navbar.is-primary .navbar-end .navbar-link:hover,.navbar.is-primary .navbar-end>a.navbar-item.is-active,.navbar.is-primary .navbar-end>a.navbar-item:focus,.navbar.is-primary .navbar-end>a.navbar-item:hover,.navbar.is-primary .navbar-start .navbar-link.is-active,.navbar.is-primary .navbar-start .navbar-link:focus,.navbar.is-primary .navbar-start .navbar-link:hover,.navbar.is-primary .navbar-start>a.navbar-item.is-active,.navbar.is-primary .navbar-start>a.navbar-item:focus,.navbar.is-primary .navbar-start>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-end .navbar-link::after,.navbar.is-primary .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-dropdown a.navbar-item.is-active{background-color:#00d1b2;color:#fff}}.navbar.is-link{background-color:#3273dc;color:#fff}.navbar.is-link .navbar-brand .navbar-link,.navbar.is-link .navbar-brand>.navbar-item{color:#fff}.navbar.is-link .navbar-brand .navbar-link.is-active,.navbar.is-link .navbar-brand .navbar-link:focus,.navbar.is-link .navbar-brand .navbar-link:hover,.navbar.is-link .navbar-brand>a.navbar-item.is-active,.navbar.is-link .navbar-brand>a.navbar-item:focus,.navbar.is-link .navbar-brand>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-link .navbar-end .navbar-link,.navbar.is-link .navbar-end>.navbar-item,.navbar.is-link .navbar-start .navbar-link,.navbar.is-link .navbar-start>.navbar-item{color:#fff}.navbar.is-link .navbar-end .navbar-link.is-active,.navbar.is-link .navbar-end .navbar-link:focus,.navbar.is-link .navbar-end .navbar-link:hover,.navbar.is-link .navbar-end>a.navbar-item.is-active,.navbar.is-link .navbar-end>a.navbar-item:focus,.navbar.is-link .navbar-end>a.navbar-item:hover,.navbar.is-link .navbar-start .navbar-link.is-active,.navbar.is-link .navbar-start .navbar-link:focus,.navbar.is-link .navbar-start .navbar-link:hover,.navbar.is-link .navbar-start>a.navbar-item.is-active,.navbar.is-link .navbar-start>a.navbar-item:focus,.navbar.is-link .navbar-start>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-end .navbar-link::after,.navbar.is-link .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-link .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-link .navbar-item.has-dropdown:hover .navbar-link{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#3273dc;color:#fff}}.navbar.is-info{background-color:#3298dc;color:#fff}.navbar.is-info .navbar-brand .navbar-link,.navbar.is-info .navbar-brand>.navbar-item{color:#fff}.navbar.is-info .navbar-brand .navbar-link.is-active,.navbar.is-info .navbar-brand .navbar-link:focus,.navbar.is-info .navbar-brand .navbar-link:hover,.navbar.is-info .navbar-brand>a.navbar-item.is-active,.navbar.is-info .navbar-brand>a.navbar-item:focus,.navbar.is-info .navbar-brand>a.navbar-item:hover{background-color:#238cd1;color:#fff}.navbar.is-info .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-info .navbar-end .navbar-link,.navbar.is-info .navbar-end>.navbar-item,.navbar.is-info .navbar-start .navbar-link,.navbar.is-info .navbar-start>.navbar-item{color:#fff}.navbar.is-info .navbar-end .navbar-link.is-active,.navbar.is-info .navbar-end .navbar-link:focus,.navbar.is-info .navbar-end .navbar-link:hover,.navbar.is-info .navbar-end>a.navbar-item.is-active,.navbar.is-info .navbar-end>a.navbar-item:focus,.navbar.is-info .navbar-end>a.navbar-item:hover,.navbar.is-info .navbar-start .navbar-link.is-active,.navbar.is-info .navbar-start .navbar-link:focus,.navbar.is-info .navbar-start .navbar-link:hover,.navbar.is-info .navbar-start>a.navbar-item.is-active,.navbar.is-info .navbar-start>a.navbar-item:focus,.navbar.is-info .navbar-start>a.navbar-item:hover{background-color:#238cd1;color:#fff}.navbar.is-info .navbar-end .navbar-link::after,.navbar.is-info .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-info .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-info .navbar-item.has-dropdown:hover .navbar-link{background-color:#238cd1;color:#fff}.navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#3298dc;color:#fff}}.navbar.is-success{background-color:#48c774;color:#fff}.navbar.is-success .navbar-brand .navbar-link,.navbar.is-success .navbar-brand>.navbar-item{color:#fff}.navbar.is-success .navbar-brand .navbar-link.is-active,.navbar.is-success .navbar-brand .navbar-link:focus,.navbar.is-success .navbar-brand .navbar-link:hover,.navbar.is-success .navbar-brand>a.navbar-item.is-active,.navbar.is-success .navbar-brand>a.navbar-item:focus,.navbar.is-success .navbar-brand>a.navbar-item:hover{background-color:#3abb67;color:#fff}.navbar.is-success .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-success .navbar-end .navbar-link,.navbar.is-success .navbar-end>.navbar-item,.navbar.is-success .navbar-start .navbar-link,.navbar.is-success .navbar-start>.navbar-item{color:#fff}.navbar.is-success .navbar-end .navbar-link.is-active,.navbar.is-success .navbar-end .navbar-link:focus,.navbar.is-success .navbar-end .navbar-link:hover,.navbar.is-success .navbar-end>a.navbar-item.is-active,.navbar.is-success .navbar-end>a.navbar-item:focus,.navbar.is-success .navbar-end>a.navbar-item:hover,.navbar.is-success .navbar-start .navbar-link.is-active,.navbar.is-success .navbar-start .navbar-link:focus,.navbar.is-success .navbar-start .navbar-link:hover,.navbar.is-success .navbar-start>a.navbar-item.is-active,.navbar.is-success .navbar-start>a.navbar-item:focus,.navbar.is-success .navbar-start>a.navbar-item:hover{background-color:#3abb67;color:#fff}.navbar.is-success .navbar-end .navbar-link::after,.navbar.is-success .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-success .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-success .navbar-item.has-dropdown:hover .navbar-link{background-color:#3abb67;color:#fff}.navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#48c774;color:#fff}}.navbar.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link,.navbar.is-warning .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link.is-active,.navbar.is-warning .navbar-brand .navbar-link:focus,.navbar.is-warning .navbar-brand .navbar-link:hover,.navbar.is-warning .navbar-brand>a.navbar-item.is-active,.navbar.is-warning .navbar-brand>a.navbar-item:focus,.navbar.is-warning .navbar-brand>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-burger{color:rgba(0,0,0,.7)}@media screen and (min-width:1024px){.navbar.is-warning .navbar-end .navbar-link,.navbar.is-warning .navbar-end>.navbar-item,.navbar.is-warning .navbar-start .navbar-link,.navbar.is-warning .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link.is-active,.navbar.is-warning .navbar-end .navbar-link:focus,.navbar.is-warning .navbar-end .navbar-link:hover,.navbar.is-warning .navbar-end>a.navbar-item.is-active,.navbar.is-warning .navbar-end>a.navbar-item:focus,.navbar.is-warning .navbar-end>a.navbar-item:hover,.navbar.is-warning .navbar-start .navbar-link.is-active,.navbar.is-warning .navbar-start .navbar-link:focus,.navbar.is-warning .navbar-start .navbar-link:hover,.navbar.is-warning .navbar-start>a.navbar-item.is-active,.navbar.is-warning .navbar-start>a.navbar-item:focus,.navbar.is-warning .navbar-start>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link::after,.navbar.is-warning .navbar-start .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ffdd57;color:rgba(0,0,0,.7)}}.navbar.is-danger{background-color:#f14668;color:#fff}.navbar.is-danger .navbar-brand .navbar-link,.navbar.is-danger .navbar-brand>.navbar-item{color:#fff}.navbar.is-danger .navbar-brand .navbar-link.is-active,.navbar.is-danger .navbar-brand .navbar-link:focus,.navbar.is-danger .navbar-brand .navbar-link:hover,.navbar.is-danger .navbar-brand>a.navbar-item.is-active,.navbar.is-danger .navbar-brand>a.navbar-item:focus,.navbar.is-danger .navbar-brand>a.navbar-item:hover{background-color:#ef2e55;color:#fff}.navbar.is-danger .navbar-brand .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-burger{color:#fff}@media screen and (min-width:1024px){.navbar.is-danger .navbar-end .navbar-link,.navbar.is-danger .navbar-end>.navbar-item,.navbar.is-danger .navbar-start .navbar-link,.navbar.is-danger .navbar-start>.navbar-item{color:#fff}.navbar.is-danger .navbar-end .navbar-link.is-active,.navbar.is-danger .navbar-end .navbar-link:focus,.navbar.is-danger .navbar-end .navbar-link:hover,.navbar.is-danger .navbar-end>a.navbar-item.is-active,.navbar.is-danger .navbar-end>a.navbar-item:focus,.navbar.is-danger .navbar-end>a.navbar-item:hover,.navbar.is-danger .navbar-start .navbar-link.is-active,.navbar.is-danger .navbar-start .navbar-link:focus,.navbar.is-danger .navbar-start .navbar-link:hover,.navbar.is-danger .navbar-start>a.navbar-item.is-active,.navbar.is-danger .navbar-start>a.navbar-item:focus,.navbar.is-danger .navbar-start>a.navbar-item:hover{background-color:#ef2e55;color:#fff}.navbar.is-danger .navbar-end .navbar-link::after,.navbar.is-danger .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:focus .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link{background-color:#ef2e55;color:#fff}.navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#f14668;color:#fff}}.navbar>.container{align-items:stretch;display:flex;min-height:3.25rem;width:100%}.navbar.has-shadow{box-shadow:0 2px 0 0 #f5f5f5}.navbar.is-fixed-bottom,.navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom{bottom:0}.navbar.is-fixed-bottom.has-shadow{box-shadow:0 -2px 0 0 #f5f5f5}.navbar.is-fixed-top{top:0}body.has-navbar-fixed-top,html.has-navbar-fixed-top{padding-top:3.25rem}body.has-navbar-fixed-bottom,html.has-navbar-fixed-bottom{padding-bottom:3.25rem}.navbar-brand,.navbar-tabs{align-items:stretch;display:flex;flex-shrink:0;min-height:3.25rem}.navbar-brand a.navbar-item:focus,.navbar-brand a.navbar-item:hover{background-color:transparent}.navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}.navbar-burger{color:#4a4a4a;cursor:pointer;display:block;height:3.25rem;position:relative;width:3.25rem;margin-left:auto}.navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;transform-origin:center;transition-duration:86ms;transition-property:background-color,opacity,transform;transition-timing-function:ease-out;width:16px}.navbar-burger span:nth-child(1){top:calc(50% - 6px)}.navbar-burger span:nth-child(2){top:calc(50% - 1px)}.navbar-burger span:nth-child(3){top:calc(50% + 4px)}.navbar-burger:hover{background-color:rgba(0,0,0,.05)}.navbar-burger.is-active span:nth-child(1){transform:translateY(5px) rotate(45deg)}.navbar-burger.is-active span:nth-child(2){opacity:0}.navbar-burger.is-active span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}.navbar-menu{display:none}.navbar-item,.navbar-link{color:#4a4a4a;display:block;line-height:1.5;padding:.5rem .75rem;position:relative}.navbar-item .icon:only-child,.navbar-link .icon:only-child{margin-left:-.25rem;margin-right:-.25rem}.navbar-link,a.navbar-item{cursor:pointer}.navbar-link.is-active,.navbar-link:focus,.navbar-link:focus-within,.navbar-link:hover,a.navbar-item.is-active,a.navbar-item:focus,a.navbar-item:focus-within,a.navbar-item:hover{background-color:#fafafa;color:#3273dc}.navbar-item{flex-grow:0;flex-shrink:0}.navbar-item img{max-height:1.75rem}.navbar-item.has-dropdown{padding:0}.navbar-item.is-expanded{flex-grow:1;flex-shrink:1}.navbar-item.is-tab{border-bottom:1px solid transparent;min-height:3.25rem;padding-bottom:calc(.5rem - 1px)}.navbar-item.is-tab:focus,.navbar-item.is-tab:hover{background-color:transparent;border-bottom-color:#3273dc}.navbar-item.is-tab.is-active{background-color:transparent;border-bottom-color:#3273dc;border-bottom-style:solid;border-bottom-width:3px;color:#3273dc;padding-bottom:calc(.5rem - 3px)}.navbar-content{flex-grow:1;flex-shrink:1}.navbar-link:not(.is-arrowless){padding-right:2.5em}.navbar-link:not(.is-arrowless)::after{border-color:#3273dc;margin-top:-.375em;right:1.125em}.navbar-dropdown{font-size:.875rem;padding-bottom:.5rem;padding-top:.5rem}.navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}.navbar-divider{background-color:#f5f5f5;border:none;display:none;height:2px;margin:.5rem 0}@media screen and (max-width:1023px){.navbar>.container{display:block}.navbar-brand .navbar-item,.navbar-tabs .navbar-item{align-items:center;display:flex}.navbar-link::after{display:none}.navbar-menu{background-color:#fff;box-shadow:0 8px 16px rgba(10,10,10,.1);padding:.5rem 0}.navbar-menu.is-active{display:block}.navbar.is-fixed-bottom-touch,.navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-touch{bottom:0}.navbar.is-fixed-bottom-touch.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-touch{top:0}.navbar.is-fixed-top .navbar-menu,.navbar.is-fixed-top-touch .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 3.25rem);overflow:auto}body.has-navbar-fixed-top-touch,html.has-navbar-fixed-top-touch{padding-top:3.25rem}body.has-navbar-fixed-bottom-touch,html.has-navbar-fixed-bottom-touch{padding-bottom:3.25rem}}@media screen and (min-width:1024px){.navbar,.navbar-end,.navbar-menu,.navbar-start{align-items:stretch;display:flex}.navbar{min-height:3.25rem}.navbar.is-spaced{padding:1rem 2rem}.navbar.is-spaced .navbar-end,.navbar.is-spaced .navbar-start{align-items:center}.navbar.is-spaced .navbar-link,.navbar.is-spaced a.navbar-item{border-radius:4px}.navbar.is-transparent .navbar-link.is-active,.navbar.is-transparent .navbar-link:focus,.navbar.is-transparent .navbar-link:hover,.navbar.is-transparent a.navbar-item.is-active,.navbar.is-transparent a.navbar-item:focus,.navbar.is-transparent a.navbar-item:hover{background-color:transparent!important}.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:focus-within .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link{background-color:transparent!important}.navbar.is-transparent .navbar-dropdown a.navbar-item:focus,.navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-burger{display:none}.navbar-item,.navbar-link{align-items:center;display:flex}.navbar-item.has-dropdown{align-items:stretch}.navbar-item.has-dropdown-up .navbar-link::after{transform:rotate(135deg) translate(.25em,-.25em)}.navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:2px solid #dbdbdb;border-radius:6px 6px 0 0;border-top:none;bottom:100%;box-shadow:0 -8px 8px rgba(10,10,10,.1);top:auto}.navbar-item.is-active .navbar-dropdown,.navbar-item.is-hoverable:focus .navbar-dropdown,.navbar-item.is-hoverable:focus-within .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar-item.is-active .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:focus .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:focus-within .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-active .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:focus .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:focus-within .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown{opacity:1;pointer-events:auto;transform:translateY(0)}.navbar-menu{flex-grow:1;flex-shrink:0}.navbar-start{justify-content:flex-start;margin-right:auto}.navbar-end{justify-content:flex-end;margin-left:auto}.navbar-dropdown{background-color:#fff;border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:2px solid #dbdbdb;box-shadow:0 8px 8px rgba(10,10,10,.1);display:none;font-size:.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}.navbar-dropdown .navbar-item{padding:.375rem 1rem;white-space:nowrap}.navbar-dropdown a.navbar-item{padding-right:3rem}.navbar-dropdown a.navbar-item:focus,.navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-dropdown{border-radius:6px;border-top:none;box-shadow:0 8px 8px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);display:block;opacity:0;pointer-events:none;top:calc(100% + (-4px));transform:translateY(-5px);transition-duration:86ms;transition-property:opacity,transform}.navbar-dropdown.is-right{left:auto;right:0}.navbar-divider{display:block}.container>.navbar .navbar-brand,.navbar>.container .navbar-brand{margin-left:-.75rem}.container>.navbar .navbar-menu,.navbar>.container .navbar-menu{margin-right:-.75rem}.navbar.is-fixed-bottom-desktop,.navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-desktop{bottom:0}.navbar.is-fixed-bottom-desktop.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-desktop{top:0}body.has-navbar-fixed-top-desktop,html.has-navbar-fixed-top-desktop{padding-top:3.25rem}body.has-navbar-fixed-bottom-desktop,html.has-navbar-fixed-bottom-desktop{padding-bottom:3.25rem}body.has-spaced-navbar-fixed-top,html.has-spaced-navbar-fixed-top{padding-top:5.25rem}body.has-spaced-navbar-fixed-bottom,html.has-spaced-navbar-fixed-bottom{padding-bottom:5.25rem}.navbar-link.is-active,a.navbar-item.is-active{color:#0a0a0a}.navbar-link.is-active:not(:focus):not(:hover),a.navbar-item.is-active:not(:focus):not(:hover){background-color:transparent}.navbar-item.has-dropdown.is-active .navbar-link,.navbar-item.has-dropdown:focus .navbar-link,.navbar-item.has-dropdown:hover .navbar-link{background-color:#fafafa}}.hero.is-fullheight-with-navbar{min-height:calc(100vh - 3.25rem)}.pagination{font-size:1rem;margin:-.25rem}.pagination.is-small{font-size:.75rem}.pagination.is-medium{font-size:1.25rem}.pagination.is-large{font-size:1.5rem}.pagination.is-rounded .pagination-next,.pagination.is-rounded .pagination-previous{padding-left:1em;padding-right:1em;border-radius:290486px}.pagination.is-rounded .pagination-link{border-radius:290486px}.pagination,.pagination-list{align-items:center;display:flex;justify-content:center;text-align:center}.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous{font-size:1em;justify-content:center;margin:.25rem;padding-left:.5em;padding-right:.5em;text-align:center}.pagination-link,.pagination-next,.pagination-previous{border-color:#dbdbdb;color:#363636;min-width:2.5em}.pagination-link:hover,.pagination-next:hover,.pagination-previous:hover{border-color:#b5b5b5;color:#363636}.pagination-link:focus,.pagination-next:focus,.pagination-previous:focus{border-color:#3273dc}.pagination-link:active,.pagination-next:active,.pagination-previous:active{box-shadow:inset 0 1px 2px rgba(10,10,10,.2)}.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled]{background-color:#dbdbdb;border-color:#dbdbdb;box-shadow:none;color:#7a7a7a;opacity:.5}.pagination-next,.pagination-previous{padding-left:.75em;padding-right:.75em;white-space:nowrap}.pagination-link.is-current{background-color:#3273dc;border-color:#3273dc;color:#fff}.pagination-ellipsis{color:#b5b5b5;pointer-events:none}.pagination-list{flex-wrap:wrap}@media screen and (max-width:768px){.pagination{flex-wrap:wrap}.pagination-next,.pagination-previous{flex-grow:1;flex-shrink:1}.pagination-list li{flex-grow:1;flex-shrink:1}}@media screen and (min-width:769px),print{.pagination-list{flex-grow:1;flex-shrink:1;justify-content:flex-start;order:1}.pagination-previous{order:2}.pagination-next{order:3}.pagination{justify-content:space-between}.pagination.is-centered .pagination-previous{order:1}.pagination.is-centered .pagination-list{justify-content:center;order:2}.pagination.is-centered .pagination-next{order:3}.pagination.is-right .pagination-previous{order:1}.pagination.is-right .pagination-next{order:2}.pagination.is-right .pagination-list{justify-content:flex-end;order:3}}.panel{border-radius:6px;box-shadow:0 .5em 1em -.125em rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.02);font-size:1rem}.panel:not(:last-child){margin-bottom:1.5rem}.panel.is-white .panel-heading{background-color:#fff;color:#0a0a0a}.panel.is-white .panel-tabs a.is-active{border-bottom-color:#fff}.panel.is-white .panel-block.is-active .panel-icon{color:#fff}.panel.is-black .panel-heading{background-color:#0a0a0a;color:#fff}.panel.is-black .panel-tabs a.is-active{border-bottom-color:#0a0a0a}.panel.is-black .panel-block.is-active .panel-icon{color:#0a0a0a}.panel.is-light .panel-heading{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.panel.is-light .panel-tabs a.is-active{border-bottom-color:#f5f5f5}.panel.is-light .panel-block.is-active .panel-icon{color:#f5f5f5}.panel.is-dark .panel-heading{background-color:#363636;color:#fff}.panel.is-dark .panel-tabs a.is-active{border-bottom-color:#363636}.panel.is-dark .panel-block.is-active .panel-icon{color:#363636}.panel.is-primary .panel-heading{background-color:#00d1b2;color:#fff}.panel.is-primary .panel-tabs a.is-active{border-bottom-color:#00d1b2}.panel.is-primary .panel-block.is-active .panel-icon{color:#00d1b2}.panel.is-link .panel-heading{background-color:#3273dc;color:#fff}.panel.is-link .panel-tabs a.is-active{border-bottom-color:#3273dc}.panel.is-link .panel-block.is-active .panel-icon{color:#3273dc}.panel.is-info .panel-heading{background-color:#3298dc;color:#fff}.panel.is-info .panel-tabs a.is-active{border-bottom-color:#3298dc}.panel.is-info .panel-block.is-active .panel-icon{color:#3298dc}.panel.is-success .panel-heading{background-color:#48c774;color:#fff}.panel.is-success .panel-tabs a.is-active{border-bottom-color:#48c774}.panel.is-success .panel-block.is-active .panel-icon{color:#48c774}.panel.is-warning .panel-heading{background-color:#ffdd57;color:rgba(0,0,0,.7)}.panel.is-warning .panel-tabs a.is-active{border-bottom-color:#ffdd57}.panel.is-warning .panel-block.is-active .panel-icon{color:#ffdd57}.panel.is-danger .panel-heading{background-color:#f14668;color:#fff}.panel.is-danger .panel-tabs a.is-active{border-bottom-color:#f14668}.panel.is-danger .panel-block.is-active .panel-icon{color:#f14668}.panel-block:not(:last-child),.panel-tabs:not(:last-child){border-bottom:1px solid #ededed}.panel-heading{background-color:#ededed;border-radius:6px 6px 0 0;color:#363636;font-size:1.25em;font-weight:700;line-height:1.25;padding:.75em 1em}.panel-tabs{align-items:flex-end;display:flex;font-size:.875em;justify-content:center}.panel-tabs a{border-bottom:1px solid #dbdbdb;margin-bottom:-1px;padding:.5em}.panel-tabs a.is-active{border-bottom-color:#4a4a4a;color:#363636}.panel-list a{color:#4a4a4a}.panel-list a:hover{color:#3273dc}.panel-block{align-items:center;color:#363636;display:flex;justify-content:flex-start;padding:.5em .75em}.panel-block input[type=checkbox]{margin-right:.75em}.panel-block>.control{flex-grow:1;flex-shrink:1;width:100%}.panel-block.is-wrapped{flex-wrap:wrap}.panel-block.is-active{border-left-color:#3273dc;color:#363636}.panel-block.is-active .panel-icon{color:#3273dc}.panel-block:last-child{border-bottom-left-radius:6px;border-bottom-right-radius:6px}a.panel-block,label.panel-block{cursor:pointer}a.panel-block:hover,label.panel-block:hover{background-color:#f5f5f5}.panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#7a7a7a;margin-right:.75em}.panel-icon .fa{font-size:inherit;line-height:inherit}.tabs{-webkit-overflow-scrolling:touch;align-items:stretch;display:flex;font-size:1rem;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs a{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;color:#4a4a4a;display:flex;justify-content:center;margin-bottom:-1px;padding:.5em 1em;vertical-align:top}.tabs a:hover{border-bottom-color:#363636;color:#363636}.tabs li{display:block}.tabs li.is-active a{border-bottom-color:#3273dc;color:#3273dc}.tabs ul{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;display:flex;flex-grow:1;flex-shrink:0;justify-content:flex-start}.tabs ul.is-left{padding-right:.75em}.tabs ul.is-center{flex:none;justify-content:center;padding-left:.75em;padding-right:.75em}.tabs ul.is-right{justify-content:flex-end;padding-left:.75em}.tabs .icon:first-child{margin-right:.5em}.tabs .icon:last-child{margin-left:.5em}.tabs.is-centered ul{justify-content:center}.tabs.is-right ul{justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:4px 4px 0 0}.tabs.is-boxed a:hover{background-color:#f5f5f5;border-bottom-color:#dbdbdb}.tabs.is-boxed li.is-active a{background-color:#fff;border-color:#dbdbdb;border-bottom-color:transparent!important}.tabs.is-fullwidth li{flex-grow:1;flex-shrink:0}.tabs.is-toggle a{border-color:#dbdbdb;border-style:solid;border-width:1px;margin-bottom:0;position:relative}.tabs.is-toggle a:hover{background-color:#f5f5f5;border-color:#b5b5b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-top-left-radius:4px;border-bottom-left-radius:4px}.tabs.is-toggle li:last-child a{border-top-right-radius:4px;border-bottom-right-radius:4px}.tabs.is-toggle li.is-active a{background-color:#3273dc;border-color:#3273dc;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}.tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:290486px;border-top-left-radius:290486px;padding-left:1.25em}.tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:290486px;border-top-right-radius:290486px;padding-right:1.25em}.tabs.is-small{font-size:.75rem}.tabs.is-medium{font-size:1.25rem}.tabs.is-large{font-size:1.5rem}.column{display:block;flex-basis:0;flex-grow:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>.column.is-narrow{flex:none}.columns.is-mobile>.column.is-full{flex:none;width:100%}.columns.is-mobile>.column.is-three-quarters{flex:none;width:75%}.columns.is-mobile>.column.is-two-thirds{flex:none;width:66.6666%}.columns.is-mobile>.column.is-half{flex:none;width:50%}.columns.is-mobile>.column.is-one-third{flex:none;width:33.3333%}.columns.is-mobile>.column.is-one-quarter{flex:none;width:25%}.columns.is-mobile>.column.is-one-fifth{flex:none;width:20%}.columns.is-mobile>.column.is-two-fifths{flex:none;width:40%}.columns.is-mobile>.column.is-three-fifths{flex:none;width:60%}.columns.is-mobile>.column.is-four-fifths{flex:none;width:80%}.columns.is-mobile>.column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>.column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>.column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>.column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>.column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>.column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>.column.is-0{flex:none;width:0%}.columns.is-mobile>.column.is-offset-0{margin-left:0}.columns.is-mobile>.column.is-1{flex:none;width:8.33333%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333%}.columns.is-mobile>.column.is-2{flex:none;width:16.66667%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66667%}.columns.is-mobile>.column.is-3{flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{flex:none;width:33.33333%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333%}.columns.is-mobile>.column.is-5{flex:none;width:41.66667%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66667%}.columns.is-mobile>.column.is-6{flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{flex:none;width:58.33333%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333%}.columns.is-mobile>.column.is-8{flex:none;width:66.66667%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66667%}.columns.is-mobile>.column.is-9{flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{flex:none;width:83.33333%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333%}.columns.is-mobile>.column.is-11{flex:none;width:91.66667%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66667%}.columns.is-mobile>.column.is-12{flex:none;width:100%}.columns.is-mobile>.column.is-offset-12{margin-left:100%}@media screen and (max-width:768px){.column.is-narrow-mobile{flex:none}.column.is-full-mobile{flex:none;width:100%}.column.is-three-quarters-mobile{flex:none;width:75%}.column.is-two-thirds-mobile{flex:none;width:66.6666%}.column.is-half-mobile{flex:none;width:50%}.column.is-one-third-mobile{flex:none;width:33.3333%}.column.is-one-quarter-mobile{flex:none;width:25%}.column.is-one-fifth-mobile{flex:none;width:20%}.column.is-two-fifths-mobile{flex:none;width:40%}.column.is-three-fifths-mobile{flex:none;width:60%}.column.is-four-fifths-mobile{flex:none;width:80%}.column.is-offset-three-quarters-mobile{margin-left:75%}.column.is-offset-two-thirds-mobile{margin-left:66.6666%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-one-third-mobile{margin-left:33.3333%}.column.is-offset-one-quarter-mobile{margin-left:25%}.column.is-offset-one-fifth-mobile{margin-left:20%}.column.is-offset-two-fifths-mobile{margin-left:40%}.column.is-offset-three-fifths-mobile{margin-left:60%}.column.is-offset-four-fifths-mobile{margin-left:80%}.column.is-0-mobile{flex:none;width:0%}.column.is-offset-0-mobile{margin-left:0}.column.is-1-mobile{flex:none;width:8.33333%}.column.is-offset-1-mobile{margin-left:8.33333%}.column.is-2-mobile{flex:none;width:16.66667%}.column.is-offset-2-mobile{margin-left:16.66667%}.column.is-3-mobile{flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{flex:none;width:33.33333%}.column.is-offset-4-mobile{margin-left:33.33333%}.column.is-5-mobile{flex:none;width:41.66667%}.column.is-offset-5-mobile{margin-left:41.66667%}.column.is-6-mobile{flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{flex:none;width:58.33333%}.column.is-offset-7-mobile{margin-left:58.33333%}.column.is-8-mobile{flex:none;width:66.66667%}.column.is-offset-8-mobile{margin-left:66.66667%}.column.is-9-mobile{flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{flex:none;width:83.33333%}.column.is-offset-10-mobile{margin-left:83.33333%}.column.is-11-mobile{flex:none;width:91.66667%}.column.is-offset-11-mobile{margin-left:91.66667%}.column.is-12-mobile{flex:none;width:100%}.column.is-offset-12-mobile{margin-left:100%}}@media screen and (min-width:769px),print{.column.is-narrow,.column.is-narrow-tablet{flex:none}.column.is-full,.column.is-full-tablet{flex:none;width:100%}.column.is-three-quarters,.column.is-three-quarters-tablet{flex:none;width:75%}.column.is-two-thirds,.column.is-two-thirds-tablet{flex:none;width:66.6666%}.column.is-half,.column.is-half-tablet{flex:none;width:50%}.column.is-one-third,.column.is-one-third-tablet{flex:none;width:33.3333%}.column.is-one-quarter,.column.is-one-quarter-tablet{flex:none;width:25%}.column.is-one-fifth,.column.is-one-fifth-tablet{flex:none;width:20%}.column.is-two-fifths,.column.is-two-fifths-tablet{flex:none;width:40%}.column.is-three-fifths,.column.is-three-fifths-tablet{flex:none;width:60%}.column.is-four-fifths,.column.is-four-fifths-tablet{flex:none;width:80%}.column.is-offset-three-quarters,.column.is-offset-three-quarters-tablet{margin-left:75%}.column.is-offset-two-thirds,.column.is-offset-two-thirds-tablet{margin-left:66.6666%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-one-third,.column.is-offset-one-third-tablet{margin-left:33.3333%}.column.is-offset-one-quarter,.column.is-offset-one-quarter-tablet{margin-left:25%}.column.is-offset-one-fifth,.column.is-offset-one-fifth-tablet{margin-left:20%}.column.is-offset-two-fifths,.column.is-offset-two-fifths-tablet{margin-left:40%}.column.is-offset-three-fifths,.column.is-offset-three-fifths-tablet{margin-left:60%}.column.is-offset-four-fifths,.column.is-offset-four-fifths-tablet{margin-left:80%}.column.is-0,.column.is-0-tablet{flex:none;width:0%}.column.is-offset-0,.column.is-offset-0-tablet{margin-left:0}.column.is-1,.column.is-1-tablet{flex:none;width:8.33333%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333%}.column.is-2,.column.is-2-tablet{flex:none;width:16.66667%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66667%}.column.is-3,.column.is-3-tablet{flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{flex:none;width:33.33333%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333%}.column.is-5,.column.is-5-tablet{flex:none;width:41.66667%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66667%}.column.is-6,.column.is-6-tablet{flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{flex:none;width:58.33333%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333%}.column.is-8,.column.is-8-tablet{flex:none;width:66.66667%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66667%}.column.is-9,.column.is-9-tablet{flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{flex:none;width:83.33333%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333%}.column.is-11,.column.is-11-tablet{flex:none;width:91.66667%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66667%}.column.is-12,.column.is-12-tablet{flex:none;width:100%}.column.is-offset-12,.column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width:1023px){.column.is-narrow-touch{flex:none}.column.is-full-touch{flex:none;width:100%}.column.is-three-quarters-touch{flex:none;width:75%}.column.is-two-thirds-touch{flex:none;width:66.6666%}.column.is-half-touch{flex:none;width:50%}.column.is-one-third-touch{flex:none;width:33.3333%}.column.is-one-quarter-touch{flex:none;width:25%}.column.is-one-fifth-touch{flex:none;width:20%}.column.is-two-fifths-touch{flex:none;width:40%}.column.is-three-fifths-touch{flex:none;width:60%}.column.is-four-fifths-touch{flex:none;width:80%}.column.is-offset-three-quarters-touch{margin-left:75%}.column.is-offset-two-thirds-touch{margin-left:66.6666%}.column.is-offset-half-touch{margin-left:50%}.column.is-offset-one-third-touch{margin-left:33.3333%}.column.is-offset-one-quarter-touch{margin-left:25%}.column.is-offset-one-fifth-touch{margin-left:20%}.column.is-offset-two-fifths-touch{margin-left:40%}.column.is-offset-three-fifths-touch{margin-left:60%}.column.is-offset-four-fifths-touch{margin-left:80%}.column.is-0-touch{flex:none;width:0%}.column.is-offset-0-touch{margin-left:0}.column.is-1-touch{flex:none;width:8.33333%}.column.is-offset-1-touch{margin-left:8.33333%}.column.is-2-touch{flex:none;width:16.66667%}.column.is-offset-2-touch{margin-left:16.66667%}.column.is-3-touch{flex:none;width:25%}.column.is-offset-3-touch{margin-left:25%}.column.is-4-touch{flex:none;width:33.33333%}.column.is-offset-4-touch{margin-left:33.33333%}.column.is-5-touch{flex:none;width:41.66667%}.column.is-offset-5-touch{margin-left:41.66667%}.column.is-6-touch{flex:none;width:50%}.column.is-offset-6-touch{margin-left:50%}.column.is-7-touch{flex:none;width:58.33333%}.column.is-offset-7-touch{margin-left:58.33333%}.column.is-8-touch{flex:none;width:66.66667%}.column.is-offset-8-touch{margin-left:66.66667%}.column.is-9-touch{flex:none;width:75%}.column.is-offset-9-touch{margin-left:75%}.column.is-10-touch{flex:none;width:83.33333%}.column.is-offset-10-touch{margin-left:83.33333%}.column.is-11-touch{flex:none;width:91.66667%}.column.is-offset-11-touch{margin-left:91.66667%}.column.is-12-touch{flex:none;width:100%}.column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width:1024px){.column.is-narrow-desktop{flex:none}.column.is-full-desktop{flex:none;width:100%}.column.is-three-quarters-desktop{flex:none;width:75%}.column.is-two-thirds-desktop{flex:none;width:66.6666%}.column.is-half-desktop{flex:none;width:50%}.column.is-one-third-desktop{flex:none;width:33.3333%}.column.is-one-quarter-desktop{flex:none;width:25%}.column.is-one-fifth-desktop{flex:none;width:20%}.column.is-two-fifths-desktop{flex:none;width:40%}.column.is-three-fifths-desktop{flex:none;width:60%}.column.is-four-fifths-desktop{flex:none;width:80%}.column.is-offset-three-quarters-desktop{margin-left:75%}.column.is-offset-two-thirds-desktop{margin-left:66.6666%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-one-third-desktop{margin-left:33.3333%}.column.is-offset-one-quarter-desktop{margin-left:25%}.column.is-offset-one-fifth-desktop{margin-left:20%}.column.is-offset-two-fifths-desktop{margin-left:40%}.column.is-offset-three-fifths-desktop{margin-left:60%}.column.is-offset-four-fifths-desktop{margin-left:80%}.column.is-0-desktop{flex:none;width:0%}.column.is-offset-0-desktop{margin-left:0}.column.is-1-desktop{flex:none;width:8.33333%}.column.is-offset-1-desktop{margin-left:8.33333%}.column.is-2-desktop{flex:none;width:16.66667%}.column.is-offset-2-desktop{margin-left:16.66667%}.column.is-3-desktop{flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{flex:none;width:33.33333%}.column.is-offset-4-desktop{margin-left:33.33333%}.column.is-5-desktop{flex:none;width:41.66667%}.column.is-offset-5-desktop{margin-left:41.66667%}.column.is-6-desktop{flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{flex:none;width:58.33333%}.column.is-offset-7-desktop{margin-left:58.33333%}.column.is-8-desktop{flex:none;width:66.66667%}.column.is-offset-8-desktop{margin-left:66.66667%}.column.is-9-desktop{flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{flex:none;width:83.33333%}.column.is-offset-10-desktop{margin-left:83.33333%}.column.is-11-desktop{flex:none;width:91.66667%}.column.is-offset-11-desktop{margin-left:91.66667%}.column.is-12-desktop{flex:none;width:100%}.column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width:1216px){.column.is-narrow-widescreen{flex:none}.column.is-full-widescreen{flex:none;width:100%}.column.is-three-quarters-widescreen{flex:none;width:75%}.column.is-two-thirds-widescreen{flex:none;width:66.6666%}.column.is-half-widescreen{flex:none;width:50%}.column.is-one-third-widescreen{flex:none;width:33.3333%}.column.is-one-quarter-widescreen{flex:none;width:25%}.column.is-one-fifth-widescreen{flex:none;width:20%}.column.is-two-fifths-widescreen{flex:none;width:40%}.column.is-three-fifths-widescreen{flex:none;width:60%}.column.is-four-fifths-widescreen{flex:none;width:80%}.column.is-offset-three-quarters-widescreen{margin-left:75%}.column.is-offset-two-thirds-widescreen{margin-left:66.6666%}.column.is-offset-half-widescreen{margin-left:50%}.column.is-offset-one-third-widescreen{margin-left:33.3333%}.column.is-offset-one-quarter-widescreen{margin-left:25%}.column.is-offset-one-fifth-widescreen{margin-left:20%}.column.is-offset-two-fifths-widescreen{margin-left:40%}.column.is-offset-three-fifths-widescreen{margin-left:60%}.column.is-offset-four-fifths-widescreen{margin-left:80%}.column.is-0-widescreen{flex:none;width:0%}.column.is-offset-0-widescreen{margin-left:0}.column.is-1-widescreen{flex:none;width:8.33333%}.column.is-offset-1-widescreen{margin-left:8.33333%}.column.is-2-widescreen{flex:none;width:16.66667%}.column.is-offset-2-widescreen{margin-left:16.66667%}.column.is-3-widescreen{flex:none;width:25%}.column.is-offset-3-widescreen{margin-left:25%}.column.is-4-widescreen{flex:none;width:33.33333%}.column.is-offset-4-widescreen{margin-left:33.33333%}.column.is-5-widescreen{flex:none;width:41.66667%}.column.is-offset-5-widescreen{margin-left:41.66667%}.column.is-6-widescreen{flex:none;width:50%}.column.is-offset-6-widescreen{margin-left:50%}.column.is-7-widescreen{flex:none;width:58.33333%}.column.is-offset-7-widescreen{margin-left:58.33333%}.column.is-8-widescreen{flex:none;width:66.66667%}.column.is-offset-8-widescreen{margin-left:66.66667%}.column.is-9-widescreen{flex:none;width:75%}.column.is-offset-9-widescreen{margin-left:75%}.column.is-10-widescreen{flex:none;width:83.33333%}.column.is-offset-10-widescreen{margin-left:83.33333%}.column.is-11-widescreen{flex:none;width:91.66667%}.column.is-offset-11-widescreen{margin-left:91.66667%}.column.is-12-widescreen{flex:none;width:100%}.column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width:1408px){.column.is-narrow-fullhd{flex:none}.column.is-full-fullhd{flex:none;width:100%}.column.is-three-quarters-fullhd{flex:none;width:75%}.column.is-two-thirds-fullhd{flex:none;width:66.6666%}.column.is-half-fullhd{flex:none;width:50%}.column.is-one-third-fullhd{flex:none;width:33.3333%}.column.is-one-quarter-fullhd{flex:none;width:25%}.column.is-one-fifth-fullhd{flex:none;width:20%}.column.is-two-fifths-fullhd{flex:none;width:40%}.column.is-three-fifths-fullhd{flex:none;width:60%}.column.is-four-fifths-fullhd{flex:none;width:80%}.column.is-offset-three-quarters-fullhd{margin-left:75%}.column.is-offset-two-thirds-fullhd{margin-left:66.6666%}.column.is-offset-half-fullhd{margin-left:50%}.column.is-offset-one-third-fullhd{margin-left:33.3333%}.column.is-offset-one-quarter-fullhd{margin-left:25%}.column.is-offset-one-fifth-fullhd{margin-left:20%}.column.is-offset-two-fifths-fullhd{margin-left:40%}.column.is-offset-three-fifths-fullhd{margin-left:60%}.column.is-offset-four-fifths-fullhd{margin-left:80%}.column.is-0-fullhd{flex:none;width:0%}.column.is-offset-0-fullhd{margin-left:0}.column.is-1-fullhd{flex:none;width:8.33333%}.column.is-offset-1-fullhd{margin-left:8.33333%}.column.is-2-fullhd{flex:none;width:16.66667%}.column.is-offset-2-fullhd{margin-left:16.66667%}.column.is-3-fullhd{flex:none;width:25%}.column.is-offset-3-fullhd{margin-left:25%}.column.is-4-fullhd{flex:none;width:33.33333%}.column.is-offset-4-fullhd{margin-left:33.33333%}.column.is-5-fullhd{flex:none;width:41.66667%}.column.is-offset-5-fullhd{margin-left:41.66667%}.column.is-6-fullhd{flex:none;width:50%}.column.is-offset-6-fullhd{margin-left:50%}.column.is-7-fullhd{flex:none;width:58.33333%}.column.is-offset-7-fullhd{margin-left:58.33333%}.column.is-8-fullhd{flex:none;width:66.66667%}.column.is-offset-8-fullhd{margin-left:66.66667%}.column.is-9-fullhd{flex:none;width:75%}.column.is-offset-9-fullhd{margin-left:75%}.column.is-10-fullhd{flex:none;width:83.33333%}.column.is-offset-10-fullhd{margin-left:83.33333%}.column.is-11-fullhd{flex:none;width:91.66667%}.column.is-offset-11-fullhd{margin-left:91.66667%}.column.is-12-fullhd{flex:none;width:100%}.column.is-offset-12-fullhd{margin-left:100%}}.columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.columns:last-child{margin-bottom:-.75rem}.columns:not(:last-child){margin-bottom:calc(1.5rem - .75rem)}.columns.is-centered{justify-content:center}.columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}.columns.is-gapless>.column{margin:0;padding:0!important}.columns.is-gapless:not(:last-child){margin-bottom:1.5rem}.columns.is-gapless:last-child{margin-bottom:0}.columns.is-mobile{display:flex}.columns.is-multiline{flex-wrap:wrap}.columns.is-vcentered{align-items:center}@media screen and (min-width:769px),print{.columns:not(.is-desktop){display:flex}}@media screen and (min-width:1024px){.columns.is-desktop{display:flex}}.columns.is-variable{--columnGap:0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}.columns.is-variable .column{padding-left:var(--columnGap);padding-right:var(--columnGap)}.columns.is-variable.is-0{--columnGap:0rem}@media screen and (max-width:768px){.columns.is-variable.is-0-mobile{--columnGap:0rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-0-tablet{--columnGap:0rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-0-tablet-only{--columnGap:0rem}}@media screen and (max-width:1023px){.columns.is-variable.is-0-touch{--columnGap:0rem}}@media screen and (min-width:1024px){.columns.is-variable.is-0-desktop{--columnGap:0rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-0-desktop-only{--columnGap:0rem}}@media screen and (min-width:1216px){.columns.is-variable.is-0-widescreen{--columnGap:0rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-0-widescreen-only{--columnGap:0rem}}@media screen and (min-width:1408px){.columns.is-variable.is-0-fullhd{--columnGap:0rem}}.columns.is-variable.is-1{--columnGap:0.25rem}@media screen and (max-width:768px){.columns.is-variable.is-1-mobile{--columnGap:0.25rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-1-tablet{--columnGap:0.25rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-1-tablet-only{--columnGap:0.25rem}}@media screen and (max-width:1023px){.columns.is-variable.is-1-touch{--columnGap:0.25rem}}@media screen and (min-width:1024px){.columns.is-variable.is-1-desktop{--columnGap:0.25rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-1-desktop-only{--columnGap:0.25rem}}@media screen and (min-width:1216px){.columns.is-variable.is-1-widescreen{--columnGap:0.25rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-1-widescreen-only{--columnGap:0.25rem}}@media screen and (min-width:1408px){.columns.is-variable.is-1-fullhd{--columnGap:0.25rem}}.columns.is-variable.is-2{--columnGap:0.5rem}@media screen and (max-width:768px){.columns.is-variable.is-2-mobile{--columnGap:0.5rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-2-tablet{--columnGap:0.5rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-2-tablet-only{--columnGap:0.5rem}}@media screen and (max-width:1023px){.columns.is-variable.is-2-touch{--columnGap:0.5rem}}@media screen and (min-width:1024px){.columns.is-variable.is-2-desktop{--columnGap:0.5rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-2-desktop-only{--columnGap:0.5rem}}@media screen and (min-width:1216px){.columns.is-variable.is-2-widescreen{--columnGap:0.5rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-2-widescreen-only{--columnGap:0.5rem}}@media screen and (min-width:1408px){.columns.is-variable.is-2-fullhd{--columnGap:0.5rem}}.columns.is-variable.is-3{--columnGap:0.75rem}@media screen and (max-width:768px){.columns.is-variable.is-3-mobile{--columnGap:0.75rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-3-tablet{--columnGap:0.75rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-3-tablet-only{--columnGap:0.75rem}}@media screen and (max-width:1023px){.columns.is-variable.is-3-touch{--columnGap:0.75rem}}@media screen and (min-width:1024px){.columns.is-variable.is-3-desktop{--columnGap:0.75rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-3-desktop-only{--columnGap:0.75rem}}@media screen and (min-width:1216px){.columns.is-variable.is-3-widescreen{--columnGap:0.75rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-3-widescreen-only{--columnGap:0.75rem}}@media screen and (min-width:1408px){.columns.is-variable.is-3-fullhd{--columnGap:0.75rem}}.columns.is-variable.is-4{--columnGap:1rem}@media screen and (max-width:768px){.columns.is-variable.is-4-mobile{--columnGap:1rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-4-tablet{--columnGap:1rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-4-tablet-only{--columnGap:1rem}}@media screen and (max-width:1023px){.columns.is-variable.is-4-touch{--columnGap:1rem}}@media screen and (min-width:1024px){.columns.is-variable.is-4-desktop{--columnGap:1rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-4-desktop-only{--columnGap:1rem}}@media screen and (min-width:1216px){.columns.is-variable.is-4-widescreen{--columnGap:1rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-4-widescreen-only{--columnGap:1rem}}@media screen and (min-width:1408px){.columns.is-variable.is-4-fullhd{--columnGap:1rem}}.columns.is-variable.is-5{--columnGap:1.25rem}@media screen and (max-width:768px){.columns.is-variable.is-5-mobile{--columnGap:1.25rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-5-tablet{--columnGap:1.25rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-5-tablet-only{--columnGap:1.25rem}}@media screen and (max-width:1023px){.columns.is-variable.is-5-touch{--columnGap:1.25rem}}@media screen and (min-width:1024px){.columns.is-variable.is-5-desktop{--columnGap:1.25rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-5-desktop-only{--columnGap:1.25rem}}@media screen and (min-width:1216px){.columns.is-variable.is-5-widescreen{--columnGap:1.25rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-5-widescreen-only{--columnGap:1.25rem}}@media screen and (min-width:1408px){.columns.is-variable.is-5-fullhd{--columnGap:1.25rem}}.columns.is-variable.is-6{--columnGap:1.5rem}@media screen and (max-width:768px){.columns.is-variable.is-6-mobile{--columnGap:1.5rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-6-tablet{--columnGap:1.5rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-6-tablet-only{--columnGap:1.5rem}}@media screen and (max-width:1023px){.columns.is-variable.is-6-touch{--columnGap:1.5rem}}@media screen and (min-width:1024px){.columns.is-variable.is-6-desktop{--columnGap:1.5rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-6-desktop-only{--columnGap:1.5rem}}@media screen and (min-width:1216px){.columns.is-variable.is-6-widescreen{--columnGap:1.5rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-6-widescreen-only{--columnGap:1.5rem}}@media screen and (min-width:1408px){.columns.is-variable.is-6-fullhd{--columnGap:1.5rem}}.columns.is-variable.is-7{--columnGap:1.75rem}@media screen and (max-width:768px){.columns.is-variable.is-7-mobile{--columnGap:1.75rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-7-tablet{--columnGap:1.75rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-7-tablet-only{--columnGap:1.75rem}}@media screen and (max-width:1023px){.columns.is-variable.is-7-touch{--columnGap:1.75rem}}@media screen and (min-width:1024px){.columns.is-variable.is-7-desktop{--columnGap:1.75rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-7-desktop-only{--columnGap:1.75rem}}@media screen and (min-width:1216px){.columns.is-variable.is-7-widescreen{--columnGap:1.75rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-7-widescreen-only{--columnGap:1.75rem}}@media screen and (min-width:1408px){.columns.is-variable.is-7-fullhd{--columnGap:1.75rem}}.columns.is-variable.is-8{--columnGap:2rem}@media screen and (max-width:768px){.columns.is-variable.is-8-mobile{--columnGap:2rem}}@media screen and (min-width:769px),print{.columns.is-variable.is-8-tablet{--columnGap:2rem}}@media screen and (min-width:769px) and (max-width:1023px){.columns.is-variable.is-8-tablet-only{--columnGap:2rem}}@media screen and (max-width:1023px){.columns.is-variable.is-8-touch{--columnGap:2rem}}@media screen and (min-width:1024px){.columns.is-variable.is-8-desktop{--columnGap:2rem}}@media screen and (min-width:1024px) and (max-width:1215px){.columns.is-variable.is-8-desktop-only{--columnGap:2rem}}@media screen and (min-width:1216px){.columns.is-variable.is-8-widescreen{--columnGap:2rem}}@media screen and (min-width:1216px) and (max-width:1407px){.columns.is-variable.is-8-widescreen-only{--columnGap:2rem}}@media screen and (min-width:1408px){.columns.is-variable.is-8-fullhd{--columnGap:2rem}}.tile{align-items:stretch;display:block;flex-basis:0;flex-grow:1;flex-shrink:1;min-height:-webkit-min-content;min-height:-moz-min-content;min-height:min-content}.tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.tile.is-ancestor:last-child{margin-bottom:-.75rem}.tile.is-ancestor:not(:last-child){margin-bottom:.75rem}.tile.is-child{margin:0!important}.tile.is-parent{padding:.75rem}.tile.is-vertical{flex-direction:column}.tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem!important}@media screen and (min-width:769px),print{.tile:not(.is-child){display:flex}.tile.is-1{flex:none;width:8.33333%}.tile.is-2{flex:none;width:16.66667%}.tile.is-3{flex:none;width:25%}.tile.is-4{flex:none;width:33.33333%}.tile.is-5{flex:none;width:41.66667%}.tile.is-6{flex:none;width:50%}.tile.is-7{flex:none;width:58.33333%}.tile.is-8{flex:none;width:66.66667%}.tile.is-9{flex:none;width:75%}.tile.is-10{flex:none;width:83.33333%}.tile.is-11{flex:none;width:91.66667%}.tile.is-12{flex:none;width:100%}}.has-text-white{color:#fff!important}a.has-text-white:focus,a.has-text-white:hover{color:#e6e6e6!important}.has-background-white{background-color:#fff!important}.has-text-black{color:#0a0a0a!important}a.has-text-black:focus,a.has-text-black:hover{color:#000!important}.has-background-black{background-color:#0a0a0a!important}.has-text-light{color:#f5f5f5!important}a.has-text-light:focus,a.has-text-light:hover{color:#dbdbdb!important}.has-background-light{background-color:#f5f5f5!important}.has-text-dark{color:#363636!important}a.has-text-dark:focus,a.has-text-dark:hover{color:#1c1c1c!important}.has-background-dark{background-color:#363636!important}.has-text-primary{color:#00d1b2!important}a.has-text-primary:focus,a.has-text-primary:hover{color:#009e86!important}.has-background-primary{background-color:#00d1b2!important}.has-text-primary-light{color:#ebfffc!important}a.has-text-primary-light:focus,a.has-text-primary-light:hover{color:#b8fff4!important}.has-background-primary-light{background-color:#ebfffc!important}.has-text-primary-dark{color:#00947e!important}a.has-text-primary-dark:focus,a.has-text-primary-dark:hover{color:#00c7a9!important}.has-background-primary-dark{background-color:#00947e!important}.has-text-link{color:#3273dc!important}a.has-text-link:focus,a.has-text-link:hover{color:#205bbc!important}.has-background-link{background-color:#3273dc!important}.has-text-link-light{color:#eef3fc!important}a.has-text-link-light:focus,a.has-text-link-light:hover{color:#c2d5f5!important}.has-background-link-light{background-color:#eef3fc!important}.has-text-link-dark{color:#2160c4!important}a.has-text-link-dark:focus,a.has-text-link-dark:hover{color:#3b79de!important}.has-background-link-dark{background-color:#2160c4!important}.has-text-info{color:#3298dc!important}a.has-text-info:focus,a.has-text-info:hover{color:#207dbc!important}.has-background-info{background-color:#3298dc!important}.has-text-info-light{color:#eef6fc!important}a.has-text-info-light:focus,a.has-text-info-light:hover{color:#c2e0f5!important}.has-background-info-light{background-color:#eef6fc!important}.has-text-info-dark{color:#1d72aa!important}a.has-text-info-dark:focus,a.has-text-info-dark:hover{color:#248fd6!important}.has-background-info-dark{background-color:#1d72aa!important}.has-text-success{color:#48c774!important}a.has-text-success:focus,a.has-text-success:hover{color:#34a85c!important}.has-background-success{background-color:#48c774!important}.has-text-success-light{color:#effaf3!important}a.has-text-success-light:focus,a.has-text-success-light:hover{color:#c8eed6!important}.has-background-success-light{background-color:#effaf3!important}.has-text-success-dark{color:#257942!important}a.has-text-success-dark:focus,a.has-text-success-dark:hover{color:#31a058!important}.has-background-success-dark{background-color:#257942!important}.has-text-warning{color:#ffdd57!important}a.has-text-warning:focus,a.has-text-warning:hover{color:#ffd324!important}.has-background-warning{background-color:#ffdd57!important}.has-text-warning-light{color:#fffbeb!important}a.has-text-warning-light:focus,a.has-text-warning-light:hover{color:#fff1b8!important}.has-background-warning-light{background-color:#fffbeb!important}.has-text-warning-dark{color:#947600!important}a.has-text-warning-dark:focus,a.has-text-warning-dark:hover{color:#c79f00!important}.has-background-warning-dark{background-color:#947600!important}.has-text-danger{color:#f14668!important}a.has-text-danger:focus,a.has-text-danger:hover{color:#ee1742!important}.has-background-danger{background-color:#f14668!important}.has-text-danger-light{color:#feecf0!important}a.has-text-danger-light:focus,a.has-text-danger-light:hover{color:#fabdc9!important}.has-background-danger-light{background-color:#feecf0!important}.has-text-danger-dark{color:#cc0f35!important}a.has-text-danger-dark:focus,a.has-text-danger-dark:hover{color:#ee2049!important}.has-background-danger-dark{background-color:#cc0f35!important}.has-text-black-bis{color:#121212!important}.has-background-black-bis{background-color:#121212!important}.has-text-black-ter{color:#242424!important}.has-background-black-ter{background-color:#242424!important}.has-text-grey-darker{color:#363636!important}.has-background-grey-darker{background-color:#363636!important}.has-text-grey-dark{color:#4a4a4a!important}.has-background-grey-dark{background-color:#4a4a4a!important}.has-text-grey{color:#7a7a7a!important}.has-background-grey{background-color:#7a7a7a!important}.has-text-grey-light{color:#b5b5b5!important}.has-background-grey-light{background-color:#b5b5b5!important}.has-text-grey-lighter{color:#dbdbdb!important}.has-background-grey-lighter{background-color:#dbdbdb!important}.has-text-white-ter{color:#f5f5f5!important}.has-background-white-ter{background-color:#f5f5f5!important}.has-text-white-bis{color:#fafafa!important}.has-background-white-bis{background-color:#fafafa!important}.is-flex-direction-row{flex-direction:row!important}.is-flex-direction-row-reverse{flex-direction:row-reverse!important}.is-flex-direction-column{flex-direction:column!important}.is-flex-direction-column-reverse{flex-direction:column-reverse!important}.is-flex-wrap-nowrap{flex-wrap:nowrap!important}.is-flex-wrap-wrap{flex-wrap:wrap!important}.is-flex-wrap-wrap-reverse{flex-wrap:wrap-reverse!important}.is-justify-content-flex-start{justify-content:flex-start!important}.is-justify-content-flex-end{justify-content:flex-end!important}.is-justify-content-center{justify-content:center!important}.is-justify-content-space-between{justify-content:space-between!important}.is-justify-content-space-around{justify-content:space-around!important}.is-justify-content-space-evenly{justify-content:space-evenly!important}.is-justify-content-start{justify-content:start!important}.is-justify-content-end{justify-content:end!important}.is-justify-content-left{justify-content:left!important}.is-justify-content-right{justify-content:right!important}.is-align-content-flex-start{align-content:flex-start!important}.is-align-content-flex-end{align-content:flex-end!important}.is-align-content-center{align-content:center!important}.is-align-content-space-between{align-content:space-between!important}.is-align-content-space-around{align-content:space-around!important}.is-align-content-space-evenly{align-content:space-evenly!important}.is-align-content-stretch{align-content:stretch!important}.is-align-content-start{align-content:start!important}.is-align-content-end{align-content:end!important}.is-align-content-baseline{align-content:baseline!important}.is-align-items-stretch{align-items:stretch!important}.is-align-items-flex-start{align-items:flex-start!important}.is-align-items-flex-end{align-items:flex-end!important}.is-align-items-center{align-items:center!important}.is-align-items-baseline{align-items:baseline!important}.is-align-items-start{align-items:start!important}.is-align-items-end{align-items:end!important}.is-align-items-self-start{align-items:self-start!important}.is-align-items-self-end{align-items:self-end!important}.is-align-self-auto{align-self:auto!important}.is-align-self-flex-start{align-self:flex-start!important}.is-align-self-flex-end{align-self:flex-end!important}.is-align-self-center{align-self:center!important}.is-align-self-baseline{align-self:baseline!important}.is-align-self-stretch{align-self:stretch!important}.is-flex-grow-0{flex-grow:0!important}.is-flex-grow-1{flex-grow:1!important}.is-flex-grow-2{flex-grow:2!important}.is-flex-grow-3{flex-grow:3!important}.is-flex-grow-4{flex-grow:4!important}.is-flex-grow-5{flex-grow:5!important}.is-flex-shrink-0{flex-shrink:0!important}.is-flex-shrink-1{flex-shrink:1!important}.is-flex-shrink-2{flex-shrink:2!important}.is-flex-shrink-3{flex-shrink:3!important}.is-flex-shrink-4{flex-shrink:4!important}.is-flex-shrink-5{flex-shrink:5!important}.is-clearfix::after{clear:both;content:" ";display:table}.is-pulled-left{float:left!important}.is-pulled-right{float:right!important}.is-radiusless{border-radius:0!important}.is-shadowless{box-shadow:none!important}.is-clickable{cursor:pointer!important}.is-clipped{overflow:hidden!important}.is-relative{position:relative!important}.is-marginless{margin:0!important}.is-paddingless{padding:0!important}.m-0{margin:0!important}.mt-0{margin-top:0!important}.mr-0{margin-right:0!important}.mb-0{margin-bottom:0!important}.ml-0{margin-left:0!important}.mx-0{margin-left:0!important;margin-right:0!important}.my-0{margin-top:0!important;margin-bottom:0!important}.m-1{margin:.25rem!important}.mt-1{margin-top:.25rem!important}.mr-1{margin-right:.25rem!important}.mb-1{margin-bottom:.25rem!important}.ml-1{margin-left:.25rem!important}.mx-1{margin-left:.25rem!important;margin-right:.25rem!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.m-2{margin:.5rem!important}.mt-2{margin-top:.5rem!important}.mr-2{margin-right:.5rem!important}.mb-2{margin-bottom:.5rem!important}.ml-2{margin-left:.5rem!important}.mx-2{margin-left:.5rem!important;margin-right:.5rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.m-3{margin:.75rem!important}.mt-3{margin-top:.75rem!important}.mr-3{margin-right:.75rem!important}.mb-3{margin-bottom:.75rem!important}.ml-3{margin-left:.75rem!important}.mx-3{margin-left:.75rem!important;margin-right:.75rem!important}.my-3{margin-top:.75rem!important;margin-bottom:.75rem!important}.m-4{margin:1rem!important}.mt-4{margin-top:1rem!important}.mr-4{margin-right:1rem!important}.mb-4{margin-bottom:1rem!important}.ml-4{margin-left:1rem!important}.mx-4{margin-left:1rem!important;margin-right:1rem!important}.my-4{margin-top:1rem!important;margin-bottom:1rem!important}.m-5{margin:1.5rem!important}.mt-5{margin-top:1.5rem!important}.mr-5{margin-right:1.5rem!important}.mb-5{margin-bottom:1.5rem!important}.ml-5{margin-left:1.5rem!important}.mx-5{margin-left:1.5rem!important;margin-right:1.5rem!important}.my-5{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.m-6{margin:3rem!important}.mt-6{margin-top:3rem!important}.mr-6{margin-right:3rem!important}.mb-6{margin-bottom:3rem!important}.ml-6{margin-left:3rem!important}.mx-6{margin-left:3rem!important;margin-right:3rem!important}.my-6{margin-top:3rem!important;margin-bottom:3rem!important}.p-0{padding:0!important}.pt-0{padding-top:0!important}.pr-0{padding-right:0!important}.pb-0{padding-bottom:0!important}.pl-0{padding-left:0!important}.px-0{padding-left:0!important;padding-right:0!important}.py-0{padding-top:0!important;padding-bottom:0!important}.p-1{padding:.25rem!important}.pt-1{padding-top:.25rem!important}.pr-1{padding-right:.25rem!important}.pb-1{padding-bottom:.25rem!important}.pl-1{padding-left:.25rem!important}.px-1{padding-left:.25rem!important;padding-right:.25rem!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.p-2{padding:.5rem!important}.pt-2{padding-top:.5rem!important}.pr-2{padding-right:.5rem!important}.pb-2{padding-bottom:.5rem!important}.pl-2{padding-left:.5rem!important}.px-2{padding-left:.5rem!important;padding-right:.5rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.p-3{padding:.75rem!important}.pt-3{padding-top:.75rem!important}.pr-3{padding-right:.75rem!important}.pb-3{padding-bottom:.75rem!important}.pl-3{padding-left:.75rem!important}.px-3{padding-left:.75rem!important;padding-right:.75rem!important}.py-3{padding-top:.75rem!important;padding-bottom:.75rem!important}.p-4{padding:1rem!important}.pt-4{padding-top:1rem!important}.pr-4{padding-right:1rem!important}.pb-4{padding-bottom:1rem!important}.pl-4{padding-left:1rem!important}.px-4{padding-left:1rem!important;padding-right:1rem!important}.py-4{padding-top:1rem!important;padding-bottom:1rem!important}.p-5{padding:1.5rem!important}.pt-5{padding-top:1.5rem!important}.pr-5{padding-right:1.5rem!important}.pb-5{padding-bottom:1.5rem!important}.pl-5{padding-left:1.5rem!important}.px-5{padding-left:1.5rem!important;padding-right:1.5rem!important}.py-5{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.p-6{padding:3rem!important}.pt-6{padding-top:3rem!important}.pr-6{padding-right:3rem!important}.pb-6{padding-bottom:3rem!important}.pl-6{padding-left:3rem!important}.px-6{padding-left:3rem!important;padding-right:3rem!important}.py-6{padding-top:3rem!important;padding-bottom:3rem!important}.is-size-1{font-size:3rem!important}.is-size-2{font-size:2.5rem!important}.is-size-3{font-size:2rem!important}.is-size-4{font-size:1.5rem!important}.is-size-5{font-size:1.25rem!important}.is-size-6{font-size:1rem!important}.is-size-7{font-size:.75rem!important}@media screen and (max-width:768px){.is-size-1-mobile{font-size:3rem!important}.is-size-2-mobile{font-size:2.5rem!important}.is-size-3-mobile{font-size:2rem!important}.is-size-4-mobile{font-size:1.5rem!important}.is-size-5-mobile{font-size:1.25rem!important}.is-size-6-mobile{font-size:1rem!important}.is-size-7-mobile{font-size:.75rem!important}}@media screen and (min-width:769px),print{.is-size-1-tablet{font-size:3rem!important}.is-size-2-tablet{font-size:2.5rem!important}.is-size-3-tablet{font-size:2rem!important}.is-size-4-tablet{font-size:1.5rem!important}.is-size-5-tablet{font-size:1.25rem!important}.is-size-6-tablet{font-size:1rem!important}.is-size-7-tablet{font-size:.75rem!important}}@media screen and (max-width:1023px){.is-size-1-touch{font-size:3rem!important}.is-size-2-touch{font-size:2.5rem!important}.is-size-3-touch{font-size:2rem!important}.is-size-4-touch{font-size:1.5rem!important}.is-size-5-touch{font-size:1.25rem!important}.is-size-6-touch{font-size:1rem!important}.is-size-7-touch{font-size:.75rem!important}}@media screen and (min-width:1024px){.is-size-1-desktop{font-size:3rem!important}.is-size-2-desktop{font-size:2.5rem!important}.is-size-3-desktop{font-size:2rem!important}.is-size-4-desktop{font-size:1.5rem!important}.is-size-5-desktop{font-size:1.25rem!important}.is-size-6-desktop{font-size:1rem!important}.is-size-7-desktop{font-size:.75rem!important}}@media screen and (min-width:1216px){.is-size-1-widescreen{font-size:3rem!important}.is-size-2-widescreen{font-size:2.5rem!important}.is-size-3-widescreen{font-size:2rem!important}.is-size-4-widescreen{font-size:1.5rem!important}.is-size-5-widescreen{font-size:1.25rem!important}.is-size-6-widescreen{font-size:1rem!important}.is-size-7-widescreen{font-size:.75rem!important}}@media screen and (min-width:1408px){.is-size-1-fullhd{font-size:3rem!important}.is-size-2-fullhd{font-size:2.5rem!important}.is-size-3-fullhd{font-size:2rem!important}.is-size-4-fullhd{font-size:1.5rem!important}.is-size-5-fullhd{font-size:1.25rem!important}.is-size-6-fullhd{font-size:1rem!important}.is-size-7-fullhd{font-size:.75rem!important}}.has-text-centered{text-align:center!important}.has-text-justified{text-align:justify!important}.has-text-left{text-align:left!important}.has-text-right{text-align:right!important}@media screen and (max-width:768px){.has-text-centered-mobile{text-align:center!important}}@media screen and (min-width:769px),print{.has-text-centered-tablet{text-align:center!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-centered-tablet-only{text-align:center!important}}@media screen and (max-width:1023px){.has-text-centered-touch{text-align:center!important}}@media screen and (min-width:1024px){.has-text-centered-desktop{text-align:center!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-centered-desktop-only{text-align:center!important}}@media screen and (min-width:1216px){.has-text-centered-widescreen{text-align:center!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-centered-widescreen-only{text-align:center!important}}@media screen and (min-width:1408px){.has-text-centered-fullhd{text-align:center!important}}@media screen and (max-width:768px){.has-text-justified-mobile{text-align:justify!important}}@media screen and (min-width:769px),print{.has-text-justified-tablet{text-align:justify!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-justified-tablet-only{text-align:justify!important}}@media screen and (max-width:1023px){.has-text-justified-touch{text-align:justify!important}}@media screen and (min-width:1024px){.has-text-justified-desktop{text-align:justify!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-justified-desktop-only{text-align:justify!important}}@media screen and (min-width:1216px){.has-text-justified-widescreen{text-align:justify!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-justified-widescreen-only{text-align:justify!important}}@media screen and (min-width:1408px){.has-text-justified-fullhd{text-align:justify!important}}@media screen and (max-width:768px){.has-text-left-mobile{text-align:left!important}}@media screen and (min-width:769px),print{.has-text-left-tablet{text-align:left!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-left-tablet-only{text-align:left!important}}@media screen and (max-width:1023px){.has-text-left-touch{text-align:left!important}}@media screen and (min-width:1024px){.has-text-left-desktop{text-align:left!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-left-desktop-only{text-align:left!important}}@media screen and (min-width:1216px){.has-text-left-widescreen{text-align:left!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-left-widescreen-only{text-align:left!important}}@media screen and (min-width:1408px){.has-text-left-fullhd{text-align:left!important}}@media screen and (max-width:768px){.has-text-right-mobile{text-align:right!important}}@media screen and (min-width:769px),print{.has-text-right-tablet{text-align:right!important}}@media screen and (min-width:769px) and (max-width:1023px){.has-text-right-tablet-only{text-align:right!important}}@media screen and (max-width:1023px){.has-text-right-touch{text-align:right!important}}@media screen and (min-width:1024px){.has-text-right-desktop{text-align:right!important}}@media screen and (min-width:1024px) and (max-width:1215px){.has-text-right-desktop-only{text-align:right!important}}@media screen and (min-width:1216px){.has-text-right-widescreen{text-align:right!important}}@media screen and (min-width:1216px) and (max-width:1407px){.has-text-right-widescreen-only{text-align:right!important}}@media screen and (min-width:1408px){.has-text-right-fullhd{text-align:right!important}}.is-capitalized{text-transform:capitalize!important}.is-lowercase{text-transform:lowercase!important}.is-uppercase{text-transform:uppercase!important}.is-italic{font-style:italic!important}.has-text-weight-light{font-weight:300!important}.has-text-weight-normal{font-weight:400!important}.has-text-weight-medium{font-weight:500!important}.has-text-weight-semibold{font-weight:600!important}.has-text-weight-bold{font-weight:700!important}.is-family-primary{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-secondary{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-sans-serif{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif!important}.is-family-monospace{font-family:monospace!important}.is-family-code{font-family:monospace!important}.is-block{display:block!important}@media screen and (max-width:768px){.is-block-mobile{display:block!important}}@media screen and (min-width:769px),print{.is-block-tablet{display:block!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-block-tablet-only{display:block!important}}@media screen and (max-width:1023px){.is-block-touch{display:block!important}}@media screen and (min-width:1024px){.is-block-desktop{display:block!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-block-desktop-only{display:block!important}}@media screen and (min-width:1216px){.is-block-widescreen{display:block!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-block-widescreen-only{display:block!important}}@media screen and (min-width:1408px){.is-block-fullhd{display:block!important}}.is-flex{display:flex!important}@media screen and (max-width:768px){.is-flex-mobile{display:flex!important}}@media screen and (min-width:769px),print{.is-flex-tablet{display:flex!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-flex-tablet-only{display:flex!important}}@media screen and (max-width:1023px){.is-flex-touch{display:flex!important}}@media screen and (min-width:1024px){.is-flex-desktop{display:flex!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-flex-desktop-only{display:flex!important}}@media screen and (min-width:1216px){.is-flex-widescreen{display:flex!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-flex-widescreen-only{display:flex!important}}@media screen and (min-width:1408px){.is-flex-fullhd{display:flex!important}}.is-inline{display:inline!important}@media screen and (max-width:768px){.is-inline-mobile{display:inline!important}}@media screen and (min-width:769px),print{.is-inline-tablet{display:inline!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-tablet-only{display:inline!important}}@media screen and (max-width:1023px){.is-inline-touch{display:inline!important}}@media screen and (min-width:1024px){.is-inline-desktop{display:inline!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-desktop-only{display:inline!important}}@media screen and (min-width:1216px){.is-inline-widescreen{display:inline!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-widescreen-only{display:inline!important}}@media screen and (min-width:1408px){.is-inline-fullhd{display:inline!important}}.is-inline-block{display:inline-block!important}@media screen and (max-width:768px){.is-inline-block-mobile{display:inline-block!important}}@media screen and (min-width:769px),print{.is-inline-block-tablet{display:inline-block!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-block-tablet-only{display:inline-block!important}}@media screen and (max-width:1023px){.is-inline-block-touch{display:inline-block!important}}@media screen and (min-width:1024px){.is-inline-block-desktop{display:inline-block!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-block-desktop-only{display:inline-block!important}}@media screen and (min-width:1216px){.is-inline-block-widescreen{display:inline-block!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-block-widescreen-only{display:inline-block!important}}@media screen and (min-width:1408px){.is-inline-block-fullhd{display:inline-block!important}}.is-inline-flex{display:inline-flex!important}@media screen and (max-width:768px){.is-inline-flex-mobile{display:inline-flex!important}}@media screen and (min-width:769px),print{.is-inline-flex-tablet{display:inline-flex!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-inline-flex-tablet-only{display:inline-flex!important}}@media screen and (max-width:1023px){.is-inline-flex-touch{display:inline-flex!important}}@media screen and (min-width:1024px){.is-inline-flex-desktop{display:inline-flex!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-inline-flex-desktop-only{display:inline-flex!important}}@media screen and (min-width:1216px){.is-inline-flex-widescreen{display:inline-flex!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-inline-flex-widescreen-only{display:inline-flex!important}}@media screen and (min-width:1408px){.is-inline-flex-fullhd{display:inline-flex!important}}.is-hidden{display:none!important}.is-sr-only{border:none!important;clip:rect(0,0,0,0)!important;height:.01em!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:.01em!important}@media screen and (max-width:768px){.is-hidden-mobile{display:none!important}}@media screen and (min-width:769px),print{.is-hidden-tablet{display:none!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-hidden-tablet-only{display:none!important}}@media screen and (max-width:1023px){.is-hidden-touch{display:none!important}}@media screen and (min-width:1024px){.is-hidden-desktop{display:none!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-hidden-desktop-only{display:none!important}}@media screen and (min-width:1216px){.is-hidden-widescreen{display:none!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-hidden-widescreen-only{display:none!important}}@media screen and (min-width:1408px){.is-hidden-fullhd{display:none!important}}.is-invisible{visibility:hidden!important}@media screen and (max-width:768px){.is-invisible-mobile{visibility:hidden!important}}@media screen and (min-width:769px),print{.is-invisible-tablet{visibility:hidden!important}}@media screen and (min-width:769px) and (max-width:1023px){.is-invisible-tablet-only{visibility:hidden!important}}@media screen and (max-width:1023px){.is-invisible-touch{visibility:hidden!important}}@media screen and (min-width:1024px){.is-invisible-desktop{visibility:hidden!important}}@media screen and (min-width:1024px) and (max-width:1215px){.is-invisible-desktop-only{visibility:hidden!important}}@media screen and (min-width:1216px){.is-invisible-widescreen{visibility:hidden!important}}@media screen and (min-width:1216px) and (max-width:1407px){.is-invisible-widescreen-only{visibility:hidden!important}}@media screen and (min-width:1408px){.is-invisible-fullhd{visibility:hidden!important}}.hero{align-items:stretch;display:flex;flex-direction:column;justify-content:space-between}.hero .navbar{background:0 0}.hero .tabs ul{border-bottom:none}.hero.is-white{background-color:#fff;color:#0a0a0a}.hero.is-white a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-white strong{color:inherit}.hero.is-white .title{color:#0a0a0a}.hero.is-white .subtitle{color:rgba(10,10,10,.9)}.hero.is-white .subtitle a:not(.button),.hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width:1023px){.hero.is-white .navbar-menu{background-color:#fff}}.hero.is-white .navbar-item,.hero.is-white .navbar-link{color:rgba(10,10,10,.7)}.hero.is-white .navbar-link.is-active,.hero.is-white .navbar-link:hover,.hero.is-white a.navbar-item.is-active,.hero.is-white a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.hero.is-white .tabs a{color:#0a0a0a;opacity:.9}.hero.is-white .tabs a:hover{opacity:1}.hero.is-white .tabs li.is-active a{opacity:1}.hero.is-white .tabs.is-boxed a,.hero.is-white .tabs.is-toggle a{color:#0a0a0a}.hero.is-white .tabs.is-boxed a:hover,.hero.is-white .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-white .tabs.is-boxed li.is-active a,.hero.is-white .tabs.is-boxed li.is-active a:hover,.hero.is-white .tabs.is-toggle li.is-active a,.hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.hero.is-white.is-bold{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}}.hero.is-black{background-color:#0a0a0a;color:#fff}.hero.is-black a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-black strong{color:inherit}.hero.is-black .title{color:#fff}.hero.is-black .subtitle{color:rgba(255,255,255,.9)}.hero.is-black .subtitle a:not(.button),.hero.is-black .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-black .navbar-menu{background-color:#0a0a0a}}.hero.is-black .navbar-item,.hero.is-black .navbar-link{color:rgba(255,255,255,.7)}.hero.is-black .navbar-link.is-active,.hero.is-black .navbar-link:hover,.hero.is-black a.navbar-item.is-active,.hero.is-black a.navbar-item:hover{background-color:#000;color:#fff}.hero.is-black .tabs a{color:#fff;opacity:.9}.hero.is-black .tabs a:hover{opacity:1}.hero.is-black .tabs li.is-active a{opacity:1}.hero.is-black .tabs.is-boxed a,.hero.is-black .tabs.is-toggle a{color:#fff}.hero.is-black .tabs.is-boxed a:hover,.hero.is-black .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-black .tabs.is-boxed li.is-active a,.hero.is-black .tabs.is-boxed li.is-active a:hover,.hero.is-black .tabs.is-toggle li.is-active a,.hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.hero.is-black.is-bold{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}@media screen and (max-width:768px){.hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}}.hero.is-light{background-color:#f5f5f5;color:rgba(0,0,0,.7)}.hero.is-light a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-light strong{color:inherit}.hero.is-light .title{color:rgba(0,0,0,.7)}.hero.is-light .subtitle{color:rgba(0,0,0,.9)}.hero.is-light .subtitle a:not(.button),.hero.is-light .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1023px){.hero.is-light .navbar-menu{background-color:#f5f5f5}}.hero.is-light .navbar-item,.hero.is-light .navbar-link{color:rgba(0,0,0,.7)}.hero.is-light .navbar-link.is-active,.hero.is-light .navbar-link:hover,.hero.is-light a.navbar-item.is-active,.hero.is-light a.navbar-item:hover{background-color:#e8e8e8;color:rgba(0,0,0,.7)}.hero.is-light .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-light .tabs a:hover{opacity:1}.hero.is-light .tabs li.is-active a{opacity:1}.hero.is-light .tabs.is-boxed a,.hero.is-light .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-light .tabs.is-boxed a:hover,.hero.is-light .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-light .tabs.is-boxed li.is-active a,.hero.is-light .tabs.is-boxed li.is-active a:hover,.hero.is-light .tabs.is-toggle li.is-active a,.hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#f5f5f5}.hero.is-light.is-bold{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}}.hero.is-dark{background-color:#363636;color:#fff}.hero.is-dark a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-dark strong{color:inherit}.hero.is-dark .title{color:#fff}.hero.is-dark .subtitle{color:rgba(255,255,255,.9)}.hero.is-dark .subtitle a:not(.button),.hero.is-dark .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-dark .navbar-menu{background-color:#363636}}.hero.is-dark .navbar-item,.hero.is-dark .navbar-link{color:rgba(255,255,255,.7)}.hero.is-dark .navbar-link.is-active,.hero.is-dark .navbar-link:hover,.hero.is-dark a.navbar-item.is-active,.hero.is-dark a.navbar-item:hover{background-color:#292929;color:#fff}.hero.is-dark .tabs a{color:#fff;opacity:.9}.hero.is-dark .tabs a:hover{opacity:1}.hero.is-dark .tabs li.is-active a{opacity:1}.hero.is-dark .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a{color:#fff}.hero.is-dark .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#363636}.hero.is-dark.is-bold{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}@media screen and (max-width:768px){.hero.is-dark.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}}.hero.is-primary{background-color:#00d1b2;color:#fff}.hero.is-primary a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-primary strong{color:inherit}.hero.is-primary .title{color:#fff}.hero.is-primary .subtitle{color:rgba(255,255,255,.9)}.hero.is-primary .subtitle a:not(.button),.hero.is-primary .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-primary .navbar-menu{background-color:#00d1b2}}.hero.is-primary .navbar-item,.hero.is-primary .navbar-link{color:rgba(255,255,255,.7)}.hero.is-primary .navbar-link.is-active,.hero.is-primary .navbar-link:hover,.hero.is-primary a.navbar-item.is-active,.hero.is-primary a.navbar-item:hover{background-color:#00b89c;color:#fff}.hero.is-primary .tabs a{color:#fff;opacity:.9}.hero.is-primary .tabs a:hover{opacity:1}.hero.is-primary .tabs li.is-active a{opacity:1}.hero.is-primary .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#00d1b2}.hero.is-primary.is-bold{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}@media screen and (max-width:768px){.hero.is-primary.is-bold .navbar-menu{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}}.hero.is-link{background-color:#3273dc;color:#fff}.hero.is-link a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-link strong{color:inherit}.hero.is-link .title{color:#fff}.hero.is-link .subtitle{color:rgba(255,255,255,.9)}.hero.is-link .subtitle a:not(.button),.hero.is-link .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-link .navbar-menu{background-color:#3273dc}}.hero.is-link .navbar-item,.hero.is-link .navbar-link{color:rgba(255,255,255,.7)}.hero.is-link .navbar-link.is-active,.hero.is-link .navbar-link:hover,.hero.is-link a.navbar-item.is-active,.hero.is-link a.navbar-item:hover{background-color:#2366d1;color:#fff}.hero.is-link .tabs a{color:#fff;opacity:.9}.hero.is-link .tabs a:hover{opacity:1}.hero.is-link .tabs li.is-active a{opacity:1}.hero.is-link .tabs.is-boxed a,.hero.is-link .tabs.is-toggle a{color:#fff}.hero.is-link .tabs.is-boxed a:hover,.hero.is-link .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-link .tabs.is-boxed li.is-active a,.hero.is-link .tabs.is-boxed li.is-active a:hover,.hero.is-link .tabs.is-toggle li.is-active a,.hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#3273dc}.hero.is-link.is-bold{background-image:linear-gradient(141deg,#1577c6 0,#3273dc 71%,#4366e5 100%)}@media screen and (max-width:768px){.hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1577c6 0,#3273dc 71%,#4366e5 100%)}}.hero.is-info{background-color:#3298dc;color:#fff}.hero.is-info a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-info strong{color:inherit}.hero.is-info .title{color:#fff}.hero.is-info .subtitle{color:rgba(255,255,255,.9)}.hero.is-info .subtitle a:not(.button),.hero.is-info .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-info .navbar-menu{background-color:#3298dc}}.hero.is-info .navbar-item,.hero.is-info .navbar-link{color:rgba(255,255,255,.7)}.hero.is-info .navbar-link.is-active,.hero.is-info .navbar-link:hover,.hero.is-info a.navbar-item.is-active,.hero.is-info a.navbar-item:hover{background-color:#238cd1;color:#fff}.hero.is-info .tabs a{color:#fff;opacity:.9}.hero.is-info .tabs a:hover{opacity:1}.hero.is-info .tabs li.is-active a{opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#3298dc}.hero.is-info.is-bold{background-image:linear-gradient(141deg,#159dc6 0,#3298dc 71%,#4389e5 100%)}@media screen and (max-width:768px){.hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg,#159dc6 0,#3298dc 71%,#4389e5 100%)}}.hero.is-success{background-color:#48c774;color:#fff}.hero.is-success a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-success strong{color:inherit}.hero.is-success .title{color:#fff}.hero.is-success .subtitle{color:rgba(255,255,255,.9)}.hero.is-success .subtitle a:not(.button),.hero.is-success .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-success .navbar-menu{background-color:#48c774}}.hero.is-success .navbar-item,.hero.is-success .navbar-link{color:rgba(255,255,255,.7)}.hero.is-success .navbar-link.is-active,.hero.is-success .navbar-link:hover,.hero.is-success a.navbar-item.is-active,.hero.is-success a.navbar-item:hover{background-color:#3abb67;color:#fff}.hero.is-success .tabs a{color:#fff;opacity:.9}.hero.is-success .tabs a:hover{opacity:1}.hero.is-success .tabs li.is-active a{opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#48c774}.hero.is-success.is-bold{background-image:linear-gradient(141deg,#29b342 0,#48c774 71%,#56d296 100%)}@media screen and (max-width:768px){.hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg,#29b342 0,#48c774 71%,#56d296 100%)}}.hero.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.hero.is-warning a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-warning strong{color:inherit}.hero.is-warning .title{color:rgba(0,0,0,.7)}.hero.is-warning .subtitle{color:rgba(0,0,0,.9)}.hero.is-warning .subtitle a:not(.button),.hero.is-warning .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1023px){.hero.is-warning .navbar-menu{background-color:#ffdd57}}.hero.is-warning .navbar-item,.hero.is-warning .navbar-link{color:rgba(0,0,0,.7)}.hero.is-warning .navbar-link.is-active,.hero.is-warning .navbar-link:hover,.hero.is-warning a.navbar-item.is-active,.hero.is-warning a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.hero.is-warning .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-warning .tabs a:hover{opacity:1}.hero.is-warning .tabs li.is-active a{opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#ffdd57}.hero.is-warning.is-bold{background-image:linear-gradient(141deg,#ffaf24 0,#ffdd57 71%,#fffa70 100%)}@media screen and (max-width:768px){.hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ffaf24 0,#ffdd57 71%,#fffa70 100%)}}.hero.is-danger{background-color:#f14668;color:#fff}.hero.is-danger a:not(.button):not(.dropdown-item):not(.tag):not(.pagination-link.is-current),.hero.is-danger strong{color:inherit}.hero.is-danger .title{color:#fff}.hero.is-danger .subtitle{color:rgba(255,255,255,.9)}.hero.is-danger .subtitle a:not(.button),.hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width:1023px){.hero.is-danger .navbar-menu{background-color:#f14668}}.hero.is-danger .navbar-item,.hero.is-danger .navbar-link{color:rgba(255,255,255,.7)}.hero.is-danger .navbar-link.is-active,.hero.is-danger .navbar-link:hover,.hero.is-danger a.navbar-item.is-active,.hero.is-danger a.navbar-item:hover{background-color:#ef2e55;color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:.9}.hero.is-danger .tabs a:hover{opacity:1}.hero.is-danger .tabs li.is-active a{opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#f14668}.hero.is-danger.is-bold{background-image:linear-gradient(141deg,#fa0a62 0,#f14668 71%,#f7595f 100%)}@media screen and (max-width:768px){.hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg,#fa0a62 0,#f14668 71%,#f7595f 100%)}}.hero.is-small .hero-body{padding:1.5rem}@media screen and (min-width:769px),print{.hero.is-medium .hero-body{padding:9rem 1.5rem}}@media screen and (min-width:769px),print{.hero.is-large .hero-body{padding:18rem 1.5rem}}.hero.is-fullheight .hero-body,.hero.is-fullheight-with-navbar .hero-body,.hero.is-halfheight .hero-body{align-items:center;display:flex}.hero.is-fullheight .hero-body>.container,.hero.is-fullheight-with-navbar .hero-body>.container,.hero.is-halfheight .hero-body>.container{flex-grow:1;flex-shrink:1}.hero.is-halfheight{min-height:50vh}.hero.is-fullheight{min-height:100vh}.hero-video{overflow:hidden}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;transform:translate3d(-50%,-50%,0)}.hero-video.is-transparent{opacity:.3}@media screen and (max-width:768px){.hero-video{display:none}}.hero-buttons{margin-top:1.5rem}@media screen and (max-width:768px){.hero-buttons .button{display:flex}.hero-buttons .button:not(:last-child){margin-bottom:.75rem}}@media screen and (min-width:769px),print{.hero-buttons{display:flex;justify-content:center}.hero-buttons .button:not(:last-child){margin-right:1.5rem}}.hero-foot,.hero-head{flex-grow:0;flex-shrink:0}.hero-body{flex-grow:1;flex-shrink:0;padding:3rem 1.5rem}.section{padding:3rem 1.5rem}@media screen and (min-width:1024px){.section.is-medium{padding:9rem 1.5rem}.section.is-large{padding:18rem 1.5rem}}.footer{background-color:#fafafa;padding:3rem 1.5rem 6rem} \ No newline at end of file diff --git a/packages/wordpress/trunk/admin/partials/typebot-admin-display.php b/packages/wordpress/trunk/admin/partials/typebot-admin-display.php index 51efc345d8..dd551c5c1b 100644 --- a/packages/wordpress/trunk/admin/partials/typebot-admin-display.php +++ b/packages/wordpress/trunk/admin/partials/typebot-admin-display.php @@ -1,255 +1,25 @@ - -
-

Typebot Settings

- First, you need to create a Typebot with our builder. It's free. -
- -
- -
-
- -
- -
-
- -
- - - -
- - - - +
+

Typebot Settings

+
    +
  1. Generate your initialization snippet in the Share tab of your typebot.
  2. +
  3. If embedding as Standard container, paste the generated shortcode anywhere on your site.
  4. +
  5. + + +
    + + +
    - - -
    -
    - -
    -
    -
  6. + + +
\ No newline at end of file diff --git a/packages/wordpress/trunk/includes/class-typebot.php b/packages/wordpress/trunk/includes/class-typebot.php index d0f1e64608..af0bbbd102 100644 --- a/packages/wordpress/trunk/includes/class-typebot.php +++ b/packages/wordpress/trunk/includes/class-typebot.php @@ -50,7 +50,7 @@ private function define_admin_hooks() private function define_public_hooks() { $plugin_public = new Typebot_Public($this->get_plugin_name(), $this->get_version()); - $this->loader->add_action('wp_head', $plugin_public, 'add_head_code'); + $this->loader->add_action('wp_enqueue_scripts', $plugin_public, 'add_head_code'); $this->loader->add_shortcode('typebot', $plugin_public, 'add_typebot_container'); } diff --git a/packages/wordpress/trunk/public/class-typebot-public.php b/packages/wordpress/trunk/public/class-typebot-public.php index 6bf4be3f4e..ce72332d96 100644 --- a/packages/wordpress/trunk/public/class-typebot-public.php +++ b/packages/wordpress/trunk/public/class-typebot-public.php @@ -4,219 +4,33 @@ class Typebot_Public { public function add_head_code() { - wp_enqueue_script( - 'typebot', - 'https://unpkg.com/typebot-js@2.2/dist/index.umd.min.js' - ); - wp_add_inline_script('typebot', $this->parse_wp_user()); - if (get_option('config_type') === 'advanced') { - echo esc_html(get_option('custom_code')); - } - if (get_option('embed_type') === 'popup') { - return $this->parse_popup_head_code(); - } - if (get_option('embed_type') === 'bubble') { - return $this->parse_bubble_head_code(); - } - } - - private function parse_popup_head_code() - { - $url = str_starts_with(get_option('url'), 'https://typebot.io') - ? 'https://viewer.typebot.io' . '/' . explode('/', get_option('url'))[3] - : get_option('url'); - if (!$url) { - return; - } - if ( - get_option('popup_included_pages') !== null && - get_option('popup_included_pages') !== '' - ) { - $paths = explode(',', get_option('popup_included_pages')); - $arr_js = 'const typebot_include_paths = ['; - foreach ($paths as $path) { - $arr_js = $arr_js . '"' . $path . '",'; + function add_module_type($tag, $handle) + { + if ('typebot' !== $handle) { + return $tag; } - $arr_js = $arr_js . ']'; - wp_add_inline_script('typebot', $arr_js); - } else { - wp_add_inline_script('typebot', 'const typebot_include_paths = null'); - } - $params = - '{ - url: "' . - $url . - '", - hiddenVariables: typebotWpUser, - }'; - if ( - get_option('popup_delay') !== null && - get_option('popup_delay') !== '' - ) { - $params = - '{ - url: "' . - $url . - '", - delay: ' . - get_option('popup_delay') * 1000 . - ' - }'; + $tag = str_replace( + ' { - let includePath = path; - let windowPath = window.location.pathname; - if (includePath.endsWith("*")) { - return windowPath.startsWith(includePath.slice(0, -1)); - } - if (includePath.endsWith("/")) { - includePath = path.slice(0, -1); - } - if (windowPath.endsWith("/")) { - windowPath = windowPath.slice(0, -1); - } - return windowPath === includePath; - }) - ) { - Typebot.initPopup(' . - $params . - '); - }' - ); - } - private function parse_bubble_head_code() - { - $url = str_starts_with(get_option('url'), 'https://typebot.io') - ? 'https://viewer.typebot.io' . '/' . explode('/', get_option('url'))[3] - : get_option('url'); - if (!$url) { - return; - } - $chat_icon = get_option('chat_icon'); - if ( - get_option('chat_included_pages') !== null && - get_option('chat_included_pages') !== '' - ) { - $paths = explode(',', get_option('chat_included_pages')); - $arr_js = 'const typebot_include_paths = ['; - foreach ($paths as $path) { - $arr_js = $arr_js . '"' . $path . '",'; - } - $arr_js = $arr_js . ']'; - wp_add_inline_script('typebot', $arr_js); - } else { - wp_add_inline_script('typebot', 'const typebot_include_paths = null'); - } - $button_color = '#0042DA'; - if ( - get_option('button_color') !== null && - get_option('button_color') !== '' - ) { - $button_color = get_option('button_color'); - } - $params = - '{ - url: "' . - $url . - '", - autoOpenDelay: ' . - (get_option('chat_delay') === '' || get_option('chat_delay') === null - ? 'undefined' - : get_option('chat_delay') * 1000) . - ', - button: { - color: "' . - $button_color . - '", - iconUrl: "' . - $chat_icon . - '", - }, - hiddenVariables: typebotWpUser, - }'; - if ( - get_option('text_content') !== '' && - get_option('text_content') !== null - ) { - $remember = - get_option('dont_show_callout_twice') === 'on' ? 'true' : 'false'; - $params = - '{ - url: "' . - $url . - '", - autoOpenDelay: ' . - (get_option('chat_delay') === '' || get_option('chat_delay') === null - ? 'undefined' - : get_option('chat_delay') * 1000) . - ', - proactiveMessage: { - avatarUrl: "' . - get_option('avatar') . - '", - textContent: "' . - get_option('text_content') . - '", - delay: ' . - get_option('bubble_delay') * 1000 . - ', - rememberClose: ' . - $remember . - ' - }, - hiddenVariables: typebotWpUser, - button: { - color: "' . - $button_color . - '", - iconUrl: "' . - $chat_icon . - '", - }, - }'; + wp_enqueue_script('typebot', 'whatever.js'); + add_filter('script_loader_tag', 'add_module_type', 10, 2); + wp_add_inline_script('typebot', $this->parse_wp_user()); + if (get_option('init_snippet') && get_option('init_snippet') !== '') { + wp_add_inline_script('typebot', get_option('init_snippet')); + wp_add_inline_script('typebot', 'Typebot.setPrefilledVariables({ typebotWpUser });'); } - wp_add_inline_script( - 'typebot', - 'if (!typebot_include_paths) { - Typebot.initBubble(' . - $params . - '); - } else if ( - typebot_include_paths.some((path) => { - let includePath = path; - let windowPath = window.location.pathname; - if (includePath.endsWith("*")) { - return windowPath.startsWith(includePath.slice(0, -1)); - } - if (includePath.endsWith("/")) { - includePath = path.slice(0, -1); - } - if (windowPath.endsWith("/")) { - windowPath = windowPath.slice(0, -1); - } - return windowPath === includePath; - }) - ) { - Typebot.initBubble(' . - $params . - '); - }' - ); } private function parse_wp_user() { $wp_user = wp_get_current_user(); - return 'if(typeof typebotWpUser === "undefined"){ - var typebotWpUser = { + return 'if(typeof window.typebotWpUser === "undefined"){ + window.typebotWpUser = { wp_id:"' . $wp_user->ID . '", @@ -238,69 +52,29 @@ private function parse_wp_user() public function add_typebot_container($attributes = []) { + $lib_url = "https://cdn.jsdelivr.net/npm/@typebot.io/js@0.0.9/dist/web.js"; $width = '100%'; $height = '500px'; - $bg_color = 'rgba(255, 255, 255, 0)'; - $url = str_starts_with(get_option('url'), 'https://typebot.io') - ? 'https://viewer.typebot.io' . '/' . explode('/', get_option('url'))[3] - : get_option('url'); - if (is_array($attributes)) { - if (array_key_exists('width', $attributes)) { - $width = sanitize_text_field($attributes['width']); - } - if (array_key_exists('height', $attributes)) { - $height = sanitize_text_field($attributes['height']); - } - if (array_key_exists('background-color', $attributes)) { - $bg_color = sanitize_text_field($attributes['background-color']); - } - if (array_key_exists('url', $attributes)) { - $url = sanitize_text_field($attributes['url']); - } + if (array_key_exists('width', $attributes)) { + $width = sanitize_text_field($attributes['width']); + } + if (array_key_exists('height', $attributes)) { + $height = sanitize_text_field($attributes['height']); } - if (!$url) { + if (array_key_exists('typebot', $attributes)) { + $typebot = sanitize_text_field($attributes['typebot']); + } + if (!$typebot) { return; } - $container_id = 'typebot-container-' . $this->generateRandomString(4); - $bot_initializer = - 'var typebot = Typebot.initContainer("' . - $container_id . - '",{ - hiddenVariables: typebotWpUser, - url: "' . - $url . - '", - })'; + $id = $this->generateRandomString(); - return '' . - '
- '; + $bot_initializer = ''; + + return '' . $bot_initializer; } private function generateRandomString($length = 10) diff --git a/packages/wordpress/trunk/typebot.php b/packages/wordpress/trunk/typebot.php index 64173a6c74..41f6fb48f3 100644 --- a/packages/wordpress/trunk/typebot.php +++ b/packages/wordpress/trunk/typebot.php @@ -3,7 +3,7 @@ /** * Plugin Name: Typebot * Description: Convert more with conversational forms - * Version: 2.1.11 + * Version: 3.0.0 * Author: Typebot * Author URI: http://typebot.io/ * License: GPL-2.0+ @@ -16,7 +16,7 @@ die(); } -define('TYPEBOT_VERSION', '2.1.11'); +define('TYPEBOT_VERSION', '3.0.0'); function activate_typebot() { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ed2392cc0..9459a932ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,13 +22,6 @@ importers: '@chakra-ui/react': 2.5.0 '@chakra-ui/styled-system': 2.5.1 '@chakra-ui/theme-tools': ^2.0.16 - '@codemirror/lang-css': 6.0.1 - '@codemirror/lang-html': 6.4.1 - '@codemirror/lang-javascript': 6.1.2 - '@codemirror/lang-json': 6.0.1 - '@codemirror/lint': 6.1.0 - '@codemirror/state': 6.2.0 - '@codemirror/theme-one-dark': ^6.1.0 '@dnd-kit/core': 6.0.7 '@dnd-kit/sortable': 7.0.2 '@dnd-kit/utilities': 3.2.1 @@ -50,6 +43,8 @@ importers: '@trpc/next': 10.9.0 '@trpc/react-query': 10.9.0 '@trpc/server': 10.9.0 + '@typebot.io/js': workspace:* + '@typebot.io/react': workspace:* '@types/canvas-confetti': 1.6.0 '@types/google-spreadsheet': 3.3.0 '@types/jsonwebtoken': 9.0.1 @@ -70,9 +65,12 @@ importers: '@udecode/plate-serializer-html': 19.2.0 '@udecode/plate-ui-link': 19.2.0 '@udecode/plate-ui-toolbar': 19.2.0 + '@uiw/codemirror-extensions-langs': ^4.19.7 + '@uiw/codemirror-theme-github': ^4.19.7 + '@uiw/codemirror-theme-tokyo-night': ^4.19.7 + '@uiw/react-codemirror': ^4.19.7 '@use-gesture/react': ^10.2.24 aws-sdk: 2.1304.0 - bot-engine: workspace:* browser-image-compression: 2.0.0 canvas-confetti: 1.6.0 chakra-react-select: ^4.4.3 @@ -121,7 +119,6 @@ importers: tinycolor2: 1.5.2 trpc-openapi: 1.1.2 tsconfig: workspace:* - typebot-js: workspace:* typescript: 4.9.4 use-debounce: 9.0.3 utils: workspace:* @@ -131,13 +128,6 @@ importers: '@chakra-ui/css-reset': 2.0.11_hp5f5nkljdiwilp4rgxyefcplu '@chakra-ui/react': 2.5.0_rmkrbeisirqa7nsh5de33nr2se '@chakra-ui/theme-tools': 2.0.16_ak67p4fuwhwgy7hpxa6ejvcgoy - '@codemirror/lang-css': 6.0.1_nzpoxphwgc7witc3f5hdaoweju - '@codemirror/lang-html': 6.4.1 - '@codemirror/lang-javascript': 6.1.2 - '@codemirror/lang-json': 6.0.1 - '@codemirror/lint': 6.1.0 - '@codemirror/state': 6.2.0 - '@codemirror/theme-one-dark': 6.1.0 '@dnd-kit/core': 6.0.7_biqbaboplfbrettd7655fr4n2y '@dnd-kit/sortable': 7.0.2_pmudlfv2z3i7vvlookxjkeidxe '@dnd-kit/utilities': 3.2.1_react@18.2.0 @@ -158,6 +148,8 @@ importers: '@trpc/next': 10.9.0_5z6xuiz5uv65txozaje5ixjdkm '@trpc/react-query': 10.9.0_3y2fjou3ckwba5czteau6vvsje '@trpc/server': 10.9.0 + '@typebot.io/js': link:../../packages/js + '@typebot.io/react': link:../../packages/react '@udecode/plate-basic-marks': 19.2.0_6cxv2rkchn37mmk4ursbonwz7a '@udecode/plate-common': 7.0.2_anhkvwz6zcr7uznpbsjwteukiu '@udecode/plate-core': 19.2.0_6cxv2rkchn37mmk4ursbonwz7a @@ -165,9 +157,12 @@ importers: '@udecode/plate-serializer-html': 19.2.0_xdtsvcftqex5yu43odi3bb6j7u '@udecode/plate-ui-link': 19.2.0_zvntoaj4wsvigktaodqapm7npu '@udecode/plate-ui-toolbar': 19.2.0_zvntoaj4wsvigktaodqapm7npu + '@uiw/codemirror-extensions-langs': 4.19.7_brqhm5cu2qxolzjg2togia2d7m + '@uiw/codemirror-theme-github': 4.19.7_a2usvefncw26hnlzt6oyk4qbeq + '@uiw/codemirror-theme-tokyo-night': 4.19.7_a2usvefncw26hnlzt6oyk4qbeq + '@uiw/react-codemirror': 4.19.7_pzrqepnlx4uklbalwyntc26e4u '@use-gesture/react': 10.2.24_react@18.2.0 aws-sdk: 2.1304.0 - bot-engine: link:../../packages/bot-engine browser-image-compression: 2.0.0 canvas-confetti: 1.6.0 chakra-react-select: 4.4.3_ujih3xq5xzlagyvac65tb5w3ru @@ -208,7 +203,6 @@ importers: swr: 2.0.3_react@18.2.0 tinycolor2: 1.5.2 trpc-openapi: 1.1.2_v7aw5md42ua3rf277jyeogdfny - typebot-js: link:../../packages/typebot-js use-debounce: 9.0.3_react@18.2.0 devDependencies: '@babel/core': 7.20.12 @@ -3341,8 +3335,15 @@ packages: '@lezer/common': 1.0.2 dev: false - /@codemirror/lang-css/6.0.1_nzpoxphwgc7witc3f5hdaoweju: - resolution: {integrity: sha512-rlLq1Dt0WJl+2epLQeAsfqIsx3lGu4HStHCJu95nGGuz2P2fNugbU3dQYafr2VRjM4eMC9HviI6jvS98CNtG5w==} + /@codemirror/lang-cpp/6.0.2: + resolution: {integrity: sha512-6oYEYUKHvrnacXxWxYa6t4puTlbN3dgV662BDfSH8+MfjQjVmP697/KYTDOqpxgerkvoNm7q5wlFMBeX8ZMocg==} + dependencies: + '@codemirror/language': 6.4.0 + '@lezer/cpp': 1.0.0 + dev: false + + /@codemirror/lang-css/6.0.2_nzpoxphwgc7witc3f5hdaoweju: + resolution: {integrity: sha512-4V4zmUOl2Glx0GWw0HiO1oGD4zvMlIQ3zx5hXOE6ipCjhohig2bhWRAasrZylH9pRNTcl1VMa59Lsl8lZWlTzw==} dependencies: '@codemirror/autocomplete': 6.4.0_3jfk5zzaw77h3sqp5wt6b7asku '@codemirror/language': 6.4.0 @@ -3357,7 +3358,7 @@ packages: resolution: {integrity: sha512-9NzhWKAkWEwjXC04DKM6yrHnxIPFTqZNLDhWfZiKLMxUiU++XoHz9n6D5EPp1igBmX0vXcpFb5Kud6XzIJhZ4A==} dependencies: '@codemirror/autocomplete': 6.4.0_3jfk5zzaw77h3sqp5wt6b7asku - '@codemirror/lang-css': 6.0.1_nzpoxphwgc7witc3f5hdaoweju + '@codemirror/lang-css': 6.0.2_nzpoxphwgc7witc3f5hdaoweju '@codemirror/lang-javascript': 6.1.2 '@codemirror/language': 6.4.0 '@codemirror/state': 6.2.0 @@ -3367,6 +3368,13 @@ packages: '@lezer/html': 1.3.0 dev: false + /@codemirror/lang-java/6.0.1: + resolution: {integrity: sha512-OOnmhH67h97jHzCuFaIEspbmsT98fNdhVhmA3zCxW0cn7l8rChDhZtwiwJ/JOKXgfm4J+ELxQihxaI7bj7mJRg==} + dependencies: + '@codemirror/language': 6.4.0 + '@lezer/java': 1.0.0 + dev: false + /@codemirror/lang-javascript/6.1.2: resolution: {integrity: sha512-OcwLfZXdQ1OHrLiIcKCn7MqZ7nx205CMKlhe+vL88pe2ymhT9+2P+QhwkYGxMICj8TDHyp8HFKVwpiisUT7iEQ==} dependencies: @@ -3386,6 +3394,125 @@ packages: '@lezer/json': 1.0.0 dev: false + /@codemirror/lang-lezer/6.0.1: + resolution: {integrity: sha512-WHwjI7OqKFBEfkunohweqA5B/jIlxaZso6Nl3weVckz8EafYbPZldQEKSDb4QQ9H9BUkle4PVELP4sftKoA0uQ==} + dependencies: + '@codemirror/language': 6.4.0 + '@codemirror/state': 6.2.0 + '@lezer/common': 1.0.2 + '@lezer/lezer': 1.1.0 + dev: false + + /@codemirror/lang-markdown/6.0.5: + resolution: {integrity: sha512-qH0THRYc2M7pIJoAp6jstXZkv8ZMVhNaBm7Bs4+0SLHhHlwX53txFy98AcPwrfq0Sh8Zi6RAuj9j/GyL8E1MKw==} + dependencies: + '@codemirror/lang-html': 6.4.1 + '@codemirror/language': 6.4.0 + '@codemirror/state': 6.2.0 + '@codemirror/view': 6.7.3 + '@lezer/common': 1.0.2 + '@lezer/markdown': 1.0.2 + dev: false + + /@codemirror/lang-php/6.0.1: + resolution: {integrity: sha512-ublojMdw/PNWa7qdN5TMsjmqkNuTBD3k6ndZ4Z0S25SBAiweFGyY68AS3xNcIOlb6DDFDvKlinLQ40vSLqf8xA==} + dependencies: + '@codemirror/lang-html': 6.4.1 + '@codemirror/language': 6.4.0 + '@codemirror/state': 6.2.0 + '@lezer/common': 1.0.2 + '@lezer/php': 1.0.1 + dev: false + + /@codemirror/lang-python/6.1.1_gmyi65uhzwvxi3jypseoerg22u: + resolution: {integrity: sha512-AddGMIKUssUAqaDKoxKWA5GAzy/CVE0eSY7/ANgNzdS1GYBkp6N49XKEyMElkuN04UsZ+bTIQdj+tVV75NMwJw==} + dependencies: + '@codemirror/autocomplete': 6.4.0_3jfk5zzaw77h3sqp5wt6b7asku + '@codemirror/language': 6.4.0 + '@lezer/python': 1.1.1 + transitivePeerDependencies: + - '@codemirror/state' + - '@codemirror/view' + - '@lezer/common' + dev: false + + /@codemirror/lang-rust/6.0.1: + resolution: {integrity: sha512-344EMWFBzWArHWdZn/NcgkwMvZIWUR1GEBdwG8FEp++6o6vT6KL9V7vGs2ONsKxxFUPXKI0SPcWhyYyl2zPYxQ==} + dependencies: + '@codemirror/language': 6.4.0 + '@lezer/rust': 1.0.0 + dev: false + + /@codemirror/lang-sql/6.3.3_nzpoxphwgc7witc3f5hdaoweju: + resolution: {integrity: sha512-VNsHju8500fkiDyDU8jZyGQ8M0iXU0SmfeCoCeAYkACcEFlX63BOT8311pICXyw43VYRbS23w54RgSEQmixGjQ==} + dependencies: + '@codemirror/autocomplete': 6.4.0_3jfk5zzaw77h3sqp5wt6b7asku + '@codemirror/language': 6.4.0 + '@codemirror/state': 6.2.0 + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.2.5 + transitivePeerDependencies: + - '@codemirror/view' + - '@lezer/common' + dev: false + + /@codemirror/lang-sql/6.4.0_nzpoxphwgc7witc3f5hdaoweju: + resolution: {integrity: sha512-UWGK1+zc9+JtkiT+XxHByp4N6VLgLvC2x0tIudrJG26gyNtn0hWOVoB0A8kh/NABPWkKl3tLWDYf2qOBJS9Zdw==} + dependencies: + '@codemirror/autocomplete': 6.4.0_3jfk5zzaw77h3sqp5wt6b7asku + '@codemirror/language': 6.4.0 + '@codemirror/state': 6.2.0 + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.1 + transitivePeerDependencies: + - '@codemirror/view' + - '@lezer/common' + dev: false + + /@codemirror/lang-wast/6.0.1: + resolution: {integrity: sha512-sQLsqhRjl2MWG3rxZysX+2XAyed48KhLBHLgq9xcKxIJu3npH/G+BIXW5NM5mHeDUjG0jcGh9BcjP0NfMStuzA==} + dependencies: + '@codemirror/language': 6.4.0 + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.2.5 + dev: false + + /@codemirror/lang-xml/6.0.2_@codemirror+view@6.7.3: + resolution: {integrity: sha512-JQYZjHL2LAfpiZI2/qZ/qzDuSqmGKMwyApYmEUUCTxLM4MWS7sATUEfIguZQr9Zjx/7gcdnewb039smF6nC2zw==} + dependencies: + '@codemirror/autocomplete': 6.4.0_3jfk5zzaw77h3sqp5wt6b7asku + '@codemirror/language': 6.4.0 + '@codemirror/state': 6.2.0 + '@lezer/common': 1.0.2 + '@lezer/xml': 1.0.1 + transitivePeerDependencies: + - '@codemirror/view' + dev: false + + /@codemirror/language-data/6.1.0_gmyi65uhzwvxi3jypseoerg22u: + resolution: {integrity: sha512-g9V23fuLRI9AEbpM6bDy1oquqgpFlIDHTihUhL21NPmxp+x67ZJbsKk+V71W7/Bj8SCqEO1PtqQA/tDGgt1nfw==} + dependencies: + '@codemirror/lang-cpp': 6.0.2 + '@codemirror/lang-css': 6.0.2_nzpoxphwgc7witc3f5hdaoweju + '@codemirror/lang-html': 6.4.1 + '@codemirror/lang-java': 6.0.1 + '@codemirror/lang-javascript': 6.1.2 + '@codemirror/lang-json': 6.0.1 + '@codemirror/lang-markdown': 6.0.5 + '@codemirror/lang-php': 6.0.1 + '@codemirror/lang-python': 6.1.1_gmyi65uhzwvxi3jypseoerg22u + '@codemirror/lang-rust': 6.0.1 + '@codemirror/lang-sql': 6.4.0_nzpoxphwgc7witc3f5hdaoweju + '@codemirror/lang-wast': 6.0.1 + '@codemirror/lang-xml': 6.0.2_@codemirror+view@6.7.3 + '@codemirror/language': 6.4.0 + '@codemirror/legacy-modes': 6.3.1 + transitivePeerDependencies: + - '@codemirror/state' + - '@codemirror/view' + - '@lezer/common' + dev: false + /@codemirror/language/6.4.0: resolution: {integrity: sha512-Wzb7GnNj8vnEtbPWiOy9H0m1fBtE28kepQNGLXekU2EEZv43BF865VKITUn+NoV8OpW6gRtvm29YEhqm46927Q==} dependencies: @@ -3393,10 +3520,16 @@ packages: '@codemirror/view': 6.7.3 '@lezer/common': 1.0.2 '@lezer/highlight': 1.1.3 - '@lezer/lr': 1.3.3 + '@lezer/lr': 1.3.1 style-mod: 4.0.0 dev: false + /@codemirror/legacy-modes/6.3.1: + resolution: {integrity: sha512-icXmCs4Mhst2F8mE0TNpmG6l7YTj1uxam3AbZaFaabINH5oWAdg2CfR/PVi+d/rqxJ+TuTnvkKK5GILHrNThtw==} + dependencies: + '@codemirror/language': 6.4.0 + dev: false + /@codemirror/lint/6.1.0: resolution: {integrity: sha512-mdvDQrjRmYPvQ3WrzF6Ewaao+NWERYtpthJvoQ3tK3t/44Ynhk8ZGjTSL9jMEv8CgSMogmt75X8ceOZRDSXHtQ==} dependencies: @@ -5615,11 +5748,18 @@ packages: resolution: {integrity: sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng==} dev: false + /@lezer/cpp/1.0.0: + resolution: {integrity: sha512-Klk3/AIEKoptmm6cNm7xTulNXjdTKkD+hVOEcz/NeRg8tIestP5hsGHJeFDR/XtyDTxsjoPjKZRIGohht7zbKw==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.1 + dev: false + /@lezer/css/1.1.1: resolution: {integrity: sha512-mSjx+unLLapEqdOYDejnGBokB5+AiJKZVclmud0MKQOKx3DLJ5b5VTCstgDDknR6iIV4gVrN6euzsCnj0A2gQA==} dependencies: '@lezer/highlight': 1.1.3 - '@lezer/lr': 1.3.3 + '@lezer/lr': 1.3.1 dev: false /@lezer/highlight/1.1.3: @@ -5633,29 +5773,84 @@ packages: dependencies: '@lezer/common': 1.0.2 '@lezer/highlight': 1.1.3 - '@lezer/lr': 1.3.3 + '@lezer/lr': 1.3.1 + dev: false + + /@lezer/java/1.0.0: + resolution: {integrity: sha512-z2EA0JHq2WoiKfQy5uOOd4t17PJtq8guh58gPkSzOnNcQ7DNbkrU+Axak+jL8+Noinwyz2tRNOseQFj+Tg+P0A==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.1 dev: false /@lezer/javascript/1.4.1: resolution: {integrity: sha512-Hqx36DJeYhKtdpc7wBYPR0XF56ZzIp0IkMO/zNNj80xcaFOV4Oj/P7TQc/8k2TxNhzl7tV5tXS8ZOCPbT4L3nA==} dependencies: '@lezer/highlight': 1.1.3 - '@lezer/lr': 1.3.3 + '@lezer/lr': 1.3.1 dev: false /@lezer/json/1.0.0: resolution: {integrity: sha512-zbAuUY09RBzCoCA3lJ1+ypKw5WSNvLqGMtasdW6HvVOqZoCpPr8eWrsGnOVWGKGn8Rh21FnrKRVlJXrGAVUqRw==} dependencies: '@lezer/highlight': 1.1.3 - '@lezer/lr': 1.3.3 + '@lezer/lr': 1.3.1 + dev: false + + /@lezer/lezer/1.1.0: + resolution: {integrity: sha512-XTomM3C2MzHNuZwjYbyYZ44IRV6rHIOvi++yAD1O4djlDoKAnikx3BFoREK2g/z8zUIc/kyWuZO9W9xN4/OR1g==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.1 + dev: false + + /@lezer/lr/1.2.5: + resolution: {integrity: sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==} + dependencies: + '@lezer/common': 1.0.2 dev: false - /@lezer/lr/1.3.3: - resolution: {integrity: sha512-JPQe3mwJlzEVqy67iQiiGozhcngbO8QBgpqZM6oL1Wj/dXckrEexpBLeFkq0edtW5IqnPRFxA24BHJni8Js69w==} + /@lezer/lr/1.3.1: + resolution: {integrity: sha512-+GymJB/+3gThkk2zHwseaJTI5oa4AuOuj1I2LCslAVq1dFZLSX8SAe4ZlJq1TjezteDXtF/+d4qeWz9JvnrG9Q==} dependencies: '@lezer/common': 1.0.2 dev: false + /@lezer/markdown/1.0.2: + resolution: {integrity: sha512-8CY0OoZ6V5EzPjSPeJ4KLVbtXdLBd8V6sRCooN5kHnO28ytreEGTyrtU/zUwo/XLRzGr/e1g44KlzKi3yWGB5A==} + dependencies: + '@lezer/common': 1.0.2 + '@lezer/highlight': 1.1.3 + dev: false + + /@lezer/php/1.0.1: + resolution: {integrity: sha512-aqdCQJOXJ66De22vzdwnuC502hIaG9EnPK2rSi+ebXyUd+j7GAX1mRjWZOVOmf3GST1YUfUCu6WXDiEgDGOVwA==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.1 + dev: false + + /@lezer/python/1.1.1: + resolution: {integrity: sha512-ArUGh9kvdaOVu6IkSaYUS9WFQeMAFVWKRuZo6vexnxoeCLnxf0Y9DCFEAMMa7W9SQBGYE55OarSpPqSkdOXSCA==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.1 + dev: false + + /@lezer/rust/1.0.0: + resolution: {integrity: sha512-IpGAxIjNxYmX9ra6GfQTSPegdCAWNeq23WNmrsMMQI7YNSvKtYxO4TX5rgZUmbhEucWn0KTBMeDEPXg99YKtTA==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.1 + dev: false + + /@lezer/xml/1.0.1: + resolution: {integrity: sha512-jMDXrV953sDAUEMI25VNrI9dz94Ai96FfeglytFINhhwQ867HKlCE2jt3AwZTCT7M528WxdDWv/Ty8e9wizwmQ==} + dependencies: + '@lezer/highlight': 1.1.3 + '@lezer/lr': 1.3.1 + dev: false + /@mdx-js/mdx/1.6.22: resolution: {integrity: sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA==} dependencies: @@ -7741,6 +7936,112 @@ packages: - scheduler dev: false + /@uiw/codemirror-extensions-basic-setup/4.19.7_z4w2cb7jeeiewgtgrbfykr2vnq: + resolution: {integrity: sha512-pk0JTFJAZOGxouBB1pdBtb59qKibO9DW4hER4VSFGBGW3TBDeXUwL/gKujhRpySHG2MtG09cgt4a3XOFIWwXTw==} + peerDependencies: + '@codemirror/autocomplete': '>=6.0.0' + '@codemirror/commands': '>=6.0.0' + '@codemirror/language': '>=6.0.0' + '@codemirror/lint': '>=6.0.0' + '@codemirror/search': '>=6.0.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + dependencies: + '@codemirror/autocomplete': 6.4.0_3jfk5zzaw77h3sqp5wt6b7asku + '@codemirror/commands': 6.2.0 + '@codemirror/language': 6.4.0 + '@codemirror/lint': 6.1.0 + '@codemirror/search': 6.2.3 + '@codemirror/state': 6.2.0 + '@codemirror/view': 6.7.3 + dev: false + + /@uiw/codemirror-extensions-langs/4.19.7_brqhm5cu2qxolzjg2togia2d7m: + resolution: {integrity: sha512-dqaOFrwv79y93pBIDJ41fWRxF4JP9ij/PVFi9YxHNILNN1RqdS0uDFj8VJ8aeHPM8w0dmQZUoDP3qySru+r5yA==} + peerDependencies: + '@codemirror/language-data': '>=6.0.0' + '@codemirror/legacy-modes': '>=6.0.0' + dependencies: + '@codemirror/lang-cpp': 6.0.2 + '@codemirror/lang-html': 6.4.1 + '@codemirror/lang-java': 6.0.1 + '@codemirror/lang-javascript': 6.1.2 + '@codemirror/lang-json': 6.0.1 + '@codemirror/lang-lezer': 6.0.1 + '@codemirror/lang-markdown': 6.0.5 + '@codemirror/lang-php': 6.0.1 + '@codemirror/lang-python': 6.1.1_gmyi65uhzwvxi3jypseoerg22u + '@codemirror/lang-rust': 6.0.1 + '@codemirror/lang-sql': 6.3.3_nzpoxphwgc7witc3f5hdaoweju + '@codemirror/lang-wast': 6.0.1 + '@codemirror/lang-xml': 6.0.2_@codemirror+view@6.7.3 + '@codemirror/language-data': 6.1.0_gmyi65uhzwvxi3jypseoerg22u + '@codemirror/legacy-modes': 6.3.1 + transitivePeerDependencies: + - '@codemirror/state' + - '@codemirror/view' + - '@lezer/common' + dev: false + + /@uiw/codemirror-theme-github/4.19.7_a2usvefncw26hnlzt6oyk4qbeq: + resolution: {integrity: sha512-yeBKOS31bJWvxHWv/hO8CC5Hz657poyuCRX533B+7c2W3B5TX48i49GC9k91yX/BfCsTVqneONk4ZgRWGJkDww==} + dependencies: + '@uiw/codemirror-themes': 4.19.7_a2usvefncw26hnlzt6oyk4qbeq + transitivePeerDependencies: + - '@codemirror/language' + - '@codemirror/state' + - '@codemirror/view' + dev: false + + /@uiw/codemirror-theme-tokyo-night/4.19.7_a2usvefncw26hnlzt6oyk4qbeq: + resolution: {integrity: sha512-D/oLEsamQ7goFYnP46kyrt60vc2Tx8nC9FWyHqJvD3DL8+ugUsQvCL2oIadCadfMm7pQrFj1ILfokb8uC9W5eg==} + dependencies: + '@uiw/codemirror-themes': 4.19.7_a2usvefncw26hnlzt6oyk4qbeq + transitivePeerDependencies: + - '@codemirror/language' + - '@codemirror/state' + - '@codemirror/view' + dev: false + + /@uiw/codemirror-themes/4.19.7_a2usvefncw26hnlzt6oyk4qbeq: + resolution: {integrity: sha512-M/42RkPI60ItlssmNuEoZO2MQvlY6fRmdX7XRUAhKjxczZoaq8xS6HIvv1whGf2zGsTrwdVTPCm6ls0l17dvPA==} + peerDependencies: + '@codemirror/language': '>=6.0.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + dependencies: + '@codemirror/language': 6.4.0 + '@codemirror/state': 6.2.0 + '@codemirror/view': 6.7.3 + dev: false + + /@uiw/react-codemirror/4.19.7_pzrqepnlx4uklbalwyntc26e4u: + resolution: {integrity: sha512-IHvpYWVSdiaHX0Fk6oY6YyAJigDnyvSpWKNUTRzsMNxB+8/wqZ8lior4TprXH0zyLxW5F1+bTyifFFTeg+X3Sw==} + peerDependencies: + '@babel/runtime': '>=7.11.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/theme-one-dark': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + codemirror: '>=6.0.0' + react: '>=16.8.0' + react-dom: '>=16.8.0' + dependencies: + '@babel/runtime': 7.20.13 + '@codemirror/commands': 6.2.0 + '@codemirror/state': 6.2.0 + '@codemirror/theme-one-dark': 6.1.0 + '@codemirror/view': 6.7.3 + '@uiw/codemirror-extensions-basic-setup': 4.19.7_z4w2cb7jeeiewgtgrbfykr2vnq + codemirror: 6.0.1_@lezer+common@1.0.2 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + transitivePeerDependencies: + - '@codemirror/autocomplete' + - '@codemirror/language' + - '@codemirror/lint' + - '@codemirror/search' + dev: false + /@use-gesture/core/10.2.24: resolution: {integrity: sha512-ZL7F9mgOn3Qlnp6QLI9jaOfcvqrx6JPE/BkdVSd8imveaFTm/a3udoO6f5Us/1XtqnL4347PsIiK6AtCvMHk2Q==} dev: false