Skip to content

Commit 67437f9

Browse files
authored
✨ Add WhatsApp integration beta test (#722)
Related to #401
1 parent cd5aaee commit 67437f9

File tree

136 files changed

+6659
-5348
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+6659
-5348
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ Interested in self-hosting Typebot on your server? Take a look at the [self-host
9999

100100
You are awesome, lets build great software together. Head over to the [Contribute guidelines](https://github.com/baptisteArno/typebot.io/blob/main/CONTRIBUTING.md) to get started. 💪
101101

102+
## Run the project locally
103+
104+
Follow the [Get started](https://github.com/baptisteArno/typebot.io/blob/main/CONTRIBUTING.md#get-started) section of the Contributing guide.
105+
102106
### Top contributors
103107

104108
<a href="https://github.com/baptistearno/typebot.io/graphs/contributors">

apps/builder/next.config.mjs

+12
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ const nextConfig = {
4040
},
4141
]
4242
},
43+
async rewrites() {
44+
return process.env.NEXT_PUBLIC_POSTHOG_KEY
45+
? [
46+
{
47+
source: '/ingest/:path*',
48+
destination:
49+
(process.env.NEXT_PUBLIC_POSTHOG_HOST ??
50+
'https://app.posthog.com') + '/:path*',
51+
},
52+
]
53+
: []
54+
},
4355
}
4456

4557
const sentryWebpackPluginOptions = {

apps/builder/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
"got": "12.6.0",
6767
"immer": "10.0.2",
6868
"jsonwebtoken": "9.0.1",
69+
"libphonenumber-js": "1.10.37",
6970
"micro": "10.0.1",
7071
"micro-cors": "0.1.1",
7172
"minio": "7.1.1",
@@ -76,6 +77,7 @@
7677
"nodemailer": "6.9.3",
7778
"nprogress": "0.2.0",
7879
"papaparse": "5.4.1",
80+
"posthog-js": "^1.77.1",
7981
"posthog-node": "3.1.1",
8082
"prettier": "2.8.8",
8183
"qs": "6.11.2",
@@ -114,9 +116,9 @@
114116
"@types/react": "18.2.15",
115117
"@types/tinycolor2": "1.4.3",
116118
"dotenv-cli": "^7.2.1",
117-
"next-runtime-env": "^1.6.2",
118119
"eslint": "8.44.0",
119120
"eslint-config-custom": "workspace:*",
121+
"next-runtime-env": "^1.6.2",
120122
"superjson": "^1.12.4",
121123
"typescript": "5.1.6",
122124
"zod": "3.21.4",
Loading
Loading
Loading

apps/builder/src/components/DropdownList.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import {
22
Button,
3+
ButtonProps,
34
chakra,
45
Menu,
56
MenuButton,
6-
MenuButtonProps,
77
MenuItem,
88
MenuList,
99
Portal,
@@ -27,7 +27,7 @@ export const DropdownList = <T extends readonly any[]>({
2727
items,
2828
placeholder = '',
2929
...props
30-
}: Props<T> & MenuButtonProps) => {
30+
}: Props<T> & ButtonProps) => {
3131
const handleMenuItemClick = (operator: T[number]) => () => {
3232
onItemSelect(operator)
3333
}

apps/builder/src/components/TextLink.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export const TextLink = ({
2626
>
2727
<chakra.span textDecor="underline" display="inline-block" {...textProps}>
2828
{isExternal ? (
29-
<HStack spacing={1}>
29+
<HStack as="span" spacing={1}>
3030
<chakra.span noOfLines={noOfLines} maxW="100%">
3131
{children}
3232
</chakra.span>

apps/builder/src/components/Toast.tsx

+29-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import {
2+
Accordion,
3+
AccordionButton,
4+
AccordionIcon,
5+
AccordionItem,
6+
AccordionPanel,
27
Flex,
38
HStack,
49
IconButton,
@@ -35,6 +40,7 @@ export const Toast = ({
3540
onClose,
3641
}: ToastProps) => {
3742
const bgColor = useColorModeValue('white', 'gray.800')
43+
const detailsLabelColor = useColorModeValue('gray.600', 'gray.400')
3844

3945
return (
4046
<Flex
@@ -56,14 +62,29 @@ export const Toast = ({
5662
</Stack>
5763

5864
{details && (
59-
<CodeEditor
60-
isReadOnly
61-
value={details.content}
62-
lang={details.lang}
63-
minWidth="300px"
64-
maxHeight="200px"
65-
maxWidth="calc(450px - 100px)"
66-
/>
65+
<Accordion allowToggle>
66+
<AccordionItem>
67+
<AccordionButton
68+
justifyContent="space-between"
69+
fontSize="sm"
70+
py="1"
71+
color={detailsLabelColor}
72+
>
73+
Details
74+
<AccordionIcon />
75+
</AccordionButton>
76+
<AccordionPanel>
77+
<CodeEditor
78+
isReadOnly
79+
value={details.content}
80+
lang={details.lang}
81+
minWidth="300px"
82+
maxHeight="200px"
83+
maxWidth="calc(450px - 100px)"
84+
/>
85+
</AccordionPanel>
86+
</AccordionItem>
87+
</Accordion>
6788
)}
6889
{(secondaryButton || primaryButton) && (
6990
<HStack>

apps/builder/src/components/inputs/SwitchWithLabel.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
FormControl,
3+
FormControlProps,
34
FormLabel,
45
HStack,
56
Switch,
@@ -13,13 +14,15 @@ export type SwitchWithLabelProps = {
1314
initialValue: boolean
1415
moreInfoContent?: string
1516
onCheckChange: (isChecked: boolean) => void
16-
} & SwitchProps
17+
justifyContent?: FormControlProps['justifyContent']
18+
} & Omit<SwitchProps, 'value' | 'justifyContent'>
1719

1820
export const SwitchWithLabel = ({
1921
label,
2022
initialValue,
2123
moreInfoContent,
2224
onCheckChange,
25+
justifyContent = 'space-between',
2326
...switchProps
2427
}: SwitchWithLabelProps) => {
2528
const [isChecked, setIsChecked] = useState(initialValue)
@@ -28,8 +31,9 @@ export const SwitchWithLabel = ({
2831
setIsChecked(!isChecked)
2932
onCheckChange(!isChecked)
3033
}
34+
3135
return (
32-
<FormControl as={HStack} justifyContent="space-between">
36+
<FormControl as={HStack} justifyContent={justifyContent}>
3337
<FormLabel mb="0">
3438
{label}
3539
{moreInfoContent && (

apps/builder/src/components/inputs/TextInput.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,13 @@ export type TextInputProps = {
3535
isDisabled?: boolean
3636
} & Pick<
3737
InputProps,
38-
'autoComplete' | 'onFocus' | 'onKeyUp' | 'type' | 'autoFocus' | 'size'
38+
| 'autoComplete'
39+
| 'onFocus'
40+
| 'onKeyUp'
41+
| 'type'
42+
| 'autoFocus'
43+
| 'size'
44+
| 'maxWidth'
3945
>
4046

4147
export const TextInput = forwardRef(function TextInput(
@@ -56,6 +62,7 @@ export const TextInput = forwardRef(function TextInput(
5662
onFocus,
5763
onKeyUp,
5864
size,
65+
maxWidth,
5966
}: TextInputProps,
6067
ref
6168
) {
@@ -122,6 +129,7 @@ export const TextInput = forwardRef(function TextInput(
122129
onBlur={updateCarretPosition}
123130
onChange={(e) => changeValue(e.target.value)}
124131
size={size}
132+
maxWidth={maxWidth}
125133
/>
126134
)
127135

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { IconProps, Icon } from '@chakra-ui/react'
2+
3+
export const WhatsAppLogo = (props: IconProps) => (
4+
<Icon viewBox="0 0 163 164" {...props}>
5+
<g filter="url(#filter0_f_1006_58)">
6+
<path
7+
d="M48.5649 132.648L50.7999 133.972C60.1869 139.543 70.9499 142.49 81.9259 142.495H81.9489C115.656 142.495 143.088 115.069 143.102 81.3599C143.108 65.0249 136.753 49.6639 125.207 38.1089C119.544 32.4103 112.807 27.8915 105.386 24.8141C97.9646 21.7368 90.0068 20.162 81.9729 20.1809C48.2399 20.1809 20.8069 47.6039 20.7949 81.3109C20.7783 92.8208 24.0195 104.101 30.1439 113.846L31.5989 116.158L25.4199 138.716L48.5649 132.648ZM7.75391 156.192L18.1929 118.078C11.7549 106.924 8.36791 94.2699 8.37191 81.3059C8.38891 40.7499 41.3929 7.75586 81.9499 7.75586C101.631 7.76586 120.104 15.4249 133.997 29.3279C147.89 43.2309 155.534 61.7109 155.527 81.3649C155.509 121.918 122.5 154.918 81.9489 154.918H81.9169C69.6039 154.913 57.5049 151.824 46.7579 145.964L7.75391 156.192Z"
8+
fill="#B3B3B3"
9+
/>
10+
</g>
11+
<path
12+
d="M7 155.436L17.439 117.322C10.9899 106.141 7.60242 93.4575 7.618 80.55C7.635 39.994 40.639 7 81.196 7C100.877 7.01 119.35 14.669 133.243 28.572C147.136 42.475 154.78 60.955 154.773 80.609C154.755 121.162 121.746 154.162 81.195 154.162H81.163C68.85 154.157 56.751 151.068 46.004 145.208L7 155.436Z"
13+
fill="white"
14+
/>
15+
<path
16+
d="M81.2171 19.425C47.4841 19.425 20.0511 46.848 20.0391 80.555C20.0225 92.065 23.2637 103.345 29.3881 113.09L30.8431 115.403L24.6641 137.961L47.8101 131.892L50.0451 133.216C59.4321 138.787 70.1951 141.733 81.1711 141.739H81.1941C114.901 141.739 142.334 114.313 142.347 80.604C142.373 72.5696 140.804 64.6099 137.732 57.1858C134.661 49.7617 130.147 43.0207 124.452 37.353C118.789 31.6543 112.052 27.1354 104.631 24.0581C97.2092 20.9807 89.2512 19.406 81.2171 19.425Z"
17+
fill="url(#paint0_linear_1006_58)"
18+
/>
19+
<path
20+
fillRule="evenodd"
21+
clipRule="evenodd"
22+
d="M62.8046 49.801C61.4266 46.74 59.9766 46.678 58.6676 46.625L55.1436 46.582C53.9176 46.582 51.9256 47.042 50.2416 48.882C48.5576 50.722 43.8066 55.169 43.8066 64.214C43.8066 73.259 50.3946 81.999 51.3126 83.227C52.2306 84.455 64.0306 103.608 82.7176 110.977C98.2466 117.101 101.407 115.883 104.779 115.577C108.151 115.271 115.656 111.13 117.187 106.837C118.718 102.544 118.719 98.866 118.26 98.097C117.801 97.328 116.575 96.871 114.735 95.951C112.895 95.031 103.858 90.584 102.173 89.97C100.488 89.356 99.2626 89.051 98.0356 90.891C96.8086 92.731 93.2896 96.87 92.2166 98.097C91.1436 99.324 90.0726 99.478 88.2326 98.559C86.3926 97.64 80.4726 95.698 73.4486 89.435C67.9836 84.562 64.2946 78.544 63.2206 76.705C62.1466 74.866 63.1066 73.87 64.0286 72.954C64.8536 72.13 65.8666 70.807 66.7876 69.734C67.7086 68.661 68.0116 67.894 68.6236 66.669C69.2356 65.444 68.9306 64.368 68.4706 63.449C68.0106 62.53 64.4386 53.437 62.8046 49.801Z"
23+
fill="white"
24+
/>
25+
<defs>
26+
<filter
27+
id="filter0_f_1006_58"
28+
x="0.691906"
29+
y="0.69386"
30+
width="161.897"
31+
height="162.56"
32+
filterUnits="userSpaceOnUse"
33+
colorInterpolationFilters="sRGB"
34+
>
35+
<feFlood floodOpacity="0" result="BackgroundImageFix" />
36+
<feBlend
37+
mode="normal"
38+
in="SourceGraphic"
39+
in2="BackgroundImageFix"
40+
result="shape"
41+
/>
42+
<feGaussianBlur
43+
stdDeviation="3.531"
44+
result="effect1_foregroundBlur_1006_58"
45+
/>
46+
</filter>
47+
<linearGradient
48+
id="paint0_linear_1006_58"
49+
x1="79.9481"
50+
y1="26.765"
51+
x2="80.5681"
52+
y2="131.29"
53+
gradientUnits="userSpaceOnUse"
54+
>
55+
<stop stopColor="#57D163" />
56+
<stop offset="1" stopColor="#23B33A" />
57+
</linearGradient>
58+
</defs>
59+
</Icon>
60+
)

apps/builder/src/features/account/UserProvider.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useToast } from '@/hooks/useToast'
88
import { updateUserQuery } from './queries/updateUserQuery'
99
import { useDebouncedCallback } from 'use-debounce'
1010
import { env } from '@typebot.io/env'
11+
import { identifyUser } from '../telemetry/posthog'
1112

1213
export const userContext = createContext<{
1314
user?: User
@@ -37,7 +38,11 @@ export const UserProvider = ({ children }: { children: ReactNode }) => {
3738
)
3839
const parsedUser = session.user as User
3940
setUser(parsedUser)
40-
if (parsedUser?.id) setSentryUser({ id: parsedUser.id })
41+
42+
if (parsedUser?.id) {
43+
setSentryUser({ id: parsedUser.id })
44+
identifyUser(parsedUser.id)
45+
}
4146
}, [session, user])
4247

4348
useEffect(() => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {
2+
Modal,
3+
ModalBody,
4+
ModalContent,
5+
ModalFooter,
6+
ModalHeader,
7+
ModalOverlay,
8+
} from '@chakra-ui/react'
9+
import { ApiTokensList } from './ApiTokensList'
10+
import { useUser } from '../hooks/useUser'
11+
12+
type Props = {
13+
isOpen: boolean
14+
onClose: () => void
15+
}
16+
export const ApiTokensModal = ({ isOpen, onClose }: Props) => {
17+
const { user } = useUser()
18+
19+
if (!user) return
20+
return (
21+
<Modal isOpen={isOpen} onClose={onClose} size="xl">
22+
<ModalOverlay />
23+
<ModalContent>
24+
<ModalHeader />
25+
<ModalBody>
26+
<ApiTokensList user={user} />
27+
</ModalBody>
28+
<ModalFooter />
29+
</ModalContent>
30+
</Modal>
31+
)
32+
}

apps/builder/src/features/account/components/CreateTokenModal.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
InputGroup,
1616
InputRightElement,
1717
} from '@chakra-ui/react'
18-
import React, { FormEvent, useState } from 'react'
18+
import React, { FormEvent, useRef, useState } from 'react'
1919
import { createApiTokenQuery } from '../queries/createApiTokenQuery'
2020
import { ApiTokenFromServer } from '../types'
2121

@@ -32,6 +32,7 @@ export const CreateTokenModal = ({
3232
onClose,
3333
onNewToken,
3434
}: Props) => {
35+
const inputRef = useRef<HTMLInputElement>(null)
3536
const scopedT = useScopedI18n('account.apiTokens.createModal')
3637
const [name, setName] = useState('')
3738
const [isSubmitting, setIsSubmitting] = useState(false)
@@ -47,8 +48,9 @@ export const CreateTokenModal = ({
4748
}
4849
setIsSubmitting(false)
4950
}
51+
5052
return (
51-
<Modal isOpen={isOpen} onClose={onClose}>
53+
<Modal isOpen={isOpen} onClose={onClose} initialFocusRef={inputRef}>
5254
<ModalOverlay />
5355
<ModalContent>
5456
<ModalHeader>
@@ -58,7 +60,7 @@ export const CreateTokenModal = ({
5860
{newTokenValue ? (
5961
<ModalBody as={Stack} spacing="4">
6062
<Text>
61-
{scopedT('copyInstruction')}
63+
{scopedT('copyInstruction')}{' '}
6264
<strong>{scopedT('securityWarning')}</strong>
6365
</Text>
6466
<InputGroup size="md">
@@ -72,6 +74,7 @@ export const CreateTokenModal = ({
7274
<ModalBody as="form" onSubmit={createToken}>
7375
<Text mb="4">{scopedT('nameInput.label')}</Text>
7476
<Input
77+
ref={inputRef}
7578
placeholder={scopedT('nameInput.placeholder')}
7679
onChange={(e) => setName(e.target.value)}
7780
/>

apps/builder/src/features/blocks/inputs/payment/components/PaymentSettings.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export const PaymentSettings = ({ options, onOptionsChange }: Props) => {
120120
currentCredentialsId={options.credentialsId}
121121
onCredentialsSelect={updateCredentials}
122122
onCreateNewClick={onOpen}
123+
credentialsName="Stripe account"
123124
/>
124125
)}
125126
</Stack>

apps/builder/src/features/blocks/inputs/payment/payment.spec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ test.describe('Payment input block', () => {
2121

2222
await page.goto(`/typebots/${typebotId}/edit`)
2323
await page.click('text=Configure...')
24-
await page.getByRole('button', { name: 'Select an account' }).click()
25-
await page.click('text=Connect new')
24+
await page.getByRole('button', { name: 'Add Stripe account' }).click()
2625
await page.fill('[placeholder="Typebot"]', 'My Stripe Account')
2726
await page.fill('[placeholder="sk_test_..."]', env.STRIPE_SECRET_KEY ?? '')
2827
await page.fill('[placeholder="sk_live_..."]', env.STRIPE_SECRET_KEY ?? '')

apps/builder/src/features/blocks/integrations/googleSheets/components/GoogleSheetsSettings.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export const GoogleSheetsSettings = ({
115115
currentCredentialsId={options?.credentialsId}
116116
onCredentialsSelect={handleCredentialsIdChange}
117117
onCreateNewClick={handleCreateNewClick}
118+
credentialsName="Sheets account"
118119
/>
119120
)}
120121
<GoogleSheetConnectModal

0 commit comments

Comments
 (0)