build: 📦️ Update packages
This commit is contained in:
@ -87,7 +87,7 @@ export const CustomDomainsDropdown = ({
|
|||||||
textAlign="left"
|
textAlign="left"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Text isTruncated overflowY="visible" h="20px">
|
<Text noOfLines={0} overflowY="visible" h="20px">
|
||||||
{currentCustomDomain ?? 'Add my domain'}
|
{currentCustomDomain ?? 'Add my domain'}
|
||||||
</Text>
|
</Text>
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
|
@ -103,7 +103,7 @@ export const CredentialsDropdown = ({
|
|||||||
textAlign="left"
|
textAlign="left"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Text isTruncated overflowY="visible" h="20px">
|
<Text noOfLines={0} overflowY="visible" h="20px">
|
||||||
{currentCredential ? currentCredential.name : defaultCredentialsLabel}
|
{currentCredential ? currentCredential.name : defaultCredentialsLabel}
|
||||||
</Text>
|
</Text>
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
Stack,
|
Stack,
|
||||||
} from '@chakra-ui/react'
|
} from '@chakra-ui/react'
|
||||||
import { ChevronLeftIcon } from 'assets/icons'
|
import { ChevronLeftIcon } from 'assets/icons'
|
||||||
import React from 'react'
|
import React, { ReactNode } from 'react'
|
||||||
|
|
||||||
type Props<T> = {
|
type Props<T> = {
|
||||||
currentItem?: T
|
currentItem?: T
|
||||||
@ -39,8 +39,8 @@ export const DropdownList = <T,>({
|
|||||||
textAlign="left"
|
textAlign="left"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<chakra.span isTruncated display="block">
|
<chakra.span noOfLines={0} display="block">
|
||||||
{currentItem ?? placeholder}
|
{(currentItem ?? placeholder) as unknown as ReactNode}
|
||||||
</chakra.span>
|
</chakra.span>
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<Portal>
|
<Portal>
|
||||||
@ -55,7 +55,7 @@ export const DropdownList = <T,>({
|
|||||||
textOverflow="ellipsis"
|
textOverflow="ellipsis"
|
||||||
onClick={handleMenuItemClick(item)}
|
onClick={handleMenuItemClick(item)}
|
||||||
>
|
>
|
||||||
{item}
|
{item as unknown as ReactNode}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -22,7 +22,7 @@ export const ConditionNodeContent = ({ item }: Props) => {
|
|||||||
byId(comparison.variableId)
|
byId(comparison.variableId)
|
||||||
)
|
)
|
||||||
return (
|
return (
|
||||||
<Wrap key={comparison.id} spacing={1} isTruncated>
|
<Wrap key={comparison.id} spacing={1} noOfLines={0}>
|
||||||
{idx > 0 && <Text>{item.content.logicalOperator ?? ''}</Text>}
|
{idx > 0 && <Text>{item.content.logicalOperator ?? ''}</Text>}
|
||||||
{variable?.name && (
|
{variable?.name && (
|
||||||
<Tag bgColor="orange.400" color="white">
|
<Tag bgColor="orange.400" color="white">
|
||||||
@ -38,7 +38,7 @@ export const ConditionNodeContent = ({ item }: Props) => {
|
|||||||
)}
|
)}
|
||||||
{comparison?.value && (
|
{comparison?.value && (
|
||||||
<Tag bgColor={'gray.200'}>
|
<Tag bgColor={'gray.200'}>
|
||||||
<Text isTruncated>{comparison.value}</Text>
|
<Text noOfLines={0}>{comparison.value}</Text>
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
</Wrap>
|
</Wrap>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Flex, FlexProps } from '@chakra-ui/react'
|
import { Flex, FlexProps } from '@chakra-ui/react'
|
||||||
import { Item } from 'models'
|
import { Item } from 'models'
|
||||||
import React from 'react'
|
import React, { ReactNode } from 'react'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
item: Item
|
item: Item
|
||||||
@ -20,7 +20,7 @@ export const ItemNodeOverlay = ({ item, ...props }: Props) => {
|
|||||||
shadow="lg"
|
shadow="lg"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{item.content ?? 'Click to edit'}
|
{(item.content ?? 'Click to edit') as ReactNode}
|
||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { Text } from '@chakra-ui/react'
|
|||||||
type Props = { label?: string }
|
type Props = { label?: string }
|
||||||
|
|
||||||
export const ConfigureContent = ({ label }: Props) => (
|
export const ConfigureContent = ({ label }: Props) => (
|
||||||
<Text color={label ? 'currentcolor' : 'gray.500'} isTruncated>
|
<Text color={label ? 'currentcolor' : 'gray.500'} noOfLines={0}>
|
||||||
{label ?? 'Configure...'}
|
{label ?? 'Configure...'}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
|
@ -9,7 +9,7 @@ export const SendEmailContent = ({ step }: Props) => {
|
|||||||
if (step.options.recipients.length === 0)
|
if (step.options.recipients.length === 0)
|
||||||
return <Text color="gray.500">Configure...</Text>
|
return <Text color="gray.500">Configure...</Text>
|
||||||
return (
|
return (
|
||||||
<Wrap isTruncated pr="6">
|
<Wrap noOfLines={2} pr="6">
|
||||||
<WrapItem>
|
<WrapItem>
|
||||||
<Text>Send email to</Text>
|
<Text>Send email to</Text>
|
||||||
</WrapItem>
|
</WrapItem>
|
||||||
|
@ -9,7 +9,7 @@ export const SetVariableContent = ({ step }: { step: SetVariableStep }) => {
|
|||||||
typebot?.variables.find(byId(step.options.variableId))?.name ?? ''
|
typebot?.variables.find(byId(step.options.variableId))?.name ?? ''
|
||||||
const expression = step.options.expressionToEvaluate ?? ''
|
const expression = step.options.expressionToEvaluate ?? ''
|
||||||
return (
|
return (
|
||||||
<Text color={'gray.500'} isTruncated>
|
<Text color={'gray.500'} noOfLines={2}>
|
||||||
{variableName === '' && expression === ''
|
{variableName === '' && expression === ''
|
||||||
? 'Click to edit...'
|
? 'Click to edit...'
|
||||||
: `${variableName} ${expression ? `= ${expression}` : ``}`}
|
: `${variableName} ${expression ? `= ${expression}` : ``}`}
|
||||||
|
@ -13,7 +13,7 @@ export const WebhookContent = ({ step: { webhookId } }: Props) => {
|
|||||||
|
|
||||||
if (!webhook?.url) return <Text color="gray.500">Configure...</Text>
|
if (!webhook?.url) return <Text color="gray.500">Configure...</Text>
|
||||||
return (
|
return (
|
||||||
<Text isTruncated pr="6">
|
<Text noOfLines={2} pr="6">
|
||||||
{webhook.method} {webhook.url}
|
{webhook.method} {webhook.url}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
|
@ -36,7 +36,7 @@ export const ProviderWebhookContent = ({ step, configuredLabel }: Props) => {
|
|||||||
if (isNotDefined(webhook?.body))
|
if (isNotDefined(webhook?.body))
|
||||||
return <Text color="gray.500">Configure...</Text>
|
return <Text color="gray.500">Configure...</Text>
|
||||||
return (
|
return (
|
||||||
<Text isTruncated pr="6">
|
<Text noOfLines={0} pr="6">
|
||||||
{webhook?.url ? configuredLabel : 'Disabled'}
|
{webhook?.url ? configuredLabel : 'Disabled'}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
|
@ -4,11 +4,13 @@ import {
|
|||||||
Plate,
|
Plate,
|
||||||
selectEditor,
|
selectEditor,
|
||||||
serializeHtml,
|
serializeHtml,
|
||||||
TDescendant,
|
TEditor,
|
||||||
|
TElement,
|
||||||
|
Value,
|
||||||
withPlate,
|
withPlate,
|
||||||
} from '@udecode/plate-core'
|
} from '@udecode/plate-core'
|
||||||
import { editorStyle, platePlugins } from 'libs/plate'
|
import { editorStyle, platePlugins } from 'libs/plate'
|
||||||
import { BaseSelection, createEditor, Transforms } from 'slate'
|
import { BaseEditor, BaseSelection, createEditor, Transforms } from 'slate'
|
||||||
import { ToolBar } from './ToolBar'
|
import { ToolBar } from './ToolBar'
|
||||||
import { parseHtmlStringToPlainText } from 'services/utils'
|
import { parseHtmlStringToPlainText } from 'services/utils'
|
||||||
import { defaultTextBubbleContent, TextBubbleContent, Variable } from 'models'
|
import { defaultTextBubbleContent, TextBubbleContent, Variable } from 'models'
|
||||||
@ -16,7 +18,7 @@ import { VariableSearchInput } from 'components/shared/VariableSearchInput'
|
|||||||
import { ReactEditor } from 'slate-react'
|
import { ReactEditor } from 'slate-react'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
initialValue: TDescendant[]
|
initialValue: TElement[]
|
||||||
onClose: (newContent: TextBubbleContent) => void
|
onClose: (newContent: TextBubbleContent) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +26,10 @@ export const TextBubbleEditor = ({ initialValue, onClose }: Props) => {
|
|||||||
const randomEditorId = useMemo(() => Math.random().toString(), [])
|
const randomEditorId = useMemo(() => Math.random().toString(), [])
|
||||||
const editor = useMemo(
|
const editor = useMemo(
|
||||||
() =>
|
() =>
|
||||||
withPlate(createEditor(), { id: randomEditorId, plugins: platePlugins }),
|
withPlate(createEditor() as TEditor<Value>, {
|
||||||
|
id: randomEditorId,
|
||||||
|
plugins: platePlugins,
|
||||||
|
}),
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
@ -65,7 +70,7 @@ export const TextBubbleEditor = ({ initialValue, onClose }: Props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const convertValueToStepContent = (value: unknown[]): TextBubbleContent => {
|
const convertValueToStepContent = (value: TElement[]): TextBubbleContent => {
|
||||||
if (value.length === 0) defaultTextBubbleContent
|
if (value.length === 0) defaultTextBubbleContent
|
||||||
const html = serializeHtml(editor, {
|
const html = serializeHtml(editor, {
|
||||||
nodes: value,
|
nodes: value,
|
||||||
@ -84,12 +89,12 @@ export const TextBubbleEditor = ({ initialValue, onClose }: Props) => {
|
|||||||
const handleVariableSelected = (variable?: Variable) => {
|
const handleVariableSelected = (variable?: Variable) => {
|
||||||
setIsVariableDropdownOpen(false)
|
setIsVariableDropdownOpen(false)
|
||||||
if (!rememberedSelection.current || !variable) return
|
if (!rememberedSelection.current || !variable) return
|
||||||
Transforms.select(editor, rememberedSelection.current)
|
Transforms.select(editor as BaseEditor, rememberedSelection.current)
|
||||||
Transforms.insertText(editor, '{{' + variable.name + '}}')
|
Transforms.insertText(editor as BaseEditor, '{{' + variable.name + '}}')
|
||||||
ReactEditor.focus(editor as unknown as ReactEditor)
|
ReactEditor.focus(editor as unknown as ReactEditor)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleChangeEditorContent = (val: unknown[]) => {
|
const handleChangeEditorContent = (val: TElement[]) => {
|
||||||
setValue(val)
|
setValue(val)
|
||||||
setIsVariableDropdownOpen(false)
|
setIsVariableDropdownOpen(false)
|
||||||
}
|
}
|
||||||
@ -110,7 +115,10 @@ export const TextBubbleEditor = ({ initialValue, onClose }: Props) => {
|
|||||||
spacing={0}
|
spacing={0}
|
||||||
cursor="text"
|
cursor="text"
|
||||||
>
|
>
|
||||||
<ToolBar onVariablesButtonClick={() => setIsVariableDropdownOpen(true)} />
|
<ToolBar
|
||||||
|
editor={editor}
|
||||||
|
onVariablesButtonClick={() => setIsVariableDropdownOpen(true)}
|
||||||
|
/>
|
||||||
<Plate
|
<Plate
|
||||||
id={randomEditorId}
|
id={randomEditorId}
|
||||||
editableProps={{
|
editableProps={{
|
||||||
|
@ -4,18 +4,24 @@ import {
|
|||||||
MARK_ITALIC,
|
MARK_ITALIC,
|
||||||
MARK_UNDERLINE,
|
MARK_UNDERLINE,
|
||||||
} from '@udecode/plate-basic-marks'
|
} from '@udecode/plate-basic-marks'
|
||||||
import { usePlateEditorRef, getPluginType } from '@udecode/plate-core'
|
import { getPluginType, PlateEditor, Value } from '@udecode/plate-core'
|
||||||
import { LinkToolbarButton } from '@udecode/plate-ui-link'
|
import { LinkToolbarButton } from '@udecode/plate-ui-link'
|
||||||
import { MarkToolbarButton } from '@udecode/plate-ui-toolbar'
|
import { MarkToolbarButton } from '@udecode/plate-ui-toolbar'
|
||||||
import { BoldIcon, ItalicIcon, UnderlineIcon, LinkIcon } from 'assets/icons'
|
import { BoldIcon, ItalicIcon, UnderlineIcon, LinkIcon } from 'assets/icons'
|
||||||
|
|
||||||
type Props = { onVariablesButtonClick: () => void } & StackProps
|
type Props = {
|
||||||
export const ToolBar = (props: Props) => {
|
editor: PlateEditor<Value>
|
||||||
const editor = usePlateEditorRef()
|
onVariablesButtonClick: () => void
|
||||||
|
} & StackProps
|
||||||
|
|
||||||
|
export const ToolBar = ({
|
||||||
|
editor,
|
||||||
|
onVariablesButtonClick,
|
||||||
|
...props
|
||||||
|
}: Props) => {
|
||||||
const handleVariablesButtonMouseDown = (e: React.MouseEvent) => {
|
const handleVariablesButtonMouseDown = (e: React.MouseEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
props.onVariablesButtonClick()
|
onVariablesButtonClick()
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<HStack
|
<HStack
|
||||||
|
@ -88,7 +88,11 @@ export const CollaboratorIdentityContent = ({
|
|||||||
{name}
|
{name}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
<Text color="gray.500" fontSize={name ? '14px' : 'inherit'} isTruncated>
|
<Text
|
||||||
|
color="gray.500"
|
||||||
|
fontSize={name ? '14px' : 'inherit'}
|
||||||
|
noOfLines={0}
|
||||||
|
>
|
||||||
{email}
|
{email}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -18,7 +18,7 @@ export const EditableTypebotName = ({ name, onNewName }: EditableProps) => {
|
|||||||
<Tooltip label="Rename">
|
<Tooltip label="Rename">
|
||||||
<Editable value={localName} onChange={setLocalName} onSubmit={onNewName}>
|
<Editable value={localName} onChange={setLocalName} onSubmit={onNewName}>
|
||||||
<EditablePreview
|
<EditablePreview
|
||||||
isTruncated
|
noOfLines={0}
|
||||||
cursor="pointer"
|
cursor="pointer"
|
||||||
maxW="200px"
|
maxW="200px"
|
||||||
overflow="hidden"
|
overflow="hidden"
|
||||||
|
@ -134,7 +134,7 @@ export const SubmissionsContent = ({
|
|||||||
() =>
|
() =>
|
||||||
publishedTypebot ? convertResultsToTableData(results, resultHeader) : [],
|
publishedTypebot ? convertResultsToTableData(results, resultHeader) : [],
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[results]
|
[publishedTypebot?.id, resultHeader.length, results]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleLogsModalClose = () => setInspectingLogsResultId(undefined)
|
const handleLogsModalClose = () => setInspectingLogsResultId(undefined)
|
||||||
|
@ -15,30 +15,30 @@
|
|||||||
"workerDirectory": "public"
|
"workerDirectory": "public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/css-reset": "^1.1.3",
|
"@chakra-ui/css-reset": "^2.0.0",
|
||||||
"@chakra-ui/react": "^1.8.7",
|
"@chakra-ui/react": "^2.0.0",
|
||||||
"@codemirror/basic-setup": "^0.19.3",
|
"@codemirror/basic-setup": "^0.20.0",
|
||||||
"@codemirror/lang-css": "^0.19.3",
|
"@codemirror/lang-css": "^0.20.0",
|
||||||
"@codemirror/lang-html": "^0.19.4",
|
"@codemirror/lang-html": "^0.20.0",
|
||||||
"@codemirror/lang-javascript": "^0.19.7",
|
"@codemirror/lang-javascript": "^0.20.0",
|
||||||
"@codemirror/lang-json": "^0.19.2",
|
"@codemirror/lang-json": "^0.20.0",
|
||||||
"@codemirror/text": "^0.19.6",
|
"@codemirror/text": "^0.19.6",
|
||||||
"@emotion/react": "^11.8.2",
|
"@emotion/react": "^11.9.0",
|
||||||
"@emotion/styled": "^11.8.1",
|
"@emotion/styled": "^11.8.1",
|
||||||
"@giphy/js-fetch-api": "^4.1.2",
|
"@giphy/js-fetch-api": "^4.1.2",
|
||||||
"@giphy/js-types": "^4.1.0",
|
"@giphy/js-types": "^4.1.0",
|
||||||
"@giphy/react-components": "^5.6.1",
|
"@giphy/react-components": "^5.7.0",
|
||||||
"@googleapis/drive": "^2.3.0",
|
"@googleapis/drive": "^2.3.0",
|
||||||
"@sentry/nextjs": "^6.19.3",
|
"@sentry/nextjs": "^6.19.7",
|
||||||
"@stripe/stripe-js": "^1.26.0",
|
"@stripe/stripe-js": "^1.29.0",
|
||||||
"@udecode/plate-basic-marks": "^10.5.0",
|
"@udecode/plate-basic-marks": "^11.0.0",
|
||||||
"@udecode/plate-common": "^7.0.2",
|
"@udecode/plate-common": "^7.0.2",
|
||||||
"@udecode/plate-core": "^10.5.0",
|
"@udecode/plate-core": "^11.0.0",
|
||||||
"@udecode/plate-link": "^10.5.0",
|
"@udecode/plate-link": "^11.0.0",
|
||||||
"@udecode/plate-ui-link": "^10.5.0",
|
"@udecode/plate-ui-link": "^11.0.0",
|
||||||
"@udecode/plate-ui-toolbar": "^10.5.0",
|
"@udecode/plate-ui-toolbar": "^11.0.0",
|
||||||
"bot-engine": "*",
|
"bot-engine": "*",
|
||||||
"browser-image-compression": "^1.0.17",
|
"browser-image-compression": "^2.0.0",
|
||||||
"canvas-confetti": "^1.5.1",
|
"canvas-confetti": "^1.5.1",
|
||||||
"cuid": "^2.1.8",
|
"cuid": "^2.1.8",
|
||||||
"db": "*",
|
"db": "*",
|
||||||
@ -46,65 +46,65 @@
|
|||||||
"dequal": "^2.0.2",
|
"dequal": "^2.0.2",
|
||||||
"emoji-mart": "^3.0.1",
|
"emoji-mart": "^3.0.1",
|
||||||
"focus-visible": "^5.2.0",
|
"focus-visible": "^5.2.0",
|
||||||
"framer-motion": "6.2.8",
|
"framer-motion": "6.3.3",
|
||||||
"google-auth-library": "^7.14.1",
|
"google-auth-library": "^8.0.2",
|
||||||
"google-spreadsheet": "^3.2.0",
|
"google-spreadsheet": "^3.2.0",
|
||||||
"got": "^12.0.3",
|
"got": "^12.0.4",
|
||||||
"htmlparser2": "^7.2.0",
|
"htmlparser2": "^8.0.1",
|
||||||
"immer": "^9.0.12",
|
"immer": "^9.0.14",
|
||||||
"js-video-url-parser": "^0.5.1",
|
"js-video-url-parser": "^0.5.1",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"kbar": "^0.1.0-beta.33",
|
"kbar": "^0.1.0-beta.34",
|
||||||
"micro": "^9.3.4",
|
"micro": "^9.3.4",
|
||||||
"micro-cors": "^0.1.1",
|
"micro-cors": "^0.1.1",
|
||||||
"minio": "^7.0.26",
|
"minio": "^7.0.28",
|
||||||
"models": "*",
|
"models": "*",
|
||||||
"next": "^12.1.4",
|
"next": "^12.1.6",
|
||||||
"next-auth": "4.3.1",
|
"next-auth": "4.3.4",
|
||||||
"nodemailer": "^6.7.3",
|
"nodemailer": "^6.7.5",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"papaparse": "^5.3.2",
|
"papaparse": "^5.3.2",
|
||||||
"prettier": "^2.6.1",
|
"prettier": "^2.6.2",
|
||||||
"react": "^17.0.2",
|
"react": "^18.1.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^18.1.0",
|
||||||
"react-draggable": "^4.4.4",
|
"react-draggable": "^4.4.5",
|
||||||
"react-table": "^7.7.0",
|
"react-table": "^7.7.0",
|
||||||
"slate": "^0.76.0",
|
"slate": "^0.78.0",
|
||||||
"slate-history": "^0.66.0",
|
"slate-history": "^0.66.0",
|
||||||
"slate-hyperscript": "^0.67.0",
|
"slate-hyperscript": "^0.77.0",
|
||||||
"slate-react": "^0.76.0",
|
"slate-react": "^0.79.0",
|
||||||
"stripe": "^8.215.0",
|
"stripe": "^9.1.0",
|
||||||
"styled-components": "^5.3.5",
|
"styled-components": "^5.3.5",
|
||||||
"svg-round-corners": "^0.3.0",
|
"svg-round-corners": "^0.3.0",
|
||||||
"swr": "^1.2.2",
|
"swr": "^1.3.0",
|
||||||
"tinycolor2": "^1.4.2",
|
"tinycolor2": "^1.4.2",
|
||||||
"typebot-js": "*",
|
"typebot-js": "*",
|
||||||
"use-debounce": "^7.0.1",
|
"use-debounce": "^8.0.1",
|
||||||
"utils": "*",
|
"utils": "*",
|
||||||
"qs": "^6.10.3"
|
"qs": "^6.10.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.20.2",
|
"@playwright/test": "^1.22.0",
|
||||||
"@types/canvas-confetti": "^1.4.2",
|
"@types/canvas-confetti": "^1.4.2",
|
||||||
"@types/emoji-mart": "^3.0.9",
|
"@types/emoji-mart": "^3.0.9",
|
||||||
"@types/google-spreadsheet": "^3.2.0",
|
"@types/google-spreadsheet": "^3.2.1",
|
||||||
"@types/jsonwebtoken": "8.5.8",
|
"@types/jsonwebtoken": "8.5.8",
|
||||||
"@types/micro-cors": "^0.1.2",
|
"@types/micro-cors": "^0.1.2",
|
||||||
"@types/minio": "^7.0.12",
|
"@types/minio": "^7.0.13",
|
||||||
"@types/node": "^17.0.23",
|
"@types/node": "^17.0.33",
|
||||||
"@types/nodemailer": "^6.4.4",
|
"@types/nodemailer": "^6.4.4",
|
||||||
"@types/nprogress": "^0.2.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
"@types/papaparse": "^5.3.2",
|
"@types/papaparse": "^5.3.2",
|
||||||
"@types/prettier": "^2.4.4",
|
"@types/prettier": "^2.6.1",
|
||||||
"@types/react": "^17.0.43",
|
"@types/react": "^18.0.9",
|
||||||
"@types/react-table": "^7.7.10",
|
"@types/react-table": "^7.7.12",
|
||||||
"@types/tinycolor2": "^1.4.3",
|
"@types/tinycolor2": "^1.4.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.17.0",
|
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.1",
|
||||||
"eslint": "<8.0.0",
|
"eslint": "<8.0.0",
|
||||||
"eslint-config-next": "12.1.4",
|
"eslint-config-next": "12.1.6",
|
||||||
"msw": "^0.39.2",
|
"msw": "^0.39.2",
|
||||||
"typescript": "^4.6.3",
|
"typescript": "^4.6.4",
|
||||||
"@types/qs": "^6.9.7"
|
"@types/qs": "^6.9.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { drive } from '@googleapis/drive'
|
import { drive } from '@googleapis/drive'
|
||||||
|
import { OAuth2Client } from 'googleapis-common'
|
||||||
import { getAuthenticatedGoogleClient } from 'libs/google-sheets'
|
import { getAuthenticatedGoogleClient } from 'libs/google-sheets'
|
||||||
import { badRequest, methodNotAllowed, notAuthenticated } from 'utils'
|
import { badRequest, methodNotAllowed, notAuthenticated } from 'utils'
|
||||||
import { setUser, withSentry } from '@sentry/nextjs'
|
import { setUser, withSentry } from '@sentry/nextjs'
|
||||||
@ -18,7 +19,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
return res.status(404).send("Couldn't find credentials in database")
|
return res.status(404).send("Couldn't find credentials in database")
|
||||||
const response = await drive({
|
const response = await drive({
|
||||||
version: 'v3',
|
version: 'v3',
|
||||||
auth: auth.client,
|
auth: auth.client as unknown as OAuth2Client,
|
||||||
}).files.list({
|
}).files.list({
|
||||||
q: "mimeType='application/vnd.google-apps.spreadsheet'",
|
q: "mimeType='application/vnd.google-apps.spreadsheet'",
|
||||||
fields: 'nextPageToken, files(id, name)',
|
fields: 'nextPageToken, files(id, name)',
|
||||||
|
@ -21,7 +21,7 @@ test.describe.parallel('Settings page', () => {
|
|||||||
typebotViewer(page).locator('a:has-text("Made with Typebot")')
|
typebotViewer(page).locator('a:has-text("Made with Typebot")')
|
||||||
).toHaveAttribute('href', 'https://www.typebot.io/?utm_source=litebadge')
|
).toHaveAttribute('href', 'https://www.typebot.io/?utm_source=litebadge')
|
||||||
await page.click('button:has-text("General")')
|
await page.click('button:has-text("General")')
|
||||||
await page.uncheck('input[type="checkbox"]', { force: true })
|
await page.click('text="Typebot.io branding"')
|
||||||
await expect(
|
await expect(
|
||||||
typebotViewer(page).locator('a:has-text("Made with Typebot")')
|
typebotViewer(page).locator('a:has-text("Made with Typebot")')
|
||||||
).toBeHidden()
|
).toBeHidden()
|
||||||
@ -59,9 +59,7 @@ test.describe.parallel('Settings page', () => {
|
|||||||
await page.click('button:has-text("Typing emulation")')
|
await page.click('button:has-text("Typing emulation")')
|
||||||
await page.fill('[data-testid="speed"] input', '350')
|
await page.fill('[data-testid="speed"] input', '350')
|
||||||
await page.fill('[data-testid="max-delay"] input', '1.5')
|
await page.fill('[data-testid="max-delay"] input', '1.5')
|
||||||
await page.uncheck('input[type="checkbox"] >> nth=-1', {
|
await page.click('text="Typing emulation" >> nth=-1')
|
||||||
force: true,
|
|
||||||
})
|
|
||||||
await expect(page.locator('[data-testid="speed"]')).toBeHidden()
|
await expect(page.locator('[data-testid="speed"]')).toBeHidden()
|
||||||
await expect(page.locator('[data-testid="max-delay"]')).toBeHidden()
|
await expect(page.locator('[data-testid="max-delay"]')).toBeHidden()
|
||||||
})
|
})
|
||||||
|
@ -23,8 +23,8 @@
|
|||||||
"clsx": "^1.1.1",
|
"clsx": "^1.1.1",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"prism-react-renderer": "^1.3.1",
|
"prism-react-renderer": "^1.3.1",
|
||||||
"react": "^17.0.2",
|
"react": "^18.1.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^18.1.0",
|
||||||
"url-loader": "^4.1.1"
|
"url-loader": "^4.1.1"
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
|
@ -8,30 +8,29 @@
|
|||||||
"analyze": "cross-env ANALYZE=true next build"
|
"analyze": "cross-env ANALYZE=true next build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@chakra-ui/react": "^1.8.7",
|
"@chakra-ui/react": "^2.0.0",
|
||||||
"@emotion/react": "^11.8.2",
|
"@emotion/react": "^11.9.0",
|
||||||
"@emotion/styled": "^11.8.1",
|
"@emotion/styled": "^11.8.1",
|
||||||
"aos": "^2.3.4",
|
"aos": "^2.3.4",
|
||||||
"bot-engine": "*",
|
"bot-engine": "*",
|
||||||
"focus-visible": "^5.2.0",
|
"focus-visible": "^5.2.0",
|
||||||
"framer-motion": "^6.2.8",
|
"framer-motion": "^6.3.3",
|
||||||
"keen-slider": "^6.6.5",
|
|
||||||
"models": "*",
|
"models": "*",
|
||||||
"next": "^12.1.4",
|
"next": "^12.1.6",
|
||||||
"react": "17.0.2",
|
"react": "18.1.0",
|
||||||
"react-dom": "17.0.2",
|
"react-dom": "18.1.0",
|
||||||
"typebot-js": "*",
|
"typebot-js": "*",
|
||||||
"utils": "*"
|
"utils": "*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/bundle-analyzer": "^12.1.4",
|
"@next/bundle-analyzer": "^12.1.6",
|
||||||
"@types/aos": "^3.0.4",
|
"@types/aos": "^3.0.4",
|
||||||
"@types/node": "^17.0.23",
|
"@types/node": "^17.0.33",
|
||||||
"@types/react": "^17.0.43",
|
"@types/react": "^18.0.9",
|
||||||
"autoprefixer": "^10.4.4",
|
"autoprefixer": "^10.4.7",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "<8.0.0",
|
"eslint": "<8.0.0",
|
||||||
"prettier": "^2.6.1",
|
"prettier": "^2.6.2",
|
||||||
"typescript": "^4.6.3"
|
"typescript": "^4.6.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,35 +12,35 @@
|
|||||||
"test:open": "PWDEBUG=1 yarn playwright test"
|
"test:open": "PWDEBUG=1 yarn playwright test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sentry/nextjs": "^6.19.3",
|
"@sentry/nextjs": "^6.19.7",
|
||||||
"bot-engine": "*",
|
"bot-engine": "*",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"cuid": "^2.1.8",
|
"cuid": "^2.1.8",
|
||||||
"db": "*",
|
"db": "*",
|
||||||
"google-spreadsheet": "^3.2.0",
|
"google-spreadsheet": "^3.2.0",
|
||||||
"got": "^12.0.3",
|
"got": "^12.0.4",
|
||||||
"models": "*",
|
"models": "*",
|
||||||
"next": "^12.1.4",
|
"next": "^12.1.6",
|
||||||
"nodemailer": "^6.7.3",
|
"nodemailer": "^6.7.5",
|
||||||
"qs": "^6.10.3",
|
"qs": "^6.10.3",
|
||||||
"react": "^17.0.2",
|
"react": "^18.1.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^18.1.0",
|
||||||
"sanitize-html": "^2.7.0",
|
"sanitize-html": "^2.7.0",
|
||||||
"utils": "*"
|
"utils": "*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.20.2",
|
"@playwright/test": "^1.22.0",
|
||||||
"@types/cors": "^2.8.12",
|
"@types/cors": "^2.8.12",
|
||||||
"@types/google-spreadsheet": "^3.2.0",
|
"@types/google-spreadsheet": "^3.2.1",
|
||||||
"@types/node": "^17.0.23",
|
"@types/node": "^17.0.33",
|
||||||
"@types/nodemailer": "^6.4.4",
|
"@types/nodemailer": "^6.4.4",
|
||||||
"@types/qs": "^6.9.7",
|
"@types/qs": "^6.9.7",
|
||||||
"@types/react": "^17.0.43",
|
"@types/react": "^18.0.9",
|
||||||
"@types/sanitize-html": "^2.6.2",
|
"@types/sanitize-html": "^2.6.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.17.0",
|
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
||||||
"eslint": "<8.0.0",
|
"eslint": "<8.0.0",
|
||||||
"eslint-config-next": "12.1.4",
|
"eslint-config-next": "12.1.6",
|
||||||
"next-transpile-modules": "^9.0.0",
|
"next-transpile-modules": "^9.0.0",
|
||||||
"typescript": "^4.6.3"
|
"typescript": "^4.6.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
"db:migrate": "yarn workspace db migration:deploy"
|
"db:migrate": "yarn workspace db migration:deploy"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"turbo": "^1.1.10"
|
"turbo": "^1.2.9"
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.17"
|
"packageManager": "yarn@1.22.17"
|
||||||
}
|
}
|
||||||
|
@ -10,40 +10,40 @@
|
|||||||
"models": "*",
|
"models": "*",
|
||||||
"qs": "^6.10.3",
|
"qs": "^6.10.3",
|
||||||
"react-frame-component": "5.2.2-alpha.1",
|
"react-frame-component": "5.2.2-alpha.1",
|
||||||
"react-phone-number-input": "^3.1.49",
|
"react-phone-number-input": "^3.1.52",
|
||||||
"react-scroll": "^1.8.6",
|
"react-scroll": "^1.8.7",
|
||||||
"react-transition-group": "^4.4.2",
|
"react-transition-group": "^4.4.2",
|
||||||
"utils": "*"
|
"utils": "*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^21.0.3",
|
"@rollup/plugin-commonjs": "^22.0.0",
|
||||||
"@rollup/plugin-json": "^4.1.0",
|
"@rollup/plugin-json": "^4.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^13.1.3",
|
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||||
"@rollup/plugin-typescript": "^8.3.1",
|
"@rollup/plugin-typescript": "^8.3.2",
|
||||||
"@types/react": "^17.0.43",
|
"@types/react": "^18.0.9",
|
||||||
"@types/react-phone-number-input": "^3.0.13",
|
"@types/react-phone-number-input": "^3.0.13",
|
||||||
"@types/react-scroll": "^1.8.3",
|
"@types/react-scroll": "^1.8.3",
|
||||||
"@types/react-transition-group": "^4.4.4",
|
"@types/react-transition-group": "^4.4.4",
|
||||||
"autoprefixer": "^10.4.4",
|
"autoprefixer": "^10.4.7",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"postcss": "^8.4.12",
|
"postcss": "^8.4.13",
|
||||||
"rollup": "^2.70.1",
|
"rollup": "^2.72.1",
|
||||||
"rollup-plugin-dts": "^4.2.0",
|
"rollup-plugin-dts": "^4.2.1",
|
||||||
"rollup-plugin-peer-deps-external": "^2.2.4",
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||||
"rollup-plugin-postcss": "^4.0.2",
|
"rollup-plugin-postcss": "^4.0.2",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"tailwindcss": "^3.0.23",
|
"tailwindcss": "^3.0.24",
|
||||||
"typescript": "^4.6.3",
|
"typescript": "^4.6.4",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.17.0",
|
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
||||||
"eslint": "<8.0.0",
|
"eslint": "<8.0.0",
|
||||||
"eslint-config-next": "12.1.4",
|
"eslint-config-next": "12.1.6",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"tslib": "^2.3.1",
|
"tslib": "^2.4.0",
|
||||||
"@types/qs": "^6.9.7"
|
"@types/qs": "^6.9.7"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^17.0.2"
|
"react": "^18.1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn rollup -c",
|
"build": "yarn rollup -c",
|
||||||
|
@ -5,13 +5,13 @@
|
|||||||
"main": "./index.ts",
|
"main": "./index.ts",
|
||||||
"types": "./index.ts",
|
"types": "./index.ts",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"prisma": "^3.12.0",
|
"prisma": "^3.14.0",
|
||||||
"ts-node": "^10.7.0",
|
"ts-node": "^10.7.0",
|
||||||
"typescript": "^4.6.3",
|
"typescript": "^4.6.4",
|
||||||
"dotenv-cli": "5.1.0"
|
"dotenv-cli": "5.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^3.12.0"
|
"@prisma/client": "^3.14.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dx": "dotenv -e ../../apps/builder/.env.local prisma db push && yarn generate:schema && yarn start:sutdio ",
|
"dx": "dotenv -e ../../apps/builder/.env.local prisma db push && yarn generate:schema && yarn start:sutdio ",
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^4.6.3"
|
"typescript": "^4.6.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "^12.1.4",
|
"next": "^12.1.6",
|
||||||
"db": "*"
|
"db": "*"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { StepBase } from '.'
|
import { StepBase } from '.'
|
||||||
|
import { TElement } from '@udecode/plate-core'
|
||||||
|
|
||||||
export type BubbleStep =
|
export type BubbleStep =
|
||||||
| TextBubbleStep
|
| TextBubbleStep
|
||||||
@ -41,7 +42,7 @@ export type EmbedBubbleStep = StepBase & {
|
|||||||
|
|
||||||
export type TextBubbleContent = {
|
export type TextBubbleContent = {
|
||||||
html: string
|
html: string
|
||||||
richText: unknown[]
|
richText: TElement[]
|
||||||
plainText: string
|
plainText: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,9 +12,6 @@
|
|||||||
"db": "*",
|
"db": "*",
|
||||||
"models": "*",
|
"models": "*",
|
||||||
"utils": "*",
|
"utils": "*",
|
||||||
"ts-node": "^10.5.0"
|
"ts-node": "^10.7.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"firebase-admin": "^10.0.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,20 +10,23 @@
|
|||||||
"test": "yarn jest"
|
"test": "yarn jest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-typescript": "^8.3.1",
|
"@rollup/plugin-typescript": "^8.3.2",
|
||||||
"@types/jest": "^27.4.1",
|
"@types/jest": "^27.5.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.17.0",
|
"@typescript-eslint/eslint-plugin": "^5.23.0",
|
||||||
"@typescript-eslint/parser": "^5.17.0",
|
"@typescript-eslint/parser": "^5.23.0",
|
||||||
"eslint": "<8.0.0",
|
"eslint": "<8.0.0",
|
||||||
"eslint-plugin-functional": "^4.2.0",
|
"eslint-plugin-functional": "^4.2.1",
|
||||||
"eslint-plugin-jest": "^26.1.3",
|
"eslint-plugin-jest": "^26.1.5",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"jest": "^27.5.1",
|
"jest": "^28.1.0",
|
||||||
"rollup": "^2.70.1",
|
"rollup": "^2.72.1",
|
||||||
"rollup-plugin-styles": "^4.0.0",
|
"rollup-plugin-styles": "^4.0.0",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"ts-jest": "^27.1.4",
|
"ts-jest": "^28.0.2",
|
||||||
"ts-loader": "^9.2.8",
|
"ts-loader": "^9.3.0",
|
||||||
"typescript": "^4.6.3"
|
"typescript": "^4.6.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jest-environment-jsdom": "^28.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,18 @@
|
|||||||
"module": "dist/esm/index.js",
|
"module": "dist/esm/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^21.0.3",
|
"@rollup/plugin-commonjs": "^22.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^13.1.3",
|
"@rollup/plugin-node-resolve": "^13.3.0",
|
||||||
"@rollup/plugin-typescript": "^8.3.1",
|
"@rollup/plugin-typescript": "^8.3.2",
|
||||||
"rollup": "^2.70.1",
|
"rollup": "^2.72.1",
|
||||||
"rollup-plugin-dts": "^4.2.0",
|
"rollup-plugin-dts": "^4.2.1",
|
||||||
"rollup-plugin-peer-deps-external": "^2.2.4",
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
||||||
"tslib": "^2.3.1",
|
"tslib": "^2.4.0",
|
||||||
"typescript": "^4.6.3"
|
"typescript": "^4.6.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"models": "*",
|
"models": "*",
|
||||||
"next": "^12.1.4"
|
"next": "^12.1.6"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn rollup -c",
|
"build": "yarn rollup -c",
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"author": "baptisteArno",
|
"author": "baptisteArno",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@prettier/plugin-php": "^0.18.3"
|
"@prettier/plugin-php": "^0.18.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"deploy": "yarn copy && yarn commit",
|
"deploy": "yarn copy && yarn commit",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"pipeline": {
|
"pipeline": {
|
||||||
"build": {
|
"build": {
|
||||||
"dependsOn": ["^build"],
|
"dependsOn": ["^build"],
|
||||||
"outputs": [".next/**", "dist/**"]
|
"outputs": [".next/**", "dist/**", "build/**"]
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"dependsOn": [],
|
"dependsOn": [],
|
||||||
|
Reference in New Issue
Block a user