♻️ Migrate Alert components to Base UI

This commit is contained in:
Baptiste Arnaud 2025-10-15 15:52:53 +02:00
parent 5cdc3a9c23
commit 47472b58f1
No known key found for this signature in database
32 changed files with 408 additions and 251 deletions

View File

@ -4,6 +4,10 @@
@tailwind components;
@tailwind utilities;
:root {
--spacing: 0.25rem;
}
h1 {
@apply text-4xl md:text-6xl;
}

View File

@ -1,8 +0,0 @@
import { Alert, AlertIcon, type AlertProps } from "@chakra-ui/react";
export const AlertInfo = (props: AlertProps) => (
<Alert status="info" rounded="md" {...props}>
<AlertIcon />
{props.children}
</Alert>
);

View File

@ -1,6 +1,4 @@
import {
Alert,
AlertIcon,
Box,
Flex,
Grid,
@ -15,6 +13,8 @@ import {
} from "@chakra-ui/react";
import { env } from "@typebot.io/env";
import { isDefined } from "@typebot.io/lib/utils";
import { Alert } from "@typebot.io/ui/components/Alert";
import { TriangleAlertIcon } from "@typebot.io/ui/icons/TriangleAlertIcon";
import { useCallback, useEffect, useRef, useState } from "react";
import { createApi } from "unsplash-js";
import type { Basic as UnsplashPhoto } from "unsplash-js/dist/methods/photos/types";
@ -153,10 +153,10 @@ export const UnsplashPicker = ({ imageSize, onImageSelect }: Props) => {
</Link>
</HStack>
{isDefined(error) && (
<Alert status="error">
<AlertIcon />
{error}
</Alert>
<Alert.Root variant="error">
<TriangleAlertIcon />
<Alert.Description>{error}</Alert.Description>
</Alert.Root>
)}
<Stack overflowY="auto" maxH="400px" ref={scrollContainer}>
{images.length > 0 && (

View File

@ -1,40 +0,0 @@
import {
Alert,
AlertIcon,
type AlertProps,
HStack,
Text,
useDisclosure,
} from "@chakra-ui/react";
import type { ChangePlanDialogProps } from "@/features/billing/components/ChangePlanDialog";
import { ChangePlanDialog } from "@/features/billing/components/ChangePlanDialog";
type Props = AlertProps & Pick<ChangePlanDialogProps, "type" | "excludedPlans">;
export const UnlockPlanAlertInfo = ({
type,
excludedPlans,
...props
}: Props) => {
const { isOpen, onClose } = useDisclosure();
return (
<Alert
status="info"
rounded="md"
justifyContent="space-between"
flexShrink={0}
{...props}
>
<HStack>
<AlertIcon />
<Text>{props.children}</Text>
</HStack>
<ChangePlanDialog
isOpen={isOpen}
onClose={onClose}
type={type}
excludedPlans={excludedPlans}
/>
</Alert>
);
};

View File

@ -1,6 +1,4 @@
import {
Alert,
AlertIcon,
Box,
Flex,
Grid,
@ -14,6 +12,8 @@ import {
} from "@chakra-ui/react";
import { env } from "@typebot.io/env";
import { isDefined } from "@typebot.io/lib/utils";
import { Alert } from "@typebot.io/ui/components/Alert";
import { TriangleAlertIcon } from "@typebot.io/ui/icons/TriangleAlertIcon";
import {
createClient,
type ErrorResponse,
@ -171,10 +171,10 @@ export const PexelsPicker = ({ onVideoSelect }: Props) => {
</HStack>
</Stack>
{isDefined(error) && (
<Alert status="error">
<AlertIcon />
{error}
</Alert>
<Alert.Root variant="error">
<TriangleAlertIcon />
<Alert.Description>{error}</Alert.Description>
</Alert.Root>
)}
<Stack overflowY="auto" maxH="400px" ref={scrollContainer}>
{videos.length > 0 && (

View File

@ -1,5 +1,6 @@
import { Alert } from "@chakra-ui/react";
import { useTranslate } from "@tolgee/react";
import { Alert } from "@typebot.io/ui/components/Alert";
import { TriangleAlertIcon } from "@typebot.io/ui/icons/TriangleAlertIcon";
type Props = {
error: string;
@ -22,8 +23,9 @@ export const SignInError = ({ error }: Props) => {
if (!errors[error]) return null;
return (
<Alert status="error" variant="solid" rounded="md">
{errors[error]}
</Alert>
<Alert.Root variant="error">
<TriangleAlertIcon />
<Alert.Description>{errors[error]}</Alert.Description>
</Alert.Root>
);
};

View File

@ -1,7 +1,5 @@
import { sanitizeUrl } from "@braintree/sanitize-url";
import {
Alert,
AlertIcon,
FormControl,
FormLabel,
HStack,
@ -16,7 +14,9 @@ import {
VStack,
} from "@chakra-ui/react";
import { useTranslate } from "@tolgee/react";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button } from "@typebot.io/ui/components/Button";
import { CheckmarkSquare02Icon } from "@typebot.io/ui/icons/CheckmarkSquare02Icon";
import { useRouter } from "next/navigation";
import { getProviders, signIn, useSession } from "next-auth/react";
import { useQueryState } from "nuqs";
@ -174,15 +174,15 @@ export const SignInForm = ({
)}
<SlideFade offsetY="20px" in={isMagicCodeSent} unmountOnExit>
<Stack spacing={3}>
<Alert status="success" w="100%">
<HStack>
<AlertIcon />
<Stack spacing={1}>
<Text fontWeight="medium">{t("auth.magicLink.title")}</Text>
<Text fontSize="sm">{t("auth.magicLink.description")}</Text>
</Stack>
</HStack>
</Alert>
<Alert.Root variant="success">
<CheckmarkSquare02Icon />
<div className="flex flex-col gap-2">
<Alert.Title>{t("auth.magicLink.title")}</Alert.Title>
<Alert.Description>
{t("auth.magicLink.description")}
</Alert.Description>
</div>
</Alert.Root>
<FormControl as={VStack} spacing={0}>
<FormLabel>Login code:</FormLabel>
<HStack>

View File

@ -1,9 +1,10 @@
import { HStack } from "@chakra-ui/react";
import { useTranslate } from "@tolgee/react";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button } from "@typebot.io/ui/components/Button";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { cx } from "@typebot.io/ui/lib/cva";
import { AlertInfo } from "@/components/AlertInfo";
import { useWorkspace } from "@/features/workspace/WorkspaceProvider";
import { ChangePlanForm } from "./ChangePlanForm";
@ -32,9 +33,12 @@ export const ChangePlanDialog = ({
<Dialog.CloseButton />
{type && (
<AlertInfo>
{t("billing.upgradeLimitLabel", { type: type })}
</AlertInfo>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
{t("billing.upgradeLimitLabel", { type: type })}
</Alert.Description>
</Alert.Root>
)}
{workspace && (
<ChangePlanForm

View File

@ -1,13 +1,8 @@
import {
Alert,
AlertIcon,
Heading,
HStack,
Stack,
Text,
} from "@chakra-ui/react";
import { Heading, HStack, Stack, Text } from "@chakra-ui/react";
import { useTranslate } from "@tolgee/react";
import { Plan } from "@typebot.io/prisma/enum";
import { Alert } from "@typebot.io/ui/components/Alert";
import { TriangleAlertIcon } from "@typebot.io/ui/icons/TriangleAlertIcon";
import type { Workspace } from "@typebot.io/workspaces/schemas";
import { useSubscriptionQuery } from "../hooks/useSubscriptionQuery";
import { BillingPortalButton } from "./BillingPortalButton";
@ -42,10 +37,12 @@ export const CurrentSubscriptionSummary = ({ workspace }: Props) => {
)}
</HStack>
{data?.subscription?.status === "past_due" && (
<Alert fontSize="sm" status="error">
<AlertIcon />
{t("billing.currentSubscription.pastDueAlert")}
</Alert>
<Alert.Root variant="error">
<TriangleAlertIcon />
<Alert.Description>
{t("billing.currentSubscription.pastDueAlert")}
</Alert.Description>
</Alert.Root>
)}
{isSubscribed && (

View File

@ -1,6 +1,5 @@
import { Image, Text } from "@chakra-ui/react";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { AlertInfo } from "@/components/AlertInfo";
import { ButtonLink } from "@/components/ButtonLink";
import { GoogleLogo } from "@/components/GoogleLogo";
import { useWorkspace } from "@/features/workspace/WorkspaceProvider";
@ -47,11 +46,6 @@ export const GoogleSheetConnectDialogBody = ({
alt="Google Spreadsheets checkboxes"
rounded="md"
/>
<AlertInfo>
Google does not provide more granular permissions than &quot;read&quot;
or &quot;write&quot; access. That&apos;s why it states that Typebot can
also delete your spreadsheets which it won&apos;t.
</AlertInfo>
<Dialog.Footer>
{workspace?.id && (
<ButtonLink

View File

@ -1,6 +1,9 @@
import { Alert, AlertIcon, Stack, Text } from "@chakra-ui/react";
import { Stack } from "@chakra-ui/react";
import type { HttpRequest } from "@typebot.io/blocks-integrations/httpRequest/schema";
import type { MakeComBlock } from "@typebot.io/blocks-integrations/makeCom/schema";
import { Alert } from "@typebot.io/ui/components/Alert";
import { CheckmarkSquare02Icon } from "@typebot.io/ui/icons/CheckmarkSquare02Icon";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { useRef } from "react";
import { ButtonLink } from "@/components/ButtonLink";
import { ExternalLinkIcon } from "@/components/icons";
@ -38,22 +41,31 @@ export const MakeComSettings = ({
return (
<Stack spacing={0}>
<Stack spacing={4}>
<Alert status={url ? "success" : "info"} rounded="md">
<AlertIcon />
{url ? (
"Your scenario is correctly configured 🚀"
) : (
<Stack>
<Text>Head up to Make.com to configure this block:</Text>
{url ? (
<Alert.Root variant="success">
<CheckmarkSquare02Icon />
<Alert.Description>
Your scenario is correctly configured 🚀
</Alert.Description>
</Alert.Root>
) : (
<Alert.Root variant="info">
<InformationSquareIcon />
<Alert.Description>
Head up to Make.com to configure this block
</Alert.Description>
<Alert.Action>
<ButtonLink
size="xs"
variant="secondary"
href="https://www.make.com/en/integrations/typebot"
target="_blank"
>
Make.com <ExternalLinkIcon />
</ButtonLink>
</Stack>
)}
</Alert>
</Alert.Action>
</Alert.Root>
)}
<HttpRequestAdvancedConfigForm
blockId={blockId}
httpRequest={options?.webhook}

View File

@ -1,8 +1,9 @@
import { Alert, AlertIcon } from "@chakra-ui/react";
import { useMutation } from "@tanstack/react-query";
import type { CreatableCredentials } from "@typebot.io/credentials/schemas";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button } from "@typebot.io/ui/components/Button";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { TriangleAlertIcon } from "@typebot.io/ui/icons/TriangleAlertIcon";
import type React from "react";
import { useState } from "react";
import { TextInput } from "@/components/inputs/TextInput";
@ -96,11 +97,13 @@ export const OpenAICredentialsDialog = ({
withVariableButton={false}
debounceTimeout={0}
/>
<Alert status="warning">
<AlertIcon />
Make sure to add a payment method to your OpenAI account. Otherwise,
it will not work after a few messages.
</Alert>
<Alert.Root variant="warning">
<TriangleAlertIcon />
<Alert.Description>
Make sure to add a payment method to your OpenAI account. Otherwise,
it will not work after a few messages.
</Alert.Description>
</Alert.Root>
<Dialog.Footer>
<Button

View File

@ -1,6 +1,9 @@
import { Alert, AlertIcon, Stack, Text } from "@chakra-ui/react";
import { Stack } from "@chakra-ui/react";
import type { HttpRequest } from "@typebot.io/blocks-integrations/httpRequest/schema";
import type { PabblyConnectBlock } from "@typebot.io/blocks-integrations/pabblyConnect/schema";
import { Alert } from "@typebot.io/ui/components/Alert";
import { CheckmarkSquare02Icon } from "@typebot.io/ui/icons/CheckmarkSquare02Icon";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { useRef } from "react";
import { ButtonLink } from "@/components/ButtonLink";
import { ExternalLinkIcon } from "@/components/icons";
@ -43,22 +46,31 @@ export const PabblyConnectSettings = ({
return (
<Stack spacing={0}>
<Stack spacing={4}>
<Alert status={url ? "success" : "info"} rounded="md">
<AlertIcon />
{url ? (
"Your scenario is correctly configured 🚀"
) : (
<Stack>
<Text>Head up to Pabbly Connect to get the webhook URL:</Text>
{url ? (
<Alert.Root variant="success">
<CheckmarkSquare02Icon />
<Alert.Description>
Your scenario is correctly configured 🚀
</Alert.Description>
</Alert.Root>
) : (
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
Head up to Pabbly Connect to get the webhook URL:
</Alert.Description>
<Alert.Action>
<ButtonLink
variant="secondary"
href="https://www.pabbly.com/connect/integrations/typebot/"
target="_blank"
size="xs"
>
Pabbly.com <ExternalLinkIcon />
</ButtonLink>
</Stack>
)}
</Alert>
</Alert.Action>
</Alert.Root>
)}
<TextInput
placeholder="Paste webhook URL..."
defaultValue={url ?? ""}

View File

@ -1,9 +1,12 @@
import { Alert, AlertIcon, Stack, Text } from "@chakra-ui/react";
import { Stack } from "@chakra-ui/react";
import type {
HttpRequest,
HttpRequestBlock,
} from "@typebot.io/blocks-integrations/httpRequest/schema";
import type { ZapierBlock } from "@typebot.io/blocks-integrations/zapier/schema";
import { Alert } from "@typebot.io/ui/components/Alert";
import { CheckmarkSquare02Icon } from "@typebot.io/ui/icons/CheckmarkSquare02Icon";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { useRef } from "react";
import { ButtonLink } from "@/components/ButtonLink";
import { ExternalLinkIcon } from "@/components/icons";
@ -42,22 +45,31 @@ export const ZapierSettings = ({
return (
<Stack spacing={0}>
<Stack spacing={4}>
<Alert status={url ? "success" : "info"} rounded="md">
<AlertIcon />
{url ? (
"Your zap is correctly configured 🚀"
) : (
<Stack>
<Text>Head up to Zapier to configure this block:</Text>
{url ? (
<Alert.Root variant="success">
<CheckmarkSquare02Icon />
<Alert.Description>
Your zap is correctly configured 🚀
</Alert.Description>
</Alert.Root>
) : (
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
Head up to Zapier to configure this block:
</Alert.Description>
<Alert.Action>
<ButtonLink
variant="secondary"
href="https://zapier.com/apps/typebot/integrations"
target="_blank"
size="xs"
>
Zapier <ExternalLinkIcon />
</ButtonLink>
</Stack>
)}
</Alert>
</Alert.Action>
</Alert.Root>
)}
<HttpRequestAdvancedConfigForm
blockId={blockId}
httpRequest={options?.webhook}

View File

@ -1,4 +1,4 @@
import { Alert, AlertIcon, Stack, Tag, Text } from "@chakra-ui/react";
import { Stack, Tag, Text } from "@chakra-ui/react";
import { isInputBlock } from "@typebot.io/blocks-core/helpers";
import {
defaultSetVariableOptions,
@ -10,9 +10,11 @@ import {
import type { SetVariableBlock } from "@typebot.io/blocks-logic/setVariable/schema";
import { timeZones } from "@typebot.io/lib/timeZones";
import { isDefined } from "@typebot.io/lib/utils";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Field } from "@typebot.io/ui/components/Field";
import { MoreInfoTooltip } from "@typebot.io/ui/components/MoreInfoTooltip";
import { Switch } from "@typebot.io/ui/components/Switch";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import type { Variable } from "@typebot.io/variables/schemas";
import { BasicSelect } from "@/components/inputs/BasicSelect";
import { CodeEditor } from "@/components/inputs/CodeEditor";
@ -318,37 +320,37 @@ const SetVariableValue = ({
}
case "Moment of the day": {
return (
<Alert fontSize="sm">
<AlertIcon />
<Text>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
Will return either <Tag size="sm">morning</Tag>,{" "}
<Tag size="sm">afternoon</Tag>,<Tag size="sm">evening</Tag> or{" "}
<Tag size="sm">night</Tag> based on the current user time.
</Text>
</Alert>
</Alert.Description>
</Alert.Root>
);
}
case "Environment name": {
return (
<Alert fontSize="sm">
<AlertIcon />
<Text>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
Will return either <Tag size="sm">web</Tag> or{" "}
<Tag size="sm">whatsapp</Tag>.
</Text>
</Alert>
</Alert.Description>
</Alert.Root>
);
}
case "Device type": {
return (
<Alert fontSize="sm">
<AlertIcon />
<Text>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
Will return either <Tag size="sm">desktop</Tag>,{" "}
<Tag size="sm">tablet</Tag> or <Tag size="sm">mobile</Tag>.
</Text>
</Alert>
</Alert.Description>
</Alert.Root>
);
}
case "Now":

View File

@ -1,8 +1,10 @@
import { Alert, Heading, HStack, Input, Stack, Text } from "@chakra-ui/react";
import { Heading, HStack, Input, Stack, Text } from "@chakra-ui/react";
import { useMutation } from "@tanstack/react-query";
import { useTranslate } from "@tolgee/react";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button } from "@typebot.io/ui/components/Button";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { useEffect, useRef, useState } from "react";
import { trpc } from "@/lib/queryClient";
@ -58,7 +60,6 @@ export const CreateCustomDomainDialog = ({
domain: hostnameDetails.domain,
subdomain: hostnameDetails.subdomain,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [inputValue]);
const onAddDomainClick = async () => {
@ -137,9 +138,12 @@ export const CreateCustomDomainDialog = ({
</Stack>
</HStack>
)}
<Alert rounded="md">
{t("customDomain.modal.warningMessage")}
</Alert>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
{t("customDomain.modal.warningMessage")}
</Alert.Description>
</Alert.Root>
</>
)}
</Stack>

View File

@ -1,14 +1,8 @@
import {
Alert,
AlertIcon,
Box,
Code,
HStack,
Stack,
Text,
} from "@chakra-ui/react";
import { Box, Code, HStack, Stack, Text } from "@chakra-ui/react";
import { useQuery } from "@tanstack/react-query";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { TriangleAlertIcon } from "@typebot.io/ui/icons/TriangleAlertIcon";
import { XCircleIcon } from "@/components/icons";
import { trpc } from "@/lib/queryClient";
@ -103,14 +97,14 @@ export const CustomDomainConfigDialog = ({
</Text>
</Stack>
</HStack>
<Alert status="warning">
<AlertIcon />
<Text>
<Alert.Root variant="warning">
<TriangleAlertIcon />
<Alert.Description>
If you are using this domain for another site, setting this TXT
record will transfer domain ownership away from that site and
break it. Please exercise caution when setting this record.
</Text>
</Alert>
</Alert.Description>
</Alert.Root>
</Stack>
) : status === "Unknown Error" ? (
<Text mb="5" fontSize="sm">
@ -153,14 +147,14 @@ export const CustomDomainConfigDialog = ({
</Text>
</Stack>
</HStack>
<Alert fontSize="sm">
<AlertIcon />
<Text>
<Alert.Root>
<TriangleAlertIcon />
<Alert.Description>
Note: for TTL, if <Code>86400</Code> is not available, set the
highest value possible. Also, domain propagation can take up to
an hour.
</Text>
</Alert>
</Alert.Description>
</Alert.Root>
</Stack>
)}
<Dialog.Footer>

View File

@ -1,6 +1,4 @@
import {
Alert,
AlertIcon,
SkeletonCircle,
SkeletonText,
Stack,
@ -10,10 +8,12 @@ import {
import { useMutation } from "@tanstack/react-query";
import { T, useTranslate } from "@tolgee/react";
import type { Prisma } from "@typebot.io/prisma/types";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button, buttonVariants } from "@typebot.io/ui/components/Button";
import { Menu } from "@typebot.io/ui/components/Menu";
import { useOpenControls } from "@typebot.io/ui/hooks/useOpenControls";
import { Folder01SolidIcon } from "@typebot.io/ui/icons/Folder01SolidIcon";
import { TriangleAlertIcon } from "@typebot.io/ui/icons/TriangleAlertIcon";
import { cn } from "@typebot.io/ui/lib/cn";
import { useRouter } from "next/router";
import { memo, useMemo } from "react";
@ -152,10 +152,12 @@ const FolderButton = ({
}}
/>
</Text>
<Alert status="warning">
<AlertIcon />
{t("folders.folderButton.deleteConfirmationMessageWarning")}
</Alert>
<Alert.Root variant="warning">
<TriangleAlertIcon />
<Alert.Description>
{t("folders.folderButton.deleteConfirmationMessageWarning")}
</Alert.Description>
</Alert.Root>
</Stack>
</ConfirmDialog>
</>

View File

@ -1,10 +1,12 @@
import { Alert, AlertIcon, Tag, Text, VStack } from "@chakra-ui/react";
import { Tag, Text, VStack } from "@chakra-ui/react";
import { useMutation } from "@tanstack/react-query";
import { T, useTranslate } from "@tolgee/react";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button, buttonVariants } from "@typebot.io/ui/components/Button";
import { Menu } from "@typebot.io/ui/components/Menu";
import { useOpenControls } from "@typebot.io/ui/hooks/useOpenControls";
import { LayoutBottomIcon } from "@typebot.io/ui/icons/LayoutBottomIcon";
import { TriangleAlertIcon } from "@typebot.io/ui/icons/TriangleAlertIcon";
import { cn } from "@typebot.io/ui/lib/cn";
import { useRouter } from "next/router";
import React, { memo } from "react";
@ -206,10 +208,12 @@ const TypebotButton = ({
}}
/>
</Text>
<Alert status="warning">
<AlertIcon />
{t("folders.typebotButton.deleteConfirmationMessageWarning")}
</Alert>
<Alert.Root variant="warning">
<TriangleAlertIcon />
<Alert.Description>
{t("folders.typebotButton.deleteConfirmationMessageWarning")}
</Alert.Description>
</Alert.Root>
</ConfirmDialog>
)}
</>

View File

@ -1,6 +1,4 @@
import {
Alert,
AlertIcon,
HStack,
SlideFade,
Stack,
@ -9,7 +7,9 @@ import {
} from "@chakra-ui/react";
import { useMutation } from "@tanstack/react-query";
import { isEmpty } from "@typebot.io/lib/utils";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button } from "@typebot.io/ui/components/Button";
import { CheckmarkSquare02Icon } from "@typebot.io/ui/icons/CheckmarkSquare02Icon";
import { type FormEvent, useState } from "react";
import { ButtonLink } from "@/components/ButtonLink";
import { BuoyIcon, ExternalLinkIcon } from "@/components/icons";
@ -109,17 +109,13 @@ export const WhatsAppPreviewInstructions = (props: StackProps) => {
Open WhatsApp Web
<ExternalLinkIcon />
</ButtonLink>
<Alert status="success" w="100%">
<HStack>
<AlertIcon />
<Stack spacing={1}>
<Text fontWeight="medium">Chat started!</Text>
<Text fontSize="sm">
The first message can take up to 2 min to be delivered.
</Text>
</Stack>
</HStack>
</Alert>
<Alert.Root variant="success">
<CheckmarkSquare02Icon />
<Alert.Title>Chat started!</Alert.Title>
<Alert.Description>
The first message can take up to 2 min to be delivered.
</Alert.Description>
</Alert.Root>
</Stack>
</SlideFade>
</Stack>

View File

@ -1,19 +1,14 @@
import {
Alert,
AlertIcon,
HStack,
Stack,
Text,
useDisclosure,
} from "@chakra-ui/react";
import { HStack, Stack, Text, useDisclosure } from "@chakra-ui/react";
import { useMutation } from "@tanstack/react-query";
import { T, useTranslate } from "@tolgee/react";
import { InputBlockType } from "@typebot.io/blocks-inputs/constants";
import { isNotDefined } from "@typebot.io/lib/utils";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button, type ButtonProps } from "@typebot.io/ui/components/Button";
import { Menu } from "@typebot.io/ui/components/Menu";
import { Tooltip } from "@typebot.io/ui/components/Tooltip";
import { ArrowDown01Icon } from "@typebot.io/ui/icons/ArrowDown01Icon";
import { TriangleAlertIcon } from "@typebot.io/ui/icons/TriangleAlertIcon";
import { cn } from "@typebot.io/ui/lib/cn";
import { useRouter } from "next/router";
import { useState } from "react";
@ -181,11 +176,13 @@ export const PublishButton = ({
<br />
Consider rephrasing with your own branding.
</span>
<Alert status="warning">
<AlertIcon />
Your workspace is at risk of being suspended if we detect a
trademark infringement down the line.
</Alert>
<Alert.Root variant="warning">
<TriangleAlertIcon />
<Alert.Description>
Your workspace is at risk of being suspended if we detect a
trademark infringement down the line.
</Alert.Description>
</Alert.Root>
</div>
</ConfirmDialog>

View File

@ -1,8 +1,9 @@
import { Heading, HStack } from "@chakra-ui/react";
import { capitalize } from "@typebot.io/lib/utils";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button } from "@typebot.io/ui/components/Button";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { AlertInfo } from "@/components/AlertInfo";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { ChevronLeftIcon } from "@/components/icons";
import { EmbedTypeMenu } from "./EmbedTypeMenu/EmbedTypeMenu";
@ -49,7 +50,12 @@ export const DeployDialog = ({
</Dialog.Title>
<Dialog.CloseButton />
{!isPublished && (
<AlertInfo>You need to publish your bot first.</AlertInfo>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
You need to publish your bot first.
</Alert.Description>
</Alert.Root>
)}
{!selectedEmbedType ? (
<EmbedTypeMenu onSelectEmbedType={onSelectEmbedType} />

View File

@ -1,6 +1,7 @@
import { Code, ListItem, OrderedList, Stack, Text } from "@chakra-ui/react";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { AlertInfo } from "@/components/AlertInfo";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { CodeEditor } from "@/components/inputs/CodeEditor";
import { TextLink } from "@/components/TextLink";
import { useTypebot } from "@/features/editor/providers/TypebotProvider";
@ -25,7 +26,12 @@ export const ApiDeployDialog = ({
<Dialog.Title>API</Dialog.Title>
<Dialog.CloseButton />
{!isPublished && (
<AlertInfo>You need to publish your bot first.</AlertInfo>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
You need to publish your bot first.
</Alert.Description>
</Alert.Root>
)}
<OrderedList spacing={4} pl="4">
<ListItem>

View File

@ -9,8 +9,9 @@ import {
Text,
} from "@chakra-ui/react";
import { env } from "@typebot.io/env";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { AlertInfo } from "@/components/AlertInfo";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { CopyButton } from "@/components/CopyButton";
import type { DialogProps } from "../DeployButton";
@ -26,7 +27,12 @@ export const BlinkDeployDialog = ({
<Dialog.Title>Blink</Dialog.Title>
<Dialog.CloseButton />
{!isPublished && (
<AlertInfo mb="4">You need to publish your bot first.</AlertInfo>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
You need to publish your bot first.
</Alert.Description>
</Alert.Root>
)}
<OrderedList spacing={4}>
<ListItem>

View File

@ -9,8 +9,9 @@ import {
Text,
} from "@chakra-ui/react";
import { env } from "@typebot.io/env";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { AlertInfo } from "@/components/AlertInfo";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { CopyButton } from "@/components/CopyButton";
import type { DialogProps } from "../DeployButton";
@ -26,7 +27,12 @@ export const FlutterFlowDeployDialog = ({
<Dialog.Title>FlutterFlow</Dialog.Title>
<Dialog.CloseButton />
{!isPublished && (
<AlertInfo mb="4">You need to publish your bot first.</AlertInfo>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
You need to publish your bot first.
</Alert.Description>
</Alert.Root>
)}
<OrderedList spacing={4}>
<ListItem>

View File

@ -9,8 +9,9 @@ import {
Text,
} from "@chakra-ui/react";
import { env } from "@typebot.io/env";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { AlertInfo } from "@/components/AlertInfo";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { CopyButton } from "@/components/CopyButton";
import type { DialogProps } from "../DeployButton";
@ -26,7 +27,12 @@ export const NotionDeployDialog = ({
<Dialog.Title>Notion</Dialog.Title>
<Dialog.CloseButton />
{!isPublished && (
<AlertInfo>You need to publish your bot first.</AlertInfo>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
You need to publish your bot first.
</Alert.Description>
</Alert.Root>
)}
<OrderedList spacing={4}>
<ListItem>

View File

@ -1,7 +1,8 @@
import { Text } from "@chakra-ui/react";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { useState } from "react";
import { AlertInfo } from "@/components/AlertInfo";
import type { DialogProps } from "../../DeployButton";
import { StandardSettings } from "../../settings/StandardSettings";
import { IframeSnippet } from "./IframeSnippet";
@ -25,7 +26,12 @@ export const IframeDeployDialog = ({
<Dialog.Title>Iframe</Dialog.Title>
<Dialog.CloseButton />
{!isPublished && (
<AlertInfo>You need to publish your bot first.</AlertInfo>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
You need to publish your bot first.
</Alert.Description>
</Alert.Root>
)}
<StandardSettings
onUpdateWindowSettings={(settings) => setInputValues({ ...settings })}

View File

@ -1,27 +1,23 @@
import {
Flex,
HStack,
ListItem,
OrderedList,
Text,
useDisclosure,
} from "@chakra-ui/react";
import { Flex, HStack, ListItem, OrderedList, Text } from "@chakra-ui/react";
import { useQuery } from "@tanstack/react-query";
import { LogicalOperator } from "@typebot.io/conditions/constants";
import type { Comparison } from "@typebot.io/conditions/schemas";
import { isDefined } from "@typebot.io/lib/utils";
import { defaultSessionExpiryTimeout } from "@typebot.io/settings/constants";
import { Accordion } from "@typebot.io/ui/components/Accordion";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button } from "@typebot.io/ui/components/Button";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { Field } from "@typebot.io/ui/components/Field";
import { MoreInfoTooltip } from "@typebot.io/ui/components/MoreInfoTooltip";
import { Switch } from "@typebot.io/ui/components/Switch";
import { AlertInfo } from "@/components/AlertInfo";
import { useOpenControls } from "@typebot.io/ui/hooks/useOpenControls";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { BasicNumberInput } from "@/components/inputs/BasicNumberInput";
import { BasicSelect } from "@/components/inputs/BasicSelect";
import { TableList } from "@/components/TableList";
import { TextLink } from "@/components/TextLink";
import { UnlockPlanAlertInfo } from "@/components/UnlockPlanAlertInfo";
import { ChangePlanDialog } from "@/features/billing/components/ChangePlanDialog";
import { PlanTag } from "@/features/billing/components/PlanTag";
import { hasProPerks } from "@/features/billing/helpers/hasProPerks";
import { CredentialsDropdown } from "@/features/credentials/components/CredentialsDropdown";
@ -43,7 +39,12 @@ export const WhatsAppDeployDialog = ({
isOpen: isCredentialsDialogOpen,
onOpen,
onClose: onCredentialsDialogClose,
} = useDisclosure();
} = useOpenControls();
const {
isOpen: isChangePlanDialogOpen,
onOpen: onChangePlanDialogOpen,
onClose: onChangePlanDialogClose,
} = useOpenControls();
const whatsAppSettings = typebot?.settings.whatsApp;
@ -169,13 +170,31 @@ export const WhatsAppDeployDialog = ({
<Dialog.Title>WhatsApp</Dialog.Title>
<Dialog.CloseButton />
{!hasProPerks(workspace) && (
<UnlockPlanAlertInfo excludedPlans={["STARTER"]}>
Upgrade your workspace to <PlanTag plan="PRO" /> to be able to
enable WhatsApp integration.
</UnlockPlanAlertInfo>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
Upgrade your workspace to <PlanTag plan="PRO" /> to be able to
enable WhatsApp integration.
</Alert.Description>
<Alert.Action>
<Button variant="secondary" onClick={onChangePlanDialogOpen}>
Upgrade
</Button>
<ChangePlanDialog
isOpen={isChangePlanDialogOpen}
onClose={onChangePlanDialogClose}
excludedPlans={["STARTER"]}
/>
</Alert.Action>
</Alert.Root>
)}
{!isPublished && phoneNumberData && (
<AlertInfo>You have modifications that can be published.</AlertInfo>
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
You have modifications that can be published.
</Alert.Description>
</Alert.Root>
)}
<OrderedList spacing={4} pl="4">
<ListItem>

View File

@ -8,14 +8,15 @@ import { parseBlockIdVariableIdMap } from "@typebot.io/results/parseBlockIdVaria
import { parseColumnsOrder } from "@typebot.io/results/parseColumnsOrder";
import { parseResultHeader } from "@typebot.io/results/parseResultHeader";
import type { Typebot } from "@typebot.io/typebot/schemas/typebot";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button } from "@typebot.io/ui/components/Button";
import { Dialog } from "@typebot.io/ui/components/Dialog";
import { Field } from "@typebot.io/ui/components/Field";
import { MoreInfoTooltip } from "@typebot.io/ui/components/MoreInfoTooltip";
import { Switch } from "@typebot.io/ui/components/Switch";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { unparse } from "papaparse";
import { useState } from "react";
import { AlertInfo } from "@/components/AlertInfo";
import { DownloadIcon } from "@/components/icons";
import { useTypebot } from "@/features/editor/providers/TypebotProvider";
import { trpc, trpcClient } from "@/lib/queryClient";
@ -165,11 +166,14 @@ export const ExportAllResultsDialog = ({ isOpen, onClose }: Props) => {
</MoreInfoTooltip>
</Field.Label>
</Field.Root>
{totalResults > 2000 ? (
<AlertInfo>The export may take a while.</AlertInfo>
) : (
<AlertInfo>The export may take up to 1 minute.</AlertInfo>
)}
<Alert.Root>
<InformationSquareIcon />
<Alert.Description>
{totalResults > 2000
? "The export may take a while."
: "The export may take up to 1 minute."}
</Alert.Description>
</Alert.Root>
{isExportLoading && (
<Stack>
<Text>Fetching all results...</Text>

View File

@ -10,7 +10,11 @@ import { getSeatsLimit } from "@typebot.io/billing/helpers/getSeatsLimit";
import { isDefined } from "@typebot.io/lib/utils";
import { WorkspaceRole } from "@typebot.io/prisma/enum";
import type { Prisma } from "@typebot.io/prisma/types";
import { UnlockPlanAlertInfo } from "@/components/UnlockPlanAlertInfo";
import { Alert } from "@typebot.io/ui/components/Alert";
import { Button } from "@typebot.io/ui/components/Button";
import { useOpenControls } from "@typebot.io/ui/hooks/useOpenControls";
import { InformationSquareIcon } from "@typebot.io/ui/icons/InformationSquareIcon";
import { ChangePlanDialog } from "@/features/billing/components/ChangePlanDialog";
import { useUser } from "@/features/user/hooks/useUser";
import { useMembers } from "../hooks/useMembers";
import { deleteInvitationQuery } from "../queries/deleteInvitationQuery";
@ -30,6 +34,12 @@ export const MembersList = () => {
workspaceId: workspace?.id,
});
const {
isOpen: isChangePlanDialogOpen,
onOpen: onChangePlanDialogOpen,
onClose: onChangePlanDialogClose,
} = useOpenControls();
const handleDeleteMemberClick = (memberId: string) => async () => {
if (!workspace) return;
await deleteMemberQuery(workspace.id, memberId);
@ -102,9 +112,26 @@ export const MembersList = () => {
return (
<Stack w="full" spacing={3}>
{!canInviteNewMember && (
<UnlockPlanAlertInfo>
{t("workspace.membersList.unlockBanner.label")}
</UnlockPlanAlertInfo>
<Alert.Root>
<InformationSquareIcon />
<Alert.Title>Unlock more members</Alert.Title>
<Alert.Description>
{t("workspace.membersList.unlockBanner.label")}
</Alert.Description>
<Alert.Action>
<Button
variant="secondary"
onClick={onChangePlanDialogOpen}
size="sm"
>
Upgrade
</Button>
<ChangePlanDialog
isOpen={isChangePlanDialogOpen}
onClose={onChangePlanDialogClose}
/>
</Alert.Action>
</Alert.Root>
)}
{isDefined(seatsLimit) && (
<Heading fontSize="2xl">

View File

@ -0,0 +1,73 @@
import { cva, type VariantProps } from "class-variance-authority";
import type * as React from "react";
import { cn } from "../lib/cn";
const alertVariants = cva(
"relative grid w-full items-start gap-x-2 rounded-xl border bg-card px-3 py-2.5 text-sm text-card-foreground has-data-[slot=alert-action]:grid-cols-[1fr_auto] has-[>svg]:grid-cols-[calc(var(--spacing)*5)_1fr] has-[>svg]:gap-x-2 has-[>svg]:has-data-[slot=alert-action]:grid-cols-[calc(var(--spacing)*4)_1fr_auto] [&>svg]:size-4 [&>svg]:mt-0.5",
{
variants: {
variant: {
info: "border-blue-6 bg-blue-2 [&>svg]:text-blue-10",
success: "border-green-6 bg-green-2 [&>svg]:text-green-10",
warning: "border-orange-6 bg-orange-2 [&>svg]:text-orange-10",
error: "border-red-6 bg-red-2 [&>svg]:text-red-10",
},
},
defaultVariants: {
variant: "info",
},
},
);
function Root({
className,
variant,
...props
}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
return (
<div
data-slot="alert"
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
);
}
function Title({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-title"
className={cn("font-medium [svg~&]:col-start-2", className)}
{...props}
/>
);
}
function Description({ className, ...props }: React.ComponentProps<"div">) {
return (
<span
data-slot="alert-description"
className={cn(
"text-sm text-muted-foreground *:[p]:leading-relaxed [svg~&]:col-start-2",
className,
)}
{...props}
/>
);
}
function Action({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-action"
className={cn(
"flex gap-1 max-sm:col-start-2 max-sm:mt-2 sm:row-start-1 sm:row-end-3 sm:self-center sm:[[data-slot=alert-description]~&]:col-start-2 sm:[[data-slot=alert-title]~&]:col-start-2 sm:[svg~&]:col-start-2 sm:[svg~[data-slot=alert-description]~&]:col-start-3 sm:[svg~[data-slot=alert-title]~&]:col-start-3",
className,
)}
{...props}
/>
);
}
export const Alert = { Root, Title, Description, Action };

View File

@ -40,6 +40,13 @@ const buttonVariants = cva(
size: "default",
iconStyle: "auto",
},
compoundVariants: [
{
size: "xs",
iconStyle: "auto",
class: "[&_svg]:size-3",
},
],
},
);