♿ (editor) Allow empty group titles
This commit is contained in:
@ -3,7 +3,7 @@ import { canReadTypebots } from '@/utils/api/dbRules'
|
|||||||
import { authenticatedProcedure } from '@/utils/server/trpc'
|
import { authenticatedProcedure } from '@/utils/server/trpc'
|
||||||
import { TRPCError } from '@trpc/server'
|
import { TRPCError } from '@trpc/server'
|
||||||
import { Group, Typebot, Webhook, WebhookBlock } from 'models'
|
import { Group, Typebot, Webhook, WebhookBlock } from 'models'
|
||||||
import { byId, isWebhookBlock } from 'utils'
|
import { byId, isWebhookBlock, parseGroupTitle } from 'utils'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
export const listWebhookBlocksProcedure = authenticatedProcedure
|
export const listWebhookBlocksProcedure = authenticatedProcedure
|
||||||
@ -55,7 +55,7 @@ export const listWebhookBlocksProcedure = authenticatedProcedure
|
|||||||
...webhookBlocks,
|
...webhookBlocks,
|
||||||
...blocks.map((b) => ({
|
...blocks.map((b) => ({
|
||||||
id: b.id,
|
id: b.id,
|
||||||
label: `${group.title} > ${b.id}`,
|
label: `${parseGroupTitle(group.title)} > ${b.id}`,
|
||||||
url: typebot?.webhooks.find(byId(b.webhookId))?.url ?? undefined,
|
url: typebot?.webhooks.find(byId(b.webhookId))?.url ?? undefined,
|
||||||
})),
|
})),
|
||||||
]
|
]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { Tag, Text } from '@chakra-ui/react'
|
import { Tag, Text } from '@chakra-ui/react'
|
||||||
import { useTypebot } from '@/features/editor'
|
import { useTypebot } from '@/features/editor'
|
||||||
import { byId, isDefined } from 'utils'
|
import { byId, isDefined, parseGroupTitle } from 'utils'
|
||||||
import { JumpBlock } from 'models/features/blocks/logic/jump'
|
import { JumpBlock } from 'models/features/blocks/logic/jump'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -15,7 +15,8 @@ export const JumpNodeBody = ({ options }: Props) => {
|
|||||||
if (!selectedGroup) return <Text color="gray.500">Configure...</Text>
|
if (!selectedGroup) return <Text color="gray.500">Configure...</Text>
|
||||||
return (
|
return (
|
||||||
<Text>
|
<Text>
|
||||||
Jump to <Tag colorScheme="blue">{selectedGroup.title}</Tag>{' '}
|
Jump to{' '}
|
||||||
|
<Tag colorScheme="blue">{parseGroupTitle(selectedGroup.title)}</Tag>{' '}
|
||||||
{isDefined(blockIndex) && blockIndex >= 0 ? (
|
{isDefined(blockIndex) && blockIndex >= 0 ? (
|
||||||
<>
|
<>
|
||||||
at block <Tag colorScheme="blue">{blockIndex + 1}</Tag>
|
at block <Tag colorScheme="blue">{blockIndex + 1}</Tag>
|
||||||
|
@ -3,7 +3,7 @@ import { useTypebot } from '@/features/editor'
|
|||||||
import { Stack } from '@chakra-ui/react'
|
import { Stack } from '@chakra-ui/react'
|
||||||
import { JumpBlock } from 'models/features/blocks/logic/jump'
|
import { JumpBlock } from 'models/features/blocks/logic/jump'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { byId } from 'utils'
|
import { byId, parseGroupTitle } from 'utils'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
groupId: string
|
groupId: string
|
||||||
@ -32,7 +32,7 @@ export const JumpSettings = ({ groupId, options, onOptionsChange }: Props) => {
|
|||||||
items={typebot.groups
|
items={typebot.groups
|
||||||
.filter((group) => group.id !== currentGroupId)
|
.filter((group) => group.id !== currentGroupId)
|
||||||
.map((group) => ({
|
.map((group) => ({
|
||||||
label: group.title,
|
label: parseGroupTitle(group.title),
|
||||||
value: group.id,
|
value: group.id,
|
||||||
}))}
|
}))}
|
||||||
selectedItem={selectedGroup?.id}
|
selectedItem={selectedGroup?.id}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Select } from '@/components/inputs/Select'
|
import { Select } from '@/components/inputs/Select'
|
||||||
import { Input } from '@chakra-ui/react'
|
import { Input } from '@chakra-ui/react'
|
||||||
import { Group } from 'models'
|
import { Group } from 'models'
|
||||||
|
import { parseGroupTitle } from 'utils'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
groups: Group[]
|
groups: Group[]
|
||||||
@ -22,7 +23,7 @@ export const GroupsDropdown = ({
|
|||||||
<Select
|
<Select
|
||||||
selectedItem={groupId}
|
selectedItem={groupId}
|
||||||
items={(groups ?? []).map((group) => ({
|
items={(groups ?? []).map((group) => ({
|
||||||
label: group.title,
|
label: parseGroupTitle(group.title),
|
||||||
value: group.id,
|
value: group.id,
|
||||||
}))}
|
}))}
|
||||||
onSelect={onGroupIdSelected}
|
onSelect={onGroupIdSelected}
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
WebhookCallBacks,
|
WebhookCallBacks,
|
||||||
} from './blocks'
|
} from './blocks'
|
||||||
import { Coordinates } from '@/features/graph'
|
import { Coordinates } from '@/features/graph'
|
||||||
|
import { parseGroupTitle } from 'utils'
|
||||||
|
|
||||||
export type GroupsActions = {
|
export type GroupsActions = {
|
||||||
createGroup: (
|
createGroup: (
|
||||||
@ -69,7 +70,7 @@ const groupsActions = (
|
|||||||
const id = createId()
|
const id = createId()
|
||||||
const newGroup: Group = {
|
const newGroup: Group = {
|
||||||
...group,
|
...group,
|
||||||
title: `${group.title} copy`,
|
title: `${parseGroupTitle(group.title)} copy`,
|
||||||
id,
|
id,
|
||||||
blocks: group.blocks.map((block) =>
|
blocks: group.blocks.map((block) =>
|
||||||
duplicateBlockDraft(id)(block, onWebhookBlockDuplicated)
|
duplicateBlockDraft(id)(block, onWebhookBlockDuplicated)
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
useBlockDnd,
|
useBlockDnd,
|
||||||
} from '../../../providers'
|
} from '../../../providers'
|
||||||
import { BlockNodesList } from '../BlockNode/BlockNodesList'
|
import { BlockNodesList } from '../BlockNode/BlockNodesList'
|
||||||
import { isDefined, isNotDefined } from 'utils'
|
import { isDefined, isEmpty, isNotDefined } from 'utils'
|
||||||
import { useTypebot, RightPanel, useEditor } from '@/features/editor'
|
import { useTypebot, RightPanel, useEditor } from '@/features/editor'
|
||||||
import { GroupNodeContextMenu } from './GroupNodeContextMenu'
|
import { GroupNodeContextMenu } from './GroupNodeContextMenu'
|
||||||
import { useDebounce } from 'use-debounce'
|
import { useDebounce } from 'use-debounce'
|
||||||
@ -108,6 +108,7 @@ const NonMemoizedDraggableGroupNode = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setGroupTitle(group.title)
|
setGroupTitle(group.title)
|
||||||
}, [group.title])
|
}, [group.title])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!currentCoordinates || isReadOnly) return
|
if (!currentCoordinates || isReadOnly) return
|
||||||
if (
|
if (
|
||||||
@ -127,7 +128,7 @@ const NonMemoizedDraggableGroupNode = ({
|
|||||||
}, [connectingIds, group.id])
|
}, [connectingIds, group.id])
|
||||||
|
|
||||||
const handleTitleSubmit = (title: string) =>
|
const handleTitleSubmit = (title: string) =>
|
||||||
title.length > 0 ? updateGroup(groupIndex, { title }) : undefined
|
updateGroup(groupIndex, { title })
|
||||||
|
|
||||||
const handleMouseEnter = () => {
|
const handleMouseEnter = () => {
|
||||||
if (isReadOnly) return
|
if (isReadOnly) return
|
||||||
@ -226,6 +227,16 @@ const NonMemoizedDraggableGroupNode = ({
|
|||||||
}}
|
}}
|
||||||
px="1"
|
px="1"
|
||||||
userSelect={'none'}
|
userSelect={'none'}
|
||||||
|
style={
|
||||||
|
isEmpty(groupTitle)
|
||||||
|
? {
|
||||||
|
display: 'block',
|
||||||
|
position: 'absolute',
|
||||||
|
top: '10px',
|
||||||
|
width: '100px',
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<EditableInput minW="0" px="1" className="prevent-group-drag" />
|
<EditableInput minW="0" px="1" className="prevent-group-drag" />
|
||||||
</Editable>
|
</Editable>
|
||||||
|
@ -43,10 +43,7 @@ export const AvatarForm = ({
|
|||||||
|
|
||||||
useOutsideClick({
|
useOutsideClick({
|
||||||
ref: popoverContainerRef,
|
ref: popoverContainerRef,
|
||||||
handler: () => {
|
handler: onClose,
|
||||||
console.log('close')
|
|
||||||
onClose()
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const isDefaultAvatar = !avatarProps?.url || avatarProps.url.includes('{{')
|
const isDefaultAvatar = !avatarProps?.url || avatarProps.url.includes('{{')
|
||||||
|
@ -2,7 +2,7 @@ import { authenticateUser } from '@/features/auth/api'
|
|||||||
import prisma from '@/lib/prisma'
|
import prisma from '@/lib/prisma'
|
||||||
import { Group, WebhookBlock } from 'models'
|
import { Group, WebhookBlock } from 'models'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { byId, isWebhookBlock } from 'utils'
|
import { byId, isWebhookBlock, parseGroupTitle } from 'utils'
|
||||||
import { methodNotAllowed } from 'utils/api'
|
import { methodNotAllowed } from 'utils/api'
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
@ -27,7 +27,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
...emptyWebhookBlocks,
|
...emptyWebhookBlocks,
|
||||||
...blocks.map((b) => ({
|
...blocks.map((b) => ({
|
||||||
blockId: b.id,
|
blockId: b.id,
|
||||||
name: `${group.title} > ${b.id}`,
|
name: `${parseGroupTitle(group.title)} > ${b.id}`,
|
||||||
url: typebot?.webhooks.find(byId(b.webhookId))?.url ?? undefined,
|
url: typebot?.webhooks.find(byId(b.webhookId))?.url ?? undefined,
|
||||||
})),
|
})),
|
||||||
]
|
]
|
||||||
|
@ -2,7 +2,7 @@ import { authenticateUser } from '@/features/auth/api'
|
|||||||
import prisma from '@/lib/prisma'
|
import prisma from '@/lib/prisma'
|
||||||
import { Group, WebhookBlock } from 'models'
|
import { Group, WebhookBlock } from 'models'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { byId, isNotDefined, isWebhookBlock } from 'utils'
|
import { byId, isNotDefined, isWebhookBlock, parseGroupTitle } from 'utils'
|
||||||
import { methodNotAllowed } from 'utils/api'
|
import { methodNotAllowed } from 'utils/api'
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
@ -32,7 +32,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
...blocks.map((s) => ({
|
...blocks.map((s) => ({
|
||||||
id: s.id,
|
id: s.id,
|
||||||
groupId: s.groupId,
|
groupId: s.groupId,
|
||||||
name: `${group.title} > ${s.id}`,
|
name: `${parseGroupTitle(group.title)} > ${s.id}`,
|
||||||
})),
|
})),
|
||||||
]
|
]
|
||||||
}, [])
|
}, [])
|
||||||
|
@ -10,7 +10,13 @@ import {
|
|||||||
InputBlockType,
|
InputBlockType,
|
||||||
ResultInSession,
|
ResultInSession,
|
||||||
} from 'models'
|
} from 'models'
|
||||||
import { isInputBlock, isDefined, byId, isNotEmpty } from './utils'
|
import {
|
||||||
|
isInputBlock,
|
||||||
|
isDefined,
|
||||||
|
byId,
|
||||||
|
isNotEmpty,
|
||||||
|
parseGroupTitle,
|
||||||
|
} from './utils'
|
||||||
|
|
||||||
export const parseResultHeader = (
|
export const parseResultHeader = (
|
||||||
typebot: Pick<Typebot, 'groups' | 'variables'>,
|
typebot: Pick<Typebot, 'groups' | 'variables'>,
|
||||||
@ -55,7 +61,7 @@ const parseInputsResultHeader = ({
|
|||||||
.flatMap((group) =>
|
.flatMap((group) =>
|
||||||
group.blocks.map((block) => ({
|
group.blocks.map((block) => ({
|
||||||
...block,
|
...block,
|
||||||
groupTitle: group.title,
|
groupTitle: parseGroupTitle(group.title),
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
.filter((block) => isInputBlock(block)) as (InputBlock & {
|
.filter((block) => isInputBlock(block)) as (InputBlock & {
|
||||||
@ -80,7 +86,8 @@ const parseInputsResultHeader = ({
|
|||||||
if (
|
if (
|
||||||
existingHeader.blocks?.some(
|
existingHeader.blocks?.some(
|
||||||
(block) => block.groupId === inputBlock.groupId
|
(block) => block.groupId === inputBlock.groupId
|
||||||
)
|
) ||
|
||||||
|
existingHeader.label.includes('Untitled')
|
||||||
) {
|
) {
|
||||||
const totalPrevious = existingHeaders.filter((h) =>
|
const totalPrevious = existingHeaders.filter((h) =>
|
||||||
h.label.includes(label)
|
h.label.includes(label)
|
||||||
|
@ -305,3 +305,6 @@ export const getAtPath = <T>(obj: T, path: string): unknown => {
|
|||||||
}
|
}
|
||||||
return current
|
return current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const parseGroupTitle = (title: string) =>
|
||||||
|
isEmpty(title) ? 'Untitled' : title
|
||||||
|
Reference in New Issue
Block a user