Skip to content

Commit

Permalink
🦴 Add share page backbone
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Dec 23, 2021
1 parent 79aede1 commit 9a78a34
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 4 deletions.
6 changes: 6 additions & 0 deletions apps/builder/assets/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,9 @@ export const PencilIcon = (props: IconProps) => (
<circle cx="11" cy="11" r="2"></circle>
</Icon>
)

export const EditIcon = (props: IconProps) => (
<Icon viewBox="0 0 24 24" {...featherIconsBaseProps} {...props}>
<path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"></path>
</Icon>
)
4 changes: 2 additions & 2 deletions apps/builder/components/dashboard/FolderContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
import { FolderPlusIcon } from 'assets/icons'
import React, { useState } from 'react'
import { createFolder, useFolders } from 'services/folders'
import { updateTypebot, useTypebots } from 'services/typebots'
import { patchTypebot, useTypebots } from 'services/typebots'
import { BackButton } from './FolderContent/BackButton'
import { CreateBotButton } from './FolderContent/CreateBotButton'
import { ButtonSkeleton, FolderButton } from './FolderContent/FolderButton'
Expand Down Expand Up @@ -83,7 +83,7 @@ export const FolderContent = ({ folder }: Props) => {

const moveTypebotToFolder = async (typebotId: string, folderId: string) => {
if (!typebots) return
const { error } = await updateTypebot(typebotId, {
const { error } = await patchTypebot(typebotId, {
folderId: folderId === 'root' ? null : folderId,
})
if (error) toast({ description: error.message })
Expand Down
70 changes: 70 additions & 0 deletions apps/builder/components/share/EditableUrl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
HStack,
Tooltip,
EditablePreview,
EditableInput,
Text,
Editable,
Button,
ButtonProps,
useEditableControls,
} from '@chakra-ui/react'
import { EditIcon } from 'assets/icons'
import { CopyButton } from 'components/shared/buttons/CopyButton'
import React from 'react'

type EditableUrlProps = {
publicId?: string
onPublicIdChange: (publicId: string) => void
}

export const EditableUrl = ({
publicId,
onPublicIdChange,
}: EditableUrlProps) => {
return (
<Editable
as={HStack}
spacing={3}
defaultValue={publicId}
onSubmit={onPublicIdChange}
>
<HStack spacing={1}>
<Text>https://</Text>
<Tooltip label="Edit">
<EditablePreview
mx={1}
bgColor="blue.500"
color="white"
px={3}
rounded="md"
cursor="pointer"
display="flex"
fontWeight="semibold"
/>
</Tooltip>

<EditableInput px={2} />

<Text>.typebot.io/</Text>
</HStack>

<HStack>
<EditButton size="xs" />
<CopyButton size="xs" textToCopy={`https://${publicId}.typebot.io/`} />
</HStack>
</Editable>
)
}

const EditButton = (props: ButtonProps) => {
const { isEditing, getEditButtonProps } = useEditableControls()

return isEditing ? (
<></>
) : (
<Button leftIcon={<EditIcon />} {...props} {...getEditButtonProps()}>
Edit
</Button>
)
}
35 changes: 35 additions & 0 deletions apps/builder/components/share/ShareContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Flex, Heading, Stack } from '@chakra-ui/react'
import { useTypebot } from 'contexts/TypebotContext'
import React from 'react'
import { parseDefaultPublicId } from 'services/typebots'
import { EditableUrl } from './EditableUrl'

export const ShareContent = () => {
const { typebot, updatePublicId } = useTypebot()

const handlePublicIdChange = (publicId: string) => {
if (publicId === typebot?.publicId) return
updatePublicId(publicId)
}
return (
<Flex h="full" w="full" justifyContent="center" align="flex-start">
<Stack maxW="1000px" w="full" pt="10" spacing={6}>
<Heading fontSize="2xl" as="h1">
Your typebot link
</Heading>
{typebot && (
<EditableUrl
publicId={
typebot?.publicId ??
parseDefaultPublicId(typebot.name, typebot.id)
}
onPublicIdChange={handlePublicIdChange}
/>
)}
<Heading fontSize="2xl" as="h1">
Embed your typebot
</Heading>
</Stack>
</Flex>
)
}
25 changes: 25 additions & 0 deletions apps/builder/components/shared/buttons/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react'
import { ButtonProps, Button, useClipboard } from '@chakra-ui/react'

interface CopyButtonProps extends ButtonProps {
textToCopy: string
onCopied?: () => void
}

export const CopyButton = (props: CopyButtonProps) => {
const { textToCopy, onCopied, ...buttonProps } = props
const { hasCopied, onCopy } = useClipboard(textToCopy)

return (
<Button
isDisabled={hasCopied}
onClick={() => {
onCopy()
if (onCopied) onCopied()
}}
{...buttonProps}
>
{!hasCopied ? 'Copy' : 'Copied'}
</Button>
)
}
7 changes: 7 additions & 0 deletions apps/builder/contexts/TypebotContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const typebotContext = createContext<{
undo: () => void
updateTheme: (theme: Theme) => void
updateSettings: (settings: Settings) => void
updatePublicId: (publicId: string) => void
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
}>({})
Expand Down Expand Up @@ -284,6 +285,11 @@ export const TypebotContext = ({
setLocalTypebot({ ...localTypebot, settings })
}

const updatePublicId = (publicId: string) => {
if (!localTypebot) return
setLocalTypebot({ ...localTypebot, publicId })
}

return (
<typebotContext.Provider
value={{
Expand All @@ -301,6 +307,7 @@ export const TypebotContext = ({
undo,
updateTheme,
updateSettings,
updatePublicId,
}}
>
{children}
Expand Down
8 changes: 8 additions & 0 deletions apps/builder/pages/api/typebots/[id].ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
})
return res.send({ typebots })
}
if (req.method === 'PUT') {
const data = JSON.parse(req.body)
const typebots = await prisma.typebot.update({
where: { id },
data,
})
return res.send({ typebots })
}
if (req.method === 'PATCH') {
const data = JSON.parse(req.body)
const typebots = await prisma.typebot.update({
Expand Down
23 changes: 23 additions & 0 deletions apps/builder/pages/typebots/[id]/share.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Flex } from '@chakra-ui/layout'
import withAuth from 'components/HOC/withUser'
import { Seo } from 'components/Seo'
import { ShareContent } from 'components/share/ShareContent'
import { TypebotHeader } from 'components/shared/TypebotHeader'
import { TypebotContext } from 'contexts/TypebotContext'
import { useRouter } from 'next/router'
import React from 'react'

const SharePage = () => {
const { query } = useRouter()
return (
<TypebotContext typebotId={query.id?.toString()}>
<Seo title="Share" />
<Flex overflow="hidden" h="100vh" flexDir="column">
<TypebotHeader />
<ShareContent />
</Flex>
</TypebotContext>
)
}

export default withAuth(SharePage)
15 changes: 13 additions & 2 deletions apps/builder/services/typebots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import shortId from 'short-uuid'
import { Typebot } from 'bot-engine'
import useSWR from 'swr'
import { fetcher, sendRequest } from './utils'
import { fetcher, sendRequest, toKebabCase } from './utils'
import { deepEqual } from 'fast-equals'

export const useTypebots = ({
Expand Down Expand Up @@ -71,7 +71,14 @@ export const deleteTypebot = async (id: string) =>
method: 'DELETE',
})

export const updateTypebot = async (id: string, typebot: Partial<Typebot>) =>
export const updateTypebot = async (id: string, typebot: Typebot) =>
sendRequest({
url: `/api/typebots/${id}`,
method: 'PUT',
body: typebot,
})

export const patchTypebot = async (id: string, typebot: Partial<Typebot>) =>
sendRequest({
url: `/api/typebots/${id}`,
method: 'PATCH',
Expand Down Expand Up @@ -157,4 +164,8 @@ export const parseTypebotToPublicTypebot = (
typebotId: typebot.id,
theme: typebot.theme,
settings: typebot.settings,
publicId: typebot.publicId,
})

export const parseDefaultPublicId = (name: string, id: string) =>
toKebabCase(`${name}-${id?.slice(0, 5)}`)
8 changes: 8 additions & 0 deletions apps/builder/services/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,11 @@ export const parseHtmlStringToPlainText = (html: string): string => {
parser.end()
return label
}

export const toKebabCase = (value: string) => {
const matched = value.match(
/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g
)
if (!matched) return ''
return matched.map((x) => x.toLowerCase()).join('-')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Warnings:
- A unique constraint covering the columns `[publicId]` on the table `PublicTypebot` will be added. If there are existing duplicate values, this will fail.
- A unique constraint covering the columns `[publicId]` on the table `Typebot` will be added. If there are existing duplicate values, this will fail.
*/
-- AlterTable
ALTER TABLE "PublicTypebot" ADD COLUMN "publicId" TEXT;

-- AlterTable
ALTER TABLE "Typebot" ADD COLUMN "publicId" TEXT;

-- CreateIndex
CREATE UNIQUE INDEX "PublicTypebot_publicId_key" ON "PublicTypebot"("publicId");

-- CreateIndex
CREATE UNIQUE INDEX "Typebot_publicId_key" ON "Typebot"("publicId");
2 changes: 2 additions & 0 deletions packages/db/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ model Typebot {
startBlock Json
theme Json
settings Json
publicId String? @unique
}

model PublicTypebot {
Expand All @@ -102,6 +103,7 @@ model PublicTypebot {
startBlock Json
theme Json
settings Json
publicId String? @unique
}

model Result {
Expand Down

1 comment on commit 9a78a34

@vercel
Copy link

@vercel vercel bot commented on 9a78a34 Dec 23, 2021

Choose a reason for hiding this comment

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

Please sign in to comment.