diff --git a/.gitignore b/.gitignore
index 0317e5179..212545374 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,5 @@ workspace.code-workspace
.turbo
apps/builder/tsconfig.tsbuildinfo
+
+yarn-error.log
diff --git a/apps/builder/assets/icons.tsx b/apps/builder/assets/icons.tsx
index 8adbf9577..9fd591967 100644
--- a/apps/builder/assets/icons.tsx
+++ b/apps/builder/assets/icons.tsx
@@ -346,3 +346,27 @@ export const SendEmailIcon = (props: IconProps) => (
)
+
+export const FacebookIcon = (props: IconProps) => (
+
+ Logo Facebook
+
+
+)
+
+export const GoogleIcon = (props: IconProps) => (
+
+ {'Logo Google'}
+
+
+)
+
+export const GithubIcon = (props: IconProps) => (
+
+ {'Logo Github'}
+
+
+)
diff --git a/apps/builder/assets/logos/GiphyLogo.tsx b/apps/builder/assets/logos/GiphyLogo.tsx
new file mode 100644
index 000000000..8e41c57f4
--- /dev/null
+++ b/apps/builder/assets/logos/GiphyLogo.tsx
@@ -0,0 +1,23 @@
+import { IconProps, Icon } from '@chakra-ui/react'
+
+export const GiphyLogo = (props: IconProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)
diff --git a/apps/builder/assets/logos/GoogleAnalyticsLogo.tsx b/apps/builder/assets/logos/GoogleAnalyticsLogo.tsx
new file mode 100644
index 000000000..74444291a
--- /dev/null
+++ b/apps/builder/assets/logos/GoogleAnalyticsLogo.tsx
@@ -0,0 +1,101 @@
+import { IconProps, Icon } from '@chakra-ui/react'
+
+export const GoogleAnalyticsLogo = (props: IconProps) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+)
diff --git a/apps/builder/assets/logos.tsx b/apps/builder/assets/logos/GoogleSheetsLogo.tsx
similarity index 51%
rename from apps/builder/assets/logos.tsx
rename to apps/builder/assets/logos/GoogleSheetsLogo.tsx
index bdc7e10b9..65eda1a21 100644
--- a/apps/builder/assets/logos.tsx
+++ b/apps/builder/assets/logos/GoogleSheetsLogo.tsx
@@ -1,73 +1,5 @@
import { IconProps, Icon } from '@chakra-ui/react'
-export const TypebotLogo = ({
- isDark,
- ...props
-}: { isDark?: boolean } & IconProps) => (
-
-
-
-
-
-
-
-)
-
-export const GithubLogo = (props: IconProps) => (
-
- {'Logo Github'}
-
-
-)
-
-export const GoogleLogo = (props: IconProps) => (
-
- {'Logo Google'}
-
-
-)
-
-export const FacebookLogo = (props: IconProps) => (
-
- Logo Facebook
-
-
-)
-
export const GoogleSheetsLogo = (props: IconProps) => (
Sheets-icon
@@ -244,125 +176,3 @@ export const GoogleSheetsLogo = (props: IconProps) => (
)
-
-export const GoogleAnalyticsLogo = (props: IconProps) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-)
-
-export const GiphyLogo = (props: IconProps) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-)
diff --git a/apps/builder/assets/logos/GtmLogo.tsx b/apps/builder/assets/logos/GtmLogo.tsx
new file mode 100644
index 000000000..d09d10734
--- /dev/null
+++ b/apps/builder/assets/logos/GtmLogo.tsx
@@ -0,0 +1,29 @@
+import { Icon, IconProps } from '@chakra-ui/react'
+
+export const GtmLogo = (props: IconProps) => (
+
+
+
+
+
+
+)
diff --git a/apps/builder/assets/logos/IframeLogo.tsx b/apps/builder/assets/logos/IframeLogo.tsx
new file mode 100644
index 000000000..8076e4e09
--- /dev/null
+++ b/apps/builder/assets/logos/IframeLogo.tsx
@@ -0,0 +1,50 @@
+import { Icon, IconProps } from '@chakra-ui/react'
+
+export const IframeLogo = (props: IconProps) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/builder/assets/logos/JavascriptLogo.tsx b/apps/builder/assets/logos/JavascriptLogo.tsx
new file mode 100644
index 000000000..1d912d8f7
--- /dev/null
+++ b/apps/builder/assets/logos/JavascriptLogo.tsx
@@ -0,0 +1,16 @@
+import { Icon, IconProps } from '@chakra-ui/react'
+
+export const JavascriptLogo = (props: IconProps) => (
+
+
+
+
+)
diff --git a/apps/builder/assets/logos/NotionLogo.tsx b/apps/builder/assets/logos/NotionLogo.tsx
new file mode 100644
index 000000000..ee4f313e9
--- /dev/null
+++ b/apps/builder/assets/logos/NotionLogo.tsx
@@ -0,0 +1,19 @@
+import { Icon, IconProps } from '@chakra-ui/react'
+
+export const NotionLogo = (props: IconProps) => (
+
+
+
+)
diff --git a/apps/builder/assets/logos/OtherLogo.tsx b/apps/builder/assets/logos/OtherLogo.tsx
new file mode 100644
index 000000000..3ce5d1cc1
--- /dev/null
+++ b/apps/builder/assets/logos/OtherLogo.tsx
@@ -0,0 +1,31 @@
+import { Icon, IconProps } from '@chakra-ui/react'
+
+export const OtherLogo = (props: IconProps) => (
+
+
+
+
+
+
+)
diff --git a/apps/builder/assets/logos/ReactLogo.tsx b/apps/builder/assets/logos/ReactLogo.tsx
new file mode 100644
index 000000000..532ac3057
--- /dev/null
+++ b/apps/builder/assets/logos/ReactLogo.tsx
@@ -0,0 +1,21 @@
+import { Icon, IconProps } from '@chakra-ui/react'
+
+export const ReactLogo = (props: IconProps) => (
+
+
+
+
+)
diff --git a/apps/builder/assets/logos/ShopifyLogo.tsx b/apps/builder/assets/logos/ShopifyLogo.tsx
new file mode 100644
index 000000000..1cf76b3d1
--- /dev/null
+++ b/apps/builder/assets/logos/ShopifyLogo.tsx
@@ -0,0 +1,25 @@
+import { Icon, IconProps } from '@chakra-ui/react'
+
+export const ShopifyLogo = (props: IconProps) => (
+
+
+
+
+
+)
diff --git a/apps/builder/assets/logos/TypebotLogo.tsx b/apps/builder/assets/logos/TypebotLogo.tsx
new file mode 100644
index 000000000..61698cb15
--- /dev/null
+++ b/apps/builder/assets/logos/TypebotLogo.tsx
@@ -0,0 +1,45 @@
+import { IconProps, Icon } from '@chakra-ui/react'
+
+export const TypebotLogo = ({
+ isDark,
+ ...props
+}: { isDark?: boolean } & IconProps) => (
+
+
+
+
+
+
+
+)
diff --git a/apps/builder/assets/logos/WebflowLogo.tsx b/apps/builder/assets/logos/WebflowLogo.tsx
new file mode 100644
index 000000000..b7da51d96
--- /dev/null
+++ b/apps/builder/assets/logos/WebflowLogo.tsx
@@ -0,0 +1,21 @@
+import { Icon, IconProps } from '@chakra-ui/react'
+
+export const WebflowLogo = (props: IconProps) => (
+
+
+
+
+)
diff --git a/apps/builder/assets/logos/WixLogo.tsx b/apps/builder/assets/logos/WixLogo.tsx
new file mode 100644
index 000000000..67bcfb1ac
--- /dev/null
+++ b/apps/builder/assets/logos/WixLogo.tsx
@@ -0,0 +1,29 @@
+import { Icon, IconProps } from '@chakra-ui/react'
+
+export const WixLogo = (props: IconProps) => (
+
+
+
+
+
+
+)
diff --git a/apps/builder/assets/logos/WordpressLogo.tsx b/apps/builder/assets/logos/WordpressLogo.tsx
new file mode 100644
index 000000000..d70846dc8
--- /dev/null
+++ b/apps/builder/assets/logos/WordpressLogo.tsx
@@ -0,0 +1,33 @@
+import { Icon, IconProps } from '@chakra-ui/react'
+
+export const WordpressLogo = (props: IconProps) => (
+
+
+
+
+
+
+
+)
diff --git a/apps/builder/assets/logos/index.tsx b/apps/builder/assets/logos/index.tsx
new file mode 100644
index 000000000..14a8d7567
--- /dev/null
+++ b/apps/builder/assets/logos/index.tsx
@@ -0,0 +1,14 @@
+export * from './GiphyLogo'
+export * from './GoogleAnalyticsLogo'
+export * from './GoogleSheetsLogo'
+export * from './GtmLogo'
+export * from './IframeLogo'
+export * from './JavascriptLogo'
+export * from './NotionLogo'
+export * from './OtherLogo'
+export * from './ReactLogo'
+export * from './ShopifyLogo'
+export * from './TypebotLogo'
+export * from './WebflowLogo'
+export * from './WordpressLogo'
+export * from './WixLogo'
diff --git a/apps/builder/components/auth/SocialLoginButtons.tsx b/apps/builder/components/auth/SocialLoginButtons.tsx
index 387303ca4..760619d57 100644
--- a/apps/builder/components/auth/SocialLoginButtons.tsx
+++ b/apps/builder/components/auth/SocialLoginButtons.tsx
@@ -1,5 +1,5 @@
-import { FacebookLogo, GithubLogo, GoogleLogo } from 'assets/logos'
import { Stack, Button } from '@chakra-ui/react'
+import { FacebookIcon, GithubIcon, GoogleIcon } from 'assets/icons'
import { signIn, useSession } from 'next-auth/react'
import React from 'react'
@@ -15,7 +15,7 @@ export const SocialLoginButtons = () => {
return (
}
+ leftIcon={}
onClick={handleGitHubClick}
data-testid="github"
isLoading={['loading', 'authenticated'].includes(status)}
@@ -23,7 +23,7 @@ export const SocialLoginButtons = () => {
Continue with GitHub
}
+ leftIcon={}
onClick={handleGoogleClick}
data-testid="google"
isLoading={['loading', 'authenticated'].includes(status)}
@@ -31,7 +31,7 @@ export const SocialLoginButtons = () => {
Continue with Google
}
+ leftIcon={}
onClick={handleFacebookClick}
data-testid="facebook"
isLoading={['loading', 'authenticated'].includes(status)}
diff --git a/apps/builder/components/share/ShareContent.tsx b/apps/builder/components/share/ShareContent.tsx
index 365052168..bb51d5a73 100644
--- a/apps/builder/components/share/ShareContent.tsx
+++ b/apps/builder/components/share/ShareContent.tsx
@@ -1,8 +1,10 @@
-import { Flex, Heading, Stack } from '@chakra-ui/react'
+import { Flex, Heading, Stack, Wrap } from '@chakra-ui/react'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
import React from 'react'
import { parseDefaultPublicId } from 'services/typebots'
+import { isDefined } from 'utils'
import { EditableUrl } from './EditableUrl'
+import { integrationsList } from './integrations/EmbedButton'
export const ShareContent = () => {
const { typebot, updateTypebot } = useTypebot()
@@ -11,24 +13,41 @@ export const ShareContent = () => {
if (publicId === typebot?.publicId) return
updateTypebot({ publicId })
}
+
+ const publicId = typebot
+ ? typebot?.publicId ?? parseDefaultPublicId(typebot.name, typebot.id)
+ : ''
+ const isPublished = isDefined(typebot?.publishedTypebotId)
+
return (
-
-
- Your typebot link
-
- {typebot && (
-
- )}
-
- Embed your typebot
-
+
+
+
+ Your typebot link
+
+ {typebot && (
+
+ )}
+
+
+
+
+ Embed your typebot
+
+
+ {integrationsList.map((IntegrationButton, idx) => (
+
+ ))}
+
+
)
diff --git a/apps/builder/components/share/integrations/EmbedButton.tsx b/apps/builder/components/share/integrations/EmbedButton.tsx
new file mode 100644
index 000000000..866bc61d4
--- /dev/null
+++ b/apps/builder/components/share/integrations/EmbedButton.tsx
@@ -0,0 +1,145 @@
+import { Button, useDisclosure, VStack, WrapItem, Text } from '@chakra-ui/react'
+import {
+ WordpressLogo,
+ ShopifyLogo,
+ WixLogo,
+ GtmLogo,
+ JavascriptLogo,
+ ReactLogo,
+ NotionLogo,
+ WebflowLogo,
+ IframeLogo,
+ OtherLogo,
+} from 'assets/logos'
+import React from 'react'
+import {
+ WordpressModal,
+ ShopifyModal,
+ WebflowModal,
+ GtmModal,
+ JavascriptModal,
+ ReactModal,
+ NotionModal,
+ IframeModal,
+} from './modals'
+
+export type ModalProps = {
+ publicId: string
+ isPublished: boolean
+ isOpen: boolean
+ onClose: () => void
+}
+
+type EmbedButtonProps = Pick & {
+ logo: JSX.Element
+ label: string
+ Modal: (props: ModalProps) => JSX.Element
+}
+
+export const EmbedButton = ({
+ logo,
+ label,
+ Modal,
+ ...modalProps
+}: EmbedButtonProps) => {
+ const { isOpen, onOpen, onClose } = useDisclosure()
+ return (
+
+
+ {logo}
+ {label}
+
+
+
+ )
+}
+
+export const integrationsList = [
+ (props: Pick) => (
+ }
+ label="Wordpress"
+ Modal={WordpressModal}
+ {...props}
+ />
+ ),
+ (props: Pick) => (
+ }
+ label="Shopify"
+ Modal={ShopifyModal}
+ {...props}
+ />
+ ),
+ (props: Pick) => (
+ }
+ label="Wix"
+ Modal={WebflowModal}
+ {...props}
+ />
+ ),
+ (props: Pick) => (
+ }
+ label="Google Tag Manager"
+ Modal={GtmModal}
+ {...props}
+ />
+ ),
+ (props: Pick) => (
+ }
+ label="HTML & Javascript"
+ Modal={JavascriptModal}
+ {...props}
+ />
+ ),
+ (props: Pick) => (
+ }
+ label="React"
+ Modal={ReactModal}
+ {...props}
+ />
+ ),
+ (props: Pick) => (
+ }
+ label="Notion"
+ Modal={NotionModal}
+ {...props}
+ />
+ ),
+ (props: Pick) => (
+ }
+ label="Webflow"
+ Modal={WebflowModal}
+ {...props}
+ />
+ ),
+ (props: Pick) => (
+ }
+ label="Iframe"
+ Modal={IframeModal}
+ {...props}
+ />
+ ),
+ (props: Pick) => (
+ }
+ label="Other"
+ Modal={JavascriptModal}
+ {...props}
+ />
+ ),
+]
diff --git a/apps/builder/components/share/integrations/modals/ChooseEmbedTypeList.tsx b/apps/builder/components/share/integrations/modals/ChooseEmbedTypeList.tsx
new file mode 100644
index 000000000..63bb8400f
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/ChooseEmbedTypeList.tsx
@@ -0,0 +1,155 @@
+import { HStack, Button, Text, Stack } from '@chakra-ui/react'
+
+type ChooseEmbedTypeListProps = {
+ onSelectEmbedType: (type: 'standard' | 'popup' | 'bubble') => void
+ disabledTypes?: ('standard' | 'popup' | 'bubble')[]
+}
+
+export const ChooseEmbedTypeList = ({
+ onSelectEmbedType,
+ disabledTypes = [],
+}: ChooseEmbedTypeListProps) => {
+ return (
+
+ onSelectEmbedType('standard')}
+ whiteSpace={'normal'}
+ spacing="6"
+ isDisabled={disabledTypes.includes('standard')}
+ >
+
+
+
+ Standard
+
+ Embed in a container on your site
+
+
+ onSelectEmbedType('popup')}
+ whiteSpace={'normal'}
+ spacing="6"
+ isDisabled={disabledTypes.includes('popup')}
+ >
+
+
+
+ Popup
+
+
+ Embed in a popup window on top of your website
+
+
+
+ onSelectEmbedType('bubble')}
+ whiteSpace={'normal'}
+ spacing="6"
+ isDisabled={disabledTypes.includes('bubble')}
+ >
+
+
+
+ Bubble
+
+
+ Embed in a chat bubble on the corner of your site
+
+
+
+
+ )
+}
+
+const StandardEmbedSvg = () => (
+
+)
+
+const PopupEmbedSvg = () => (
+
+)
+
+const BubbleEmbedSvg = () => (
+
+)
diff --git a/apps/builder/components/share/integrations/modals/GtmModal/GtmInstructions.tsx b/apps/builder/components/share/integrations/modals/GtmModal/GtmInstructions.tsx
new file mode 100644
index 000000000..a76252fe4
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/GtmModal/GtmInstructions.tsx
@@ -0,0 +1,131 @@
+import { OrderedList, ListItem, Tag } from '@chakra-ui/react'
+import { useState } from 'react'
+
+type GtmInstructionsProps = {
+ type: 'standard' | 'popup' | 'bubble'
+}
+
+export const GtmInstructions = ({ type }: GtmInstructionsProps) => {
+ switch (type) {
+ case 'standard': {
+ return
+ }
+ case 'popup': {
+ return
+ }
+ case 'bubble': {
+ return
+ }
+ }
+}
+
+const StandardInstructions = () => {
+ // const [windowSizes, setWindowSizes] = useState({
+ // height: '100%',
+ // width: '100%',
+ // })
+
+ // const jsCode = parseInitContainerCode({
+ // publishId: chatbot?.publishId ?? '',
+ // backgroundColor: chatbot?.themeColors.chatbotBackground.value,
+ // customDomain: chatbot?.customDomains[0],
+ // })
+ // const headCode = `${typebotJsHtml}
+ // `
+
+ // const elementCode = ``
+ return (
+
+
+ On your GTM account dashboard, click on Add a new tag
+
+
+ Choose Custom HTML tag type
+
+
+ Paste the code below:
+ {/* sendGtmCopyEvent('standard')}
+ /> */}
+
+
+ On your webpage, you need to have an element on which the typebot will
+ go. It needs to have the id typebot-container:
+ {/*
+ setWindowSizes({
+ height: sizes.heightLabel,
+ width: sizes.widthLabel,
+ })
+ }
+ />
+ */}
+
+
+ )
+}
+
+const PopupInstructions = () => {
+ // const [inputValue, setInputValue] = useState(0)
+
+ return (
+
+
+ On your GTM account dashboard, click on Add a new tag
+
+
+ Choose Custom HTML tag type
+
+
+ Paste the code below:
+ {/* setInputValue(settings.delay ?? 0)}
+ />
+ sendGtmCopyEvent('popup')}
+ /> */}
+
+
+ )
+}
+
+const BubbleInstructions = () => {
+ // const [inputValues, setInputValues] = useState<
+ // Pick
+ // >({
+ // proactiveMessage: undefined,
+ // button: {
+ // color: '',
+ // iconUrl: '',
+ // },
+ // })
+
+ return (
+
+
+ On your GTM account dashboard, click on Add a new tag
+
+
+ Choose Custom HTML tag type
+
+
+ Paste the code below:
+ {/* setInputValues({ ...settings })}
+ />
+ sendGtmCopyEvent('bubble')}
+ /> */}
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/GtmModal/index.tsx b/apps/builder/components/share/integrations/modals/GtmModal/index.tsx
new file mode 100644
index 000000000..0bfdc02d0
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/GtmModal/index.tsx
@@ -0,0 +1,63 @@
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalCloseButton,
+ ModalBody,
+ ModalFooter,
+ IconButton,
+ Heading,
+ HStack,
+} from '@chakra-ui/react'
+import { ChevronLeftIcon } from 'assets/icons'
+import React, { useState } from 'react'
+import { ModalProps } from '../../EmbedButton'
+import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList'
+import { capitalize } from 'utils'
+import { PublishFirstInfo } from 'components/shared/Info'
+import { GtmInstructions } from './GtmInstructions'
+
+export const GtmModal = ({ isOpen, onClose, isPublished }: ModalProps) => {
+ const [chosenEmbedType, setChosenEmbedType] = useState<
+ 'standard' | 'popup' | 'bubble' | undefined
+ >()
+ return (
+
+
+
+
+
+ {chosenEmbedType && (
+ }
+ aria-label="back"
+ variant="ghost"
+ colorScheme="gray"
+ mr={2}
+ onClick={() => setChosenEmbedType(undefined)}
+ />
+ )}
+
+ Javascript {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`}
+
+
+
+
+
+ {!isPublished && }
+ {!chosenEmbedType ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/IframeModal.tsx b/apps/builder/components/share/integrations/modals/IframeModal.tsx
new file mode 100644
index 000000000..1769a75a0
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/IframeModal.tsx
@@ -0,0 +1,41 @@
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalCloseButton,
+ ModalBody,
+ Stack,
+ ModalFooter,
+ Text,
+} from '@chakra-ui/react'
+import { PublishFirstInfo } from 'components/shared/Info'
+import { useState } from 'react'
+import { ModalProps } from '../EmbedButton'
+
+export const IframeModal = ({
+ isPublished,
+ publicId,
+ isOpen,
+ onClose,
+}: ModalProps) => {
+ const [inputValues, setInputValues] = useState({
+ heightLabel: '100%',
+ widthLabel: '100%',
+ })
+
+ return (
+
+
+
+ Iframe
+
+
+ {!isPublished && }
+ Paste this anywhere in your HTML code:
+
+
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/Javascript/JavascriptInstructions.tsx b/apps/builder/components/share/integrations/modals/Javascript/JavascriptInstructions.tsx
new file mode 100644
index 000000000..66112499c
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/Javascript/JavascriptInstructions.tsx
@@ -0,0 +1,95 @@
+import { Stack, Tag, Text } from '@chakra-ui/react'
+
+type JavascriptInstructionsProps = {
+ type: 'standard' | 'popup' | 'bubble'
+}
+
+export const JavascriptInstructions = ({
+ type,
+}: JavascriptInstructionsProps) => {
+ switch (type) {
+ case 'standard': {
+ return
+ }
+ case 'popup': {
+ return
+ }
+ case 'bubble': {
+ return
+ }
+ }
+}
+
+const StandardInstructions = () => {
+ // const [inputValues, setInputValues] = useState({
+ // heightLabel: '100%',
+ // widthLabel: '100%',
+ // })
+
+ return (
+
+
+ Paste this anywhere in the body
+
+ {/* setInputValues({ ...settings })}
+ />
+ sendJsCopyEvent('standard')}
+ /> */}
+
+ )
+}
+
+const PopupInstructions = () => {
+ // const [inputValue, setInputValue] = useState(0)
+
+ return (
+
+
+ Paste this anywhere in the body
+
+ {/* setInputValues({ ...settings })}
+ />
+ sendJsCopyEvent('standard')}
+ /> */}
+
+ )
+}
+
+const BubbleInstructions = () => {
+ // const [inputValues, setInputValues] = useState<
+ // Pick
+ // >({
+ // proactiveMessage: undefined,
+ // button: {
+ // color: '',
+ // iconUrl: '',
+ // },
+ // })
+
+ return (
+
+
+ Paste this anywhere in the body
+
+ {/* setInputValues({ ...settings })}
+ />
+ sendJsCopyEvent('standard')}
+ /> */}
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/Javascript/JavascriptModal.tsx b/apps/builder/components/share/integrations/modals/Javascript/JavascriptModal.tsx
new file mode 100644
index 000000000..5607f643a
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/Javascript/JavascriptModal.tsx
@@ -0,0 +1,67 @@
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalCloseButton,
+ ModalBody,
+ ModalFooter,
+ IconButton,
+ Heading,
+ HStack,
+} from '@chakra-ui/react'
+import { ChevronLeftIcon } from 'assets/icons'
+import React, { useState } from 'react'
+import { ModalProps } from '../../EmbedButton'
+import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList'
+import { capitalize } from 'utils'
+import { PublishFirstInfo } from 'components/shared/Info'
+import { JavascriptInstructions } from './JavascriptInstructions'
+
+export const JavascriptModal = ({
+ isOpen,
+ onClose,
+ isPublished,
+}: ModalProps) => {
+ const [chosenEmbedType, setChosenEmbedType] = useState<
+ 'standard' | 'popup' | 'bubble' | undefined
+ >()
+ return (
+
+
+
+
+
+ {chosenEmbedType && (
+ }
+ aria-label="back"
+ variant="ghost"
+ colorScheme="gray"
+ mr={2}
+ onClick={() => setChosenEmbedType(undefined)}
+ />
+ )}
+
+ Javascript {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`}
+
+
+
+
+
+ {!isPublished && }
+ {!chosenEmbedType ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/NotionModal.tsx b/apps/builder/components/share/integrations/modals/NotionModal.tsx
new file mode 100644
index 000000000..9cfe10f06
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/NotionModal.tsx
@@ -0,0 +1,62 @@
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ Heading,
+ ModalCloseButton,
+ ModalBody,
+ OrderedList,
+ ListItem,
+ Tag,
+ InputGroup,
+ Input,
+ InputRightElement,
+ ModalFooter,
+} from '@chakra-ui/react'
+import { CopyButton } from 'components/shared/buttons/CopyButton'
+import { PublishFirstInfo } from 'components/shared/Info'
+import { ModalProps } from '../EmbedButton'
+
+export const NotionModal = ({
+ isPublished,
+ publicId,
+ isOpen,
+ onClose,
+}: ModalProps): JSX.Element => {
+ return (
+
+
+
+
+ Notion
+
+
+
+ {!isPublished && }
+
+
+ Type /embed
+
+
+ Paste your typebot URL
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/React/ReactInstructions.tsx b/apps/builder/components/share/integrations/modals/React/ReactInstructions.tsx
new file mode 100644
index 000000000..20dd673aa
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/React/ReactInstructions.tsx
@@ -0,0 +1,86 @@
+import { Stack, Text } from '@chakra-ui/react'
+import { CodeEditor } from 'components/shared/CodeEditor'
+
+type Props = {
+ type: 'standard' | 'popup' | 'bubble'
+}
+
+export const ReactInstructions = ({ type }: Props) => {
+ switch (type) {
+ case 'standard': {
+ return
+ }
+ case 'popup': {
+ return
+ }
+ case 'bubble': {
+ return
+ }
+ }
+}
+
+const StandardInstructions = () => {
+ // const [inputValues, setInputValues] = useState({
+ // heightLabel: '100%',
+ // widthLabel: '100%',
+ // })
+
+ return (
+
+ {/* */}
+ {/* setInputValues({ ...settings })}
+ /> */}
+ {/* {t('insert-the-typebot-container')}
+ */}
+
+ )
+}
+
+const PopupInstructions = () => {
+ // const [inputValue, setInputValue] = useState(0)
+
+ return (
+
+ {/*
+ setInputValue(settings.delay ?? 0)}
+ />
+ {t('initialize-the-typebot')}
+ */}
+
+ )
+}
+
+const BubbleInstructions = () => {
+ // const { t } = useTranslation()
+ // const [inputValues, setInputValues] = useState<
+ // Pick
+ // >({
+ // proactiveMessage: undefined,
+ // button: {
+ // color: '',
+ // iconUrl: '',
+ // },
+ // })
+
+ return (
+
+ {/*
+ setInputValues({ ...settings })}
+ />
+ {t('initialize-the-typebot')}
+ */}
+
+ )
+}
+
+const InstallPackageInstruction = () => {
+ return (
+
+ Install the package:
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/React/ReactModal.tsx b/apps/builder/components/share/integrations/modals/React/ReactModal.tsx
new file mode 100644
index 000000000..315e8b5ff
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/React/ReactModal.tsx
@@ -0,0 +1,63 @@
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalCloseButton,
+ ModalBody,
+ ModalFooter,
+ IconButton,
+ Heading,
+ HStack,
+} from '@chakra-ui/react'
+import { ChevronLeftIcon } from 'assets/icons'
+import React, { useState } from 'react'
+import { ModalProps } from '../../EmbedButton'
+import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList'
+import { capitalize } from 'utils'
+import { PublishFirstInfo } from 'components/shared/Info'
+import { ReactInstructions } from './ReactInstructions'
+
+export const ReactModal = ({ isOpen, onClose, isPublished }: ModalProps) => {
+ const [chosenEmbedType, setChosenEmbedType] = useState<
+ 'standard' | 'popup' | 'bubble' | undefined
+ >()
+ return (
+
+
+
+
+
+ {chosenEmbedType && (
+ }
+ aria-label="back"
+ variant="ghost"
+ colorScheme="gray"
+ mr={2}
+ onClick={() => setChosenEmbedType(undefined)}
+ />
+ )}
+
+ React {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`}
+
+
+
+
+
+ {!isPublished && }
+ {!chosenEmbedType ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/ShopifyModal/ShopifyInstructions.tsx b/apps/builder/components/share/integrations/modals/ShopifyModal/ShopifyInstructions.tsx
new file mode 100644
index 000000000..a6dc3f24e
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/ShopifyModal/ShopifyInstructions.tsx
@@ -0,0 +1,130 @@
+import { OrderedList, ListItem, Tag } from '@chakra-ui/react'
+
+type ShopifyInstructionsProps = {
+ type: 'standard' | 'popup' | 'bubble'
+}
+
+export const ShopifyInstructions = ({ type }: ShopifyInstructionsProps) => {
+ switch (type) {
+ case 'standard': {
+ return
+ }
+ case 'popup': {
+ return
+ }
+ case 'bubble': {
+ return
+ }
+ }
+}
+
+const StandardInstructions = () => {
+ // const backgroundColor = chatbot?.themeColors.siteBackground.value
+ // const [windowSizes, setWindowSizes] = useState({
+ // height: '100%',
+ // width: '100%',
+ // })
+
+ // const jsCode = parseInitContainerCode({
+ // publishId: chatbot?.publishId ?? '',
+ // customDomain: chatbot?.customDomains[0],
+ // backgroundColor: chatbot?.themeColors.chatbotBackground.value,
+ // })
+ // const headCode = `${typebotJsHtml}
+ // `
+
+ // const elementCode = ``
+
+ return (
+
+
+ On your shop dashboard in the Themes page, click on{' '}
+ Actions {'>'} Edit code
+
+
+ In Layout {'>'} theme.liquid file, paste this code just
+ before the closing head tag:
+ {/* sendShopifyCopyEvent('standard')}
+ /> */}
+
+
+ Then, you can place an element on which the typebot will go in any file
+ in the body tags. It needs to have the id{' '}
+ typebot-container:
+ {/*
+ setWindowSizes({
+ height: sizes.heightLabel,
+ width: sizes.widthLabel,
+ })
+ }
+ />
+ */}
+
+
+ )
+}
+
+const PopupInstructions = () => {
+ // const [inputValue, setInputValue] = useState(0)
+
+ return (
+
+
+ On your shop dashboard in the Themes page, click on{' '}
+ Actions {'>'} Edit code
+
+
+ In Layout {'>'} theme.liquid file, paste this code just
+ before the closing head tag:
+ {/* setInputValue(settings.delay ?? 0)}
+ />
+ sendShopifyCopyEvent('popup')}
+ /> */}
+
+
+ )
+}
+
+const BubbleInstructions = () => {
+ // const [inputValues, setInputValues] = useState<
+ // Pick
+ // >({
+ // proactiveMessage: undefined,
+ // button: {
+ // color: '',
+ // iconUrl: '',
+ // },
+ // })
+
+ return (
+
+
+ On your shop dashboard in the Themes page, click on{' '}
+ Actions {'>'} Edit code
+
+
+ In Layout {'>'} theme.liquid file, paste this code just
+ before the closing head tag:
+ {/* setInputValues({ ...settings })}
+ />
+ sendShopifyCopyEvent('bubble')}
+ /> */}
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/ShopifyModal/index.tsx b/apps/builder/components/share/integrations/modals/ShopifyModal/index.tsx
new file mode 100644
index 000000000..dce574475
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/ShopifyModal/index.tsx
@@ -0,0 +1,63 @@
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalCloseButton,
+ ModalBody,
+ ModalFooter,
+ IconButton,
+ Heading,
+ HStack,
+} from '@chakra-ui/react'
+import { ChevronLeftIcon } from 'assets/icons'
+import React, { useState } from 'react'
+import { ModalProps } from '../../EmbedButton'
+import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList'
+import { capitalize } from 'utils'
+import { PublishFirstInfo } from 'components/shared/Info'
+import { ShopifyInstructions } from './ShopifyInstructions'
+
+export const ShopifyModal = ({ isOpen, onClose, isPublished }: ModalProps) => {
+ const [chosenEmbedType, setChosenEmbedType] = useState<
+ 'standard' | 'popup' | 'bubble' | undefined
+ >()
+ return (
+
+
+
+
+
+ {chosenEmbedType && (
+ }
+ aria-label="back"
+ variant="ghost"
+ colorScheme="gray"
+ mr={2}
+ onClick={() => setChosenEmbedType(undefined)}
+ />
+ )}
+
+ Shopify {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`}
+
+
+
+
+
+ {!isPublished && }
+ {!chosenEmbedType ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/WebflowModal/WebflowInstructions.tsx b/apps/builder/components/share/integrations/modals/WebflowModal/WebflowInstructions.tsx
new file mode 100644
index 000000000..0d76910ce
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/WebflowModal/WebflowInstructions.tsx
@@ -0,0 +1,107 @@
+import { OrderedList, ListItem, Tag, Text, Stack } from '@chakra-ui/react'
+
+type WebflowInstructionsProps = {
+ type: 'standard' | 'popup' | 'bubble'
+}
+
+export const WebflowInstructions = ({ type }: WebflowInstructionsProps) => {
+ switch (type) {
+ case 'standard': {
+ return
+ }
+ case 'popup': {
+ return
+ }
+ case 'bubble': {
+ return
+ }
+ default:
+ return <>>
+ }
+}
+
+const StandardInstructions = () => (
+
+ In the Webflow editor:
+
+
+ Press A to open the Add elements panel
+
+
+ Add an embed element from the components
+ section and paste this code:
+ {/* sendWebflowCopyEvent('standard')}
+ /> */}
+
+
+
+)
+
+const PopupInstructions = () => {
+ // const [inputValue, setInputValue] = useState(0)
+
+ return (
+
+ In the Webflow editor
+
+
+ Press A to open the Add elements panel
+
+
+ Add an embed element from the components
+ section and paste this code:
+ {/* setInputValue(settings.delay ?? 0)}
+ mt={4}
+ />
+ sendWebflowCopyEvent('popup')}
+ /> */}
+
+
+
+ )
+}
+
+const BubbleInstructions = () => {
+ // const [inputValues, setInputValues] = useState<
+ // Pick
+ // >({
+ // proactiveMessage: undefined,
+ // button: {
+ // color: '',
+ // iconUrl: '',
+ // },
+ // })
+
+ return (
+
+ In the Webflow editor
+
+
+ Press A to open the Add elements panel
+
+
+ Add an embed element from the components
+ section and paste this code:
+ {/* setInputValues({ ...settings })}
+ mt={4}
+ />
+ sendWebflowCopyEvent('bubble')}
+ /> */}
+
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/WebflowModal/index.tsx b/apps/builder/components/share/integrations/modals/WebflowModal/index.tsx
new file mode 100644
index 000000000..7cd61abf9
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/WebflowModal/index.tsx
@@ -0,0 +1,63 @@
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalCloseButton,
+ ModalBody,
+ ModalFooter,
+ IconButton,
+ Heading,
+ HStack,
+} from '@chakra-ui/react'
+import { ChevronLeftIcon } from 'assets/icons'
+import React, { useState } from 'react'
+import { ModalProps } from '../../EmbedButton'
+import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList'
+import { capitalize } from 'utils'
+import { PublishFirstInfo } from 'components/shared/Info'
+import { WebflowInstructions } from './WebflowInstructions'
+
+export const WebflowModal = ({ isOpen, onClose, isPublished }: ModalProps) => {
+ const [chosenEmbedType, setChosenEmbedType] = useState<
+ 'standard' | 'popup' | 'bubble' | undefined
+ >()
+ return (
+
+
+
+
+
+ {chosenEmbedType && (
+ }
+ aria-label="back"
+ variant="ghost"
+ colorScheme="gray"
+ mr={2}
+ onClick={() => setChosenEmbedType(undefined)}
+ />
+ )}
+
+ Webflow {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`}
+
+
+
+
+
+ {!isPublished && }
+ {!chosenEmbedType ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/WixModal/WixInstructions.tsx b/apps/builder/components/share/integrations/modals/WixModal/WixInstructions.tsx
new file mode 100644
index 000000000..f0c4c1c5a
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/WixModal/WixInstructions.tsx
@@ -0,0 +1,96 @@
+import { ListItem, OrderedList, Tag } from '@chakra-ui/react'
+import { useState } from 'react'
+
+type WixInstructionsProps = {
+ type: 'standard' | 'popup' | 'bubble'
+}
+
+export const WixInstructions = ({ type }: WixInstructionsProps) => {
+ switch (type) {
+ case 'standard': {
+ return
+ }
+ case 'popup': {
+ return
+ }
+ case 'bubble': {
+ return
+ }
+ }
+}
+
+const StandardInstructions = () => {
+ return (
+ <>
+
+
+ In the Wix Website Editor:
+
+ Add {'>'} Embed {'>'} Embed a Widget
+
+
+
+ Click on Enter code and paste this code:
+
+
+ >
+ )
+}
+
+const PopupInstructions = () => {
+ // const [inputValue, setInputValue] = useState(0)
+
+ return (
+ <>
+
+
+ Go to Settings in your dashboard on Wix
+
+
+ Click on Custom Code under Advanced
+
+
+ Click + Add Custom Code at the top right.
+
+ Paste this snippet in the code box:
+
+ >
+ )
+}
+
+const BubbleInstructions = () => {
+ // const [inputValues, setInputValues] = useState<
+ // Pick
+ // >({
+ // proactiveMessage: undefined,
+ // button: {
+ // color: '',
+ // iconUrl: '',
+ // },
+ // })
+
+ return (
+ <>
+
+
+ Go to Settings in your dashboard on Wix
+
+
+ Click on Custom Code under Advanced
+
+
+ Click + Add Custom Code at the top right.
+
+ Paste this snippet in the code box:
+
+ {/* setInputValues({ ...settings })}
+ />
+ sendWixCopyEvent('bubble')}
+ /> */}
+ >
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/WixModal/index.tsx b/apps/builder/components/share/integrations/modals/WixModal/index.tsx
new file mode 100644
index 000000000..339fb858e
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/WixModal/index.tsx
@@ -0,0 +1,63 @@
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ ModalCloseButton,
+ ModalBody,
+ ModalFooter,
+ IconButton,
+ Heading,
+ HStack,
+} from '@chakra-ui/react'
+import { ChevronLeftIcon } from 'assets/icons'
+import React, { useState } from 'react'
+import { ModalProps } from '../../EmbedButton'
+import { ChooseEmbedTypeList } from '../ChooseEmbedTypeList'
+import { WixInstructions } from './WixInstructions'
+import { capitalize } from 'utils'
+import { PublishFirstInfo } from 'components/shared/Info'
+
+export const WixModal = ({ isOpen, onClose, isPublished }: ModalProps) => {
+ const [chosenEmbedType, setChosenEmbedType] = useState<
+ 'standard' | 'popup' | 'bubble' | undefined
+ >()
+ return (
+
+
+
+
+
+ {chosenEmbedType && (
+ }
+ aria-label="back"
+ variant="ghost"
+ colorScheme="gray"
+ mr={2}
+ onClick={() => setChosenEmbedType(undefined)}
+ />
+ )}
+
+ Wix {chosenEmbedType && `- ${capitalize(chosenEmbedType)}`}
+
+
+
+
+
+ {!isPublished && }
+ {!chosenEmbedType ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/WordpressModal.tsx b/apps/builder/components/share/integrations/modals/WordpressModal.tsx
new file mode 100644
index 000000000..e4a7064d6
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/WordpressModal.tsx
@@ -0,0 +1,66 @@
+import {
+ Modal,
+ ModalOverlay,
+ ModalContent,
+ ModalHeader,
+ Heading,
+ ModalCloseButton,
+ ModalBody,
+ OrderedList,
+ ListItem,
+ InputGroup,
+ Input,
+ InputRightElement,
+ ModalFooter,
+ Link,
+} from '@chakra-ui/react'
+import { ExternalLinkIcon } from 'assets/icons'
+import { CopyButton } from 'components/shared/buttons/CopyButton'
+import { PublishFirstInfo } from 'components/shared/Info'
+import { ModalProps } from '../EmbedButton'
+
+export const WordpressModal = ({
+ publicId,
+ isPublished,
+ isOpen,
+ onClose,
+}: ModalProps): JSX.Element => {
+ return (
+
+
+
+
+ WordPress
+
+
+
+ {!isPublished && }
+
+
+ Install{' '}
+
+ the official Typebot WordPress plugin
+
+
+
+
+ Copy your typebot ID
+
+
+
+
+
+
+
+ Complete the setup in your Wordpress interface
+
+
+
+
+
+ )
+}
diff --git a/apps/builder/components/share/integrations/modals/index.tsx b/apps/builder/components/share/integrations/modals/index.tsx
new file mode 100644
index 000000000..d2393557c
--- /dev/null
+++ b/apps/builder/components/share/integrations/modals/index.tsx
@@ -0,0 +1,9 @@
+export * from './IframeModal'
+export * from './NotionModal'
+export * from './WordpressModal'
+export * from './WixModal'
+export * from './GtmModal'
+export * from './React/ReactModal'
+export * from './Javascript/JavascriptModal'
+export * from './WebflowModal'
+export * from './ShopifyModal'
diff --git a/apps/builder/components/shared/CodeEditor.tsx b/apps/builder/components/shared/CodeEditor.tsx
index deed92364..ed1b6068f 100644
--- a/apps/builder/components/shared/CodeEditor.tsx
+++ b/apps/builder/components/shared/CodeEditor.tsx
@@ -6,7 +6,7 @@ import { useEffect, useRef, useState } from 'react'
type Props = {
value: string
- lang: 'css' | 'json'
+ lang?: 'css' | 'json'
onChange?: (value: string) => void
isReadOnly?: boolean
}
@@ -46,7 +46,8 @@ export const CodeEditor = ({
basicSetup,
EditorState.readOnly.of(isReadOnly),
]
- extensions.push(lang === 'json' ? json() : css())
+ if (lang === 'json') extensions.push(json())
+ if (lang === 'css') extensions.push(css())
const editor = new EditorView({
state: EditorState.create({
extensions,
diff --git a/apps/builder/components/shared/Info.tsx b/apps/builder/components/shared/Info.tsx
index 6e87da4fe..91b964ecf 100644
--- a/apps/builder/components/shared/Info.tsx
+++ b/apps/builder/components/shared/Info.tsx
@@ -7,3 +7,7 @@ export const Info = (props: AlertProps) => (
{props.children}
)
+
+export const PublishFirstInfo = (props: AlertProps) => (
+ You need to publish your typebot first
+)
diff --git a/apps/builder/pages/typebots/[typebotId]/share.tsx b/apps/builder/pages/typebots/[typebotId]/share.tsx
index 043a9b479..f70fa9567 100644
--- a/apps/builder/pages/typebots/[typebotId]/share.tsx
+++ b/apps/builder/pages/typebots/[typebotId]/share.tsx
@@ -5,7 +5,7 @@ import { TypebotHeader } from 'components/shared/TypebotHeader'
import React from 'react'
const SharePage = () => (
-
+
diff --git a/packages/utils/src/utils.ts b/packages/utils/src/utils.ts
index 5e3a8d996..ca3d66890 100644
--- a/packages/utils/src/utils.ts
+++ b/packages/utils/src/utils.ts
@@ -126,3 +126,5 @@ export const stepHasItems = (
): step is ConditionStep | ChoiceInputStep => 'items' in step
export const byId = (id?: string) => (obj: { id: string }) => obj.id === id
+
+export const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1)