2
0

Add dark mode (#191)

Closes #189
This commit is contained in:
Baptiste Arnaud
2022-12-20 16:55:43 +01:00
committed by GitHub
parent 054cbb3585
commit 3394fa5e0a
77 changed files with 1782 additions and 601 deletions

View File

@@ -12,8 +12,10 @@
"test:report": "pnpm playwright show-report" "test:report": "pnpm playwright show-report"
}, },
"dependencies": { "dependencies": {
"@chakra-ui/anatomy": "^2.1.0",
"@chakra-ui/css-reset": "2.0.10", "@chakra-ui/css-reset": "2.0.10",
"@chakra-ui/react": "2.4.4", "@chakra-ui/react": "2.4.4",
"@chakra-ui/theme-tools": "^2.0.14",
"@codemirror/lang-css": "6.0.1", "@codemirror/lang-css": "6.0.1",
"@codemirror/lang-html": "6.4.0", "@codemirror/lang-html": "6.4.0",
"@codemirror/lang-javascript": "6.1.2", "@codemirror/lang-javascript": "6.1.2",
@@ -51,6 +53,7 @@
"bot-engine": "workspace:*", "bot-engine": "workspace:*",
"browser-image-compression": "2.0.0", "browser-image-compression": "2.0.0",
"canvas-confetti": "1.6.0", "canvas-confetti": "1.6.0",
"chakra-react-select": "^4.4.3",
"codemirror": "6.0.1", "codemirror": "6.0.1",
"cuid": "2.1.8", "cuid": "2.1.8",
"deep-object-diff": "1.1.9", "deep-object-diff": "1.1.9",
@@ -87,6 +90,7 @@
"styled-components": "5.3.6", "styled-components": "5.3.6",
"svg-round-corners": "0.4.1", "svg-round-corners": "0.4.1",
"swr": "2.0.0", "swr": "2.0.0",
"thememirror": "^2.0.1",
"tinycolor2": "1.4.2", "tinycolor2": "1.4.2",
"trpc-openapi": "1.0.0", "trpc-openapi": "1.0.0",
"typebot-js": "workspace:*", "typebot-js": "workspace:*",

View File

@@ -0,0 +1,638 @@
{
"id": "cl128l5vx007509il86n74oer",
"createdAt": "2022-03-22T14:33:05.037Z",
"updatedAt": "2022-03-22T16:33:37.928Z",
"name": "Onboarding",
"publishedTypebotId": "cl128n64i00092e69wenv1dlx",
"folderId": null,
"groups": [
{
"id": "cl1265zct0000mb1a6bir36w7",
"blocks": [
{
"id": "cl1265zct0001mb1afel460do",
"type": "start",
"label": "Start",
"groupId": "cl1265zct0000mb1a6bir36w7",
"outgoingEdgeId": "cl1266kt100082e6d1wks5dtp"
}
],
"title": "Start",
"graphCoordinates": { "x": 0, "y": 0 }
},
{
"id": "cl1266bah00032e6dgdnj4vgz",
"blocks": [
{
"id": "cl1266bam00042e6dm0gn22vy",
"type": "Condition",
"items": [
{
"id": "cl1266bam00052e6dn1sdjnax",
"type": 1,
"blockId": "cl1266bam00042e6dm0gn22vy",
"content": {
"comparisons": [
{
"id": "cl1266cg600062e6d76qwk74v",
"variableId": "cl126f4hf000i2e6d8zvzc3t1",
"comparisonOperator": "Is set"
}
],
"logicalOperator": "AND"
},
"outgoingEdgeId": "cl12bk3j6000c2e69bak89ja9"
}
],
"groupId": "cl1266bah00032e6dgdnj4vgz",
"outgoingEdgeId": "cl12bnfyd000g2e69g7lr3czq"
}
],
"title": "Group #1",
"graphCoordinates": { "x": 266, "y": 162 }
},
{
"id": "cl1267q1z000d2e6d949f2ge4",
"blocks": [
{
"id": "cl1267q2c000e2e6dynjeg83n",
"type": "text",
"groupId": "cl1267q1z000d2e6d949f2ge4",
"content": {
"html": "<div>Welcome 👋</div>",
"richText": [
{ "type": "p", "children": [{ "text": "Welcome 👋" }] }
],
"plainText": "Welcome 👋"
}
},
{
"id": "cl1267y1u000f2e6d4rlglv6g",
"type": "text",
"groupId": "cl1267q1z000d2e6d949f2ge4",
"content": {
"html": "<div>What&#x27;s your name?</div>",
"richText": [
{ "type": "p", "children": [{ "text": "What's your name?" }] }
],
"plainText": "What's your name?"
}
},
{
"id": "cl126820m000g2e6dfleq78bt",
"type": "text input",
"groupId": "cl1267q1z000d2e6d949f2ge4",
"options": {
"isLong": false,
"labels": {
"button": "Send",
"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"
}
],
"title": "Group #3",
"graphCoordinates": { "x": 269, "y": 381 }
},
{
"id": "cl126ixoq000p2e6dfbz9sype",
"blocks": [
{
"id": "cl1266v6f000a2e6db7wj3ux7",
"type": "text",
"groupId": "cl126ixoq000p2e6dfbz9sype",
"content": {
"html": "<div>Welcome {{Name}} 👋</div>",
"richText": [
{ "type": "p", "children": [{ "text": "Welcome {{Name}} 👋" }] }
],
"plainText": "Welcome {{Name}} 👋"
}
},
{
"id": "cl126hb9m000l2e6d5qk3mohn",
"type": "text",
"groupId": "cl126ixoq000p2e6dfbz9sype",
"content": {
"html": "<div>I&#x27;m super pumped that you&#x27;ve decided to try out Typebot 😍</div>",
"richText": [
{
"type": "p",
"children": [
{
"text": "I'm super pumped that you've decided to try out Typebot 😍"
}
]
}
],
"plainText": "I'm super pumped that you've decided to try out Typebot 😍"
}
},
{
"id": "cl126hpw1000m2e6dneousygl",
"type": "text",
"groupId": "cl126ixoq000p2e6dfbz9sype",
"content": {
"html": "<div>You are small steps away from meaningful, hyper-personalized experience for your users</div>",
"richText": [
{
"type": "p",
"children": [
{
"text": "You are small steps away from meaningful, hyper-personalized experience for your users"
}
]
}
],
"plainText": "You are small steps away from meaningful, hyper-personalized experience for your users"
}
},
{
"id": "cl126guhd000k2e6d6ypkex9z",
"type": "text",
"groupId": "cl126ixoq000p2e6dfbz9sype",
"content": {
"html": "<div>Let&#x27;s get you set up for your Typebot journey.</div>",
"richText": [
{
"type": "p",
"children": [
{ "text": "Let's get you set up for your Typebot journey." }
]
}
],
"plainText": "Let's get you set up for your Typebot journey."
}
},
{
"id": "cl126ixp9000q2e6dslh0zypi",
"type": "text",
"groupId": "cl126ixoq000p2e6dfbz9sype",
"content": {
"html": "<div>Do you work for a specific company?</div>",
"richText": [
{
"type": "p",
"children": [{ "text": "Do you work for a specific company?" }]
}
],
"plainText": "Do you work for a specific company?"
}
},
{
"id": "cl126jb2q000r2e6dgqlnxnt8",
"type": "choice input",
"items": [
{
"id": "cl126jb2q000s2e6dm60yq5p2",
"type": 0,
"blockId": "cl126jb2q000r2e6dgqlnxnt8",
"content": "Yes",
"outgoingEdgeId": "cl126jsoo000x2e6ditu7dgf8"
},
{
"id": "cl126jc5a000t2e6dqv91w7j6",
"type": 0,
"blockId": "cl126jb2q000r2e6dgqlnxnt8",
"content": "No",
"outgoingEdgeId": "cl126l5tx00122e6dmisci6h5"
}
],
"groupId": "cl126ixoq000p2e6dfbz9sype",
"options": { "buttonLabel": "Send", "isMultipleChoice": false }
}
],
"title": "Group #5",
"graphCoordinates": { "x": 614, "y": 244 }
},
{
"id": "cl126jioj000u2e6dqssno3hv",
"blocks": [
{
"id": "cl126jioz000v2e6dwrk1f2cb",
"type": "text input",
"groupId": "cl126jioj000u2e6dqssno3hv",
"options": {
"isLong": false,
"labels": {
"button": "Send",
"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"
}
],
"title": "Group #6",
"graphCoordinates": { "x": 969, "y": 308 }
},
{
"id": "cl126krbp00102e6dnjelmfa1",
"blocks": [
{
"id": "cl126krck00112e6d1m6ctxpn",
"type": "text",
"groupId": "cl126krbp00102e6dnjelmfa1",
"content": {
"html": "<div>What type of forms are you planning to build with Typebot?</div>",
"richText": [
{
"type": "p",
"children": [
{
"text": "What type of forms are you planning to build with Typebot?"
}
]
}
],
"plainText": "What type of forms are you planning to build with Typebot?"
}
},
{
"id": "cl126lb8v00142e6duv5qe08l",
"type": "choice input",
"items": [
{
"id": "cl126onz9001g2e6dk0nbjeu6",
"type": 0,
"blockId": "cl126lb8v00142e6duv5qe08l",
"content": "Lead qualification"
},
{
"id": "cl126lm6c00172e6d1pfvdiju",
"type": 0,
"blockId": "cl126lb8v00142e6duv5qe08l",
"content": "Customer support"
},
{
"id": "cl126orr2001h2e6d0fqs7737",
"type": 0,
"blockId": "cl126lb8v00142e6duv5qe08l",
"content": "Customer research"
},
{
"id": "cl126oudu001i2e6dktwi7qwv",
"type": 0,
"blockId": "cl126lb8v00142e6duv5qe08l",
"content": "User onboarding"
},
{
"id": "cl126luv500192e6dl317ssyr",
"type": 0,
"blockId": "cl126lb8v00142e6duv5qe08l",
"content": "Quizzes"
},
{
"id": "cl126lz8q001a2e6d8b9lb3b5",
"type": 0,
"blockId": "cl126lb8v00142e6duv5qe08l",
"content": "Content distribution"
},
{
"id": "cl126nf7k001d2e6dg2zczjgz",
"type": 0,
"blockId": "cl126lb8v00142e6duv5qe08l",
"content": "FAQ"
},
{
"id": "cl126ngy8001e2e6ddfo5s9fm",
"type": 0,
"blockId": "cl126lb8v00142e6duv5qe08l",
"content": "Other"
}
],
"groupId": "cl126krbp00102e6dnjelmfa1",
"options": {
"variableId": "cl126mo3t001b2e6dvyi16bkd",
"buttonLabel": "Send",
"isMultipleChoice": true
}
},
{
"id": "cl128ain900172e6d1osj4u90",
"type": "Code",
"groupId": "cl126krbp00102e6dnjelmfa1",
"options": {
"name": "Store categories in DB",
"content": "postMessage({from: \"typebot\", action: \"storeCategories\", content: {{Categories}}}, \"*\")"
},
"outgoingEdgeId": "cl128azam00182e6dct61k7v5"
}
],
"title": "Group #6",
"graphCoordinates": { "x": 1218, "y": 510 }
},
{
"id": "cl126p75m001j2e6d73qmes0m",
"blocks": [
{
"id": "cl126p76d001k2e6dbhnf2ysq",
"type": "text",
"groupId": "cl126p75m001j2e6d73qmes0m",
"content": {
"html": "<div>Thank you for answering those questions!</div>",
"richText": [
{
"type": "p",
"children": [
{ "text": "Thank you for answering those questions!" }
]
}
],
"plainText": "Thank you for answering those questions!"
}
},
{
"id": "cl128375600112e6d4l0jtuyf",
"type": "Code",
"groupId": "cl126p75m001j2e6d73qmes0m",
"options": {
"name": "Shoot confettis",
"content": "postMessage({from: \"typebot\", action: \"shootConfettis\"}, \"*\")"
}
},
{
"id": "cl126rfy6001t2e6d21gcb6b0",
"type": "image",
"groupId": "cl126p75m001j2e6d73qmes0m",
"content": {
"url": "https://media4.giphy.com/media/l0amJzVHIAfl7jMDos/giphy.gif?cid=fe3852a3i4c33635xdtj3nesr9uq4zteujaab6b0jr42gpxx&rid=giphy.gif&ct=g"
}
},
{
"id": "cl126txta001y2e6dtxrbsnek",
"type": "text",
"groupId": "cl126p75m001j2e6d73qmes0m",
"content": {
"html": "<div>You can reach out to me using the contact bubble on the bottom right corner 🤓</div>",
"richText": [
{
"type": "p",
"children": [
{
"text": "You can reach out to me using the contact bubble on the bottom right corner 🤓"
}
]
}
],
"plainText": "You can reach out to me using the contact bubble on the bottom right corner 🤓"
}
},
{
"id": "cl12buyly00172e6991bz38ch",
"groupId": "cl126p75m001j2e6d73qmes0m",
"type": "text",
"content": {
"html": "<div>Let&#x27;s create your first typebot...</div>",
"richText": [
{
"type": "p",
"children": [{ "text": "Let's create your first typebot..." }]
}
],
"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)"
}
}
],
"title": "Group #7",
"graphCoordinates": { "x": 1612, "y": 1103 }
},
{
"id": "cl126pv6w001n2e6dp0qkvthu",
"blocks": [
{
"id": "cl127yxym000b2e6d9hksxo6h",
"type": "text",
"groupId": "cl126pv6w001n2e6dp0qkvthu",
"content": {
"html": "<div>What else?</div>",
"richText": [
{ "type": "p", "children": [{ "text": "What else?" }] }
],
"plainText": "What else?"
}
},
{
"id": "cl126pv7n001o2e6dajltc4qz",
"type": "text input",
"groupId": "cl126pv6w001n2e6dp0qkvthu",
"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"
}
],
"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 }
}
],
"variables": [
{ "id": "cl126f4hf000i2e6d8zvzc3t1", "name": "Name" },
{ "id": "cl126jqww000w2e6dq9yv4ifq", "name": "Company" },
{ "id": "cl126mo3t001b2e6dvyi16bkd", "name": "Categories" },
{ "id": "cl126q38p001q2e6d0hj23f6b", "name": "Other categories" }
],
"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" },
"from": {
"itemId": "cl126jc5a000t2e6dqv91w7j6",
"blockId": "cl126jb2q000r2e6dgqlnxnt8",
"groupId": "cl126ixoq000p2e6dfbz9sype"
}
},
{
"id": "cl1278r3b002z2e6d6d6rk9dh",
"to": { "groupId": "cl126pv6w001n2e6dp0qkvthu" },
"from": {
"itemId": "cl1278gyk002x2e6dwmpzs3nf",
"blockId": "cl1278gyk002w2e6d744eb87n",
"groupId": "cl1278gx9002v2e6d4kf3v89s"
}
},
{
"id": "cl1278trd00312e6dxmzhcmmn",
"to": { "groupId": "cl126p75m001j2e6d73qmes0m" },
"from": {
"blockId": "cl1278gyk002w2e6d744eb87n",
"groupId": "cl1278gx9002v2e6d4kf3v89s"
}
},
{
"id": "cl128ag8i00162e6dufv3tgo0",
"to": { "groupId": "cl126krbp00102e6dnjelmfa1" },
"from": {
"blockId": "cl12890kw00132e6dp9v5dexm",
"groupId": "cl126jioj000u2e6dqssno3hv"
}
},
{
"id": "cl128azam00182e6dct61k7v5",
"to": { "groupId": "cl1278gx9002v2e6d4kf3v89s" },
"from": {
"blockId": "cl128ain900172e6d1osj4u90",
"groupId": "cl126krbp00102e6dnjelmfa1"
}
},
{
"id": "cl128c0fu001a2e6droq69g6z",
"to": { "groupId": "cl126p75m001j2e6d73qmes0m" },
"from": {
"blockId": "cl128b34o00192e6dqjxs3cxf",
"groupId": "cl126pv6w001n2e6dp0qkvthu"
}
},
{
"from": {
"groupId": "cl1266bah00032e6dgdnj4vgz",
"blockId": "cl1266bam00042e6dm0gn22vy",
"itemId": "cl1266bam00052e6dn1sdjnax"
},
"to": { "groupId": "cl126ixoq000p2e6dfbz9sype" },
"id": "cl12bk3j6000c2e69bak89ja9"
},
{
"from": {
"groupId": "cl1267q1z000d2e6d949f2ge4",
"blockId": "cl1289y1s00142e6dvbkpvbje"
},
"to": {
"groupId": "cl126ixoq000p2e6dfbz9sype",
"blockId": "cl126hb9m000l2e6d5qk3mohn"
},
"id": "cl12bk56s000d2e69oll3nqxm"
},
{
"from": {
"groupId": "cl1266bah00032e6dgdnj4vgz",
"blockId": "cl1266bam00042e6dm0gn22vy"
},
"to": { "groupId": "cl1267q1z000d2e6d949f2ge4" },
"id": "cl12bnfyd000g2e69g7lr3czq"
}
],
"theme": {
"chat": {
"inputs": {
"color": "#ffffff",
"backgroundColor": "#1e293b",
"placeholderColor": "#9095A0"
},
"buttons": { "color": "#ffffff", "backgroundColor": "#1a5fff" },
"hostBubbles": { "color": "#ffffff", "backgroundColor": "#1e293b" },
"guestBubbles": { "color": "#FFFFFF", "backgroundColor": "#FF8E21" },
"hostAvatar": {
"isEnabled": true,
"url": "https://s3.eu-west-3.amazonaws.com/typebot/public/typebots/cl0s4zy9m247009l1ybkoojxe/me-square.png"
}
},
"general": {
"font": "Open Sans",
"background": { "type": "Color", "content": "#171923" }
},
"customCss": ".typebot-button {box-shadow: inset 0 1px 0 0 rgb(255 255 255/0.2)}"
},
"settings": {
"general": {
"isBrandingEnabled": true,
"isInputPrefillEnabled": true,
"isNewResultOnRefreshEnabled": false
},
"metadata": {
"description": "Build beautiful conversational forms and embed them directly in your applications without a line of code. Triple your response rate and collect answers that has more value compared to a traditional form."
},
"typingEmulation": { "speed": 300, "enabled": true, "maxDelay": 1.5 }
},
"publicId": "typebot-onboarding",
"customDomain": null
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

@@ -1,9 +1,9 @@
.cm-editor { .cm-editor {
outline: 0px solid transparent !important; outline: 0px solid transparent !important;
height: 100%; height: 100%;
border-radius: 1rem;
} }
.cm-scroller { .cm-scroller {
border-radius: 5px; border-radius: 5px;
border: 1px solid #e5e7eb;
} }

View File

@@ -10,9 +10,6 @@
text-decoration: underline; text-decoration: underline;
} }
.slate-ToolbarButton-active {
color: blue !important;
}
.slate-ToolbarButton-active > svg { .slate-ToolbarButton-active > svg {
stroke-width: 2px; stroke-width: 2px;
} }
@@ -22,7 +19,6 @@
} }
.slate-a { .slate-a {
color: blue !important;
text-decoration: underline; text-decoration: underline;
} }

View File

@@ -1,7 +1,7 @@
import { AlertProps, Alert, AlertIcon } from '@chakra-ui/react' import { AlertProps, Alert, AlertIcon } from '@chakra-ui/react'
export const AlertInfo = (props: AlertProps) => ( export const AlertInfo = (props: AlertProps) => (
<Alert status="info" bgColor={'blue.50'} rounded="md" {...props}> <Alert status="info" rounded="md" {...props}>
<AlertIcon /> <AlertIcon />
{props.children} {props.children}
</Alert> </Alert>

View File

@@ -1,4 +1,10 @@
import { Box, BoxProps, HStack } from '@chakra-ui/react' import {
Box,
BoxProps,
HStack,
useColorMode,
useColorModeValue,
} from '@chakra-ui/react'
import { EditorView, basicSetup } from 'codemirror' import { EditorView, basicSetup } from 'codemirror'
import { EditorState } from '@codemirror/state' import { EditorState } from '@codemirror/state'
import { json, jsonParseLinter } from '@codemirror/lang-json' import { json, jsonParseLinter } from '@codemirror/lang-json'
@@ -11,6 +17,7 @@ import { linter, LintSource } from '@codemirror/lint'
import { VariablesButton } from '@/features/variables' import { VariablesButton } from '@/features/variables'
import { Variable } from 'models' import { Variable } from 'models'
import { env } from 'utils' import { env } from 'utils'
import { espresso, dracula } from 'thememirror'
const linterExtension = linter(jsonParseLinter() as unknown as LintSource) const linterExtension = linter(jsonParseLinter() as unknown as LintSource)
@@ -33,6 +40,7 @@ export const CodeEditor = ({
debounceTimeout = 1000, debounceTimeout = 1000,
...props ...props
}: Props & Omit<BoxProps, 'onChange'>) => { }: Props & Omit<BoxProps, 'onChange'>) => {
const isDark = useColorMode().colorMode === 'dark'
const editorContainer = useRef<HTMLDivElement | null>(null) const editorContainer = useRef<HTMLDivElement | null>(null)
const editorView = useRef<EditorView | null>(null) const editorView = useRef<EditorView | null>(null)
const [, setPlainTextValue] = useState(value) const [, setPlainTextValue] = useState(value)
@@ -84,6 +92,7 @@ export const CodeEditor = ({
updateListenerExtension, updateListenerExtension,
basicSetup, basicSetup,
EditorState.readOnly.of(isReadOnly), EditorState.readOnly.of(isReadOnly),
isDark ? dracula : espresso,
] ]
if (lang === 'json') { if (lang === 'json') {
extensions.push(json()) extensions.push(json())
@@ -130,7 +139,13 @@ export const CodeEditor = ({
} }
return ( return (
<HStack align="flex-end" spacing={0}> <HStack
align="flex-end"
spacing={0}
borderWidth={'1px'}
borderRadius="md"
bg={useColorModeValue('#FCFCFC', '#2D2F3F')}
>
<Box <Box
w={isVariableButtonDisplayed ? 'calc(100% - 32px)' : '100%'} w={isVariableButtonDisplayed ? 'calc(100% - 32px)' : '100%'}
ref={editorContainer} ref={editorContainer}

View File

@@ -5,6 +5,7 @@ import {
PopoverTrigger, PopoverTrigger,
PopoverContent, PopoverContent,
Flex, Flex,
useColorModeValue,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import React from 'react' import React from 'react'
import { EmojiOrImageIcon } from './EmojiOrImageIcon' import { EmojiOrImageIcon } from './EmojiOrImageIcon'
@@ -23,6 +24,8 @@ export const EditableEmojiOrImageIcon = ({
onChangeIcon, onChangeIcon,
boxSize, boxSize,
}: Props) => { }: Props) => {
const bg = useColorModeValue('gray.100', 'gray.700')
return ( return (
<Popover isLazy> <Popover isLazy>
{({ onClose }: { onClose: () => void }) => ( {({ onClose }: { onClose: () => void }) => (
@@ -32,7 +35,9 @@ export const EditableEmojiOrImageIcon = ({
cursor="pointer" cursor="pointer"
p="2" p="2"
rounded="md" rounded="md"
_hover={{ bgColor: 'gray.100' }} _hover={{
bg,
}}
transition="background-color 0.2s" transition="background-color 0.2s"
data-testid="editable-icon" data-testid="editable-icon"
> >

View File

@@ -3,12 +3,13 @@ import {
useOutsideClick, useOutsideClick,
Flex, Flex,
Popover, Popover,
PopoverTrigger,
Input, Input,
PopoverContent, PopoverContent,
Button, Button,
InputProps, InputProps,
HStack, HStack,
useColorModeValue,
PopoverAnchor,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { Variable } from 'models' import { Variable } from 'models'
import { useState, useRef, useEffect, ChangeEvent, ReactNode } from 'react' import { useState, useRef, useEffect, ChangeEvent, ReactNode } from 'react'
@@ -32,6 +33,7 @@ export const SearchableDropdown = ({
onValueChange, onValueChange,
...inputProps ...inputProps
}: Props) => { }: Props) => {
const bg = useColorModeValue('gray.200', 'gray.700')
const [carretPosition, setCarretPosition] = useState<number>(0) const [carretPosition, setCarretPosition] = useState<number>(0)
const { onOpen, onClose, isOpen } = useDisclosure() const { onOpen, onClose, isOpen } = useDisclosure()
const [inputValue, setInputValue] = useState(selectedItem ?? '') const [inputValue, setInputValue] = useState(selectedItem ?? '')
@@ -172,13 +174,13 @@ export const SearchableDropdown = ({
offset={[0, 0]} offset={[0, 0]}
isLazy isLazy
> >
<PopoverTrigger> <PopoverAnchor>
<HStack spacing={0} align={'flex-end'} w="full"> <HStack spacing={0} align={'flex-end'} w="full">
<Input <Input
ref={inputRef} ref={inputRef}
value={inputValue} value={inputValue}
onChange={onInputChange} onChange={onInputChange}
onClick={onOpen} onFocus={onOpen}
type="text" type="text"
onKeyUp={handleKeyUp} onKeyUp={handleKeyUp}
{...inputProps} {...inputProps}
@@ -190,7 +192,7 @@ export const SearchableDropdown = ({
/> />
)} )}
</HStack> </HStack>
</PopoverTrigger> </PopoverAnchor>
<PopoverContent <PopoverContent
maxH="35vh" maxH="35vh"
overflowY="scroll" overflowY="scroll"
@@ -215,9 +217,7 @@ export const SearchableDropdown = ({
colorScheme="gray" colorScheme="gray"
role="menuitem" role="menuitem"
variant="ghost" variant="ghost"
bgColor={ bg={keyboardFocusIndex === idx ? bg : 'transparent'}
keyboardFocusIndex === idx ? 'gray.200' : 'transparent'
}
justifyContent="flex-start" justifyContent="flex-start"
> >
{typeof item === 'string' ? item : item.label} {typeof item === 'string' ? item : item.label}

View File

@@ -1,6 +1,5 @@
import { import {
useDisclosure, useDisclosure,
useOutsideClick,
Flex, Flex,
Popover, Popover,
PopoverTrigger, PopoverTrigger,
@@ -10,6 +9,9 @@ import {
InputProps, InputProps,
IconButton, IconButton,
HStack, HStack,
useColorModeValue,
PopoverAnchor,
useOutsideClick,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { EditIcon, PlusIcon, TrashIcon } from '@/components/icons' import { EditIcon, PlusIcon, TrashIcon } from '@/components/icons'
import { useTypebot } from '@/features/editor' import { useTypebot } from '@/features/editor'
@@ -35,6 +37,7 @@ export const VariableSearchInput = ({
debounceTimeout = 1000, debounceTimeout = 1000,
...inputProps ...inputProps
}: Props) => { }: Props) => {
const bg = useColorModeValue('gray.200', 'gray.700')
const { onOpen, onClose, isOpen } = useDisclosure() const { onOpen, onClose, isOpen } = useDisclosure()
const { typebot, createVariable, deleteVariable, updateVariable } = const { typebot, createVariable, deleteVariable, updateVariable } =
useTypebot() useTypebot()
@@ -56,7 +59,7 @@ export const VariableSearchInput = ({
number | undefined number | undefined
>() >()
const dropdownRef = useRef(null) const dropdownRef = useRef(null)
const inputRef = useRef(null) const inputRef = useRef<HTMLInputElement>(null)
const createVariableItemRef = useRef<HTMLButtonElement | null>(null) const createVariableItemRef = useRef<HTMLButtonElement | null>(null)
const itemsRef = useRef<(HTMLButtonElement | null)[]>([]) const itemsRef = useRef<(HTMLButtonElement | null)[]>([])
@@ -80,7 +83,6 @@ export const VariableSearchInput = ({
const onInputChange = (e: ChangeEvent<HTMLInputElement>) => { const onInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value) setInputValue(e.target.value)
debounced(e.target.value) debounced(e.target.value)
onOpen()
if (e.target.value === '') { if (e.target.value === '') {
onSelectVariable(undefined) onSelectVariable(undefined)
setFilteredItems([...variables.slice(0, 50)]) setFilteredItems([...variables.slice(0, 50)])
@@ -175,18 +177,18 @@ export const VariableSearchInput = ({
isLazy isLazy
offset={[0, 2]} offset={[0, 2]}
> >
<PopoverTrigger> <PopoverAnchor>
<Input <Input
data-testid="variables-input" data-testid="variables-input"
ref={inputRef} ref={inputRef}
value={inputValue} value={inputValue}
onChange={onInputChange} onChange={onInputChange}
onClick={onOpen} onFocus={onOpen}
onKeyUp={handleKeyUp} onKeyUp={handleKeyUp}
placeholder={inputProps.placeholder ?? 'Select a variable'} placeholder={inputProps.placeholder ?? 'Select a variable'}
{...inputProps} {...inputProps}
/> />
</PopoverTrigger> </PopoverAnchor>
<PopoverContent <PopoverContent
maxH="35vh" maxH="35vh"
overflowY="scroll" overflowY="scroll"
@@ -207,7 +209,7 @@ export const VariableSearchInput = ({
variant="ghost" variant="ghost"
justifyContent="flex-start" justifyContent="flex-start"
leftIcon={<PlusIcon />} leftIcon={<PlusIcon />}
bgColor={keyboardFocusIndex === 0 ? 'gray.200' : 'transparent'} bgColor={keyboardFocusIndex === 0 ? bg : 'transparent'}
> >
Create &quot;{inputValue}&quot; Create &quot;{inputValue}&quot;
</Button> </Button>
@@ -232,9 +234,7 @@ export const VariableSearchInput = ({
variant="ghost" variant="ghost"
justifyContent="space-between" justifyContent="space-between"
bgColor={ bgColor={
keyboardFocusIndex === indexInList keyboardFocusIndex === indexInList ? bg : 'transparent'
? 'gray.200'
: 'transparent'
} }
> >
{item.name} {item.name}

View File

@@ -106,7 +106,6 @@ export const TextBox = ({
onKeyUp={handleKeyUp} onKeyUp={handleKeyUp}
onClick={handleKeyUp} onClick={handleKeyUp}
onChange={handleChange} onChange={handleChange}
bgColor={'white'}
{...props} {...props}
/> />
) )

View File

@@ -0,0 +1,66 @@
import {
RadioGroup,
HStack,
VStack,
Stack,
Radio,
Image,
Text,
} from '@chakra-ui/react'
const appearanceData = [
{
value: 'light',
label: 'Light',
image: '/images/light-mode.png',
},
{
value: 'dark',
label: 'Dark',
image: '/images/dark-mode.png',
},
{
value: 'system',
label: 'System',
image: '/images/system-mode.png',
},
]
type Props = {
defaultValue: string
onChange: (value: string) => void
}
export const AppearanceRadioGroup = ({ defaultValue, onChange }: Props) => (
<RadioGroup onChange={onChange} defaultValue={defaultValue}>
<HStack spacing={4} w="full" align="stretch">
{appearanceData.map((option) => (
<VStack
key={option.value}
as="label"
htmlFor={option.label}
cursor="pointer"
borderWidth="1px"
borderRadius="md"
w="full"
spacing={2}
justifyContent="space-between"
pb={6}
>
<VStack spacing={4}>
<Image
src={option.image}
alt="Theme preview"
borderTopRadius="md"
/>
<Stack>
<Text fontWeight="bold">{option.label}</Text>
</Stack>
</VStack>
<Radio value={option.value} id={option.label} />
</VStack>
))}
</HStack>
</RadioGroup>
)

View File

@@ -0,0 +1,64 @@
import { MouseIcon, LaptopIcon } from '@/components/icons'
import {
HStack,
Radio,
RadioGroup,
Stack,
VStack,
Text,
} from '@chakra-ui/react'
import { GraphNavigation } from 'db'
const graphNavigationData = [
{
value: GraphNavigation.MOUSE,
label: 'Mouse',
description:
'Move by dragging the board and zoom in/out using the scroll wheel',
icon: <MouseIcon boxSize="35px" />,
},
{
value: GraphNavigation.TRACKPAD,
label: 'Trackpad',
description: 'Move the board using 2 fingers and zoom in/out by pinching',
icon: <LaptopIcon boxSize="35px" />,
},
]
type Props = {
defaultValue: string
onChange: (value: string) => void
}
export const GraphNavigationRadioGroup = ({
defaultValue,
onChange,
}: Props) => (
<RadioGroup onChange={onChange} defaultValue={defaultValue}>
<HStack spacing={4} w="full" align="stretch">
{graphNavigationData.map((option) => (
<VStack
key={option.value}
as="label"
htmlFor={option.label}
cursor="pointer"
borderWidth="1px"
borderRadius="md"
w="full"
p="6"
spacing={6}
justifyContent="space-between"
>
<VStack spacing={6}>
{option.icon}
<Stack>
<Text fontWeight="bold">{option.label}</Text>
<Text>{option.description}</Text>
</Stack>
</VStack>
<Radio value={option.value} id={option.label} />
</VStack>
))}
</HStack>
</RadioGroup>
)

View File

@@ -0,0 +1,44 @@
import { Stack, Heading, useColorMode } from '@chakra-ui/react'
import { useUser } from '@/features/account'
import { GraphNavigation } from 'db'
import React, { useEffect } from 'react'
import { GraphNavigationRadioGroup } from './GraphNavigationRadioGroup'
import { AppearanceRadioGroup } from './AppearanceRadioGroup'
export const UserPreferencesForm = () => {
const { setColorMode } = useColorMode()
const { saveUser, user } = useUser()
useEffect(() => {
if (!user?.graphNavigation)
saveUser({ graphNavigation: GraphNavigation.TRACKPAD })
}, [saveUser, user?.graphNavigation])
const changeGraphNavigation = async (value: string) => {
await saveUser({ graphNavigation: value as GraphNavigation })
}
const changeAppearance = async (value: string) => {
setColorMode(value)
await saveUser({ preferredAppAppearance: value })
}
return (
<Stack spacing={12}>
<Stack spacing={6}>
<Heading size="md">Editor Navigation</Heading>
<GraphNavigationRadioGroup
defaultValue={user?.graphNavigation ?? GraphNavigation.TRACKPAD}
onChange={changeGraphNavigation}
/>
</Stack>
<Stack spacing={6}>
<Heading size="md">Appearance</Heading>
<AppearanceRadioGroup
defaultValue={user?.preferredAppAppearance ?? 'system'}
onChange={changeAppearance}
/>
</Stack>
</Stack>
)
}

View File

@@ -0,0 +1,2 @@
export * from './MyAccountForm'
export * from './UserPreferenceForm/UserPreferencesForm'

View File

@@ -1,3 +1,3 @@
export { UserProvider, useUser } from './UserProvider' export { UserProvider, useUser } from './UserProvider'
export type { ApiTokenFromServer } from './types' export type { ApiTokenFromServer } from './types'
export { MyAccountForm } from './components/MyAccountForm' export * from './components'

View File

@@ -1,4 +1,9 @@
import { Flex, Spinner, useDisclosure } from '@chakra-ui/react' import {
Flex,
Spinner,
useColorModeValue,
useDisclosure,
} from '@chakra-ui/react'
import { useToast } from '@/hooks/useToast' import { useToast } from '@/hooks/useToast'
import { useTypebot } from '@/features/editor' import { useTypebot } from '@/features/editor'
import { Stats } from 'models' import { Stats } from 'models'
@@ -24,7 +29,13 @@ export const AnalyticsGraphContainer = ({ stats }: { stats?: Stats }) => {
<Flex <Flex
w="full" w="full"
pos="relative" pos="relative"
bgColor="gray.50" bgColor={useColorModeValue('white', 'gray.850')}
backgroundImage={useColorModeValue(
'radial-gradient(#c6d0e1 1px, transparent 0)',
'radial-gradient(#2f2f39 1px, transparent 0)'
)}
backgroundSize="40px 40px"
backgroundPosition="-19px -19px"
h="full" h="full"
justifyContent="center" justifyContent="center"
> >

View File

@@ -5,6 +5,7 @@ import {
Stat, Stat,
StatLabel, StatLabel,
StatNumber, StatNumber,
useColorModeValue,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { Stats } from 'models' import { Stats } from 'models'
import React from 'react' import React from 'react'
@@ -13,9 +14,11 @@ export const StatsCards = ({
stats, stats,
...props ...props
}: { stats?: Stats } & GridProps) => { }: { stats?: Stats } & GridProps) => {
const bg = useColorModeValue('white', 'gray.900')
return ( return (
<SimpleGrid columns={{ base: 1, md: 3 }} spacing="6" {...props}> <SimpleGrid columns={{ base: 1, md: 3 }} spacing="6" {...props}>
<Stat bgColor="white" p="4" rounded="md" boxShadow="md"> <Stat bgColor={bg} p="4" rounded="md" boxShadow="md">
<StatLabel>Views</StatLabel> <StatLabel>Views</StatLabel>
{stats ? ( {stats ? (
<StatNumber>{stats.totalViews}</StatNumber> <StatNumber>{stats.totalViews}</StatNumber>
@@ -23,7 +26,7 @@ export const StatsCards = ({
<Skeleton w="50%" h="10px" mt="2" /> <Skeleton w="50%" h="10px" mt="2" />
)} )}
</Stat> </Stat>
<Stat bgColor="white" p="4" rounded="md" boxShadow="md"> <Stat bgColor={bg} p="4" rounded="md" boxShadow="md">
<StatLabel>Starts</StatLabel> <StatLabel>Starts</StatLabel>
{stats ? ( {stats ? (
<StatNumber>{stats.totalStarts}</StatNumber> <StatNumber>{stats.totalStarts}</StatNumber>
@@ -31,7 +34,7 @@ export const StatsCards = ({
<Skeleton w="50%" h="10px" mt="2" /> <Skeleton w="50%" h="10px" mt="2" />
)} )}
</Stat> </Stat>
<Stat bgColor="white" p="4" rounded="md" boxShadow="md"> <Stat bgColor={bg} p="4" rounded="md" boxShadow="md">
<StatLabel>Completion rate</StatLabel> <StatLabel>Completion rate</StatLabel>
{stats ? ( {stats ? (
<StatNumber> <StatNumber>

View File

@@ -8,6 +8,7 @@ export const mockedUser: User = {
createdAt: new Date(), createdAt: new Date(),
emailVerified: null, emailVerified: null,
graphNavigation: 'TRACKPAD', graphNavigation: 'TRACKPAD',
preferredAppAppearance: null,
image: 'https://avatars.githubusercontent.com/u/16015833?v=4', image: 'https://avatars.githubusercontent.com/u/16015833?v=4',
lastActivityAt: new Date(), lastActivityAt: new Date(),
onboardingCategories: [], onboardingCategories: [],

View File

@@ -44,7 +44,7 @@ export const CurrentSubscriptionContent = ({
const isSubscribed = (plan === Plan.STARTER || plan === Plan.PRO) && stripeId const isSubscribed = (plan === Plan.STARTER || plan === Plan.PRO) && stripeId
return ( return (
<Stack spacing="2"> <Stack spacing="4">
<Heading fontSize="3xl">Subscription</Heading> <Heading fontSize="3xl">Subscription</Heading>
<HStack data-testid="current-subscription"> <HStack data-testid="current-subscription">
<Text>Current workspace subscription: </Text> <Text>Current workspace subscription: </Text>
@@ -70,7 +70,7 @@ export const CurrentSubscriptionContent = ({
{isSubscribed && !isCancelling && ( {isSubscribed && !isCancelling && (
<> <>
<Stack spacing="1"> <Stack spacing="4">
<Text fontSize="sm"> <Text fontSize="sm">
Need to change payment method or billing information? Head over to Need to change payment method or billing information? Head over to
your billing portal: your billing portal:

View File

@@ -12,6 +12,7 @@ import {
Tooltip, Tooltip,
Flex, Flex,
Tag, Tag,
useColorModeValue,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { ChevronLeftIcon } from '@/components/icons' import { ChevronLeftIcon } from '@/components/icons'
import { useWorkspace } from '@/features/workspace' import { useWorkspace } from '@/features/workspace'
@@ -126,7 +127,7 @@ export const ProPlanContent = ({
flex="1" flex="1"
flexShrink={0} flexShrink={0}
borderWidth="1px" borderWidth="1px"
borderColor="blue.500" borderColor={useColorModeValue('blue.500', 'blue.300')}
rounded="lg" rounded="lg"
> >
<Flex justifyContent="center"> <Flex justifyContent="center">
@@ -134,6 +135,7 @@ export const ProPlanContent = ({
pos="absolute" pos="absolute"
top="-10px" top="-10px"
colorScheme="blue" colorScheme="blue"
bg={useColorModeValue('blue.500', 'blue.400')}
variant="solid" variant="solid"
fontWeight="semibold" fontWeight="semibold"
style={{ marginTop: 0 }} style={{ marginTop: 0 }}

View File

@@ -1,6 +1,7 @@
import { import {
Flex, Flex,
Stack, Stack,
useColorModeValue,
useEventListener, useEventListener,
useOutsideClick, useOutsideClick,
} from '@chakra-ui/react' } from '@chakra-ui/react'
@@ -20,6 +21,7 @@ import { ReactEditor } from 'slate-react'
import { serializeHtml } from '@udecode/plate-serializer-html' import { serializeHtml } from '@udecode/plate-serializer-html'
import { parseHtmlStringToPlainText } from '../../utils' import { parseHtmlStringToPlainText } from '../../utils'
import { VariableSearchInput } from '@/components/VariableSearchInput' import { VariableSearchInput } from '@/components/VariableSearchInput'
import { colors } from '@/lib/theme'
type TextBubbleEditorContentProps = { type TextBubbleEditorContentProps = {
id: string id: string
@@ -32,6 +34,7 @@ const TextBubbleEditorContent = ({
textEditorValue, textEditorValue,
onClose, onClose,
}: TextBubbleEditorContentProps) => { }: TextBubbleEditorContentProps) => {
const variableInputBg = useColorModeValue('white', 'gray.900')
const editor = usePlateEditorRef() const editor = usePlateEditorRef()
const varDropdownRef = useRef<HTMLDivElement | null>(null) const varDropdownRef = useRef<HTMLDivElement | null>(null)
const rememberedSelection = useRef<BaseSelection | null>(null) const rememberedSelection = useRef<BaseSelection | null>(null)
@@ -112,12 +115,27 @@ const TextBubbleEditorContent = ({
pos="relative" pos="relative"
spacing={0} spacing={0}
cursor="text" cursor="text"
sx={{
'.slate-ToolbarButton-active': {
color: useColorModeValue('blue.500', 'blue.300') + ' !important',
},
'.PlateFloatingLink___StyledFloatingLinkInsertRoot-sc-1bralnd-8': {
backgroundColor: useColorModeValue('white', 'gray.800'),
borderWidth: 1,
},
'.PlateFloatingLink___StyledDiv2-sc-1bralnd-2': {
backgroundColor: useColorModeValue('gray.200', 'gray.600'),
},
'.slate-a': {
color: useColorModeValue('blue.500', 'blue.300'),
},
}}
> >
<ToolBar onVariablesButtonClick={() => setIsVariableDropdownOpen(true)} /> <ToolBar onVariablesButtonClick={() => setIsVariableDropdownOpen(true)} />
<Plate <Plate
id={id} id={id}
editableProps={{ editableProps={{
style: editorStyle, style: editorStyle(useColorModeValue('white', colors.gray[850])),
autoFocus: true, autoFocus: true,
onFocus: () => { onFocus: () => {
if (editor.children.length === 0) return if (editor.children.length === 0) return
@@ -138,7 +156,7 @@ const TextBubbleEditorContent = ({
ref={varDropdownRef} ref={varDropdownRef}
shadow="lg" shadow="lg"
rounded="md" rounded="md"
bgColor="white" bg={variableInputBg}
w="250px" w="250px"
zIndex={10} zIndex={10}
> >

View File

@@ -1,4 +1,9 @@
import { StackProps, HStack, IconButton } from '@chakra-ui/react' import {
StackProps,
HStack,
IconButton,
useColorModeValue,
} from '@chakra-ui/react'
import { import {
MARK_BOLD, MARK_BOLD,
MARK_ITALIC, MARK_ITALIC,
@@ -27,7 +32,7 @@ export const ToolBar = ({ onVariablesButtonClick, ...props }: Props) => {
} }
return ( return (
<HStack <HStack
bgColor={'white'} bgColor={useColorModeValue('white', 'gray.850')}
borderTopRadius="md" borderTopRadius="md"
p={2} p={2}
w="full" w="full"

View File

@@ -126,7 +126,8 @@ export const WebhookSettings = ({
const handleTestRequestClick = async () => { const handleTestRequestClick = async () => {
if (!typebot || !localWebhook) return if (!typebot || !localWebhook) return
setIsTestResponseLoading(true) setIsTestResponseLoading(true)
await Promise.all([updateWebhook(localWebhook.id, localWebhook), save()]) await updateWebhook(localWebhook.id, localWebhook)
await save()
const { data, error } = await executeWebhook( const { data, error } = await executeWebhook(
typebot.id, typebot.id,
convertVariablesForTestToVariables( convertVariablesForTestToVariables(

View File

@@ -12,6 +12,7 @@ import {
Text, Text,
Tag, Tag,
Flex, Flex,
Skeleton,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { ChevronLeftIcon } from '@/components/icons' import { ChevronLeftIcon } from '@/components/icons'
import { useToast } from '@/hooks/useToast' import { useToast } from '@/hooks/useToast'
@@ -205,9 +206,12 @@ export const CollaborationList = () => {
/> />
))} ))}
{(isCollaboratorsLoading || isInvitationsLoading) && ( {(isCollaboratorsLoading || isInvitationsLoading) && (
<HStack p="4"> <HStack p="4" justifyContent="space-between">
<SkeletonCircle boxSize="32px" /> <HStack>
<SkeletonText width="200px" noOfLines={2} /> <SkeletonCircle boxSize="32px" />
<Skeleton width="230px" h="10px" />
</HStack>
<Skeleton width="80px" h="10px" />
</HStack> </HStack>
)} )}
</Stack> </Stack>

View File

@@ -4,6 +4,7 @@ import {
ModalBody, ModalBody,
ModalContent, ModalContent,
ModalOverlay, ModalOverlay,
useColorModeValue,
useDisclosure, useDisclosure,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { TypebotViewer } from 'bot-engine' import { TypebotViewer } from 'bot-engine'
@@ -18,6 +19,10 @@ import { parseTypebotToPublicTypebot } from '@/features/publish'
type Props = { totalTypebots: number } type Props = { totalTypebots: number }
export const OnboardingModal = ({ totalTypebots }: Props) => { export const OnboardingModal = ({ totalTypebots }: Props) => {
const botPath = useColorModeValue(
'/bots/onboarding.json',
'/bots/onboarding-dark.json'
)
const { user, saveUser } = useUser() const { user, saveUser } = useUser()
const { isOpen, onOpen, onClose } = useDisclosure() const { isOpen, onOpen, onClose } = useDisclosure()
const [typebot, setTypebot] = useState<Typebot>() const [typebot, setTypebot] = useState<Typebot>()
@@ -30,7 +35,6 @@ export const OnboardingModal = ({ totalTypebots }: Props) => {
useEffect(() => { useEffect(() => {
fetchTemplate() fetchTemplate()
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) }, [])
@@ -73,7 +77,7 @@ export const OnboardingModal = ({ totalTypebots }: Props) => {
} }
const fetchTemplate = async () => { const fetchTemplate = async () => {
const { data, error } = await sendRequest(`/bots/onboarding.json`) const { data, error } = await sendRequest(botPath)
if (error) if (error)
return showToast({ title: error.name, description: error.message }) return showToast({ title: error.name, description: error.message })
setTypebot(data as Typebot) setTypebot(data as Typebot)
@@ -116,7 +120,7 @@ export const OnboardingModal = ({ totalTypebots }: Props) => {
/> />
<ModalOverlay /> <ModalOverlay />
<ModalContent h="85vh"> <ModalContent h="85vh">
<ModalBody> <ModalBody p="10">
{typebot && ( {typebot && (
<TypebotViewer <TypebotViewer
apiHost={getViewerUrl({ isBuilder: true })} apiHost={getViewerUrl({ isBuilder: true })}
@@ -125,6 +129,7 @@ export const OnboardingModal = ({ totalTypebots }: Props) => {
Name: user?.name?.split(' ')[0] ?? undefined, Name: user?.name?.split(' ')[0] ?? undefined,
}} }}
onNewAnswer={handleNewAnswer} onNewAnswer={handleNewAnswer}
style={{ borderRadius: '0.25rem' }}
/> />
)} )}
</ModalBody> </ModalBody>

View File

@@ -1,5 +1,11 @@
import { Flex, HStack, StackProps, Text, Tooltip } from '@chakra-ui/react' import {
import { BlockType, DraggableBlockType } from 'models' Flex,
HStack,
Text,
Tooltip,
useColorModeValue,
} from '@chakra-ui/react'
import { DraggableBlockType } from 'models'
import { useBlockDnd } from '@/features/graph' import { useBlockDnd } from '@/features/graph'
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { BlockIcon } from './BlockIcon' import { BlockIcon } from './BlockIcon'
@@ -28,17 +34,17 @@ export const BlockCard = ({
<Flex pos="relative"> <Flex pos="relative">
<HStack <HStack
borderWidth="1px" borderWidth="1px"
borderColor="gray.200" borderColor={useColorModeValue('gray.200', 'gray.800')}
rounded="lg" rounded="lg"
flex="1" flex="1"
cursor={'grab'} cursor={'grab'}
opacity={isMouseDown || isDisabled ? '0.4' : '1'} opacity={isMouseDown || isDisabled ? '0.4' : '1'}
onMouseDown={handleMouseDown} onMouseDown={handleMouseDown}
bgColor="gray.50" bgColor={useColorModeValue('gray.50', 'gray.850')}
px="4" px="4"
py="2" py="2"
_hover={{ shadow: 'md' }} _hover={useColorModeValue({ shadow: 'md' }, { bgColor: 'gray.800' })}
transition="box-shadow 200ms" transition="box-shadow 200ms, background-color 200ms"
pointerEvents={isDisabled ? 'none' : 'auto'} pointerEvents={isDisabled ? 'none' : 'auto'}
> >
{!isMouseDown ? ( {!isMouseDown ? (
@@ -46,38 +52,9 @@ export const BlockCard = ({
<BlockIcon type={type} /> <BlockIcon type={type} />
<BlockTypeLabel type={type} /> <BlockTypeLabel type={type} />
</> </>
) : ( ) : null}
<Text color="white" userSelect="none">
Placeholder
</Text>
)}
</HStack> </HStack>
</Flex> </Flex>
</Tooltip> </Tooltip>
) )
} }
export const BlockCardOverlay = ({
type,
...props
}: StackProps & { type: BlockType }) => {
return (
<HStack
borderWidth="1px"
rounded="lg"
cursor={'grabbing'}
w="147px"
transition="none"
pointerEvents="none"
px="4"
py="2"
bgColor="white"
shadow="xl"
zIndex={2}
{...props}
>
<BlockIcon type={type} />
<BlockTypeLabel type={type} />
</HStack>
)
}

View File

@@ -0,0 +1,30 @@
import { StackProps, HStack, useColorModeValue } from '@chakra-ui/react'
import { BlockType } from 'models'
import { BlockIcon } from './BlockIcon'
import { BlockTypeLabel } from './BlockTypeLabel'
export const BlockCardOverlay = ({
type,
...props
}: StackProps & { type: BlockType }) => {
return (
<HStack
borderWidth="1px"
rounded="lg"
cursor={'grabbing'}
w="147px"
transition="none"
pointerEvents="none"
px="4"
py="2"
borderColor={useColorModeValue('gray.200', 'gray.800')}
bgColor={useColorModeValue('gray.50', 'gray.850')}
shadow="xl"
zIndex={2}
{...props}
>
<BlockIcon type={type} />
<BlockTypeLabel type={type} />
</HStack>
)
}

View File

@@ -1,4 +1,4 @@
import { IconProps } from '@chakra-ui/react' import { IconProps, useColorModeValue } from '@chakra-ui/react'
import { import {
BubbleBlockType, BubbleBlockType,
InputBlockType, InputBlockType,
@@ -40,47 +40,50 @@ import { AudioBubbleIcon } from '@/features/blocks/bubbles/audio'
type BlockIconProps = { type: BlockType } & IconProps type BlockIconProps = { type: BlockType } & IconProps
export const BlockIcon = ({ type, ...props }: BlockIconProps) => { export const BlockIcon = ({ type, ...props }: BlockIconProps) => {
const blue = useColorModeValue('blue.500', 'blue.300')
const orange = useColorModeValue('orange.500', 'orange.300')
const purple = useColorModeValue('purple.500', 'purple.300')
switch (type) { switch (type) {
case BubbleBlockType.TEXT: case BubbleBlockType.TEXT:
return <TextBubbleIcon {...props} /> return <TextBubbleIcon color={blue} {...props} />
case BubbleBlockType.IMAGE: case BubbleBlockType.IMAGE:
return <ImageBubbleIcon {...props} /> return <ImageBubbleIcon color={blue} {...props} />
case BubbleBlockType.VIDEO: case BubbleBlockType.VIDEO:
return <VideoBubbleIcon {...props} /> return <VideoBubbleIcon color={blue} {...props} />
case BubbleBlockType.EMBED: case BubbleBlockType.EMBED:
return <EmbedBubbleIcon {...props} /> return <EmbedBubbleIcon color={blue} {...props} />
case BubbleBlockType.AUDIO: case BubbleBlockType.AUDIO:
return <AudioBubbleIcon {...props} /> return <AudioBubbleIcon color={blue} {...props} />
case InputBlockType.TEXT: case InputBlockType.TEXT:
return <TextInputIcon {...props} /> return <TextInputIcon color={orange} {...props} />
case InputBlockType.NUMBER: case InputBlockType.NUMBER:
return <NumberInputIcon {...props} /> return <NumberInputIcon color={orange} {...props} />
case InputBlockType.EMAIL: case InputBlockType.EMAIL:
return <EmailInputIcon {...props} /> return <EmailInputIcon color={orange} {...props} />
case InputBlockType.URL: case InputBlockType.URL:
return <UrlInputIcon {...props} /> return <UrlInputIcon color={orange} {...props} />
case InputBlockType.DATE: case InputBlockType.DATE:
return <DateInputIcon {...props} /> return <DateInputIcon color={orange} {...props} />
case InputBlockType.PHONE: case InputBlockType.PHONE:
return <PhoneInputIcon {...props} /> return <PhoneInputIcon color={orange} {...props} />
case InputBlockType.CHOICE: case InputBlockType.CHOICE:
return <ButtonsInputIcon {...props} /> return <ButtonsInputIcon color={orange} {...props} />
case InputBlockType.PAYMENT: case InputBlockType.PAYMENT:
return <PaymentInputIcon {...props} /> return <PaymentInputIcon color={orange} {...props} />
case InputBlockType.RATING: case InputBlockType.RATING:
return <RatingInputIcon {...props} /> return <RatingInputIcon color={orange} {...props} />
case InputBlockType.FILE: case InputBlockType.FILE:
return <FileInputIcon {...props} /> return <FileInputIcon color={orange} {...props} />
case LogicBlockType.SET_VARIABLE: case LogicBlockType.SET_VARIABLE:
return <SetVariableIcon {...props} /> return <SetVariableIcon color={purple} {...props} />
case LogicBlockType.CONDITION: case LogicBlockType.CONDITION:
return <ConditionIcon {...props} /> return <ConditionIcon color={purple} {...props} />
case LogicBlockType.REDIRECT: case LogicBlockType.REDIRECT:
return <RedirectIcon {...props} /> return <RedirectIcon color={purple} {...props} />
case LogicBlockType.CODE: case LogicBlockType.CODE:
return <CodeIcon {...props} /> return <CodeIcon color={purple} {...props} />
case LogicBlockType.TYPEBOT_LINK: case LogicBlockType.TYPEBOT_LINK:
return <TypebotLinkIcon {...props} /> return <TypebotLinkIcon color={purple} {...props} />
case IntegrationBlockType.GOOGLE_SHEETS: case IntegrationBlockType.GOOGLE_SHEETS:
return <GoogleSheetsLogo {...props} /> return <GoogleSheetsLogo {...props} />
case IntegrationBlockType.GOOGLE_ANALYTICS: case IntegrationBlockType.GOOGLE_ANALYTICS:

View File

@@ -8,6 +8,7 @@ import {
IconButton, IconButton,
Tooltip, Tooltip,
Fade, Fade,
useColorModeValue,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { import {
BubbleBlockType, BubbleBlockType,
@@ -18,9 +19,10 @@ import {
} from 'models' } from 'models'
import { useBlockDnd } from '@/features/graph' import { useBlockDnd } from '@/features/graph'
import React, { useState } from 'react' import React, { useState } from 'react'
import { BlockCard, BlockCardOverlay } from './BlockCard' import { BlockCard } from './BlockCard'
import { LockedIcon, UnlockedIcon } from '@/components/icons' import { LockedIcon, UnlockedIcon } from '@/components/icons'
import { headerHeight } from '../../constants' import { headerHeight } from '../../constants'
import { BlockCardOverlay } from './BlockCardOverlay'
export const BlocksSideBar = () => { export const BlocksSideBar = () => {
const { setDraggedBlockType, draggedBlockType } = useBlockDnd() const { setDraggedBlockType, draggedBlockType } = useBlockDnd()
@@ -93,7 +95,7 @@ export const BlocksSideBar = () => {
pt="2" pt="2"
pb="10" pb="10"
px="4" px="4"
bgColor="white" bgColor={useColorModeValue('white', 'gray.900')}
spacing={6} spacing={6}
userSelect="none" userSelect="none"
overflowY="scroll" overflowY="scroll"
@@ -105,14 +107,13 @@ export const BlocksSideBar = () => {
icon={isLocked ? <LockedIcon /> : <UnlockedIcon />} icon={isLocked ? <LockedIcon /> : <UnlockedIcon />}
aria-label={isLocked ? 'Unlock' : 'Lock'} aria-label={isLocked ? 'Unlock' : 'Lock'}
size="sm" size="sm"
variant="outline"
onClick={handleLockClick} onClick={handleLockClick}
/> />
</Tooltip> </Tooltip>
</Flex> </Flex>
<Stack> <Stack>
<Text fontSize="sm" fontWeight="semibold" color="gray.600"> <Text fontSize="sm" fontWeight="semibold">
Bubbles Bubbles
</Text> </Text>
<SimpleGrid columns={2} spacing="3"> <SimpleGrid columns={2} spacing="3">
@@ -123,7 +124,7 @@ export const BlocksSideBar = () => {
</Stack> </Stack>
<Stack> <Stack>
<Text fontSize="sm" fontWeight="semibold" color="gray.600"> <Text fontSize="sm" fontWeight="semibold">
Inputs Inputs
</Text> </Text>
<SimpleGrid columns={2} spacing="3"> <SimpleGrid columns={2} spacing="3">
@@ -134,7 +135,7 @@ export const BlocksSideBar = () => {
</Stack> </Stack>
<Stack> <Stack>
<Text fontSize="sm" fontWeight="semibold" color="gray.600"> <Text fontSize="sm" fontWeight="semibold">
Logic Logic
</Text> </Text>
<SimpleGrid columns={2} spacing="3"> <SimpleGrid columns={2} spacing="3">
@@ -145,7 +146,7 @@ export const BlocksSideBar = () => {
</Stack> </Stack>
<Stack> <Stack>
<Text fontSize="sm" fontWeight="semibold" color="gray.600"> <Text fontSize="sm" fontWeight="semibold">
Integrations Integrations
</Text> </Text>
<SimpleGrid columns={2} spacing="3"> <SimpleGrid columns={2} spacing="3">

View File

@@ -1,10 +1,12 @@
import { import {
Flex,
FlexProps,
IconButton, IconButton,
Menu, Menu,
MenuButton, MenuButton,
MenuButtonProps,
MenuItem, MenuItem,
MenuList, MenuList,
useColorModeValue,
useDisclosure, useDisclosure,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import assert from 'assert' import assert from 'assert'
@@ -21,7 +23,7 @@ import { isNotDefined } from 'utils'
import { EditorSettingsModal } from './EditorSettingsModal' import { EditorSettingsModal } from './EditorSettingsModal'
import { parseDefaultPublicId } from '@/features/publish' import { parseDefaultPublicId } from '@/features/publish'
export const BoardMenuButton = (props: MenuButtonProps) => { export const BoardMenuButton = (props: FlexProps) => {
const { query } = useRouter() const { query } = useRouter()
const { typebot } = useTypebot() const { typebot } = useTypebot()
const { user } = useUser() const { user } = useUser()
@@ -55,25 +57,30 @@ export const BoardMenuButton = (props: MenuButtonProps) => {
setIsDownloading(false) setIsDownloading(false)
} }
return ( return (
<Menu> <Flex
<MenuButton bgColor={useColorModeValue('white', 'gray.900')}
as={IconButton} rounded="md"
bgColor="white" {...props}
icon={<MoreVerticalIcon transform={'rotate(90deg)'} />} >
isLoading={isDownloading} <Menu>
size="sm" <MenuButton
shadow="lg" as={IconButton}
{...props} icon={<MoreVerticalIcon transform={'rotate(90deg)'} />}
/> isLoading={isDownloading}
<MenuList> size="sm"
<MenuItem icon={<SettingsIcon />} onClick={onOpen}> shadow="lg"
Editor settings bgColor={useColorModeValue('white', undefined)}
</MenuItem> />
<MenuItem icon={<DownloadIcon />} onClick={downloadFlow}> <MenuList>
Export flow <MenuItem icon={<SettingsIcon />} onClick={onOpen}>
</MenuItem> Editor settings
</MenuList> </MenuItem>
<EditorSettingsModal isOpen={isOpen} onClose={onClose} /> <MenuItem icon={<DownloadIcon />} onClick={downloadFlow}>
</Menu> Export flow
</MenuItem>
</MenuList>
<EditorSettingsModal isOpen={isOpen} onClose={onClose} />
</Menu>
</Flex>
) )
} }

View File

@@ -5,7 +5,7 @@ import {
GraphProvider, GraphProvider,
GroupsCoordinatesProvider, GroupsCoordinatesProvider,
} from '@/features/graph' } from '@/features/graph'
import { Flex, Spinner } from '@chakra-ui/react' import { Flex, Spinner, useColorModeValue } from '@chakra-ui/react'
import { import {
EditorProvider, EditorProvider,
useEditor, useEditor,
@@ -31,8 +31,11 @@ export const EditTypebotPage = () => {
flex="1" flex="1"
pos="relative" pos="relative"
h="full" h="full"
background="#f4f5f8" bgColor={useColorModeValue('#f4f5f8', 'gray.850')}
backgroundImage="radial-gradient(#c6d0e1 1px, transparent 0)" backgroundImage={useColorModeValue(
'radial-gradient(#c6d0e1 1px, transparent 0)',
'radial-gradient(#2f2f39 1px, transparent 0)'
)}
backgroundSize="40px 40px" backgroundSize="40px 40px"
backgroundPosition="-19px -19px" backgroundPosition="-19px -19px"
> >
@@ -48,12 +51,7 @@ export const EditTypebotPage = () => {
</GraphProvider> </GraphProvider>
</GraphDndProvider> </GraphDndProvider>
) : ( ) : (
<Flex <Flex justify="center" align="center" boxSize="full">
justify="center"
align="center"
boxSize="full"
bgColor="rgba(255, 255, 255, 0.5)"
>
<Spinner color="gray" /> <Spinner color="gray" />
</Flex> </Flex>
)} )}

View File

@@ -1,77 +0,0 @@
import {
Stack,
Heading,
HStack,
Text,
Radio,
RadioGroup,
VStack,
} from '@chakra-ui/react'
import { MouseIcon, LaptopIcon } from '@/components/icons'
import { useUser } from '@/features/account'
import { GraphNavigation } from 'db'
import React, { useState } from 'react'
type Props = {
defaultGraphNavigation: GraphNavigation
}
export const EditorSettingsForm = ({ defaultGraphNavigation }: Props) => {
const { saveUser } = useUser()
const [value, setValue] = useState<string>(defaultGraphNavigation)
const changeEditorNavigation = (value: string) => {
setValue(value)
saveUser({ graphNavigation: value as GraphNavigation }).then()
}
const options = [
{
value: GraphNavigation.MOUSE,
label: 'Mouse',
description:
'Move by dragging the board and zoom in/out using the scroll wheel',
icon: <MouseIcon boxSize="35px" />,
},
{
value: GraphNavigation.TRACKPAD,
label: 'Trackpad',
description: 'Move the board using 2 fingers and zoom in/out by pinching',
icon: <LaptopIcon boxSize="35px" />,
},
]
return (
<Stack spacing={6}>
<Heading size="md">Editor Navigation</Heading>
<RadioGroup onChange={changeEditorNavigation} value={value}>
<HStack spacing={4} w="full" align="stretch">
{options.map((option) => (
<VStack
key={option.value}
as="label"
htmlFor={option.label}
cursor="pointer"
borderWidth="1px"
borderRadius="md"
w="full"
p="6"
spacing={6}
justifyContent="space-between"
>
<VStack spacing={6}>
{option.icon}
<Stack>
<Text fontWeight="bold">{option.label}</Text>
<Text>{option.description}</Text>
</Stack>
</VStack>
<Radio value={option.value} id={option.label} />
</VStack>
))}
</HStack>
</RadioGroup>
</Stack>
)
}

View File

@@ -1,4 +1,4 @@
import { useUser } from '@/features/account' import { UserPreferencesForm } from '@/features/account'
import { import {
Modal, Modal,
ModalBody, ModalBody,
@@ -6,31 +6,21 @@ import {
ModalContent, ModalContent,
ModalOverlay, ModalOverlay,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { GraphNavigation } from 'db'
import React from 'react' import React from 'react'
import { EditorSettingsForm } from './EditorSettingsForm'
type Props = { type Props = {
isOpen: boolean isOpen: boolean
onClose: () => void onClose: () => void
} }
export const EditorSettingsModal = ({ isOpen, onClose }: Props) => { export const EditorSettingsModal = ({ isOpen, onClose }: Props) => (
const { user } = useUser() <Modal isOpen={isOpen} onClose={onClose} size="xl">
<ModalOverlay />
return ( <ModalContent>
<Modal isOpen={isOpen} onClose={onClose} size="xl"> <ModalCloseButton />
<ModalOverlay /> <ModalBody pt="12" pb="8" px="8">
<ModalContent> <UserPreferencesForm />
<ModalCloseButton /> </ModalBody>
<ModalBody pt="12" pb="8" px="8"> </ModalContent>
<EditorSettingsForm </Modal>
defaultGraphNavigation={ )
user?.graphNavigation ?? GraphNavigation.TRACKPAD
}
/>
</ModalBody>
</ModalContent>
</Modal>
)
}

View File

@@ -127,10 +127,9 @@ export const GettingStartedModal = () => {
height="315" height="315"
src="https://www.youtube.com/embed/jp3ggg_42-M" src="https://www.youtube.com/embed/jp3ggg_42-M"
title="YouTube video player" title="YouTube video player"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen allowFullScreen
style={{ borderRadius: '0.5rem' }} style={{ borderRadius: '0.5rem', border: 'none' }}
/> />
<Accordion allowToggle> <Accordion allowToggle>
<AccordionItem> <AccordionItem>
@@ -146,20 +145,18 @@ export const GettingStartedModal = () => {
height="315" height="315"
src="https://www.youtube.com/embed/6BudIC4GYNk" src="https://www.youtube.com/embed/6BudIC4GYNk"
title="YouTube video player" title="YouTube video player"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen allowFullScreen
style={{ borderRadius: '0.5rem' }} style={{ borderRadius: '0.5rem', border: 'none' }}
/> />
<iframe <iframe
width="100%" width="100%"
height="315" height="315"
src="https://www.youtube.com/embed/ZuyDwFLRbfQ" src="https://www.youtube.com/embed/ZuyDwFLRbfQ"
title="YouTube video player" title="YouTube video player"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen allowFullScreen
style={{ borderRadius: '0.5rem' }} style={{ borderRadius: '0.5rem', border: 'none' }}
/> />
</AccordionPanel> </AccordionPanel>
</AccordionItem> </AccordionItem>

View File

@@ -5,6 +5,7 @@ import {
Fade, Fade,
Flex, Flex,
FlexProps, FlexProps,
useColorMode,
useEventListener, useEventListener,
UseToastOptions, UseToastOptions,
VStack, VStack,
@@ -21,6 +22,7 @@ import { headerHeight } from '../constants'
import { parseTypebotToPublicTypebot } from '@/features/publish' import { parseTypebotToPublicTypebot } from '@/features/publish'
export const PreviewDrawer = () => { export const PreviewDrawer = () => {
const isDark = useColorMode().colorMode === 'dark'
const { typebot } = useTypebot() const { typebot } = useTypebot()
const { setRightPanel, startPreviewAtGroup } = useEditor() const { setRightPanel, startPreviewAtGroup } = useEditor()
const { setPreviewingEdge } = useGraph() const { setPreviewingEdge } = useGraph()
@@ -68,7 +70,8 @@ export const PreviewDrawer = () => {
top={`0`} top={`0`}
h={`100%`} h={`100%`}
w={`${width}px`} w={`${width}px`}
bgColor="white" bgColor={isDark ? 'gray.900' : 'white'}
borderLeftWidth={'1px'}
shadow="lg" shadow="lg"
borderLeftRadius={'lg'} borderLeftRadius={'lg'}
onMouseOver={() => setIsResizeHandleVisible(true)} onMouseOver={() => setIsResizeHandleVisible(true)}
@@ -78,6 +81,7 @@ export const PreviewDrawer = () => {
> >
<Fade in={isResizeHandleVisible}> <Fade in={isResizeHandleVisible}>
<ResizeHandle <ResizeHandle
isDark={isDark}
pos="absolute" pos="absolute"
left="-7.5px" left="-7.5px"
top={`calc(50% - ${headerHeight}px)`} top={`calc(50% - ${headerHeight}px)`}
@@ -107,6 +111,7 @@ export const PreviewDrawer = () => {
onNewLog={handleNewLog} onNewLog={handleNewLog}
startGroupId={startPreviewAtGroup} startGroupId={startPreviewAtGroup}
isPreview isPreview
style={{ borderRadius: '10px' }}
/> />
</Flex> </Flex>
)} )}
@@ -115,20 +120,26 @@ export const PreviewDrawer = () => {
) )
} }
const ResizeHandle = (props: FlexProps) => { const ResizeHandle = (props: FlexProps & { isDark: boolean }) => {
return ( return (
<Flex <Flex
w="15px" w="15px"
h="50px" h="50px"
borderWidth={'1px'} borderWidth={'1px'}
bgColor={'white'} bgColor={props.isDark ? 'gray.800' : 'white'}
cursor={'col-resize'} cursor={'col-resize'}
justifyContent={'center'} justifyContent={'center'}
align={'center'} align={'center'}
borderRadius={'sm'}
{...props} {...props}
> >
<Box w="2px" bgColor={'gray.300'} h="70%" mr="0.5" /> <Box
<Box w="2px" bgColor={'gray.300'} h="70%" /> w="2px"
bgColor={props.isDark ? 'gray.600' : 'gray.300'}
h="70%"
mr="0.5"
/>
<Box w="2px" bgColor={props.isDark ? 'gray.600' : 'gray.300'} h="70%" />
</Flex> </Flex>
) )
} }

View File

@@ -39,7 +39,7 @@ export const EditableTypebotName = ({
fontSize="14px" fontSize="14px"
minW="30px" minW="30px"
minH="20px" minH="20px"
bgColor={currentName === '' ? 'gray.100' : 'white'} bgColor={currentName === '' ? 'gray.100' : 'inherit'}
/> />
<EditableInput fontSize="14px" /> <EditableInput fontSize="14px" />
</Editable> </Editable>

View File

@@ -6,6 +6,7 @@ import {
Tooltip, Tooltip,
Spinner, Spinner,
Text, Text,
useColorModeValue,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { import {
BuoyIcon, BuoyIcon,
@@ -66,7 +67,7 @@ export const TypebotHeader = () => {
h={`${headerHeight}px`} h={`${headerHeight}px`}
zIndex={100} zIndex={100}
pos="relative" pos="relative"
bgColor="white" bgColor={useColorModeValue('white', 'gray.900')}
flexShrink={0} flexShrink={0}
> >
<HStack <HStack
@@ -201,6 +202,7 @@ export const TypebotHeader = () => {
<CollaborationMenuButton isLoading={isNotDefined(typebot)} /> <CollaborationMenuButton isLoading={isNotDefined(typebot)} />
{router.pathname.includes('/edit') && isNotDefined(rightPanel) && ( {router.pathname.includes('/edit') && isNotDefined(rightPanel) && (
<Button <Button
colorScheme="gray"
onClick={handlePreviewClick} onClick={handlePreviewClick}
isLoading={isNotDefined(typebot)} isLoading={isNotDefined(typebot)}
size="sm" size="sm"

View File

@@ -1,7 +1,6 @@
export { TypebotProvider, useTypebot } from './providers/TypebotProvider' export { TypebotProvider, useTypebot } from './providers/TypebotProvider'
export { TypebotHeader } from './components/TypebotHeader' export { TypebotHeader } from './components/TypebotHeader'
export { EditTypebotPage } from './components/EditTypebotPage' export { EditTypebotPage } from './components/EditTypebotPage'
export { EditorSettingsForm } from './components/EditorSettingsForm'
export { headerHeight } from './constants' export { headerHeight } from './constants'
export { BlockIcon } from './components/BlocksSideBar/BlockIcon' export { BlockIcon } from './components/BlocksSideBar/BlockIcon'
export { RightPanel, useEditor } from './providers/EditorProvider' export { RightPanel, useEditor } from './providers/EditorProvider'

View File

@@ -15,6 +15,7 @@ import {
SkeletonText, SkeletonText,
SkeletonCircle, SkeletonCircle,
WrapItem, WrapItem,
useColorModeValue,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { FolderIcon, MoreVerticalIcon } from '@/components/icons' import { FolderIcon, MoreVerticalIcon } from '@/components/icons'
import { ConfirmModal } from '@/components/ConfirmModal' import { ConfirmModal } from '@/components/ConfirmModal'
@@ -118,7 +119,9 @@ export const FolderButton = ({
onSubmit={onRenameSubmit} onSubmit={onRenameSubmit}
> >
<EditablePreview <EditablePreview
_hover={{ bgColor: 'gray.300' }} _hover={{
bg: useColorModeValue('gray.100', 'gray.700'),
}}
px="2" px="2"
textAlign="center" textAlign="center"
/> />

View File

@@ -102,7 +102,6 @@ export const TypebotButton = ({
display="flex" display="flex"
flexDir="column" flexDir="column"
variant="outline" variant="outline"
color="gray.800"
w="225px" w="225px"
h="270px" h="270px"
mr={{ sm: 6 }} mr={{ sm: 6 }}

View File

@@ -1,7 +1,7 @@
import { Coordinates, useGraph, useGroupsCoordinates } from '../../providers' import { Coordinates, useGraph, useGroupsCoordinates } from '../../providers'
import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react' import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react'
import { Edge as EdgeProps } from 'models' import { Edge as EdgeProps } from 'models'
import { Portal, useDisclosure } from '@chakra-ui/react' import { color, Portal, useColorMode, useDisclosure } from '@chakra-ui/react'
import { useTypebot } from '@/features/editor' import { useTypebot } from '@/features/editor'
import { EdgeMenu } from './EdgeMenu' import { EdgeMenu } from './EdgeMenu'
import { colors } from '@/lib/theme' import { colors } from '@/lib/theme'
@@ -23,6 +23,7 @@ type Props = {
edge: EdgeProps edge: EdgeProps
} }
export const Edge = ({ edge }: Props) => { export const Edge = ({ edge }: Props) => {
const isDark = useColorMode().colorMode === 'dark'
const { deleteEdge } = useTypebot() const { deleteEdge } = useTypebot()
const { const {
previewingEdge, previewingEdge,
@@ -141,7 +142,13 @@ export const Edge = ({ edge }: Props) => {
<path <path
data-testid="edge" data-testid="edge"
d={path} d={path}
stroke={isPreviewing ? colors.blue[400] : colors.gray[400]} stroke={
isPreviewing
? colors.blue[400]
: isDark
? colors.gray[700]
: colors.gray[400]
}
strokeWidth="2px" strokeWidth="2px"
markerEnd={isPreviewing ? 'url(#blue-arrow)' : 'url(#arrow)'} markerEnd={isPreviewing ? 'url(#blue-arrow)' : 'url(#arrow)'}
fill="none" fill="none"

View File

@@ -1,4 +1,4 @@
import { chakra } from '@chakra-ui/react' import { chakra, useColorMode } from '@chakra-ui/react'
import { colors } from '@/lib/theme' import { colors } from '@/lib/theme'
import { Edge as EdgeProps } from 'models' import { Edge as EdgeProps } from 'models'
import React from 'react' import React from 'react'
@@ -17,72 +17,75 @@ export const Edges = ({
edges, edges,
answersCounts, answersCounts,
onUnlockProPlanClick, onUnlockProPlanClick,
}: Props) => ( }: Props) => {
<chakra.svg const isDark = useColorMode().colorMode === 'dark'
width="full" return (
height="full" <chakra.svg
overflow="visible" width="full"
pos="absolute" height="full"
left="0" overflow="visible"
top="0" pos="absolute"
shapeRendering="geometricPrecision" left="0"
> top="0"
<DrawingEdge /> shapeRendering="geometricPrecision"
{edges.map((edge) => (
<Edge key={edge.id} edge={edge} />
))}
{answersCounts?.map((answerCount) => (
<DropOffEdge
key={answerCount.groupId}
answersCounts={answersCounts}
groupId={answerCount.groupId}
onUnlockProPlanClick={onUnlockProPlanClick}
/>
))}
<marker
id={'arrow'}
refX="8"
refY="4"
orient="auto"
viewBox="0 0 20 20"
markerUnits="userSpaceOnUse"
markerWidth="20"
markerHeight="20"
> >
<path <DrawingEdge />
d="M7.07138888,5.50174526 L2.43017246,7.82235347 C1.60067988,8.23709976 0.592024983,7.90088146 0.177278692,7.07138888 C0.0606951226,6.83822174 0,6.58111307 0,6.32042429 L0,1.67920787 C0,0.751806973 0.751806973,0 1.67920787,0 C1.93989666,0 2.19700532,0.0606951226 2.43017246,0.177278692 L7,3 C7.82949258,3.41474629 8.23709976,3.92128809 7.82235347,4.75078067 C7.6598671,5.07575341 7.39636161,5.33925889 7.07138888,5.50174526 Z" {edges.map((edge) => (
fill={colors.gray[400]} <Edge key={edge.id} edge={edge} />
/> ))}
</marker> {answersCounts?.map((answerCount) => (
<marker <DropOffEdge
id={'blue-arrow'} key={answerCount.groupId}
refX="8" answersCounts={answersCounts}
refY="4" groupId={answerCount.groupId}
orient="auto" onUnlockProPlanClick={onUnlockProPlanClick}
viewBox="0 0 20 20" />
markerUnits="userSpaceOnUse" ))}
markerWidth="20" <marker
markerHeight="20" id={'arrow'}
> refX="8"
<path refY="4"
d="M7.07138888,5.50174526 L2.43017246,7.82235347 C1.60067988,8.23709976 0.592024983,7.90088146 0.177278692,7.07138888 C0.0606951226,6.83822174 0,6.58111307 0,6.32042429 L0,1.67920787 C0,0.751806973 0.751806973,0 1.67920787,0 C1.93989666,0 2.19700532,0.0606951226 2.43017246,0.177278692 L7,3 C7.82949258,3.41474629 8.23709976,3.92128809 7.82235347,4.75078067 C7.6598671,5.07575341 7.39636161,5.33925889 7.07138888,5.50174526 Z" orient="auto"
fill={colors.blue[400]} viewBox="0 0 20 20"
/> markerUnits="userSpaceOnUse"
</marker> markerWidth="20"
<marker markerHeight="20"
id={'red-arrow'} >
refX="8" <path
refY="4" d="M7.07138888,5.50174526 L2.43017246,7.82235347 C1.60067988,8.23709976 0.592024983,7.90088146 0.177278692,7.07138888 C0.0606951226,6.83822174 0,6.58111307 0,6.32042429 L0,1.67920787 C0,0.751806973 0.751806973,0 1.67920787,0 C1.93989666,0 2.19700532,0.0606951226 2.43017246,0.177278692 L7,3 C7.82949258,3.41474629 8.23709976,3.92128809 7.82235347,4.75078067 C7.6598671,5.07575341 7.39636161,5.33925889 7.07138888,5.50174526 Z"
orient="auto" fill={isDark ? colors.gray[600] : colors.gray[400]}
viewBox="0 0 20 20" />
markerUnits="userSpaceOnUse" </marker>
markerWidth="20" <marker
markerHeight="20" id={'blue-arrow'}
> refX="8"
<path refY="4"
d="M7.07138888,5.50174526 L2.43017246,7.82235347 C1.60067988,8.23709976 0.592024983,7.90088146 0.177278692,7.07138888 C0.0606951226,6.83822174 0,6.58111307 0,6.32042429 L0,1.67920787 C0,0.751806973 0.751806973,0 1.67920787,0 C1.93989666,0 2.19700532,0.0606951226 2.43017246,0.177278692 L7,3 C7.82949258,3.41474629 8.23709976,3.92128809 7.82235347,4.75078067 C7.6598671,5.07575341 7.39636161,5.33925889 7.07138888,5.50174526 Z" orient="auto"
fill="#e53e3e" viewBox="0 0 20 20"
/> markerUnits="userSpaceOnUse"
</marker> markerWidth="20"
</chakra.svg> markerHeight="20"
) >
<path
d="M7.07138888,5.50174526 L2.43017246,7.82235347 C1.60067988,8.23709976 0.592024983,7.90088146 0.177278692,7.07138888 C0.0606951226,6.83822174 0,6.58111307 0,6.32042429 L0,1.67920787 C0,0.751806973 0.751806973,0 1.67920787,0 C1.93989666,0 2.19700532,0.0606951226 2.43017246,0.177278692 L7,3 C7.82949258,3.41474629 8.23709976,3.92128809 7.82235347,4.75078067 C7.6598671,5.07575341 7.39636161,5.33925889 7.07138888,5.50174526 Z"
fill={colors.blue[400]}
/>
</marker>
<marker
id={'red-arrow'}
refX="8"
refY="4"
orient="auto"
viewBox="0 0 20 20"
markerUnits="userSpaceOnUse"
markerWidth="20"
markerHeight="20"
>
<path
d="M7.07138888,5.50174526 L2.43017246,7.82235347 C1.60067988,8.23709976 0.592024983,7.90088146 0.177278692,7.07138888 C0.0606951226,6.83822174 0,6.58111307 0,6.32042429 L0,1.67920787 C0,0.751806973 0.751806973,0 1.67920787,0 C1.93989666,0 2.19700532,0.0606951226 2.43017246,0.177278692 L7,3 C7.82949258,3.41474629 8.23709976,3.92128809 7.82235347,4.75078067 C7.6598671,5.07575341 7.39636161,5.33925889 7.07138888,5.50174526 Z"
fill="#e53e3e"
/>
</marker>
</chakra.svg>
)
}

View File

@@ -1,4 +1,9 @@
import { BoxProps, Flex, useEventListener } from '@chakra-ui/react' import {
BoxProps,
Flex,
useColorModeValue,
useEventListener,
} from '@chakra-ui/react'
import { useGraph, useGroupsCoordinates } from '../../providers' import { useGraph, useGroupsCoordinates } from '../../providers'
import { Source } from 'models' import { Source } from 'models'
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
@@ -9,6 +14,9 @@ export const SourceEndpoint = ({
}: BoxProps & { }: BoxProps & {
source: Source source: Source
}) => { }) => {
const color = useColorModeValue('blue.200', 'blue.100')
const connectedColor = useColorModeValue('blue.300', 'blue.200')
const bg = useColorModeValue('gray.100', 'gray.700')
const [ranOnce, setRanOnce] = useState(false) const [ranOnce, setRanOnce] = useState(false)
const { setConnectingIds, addSourceEndpoint, previewingEdge } = useGraph() const { setConnectingIds, addSourceEndpoint, previewingEdge } = useGraph()
@@ -61,7 +69,7 @@ export const SourceEndpoint = ({
boxSize="20px" boxSize="20px"
justify="center" justify="center"
align="center" align="center"
bgColor="gray.100" bg={bg}
rounded="full" rounded="full"
> >
<Flex <Flex
@@ -72,8 +80,8 @@ export const SourceEndpoint = ({
borderColor={ borderColor={
previewingEdge?.from.blockId === source.blockId && previewingEdge?.from.blockId === source.blockId &&
previewingEdge.from.itemId === source.itemId previewingEdge.from.itemId === source.itemId
? 'blue.300' ? connectedColor
: 'blue.200' : color
} }
/> />
</Flex> </Flex>

View File

@@ -3,6 +3,7 @@ import {
HStack, HStack,
Popover, Popover,
PopoverTrigger, PopoverTrigger,
useColorModeValue,
useDisclosure, useDisclosure,
useEventListener, useEventListener,
} from '@chakra-ui/react' } from '@chakra-ui/react'
@@ -26,17 +27,17 @@ import { SourceEndpoint } from '../../Endpoints/SourceEndpoint'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { SettingsModal } from './SettingsPopoverContent/SettingsModal' import { SettingsModal } from './SettingsPopoverContent/SettingsModal'
import { BlockSettings } from './SettingsPopoverContent/SettingsPopoverContent' import { BlockSettings } from './SettingsPopoverContent/SettingsPopoverContent'
import { TextBubbleEditor } from '../../../../blocks/bubbles/textBubble/components/TextBubbleEditor'
import { TargetEndpoint } from '../../Endpoints' import { TargetEndpoint } from '../../Endpoints'
import { MediaBubblePopoverContent } from './MediaBubblePopoverContent' import { MediaBubblePopoverContent } from './MediaBubblePopoverContent'
import {
NodePosition,
useBlockDnd,
useDragDistance,
useGraph,
} from '../../../providers'
import { ContextMenu } from '@/components/ContextMenu' import { ContextMenu } from '@/components/ContextMenu'
import { setMultipleRefs } from '@/utils/helpers' import { setMultipleRefs } from '@/utils/helpers'
import { TextBubbleEditor } from '@/features/blocks/bubbles/textBubble'
import {
NodePosition,
useGraph,
useBlockDnd,
useDragDistance,
} from '../../../providers'
import { hasDefaultConnector } from '../../../utils' import { hasDefaultConnector } from '../../../utils'
export const BlockNode = ({ export const BlockNode = ({
@@ -50,6 +51,9 @@ export const BlockNode = ({
indices: { blockIndex: number; groupIndex: number } indices: { blockIndex: number; groupIndex: number }
onMouseDown?: (blockNodePosition: NodePosition, block: DraggableBlock) => void onMouseDown?: (blockNodePosition: NodePosition, block: DraggableBlock) => void
}) => { }) => {
const bg = useColorModeValue('gray.50', 'gray.850')
const previewingBorderColor = useColorModeValue('blue.400', 'blue.300')
const borderColor = useColorModeValue('gray.200', 'gray.800')
const { query } = useRouter() const { query } = useRouter()
const { const {
setConnectingIds, setConnectingIds,
@@ -165,7 +169,7 @@ export const BlockNode = ({
<ContextMenu<HTMLDivElement> <ContextMenu<HTMLDivElement>
renderMenu={() => <BlockNodeContextMenu indices={indices} />} renderMenu={() => <BlockNodeContextMenu indices={indices} />}
> >
{(ref, isOpened) => ( {(ref, isContextMenuOpened) => (
<Popover <Popover
placement="left" placement="left"
isLazy isLazy
@@ -186,12 +190,18 @@ export const BlockNode = ({
flex="1" flex="1"
userSelect="none" userSelect="none"
p="3" p="3"
borderWidth={isOpened || isPreviewing ? '2px' : '1px'} borderWidth={
borderColor={isOpened || isPreviewing ? 'blue.400' : 'gray.200'} isContextMenuOpened || isPreviewing ? '2px' : '1px'
margin={isOpened || isPreviewing ? '-1px' : 0} }
borderColor={
isContextMenuOpened || isPreviewing
? previewingBorderColor
: borderColor
}
margin={isContextMenuOpened || isPreviewing ? '-1px' : 0}
rounded="lg" rounded="lg"
cursor={'pointer'} cursor={'pointer'}
bgColor="gray.50" bg={bg}
align="flex-start" align="flex-start"
w="full" w="full"
transition="border-color 0.2s" transition="border-color 0.2s"

View File

@@ -1,5 +1,5 @@
import { BlockIcon } from '@/features/editor' import { BlockIcon } from '@/features/editor'
import { StackProps, HStack } from '@chakra-ui/react' import { StackProps, HStack, useColorModeValue } from '@chakra-ui/react'
import { StartBlock, Block, BlockIndices } from 'models' import { StartBlock, Block, BlockIndices } from 'models'
import { BlockNodeContent } from './BlockNodeContent/BlockNodeContent' import { BlockNodeContent } from './BlockNodeContent/BlockNodeContent'
@@ -13,7 +13,8 @@ export const BlockNodeOverlay = ({
p="3" p="3"
borderWidth="1px" borderWidth="1px"
rounded="lg" rounded="lg"
bgColor="white" borderColor={useColorModeValue('gray.200', 'gray.800')}
bgColor={useColorModeValue('gray.50', 'gray.850')}
cursor={'grab'} cursor={'grab'}
w="264px" w="264px"
pointerEvents="none" pointerEvents="none"

View File

@@ -10,6 +10,8 @@ import { useEffect, useRef, useState } from 'react'
import { useTypebot } from '@/features/editor' import { useTypebot } from '@/features/editor'
import { BlockNode } from './BlockNode' import { BlockNode } from './BlockNode'
import { BlockNodeOverlay } from './BlockNodeOverlay' import { BlockNodeOverlay } from './BlockNodeOverlay'
import { PlaceholderNode } from '../PlaceholderNode'
import { isDefined } from 'utils'
type Props = { type Props = {
groupId: string groupId: string
@@ -49,7 +51,7 @@ export const BlockNodesList = ({
const isDraggingOnCurrentGroup = const isDraggingOnCurrentGroup =
(draggedBlock || draggedBlockType) && mouseOverGroup?.id === groupId (draggedBlock || draggedBlockType) && mouseOverGroup?.id === groupId
const showSortPlaceholders = const showSortPlaceholders =
!isStartGroup && (draggedBlock || draggedBlockType) !isStartGroup && isDefined(draggedBlock || draggedBlockType)
useEffect(() => { useEffect(() => {
if (mouseOverGroup?.id !== groupId) setExpandedPlaceholderIndex(undefined) if (mouseOverGroup?.id !== groupId) setExpandedPlaceholderIndex(undefined)
@@ -126,17 +128,10 @@ export const BlockNodesList = ({
transition="none" transition="none"
pointerEvents={isReadOnly || isStartGroup ? 'none' : 'auto'} pointerEvents={isReadOnly || isStartGroup ? 'none' : 'auto'}
> >
<Flex <PlaceholderNode
ref={handlePushElementRef(0)} isVisible={showSortPlaceholders}
h={ isExpanded={expandedPlaceholderIndex === 0}
showSortPlaceholders && expandedPlaceholderIndex === 0 onRef={handlePushElementRef(0)}
? '50px'
: '2px'
}
bgColor={'gray.300'}
visibility={showSortPlaceholders ? 'visible' : 'hidden'}
rounded="lg"
transition={showSortPlaceholders ? 'height 200ms' : 'none'}
/> />
{typebot && {typebot &&
blocks.map((block, idx) => ( blocks.map((block, idx) => (
@@ -148,17 +143,10 @@ export const BlockNodesList = ({
isConnectable={blocks.length - 1 === idx} isConnectable={blocks.length - 1 === idx}
onMouseDown={handleBlockMouseDown(idx)} onMouseDown={handleBlockMouseDown(idx)}
/> />
<Flex <PlaceholderNode
ref={handlePushElementRef(idx + 1)} isVisible={showSortPlaceholders}
h={ isExpanded={expandedPlaceholderIndex === idx + 1}
showSortPlaceholders && expandedPlaceholderIndex === idx + 1 onRef={handlePushElementRef(idx + 1)}
? '50px'
: '2px'
}
bgColor={'gray.300'}
visibility={showSortPlaceholders ? 'visible' : 'hidden'}
rounded="lg"
transition={showSortPlaceholders ? 'height 200ms' : 'none'}
/> />
</Stack> </Stack>
))} ))}

View File

@@ -4,6 +4,7 @@ import {
EditablePreview, EditablePreview,
IconButton, IconButton,
Stack, Stack,
useColorModeValue,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import React, { memo, useCallback, useEffect, useRef, useState } from 'react' import React, { memo, useCallback, useEffect, useRef, useState } from 'react'
import { Group } from 'models' import { Group } from 'models'
@@ -52,6 +53,10 @@ const NonMemoizedDraggableGroupNode = ({
groupIndex, groupIndex,
onGroupDrag, onGroupDrag,
}: Props & { onGroupDrag: (newCoord: Coordinates) => void }) => { }: Props & { onGroupDrag: (newCoord: Coordinates) => void }) => {
const bg = useColorModeValue('white', 'gray.900')
const previewingBorderColor = useColorModeValue('blue.400', 'blue.300')
const borderColor = useColorModeValue('white', 'gray.800')
const editableHoverBg = useColorModeValue('gray.100', 'gray.700')
const { const {
connectingIds, connectingIds,
setConnectingIds, setConnectingIds,
@@ -172,16 +177,20 @@ const NonMemoizedDraggableGroupNode = ({
renderMenu={() => <GroupNodeContextMenu groupIndex={groupIndex} />} renderMenu={() => <GroupNodeContextMenu groupIndex={groupIndex} />}
isDisabled={isReadOnly || isStartGroup} isDisabled={isReadOnly || isStartGroup}
> >
{(ref, isOpened) => ( {(ref, isContextMenuOpened) => (
<Stack <Stack
ref={setMultipleRefs([ref, groupRef])} ref={setMultipleRefs([ref, groupRef])}
data-testid="group" data-testid="group"
p="4" p="4"
rounded="xl" rounded="xl"
bgColor="#ffffff" bg={bg}
borderWidth="2px" borderWidth={
isConnecting || isContextMenuOpened || isPreviewing ? '2px' : '1px'
}
borderColor={ borderColor={
isConnecting || isOpened || isPreviewing ? 'blue.400' : '#ffffff' isConnecting || isContextMenuOpened || isPreviewing
? previewingBorderColor
: borderColor
} }
w="300px" w="300px"
transition="border 300ms, box-shadow 200ms" transition="border 300ms, box-shadow 200ms"
@@ -208,7 +217,9 @@ const NonMemoizedDraggableGroupNode = ({
pr="8" pr="8"
> >
<EditablePreview <EditablePreview
_hover={{ bgColor: 'gray.200' }} _hover={{
bg: editableHoverBg,
}}
px="1" px="1"
userSelect={'none'} userSelect={'none'}
/> />

View File

@@ -1,4 +1,4 @@
import { Flex } from '@chakra-ui/react' import { Flex, useColorModeValue } from '@chakra-ui/react'
import { import {
Coordinates, Coordinates,
useGraph, useGraph,
@@ -30,6 +30,9 @@ export const ItemNode = ({
onMouseDown, onMouseDown,
connectionDisabled, connectionDisabled,
}: Props) => { }: Props) => {
const previewingBorderColor = useColorModeValue('blue.400', 'blue.300')
const borderColor = useColorModeValue('gray.200', 'gray.700')
const bg = useColorModeValue('white', undefined)
const { typebot } = useTypebot() const { typebot } = useTypebot()
const { previewingEdge } = useGraph() const { previewingEdge } = useGraph()
const [isMouseOver, setIsMouseOver] = useState(false) const [isMouseOver, setIsMouseOver] = useState(false)
@@ -59,7 +62,7 @@ export const ItemNode = ({
<ContextMenu<HTMLDivElement> <ContextMenu<HTMLDivElement>
renderMenu={() => <ItemNodeContextMenu indices={indices} />} renderMenu={() => <ItemNodeContextMenu indices={indices} />}
> >
{(ref, isOpened) => ( {(ref, isContextMenuOpened) => (
<Flex <Flex
data-testid="item" data-testid="item"
pos="relative" pos="relative"
@@ -74,10 +77,14 @@ export const ItemNode = ({
_hover={{ shadow: 'md' }} _hover={{ shadow: 'md' }}
transition="box-shadow 200ms, border-color 200ms" transition="box-shadow 200ms, border-color 200ms"
rounded="md" rounded="md"
borderWidth={isOpened || isPreviewing ? '2px' : '1px'} bg={bg}
borderColor={isOpened || isPreviewing ? 'blue.400' : 'gray.100'} borderWidth={isContextMenuOpened || isPreviewing ? '2px' : '1px'}
margin={isOpened || isPreviewing ? '-1px' : 0} borderColor={
bgColor="white" isContextMenuOpened || isPreviewing
? previewingBorderColor
: borderColor
}
margin={isContextMenuOpened || isPreviewing ? '-1px' : 0}
w="full" w="full"
> >
<ItemNodeContent <ItemNodeContent

View File

@@ -1,4 +1,4 @@
import { Flex, FlexProps } from '@chakra-ui/react' import { Flex, FlexProps, useColorModeValue } from '@chakra-ui/react'
import { Item } from 'models' import { Item } from 'models'
import React, { ReactNode } from 'react' import React, { ReactNode } from 'react'
@@ -12,9 +12,9 @@ export const ItemNodeOverlay = ({ item, ...props }: Props) => {
px="4" px="4"
py="2" py="2"
rounded="md" rounded="md"
bgColor="white" bgColor={useColorModeValue('white', 'gray.850')}
borderWidth="1px" borderWidth="1px"
borderColor={'gray.300'} borderColor={useColorModeValue('gray.200', 'gray.700')}
w="212px" w="212px"
pointerEvents="none" pointerEvents="none"
shadow="lg" shadow="lg"

View File

@@ -1,4 +1,11 @@
import { Flex, Portal, Stack, Text, useEventListener } from '@chakra-ui/react' import {
Flex,
Portal,
Stack,
Text,
useColorModeValue,
useEventListener,
} from '@chakra-ui/react'
import { import {
computeNearestPlaceholderIndex, computeNearestPlaceholderIndex,
useBlockDnd, useBlockDnd,
@@ -10,6 +17,8 @@ import { BlockIndices, BlockWithItems, LogicBlockType, Item } from 'models'
import React, { useEffect, useRef, useState } from 'react' import React, { useEffect, useRef, useState } from 'react'
import { ItemNode } from './ItemNode' import { ItemNode } from './ItemNode'
import { SourceEndpoint } from '../../Endpoints' import { SourceEndpoint } from '../../Endpoints'
import { ItemNodeOverlay } from './ItemNodeOverlay'
import { PlaceholderNode } from '../PlaceholderNode'
type Props = { type Props = {
block: BlockWithItems block: BlockWithItems
@@ -121,13 +130,10 @@ export const ItemNodesList = ({
return ( return (
<Stack flex={1} spacing={1} maxW="full" onClick={stopPropagating}> <Stack flex={1} spacing={1} maxW="full" onClick={stopPropagating}>
<Flex <PlaceholderNode
ref={handlePushElementRef(0)} isVisible={showPlaceholders}
h={showPlaceholders && expandedPlaceholderIndex === 0 ? '50px' : '2px'} isExpanded={expandedPlaceholderIndex === 0}
bgColor={'gray.300'} onRef={handlePushElementRef(0)}
visibility={showPlaceholders ? 'visible' : 'hidden'}
rounded="lg"
transition={showPlaceholders ? 'height 200ms' : 'none'}
/> />
{block.items.map((item, idx) => ( {block.items.map((item, idx) => (
<Stack key={item.id} spacing={1}> <Stack key={item.id} spacing={1}>
@@ -136,45 +142,14 @@ export const ItemNodesList = ({
indices={{ groupIndex, blockIndex, itemIndex: idx }} indices={{ groupIndex, blockIndex, itemIndex: idx }}
onMouseDown={handleBlockMouseDown(idx)} onMouseDown={handleBlockMouseDown(idx)}
/> />
<Flex <PlaceholderNode
ref={handlePushElementRef(idx + 1)} isVisible={showPlaceholders}
h={ isExpanded={expandedPlaceholderIndex === idx + 1}
showPlaceholders && expandedPlaceholderIndex === idx + 1 onRef={handlePushElementRef(idx + 1)}
? '50px'
: '2px'
}
bgColor={'gray.300'}
visibility={showPlaceholders ? 'visible' : 'hidden'}
rounded="lg"
transition={showPlaceholders ? 'height 200ms' : 'none'}
/> />
</Stack> </Stack>
))} ))}
{isLastBlock && ( {isLastBlock && <DefaultItemNode block={block} />}
<Flex
px="4"
py="2"
borderWidth="1px"
borderColor="gray.300"
bgColor={'gray.50'}
rounded="md"
pos="relative"
align="center"
cursor="not-allowed"
>
<Text color="gray.500">
{block.type === LogicBlockType.CONDITION ? 'Else' : 'Default'}
</Text>
<SourceEndpoint
source={{
groupId: block.groupId,
blockId: block.id,
}}
pos="absolute"
right="-49px"
/>
</Flex>
)}
{draggedItem && draggedItem.blockId === block.id && ( {draggedItem && draggedItem.blockId === block.id && (
<Portal> <Portal>
@@ -189,14 +164,38 @@ export const ItemNodesList = ({
w="220px" w="220px"
transformOrigin="0 0 0" transformOrigin="0 0 0"
> >
<ItemNode <ItemNodeOverlay item={draggedItem} />
item={draggedItem}
indices={{ groupIndex, blockIndex, itemIndex: 0 }}
connectionDisabled
/>
</Flex> </Flex>
</Portal> </Portal>
)} )}
</Stack> </Stack>
) )
} }
const DefaultItemNode = ({ block }: { block: BlockWithItems }) => {
return (
<Flex
px="4"
py="2"
borderWidth="1px"
borderColor={useColorModeValue('gray.300', undefined)}
bgColor={useColorModeValue('gray.50', 'gray.850')}
rounded="md"
pos="relative"
align="center"
cursor="not-allowed"
>
<Text color="gray.500">
{block.type === LogicBlockType.CONDITION ? 'Else' : 'Default'}
</Text>
<SourceEndpoint
source={{
groupId: block.groupId,
blockId: block.id,
}}
pos="absolute"
right="-49px"
/>
</Flex>
)
}

View File

@@ -0,0 +1,21 @@
import { Flex, useColorModeValue } from '@chakra-ui/react'
import React from 'react'
type Props = {
isVisible: boolean
isExpanded: boolean
onRef: (ref: HTMLDivElement) => void
}
export const PlaceholderNode = ({ isVisible, isExpanded, onRef }: Props) => {
return (
<Flex
ref={onRef}
h={isExpanded ? '50px' : '2px'}
bgColor={useColorModeValue('gray.300', 'gray.700')}
visibility={isVisible ? 'visible' : 'hidden'}
rounded="lg"
transition={isVisible ? 'height 200ms' : 'none'}
/>
)
}

View File

@@ -1,4 +1,4 @@
import { Stack, IconButton } from '@chakra-ui/react' import { Stack, IconButton, useColorModeValue } from '@chakra-ui/react'
import { PlusIcon, MinusIcon } from '@/components/icons' import { PlusIcon, MinusIcon } from '@/components/icons'
import { headerHeight } from '@/features/editor' import { headerHeight } from '@/features/editor'
@@ -14,7 +14,7 @@ export const ZoomButtons = ({
pos="fixed" pos="fixed"
top={`calc(${headerHeight}px + 70px)`} top={`calc(${headerHeight}px + 70px)`}
right="40px" right="40px"
bgColor="white" bgColor={useColorModeValue('white', 'gray.900')}
rounded="md" rounded="md"
zIndex={1} zIndex={1}
spacing="0" spacing="0"
@@ -25,7 +25,7 @@ export const ZoomButtons = ({
aria-label={'Zoom in'} aria-label={'Zoom in'}
size="sm" size="sm"
onClick={onZoomIn} onClick={onZoomIn}
bgColor="white" bgColor={useColorModeValue('white', undefined)}
borderBottomRadius={0} borderBottomRadius={0}
/> />
<IconButton <IconButton
@@ -33,7 +33,7 @@ export const ZoomButtons = ({
aria-label={'Zoom out'} aria-label={'Zoom out'}
size="sm" size="sm"
onClick={onZoomOut} onClick={onZoomOut}
bgColor="white" bgColor={useColorModeValue('white', undefined)}
borderTopRadius={0} borderTopRadius={0}
/> />
</Stack> </Stack>

View File

@@ -111,7 +111,7 @@ export const ChatEmbedSettings = ({
/> />
</HStack> </HStack>
<HStack justifyContent="space-between"> <HStack justifyContent="space-between">
<FormLabel htmlFor="custom-icon" mb="1" flexShrink={0}> <FormLabel htmlFor="custom-icon" mb="0" flexShrink={0}>
Custom button icon? Custom button icon?
</FormLabel> </FormLabel>
<Switch <Switch
@@ -139,9 +139,8 @@ export const ChatEmbedSettings = ({
alignItems="center" alignItems="center"
w="full" w="full"
justifyContent="space-between" justifyContent="space-between"
pr={1}
> >
<FormLabel htmlFor="fullscreen-option" mb="1"> <FormLabel htmlFor="fullscreen-option" mb="0">
Enable popup message? Enable popup message?
</FormLabel> </FormLabel>
<Switch <Switch

View File

@@ -2,18 +2,22 @@ import { Icon, IconProps } from '@chakra-ui/react'
export const NotionLogo = (props: IconProps) => ( export const NotionLogo = (props: IconProps) => (
<Icon <Icon
width="246" width="100"
height="246" height="100"
viewBox="0 0 246 246" viewBox="0 0 100 100"
fill="none" fill="white"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
{...props} {...props}
> >
<path
d="M6.017 4.313l55.333 -4.087c6.797 -0.583 8.543 -0.19 12.817 2.917l17.663 12.443c2.913 2.14 3.883 2.723 3.883 5.053v68.243c0 4.277 -1.553 6.807 -6.99 7.193L24.467 99.967c-4.08 0.193 -6.023 -0.39 -8.16 -3.113L3.3 79.94c-2.333 -3.113 -3.3 -5.443 -3.3 -8.167V11.113c0 -3.497 1.553 -6.413 6.017 -6.8z"
fill="#fff"
/>
<path <path
fillRule="evenodd" fillRule="evenodd"
clipRule="evenodd" clipRule="evenodd"
d="M46.0982 43.7783C53.7102 49.9627 56.5657 49.4909 70.8599 48.5371L205.616 40.4456C208.474 40.4456 206.098 37.5944 205.144 37.1206L182.764 20.9415C178.476 17.6122 172.763 13.7995 161.813 14.7532L31.3283 24.2703C26.5695 24.7422 25.6191 27.1216 27.5142 29.0287L46.0982 43.7783ZM54.1887 75.1833V216.97C54.1887 224.59 57.9966 227.441 66.5672 226.97L214.664 218.4C223.239 217.929 224.194 212.688 224.194 206.497V65.6619C224.194 59.4818 221.817 56.1491 216.568 56.6248L61.805 65.6619C56.0934 66.1419 54.1883 68.9989 54.1883 75.1833H54.1887ZM200.39 82.789C201.339 87.0755 200.39 91.3581 196.095 91.84L188.96 93.2618V197.938C182.764 201.268 177.051 203.172 172.291 203.172C164.668 203.172 162.759 200.791 157.05 193.658L110.375 120.384V191.278L125.145 194.611C125.145 194.611 125.145 203.172 113.229 203.172L80.3785 205.077C79.4242 203.172 80.3785 198.418 83.7107 197.465L92.2832 195.089V101.353L80.3809 100.399C79.4261 96.1126 81.8036 89.932 88.4753 89.4525L123.716 87.0769L172.291 161.305V95.6407L159.906 94.2194C158.955 88.9792 162.759 85.1742 167.522 84.7023L200.39 82.789ZM20.3726 11.4244L156.097 1.42918C172.765 -0.000288379 177.053 0.957344 187.529 8.56689L230.854 39.0181C238.003 44.2545 240.386 45.6801 240.386 51.3884V218.4C240.386 228.867 236.572 235.057 223.242 236.005L65.624 245.523C55.6168 246 50.8541 244.574 45.6134 237.908L13.7082 196.513C7.99173 188.893 5.61426 183.192 5.61426 176.523V28.0715C5.61426 19.512 9.42842 12.3719 20.3726 11.4244V11.4244Z" d="M61.35 0.227l-55.333 4.087C1.553 4.7 0 7.617 0 11.113v60.66c0 2.723 0.967 5.053 3.3 8.167l13.007 16.913c2.137 2.723 4.08 3.307 8.16 3.113l64.257 -3.89c5.433 -0.387 6.99 -2.917 6.99 -7.193V20.64c0 -2.21 -0.873 -2.847 -3.443 -4.733L74.167 3.143c-4.273 -3.107 -6.02 -3.5 -12.817 -2.917zM25.92 19.523c-5.247 0.353 -6.437 0.433 -9.417 -1.99L8.927 11.507c-0.77 -0.78 -0.383 -1.753 1.557 -1.947l53.193 -3.887c4.467 -0.39 6.793 1.167 8.54 2.527l9.123 6.61c0.39 0.197 1.36 1.36 0.193 1.36l-54.933 3.307 -0.68 0.047zM19.803 88.3V30.367c0 -2.53 0.777 -3.697 3.103 -3.893L86 22.78c2.14 -0.193 3.107 1.167 3.107 3.693v57.547c0 2.53 -0.39 4.67 -3.883 4.863l-60.377 3.5c-3.493 0.193 -5.043 -0.97 -5.043 -4.083zm59.6 -54.827c0.387 1.75 0 3.5 -1.75 3.7l-2.91 0.577v42.773c-2.527 1.36 -4.853 2.137 -6.797 2.137 -3.107 0 -3.883 -0.973 -6.21 -3.887l-19.03 -29.94v28.967l6.02 1.363s0 3.5 -4.857 3.5l-13.39 0.777c-0.39 -0.78 0 -2.723 1.357 -3.11l3.497 -0.97v-38.3L30.48 40.667c-0.39 -1.75 0.58 -4.277 3.3 -4.473l14.367 -0.967 19.8 30.327v-26.83l-5.047 -0.58c-0.39 -2.143 1.163 -3.7 3.103 -3.89l13.4 -0.78z"
fill="black" fill="#000"
/> />
</Icon> </Icon>
) )

View File

@@ -1,31 +1,35 @@
import { Icon, IconProps } from '@chakra-ui/react' import { colors } from '@/lib/theme'
import { Icon, IconProps, useColorModeValue } from '@chakra-ui/react'
export const OtherLogo = (props: IconProps) => ( export const OtherLogo = (props: IconProps) => {
<Icon const stroke = useColorModeValue('black', colors.gray[200])
width="512" return (
height="512" <Icon
viewBox="0 0 512 512" width="512"
fill="none" height="512"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"
{...props} fill="none"
> xmlns="http://www.w3.org/2000/svg"
<path {...props}
d="M256 282C270.359 282 282 270.359 282 256C282 241.641 270.359 230 256 230C241.641 230 230 241.641 230 256C230 270.359 241.641 282 256 282Z" >
fill="black" <path
/> d="M256 282C270.359 282 282 270.359 282 256C282 241.641 270.359 230 256 230C241.641 230 230 241.641 230 256C230 270.359 241.641 282 256 282Z"
<path fill={stroke}
d="M346 282C360.359 282 372 270.359 372 256C372 241.641 360.359 230 346 230C331.641 230 320 241.641 320 256C320 270.359 331.641 282 346 282Z" />
fill="black" <path
/> d="M346 282C360.359 282 372 270.359 372 256C372 241.641 360.359 230 346 230C331.641 230 320 241.641 320 256C320 270.359 331.641 282 346 282Z"
<path fill={stroke}
d="M166 282C180.359 282 192 270.359 192 256C192 241.641 180.359 230 166 230C151.641 230 140 241.641 140 256C140 270.359 151.641 282 166 282Z" />
fill="black" <path
/> d="M166 282C180.359 282 192 270.359 192 256C192 241.641 180.359 230 166 230C151.641 230 140 241.641 140 256C140 270.359 151.641 282 166 282Z"
<path fill={stroke}
d="M448 256C448 150 362 64 256 64C150 64 64 150 64 256C64 362 150 448 256 448C362 448 448 362 448 256Z" />
stroke="black" <path
strokeWidth="32" d="M448 256C448 150 362 64 256 64C150 64 64 150 64 256C64 362 150 448 256 448C362 448 448 362 448 256Z"
strokeMiterlimit="10" stroke={stroke}
/> strokeWidth="32"
</Icon> strokeMiterlimit="10"
) />
</Icon>
)
}

View File

@@ -1,29 +1,32 @@
import { Icon, IconProps } from '@chakra-ui/react' import { Icon, IconProps, useColorModeValue } from '@chakra-ui/react'
export const WixLogo = (props: IconProps) => ( export const WixLogo = (props: IconProps) => {
<Icon const fill = useColorModeValue('black', 'white')
width="311" return (
height="121" <Icon
viewBox="0 0 311 121" width="311"
fill="none" height="121"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 311 121"
{...props} fill="none"
> xmlns="http://www.w3.org/2000/svg"
<path {...props}
d="M178 2.29971C172 5.29971 169.4 10.8997 169.4 26.0997C169.4 26.0997 172.4 23.0997 177.2 21.2997C180.7 19.9997 183.2 18.2997 185 16.9997C190.2 13.0997 191 8.39971 191 0.199713C190.9 0.199713 182.7 -0.300287 178 2.29971Z" >
fill="#FBBD71" <path
/> d="M178 2.29971C172 5.29971 169.4 10.8997 169.4 26.0997C169.4 26.0997 172.4 23.0997 177.2 21.2997C180.7 19.9997 183.2 18.2997 185 16.9997C190.2 13.0997 191 8.39971 191 0.199713C190.9 0.199713 182.7 -0.300287 178 2.29971Z"
<path fill="#FBBD71"
d="M141.3 5.79963C136.1 10.0996 134.8 17.4996 134.8 17.4996L118 81.8996L104.2 29.1996C102.9 23.5996 100.3 16.6996 96.4 11.8996C91.6 5.79963 81.6 5.39963 80.4 5.39963C79.5 5.39963 69.6 5.79963 64.4 11.8996C60.5 16.6996 57.9 23.5996 56.6 29.1996L43.6 81.8996L26.8 17.4996C26.8 17.4996 25.5 10.5996 20.3 5.79963C12.1 -1.60037 0 0.199629 0 0.199629L32 120.7C32 120.7 42.4 121.6 47.6 119C54.5 115.5 58 113 61.9 96.4996C65.8 81.7996 76.2 38.9996 77 35.9996C77.4 34.6996 78.3 30.7996 80.9 30.7996C83.5 30.7996 84.4 34.2996 84.8 35.9996C85.7 38.9996 96 81.7996 99.9 96.4996C104.2 112.9 107.2 115.5 114.2 119C119.4 121.6 129.8 120.7 129.8 120.7L161.6 0.199629C161.6 0.199629 149.5 -1.50037 141.3 5.79963Z" />
fill="black" <path
/> d="M141.3 5.79963C136.1 10.0996 134.8 17.4996 134.8 17.4996L118 81.8996L104.2 29.1996C102.9 23.5996 100.3 16.6996 96.4 11.8996C91.6 5.79963 81.6 5.39963 80.4 5.39963C79.5 5.39963 69.6 5.79963 64.4 11.8996C60.5 16.6996 57.9 23.5996 56.6 29.1996L43.6 81.8996L26.8 17.4996C26.8 17.4996 25.5 10.5996 20.3 5.79963C12.1 -1.60037 0 0.199629 0 0.199629L32 120.7C32 120.7 42.4 121.6 47.6 119C54.5 115.5 58 113 61.9 96.4996C65.8 81.7996 76.2 38.9996 77 35.9996C77.4 34.6996 78.3 30.7996 80.9 30.7996C83.5 30.7996 84.4 34.2996 84.8 35.9996C85.7 38.9996 96 81.7996 99.9 96.4996C104.2 112.9 107.2 115.5 114.2 119C119.4 121.6 129.8 120.7 129.8 120.7L161.6 0.199629C161.6 0.199629 149.5 -1.50037 141.3 5.79963Z"
<path fill={fill}
d="M190.9 19.5996C190.9 19.5996 188.7 22.5996 184.4 25.1996C181.4 26.8996 178.8 27.7996 175.8 29.4996C170.6 32.0996 169.3 34.6996 169.3 38.5996V39.8996V46.3996V47.6996V120.3C169.3 120.3 177.5 121.2 182.7 118.6C189.6 115.1 190.9 111.7 190.9 96.9996V24.3996V19.5996Z" />
fill="black" <path
/> d="M190.9 19.5996C190.9 19.5996 188.7 22.5996 184.4 25.1996C181.4 26.8996 178.8 27.7996 175.8 29.4996C170.6 32.0996 169.3 34.6996 169.3 38.5996V39.8996V46.3996V47.6996V120.3C169.3 120.3 177.5 121.2 182.7 118.6C189.6 115.1 190.9 111.7 190.9 96.9996V24.3996V19.5996Z"
<path fill={fill}
d="M270.4 60.7003L311 0.600311C311 0.600311 294.2 -2.39969 285.5 5.40031C279.9 10.2003 274.3 19.2003 274.3 19.2003L259.6 40.8003C258.7 42.1003 257.9 43.0003 256.6 43.0003C255.3 43.0003 254 41.7003 253.6 40.8003L238.9 19.2003C238.9 19.2003 232.9 10.6003 227.7 5.40031C219.1 -2.39969 202.2 0.600311 202.2 0.600311L241.5 60.6003L201.3 120.6C201.3 120.6 219 122.8 227.7 115C233.3 110.2 238.5 102 238.5 102L253.2 80.4003C254.1 79.1003 254.9 78.2003 256.2 78.2003C257.5 78.2003 258.8 79.5003 259.2 80.4003L273.9 102C273.9 102 279.5 110.2 284.7 115C293.3 122.8 310.6 120.6 310.6 120.6L270.4 60.7003Z" />
fill="black" <path
/> d="M270.4 60.7003L311 0.600311C311 0.600311 294.2 -2.39969 285.5 5.40031C279.9 10.2003 274.3 19.2003 274.3 19.2003L259.6 40.8003C258.7 42.1003 257.9 43.0003 256.6 43.0003C255.3 43.0003 254 41.7003 253.6 40.8003L238.9 19.2003C238.9 19.2003 232.9 10.6003 227.7 5.40031C219.1 -2.39969 202.2 0.600311 202.2 0.600311L241.5 60.6003L201.3 120.6C201.3 120.6 219 122.8 227.7 115C233.3 110.2 238.5 102 238.5 102L253.2 80.4003C254.1 79.1003 254.9 78.2003 256.2 78.2003C257.5 78.2003 258.8 79.5003 259.2 80.4003L273.9 102C273.9 102 279.5 110.2 284.7 115C293.3 122.8 310.6 120.6 310.6 120.6L270.4 60.7003Z"
</Icon> fill={fill}
) />
</Icon>
)
}

View File

@@ -1,33 +1,37 @@
import { Icon, IconProps } from '@chakra-ui/react' import { colors } from '@/lib/theme'
import * as react from '@chakra-ui/react'
export const WordpressLogo = (props: IconProps) => ( export const WordpressLogo = (props: react.IconProps) => {
<Icon const fill = react.useColorModeValue('#464342', colors.gray[400])
width="123" return (
height="123" <react.Icon
viewBox="0 0 123 123" width="123"
fill="none" height="123"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 123 123"
{...props} fill="none"
> xmlns="http://www.w3.org/2000/svg"
<path {...props}
d="M8.70801 61.2601C8.70801 82.0621 20.797 100.039 38.327 108.558L13.258 39.8721C10.342 46.4081 8.70801 53.6411 8.70801 61.2601Z" >
fill="#464342" <path
/> d="M8.70801 61.2601C8.70801 82.0621 20.797 100.039 38.327 108.558L13.258 39.8721C10.342 46.4081 8.70801 53.6411 8.70801 61.2601Z"
<path fill={fill}
d="M96.7396 58.608C96.7396 52.113 94.4066 47.615 92.4056 44.114C89.7416 39.785 87.2446 36.119 87.2446 31.79C87.2446 26.959 90.9086 22.462 96.0696 22.462C96.3026 22.462 96.5236 22.491 96.7506 22.504C87.4006 13.938 74.9436 8.70801 61.2616 8.70801C42.9016 8.70801 26.7486 18.128 17.3516 32.396C18.5846 32.433 19.7466 32.459 20.7336 32.459C26.2306 32.459 34.7396 31.792 34.7396 31.792C37.5726 31.625 37.9066 35.786 35.0766 36.121C35.0766 36.121 32.2296 36.456 29.0616 36.622L48.1996 93.547L59.7006 59.054L51.5126 36.62C48.6826 36.454 46.0016 36.119 46.0016 36.119C43.1696 35.953 43.5016 31.623 46.3336 31.79C46.3336 31.79 55.0126 32.457 60.1766 32.457C65.6726 32.457 74.1826 31.79 74.1826 31.79C77.0176 31.623 77.3506 35.784 74.5196 36.119C74.5196 36.119 71.6666 36.454 68.5046 36.62L87.4966 93.114L92.7386 75.597C95.0106 68.328 96.7396 63.107 96.7396 58.608Z" />
fill="#464342" <path
/> d="M96.7396 58.608C96.7396 52.113 94.4066 47.615 92.4056 44.114C89.7416 39.785 87.2446 36.119 87.2446 31.79C87.2446 26.959 90.9086 22.462 96.0696 22.462C96.3026 22.462 96.5236 22.491 96.7506 22.504C87.4006 13.938 74.9436 8.70801 61.2616 8.70801C42.9016 8.70801 26.7486 18.128 17.3516 32.396C18.5846 32.433 19.7466 32.459 20.7336 32.459C26.2306 32.459 34.7396 31.792 34.7396 31.792C37.5726 31.625 37.9066 35.786 35.0766 36.121C35.0766 36.121 32.2296 36.456 29.0616 36.622L48.1996 93.547L59.7006 59.054L51.5126 36.62C48.6826 36.454 46.0016 36.119 46.0016 36.119C43.1696 35.953 43.5016 31.623 46.3336 31.79C46.3336 31.79 55.0126 32.457 60.1766 32.457C65.6726 32.457 74.1826 31.79 74.1826 31.79C77.0176 31.623 77.3506 35.784 74.5196 36.119C74.5196 36.119 71.6666 36.454 68.5046 36.62L87.4966 93.114L92.7386 75.597C95.0106 68.328 96.7396 63.107 96.7396 58.608Z"
<path fill={fill}
d="M62.184 65.8574L46.416 111.676C51.124 113.06 56.103 113.817 61.262 113.817C67.382 113.817 73.251 112.759 78.714 110.838C78.573 110.613 78.445 110.374 78.34 110.114L62.184 65.8574Z" />
fill="#464342" <path
/> d="M62.184 65.8574L46.416 111.676C51.124 113.06 56.103 113.817 61.262 113.817C67.382 113.817 73.251 112.759 78.714 110.838C78.573 110.613 78.445 110.374 78.34 110.114L62.184 65.8574Z"
<path fill={fill}
d="M107.376 36.0459C107.602 37.7199 107.73 39.5169 107.73 41.4499C107.73 46.7829 106.734 52.7779 103.734 60.2739L87.6807 106.687C103.305 97.5759 113.814 80.6489 113.814 61.2609C113.815 52.1239 111.481 43.5319 107.376 36.0459Z" />
fill="#464342" <path
/> d="M107.376 36.0459C107.602 37.7199 107.73 39.5169 107.73 41.4499C107.73 46.7829 106.734 52.7779 103.734 60.2739L87.6807 106.687C103.305 97.5759 113.814 80.6489 113.814 61.2609C113.815 52.1239 111.481 43.5319 107.376 36.0459Z"
<path fill={fill}
d="M61.262 0C27.483 0 0 27.481 0 61.26C0 95.043 27.483 122.523 61.262 122.523C95.04 122.523 122.527 95.043 122.527 61.26C122.526 27.481 95.04 0 61.262 0ZM61.262 119.715C29.032 119.715 2.809 93.492 2.809 61.26C2.809 29.03 29.031 2.809 61.262 2.809C93.491 2.809 119.712 29.03 119.712 61.26C119.712 93.492 93.491 119.715 61.262 119.715Z" />
fill="#464342" <path
/> d="M61.262 0C27.483 0 0 27.481 0 61.26C0 95.043 27.483 122.523 61.262 122.523C95.04 122.523 122.527 95.043 122.527 61.26C122.526 27.481 95.04 0 61.262 0ZM61.262 119.715C29.032 119.715 2.809 93.492 2.809 61.26C2.809 29.03 29.031 2.809 61.262 2.809C93.491 2.809 119.712 29.03 119.712 61.26C119.712 93.492 93.491 119.715 61.262 119.715Z"
</Icon> fill={fill}
) />
</react.Icon>
)
}

View File

@@ -5,7 +5,14 @@ import { useUsage } from '@/features/billing'
import { useTypebot, TypebotHeader } from '@/features/editor' import { useTypebot, TypebotHeader } from '@/features/editor'
import { useWorkspace } from '@/features/workspace' import { useWorkspace } from '@/features/workspace'
import { useToast } from '@/hooks/useToast' import { useToast } from '@/hooks/useToast'
import { Flex, HStack, Button, Tag, Text } from '@chakra-ui/react' import {
Flex,
HStack,
Button,
Tag,
Text,
useColorModeValue,
} from '@chakra-ui/react'
import Link from 'next/link' import Link from 'next/link'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useMemo } from 'react' import { useMemo } from 'react'
@@ -121,8 +128,8 @@ export const ResultsPage = () => {
<Flex <Flex
pos="absolute" pos="absolute"
zIndex={2} zIndex={2}
bgColor="white"
w="full" w="full"
bg={useColorModeValue('white', 'gray.900')}
justifyContent="center" justifyContent="center"
h="60px" h="60px"
display={['none', 'flex']} display={['none', 'flex']}

View File

@@ -1,4 +1,4 @@
import { chakra, Fade, Button } from '@chakra-ui/react' import { chakra, Fade, Button, useColorModeValue } from '@chakra-ui/react'
import { Cell as CellProps, flexRender } from '@tanstack/react-table' import { Cell as CellProps, flexRender } from '@tanstack/react-table'
import { ExpandIcon } from '@/components/icons' import { ExpandIcon } from '@/components/icons'
import { memo } from 'react' import { memo } from 'react'
@@ -26,7 +26,7 @@ const Cell = ({
px="4" px="4"
py="2" py="2"
border="1px" border="1px"
borderColor="gray.200" borderColor={useColorModeValue('gray.200', 'gray.700')}
whiteSpace="nowrap" whiteSpace="nowrap"
wordBreak="normal" wordBreak="normal"
overflow="hidden" overflow="hidden"

View File

@@ -1,4 +1,4 @@
import { Box, BoxProps, chakra } from '@chakra-ui/react' import { Box, BoxProps, chakra, useColorModeValue } from '@chakra-ui/react'
import { flexRender, HeaderGroup } from '@tanstack/react-table' import { flexRender, HeaderGroup } from '@tanstack/react-table'
import React from 'react' import React from 'react'
import { TableData } from '../../types' import { TableData } from '../../types'
@@ -8,6 +8,7 @@ type Props = {
} }
export const HeaderRow = ({ headerGroup }: Props) => { export const HeaderRow = ({ headerGroup }: Props) => {
const borderColor = useColorModeValue('gray.200', 'gray.700')
return ( return (
<tr key={headerGroup.id}> <tr key={headerGroup.id}>
{headerGroup.headers.map((header) => { {headerGroup.headers.map((header) => {
@@ -18,7 +19,7 @@ export const HeaderRow = ({ headerGroup }: Props) => {
py="2" py="2"
pos="relative" pos="relative"
border="1px" border="1px"
borderColor="gray.200" borderColor={borderColor}
fontWeight="normal" fontWeight="normal"
whiteSpace="nowrap" whiteSpace="nowrap"
wordBreak="normal" wordBreak="normal"

View File

@@ -1,4 +1,11 @@
import { chakra, Checkbox, Flex, Skeleton } from '@chakra-ui/react' import {
chakra,
Checkbox,
Flex,
Skeleton,
useColorMode,
useColorModeValue,
} from '@chakra-ui/react'
import React from 'react' import React from 'react'
type LoadingRowsProps = { type LoadingRowsProps = {
@@ -6,6 +13,7 @@ type LoadingRowsProps = {
} }
export const LoadingRows = ({ totalColumns }: LoadingRowsProps) => { export const LoadingRows = ({ totalColumns }: LoadingRowsProps) => {
const borderColor = useColorModeValue('gray.200', 'gray.700')
return ( return (
<> <>
{Array.from(Array(3)).map((_, idx) => ( {Array.from(Array(3)).map((_, idx) => (
@@ -14,7 +22,7 @@ export const LoadingRows = ({ totalColumns }: LoadingRowsProps) => {
px="2" px="2"
py="2" py="2"
border="1px" border="1px"
borderColor="gray.200" borderColor={borderColor}
width="40px" width="40px"
> >
<Flex ml="1"> <Flex ml="1">
@@ -28,7 +36,7 @@ export const LoadingRows = ({ totalColumns }: LoadingRowsProps) => {
px="4" px="4"
py="2" py="2"
border="1px" border="1px"
borderColor="gray.200" borderColor={borderColor}
> >
<Skeleton height="5px" w="full" /> <Skeleton height="5px" w="full" />
</chakra.td> </chakra.td>

View File

@@ -6,6 +6,7 @@ import {
HStack, HStack,
Stack, Stack,
Text, Text,
useColorModeValue,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { AlignLeftTextIcon } from '@/components/icons' import { AlignLeftTextIcon } from '@/components/icons'
import { ResultHeaderCell, ResultsTablePreferences } from 'models' import { ResultHeaderCell, ResultsTablePreferences } from 'models'
@@ -26,6 +27,7 @@ import { HeaderRow } from './HeaderRow'
import { CellValueType, TableData } from '../../types' import { CellValueType, TableData } from '../../types'
import { HeaderIcon, parseAccessor } from '../../utils' import { HeaderIcon, parseAccessor } from '../../utils'
import { IndeterminateCheckbox } from './IndeterminateCheckbox' import { IndeterminateCheckbox } from './IndeterminateCheckbox'
import { colors } from '@/lib/theme'
type ResultsTableProps = { type ResultsTableProps = {
resultHeader: ResultHeaderCell[] resultHeader: ResultHeaderCell[]
@@ -46,6 +48,7 @@ export const ResultsTable = ({
onLogOpenIndex, onLogOpenIndex,
onResultExpandIndex, onResultExpandIndex,
}: ResultsTableProps) => { }: ResultsTableProps) => {
const background = useColorModeValue('white', colors.gray[900])
const { updateTypebot } = useTypebot() const { updateTypebot } = useTypebot()
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({}) const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({})
const [columnsVisibility, setColumnsVisibility] = useState< const [columnsVisibility, setColumnsVisibility] = useState<
@@ -204,7 +207,16 @@ export const ResultsTable = ({
onColumnOrderChange={instance.setColumnOrder} onColumnOrderChange={instance.setColumnOrder}
/> />
</Flex> </Flex>
<Box className="table-wrapper" overflow="scroll" rounded="md"> <Box
overflow="scroll"
rounded="md"
data-testid="results-table"
backgroundImage={`linear-gradient(to right, ${background}, ${background}), linear-gradient(to right, ${background}, ${background}),linear-gradient(to right, rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0)),linear-gradient(to left, rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0));`}
backgroundPosition="left center, right center, left center, right center"
backgroundRepeat="no-repeat"
backgroundSize="30px 100%, 30px 100%, 15px 100%, 15px 100%"
backgroundAttachment="local, local, scroll, scroll"
>
<chakra.table rounded="md"> <chakra.table rounded="md">
<thead> <thead>
{instance.getHeaderGroups().map((headerGroup) => ( {instance.getHeaderGroups().map((headerGroup) => (
@@ -225,7 +237,13 @@ export const ResultsTable = ({
/> />
))} ))}
{hasMore === true && ( {hasMore === true && (
<LoadingRows totalColumns={columns.length - 1} /> <LoadingRows
totalColumns={
resultHeader.filter(
(header) => columnsVisibility[header.id] !== false
).length + 1
}
/>
)} )}
</tbody> </tbody>
</chakra.table> </chakra.table>

View File

@@ -159,7 +159,7 @@ const validateExportAll = (data: unknown[]) => {
const scrollToBottom = (page: Page) => const scrollToBottom = (page: Page) =>
page.evaluate(() => { page.evaluate(() => {
const tableWrapper = document.querySelector('.table-wrapper') const tableWrapper = document.querySelector('[data-testid="results-table"]')
if (!tableWrapper) return if (!tableWrapper) return
tableWrapper.scrollTo(0, tableWrapper.scrollHeight) tableWrapper.scrollTo(0, tableWrapper.scrollHeight)
}) })

View File

@@ -1,4 +1,11 @@
import { VStack, Heading, Stack, Button, useDisclosure } from '@chakra-ui/react' import {
VStack,
Heading,
Stack,
Button,
useDisclosure,
useColorModeValue,
} from '@chakra-ui/react'
import { ToolIcon, TemplateIcon, DownloadIcon } from '@/components/icons' import { ToolIcon, TemplateIcon, DownloadIcon } from '@/components/icons'
import { Typebot } from 'models' import { Typebot } from 'models'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
@@ -67,7 +74,13 @@ export const CreateNewTypebotButtons = () => {
w="full" w="full"
py="8" py="8"
fontSize="lg" fontSize="lg"
leftIcon={<ToolIcon color="blue.500" boxSize="25px" mr="2" />} leftIcon={
<ToolIcon
color={useColorModeValue('blue.500', 'blue.300')}
boxSize="25px"
mr="2"
/>
}
onClick={() => handleCreateSubmit()} onClick={() => handleCreateSubmit()}
isLoading={isLoading} isLoading={isLoading}
> >
@@ -78,7 +91,13 @@ export const CreateNewTypebotButtons = () => {
w="full" w="full"
py="8" py="8"
fontSize="lg" fontSize="lg"
leftIcon={<TemplateIcon color="orange.500" boxSize="25px" mr="2" />} leftIcon={
<TemplateIcon
color={useColorModeValue('orange.500', 'orange.300')}
boxSize="25px"
mr="2"
/>
}
onClick={onOpen} onClick={onOpen}
isLoading={isLoading} isLoading={isLoading}
> >
@@ -89,7 +108,13 @@ export const CreateNewTypebotButtons = () => {
w="full" w="full"
py="8" py="8"
fontSize="lg" fontSize="lg"
leftIcon={<DownloadIcon color="purple.500" boxSize="25px" mr="2" />} leftIcon={
<DownloadIcon
color={useColorModeValue('purple.500', 'purple.300')}
boxSize="25px"
mr="2"
/>
}
isLoading={isLoading} isLoading={isLoading}
onNewTypebot={handleCreateSubmit} onNewTypebot={handleCreateSubmit}
> >

View File

@@ -2,6 +2,7 @@ import {
Box, Box,
Flex, Flex,
HStack, HStack,
useColorModeValue,
useRadio, useRadio,
useRadioGroup, useRadioGroup,
UseRadioProps, UseRadioProps,
@@ -57,9 +58,14 @@ export const RadioCard = (props: UseRadioProps & { children: ReactNode }) => {
borderWidth="1px" borderWidth="1px"
borderRadius="md" borderRadius="md"
_checked={{ _checked={{
bg: 'orange.400', borderWidth: '2px',
color: 'white', borderColor: 'blue.400',
borderColor: 'orange.400', }}
_hover={{
bgColor: useColorModeValue('gray.100', 'gray.700'),
}}
_active={{
bgColor: useColorModeValue('gray.200', 'gray.600'),
}} }}
px={5} px={5}
py={2} py={2}

View File

@@ -8,6 +8,7 @@ import {
Stack, Stack,
Tag, Tag,
Text, Text,
useColorModeValue,
} from '@chakra-ui/react' } from '@chakra-ui/react'
import { WorkspaceRole } from 'db' import { WorkspaceRole } from 'db'
import React from 'react' import React from 'react'
@@ -38,9 +39,15 @@ export const MemberItem = ({
}: Props) => { }: Props) => {
const handleAdminClick = () => onSelectNewRole(WorkspaceRole.ADMIN) const handleAdminClick = () => onSelectNewRole(WorkspaceRole.ADMIN)
const handleMemberClick = () => onSelectNewRole(WorkspaceRole.MEMBER) const handleMemberClick = () => onSelectNewRole(WorkspaceRole.MEMBER)
return ( return (
<Menu placement="bottom-end" isLazy> <Menu placement="bottom-end" isLazy>
<MenuButton _hover={{ backgroundColor: 'gray.100' }} borderRadius="md"> <MenuButton
_hover={{
bg: useColorModeValue('gray.100', 'gray.700'),
}}
borderRadius="md"
>
<MemberIdentityContent <MemberIdentityContent
email={email} email={email}
name={name} name={name}

View File

@@ -96,7 +96,7 @@ export const MembersList = () => {
return ( return (
<Stack w="full" spacing={3}> <Stack w="full" spacing={3}>
{!canInviteNewMember && ( {true && (
<UnlockPlanAlertInfo <UnlockPlanAlertInfo
contentLabel={` contentLabel={`
Upgrade your plan to work with more team members, and unlock awesome Upgrade your plan to work with more team members, and unlock awesome

View File

@@ -20,9 +20,8 @@ import { useState } from 'react'
import { MembersList } from './MembersList' import { MembersList } from './MembersList'
import { WorkspaceSettingsForm } from './WorkspaceSettingsForm' import { WorkspaceSettingsForm } from './WorkspaceSettingsForm'
import { useWorkspace } from '../WorkspaceProvider' import { useWorkspace } from '../WorkspaceProvider'
import { MyAccountForm } from '@/features/account' import { MyAccountForm, UserPreferencesForm } from '@/features/account'
import { BillingContent } from '@/features/billing' import { BillingContent } from '@/features/billing'
import { EditorSettingsForm } from '@/features/editor'
type Props = { type Props = {
isOpen: boolean isOpen: boolean
@@ -177,9 +176,7 @@ const SettingsContent = ({
case 'my-account': case 'my-account':
return <MyAccountForm /> return <MyAccountForm />
case 'user-settings': case 'user-settings':
return ( return <UserPreferencesForm />
<EditorSettingsForm defaultGraphNavigation={defaultGraphNavigation} />
)
case 'workspace-settings': case 'workspace-settings':
return <WorkspaceSettingsForm onClose={onClose} /> return <WorkspaceSettingsForm onClose={onClose} />
case 'members': case 'members':

View File

@@ -7,12 +7,12 @@ import { createPlugins } from '@udecode/plate-core'
import { createLinkPlugin, ELEMENT_LINK } from '@udecode/plate-link' import { createLinkPlugin, ELEMENT_LINK } from '@udecode/plate-link'
import { PlateFloatingLink } from '@udecode/plate-ui-link' import { PlateFloatingLink } from '@udecode/plate-ui-link'
export const editorStyle: React.CSSProperties = { export const editorStyle = (backgroundColor: string): React.CSSProperties => ({
flex: 1, flex: 1,
padding: '1rem', padding: '1rem',
backgroundColor: 'white', backgroundColor,
borderRadius: '0.25rem', borderRadius: '0.25rem',
} })
export const platePlugins = createPlugins( export const platePlugins = createPlugins(
[ [

View File

@@ -1,22 +1,44 @@
import { extendTheme } from '@chakra-ui/react' import {
createMultiStyleConfigHelpers,
defineStyleConfig,
extendTheme,
StyleFunctionProps,
type ThemeConfig,
} from '@chakra-ui/react'
import { mode } from '@chakra-ui/theme-tools'
import {
alertAnatomy,
accordionAnatomy,
menuAnatomy,
modalAnatomy,
popoverAnatomy,
switchAnatomy,
} from '@chakra-ui/anatomy'
const config: ThemeConfig = {
initialColorMode: 'system',
useSystemColorMode: true,
}
const fonts = { const fonts = {
heading: 'Outfit', heading:
body: 'Open Sans', "Outfit, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",
body: "Open Sans, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'",
} }
export const colors = { export const colors = {
gray: { gray: {
50: '#F9FAFB', 50: '#fafafa',
100: '#F3F4F6', 100: '#f4f4f5',
200: '#E5E7EB', 200: '#e4e4e7',
300: '#D1D5DB', 300: '#d4d4d8',
400: '#9CA3AF', 400: '#a1a1aa',
500: '#6B7280', 500: '#71717a',
600: '#4B5563', 600: '#52525b',
700: '#374151', 700: '#3f3f46',
800: '#1F2937', 800: '#27272a',
900: '#111827', 850: '#1f1f23',
900: '#18181b',
}, },
blue: { blue: {
50: '#e0edff', 50: '#e0edff',
@@ -56,33 +78,137 @@ export const colors = {
}, },
} }
const Modal = createMultiStyleConfigHelpers(
modalAnatomy.keys
).defineMultiStyleConfig({
baseStyle: ({ colorMode }) => ({
dialog: { bg: colorMode === 'dark' ? 'gray.800' : 'white' },
}),
})
const Popover = createMultiStyleConfigHelpers(
popoverAnatomy.keys
).defineMultiStyleConfig({
baseStyle: ({ colorMode }) => ({
popper: {
width: 'fit-content',
maxWidth: 'fit-content',
},
content: {
bg: colorMode === 'dark' ? 'gray.800' : 'white',
},
}),
})
const Menu = createMultiStyleConfigHelpers(
menuAnatomy.keys
).defineMultiStyleConfig({
baseStyle: ({ colorMode }) => ({
list: {
shadow: 'lg',
bg: colorMode === 'dark' ? 'gray.800' : 'white',
},
item: {
bg: colorMode === 'dark' ? 'gray.800' : 'white',
_hover: {
bg: colorMode === 'dark' ? 'gray.700' : 'gray.100',
},
},
}),
})
const Accordion = createMultiStyleConfigHelpers(
accordionAnatomy.keys
).defineMultiStyleConfig({
baseStyle: ({ colorMode }) => ({
button: {
_hover: {
bg: colorMode === 'dark' ? 'gray.800' : 'gray.100',
},
},
}),
})
const Button = defineStyleConfig({
baseStyle: ({ colorMode }) => ({
bg: colorMode === 'dark' ? 'gray.800' : 'white',
}),
variants: {
solid: ({ colorMode, colorScheme }) => {
if (colorScheme !== 'blue') return {}
return {
bg: colorMode === 'dark' ? 'blue.400' : 'blue.500',
color: 'white',
_hover: {
bg: colorMode === 'dark' ? 'blue.500' : 'blue.600',
},
_active: {
bg: colorMode === 'dark' ? 'blue.600' : 'blue.700',
},
}
},
outline: {
bg: 'transparent',
},
ghost: {
bg: 'transparent',
},
},
})
const Alert = createMultiStyleConfigHelpers(
alertAnatomy.keys
).defineMultiStyleConfig({
variants: {
subtle: ({ colorScheme, colorMode }) => {
if (colorScheme !== 'blue' || colorMode === 'dark') return {}
return {
container: {
bg: 'blue.50',
},
}
},
},
})
const Switch = createMultiStyleConfigHelpers(
switchAnatomy.keys
).defineMultiStyleConfig({
baseStyle: ({ colorMode, colorScheme }) => ({
track: {
_checked: {
bg: colorMode === 'dark' ? `${colorScheme}.400` : `${colorScheme}.500`,
},
},
}),
})
const components = { const components = {
Modal,
Popover,
Menu,
Button,
Accordion,
Alert,
Switch,
Spinner: { Spinner: {
defaultProps: { defaultProps: {
colorScheme: 'blue', colorScheme: 'blue',
}, },
}, },
NumberInput: { NumberInput: {
defaultProps: { baseStyle: {
focusBorderColor: 'blue.200', focusBorderColor: 'blue.200',
}, },
}, },
Input: { Input: {
defaultProps: { baseStyle: {
focusBorderColor: 'blue.200', focusBorderColor: 'blue.200',
}, },
}, },
Textarea: { Textarea: {
defaultProps: {
focusBorderColor: 'blue.200',
},
},
Popover: {
baseStyle: { baseStyle: {
popper: { focusBorderColor: 'blue.200',
width: 'fit-content',
maxWidth: 'fit-content',
},
}, },
}, },
Link: { Link: {
@@ -90,20 +216,25 @@ const components = {
_hover: { textDecoration: 'none' }, _hover: { textDecoration: 'none' },
}, },
}, },
Menu: {
parts: ['list'],
defaultProps: {
list: {
shadow: 'lg',
},
},
},
Tooltip: { Tooltip: {
defaultProps: { baseStyle: {
rounded: 'md', rounded: 'md',
hasArrow: true,
}, },
}, },
} }
export const customTheme: any = extendTheme({ colors, fonts, components }) const styles = {
global: (props: StyleFunctionProps) => ({
body: {
bg: mode('white', 'gray.900')(props),
},
}),
}
export const customTheme = extendTheme({
colors,
fonts,
components,
config,
styles,
})

View File

@@ -1,4 +1,5 @@
/* eslint-disable @next/next/no-sync-scripts */ import { customTheme } from '@/lib/theme'
import { ColorModeScript } from '@chakra-ui/react'
import { Html, Head, Main, NextScript } from 'next/document' import { Html, Head, Main, NextScript } from 'next/document'
const Document = () => ( const Document = () => (
@@ -10,9 +11,11 @@ const Document = () => (
rel="stylesheet" rel="stylesheet"
/> />
<meta name="google" content="notranslate" /> <meta name="google" content="notranslate" />
{/* eslint-disable-next-line @next/next/no-sync-scripts */}
<script src="/__env.js" /> <script src="/__env.js" />
</Head> </Head>
<body> <body>
<ColorModeScript initialColorMode={customTheme.config.initialColorMode} />
<Main /> <Main />
<NextScript /> <NextScript />
</body> </body>

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "preferredAppAppearance" TEXT;

View File

@@ -48,6 +48,7 @@ model User {
company String? company String?
onboardingCategories String[] onboardingCategories String[]
graphNavigation GraphNavigation? graphNavigation GraphNavigation?
preferredAppAppearance String?
accounts Account[] accounts Account[]
apiTokens ApiToken[] apiTokens ApiToken[]
CollaboratorsOnTypebots CollaboratorsOnTypebots[] CollaboratorsOnTypebots CollaboratorsOnTypebots[]

85
pnpm-lock.yaml generated
View File

@@ -17,9 +17,11 @@ importers:
apps/builder: apps/builder:
specifiers: specifiers:
'@babel/core': 7.20.5 '@babel/core': 7.20.5
'@chakra-ui/anatomy': ^2.1.0
'@chakra-ui/css-reset': 2.0.10 '@chakra-ui/css-reset': 2.0.10
'@chakra-ui/react': 2.4.4 '@chakra-ui/react': 2.4.4
'@chakra-ui/styled-system': 2.5.0 '@chakra-ui/styled-system': 2.5.0
'@chakra-ui/theme-tools': ^2.0.14
'@codemirror/lang-css': 6.0.1 '@codemirror/lang-css': 6.0.1
'@codemirror/lang-html': 6.4.0 '@codemirror/lang-html': 6.4.0
'@codemirror/lang-javascript': 6.1.2 '@codemirror/lang-javascript': 6.1.2
@@ -71,6 +73,7 @@ importers:
bot-engine: workspace:* bot-engine: workspace:*
browser-image-compression: 2.0.0 browser-image-compression: 2.0.0
canvas-confetti: 1.6.0 canvas-confetti: 1.6.0
chakra-react-select: ^4.4.3
codemirror: 6.0.1 codemirror: 6.0.1
cuid: 2.1.8 cuid: 2.1.8
db: workspace:* db: workspace:*
@@ -114,6 +117,7 @@ importers:
superjson: ^1.12.0 superjson: ^1.12.0
svg-round-corners: 0.4.1 svg-round-corners: 0.4.1
swr: 2.0.0 swr: 2.0.0
thememirror: ^2.0.1
tinycolor2: 1.4.2 tinycolor2: 1.4.2
trpc-openapi: 1.0.0 trpc-openapi: 1.0.0
tsconfig: workspace:* tsconfig: workspace:*
@@ -123,8 +127,10 @@ importers:
utils: workspace:* utils: workspace:*
zod: 3.20.2 zod: 3.20.2
dependencies: dependencies:
'@chakra-ui/anatomy': 2.1.0
'@chakra-ui/css-reset': 2.0.10_hp5f5nkljdiwilp4rgxyefcplu '@chakra-ui/css-reset': 2.0.10_hp5f5nkljdiwilp4rgxyefcplu
'@chakra-ui/react': 2.4.4_przcunyodmsiq2duiyjrphchze '@chakra-ui/react': 2.4.4_przcunyodmsiq2duiyjrphchze
'@chakra-ui/theme-tools': 2.0.14_egcizgpwtsioofm7hx34ab4iqi
'@codemirror/lang-css': 6.0.1_oaehsghbizwy4uu4eqfxbgnija '@codemirror/lang-css': 6.0.1_oaehsghbizwy4uu4eqfxbgnija
'@codemirror/lang-html': 6.4.0 '@codemirror/lang-html': 6.4.0
'@codemirror/lang-javascript': 6.1.2 '@codemirror/lang-javascript': 6.1.2
@@ -162,6 +168,7 @@ importers:
bot-engine: link:../../packages/bot-engine bot-engine: link:../../packages/bot-engine
browser-image-compression: 2.0.0 browser-image-compression: 2.0.0
canvas-confetti: 1.6.0 canvas-confetti: 1.6.0
chakra-react-select: 4.4.3_rimn5i5k53tiepfjihjwcsjnou
codemirror: 6.0.1_@lezer+common@1.0.2 codemirror: 6.0.1_@lezer+common@1.0.2
cuid: 2.1.8 cuid: 2.1.8
deep-object-diff: 1.1.9 deep-object-diff: 1.1.9
@@ -198,6 +205,7 @@ importers:
styled-components: 5.3.6_7i5myeigehqah43i5u7wbekgba styled-components: 5.3.6_7i5myeigehqah43i5u7wbekgba
svg-round-corners: 0.4.1 svg-round-corners: 0.4.1
swr: 2.0.0_react@18.2.0 swr: 2.0.0_react@18.2.0
thememirror: 2.0.1_jxyczrmcrnl7x5ydpuf7hgjfya
tinycolor2: 1.4.2 tinycolor2: 1.4.2
trpc-openapi: 1.0.0_nuoe2uo2ngdjbmcrwljfpdxrve trpc-openapi: 1.0.0_nuoe2uo2ngdjbmcrwljfpdxrve
typebot-js: link:../../packages/typebot-js typebot-js: link:../../packages/typebot-js
@@ -4387,12 +4395,22 @@ packages:
resolution: {integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==} resolution: {integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==}
dev: false dev: false
/@floating-ui/core/1.0.4:
resolution: {integrity: sha512-FPFLbg2b06MIw1dqk2SOEMAMX3xlrreGjcui5OTxfBDtaKTmh0kioOVjT8gcfl58juawL/yF+S+gnq8aUYQx/Q==}
dev: false
/@floating-ui/dom/0.5.4: /@floating-ui/dom/0.5.4:
resolution: {integrity: sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==} resolution: {integrity: sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==}
dependencies: dependencies:
'@floating-ui/core': 0.7.3 '@floating-ui/core': 0.7.3
dev: false dev: false
/@floating-ui/dom/1.0.12:
resolution: {integrity: sha512-HeG/wHoa2laUHlDX3xkzqlUqliAfa+zqV04LaKIwNCmCNaW2p0fQi4/Kd0LB4GdFoJ2UllLFq5gWnXAd67lg7w==}
dependencies:
'@floating-ui/core': 1.0.4
dev: false
/@floating-ui/react-dom-interactions/0.6.6_ib3m5ricvtkl2cll7qpr2f6lvq: /@floating-ui/react-dom-interactions/0.6.6_ib3m5ricvtkl2cll7qpr2f6lvq:
resolution: {integrity: sha512-qnao6UPjSZNHnXrF+u4/n92qVroQkx0Umlhy3Avk1oIebm/5ee6yvDm4xbHob0OjY7ya8WmUnV3rQlPwX3Atwg==} resolution: {integrity: sha512-qnao6UPjSZNHnXrF+u4/n92qVroQkx0Umlhy3Avk1oIebm/5ee6yvDm4xbHob0OjY7ya8WmUnV3rQlPwX3Atwg==}
peerDependencies: peerDependencies:
@@ -6149,7 +6167,6 @@ packages:
resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==} resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==}
dependencies: dependencies:
'@types/react': 18.0.26 '@types/react': 18.0.26
dev: true
/@types/react/18.0.26: /@types/react/18.0.26:
resolution: {integrity: sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==} resolution: {integrity: sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==}
@@ -7897,6 +7914,34 @@ packages:
resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==} resolution: {integrity: sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg==}
dev: false dev: false
/chakra-react-select/4.4.3_rimn5i5k53tiepfjihjwcsjnou:
resolution: {integrity: sha512-anDgJyYUpIapTmUbgXB+Iw5hJ90hOPvgoUPUaYdO5q9zY2VBFhQ1L0gBMqWAQxiKUmuHpwQypf8sPoVtd0b3KA==}
peerDependencies:
'@chakra-ui/form-control': ^2.0.0
'@chakra-ui/icon': ^3.0.0
'@chakra-ui/layout': ^2.0.0
'@chakra-ui/menu': ^2.0.0
'@chakra-ui/spinner': ^2.0.0
'@chakra-ui/system': ^2.0.0
'@emotion/react': ^11.8.1
react: ^18.0.0
react-dom: ^18.0.0
dependencies:
'@chakra-ui/form-control': 2.0.13_iuckyjetixsa6e5uo5ymtjguvq
'@chakra-ui/icon': 3.0.13_iuckyjetixsa6e5uo5ymtjguvq
'@chakra-ui/layout': 2.1.11_iuckyjetixsa6e5uo5ymtjguvq
'@chakra-ui/menu': 2.1.5_v3e3sxd2uk3a5ht2jlbmshfate
'@chakra-ui/spinner': 2.0.11_iuckyjetixsa6e5uo5ymtjguvq
'@chakra-ui/system': 2.3.5_dovxhg2tvkkxkdnqyoum6wzcxm
'@emotion/react': 11.10.5_xl5my4wapvq2ctl7qwehtbgorq
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
react-select: 5.7.0_bkycudvrb3j3gvocoupj7qjewi
transitivePeerDependencies:
- '@babel/core'
- '@types/react'
dev: false
/chalk/2.4.2: /chalk/2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -13128,6 +13173,10 @@ packages:
fs-monkey: 1.0.3 fs-monkey: 1.0.3
dev: false dev: false
/memoize-one/6.0.0:
resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
dev: false
/mensch/0.3.4: /mensch/0.3.4:
resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==}
@@ -15692,6 +15741,28 @@ packages:
react-dom: 18.2.0_react@18.2.0 react-dom: 18.2.0_react@18.2.0
dev: false dev: false
/react-select/5.7.0_bkycudvrb3j3gvocoupj7qjewi:
resolution: {integrity: sha512-lJGiMxCa3cqnUr2Jjtg9YHsaytiZqeNOKeibv6WF5zbK/fPegZ1hg3y/9P1RZVLhqBTs0PfqQLKuAACednYGhQ==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
'@babel/runtime': 7.20.6
'@emotion/cache': 11.10.5
'@emotion/react': 11.10.5_xl5my4wapvq2ctl7qwehtbgorq
'@floating-ui/dom': 1.0.12
'@types/react-transition-group': 4.4.5
memoize-one: 6.0.0
prop-types: 15.8.1
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
react-transition-group: 4.4.5_biqbaboplfbrettd7655fr4n2y
use-isomorphic-layout-effect: 1.1.2_kzbn2opkn2327fwg5yzwzya5o4
transitivePeerDependencies:
- '@babel/core'
- '@types/react'
dev: false
/react-ssr-prepass/1.5.0_react@18.2.0: /react-ssr-prepass/1.5.0_react@18.2.0:
resolution: {integrity: sha512-yFNHrlVEReVYKsLI5lF05tZoHveA5pGzjFbFJY/3pOqqjGOmMmqx83N4hIjN2n6E1AOa+eQEUxs3CgRnPmT0RQ==} resolution: {integrity: sha512-yFNHrlVEReVYKsLI5lF05tZoHveA5pGzjFbFJY/3pOqqjGOmMmqx83N4hIjN2n6E1AOa+eQEUxs3CgRnPmT0RQ==}
peerDependencies: peerDependencies:
@@ -17287,6 +17358,18 @@ packages:
/text-table/0.2.0: /text-table/0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
/thememirror/2.0.1_jxyczrmcrnl7x5ydpuf7hgjfya:
resolution: {integrity: sha512-d5i6FVvWWPkwrm4cHLI3t9AT1OrkAt7Ig8dtdYSofgF7C/eiyNuq6zQzSTusWTde3jpW9WLvA9J/fzNKMUsd0w==}
peerDependencies:
'@codemirror/language': ^6.0.0
'@codemirror/state': ^6.0.0
'@codemirror/view': ^6.0.0
dependencies:
'@codemirror/language': 6.3.2
'@codemirror/state': 6.1.4
'@codemirror/view': 6.7.1
dev: false
/thenify-all/1.6.0: /thenify-all/1.6.0:
resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
engines: {node: '>=0.8'} engines: {node: '>=0.8'}