Skip to content

Commit

Permalink
📝 Add Contribute docs
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Jan 3, 2024
1 parent b395729 commit 65f4fb0
Show file tree
Hide file tree
Showing 66 changed files with 1,453 additions and 519 deletions.
108 changes: 1 addition & 107 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,109 +1,3 @@
# Contributing to Typebot

You are considering contributing to Typebot. I thank you for this 🙏.

Any contributions you make are **greatly appreciated**. It can be anything from typo fixes to new features.

Let's [discuss](https://github.com/baptisteArno/typebot.io/discussions/new) about what you want to implement before creating a PR if you are unsure about the requirements or the vision of Typebot.

Typebot is a Monorepo powered by [Turborepo](https://turborepo.org/). It is composed of 2 main applications:

- the builder ([`./apps/builder`](apps/builder)), where you build your typebots
- the viewer ([`./apps/viewer`](./apps/viewer)), where your user answer the typebot

These apps are built with awesome web technologies including [Typescript](https://www.typescriptlang.org/), [Next.js](https://nextjs.org/), [Prisma](https://www.prisma.io/), [Chakra UI](https://chakra-ui.com/), [Tailwind CSS](https://tailwindcss.com/).

## Get started

1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your
own GitHub account and then
[clone](https://help.github.com/articles/cloning-a-repository/) it to your local device.

2. Create a new branch:

```sh
git checkout -b MY_BRANCH_NAME
```

## Running the project locally

1. Install dependencies

```sh
cd typebot.io
pnpm i
```

2. Set up environment variables

Copy [`.env.dev.example`](./.env.dev.example) to `.env`

Check out the [Configuration guide](https://docs.typebot.io/self-hosting/configuration) if you want to enable more options

3. Make sure you have [Docker](https://docs.docker.com/compose/install/) running

4. Make sure you have Node.js installed. I suggest you use [`nvm`](https://github.com/nvm-sh/nvm) allowing you to manage different versions. Once you installed nvm, you can install and use the latest version of Node.js: `nvm install && nvm use`

5. Start the builder and viewer

```sh
pnpm dev
```

Builder is available at [`http://localhost:3000`](http://localhost:3000)

Viewer is available at [`http://localhost:3001`](http://localhost:3001)

Database inspector is available at [`http://localhost:5555`](http://localhost:5555)

By default, you can easily authenticate in the builder using the "Github Sign In" button. For other options, check out the [Configuration guide](https://docs.typebot.io/self-hosting/configuration)

6. (Optionnal) Start the landing page

Copy [`apps/landing-page/.env.local.example`](apps/landing-page/.env.local.example) to `apps/landing-page/.env.local`

```sh
cd apps/landing-page
pnpm dev
```

7. (Optionnal) Start the docs

```sh
cd apps/docs
pnpm start
```

I know the project can be a bit hard to understand at first. I'm working on improving the documentation and the codebase to make it easier to contribute. If you have any questions, feel free to [open a discussion](https://github.com/baptisteArno/typebot.io/discussions/new)

## How to create a new integration block

The first step to create a new Typebot block is to define its schema. For this you need to

1. Add your integration in the enum `IntegrationBlockType` in [`packages/schemas/features/blocks/integrations/enums.ts`](packages/schemas/features/blocks/integrations/enums.ts)
2. Create a new file in [`packages/schemas/features/blocks/integrations`](packages/schemas/features/blocks/integrations).

Your schema should look like:

```ts
import { z } from 'zod'
import { blockBaseSchema } from '../baseSchemas'

export const myIntegrationBlockSchema = blockBaseSchema.merge(
z.object({
type: z.enum([IntegrationBlockType.MY_INTEGRATION]),
options: z.object({
//...
}),
})
)

export type MyIntegrationBlock = z.infer<typeof myIntegrationBlockSchema>
```
3. Add `myIntegrationBlockSchema` to `blockSchema` in `packages/schemas/features/blocks/schemas.ts`
As soon as you have defined your schema, you can start implementing your block in the builder and the viewer.
Since the code is strictly typed, you should see exactly where you need to add your integration-specific code.
To sum it up you need to create a folder in [`apps/builder/src/features/blocks/integrations`](apps/builder/src/features/blocks/integrations) and in [`apps/viewer/src/features/blocks/integrations`](apps/viewer/src/features/blocks/integrations)
All the content has been moved [here](https://docs.typebot.io/contribute/overview). ❤️
15 changes: 3 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,15 @@ You'll find a lot of resources to help you get started with Typebot in the [docu

## Self-hosting

Interested in self-hosting Typebot on your server? Take a look at the [self-hosting installation instructions](https://docs.typebot.io/self-hosting).
Interested in self-hosting Typebot on your server? Take a look at the [self-hosting installation instructions](https://docs.typebot.io/self-hosting/get-started).

## How to Contribute

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. 💪

### Claim these open bounties 👇

<a href="https://console.algora.io/org/typebot/bounties?status=open">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://console.algora.io/api/og/typebot/bounties.png?p=0&status=open&theme=dark">
<img alt="Bounties of typebot" src="https://console.algora.io/api/og/typebot/bounties.png?p=0&status=open&theme=light">
</picture>
</a>
You are awesome, lets build great software together. Head over to the [Contribute docs](https://docs.typebot.io/contribute/overview) to get started. 💪

## Run the project locally

Follow the [Get started](https://github.com/baptisteArno/typebot.io/blob/main/CONTRIBUTING.md#get-started) section of the Contributing guide.
Follow the [Local installation](https://docs.typebot.io/contribute/guides/local-installation) section of in the Contributing docs.

### Top contributors

Expand Down
2 changes: 1 addition & 1 deletion apps/builder/src/components/DropdownList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export const DropdownList = <T extends readonly any[]>({
isRequired={isRequired}
as={direction === 'column' ? Stack : HStack}
justifyContent="space-between"
width={label ? 'full' : 'auto'}
width={props.width === 'full' || label ? 'full' : 'auto'}
spacing={direction === 'column' ? 2 : 3}
>
{label && (
Expand Down
135 changes: 135 additions & 0 deletions apps/builder/src/components/PrimitiveList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { Box, Button, Fade, Flex, IconButton, Stack } from '@chakra-ui/react'
import { TrashIcon, PlusIcon } from '@/components/icons'
import React, { useEffect, useState } from 'react'
import { createId } from '@paralleldrive/cuid2'

type ItemWithId<T extends number | string | boolean> = { id: string; value?: T }

export type TableListItemProps<T> = {
item: T
onItemChange: (item: T) => void
}

type Props<T extends number | string | boolean> = {
initialItems?: T[]
addLabel?: string
newItemDefaultProps?: Partial<T>
hasDefaultItem?: boolean
ComponentBetweenItems?: (props: unknown) => JSX.Element
onItemsChange: (items: T[]) => void
children: (props: TableListItemProps<T>) => JSX.Element
}

const addIdToItems = <T extends number | string | boolean>(
items: T[]
): ItemWithId<T>[] => items.map((item) => ({ id: createId(), value: item }))

const removeIdFromItems = <T extends number | string | boolean>(
items: ItemWithId<T>[]
): T[] => items.map((item) => item.value as T)

export const PrimitiveList = <T extends number | string | boolean>({
initialItems,
addLabel = 'Add',
hasDefaultItem,
children,
ComponentBetweenItems,
onItemsChange,
}: Props<T>) => {
const [items, setItems] = useState<ItemWithId<T>[]>()
const [showDeleteIndex, setShowDeleteIndex] = useState<number | null>(null)

useEffect(() => {
if (items) return
if (initialItems) {
setItems(addIdToItems(initialItems))
} else if (hasDefaultItem) {
setItems(addIdToItems([]))
} else {
setItems([])
}
}, [hasDefaultItem, initialItems, items])

const createItem = () => {
if (!items) return
const newItems = [...items, { id: createId() }]
setItems(newItems)
onItemsChange(removeIdFromItems(newItems))
}

const updateItem = (itemIndex: number, newValue: T) => {
if (!items) return
const newItems = items.map((item, idx) =>
idx === itemIndex ? { ...item, value: newValue } : item
)
setItems(newItems)
onItemsChange(removeIdFromItems(newItems))
}

const deleteItem = (itemIndex: number) => () => {
if (!items) return
const newItems = [...items]
newItems.splice(itemIndex, 1)
setItems([...newItems])
onItemsChange(removeIdFromItems([...newItems]))
}

const handleMouseEnter = (itemIndex: number) => () =>
setShowDeleteIndex(itemIndex)

const handleCellChange = (itemIndex: number) => (item: T) =>
updateItem(itemIndex, item)

const handleMouseLeave = () => setShowDeleteIndex(null)

return (
<Stack spacing={0}>
{items?.map((item, itemIndex) => (
<Box key={item.id}>
{itemIndex !== 0 && ComponentBetweenItems && (
<ComponentBetweenItems />
)}
<Flex
pos="relative"
onMouseEnter={handleMouseEnter(itemIndex)}
onMouseLeave={handleMouseLeave}
mt={itemIndex !== 0 && ComponentBetweenItems ? 4 : 0}
justifyContent="center"
pb="4"
>
{children({
item: item.value as T,
onItemChange: handleCellChange(itemIndex),
})}
<Fade
in={showDeleteIndex === itemIndex}
style={{
position: 'absolute',
left: '-15px',
top: '-15px',
zIndex: 1,
}}
unmountOnExit
>
<IconButton
icon={<TrashIcon />}
aria-label="Remove cell"
onClick={deleteItem(itemIndex)}
size="sm"
shadow="md"
/>
</Fade>
</Flex>
</Box>
))}
<Button
leftIcon={<PlusIcon />}
onClick={createItem}
flexShrink={0}
colorScheme="blue"
>
{addLabel}
</Button>
</Stack>
)
}
2 changes: 1 addition & 1 deletion apps/builder/src/components/inputs/NumberInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export const NumberInput = <HasVariable extends boolean>({
as={direction === 'column' ? Stack : HStack}
isRequired={isRequired}
justifyContent="space-between"
width={label ? 'full' : 'auto'}
width={label || props.width === 'full' ? 'full' : 'auto'}
spacing={direction === 'column' ? 2 : 3}
>
{label && (
Expand Down
4 changes: 3 additions & 1 deletion apps/builder/src/components/inputs/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type TextInputProps = {
placeholder?: string
isDisabled?: boolean
direction?: 'row' | 'column'
width?: 'full'
} & Pick<
InputProps,
| 'autoComplete'
Expand Down Expand Up @@ -66,6 +67,7 @@ export const TextInput = forwardRef(function TextInput(
size,
maxWidth,
direction = 'column',
width,
}: TextInputProps,
ref
) {
Expand Down Expand Up @@ -141,7 +143,7 @@ export const TextInput = forwardRef(function TextInput(
isRequired={isRequired}
as={direction === 'column' ? Stack : HStack}
justifyContent="space-between"
width={label ? 'full' : 'auto'}
width={label || width === 'full' ? 'full' : 'auto'}
spacing={direction === 'column' ? 2 : 3}
>
{label && (
Expand Down
5 changes: 3 additions & 2 deletions apps/builder/src/components/inputs/Textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type Props = {
helperText?: ReactNode
onChange: (value: string) => void
direction?: 'row' | 'column'
} & Pick<TextareaProps, 'minH'>
} & Pick<TextareaProps, 'minH' | 'width'>

export const Textarea = ({
id,
Expand All @@ -43,6 +43,7 @@ export const Textarea = ({
minH,
helperText,
direction = 'column',
width,
}: Props) => {
const inputRef = useRef<HTMLTextAreaElement | null>(null)
const [isTouched, setIsTouched] = useState(false)
Expand Down Expand Up @@ -108,7 +109,7 @@ export const Textarea = ({
isRequired={isRequired}
as={direction === 'column' ? Stack : HStack}
justifyContent="space-between"
width={label ? 'full' : 'auto'}
width={label || width === 'full' ? 'full' : 'auto'}
spacing={direction === 'column' ? 2 : 3}
>
{label && (
Expand Down
4 changes: 3 additions & 1 deletion apps/builder/src/components/inputs/VariableSearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Props = {
helperText?: ReactNode
moreInfoTooltip?: string
direction?: 'row' | 'column'
width?: 'full'
} & Omit<InputProps, 'placeholder'>

export const VariableSearchInput = ({
Expand All @@ -58,6 +59,7 @@ export const VariableSearchInput = ({
moreInfoTooltip,
direction = 'column',
isRequired,
width,
...inputProps
}: Props) => {
const focusedItemBgColor = useColorModeValue('gray.200', 'gray.700')
Expand Down Expand Up @@ -196,7 +198,7 @@ export const VariableSearchInput = ({
isRequired={isRequired}
as={direction === 'column' ? Stack : HStack}
justifyContent="space-between"
width={label ? 'full' : 'auto'}
width={label || width === 'full' ? 'full' : 'auto'}
spacing={direction === 'column' ? 2 : 3}
>
{label && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Props = {
moreInfoTooltip?: string
direction?: 'row' | 'column'
isRequired?: boolean
width?: 'full'
onChange: (value: string | undefined) => void
}
export const ForgeSelectInput = ({
Expand All @@ -38,6 +39,7 @@ export const ForgeSelectInput = ({
moreInfoTooltip,
isRequired,
direction = 'column',
width,
onChange,
}: Props) => {
const { workspace } = useWorkspace()
Expand Down Expand Up @@ -83,7 +85,7 @@ export const ForgeSelectInput = ({
isRequired={isRequired}
as={direction === 'column' ? Stack : HStack}
justifyContent="space-between"
width={label ? 'full' : 'auto'}
width={label || width === 'full' ? 'full' : 'auto'}
spacing={direction === 'column' ? 2 : 3}
>
{label && (
Expand Down

2 comments on commit 65f4fb0

@vercel
Copy link

@vercel vercel bot commented on 65f4fb0 Jan 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2 – ./apps/viewer

team.cr8.ai
triprint.my
wolf.cr8.ai
ai.meant.com
alphamen.pro
bet7k.online
bot.afric.ai
bot.grace.bj
bot.tobb.pro
chatgov.site
cinecorn.com
lb.ticketfute.com
lotedeleilao.site
mariwelash.com.br
metodoelev.com.br
nait.aarealtor.co
nextlevelnico.com
nutriandreia.shop
order.chatjer.com
ov1.wpwakanda.com
ov2.wpwakanda.com
ov3.wpwakanda.com
pcb.drapamela.com
ren.myhartanah.co
searacomvoce.site
seribusebulan.com
softwarelucra.com
support.triplo.ai
survey.collab.day
test.eqfeqfeq.com
titan.jetdigi.com
viewer.typebot.io
webtechads.online
welcome.triplo.ai
www.thegymgame.it
www.typebot.co.li
zeropendencia.com
1988.bouclidom.com
a.onewebcenter.com
acordorenovado.com
amostra-safe.click
andreimayer.com.br
bebesemcolicas.com
beneficiobr.online
bot.codefree.space
bot.flowmattic.xyz
bot.innovacion.fun
bot.jogodospix.com
bot.jogomilion.com
bot.lucide.contact
bot.mailerflow.com
bot.neferlopez.com
bot.newaiguide.com
bot.photonative.de
bot.rajatanjak.com
bot.samplehunt.com
bot.sinalcerto.com
bot.smoothmenu.com
bot.wphelpchat.com
bots.robomotion.io
viewer-v2-typebot-io.vercel.app
mdb.assessoria.mauricio.progenbr.com
mdb.evento.autocadastro.progenbr.com
form.shopmercedesbenzsouthorlando.com
mdb.evento.equipeinterna.progenbr.com
bot.studiotecnicoimmobiliaremerelli.it
mdb.assessoria.boaventura.progenbr.com
mdb.assessoria.jtrebesqui.progenbr.com
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
gabinete.baleia.formulario.progenbr.com
mdb.assessoria.carreirinha.progenbr.com
chrome-os-inquiry-system.itschromeos.com
mdb.assessoria.paulomarques.progenbr.com
viewer-v2-git-main-typebot-io.vercel.app
main-menu-for-itschromeos.itschromeos.com
mdb.assessoria.qrcode.ademir.progenbr.com
mdb.assessoria.qrcode.arthur.progenbr.com
mdb.assessoria.qrcode.danilo.progenbr.com
mdb.assessoria.qrcode.marcao.progenbr.com
mdb.assessoria.qrcode.marcio.progenbr.com
mdb.assessoria.qrcode.aloisio.progenbr.com
mdb.assessoria.qrcode.girotto.progenbr.com
mdb.assessoria.qrcode.marinho.progenbr.com
mdb.assessoria.qrcode.rodrigo.progenbr.com
mdb.assessoria.carlosalexandre.progenbr.com
mdb.assessoria.qrcode.desideri.progenbr.com
mdb.assessoria.qrcode.fernanda.progenbr.com
mdb.assessoria.qrcode.jbatista.progenbr.com
mdb.assessoria.qrcode.mauricio.progenbr.com
mdb.assessoria.fernanda.regional.progenbr.com
mdb.assessoria.qrcode.boaventura.progenbr.com
mdb.assessoria.qrcode.jtrebesqui.progenbr.com
mdb.assessoria.qrcode.carreirinha.progenbr.com
mdb.assessoria.qrcode.paulomarques.progenbr.com
mdb.assessoria.qrcode.carlosalexandre.progenbr.com
mdb.assessoria.qrcode.fernanda.regional.progenbr.com
www.typebot.io
get-typebot.com
www.get-typebot.com

@vercel
Copy link

@vercel vercel bot commented on 65f4fb0 Jan 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

builder-v2-typebot-io.vercel.app
app.typebot.io
builder-v2-git-main-typebot-io.vercel.app

Please sign in to comment.