Repository: formbricks/formbricks
Pull request #5002: fix: unify alert component
Author: login: jakobsitory
Created: 2025-03-19T09:04:23Z | Merged: 2025-03-27T13:03:30Z

<!-- We require pull request titles to follow the Conventional Commits specification ( https://www.conventionalcommits.org/en/v1.0.0/#summary ). Please make sure your title follow these conventions -->

## What does this PR do?

<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -->

- Expanded the Alert component to have multiple variants (default, error, warning, info, success) that feature distinct styling
- Added new small size
- Created storybook entry for variants and sizes

Fixes that there are multiple alert components in use
![image](https://github.com/user-attachments/assets/d56cf475-1534-47c9-9329-fedae69ed2ed)


<!-- Please provide a screenshots or a loom video for visual changes to speed up reviews
 Loom Video: https://www.loom.com/
-->

## How should this be tested?

<!-- Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration -->

- Run `pnpm run storybook`
- go to [localhost:6006/?path=/docs/ui-alert--docs](http://localhost:6006/?path=/docs/ui-alert--docs)

## Checklist

<!-- We're starting to get more and more contributions. Please help us making this efficient for all of us and go through this checklist. Please tick off what you did  -->

### Required

- [x] Filled out the "How to test" section in this PR
- [x] Read [How we Code at Formbricks](<[https://github.com/formbricks/formbricks/blob/main/CONTRIBUTING.md](https://formbricks.com/docs/contributing/how-we-code)>)
- [x] Self-reviewed my own code
- [ ] Commented on my code in hard-to-understand bits
- [x] Ran `pnpm build`
- [x] Checked for warnings, there are none
- [x] Removed all `console.logs`
- [x] Merged the latest changes from main onto my branch with `git pull origin main`
- [ ] My changes don't cause any responsiveness issues
- [x] First PR at Formbricks? [Please sign the CLA!](https://cla-assistant.io/formbricks/formbricks) Without it we wont be able to merge it 🙏

### Appreciated

- [x] If a UI change was made: Added a screen recording or screenshots to this PR
- [ ] Updated the Formbricks Docs if changes were necessary


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Enhanced alert components now support configurable sizes and include an interactive action button.
  - Expanded variations showcase Small, Warning, Info, and Success alerts for a tailored notification experience.

- **Style**
  - Updated color themes provide a consistent and polished visual appearance for different alert statuses.
  - New color categories for info, warning, success, and revised error enhance color management within the application.

- **Bug Fixes**
  - Improved rendering logic ensures alerts display correctly based on their variant and size properties.

- **Tests**
  - Comprehensive test suite added for the Alert component and its stories, verifying rendering and functionality across various scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/apps/web/modules/ui/components/alert/index.test.tsx b/apps/web/modules/ui/components/alert/index.test.tsx
new file mode 100644
index 000000000000..08c218f24be5
--- /dev/null
+++ b/apps/web/modules/ui/components/alert/index.test.tsx
@@ -0,0 +1,74 @@
+import { fireEvent, render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import { Alert, AlertButton, AlertDescription, AlertTitle } from "./index";
+
+describe("Alert", () => {
+  it("renders with default variant", () => {
+    render(
+      <Alert>
+        <AlertTitle>Test Title</AlertTitle>
+        <AlertDescription>Test Description</AlertDescription>
+      </Alert>
+    );
+
+    expect(screen.getByRole("alert")).toBeInTheDocument();
+    expect(screen.getByText("Test Title")).toBeInTheDocument();
+    expect(screen.getByText("Test Description")).toBeInTheDocument();
+  });
+
+  it("renders with different variants", () => {
+    const variants = ["default", "error", "warning", "info", "success"] as const;
+
+    variants.forEach((variant) => {
+      const { container } = render(
+        <Alert variant={variant}>
+          <AlertTitle>Test Title</AlertTitle>
+        </Alert>
+      );
+
+      expect(container.firstChild).toHaveClass(
+        variant === "default" ? "text-foreground" : `text-${variant}-foreground`
+      );
+    });
+  });
+
+  it("renders with different sizes", () => {
+    const sizes = ["default", "small"] as const;
+
+    sizes.forEach((size) => {
+      const { container } = render(
+        <Alert size={size}>
+          <AlertTitle>Test Title</AlertTitle>
+        </Alert>
+      );
+
+      expect(container.firstChild).toHaveClass(size === "default" ? "py-3" : "py-2");
+    });
+  });
+
+  it("renders with button and handles click", () => {
+    const handleClick = vi.fn();
+
+    render(
+      <Alert>
+        <AlertTitle>Test Title</AlertTitle>
+        <AlertButton onClick={handleClick}>Click me</AlertButton>
+      </Alert>
+    );
+
+    const button = screen.getByText("Click me");
+    expect(button).toBeInTheDocument();
+    fireEvent.click(button);
+    expect(handleClick).toHaveBeenCalledTimes(1);
+  });
+
+  it("applies custom className", () => {
+    const { container } = render(
+      <Alert className="custom-class">
+        <AlertTitle>Test Title</AlertTitle>
+      </Alert>
+    );
+
+    expect(container.firstChild).toHaveClass("custom-class");
+  });
+});
diff --git a/apps/web/modules/ui/components/alert/index.tsx b/apps/web/modules/ui/components/alert/index.tsx
index d9e3c89c6e63..c17f7f4b4ef2 100644
--- a/apps/web/modules/ui/components/alert/index.tsx
+++ b/apps/web/modules/ui/components/alert/index.tsx
@@ -1,49 +1,141 @@
+"use client";
+
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
+import { createContext, useContext } from "react";
 import { cn } from "@formbricks/lib/cn";
+import { Button, ButtonProps } from "../button";
+
+// Create a context to share variant and size with child components
+interface AlertContextValue {
+  variant?: "default" | "error" | "warning" | "info" | "success" | null;
+  size?: "default" | "small" | null;
+}
+
+const AlertContext = createContext<AlertContextValue>({
+  variant: "default",
+  size: "default",
+});
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+const useAlertContext = () => useContext(AlertContext);
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-lg border [&>svg]:size-4 [&>svg]:text-foreground", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error:
+        "text-error-foreground border-error/50 [&_button]:bg-error-background [&_button]:text-error-foreground [&_button:hover]:bg-error-background-muted",
+      warning:
+        "text-warning-foreground border-warning/50 [&_button]:bg-warning-background [&_button]:text-warning-foreground [&_button:hover]:bg-warning-background-muted",
+      info: "text-info-foreground border-info/50 [&_button]:bg-info-background [&_button]:text-info-foreground [&_button:hover]:bg-info-background-muted",
+      success:
+        "text-success-foreground border-success/50 [&_button]:bg-success-background [&_button]:text-success-foreground [&_button:hover]:bg-success-background-muted",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default:
+        "py-3 px-4 text-sm grid grid-cols-[1fr_auto] grid-rows-[auto_auto] gap-x-3 [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-7",
+      small:
+        "px-3 py-2 text-xs flex items-center justify-between gap-2 [&>svg]:flex-shrink-0 [&_button]:text-xs [&_button]:bg-transparent [&_button:hover]:bg-transparent [&>svg~*]:pl-0",
     },
-  }
-);
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+const alertVariantIcons: Record<"default" | "error" | "warning" | "info" | "success", React.ReactNode> = {
+  default: null,
+  error: <AlertCircle className="size-4" />,
+  warning: <AlertTriangle className="size-4" />,
+  info: <Info className="size-4" />,
+  success: <CheckCircle2Icon className="size-4" />,
+};
 
 const Alert = React.forwardRef<
   HTMLDivElement,
-  React.HTMLAttributes<HTMLDivElement> &
-    VariantProps<typeof alertVariants> & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, variant, ...props }, ref) => (
-  <div ref={ref} role="alert" className={cn(alertVariants({ variant }), className)} {...props} />
-));
+  React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
+>(({ className, variant, size, ...props }, ref) => {
+  const variantIcon = variant ? (variant !== "default" ? alertVariantIcons[variant] : null) : null;
+
+  return (
+    <AlertContext.Provider value={{ variant, size }}>
+      <div ref={ref} role="alert" className={cn(alertVariants({ variant, size }), className)} {...props}>
+        {variantIcon}
+        {props.children}
+      </div>
+    </AlertContext.Provider>
+  );
+});
 Alert.displayName = "Alert";
 
-const AlertTitle = React.forwardRef<
-  HTMLParagraphElement,
-  React.HTMLAttributes<HTMLHeadingElement> & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, ...props }, ref) => (
-  <h5 ref={ref} className={cn("mb-1 cursor-default font-medium leading-none", className)} {...props} />
-));
+const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
+  ({ className, ...props }, ref) => {
+    const { size } = useAlertContext();
+    return (
+      <h5
+        ref={ref}
+        className={cn(
+          "col-start-1 row-start-1 font-medium leading-none tracking-tight",
+          size === "small" ? "min-w-0 flex-shrink truncate" : "col-start-1 row-start-1",
+          className
+        )}
+        {...props}
+      />
+    );
+  }
+);
+
 AlertTitle.displayName = "AlertTitle";
 
-const AlertDescription = React.forwardRef<
-  HTMLParagraphElement,
-  React.HTMLAttributes<HTMLParagraphElement> & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, ...props }, ref) => (
-  <div ref={ref} className={cn("cursor-default text-sm [&_p]:leading-relaxed", className)} {...props} />
-));
+const AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
+  ({ className, ...props }, ref) => {
+    const { size } = useAlertContext();
+
+    return (
+      <div
+        ref={ref}
+        className={cn(
+          "[&_p]:leading-relaxed",
+          size === "small"
+            ? "hidden min-w-0 flex-shrink flex-grow truncate opacity-80 sm:block" // Hidden on very small screens, limited width
+            : "col-start-1 row-start-2",
+          className
+        )}
+        {...props}
+      />
+    );
+  }
+);
 AlertDescription.displayName = "AlertDescription";
 
-export { Alert, AlertDescription, AlertTitle };
+const AlertButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
+  ({ className, variant, size, children, ...props }, ref) => {
+    const { size: alertSize } = useAlertContext();
+
+    // Determine button styling based on alert context
+    const buttonVariant = variant || (alertSize === "small" ? "link" : "secondary");
+    const buttonSize = size || (alertSize === "small" ? "sm" : "default");
+
+    return (
+      <div
+        className={cn(
+          "self-end",
+          alertSize === "small"
+            ? "-my-2 -mr-3 ml-auto flex-shrink-0"
+            : "col-start-2 row-span-2 row-start-1 flex items-center justify-center"
+        )}>
+        <Button ref={ref} variant={buttonVariant} size={buttonSize} className={className} {...props}>
+          {children}
+        </Button>
+      </div>
+    );
+  }
+);
+
+AlertButton.displayName = "AlertButton";
+
+// Export the new component
+export { Alert, AlertTitle, AlertDescription, AlertButton };
diff --git a/apps/web/modules/ui/components/alert/stories.test.tsx b/apps/web/modules/ui/components/alert/stories.test.tsx
new file mode 100644
index 000000000000..af33eef348e2
--- /dev/null
+++ b/apps/web/modules/ui/components/alert/stories.test.tsx
@@ -0,0 +1,62 @@
+import { cleanup, render, screen } from "@testing-library/react";
+import { afterEach, describe, expect, it } from "vitest";
+import { Default, Error, Info, Small, Success, Warning, withButtonAndIcon } from "./stories";
+
+describe("Alert Stories", () => {
+  const renderStory = (Story: any) => {
+    return render(Story.render(Story.args));
+  };
+
+  afterEach(() => {
+    cleanup();
+  });
+
+  it("renders Default story", () => {
+    renderStory(Default);
+    expect(screen.getByText("Alert Title")).toBeInTheDocument();
+    expect(screen.getByText("This is an important notification.")).toBeInTheDocument();
+    expect(screen.queryByRole("button")).not.toBeInTheDocument();
+  });
+
+  it("renders Small story", () => {
+    renderStory(Small);
+    expect(screen.getByText("Information Alert")).toBeInTheDocument();
+    expect(screen.getByRole("button")).toBeInTheDocument();
+    expect(screen.getByText("Learn more")).toBeInTheDocument();
+  });
+
+  it("renders withButtonAndIcon story", () => {
+    renderStory(withButtonAndIcon);
+    expect(screen.getByText("Alert Title")).toBeInTheDocument();
+    expect(screen.getByRole("button")).toBeInTheDocument();
+    expect(screen.getByText("Learn more")).toBeInTheDocument();
+  });
+
+  it("renders Error story", () => {
+    renderStory(Error);
+    expect(screen.getByText("Error Alert")).toBeInTheDocument();
+    expect(screen.getByText("Your session has expired. Please log in again.")).toBeInTheDocument();
+    expect(screen.getByText("Log in")).toBeInTheDocument();
+  });
+
+  it("renders Warning story", () => {
+    renderStory(Warning);
+    expect(screen.getByText("Warning Alert")).toBeInTheDocument();
+    expect(screen.getByText("You are editing sensitive data. Be cautious")).toBeInTheDocument();
+    expect(screen.getByText("Proceed")).toBeInTheDocument();
+  });
+
+  it("renders Info story", () => {
+    renderStory(Info);
+    expect(screen.getByText("Info Alert")).toBeInTheDocument();
+    expect(screen.getByText("There was an update to your application.")).toBeInTheDocument();
+    expect(screen.getByText("Refresh")).toBeInTheDocument();
+  });
+
+  it("renders Success story", () => {
+    renderStory(Success);
+    expect(screen.getByText("Success Alert")).toBeInTheDocument();
+    expect(screen.getByText("This worked! Please proceed.")).toBeInTheDocument();
+    expect(screen.getByText("Close")).toBeInTheDocument();
+  });
+});
diff --git a/apps/web/modules/ui/components/alert/stories.tsx b/apps/web/modules/ui/components/alert/stories.tsx
index ff9017829b72..4d45ed1edbc1 100644
--- a/apps/web/modules/ui/components/alert/stories.tsx
+++ b/apps/web/modules/ui/components/alert/stories.tsx
@@ -1,49 +1,252 @@
-import type { Meta, StoryObj } from "@storybook/react";
-import { AlertCircle } from "lucide-react";
-import { Alert, AlertDescription, AlertTitle } from "./index";
+import { Meta, StoryObj } from "@storybook/react";
+import { LightbulbIcon } from "lucide-react";
+import { Alert, AlertButton, AlertDescription, AlertTitle } from "./index";
 
-const meta = {
-  title: "ui/Alert",
+// We'll define the story options separately from the component props
+interface StoryOptions {
+  title: string;
+  description: string;
+  showIcon: boolean;
+  showButton: boolean;
+  actionButtonText: string;
+}
+
+type StoryProps = React.ComponentProps<typeof Alert> & StoryOptions;
+
+const meta: Meta<StoryProps> = {
+  title: "UI/Alert",
   component: Alert,
   tags: ["autodocs"],
+  parameters: {
+    controls: {
+      sort: "requiredFirst",
+      exclude: [],
+    },
+  },
+  // These argTypes are for story controls, not component props
   argTypes: {
     variant: {
-      control: "radio",
-      options: ["default", "error"],
+      control: "select",
+      options: ["default", "error", "warning", "info", "success"],
+      description: "Style variant of the alert",
+      table: {
+        category: "Appearance",
+        type: { summary: "string" },
+        defaultValue: { summary: "default" },
+      },
+      order: 1,
+    },
+    size: {
+      control: "select",
+      options: ["default", "small"],
+      description: "Size of the alert component",
+      table: {
+        category: "Appearance",
+        type: { summary: "string" },
+        defaultValue: { summary: "default" },
+      },
+      order: 2,
+    },
+    showIcon: {
+      control: "boolean",
+      description: "Whether to show an icon",
+      table: {
+        category: "Appearance",
+        type: { summary: "boolean" },
+      },
+      order: 3,
+    },
+    showButton: {
+      control: "boolean",
+      description: "Whether to show action buttons",
+      table: {
+        category: "Appearance",
+        type: { summary: "boolean" },
+      },
+      order: 4,
+    },
+    title: {
+      control: "text",
+      description: "Alert title text",
+      table: {
+        category: "Content",
+        type: { summary: "string" },
+      },
+      order: 1,
+    },
+    description: {
+      control: "text",
+      description: "Alert description text",
+      table: {
+        category: "Content",
+        type: { summary: "string" },
+      },
+      order: 2,
+    },
+    actionButtonText: {
+      control: "text",
+      description: "Text for the action button",
+      table: {
+        category: "Content",
+        type: { summary: "string" },
+      },
+      order: 2,
     },
   },
+};
+
+export default meta;
+
+// Our story type just specifies Alert props plus our story options
+type Story = StoryObj<typeof Alert> & { args: StoryOptions };
+
+// Create a common render function to reduce duplication
+const renderAlert = (args: StoryProps) => {
+  // Extract component props
+  const { variant = "default", size = "default", className = "" } = args;
+
+  // Extract story content options
+  const {
+    title = "",
+    description = "",
+    showIcon = false,
+    showButton = false,
+    actionButtonText = "",
+  } = args as StoryOptions;
+
+  return (
+    <Alert variant={variant} size={size} className={className}>
+      {showIcon && <LightbulbIcon />}
+      <AlertTitle className={showIcon ? "pl-7" : ""}>{title}</AlertTitle>
+      {description && <AlertDescription className={showIcon ? "pl-7" : ""}>{description}</AlertDescription>}
+      {showButton && <AlertButton onClick={() => alert("Button clicked")}>{actionButtonText}</AlertButton>}
+    </Alert>
+  );
+};
+
+// Basic example with direct props
+export const Default: Story = {
+  render: renderAlert,
   args: {
     variant: "default",
+    showIcon: false,
+    showButton: false,
+    title: "Alert Title",
+    description: "This is an important notification.",
+    actionButtonText: "Learn more",
   },
-  render: (args) => (
-    <Alert {...args}>
-      <AlertTitle>This is an alert</AlertTitle>
-      <AlertDescription>This is a description</AlertDescription>
-    </Alert>
-  ),
-} satisfies Meta<typeof Alert>;
-
-export default meta;
+};
 
-type Story = StoryObj<typeof meta>;
+// Small size example
+export const Small: Story = {
+  render: renderAlert,
+  args: {
+    variant: "default",
+    size: "small",
+    title: "Information Alert",
+    description: "This is an important notification.",
+    showIcon: false,
+    showButton: true,
+    actionButtonText: "Learn more",
+  },
+  parameters: {
+    docs: {
+      description: {
+        story: "Use if space is limited or the alert is not the main focus.",
+      },
+    },
+  },
+};
 
-export const Default: Story = {};
+// With custom icon
+export const withButtonAndIcon: Story = {
+  render: renderAlert,
+  args: {
+    variant: "default",
+    title: "Alert Title",
+    description: "This is an important notification.",
+    showIcon: true,
+    showButton: true,
+    actionButtonText: "Learn more",
+  },
+};
 
+// Error variant
 export const Error: Story = {
+  render: renderAlert,
   args: {
     variant: "error",
+    title: "Error Alert",
+    description: "Your session has expired. Please log in again.",
+    showIcon: false,
+    showButton: true,
+    actionButtonText: "Log in",
+  },
+  parameters: {
+    docs: {
+      description: {
+        story: "Only use if the user needs to take immediate action or there is a critical error.",
+      },
+    },
   },
 };
 
-export const WithIcon: Story = {
+// Warning variant
+export const Warning: Story = {
+  render: renderAlert,
   args: {
-    variant: "error",
+    variant: "warning",
+    title: "Warning Alert",
+    description: "You are editing sensitive data. Be cautious",
+    showIcon: false,
+    showButton: true,
+    actionButtonText: "Proceed",
+  },
+  parameters: {
+    docs: {
+      description: {
+        story: "Use this to make the user aware of potential issues.",
+      },
+    },
+  },
+};
+
+// Info variant
+export const Info: Story = {
+  render: renderAlert,
+  args: {
+    variant: "info",
+    title: "Info Alert",
+    description: "There was an update to your application.",
+    showIcon: false,
+    showButton: true,
+    actionButtonText: "Refresh",
+  },
+  parameters: {
+    docs: {
+      description: {
+        story: "Use this to give contextual information and support the user.",
+      },
+    },
+  },
+};
+
+// Success variant
+export const Success: Story = {
+  render: renderAlert,
+  args: {
+    variant: "success",
+    title: "Success Alert",
+    description: "This worked! Please proceed.",
+    showIcon: false,
+    showButton: true,
+    actionButtonText: "Close",
+  },
+  parameters: {
+    docs: {
+      description: {
+        story: "Use this to give positive feedback.",
+      },
+    },
   },
-  render: (args) => (
-    <Alert {...args}>
-      <AlertCircle className="h-4 w-4" />
-      <AlertTitle>This is an alert</AlertTitle>
-      <AlertDescription>This is a description</AlertDescription>
-    </Alert>
-  ),
 };
diff --git a/apps/web/tailwind.config.js b/apps/web/tailwind.config.js
index 774c90f9e002..120265f69d53 100644
--- a/apps/web/tailwind.config.js
+++ b/apps/web/tailwind.config.js
@@ -1,3 +1,5 @@
+const colors = require("tailwindcss/colors");
+
 module.exports = {
   content: [
     // app content
@@ -39,7 +41,7 @@ module.exports = {
           dark: "#00C4B8",
         },
         focus: "var(--formbricks-focus, #1982fc)",
-        error: "rgb(from var(--formbricks-error) r g b / <alpha-value>)",
+        // error: "rgb(from var(--formbricks-error) r g b / <alpha-value>)",
         brandnew: "var(--formbricks-brand, #038178)",
         primary: {
           DEFAULT: "#0f172a",
@@ -57,6 +59,34 @@ module.exports = {
           DEFAULT: "#f4f6f8", // light gray background
           foreground: "#0f172a", // same as primary default for consistency
         },
+        info: {
+          DEFAULT: colors.blue[600],
+          foreground: colors.blue[900],
+          muted: colors.blue[700],
+          background: colors.blue[50],
+          "background-muted": colors.blue[100],
+        },
+        warning: {
+          DEFAULT: colors.amber[500],
+          foreground: colors.amber[900],
+          muted: colors.amber[700],
+          background: colors.amber[50],
+          "background-muted": colors.amber[100],
+        },
+        success: {
+          DEFAULT: colors.green[600],
+          foreground: colors.green[900],
+          muted: colors.green[700],
+          background: colors.green[50],
+          "background-muted": colors.green[100],
+        },
+        error: {
+          DEFAULT: colors.red[600],
+          foreground: colors.red[900],
+          muted: colors.red[700],
+          background: colors.red[50],
+          "background-muted": colors.red[100],
+        },
       },
       keyframes: {
         fadeIn: {
diff --git a/apps/web/vite.config.mts b/apps/web/vite.config.mts
index cd78a293485d..25297608c915 100644
--- a/apps/web/vite.config.mts
+++ b/apps/web/vite.config.mts
@@ -26,6 +26,7 @@ export default defineConfig({
         "modules/email/components/email-template.tsx",
         "modules/email/emails/survey/follow-up.tsx",
         "modules/ui/components/post-hog-client/*.tsx",
+        "modules/ui/components/alert/*.tsx",
         "app/(app)/environments/**/layout.tsx",
         "app/(app)/environments/**/settings/(organization)/general/page.tsx",
         "app/(app)/environments/**/components/PosthogIdentify.tsx",


Reviews:
user: login: jobenjada
body: Hey! Overall, it looks quite good, even though rather complicated at times. I've consulted a bit on GPT and my rather limited code intuition so take my feedback with a grain of salt ;) 

On storybook:

I find the hover state of the button too subtle, but that's a button component issue:

<img width="134" alt="image" src="https://github.com/user-attachments/assets/0c612e9b-59e1-406e-b499-d94f740569a8" />

Very long button text breaks it (can likely be ignored but wanted to share):
<img width="476" alt="image" src="https://github.com/user-attachments/assets/1cbbc7f4-5129-44ec-80cf-6139632245f3" />

I feel this should be the first setting, not the last:
<img width="1034" alt="image" src="https://github.com/user-attachments/assets/9781beda-7da6-41bd-9f68-3dc41b20ac76" />

Overall, great work on Storybook! 💪 💪💪

state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-19T19:21:48Z
user: login: jakobsitory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T16:35:13Z
user: login: jakobsitory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T16:35:32Z
user: login: jakobsitory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T16:36:17Z
user: login: jakobsitory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T16:37:01Z
user: login: jakobsitory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T16:37:47Z
user: login: jakobsitory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T16:39:58Z
user: login: jakobsitory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-20T16:40:53Z
user: login: jakobsitory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T15:44:54Z
user: login: jakobsitory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T15:48:31Z
user: login: jakobsitory
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T15:49:15Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (2)</summary><blockquote>

<details>
<summary>apps/web/tailwind.config.js (1)</summary><blockquote>

`44-44`: **Consider removing commented-out code.**

The commented-out error color definition suggests you're transitioning to the new structured approach, which is good. For code cleanliness, you might want to remove this line once the transition is complete.

</blockquote></details>
<details>
<summary>apps/web/modules/ui/components/alert/stories.tsx (1)</summary><blockquote>

`14-94`: **Comprehensive Storybook metadata with well-organized controls.**

The metadata is well-structured with:
- Appropriate control types for each option
- Clear descriptions
- Logical categorization
- Explicit ordering

One minor suggestion: consider avoiding `as any` when possible to maintain type safety.


```diff
-  } as any,
+  },
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 010784c2b2285dccf48549e155ba9954da3f7c20 and 0ea891c32f9028fe35019ac108e162a8707d3dbc.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `apps/web/modules/ui/components/alert/index.tsx` (1 hunks)
* `apps/web/modules/ui/components/alert/stories.tsx` (1 hunks)
* `apps/web/tailwind.config.js` (3 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧬 Code Definitions (2)</summary>

<details>
<summary>apps/web/modules/ui/components/alert/index.tsx (1)</summary><blockquote>

<details>
<summary>apps/web/modules/ui/components/alert/stories.tsx (1)</summary>

* `Info` (213-230)

</details>

</blockquote></details>
<details>
<summary>apps/web/modules/ui/components/alert/stories.tsx (1)</summary><blockquote>

<details>
<summary>apps/web/modules/ui/components/alert/index.tsx (4)</summary>

* `Alert` (141-141)
* `AlertTitle` (141-141)
* `AlertDescription` (141-141)
* `AlertButton` (141-141)

</details>

</blockquote></details>

</details><details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>apps/web/modules/ui/components/alert/stories.tsx</summary>

[error] 173-173: Do not shadow the global "Error" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (1)</summary>

* GitHub Check: Tusk Tester

</details>

<details>
<summary>🔇 Additional comments (20)</summary><blockquote>

<details>
<summary>apps/web/tailwind.config.js (2)</summary>

`1-2`: **Good addition of Tailwind colors module.**

Using the official `tailwindcss/colors` module is a great choice for standardizing color usage across the application.

---

`62-89`: **Excellent structured color definitions for alert variants.**

The new color definitions for info, warning, success, and error are well-structured with consistent properties:
- `DEFAULT` for the main color
- `foreground` for text
- `muted` for slightly darker versions
- `background` and `background-muted` for backgrounds

This approach creates a cohesive design system that will make implementing and maintaining the alert variants much easier.

</details>
<details>
<summary>apps/web/modules/ui/components/alert/stories.tsx (9)</summary>

`1-3`: **LGTM!**

Good update to the imports to include LightbulbIcon and AlertButton for the enhanced alert stories.

---

`5-12`: **Good separation of story options from component props.**

Creating a dedicated interface for story options improves code organization and makes it clear which options are for stories versus component props.

---

`101-123`: **Great DRY approach with common render function.**

The `renderAlert` function effectively reduces duplication across stories while maintaining flexibility. The separation of component props from story options makes the code more maintainable.

---

`126-136`: **LGTM!**

Updated Default story with appropriate props and description.

---

`139-157`: **Good addition of Small size variant.**

The Small size variant is well-documented with clear usage guidance in the description.

---

`160-170`: **LGTM!**

The withButtonAndIcon story effectively demonstrates the combined use of icon and button.

---

`193-210`: **Good implementation of Warning variant.**

The Warning variant story is well-configured with appropriate title, description, and docs.

---

`213-230`: **Good implementation of Info variant.**

The Info variant story is well-configured with clear usage guidance.

---

`233-250`: **Good implementation of Success variant.**

The Success variant story completes the set of contextual alert variants with appropriate styling and documentation.

</details>
<details>
<summary>apps/web/modules/ui/components/alert/index.tsx (9)</summary>

`1-9`: **Good imports and client directive.**

Adding the required imports for the new alert variants and the Button component. The `use client` directive is correctly placed at the top of the file.

---

`10-22`: **Excellent context-based approach for sharing alert properties.**

Creating a context to share variant and size between the Alert and its child components is a clean way to avoid prop drilling. The hook implementation makes it easy for child components to consume these values.

---

`24-47`: **Well-structured variant and size styles.**

The alert variants are comprehensive with:
- Consistent styling across variants
- Detailed selector patterns for nested elements
- Clear size variations with responsive considerations
- Good use of Tailwind's utility classes

The CSS selectors are advanced but readable, making it easy to understand the styling hierarchy.

---

`49-55`: **Good standardization of variant icons.**

Defining a consistent set of icons for each variant ensures visual consistency across the application.

---

`57-71`: **Clean implementation of Alert component with context provider.**

The component correctly:
- Wraps children in the context provider
- Conditionally renders the appropriate variant icon
- Applies variant and size styling

This implementation makes the Alert component both flexible and easy to extend.

---

`74-90`: **Well-adapted AlertTitle with context awareness.**

The AlertTitle component now consumes the context to adapt its styling based on the alert size. The conditional classes for small size handle truncation appropriately.

---

`93-112`: **Good responsive design in AlertDescription.**

The AlertDescription component adapts well to different sizes with:
- Appropriate positioning based on size
- Responsive visibility (hidden on very small screens for small size)
- Proper truncation for overflow

This ensures the alert remains usable across different device sizes.

---

`114-138`: **Well-implemented AlertButton component.**

The AlertButton:
- Correctly adapts its style based on the alert context
- Has appropriate positioning for different alert sizes
- Uses sensible defaults that can be overridden with props

This component completes the alert system by providing standardized action buttons.

---

`139-141`: **Proper export of all alert components.**

The export includes all the necessary components for the alert system.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-24T17:20:43Z
user: login: Dhruwang
body: Looks good, I just fixed some minor type issues 😊🚀✅
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-27T11:54:44Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>apps/web/modules/ui/components/alert/stories.test.tsx (1)</summary><blockquote>

`5-8`: **Consider typing the renderStory function parameters**

The function uses `any` type for the Story parameter, which loses type safety. Since you're using TypeScript, you could provide a more specific type.


```diff
- const renderStory = (Story: any) => {
+ import type { StoryObj } from "@storybook/react";
+ 
+ const renderStory = <T,>(Story: StoryObj<T>) => {
    return render(Story.render(Story.args));
  };
```

</blockquote></details>
<details>
<summary>apps/web/modules/ui/components/alert/index.test.tsx (2)</summary><blockquote>

`19-33`: **Add test for variant-specific classes beyond text color**

While the test checks if the text color class is applied based on the variant, it doesn't verify other variant-specific classes that might be applied (like background color, border, etc.).


Consider extending the test to check for additional variant-specific classes:

```diff
  expect(container.firstChild).toHaveClass(
    variant === "default" ? "text-foreground" : `text-${variant}-foreground`
  );
+ expect(container.firstChild).toHaveClass(
+   variant === "default" ? "bg-background" : `bg-${variant}-background`
+ );
```

Also, consider adding a cleanup between test iterations to ensure a clean DOM:

```diff
  variants.forEach((variant) => {
+   afterEach(cleanup);
    const { container } = render(
      // ...
```

---

`5-74`: **Consider adding tests for combinations of variants and sizes**

The current tests check variants and sizes independently, but it would be valuable to test combinations to ensure they work together correctly.


```typescript
it("renders with combination of variant and size", () => {
  const { container } = render(
    <Alert variant="error" size="small">
      <AlertTitle>Test Title</AlertTitle>
    </Alert>
  );

  expect(container.firstChild).toHaveClass("text-error-foreground");
  expect(container.firstChild).toHaveClass("py-2");
});
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between ec5359cbce94db40530106c1ffb7af8591e580f8 and 42ae9b7415f241a3dc94df6e8c07779baf0df945.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `apps/web/modules/ui/components/alert/index.test.tsx` (1 hunks)
* `apps/web/modules/ui/components/alert/stories.test.tsx` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

<details>
<summary>apps/web/modules/ui/components/alert/stories.test.tsx</summary>

[error] 3-3: Do not shadow the global "Error" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (3)</summary>

* GitHub Check: Run E2E Tests / Run E2E Tests
* GitHub Check: Build Formbricks-web / Build Formbricks-web
* GitHub Check: SonarQube

</details>

<details>
<summary>🔇 Additional comments (12)</summary><blockquote>

<details>
<summary>apps/web/modules/ui/components/alert/stories.test.tsx (7)</summary>

`14-19`: **LGTM! Test for Default story**

The test correctly verifies the rendering of the Default alert story and checks that no button is present.

---

`21-26`: **LGTM! Test for Small story**

The test properly checks the small variant alert rendering, including the presence of a button and its text.

---

`28-33`: **LGTM! Test for withButtonAndIcon story**

The test correctly verifies that the alert with button and icon renders as expected.

---

`35-40`: **LGTM! Test for Error story**

The test properly checks the error variant alert rendering with the correct text and button.

---

`42-47`: **LGTM! Test for Warning story**

The test correctly verifies the warning variant alert rendering with appropriate content.

---

`49-54`: **LGTM! Test for Info story**

The test properly checks the info variant alert rendering with the correct text and button.

---

`56-61`: **LGTM! Test for Success story**

The test correctly verifies the success variant alert rendering with appropriate content.

</details>
<details>
<summary>apps/web/modules/ui/components/alert/index.test.tsx (5)</summary>

`1-4`: **LGTM! Imports and setup**

The test file has all the necessary imports for testing the Alert component and its subcomponents.

---

`5-17`: **LGTM! Default variant rendering test**

The test correctly verifies that the Alert component renders with its default variant and checks for the presence of title and description elements.

---

`35-47`: **LGTM! Size rendering tests**

The test properly checks that the correct padding classes are applied based on the size prop.

---

`49-63`: **LGTM! Button rendering and click handling test**

The test effectively verifies that the AlertButton renders correctly and that click events are properly handled.

---

`65-73`: **LGTM! Custom class application test**

The test correctly verifies that custom classes can be applied to the Alert component.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T12:15:44Z

Review comments:
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: do we want to start moving to the design tokens here? If so, any idea how? I suppose something like `text-error` is what we're looking for
created_at: 2025-03-19T18:46:05Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: learned about cloneElement and consulted with GPT, seems to be the right choice here ✅
created_at: 2025-03-19T18:46:30Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: I believe we keep props at the top of the file, but might be wrong

created_at: 2025-03-19T18:48:54Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: Why did you decide to introduce an additional prop for this? You could check if children is truthy, right?
created_at: 2025-03-19T18:50:49Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
+    if (allowChildren && children) {
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: this feels complicated, how about

```
const extractedTitle = title ? <AlertTitle>{title}</AlertTitle> : null;
const extractedDescription = description ? <AlertDescription>{description}</AlertDescription> : null;
```
created_at: 2025-03-19T18:55:11Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: also rather complex, how about

```
const renderIcon = icon !== null && (React.isValidElement(icon) ? 
  React.cloneElement(icon, { className: cn("h-4 w-4", icon.props.className) }) 
  : variantIcons[variant]
);
```
created_at: 2025-03-19T18:56:32Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
+    if (allowChildren && children) {
+      childrenArray.forEach((child) => {
+        if (React.isValidElement(child)) {
+          const element = child as React.ReactElement<{ children?: React.ReactNode }>;
+          if (element.type === AlertTitle && !extractedTitle) {
+            extractedTitle = element.props.children;
+          } else if (element.type === AlertDescription && !extractedDescription) {
+            extractedDescription = element.props.children;
+          } else {
+            otherContent.push(child);
+          }
+        } else {
+          otherContent.push(child);
+        }
+      });
+    }
+
+    // Compute condition: true if only a title or only a description is provided
+    const singleText = (extractedTitle ? 1 : 0) + (extractedDescription ? 1 : 0) === 1;
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: this feels quite unconventional 🤔
created_at: 2025-03-19T19:01:18Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
+    if (allowChildren && children) {
+      childrenArray.forEach((child) => {
+        if (React.isValidElement(child)) {
+          const element = child as React.ReactElement<{ children?: React.ReactNode }>;
+          if (element.type === AlertTitle && !extractedTitle) {
+            extractedTitle = element.props.children;
+          } else if (element.type === AlertDescription && !extractedDescription) {
+            extractedDescription = element.props.children;
+          } else {
+            otherContent.push(child);
+          }
+        } else {
+          otherContent.push(child);
+        }
+      });
+    }
+
+    // Compute condition: true if only a title or only a description is provided
+    const singleText = (extractedTitle ? 1 : 0) + (extractedDescription ? 1 : 0) === 1;
+
+    // Handle button creation from either React element or props object
+    const buttonElement = React.isValidElement(button) ? (
+      button
+    ) : button ? (
+      <AlertButton
+        onClick={button.onClick}
+        loading={button.loading}
+        disabled={button.disabled}
+        className={button.className}
+        variant={button.variant}>
+        {button.label}
+      </AlertButton>
+    ) : undefined;
+
+    const buttonStyles = getButtonStyles(variant || "default", size || "default");
+
+    // Create structured content
+    const titleElement = extractedTitle ? <AlertTitle>{extractedTitle}</AlertTitle> : null;
+    const descriptionElement = extractedDescription ? (
+      size === "small" ? (
+        <AlertDescription className="truncate">{extractedDescription}</AlertDescription>
+      ) : (
+        <AlertDescription>{extractedDescription}</AlertDescription>
+      )
+    ) : null;
+
+    return (
+      <div ref={ref} role="alert" className={cn(alertVariants({ variant, size }), className)} {...props}>
+        {size === "default" ? (
+          <div className="flex">
+            <div className="flex flex-grow items-center gap-2">
+              {/* Icon section with optional click handler */}
+              {renderIcon && (
+                <div
+                  className={cn(
+                    singleText ? "self-center" : "mt-0.5 self-start",
+                    "flex-shrink-0",
+                    onIconClick && "cursor-pointer"
+                  )}
+                  onClick={onIconClick}>
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: you want to make the icon clickable?
created_at: 2025-03-19T19:02:47Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: can we avoid that and refactor the components where this would needed? I don't think it makes sense to provide backwards compitability for an alert component 🤓
created_at: 2025-03-19T19:04:29Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
+    if (allowChildren && children) {
+      childrenArray.forEach((child) => {
+        if (React.isValidElement(child)) {
+          const element = child as React.ReactElement<{ children?: React.ReactNode }>;
+          if (element.type === AlertTitle && !extractedTitle) {
+            extractedTitle = element.props.children;
+          } else if (element.type === AlertDescription && !extractedDescription) {
+            extractedDescription = element.props.children;
+          } else {
+            otherContent.push(child);
+          }
+        } else {
+          otherContent.push(child);
+        }
+      });
+    }
+
+    // Compute condition: true if only a title or only a description is provided
+    const singleText = (extractedTitle ? 1 : 0) + (extractedDescription ? 1 : 0) === 1;
+
+    // Handle button creation from either React element or props object
+    const buttonElement = React.isValidElement(button) ? (
+      button
+    ) : button ? (
+      <AlertButton
+        onClick={button.onClick}
+        loading={button.loading}
+        disabled={button.disabled}
+        className={button.className}
+        variant={button.variant}>
+        {button.label}
+      </AlertButton>
+    ) : undefined;
+
+    const buttonStyles = getButtonStyles(variant || "default", size || "default");
+
+    // Create structured content
+    const titleElement = extractedTitle ? <AlertTitle>{extractedTitle}</AlertTitle> : null;
+    const descriptionElement = extractedDescription ? (
+      size === "small" ? (
+        <AlertDescription className="truncate">{extractedDescription}</AlertDescription>
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: why not use one AlertDescription and set the className dynamically based on the size? 🤓
created_at: 2025-03-19T19:05:45Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
+    if (allowChildren && children) {
+      childrenArray.forEach((child) => {
+        if (React.isValidElement(child)) {
+          const element = child as React.ReactElement<{ children?: React.ReactNode }>;
+          if (element.type === AlertTitle && !extractedTitle) {
+            extractedTitle = element.props.children;
+          } else if (element.type === AlertDescription && !extractedDescription) {
+            extractedDescription = element.props.children;
+          } else {
+            otherContent.push(child);
+          }
+        } else {
+          otherContent.push(child);
+        }
+      });
+    }
+
+    // Compute condition: true if only a title or only a description is provided
+    const singleText = (extractedTitle ? 1 : 0) + (extractedDescription ? 1 : 0) === 1;
+
+    // Handle button creation from either React element or props object
+    const buttonElement = React.isValidElement(button) ? (
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: also feels unconventional, there must a better way to solve this
created_at: 2025-03-19T19:07:21Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
+    if (allowChildren && children) {
+      childrenArray.forEach((child) => {
+        if (React.isValidElement(child)) {
+          const element = child as React.ReactElement<{ children?: React.ReactNode }>;
+          if (element.type === AlertTitle && !extractedTitle) {
+            extractedTitle = element.props.children;
+          } else if (element.type === AlertDescription && !extractedDescription) {
+            extractedDescription = element.props.children;
+          } else {
+            otherContent.push(child);
+          }
+        } else {
+          otherContent.push(child);
+        }
+      });
+    }
+
+    // Compute condition: true if only a title or only a description is provided
+    const singleText = (extractedTitle ? 1 : 0) + (extractedDescription ? 1 : 0) === 1;
+
+    // Handle button creation from either React element or props object
+    const buttonElement = React.isValidElement(button) ? (
+      button
+    ) : button ? (
+      <AlertButton
+        onClick={button.onClick}
+        loading={button.loading}
+        disabled={button.disabled}
+        className={button.className}
+        variant={button.variant}>
+        {button.label}
+      </AlertButton>
+    ) : undefined;
+
+    const buttonStyles = getButtonStyles(variant || "default", size || "default");
+
+    // Create structured content
+    const titleElement = extractedTitle ? <AlertTitle>{extractedTitle}</AlertTitle> : null;
+    const descriptionElement = extractedDescription ? (
+      size === "small" ? (
+        <AlertDescription className="truncate">{extractedDescription}</AlertDescription>
+      ) : (
+        <AlertDescription>{extractedDescription}</AlertDescription>
+      )
+    ) : null;
+
+    return (
+      <div ref={ref} role="alert" className={cn(alertVariants({ variant, size }), className)} {...props}>
+        {size === "default" ? (
+          <div className="flex">
+            <div className="flex flex-grow items-center gap-2">
+              {/* Icon section with optional click handler */}
+              {renderIcon && (
+                <div
+                  className={cn(
+                    singleText ? "self-center" : "mt-0.5 self-start",
+                    "flex-shrink-0",
+                    onIconClick && "cursor-pointer"
+                  )}
+                  onClick={onIconClick}>
+                  {renderIcon}
+                </div>
+              )}
+
+              {/* Content section */}
+              <div className="space-y-1">
+                {titleElement}
+                {descriptionElement}
+                {allowChildren && otherContent}
+              </div>
+            </div>
+
+            {/* Button section for default size */}
+            {buttonElement && (
+              <div className="ml-4 flex-shrink-0 self-end">
+                <ButtonWrapper buttonStyles={buttonStyles}>{buttonElement}</ButtonWrapper>
+              </div>
+            )}
+          </div>
+        ) : (
+          <div className="flex items-center gap-2">
+            {/* Icon section with optional click handler */}
+            {renderIcon && (
+              <div className={cn("flex-shrink-0", onIconClick && "cursor-pointer")} onClick={onIconClick}>
+                {renderIcon}
+              </div>
+            )}
+
+            {/* Content section for small size - horizontal layout with truncation */}
+            <div className="flex min-w-0 flex-1 items-baseline space-x-1 py-2 pr-3">
+              {titleElement &&
+                React.cloneElement(titleElement as React.ReactElement<{ className?: string }>, {
+                  className: cn(
+                    "truncate",
+                    (titleElement as React.ReactElement<{ className?: string }>).props?.className || ""
+                  ),
+                })}
+              {descriptionElement && (
+                <div className="hidden truncate text-sm opacity-80 sm:block">{descriptionElement}</div>
+              )}
+              {allowChildren && otherContent}
+            </div>
+
+            {/* Button section for small size */}
+            {buttonElement && (
+              <div className="flex-shrink-0">
+                <ButtonWrapper buttonStyles={buttonStyles}>{buttonElement}</ButtonWrapper>
+              </div>
+            )}
+          </div>
+        )}
+      </div>
+    );
   }
 );
-
-const Alert = React.forwardRef<
-  HTMLDivElement,
-  React.HTMLAttributes<HTMLDivElement> &
-    VariantProps<typeof alertVariants> & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, variant, ...props }, ref) => (
-  <div ref={ref} role="alert" className={cn(alertVariants({ variant }), className)} {...props} />
-));
 Alert.displayName = "Alert";
 
-const AlertTitle = React.forwardRef<
-  HTMLParagraphElement,
-  React.HTMLAttributes<HTMLHeadingElement> & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, ...props }, ref) => (
-  <h5 ref={ref} className={cn("mb-1 cursor-default font-medium leading-none", className)} {...props} />
-));
+// AlertTitle component
+const AlertTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(
+  ({ className, children, ...props }, ref) => (
+    <h5 ref={ref} className={cn("text-sm font-medium leading-none", className)} {...props}>
+      {children}
+    </h5>
+  )
+);
 AlertTitle.displayName = "AlertTitle";
 
-const AlertDescription = React.forwardRef<
-  HTMLParagraphElement,
-  React.HTMLAttributes<HTMLParagraphElement> & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, ...props }, ref) => (
-  <div ref={ref} className={cn("cursor-default text-sm [&_p]:leading-relaxed", className)} {...props} />
-));
+// AlertDescription component
+const AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
+  ({ className, children, ...props }, ref) => (
+    <div ref={ref} className={cn("text-sm", className)} {...props}>
+      {children}
+    </div>
+  )
+);
 AlertDescription.displayName = "AlertDescription";
 
-export { Alert, AlertDescription, AlertTitle };
+// AlertButton component
+const AlertButton = React.forwardRef<
+  HTMLButtonElement,
+  React.ButtonHTMLAttributes<HTMLButtonElement> & { loading?: boolean; variant?: string }
+>(({ className, loading, children, ...props }, ref) => (
+  <Button
+    ref={ref}
+    className={cn("shrink-0", className)}
+    disabled={loading || props.disabled}
+    {...props}
+    variant={
+      props.variant as
+        | "default"
+        | "link"
+        | "secondary"
+        | "destructive"
+        | "outline"
+        | "ghost"
+        | null
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jobenjada
body: we need null and undefined here?
created_at: 2025-03-19T19:10:51Z
author_association: MEMBER
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jakobsitory
body: Moved props to the top
created_at: 2025-03-20T16:35:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jakobsitory
body: removed additional prop
created_at: 2025-03-20T16:35:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
+    if (allowChildren && children) {
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jakobsitory
body: Simplified how text consts are set
created_at: 2025-03-20T16:36:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jakobsitory
body: Used a simpler way, similar to the proposed one
created_at: 2025-03-20T16:37:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
+    if (allowChildren && children) {
+      childrenArray.forEach((child) => {
+        if (React.isValidElement(child)) {
+          const element = child as React.ReactElement<{ children?: React.ReactNode }>;
+          if (element.type === AlertTitle && !extractedTitle) {
+            extractedTitle = element.props.children;
+          } else if (element.type === AlertDescription && !extractedDescription) {
+            extractedDescription = element.props.children;
+          } else {
+            otherContent.push(child);
+          }
+        } else {
+          otherContent.push(child);
+        }
+      });
+    }
+
+    // Compute condition: true if only a title or only a description is provided
+    const singleText = (extractedTitle ? 1 : 0) + (extractedDescription ? 1 : 0) === 1;
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jakobsitory
body: Solved this via a simpler conditional logic
created_at: 2025-03-20T16:37:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jakobsitory
body: Removed handling of 'legacy' alerts. Set up a test page with all current alert instances. Will make sure, they keep working in branch [fix-replace-alert-component](https://github.com/formbricks/formbricks/tree/fix-replace-alert-component)
created_at: 2025-03-20T16:39:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
+    if (allowChildren && children) {
+      childrenArray.forEach((child) => {
+        if (React.isValidElement(child)) {
+          const element = child as React.ReactElement<{ children?: React.ReactNode }>;
+          if (element.type === AlertTitle && !extractedTitle) {
+            extractedTitle = element.props.children;
+          } else if (element.type === AlertDescription && !extractedDescription) {
+            extractedDescription = element.props.children;
+          } else {
+            otherContent.push(child);
+          }
+        } else {
+          otherContent.push(child);
+        }
+      });
+    }
+
+    // Compute condition: true if only a title or only a description is provided
+    const singleText = (extractedTitle ? 1 : 0) + (extractedDescription ? 1 : 0) === 1;
+
+    // Handle button creation from either React element or props object
+    const buttonElement = React.isValidElement(button) ? (
+      button
+    ) : button ? (
+      <AlertButton
+        onClick={button.onClick}
+        loading={button.loading}
+        disabled={button.disabled}
+        className={button.className}
+        variant={button.variant}>
+        {button.label}
+      </AlertButton>
+    ) : undefined;
+
+    const buttonStyles = getButtonStyles(variant || "default", size || "default");
+
+    // Create structured content
+    const titleElement = extractedTitle ? <AlertTitle>{extractedTitle}</AlertTitle> : null;
+    const descriptionElement = extractedDescription ? (
+      size === "small" ? (
+        <AlertDescription className="truncate">{extractedDescription}</AlertDescription>
+      ) : (
+        <AlertDescription>{extractedDescription}</AlertDescription>
+      )
+    ) : null;
+
+    return (
+      <div ref={ref} role="alert" className={cn(alertVariants({ variant, size }), className)} {...props}>
+        {size === "default" ? (
+          <div className="flex">
+            <div className="flex flex-grow items-center gap-2">
+              {/* Icon section with optional click handler */}
+              {renderIcon && (
+                <div
+                  className={cn(
+                    singleText ? "self-center" : "mt-0.5 self-start",
+                    "flex-shrink-0",
+                    onIconClick && "cursor-pointer"
+                  )}
+                  onClick={onIconClick}>
+                  {renderIcon}
+                </div>
+              )}
+
+              {/* Content section */}
+              <div className="space-y-1">
+                {titleElement}
+                {descriptionElement}
+                {allowChildren && otherContent}
+              </div>
+            </div>
+
+            {/* Button section for default size */}
+            {buttonElement && (
+              <div className="ml-4 flex-shrink-0 self-end">
+                <ButtonWrapper buttonStyles={buttonStyles}>{buttonElement}</ButtonWrapper>
+              </div>
+            )}
+          </div>
+        ) : (
+          <div className="flex items-center gap-2">
+            {/* Icon section with optional click handler */}
+            {renderIcon && (
+              <div className={cn("flex-shrink-0", onIconClick && "cursor-pointer")} onClick={onIconClick}>
+                {renderIcon}
+              </div>
+            )}
+
+            {/* Content section for small size - horizontal layout with truncation */}
+            <div className="flex min-w-0 flex-1 items-baseline space-x-1 py-2 pr-3">
+              {titleElement &&
+                React.cloneElement(titleElement as React.ReactElement<{ className?: string }>, {
+                  className: cn(
+                    "truncate",
+                    (titleElement as React.ReactElement<{ className?: string }>).props?.className || ""
+                  ),
+                })}
+              {descriptionElement && (
+                <div className="hidden truncate text-sm opacity-80 sm:block">{descriptionElement}</div>
+              )}
+              {allowChildren && otherContent}
+            </div>
+
+            {/* Button section for small size */}
+            {buttonElement && (
+              <div className="flex-shrink-0">
+                <ButtonWrapper buttonStyles={buttonStyles}>{buttonElement}</ButtonWrapper>
+              </div>
+            )}
+          </div>
+        )}
+      </div>
+    );
   }
 );
-
-const Alert = React.forwardRef<
-  HTMLDivElement,
-  React.HTMLAttributes<HTMLDivElement> &
-    VariantProps<typeof alertVariants> & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, variant, ...props }, ref) => (
-  <div ref={ref} role="alert" className={cn(alertVariants({ variant }), className)} {...props} />
-));
 Alert.displayName = "Alert";
 
-const AlertTitle = React.forwardRef<
-  HTMLParagraphElement,
-  React.HTMLAttributes<HTMLHeadingElement> & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, ...props }, ref) => (
-  <h5 ref={ref} className={cn("mb-1 cursor-default font-medium leading-none", className)} {...props} />
-));
+// AlertTitle component
+const AlertTitle = React.forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>(
+  ({ className, children, ...props }, ref) => (
+    <h5 ref={ref} className={cn("text-sm font-medium leading-none", className)} {...props}>
+      {children}
+    </h5>
+  )
+);
 AlertTitle.displayName = "AlertTitle";
 
-const AlertDescription = React.forwardRef<
-  HTMLParagraphElement,
-  React.HTMLAttributes<HTMLParagraphElement> & { dangerouslySetInnerHTML?: { __html: string } }
->(({ className, ...props }, ref) => (
-  <div ref={ref} className={cn("cursor-default text-sm [&_p]:leading-relaxed", className)} {...props} />
-));
+// AlertDescription component
+const AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
+  ({ className, children, ...props }, ref) => (
+    <div ref={ref} className={cn("text-sm", className)} {...props}>
+      {children}
+    </div>
+  )
+);
 AlertDescription.displayName = "AlertDescription";
 
-export { Alert, AlertDescription, AlertTitle };
+// AlertButton component
+const AlertButton = React.forwardRef<
+  HTMLButtonElement,
+  React.ButtonHTMLAttributes<HTMLButtonElement> & { loading?: boolean; variant?: string }
+>(({ className, loading, children, ...props }, ref) => (
+  <Button
+    ref={ref}
+    className={cn("shrink-0", className)}
+    disabled={loading || props.disabled}
+    {...props}
+    variant={
+      props.variant as
+        | "default"
+        | "link"
+        | "secondary"
+        | "destructive"
+        | "outline"
+        | "ghost"
+        | null
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jakobsitory
body: removed undefined
created_at: 2025-03-20T16:40:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jakobsitory
body: Introduced design tokens in the tailwind .config after consulting Dhru
created_at: 2025-03-24T15:44:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
+    if (allowChildren && children) {
+      childrenArray.forEach((child) => {
+        if (React.isValidElement(child)) {
+          const element = child as React.ReactElement<{ children?: React.ReactNode }>;
+          if (element.type === AlertTitle && !extractedTitle) {
+            extractedTitle = element.props.children;
+          } else if (element.type === AlertDescription && !extractedDescription) {
+            extractedDescription = element.props.children;
+          } else {
+            otherContent.push(child);
+          }
+        } else {
+          otherContent.push(child);
+        }
+      });
+    }
+
+    // Compute condition: true if only a title or only a description is provided
+    const singleText = (extractedTitle ? 1 : 0) + (extractedDescription ? 1 : 0) === 1;
+
+    // Handle button creation from either React element or props object
+    const buttonElement = React.isValidElement(button) ? (
+      button
+    ) : button ? (
+      <AlertButton
+        onClick={button.onClick}
+        loading={button.loading}
+        disabled={button.disabled}
+        className={button.className}
+        variant={button.variant}>
+        {button.label}
+      </AlertButton>
+    ) : undefined;
+
+    const buttonStyles = getButtonStyles(variant || "default", size || "default");
+
+    // Create structured content
+    const titleElement = extractedTitle ? <AlertTitle>{extractedTitle}</AlertTitle> : null;
+    const descriptionElement = extractedDescription ? (
+      size === "small" ? (
+        <AlertDescription className="truncate">{extractedDescription}</AlertDescription>
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jakobsitory
body: reworked this part
created_at: 2025-03-24T15:48:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,49 +1,318 @@
+"use client";
+
+import { Button } from "@/modules/ui/components/button";
 import { VariantProps, cva } from "class-variance-authority";
+import { AlertCircle, AlertTriangle, CheckCircle2Icon, Info } from "lucide-react";
 import * as React from "react";
 import { cn } from "@formbricks/lib/cn";
 
-const alertVariants = cva(
-  "relative w-full rounded-xl border p-3 [&>svg]:absolute [&>svg]:text-foreground [&>svg]:left-3 [&>svg]:top-3 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-9",
-  {
-    variants: {
-      variant: {
-        default: "bg-background text-foreground",
-        destructive:
-          "text-destructive border-destructive/50 dark:border-destructive [&>svg]:text-destructive text-destructive",
-        info: "text-slate-800 bg-brand/5",
-        warning: "text-yellow-700 bg-yellow-50",
-        error: "border-error/50 dark:border-error [&>svg]:text-error text-error",
-      },
+// Define variant icons - now using cloneElement to ensure consistent sizing
+const variantIcons = {
+  default: null,
+  error: <AlertCircle className="text-red-600" />,
+  warning: <AlertTriangle className="text-amber-600" />,
+  info: <Info className="text-blue-600" />,
+  success: <CheckCircle2Icon className="text-green-600" />,
+};
+
+// Define alert styles with variants
+const alertVariants = cva("relative w-full rounded-md border", {
+  variants: {
+    variant: {
+      default: "text-foreground border-border",
+      error: "text-red-800 border-red-600/50",
+      warning: "text-amber-800 border-amber-600/50",
+      info: "text-blue-800 border-blue-600/50",
+      success: "text-green-800 border-green-600/50",
     },
-    defaultVariants: {
-      variant: "default",
+    size: {
+      default: "py-3 px-4",
+      small: "pl-3",
     },
+  },
+  defaultVariants: {
+    variant: "default",
+    size: "default",
+  },
+});
+
+// Define button styles based on variant
+const getButtonStyles = (variant: string, size: string) => {
+  const variantStyles = {
+    default: "secondary",
+    error: "bg-red-50 text-red-800 shadow-none hover:bg-red-50/80",
+    warning: "bg-amber-50 text-amber-800 shadow-none hover:bg-amber-50/80",
+    info: "bg-blue-50 text-blue-800 shadow-none hover:bg-blue-50/80",
+    success: "bg-green-50 text-green-800 shadow-none hover:bg-green-50/80",
+  };
+
+  const variantTextColors = {
+    default: "",
+    error: "text-red-800",
+    warning: "text-amber-800",
+    info: "text-blue-800",
+    success: "text-green-800",
+  };
+
+  return {
+    variant: size === "small" ? "link" : variant === "default" ? "secondary" : undefined,
+    size: size === "small" ? "sm" : "default",
+    className:
+      size === "small" && variant !== "default"
+        ? variantTextColors[variant as keyof typeof variantTextColors]
+        : variant !== "default" && size !== "small"
+          ? variantStyles[variant as keyof typeof variantStyles]
+          : "",
+  };
+};
+
+// Enhanced interface for Alert props
+interface AlertProps
+  extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">,
+    VariantProps<typeof alertVariants> {
+  icon?: React.ReactNode;
+  onIconClick?: (e: React.MouseEvent) => void;
+  title?: React.ReactNode;
+  description?: React.ReactNode;
+  button?:
+    | React.ReactElement
+    | {
+        label: string;
+        onClick: (e: React.MouseEvent) => void;
+        loading?: boolean;
+        disabled?: boolean;
+        className?: string;
+        variant?: string;
+      };
+  allowChildren?: boolean;
+}
+
+// Add new ButtonWrapper component
+const ButtonWrapper = ({
+  buttonStyles,
+  children,
+}: {
+  buttonStyles: { variant?: string; size?: string; className?: string };
+  children: React.ReactElement<{ variant?: string; size?: string; className?: string }>;
+}) => {
+  return React.cloneElement(children, {
+    variant: buttonStyles.variant ?? children.props.variant,
+    size: buttonStyles.size ?? children.props.size,
+    className: cn(children.props.className, buttonStyles.className),
+  });
+};
+
+const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
+  (
+    {
+      className,
+      variant = "default",
+      size = "default",
+      icon,
+      title,
+      description,
+      button,
+      onIconClick,
+      children,
+      allowChildren = true, // updated default to true
+      ...props
+    },
+    ref
+  ) => {
+    // Get the icon based on variant or use provided icon, and ensure consistent sizing
+    let renderIcon: React.ReactNode = null;
+
+    if (icon !== null) {
+      if (icon === undefined && variantIcons[variant as keyof typeof variantIcons]) {
+        // Use variant icon with consistent sizing
+        const variantIcon = variantIcons[variant as keyof typeof variantIcons];
+        renderIcon = variantIcon
+          ? React.cloneElement(variantIcon, {
+              className: cn("h-4 w-4", (variantIcon.props as Partial<{ className: string }>)?.className),
+            })
+          : null;
+      } else if (React.isValidElement(icon)) {
+        // Apply consistent sizing to custom icon unless it explicitly has size classes
+        const iconProps: { className?: string } = icon.props || {};
+        const hasExplicitSize =
+          iconProps.className && (iconProps.className.includes("h-") || iconProps.className.includes("w-"));
+
+        renderIcon = hasExplicitSize
+          ? icon
+          : React.cloneElement(icon as React.ReactElement<{ className?: string }>, {
+              className: cn("h-4 w-4", iconProps.className),
+            });
+      }
+    }
+
+    // Process children if they're provided alongside structured props
+    const childrenArray = React.Children.toArray(children);
+    let extractedTitle: React.ReactNode = title;
+    let extractedDescription: React.ReactNode = description;
+    let otherContent: React.ReactNode[] = [];
+
+    // Process children for backward compatibility
+    if (allowChildren && children) {
+      childrenArray.forEach((child) => {
+        if (React.isValidElement(child)) {
+          const element = child as React.ReactElement<{ children?: React.ReactNode }>;
+          if (element.type === AlertTitle && !extractedTitle) {
+            extractedTitle = element.props.children;
+          } else if (element.type === AlertDescription && !extractedDescription) {
+            extractedDescription = element.props.children;
+          } else {
+            otherContent.push(child);
+          }
+        } else {
+          otherContent.push(child);
+        }
+      });
+    }
+
+    // Compute condition: true if only a title or only a description is provided
+    const singleText = (extractedTitle ? 1 : 0) + (extractedDescription ? 1 : 0) === 1;
+
+    // Handle button creation from either React element or props object
+    const buttonElement = React.isValidElement(button) ? (
+      button
+    ) : button ? (
+      <AlertButton
+        onClick={button.onClick}
+        loading={button.loading}
+        disabled={button.disabled}
+        className={button.className}
+        variant={button.variant}>
+        {button.label}
+      </AlertButton>
+    ) : undefined;
+
+    const buttonStyles = getButtonStyles(variant || "default", size || "default");
+
+    // Create structured content
+    const titleElement = extractedTitle ? <AlertTitle>{extractedTitle}</AlertTitle> : null;
+    const descriptionElement = extractedDescription ? (
+      size === "small" ? (
+        <AlertDescription className="truncate">{extractedDescription}</AlertDescription>
+      ) : (
+        <AlertDescription>{extractedDescription}</AlertDescription>
+      )
+    ) : null;
+
+    return (
+      <div ref={ref} role="alert" className={cn(alertVariants({ variant, size }), className)} {...props}>
+        {size === "default" ? (
+          <div className="flex">
+            <div className="flex flex-grow items-center gap-2">
+              {/* Icon section with optional click handler */}
+              {renderIcon && (
+                <div
+                  className={cn(
+                    singleText ? "self-center" : "mt-0.5 self-start",
+                    "flex-shrink-0",
+                    onIconClick && "cursor-pointer"
+                  )}
+                  onClick={onIconClick}>
path: apps/web/modules/ui/components/alert/index.tsx
user: login: jakobsitory
body: Removed click for icon
created_at: 2025-03-24T15:49:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,49 +1,250 @@
-import type { Meta, StoryObj } from "@storybook/react";
-import { AlertCircle } from "lucide-react";
-import { Alert, AlertDescription, AlertTitle } from "./index";
+import { Meta, StoryObj } from "@storybook/react";
+import { LightbulbIcon } from "lucide-react";
+import { Alert, AlertButton, AlertDescription, AlertTitle } from "./index";
 
-const meta = {
-  title: "ui/Alert",
+// We'll define the story options separately from the component props
+interface StoryOptions {
+  title: string;
+  description: string;
+  showIcon: boolean;
+  showButton: boolean;
+  actionButtonText: string;
+}
+
+const meta: Meta<typeof Alert> = {
+  title: "UI/Alert",
   component: Alert,
   tags: ["autodocs"],
+  parameters: {
+    controls: {
+      sort: "requiredFirst",
+      exclude: [],
+    },
+  },
+  // These argTypes are for story controls, not component props
   argTypes: {
     variant: {
-      control: "radio",
-      options: ["default", "error"],
+      control: "select",
+      options: ["default", "error", "warning", "info", "success"],
+      description: "Style variant of the alert",
+      table: {
+        category: "Appearance",
+        type: { summary: "string" },
+        defaultValue: { summary: "default" },
+      },
+      order: 1,
     },
-  },
+    size: {
+      control: "select",
+      options: ["default", "small"],
+      description: "Size of the alert component",
+      table: {
+        category: "Appearance",
+        type: { summary: "string" },
+        defaultValue: { summary: "default" },
+      },
+      order: 2,
+    },
+    showIcon: {
+      control: "boolean",
+      description: "Whether to show an icon",
+      table: {
+        category: "Appearance",
+        type: { summary: "boolean" },
+      },
+      order: 3,
+    },
+    showButton: {
+      control: "boolean",
+      description: "Whether to show action buttons",
+      table: {
+        category: "Appearance",
+        type: { summary: "boolean" },
+      },
+      order: 4,
+    },
+    title: {
+      control: "text",
+      description: "Alert title text",
+      table: {
+        category: "Content",
+        type: { summary: "string" },
+      },
+      order: 1,
+    },
+    description: {
+      control: "text",
+      description: "Alert description text",
+      table: {
+        category: "Content",
+        type: { summary: "string" },
+      },
+      order: 2,
+    },
+    actionButtonText: {
+      control: "text",
+      description: "Text for the action button",
+      table: {
+        category: "Content",
+        type: { summary: "string" },
+      },
+      order: 2,
+    },
+  } as any,
+};
+
+export default meta;
+
+// Our story type just specifies Alert props plus our story options
+type Story = StoryObj<typeof Alert> & { args: StoryOptions };
+
+// Create a common render function to reduce duplication
+const renderAlert = (args: any) => {
+  // Extract component props
+  const { variant = "default", size = "default", className = "" } = args;
+
+  // Extract story content options
+  const {
+    title = "",
+    description = "",
+    showIcon = false,
+    showButton = false,
+    actionButtonText = "",
+  } = args as StoryOptions;
+
+  return (
+    <Alert variant={variant} size={size} className={className}>
+      {showIcon && <LightbulbIcon />}
+      <AlertTitle className={showIcon ? "pl-7" : ""}>{title}</AlertTitle>
+      {description && <AlertDescription className={showIcon ? "pl-7" : ""}>{description}</AlertDescription>}
+      {showButton && <AlertButton onClick={() => alert("Button clicked")}>{actionButtonText}</AlertButton>}
+    </Alert>
+  );
+};
+
+// Basic example with direct props
+export const Default: Story = {
+  render: renderAlert,
   args: {
     variant: "default",
+    showIcon: false,
+    showButton: false,
+    title: "Alert Title",
+    description: "This is an important notification.",
+    actionButtonText: "Learn more",
   },
-  render: (args) => (
-    <Alert {...args}>
-      <AlertTitle>This is an alert</AlertTitle>
-      <AlertDescription>This is a description</AlertDescription>
-    </Alert>
-  ),
-} satisfies Meta<typeof Alert>;
-
-export default meta;
+};
 
-type Story = StoryObj<typeof meta>;
+// Small size example
+export const Small: Story = {
+  render: renderAlert,
+  args: {
+    variant: "default",
+    size: "small",
+    title: "Information Alert",
+    description: "This is an important notification.",
+    showIcon: false,
+    showButton: true,
+    actionButtonText: "Learn more",
+  },
+  parameters: {
+    docs: {
+      description: {
+        story: "Use if space is limited or the alert is not the main focus.",
+      },
+    },
+  },
+};
 
-export const Default: Story = {};
+// With custom icon
+export const withButtonAndIcon: Story = {
+  render: renderAlert,
+  args: {
+    variant: "default",
+    title: "Alert Title",
+    description: "This is an important notification.",
+    showIcon: true,
+    showButton: true,
+    actionButtonText: "Learn more",
+  },
+};
 
+// Error variant
 export const Error: Story = {
+  render: renderAlert,
   args: {
     variant: "error",
+    title: "Error Alert",
+    description: "Your session has expired. Please log in again.",
+    showIcon: false,
+    showButton: true,
+    actionButtonText: "Log in",
+  },
+  parameters: {
+    docs: {
+      description: {
+        story: "Only use if the user needs to take immediate action or there is a critical error.",
+      },
+    },
   },
 };
path: apps/web/modules/ui/components/alert/stories.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Avoid shadowing the global Error property.**

The static analysis tool correctly identified that the story name 'Error' shadows the global Error constructor.

```diff
-export const Error: Story = {
+export const ErrorAlert: Story = {
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
export const ErrorAlert: Story = {
  render: renderAlert,
  args: {
    variant: "error",
    title: "Error Alert",
    description: "Your session has expired. Please log in again.",
    showIcon: false,
    showButton: true,
    actionButtonText: "Log in",
  },
  parameters: {
    docs: {
      description: {
        story: "Only use if the user needs to take immediate action or there is a critical error.",
      },
    },
  },
};
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 173-173: Do not shadow the global "Error" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-24T17:20:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+import { cleanup, render, screen } from "@testing-library/react";
+import { afterEach, describe, expect, it } from "vitest";
+import { Default, Error, Info, Small, Success, Warning, withButtonAndIcon } from "./stories";
path: apps/web/modules/ui/components/alert/stories.test.tsx
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Avoid shadowing global Error object**

The import of `Error` from `./stories` shadows the global `Error` object, which can lead to confusing code and potential bugs.


Consider renaming the imported story:

```diff
- import { Default, Error, Info, Small, Success, Warning, withButtonAndIcon } from "./stories";
+ import { Default, ErrorAlert, Info, Small, Success, Warning, withButtonAndIcon } from "./stories";
```

Then update the corresponding test:

```diff
  it("renders Error story", () => {
-   renderStory(Error);
+   renderStory(ErrorAlert);
    expect(screen.getByText("Error Alert")).toBeInTheDocument();
    // ...
  });
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
import { Default, ErrorAlert, Info, Small, Success, Warning, withButtonAndIcon } from "./stories";

// ... other code ...

describe("Alert stories", () => {
  it("renders Error story", () => {
-   renderStory(Error);
+   renderStory(ErrorAlert);
    expect(screen.getByText("Error Alert")).toBeInTheDocument();
    // ...
  });
});
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 Biome (1.9.4)</summary>

[error] 3-3: Do not shadow the global "Error" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-27T12:15:43Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-03-19T09:04:28Z
body: [vc]: #RPuklJfQ6w5wD4f/RwYujj6SvsP/4RLTQ0F17i4Y+9A=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJmb3JtYnJpY2tzLWNsb3VkIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IiJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vZm9ybWJyaWNrcy9mb3JtYnJpY2tzLWNsb3VkLzYzd0VKN3ZlMU1kREtWR0Q4WlNIcDg4REpISkQiLCJuZXh0Q29tbWl0U3RhdHVzIjoiSUdOT1JFRCIsInJvb3REaXJlY3RvcnkiOiJhcHBzL3dlYiIsInByZXZpZXdVcmwiOiJmb3JtYnJpY2tzLWNsb3VkLWdpdC1maXgtdW5pZnktYWxlcnQtY29tcG9uZW50LWZvcm1icmlja3MudmVyY2VsLmFwcCJ9XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)


<details><summary>1 Skipped Deployment</summary>

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **formbricks-cloud** | ⬜️ Ignored ([Inspect](https://vercel.com/formbricks/formbricks-cloud/63wEJ7ve1MdDKVGD8ZSHp88DJHJD)) | [Visit Preview](https://formbricks-cloud-git-fix-unify-alert-component-formbricks.vercel.app) |  | Mar 27, 2025 0:30am |
</details>


author_association: NONE
user: login: github-actions[bot]
created_at: 2025-03-19T09:04:40Z
body: Thank you for following the naming conventions for pull request titles! 🙏
<!-- Sticky Pull Request Commentpr-title-lint-error -->
author_association: CONTRIBUTOR
user: login: coderabbitai[bot]
created_at: 2025-03-24T17:20:38Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
The changes introduce a context-based approach for managing the alert components. A new `AlertContext` and its corresponding hook are added to provide `variant` and `size` values, and the `Alert` component is updated to accept a `size` prop. The child components—`AlertTitle`, `AlertDescription`, and the new `AlertButton`—consume this context to adjust their styling. Updates to the Storybook stories include a new `StoryOptions` interface, an expanded `meta` configuration with additional controls, and a common render function to streamline story rendering. Additionally, the Tailwind CSS configuration is enhanced by importing the `tailwindcss/colors` module and defining structured color palettes for `info`, `warning`, `success`, and `error`. A new test suite for the `Alert` component and a test file for the stories have also been introduced to verify rendering and functionality.

## Suggested reviewers
- jobenjada

<!-- walkthrough_end -->


---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 42ae9b7415f241a3dc94df6e8c07779baf0df945 and 5fb6e99fa855f8448144bf785483c6d6d046a711.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `apps/web/vite.config.mts` (1 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: Run E2E Tests / Run E2E Tests
* GitHub Check: Run Unit Tests / Unit Tests
* GitHub Check: Run Linters / Linters
* GitHub Check: SonarQube
* GitHub Check: Analyze (javascript)

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>apps/web/vite.config.mts (1)</summary>

`29-29`: **Good addition to test coverage configuration**

Including alert component files in the coverage configuration is a good practice, ensuring that the newly unified alert component with its multiple variants and sizes will be properly tracked in test coverage reports.

</details>

</blockquote></details>

</details>
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpADK2VmgqyjcEj2wAbb5AokAI7Y0riQ4rjedJAARABm8AAeXNgY8HHyaFEUYUzM3PjkGLgxkADuaMiI2ALM6jT0cuGwntiIlJBCaADWgojq+K7oGPSo8ErF6fDRlZAYTgIdAKwADCsATPxYAOLqABI1GjAtPBTwzC7ygiJikp74cc2otijIBJBksJgMnrgnAILZXIsApFMJNeDFCiKbAMSFESDMHzibhRSASFzwTC4RAAGhQGAY3mwtHhkCUcTQyPxlGhFHxFQoaQwRHxkLi+HxmHo1QYP0QeI+aAYsEgcRI1GwZxZ5KQ4kJYUQuFk3nhR3+tFJ4kKWW8si5cxIZUgiAuvhN8AAXp4MWdsZAvjISGR0Jq6FyRugHARXAJ8Phuh8ofJHZBFi6GAFqNF3ohYPgygxKr8Wu10ZjschuRbrYgNO4EMhsNxaNHXbQAgLpM1PEhqvdHl8JGSkd4UWispRgflCuxkJDIG0SPi/pQSE1UulZGTR+ggWLUrcdarleV1AgsLO0NxUfAk9qMEcPNY7JCiSTq2h0UhsFl/CReNJ2NR4IV+I9ZyLMKRkBclAyEBFRhMDDG1pjKaIBy3XBcGFFpRguUh8wMaB8HCUIa0YL4WWkfEhwoLMAgJJUKFhBpwjQ0jNxOPILk9AADbgMG4Zh/FSE0fVkP0A3o4Z6AwNBmyIMt3lnRBuFEKZom8fAkz8ABVGwABkKOvI1MNoWSnGfA8xUGTDOxyBQezBZDjxFURulVJUCXPJRRmozwXkhUl92rP5qEw/CHVmPJURIcj2ikKg/ACYJ4ACehYMQbpBV5UVZgSXwyXwPBMJoJUyUhEiyNfDA4pIbw4jAAJmyNGcaMUYc+KDappQRDywlHIiXE8DA0MZZkiDzSA9gTEhgvxRAWFrGhmGQAILgHRcWgYbp3Vs4lSRlWj2BSrA/KiRIFCUE1RAPLNPSYDAEgoOoZUazCvxw5BNLmfBcipNNK1BfopHIAVXnrPMjAAMX0uIpWa8kAu0bxBVnF4kywRZ0D5aR2noTzZw5XwEzJRSlK4ABtWAYO4RAOAAeiJoh10OPIiY5M6BDOObECpwZmFpvdYqJ7gfG8InVg2ABdAAKPHcAJ4nSfJgQNEp6nmbptnpZZ+n2c57m1nWABKZD9GMcAoDIegHhwAhiDIZRyNW4ouF4fhhH2u4ZHkJglCoVR1C0HQtZMKA4DGQ7DcIUhyCoM2WDYC3/DQY1qmcIYmkd5QXc0bRdDAQxtdMAxtwJomIIEInmBhKIGeweAib83tigZwzcCJlySESDQcWSAwYhbgwLEgf4AEljcD6MeScC4hgN66fwLTwR+rSFcGhWhYU8K9jpoRJcDAARkyRndoTg1S6LQMmZSr9M7Qrmr+lzAlMPowEu14svTI7w1jSvoEAGFCiX3BeNQSMJXI95eHwM2Xa9FbRYmKLxbM9Ez4kF4hiYk7k0IingN4egd8+xHhOPRKuAA1DMFdeKAwVHlV4g4Sx91UmeJa89H6QCgVaGBR8wG4C5GjMoZJqayjiOKAIxQOIqjJGvRGWxMLQPfAZIEGDPDPxvsZUE7B7rGmFD8bgOIvR0OtLxAB3AL5oE1OoYholMGgOxJo6E3APSjDCGUKgBMUCqMXvIqCmDr45DfsUOumgrDQiAZQei+JuCVH6DKTSZRHKMClDwsIIC8GfxquomBZlnFAmgOoKIECGIuNwAAEWkJGeAKi8q3xBOXVRrVESKCklFNCeBkH0MvkOTJbiP68XjAGFhMk2EXRaKxd4ujtxNRaBFPhhcwzr2EVuIEAByKo9DJG0MyQAITwAQDARSTKOP7FCGEPxaAAT3AlcGiDChR3cicQ+Djl6qSUDQM6kJRpVGVKqGUgjohvgmV2aZOZ56emMcUOZgDlDmhymIKUDZxFdlkSU/syBPjfBjGhaoO5BgDKfDQ8UkpKxskJEtCq889G6QNnuY5ek+Cki4WOXhh9lQSR+ihTBOCYmIF4kqFU1YmyeHDFgfoRA0gJGhpEeQpUkALRHriyAL97D2EYN4QJl4dyqnheCnIh0oqDL4EglBPC5mPmbKlZA9FSw4WhG0ewAVO4YEDnsaAABZJSpj8DaNDJy+8+cpD0DiNCHpSSb74mkTkFJkQYEWPmUCHJiA8kFMKGsuRFchrTwlMwJ5uKhlT0oJSfkyEADywVdQjhothH8QZsI/EwnELa8A1ArkyJ6NoKhamrgNu8oyaCT5NF1OjGUHCLm4FvH4FlTyETZnOAAt1kKwSQv6AeZC5hLCAhuS+YlhjPBKCJC4Bd+UxF1wKDkV5fAOYCFVAwIMERpiICMFAAAcupbtjAowNC4PRY6NlGnvw8V/LAWCdwMxznnAu0gibF1LsUsElcgQ1xGHXBuiBEj0QMJe9SrTAy6Psg+p9YQGmv1fcvd9tDM7fvHL+2ehcAMlxbTiImVdwNKHro3WDUBrUBXjEjN0tBUPHLCIs5ZUaL6fqzj+/ORH/2AbI6BrsVHIO0bg5ABjfxFAWh5Ri1oZD720LQx3IEOHeP4dzgJnwQnSPAb7BRsDtcaPQd4oAJMJVPsfUzIgcWns4Ed08R4ThmK7GbE6ZqDMHID83asaShF59XQPtdwNWUmZNMfkwJHtRFiylhU4+mzmSA1pJ43hxzOm/1FwM+s9zlGvO0cgFZpL+UOPJNSQw+zGX+PZZI0BvL5GCsQbMz5vzCZBztH1RhrsTS33hfo4xuT3KYugtIQlugbGyu2ZyGGiNB5NM1ac3V1zjXRM5HE61yz1npuZLm2cSNqz0tfsy4RvTOWGsxqayZlr3neLteNEObr7QX3uOwwNyAABRRI270PKcm7QrdyLIAAG8ZvMPB/t/JB58Qpcq5AAAvgAbkWyd2rgmLsiY8xtwr5niuA5+8DsHmTYcVcDaTrsUPDsU5yEsmCb5keo748tjH9WsfNeo3d5urc4PpyW1l1nq2rvrerkqQYp7vMcG5zENus7u4B1NtEKOg8riPAnmeuly70iPBG1MflepylktPUq7sV2OLi8nmE+wnFuKBmOgkIgUp11zOHcDpU0ZQ6qPZaBF08XyHvEC8Azj9OjuQKUvAIgeMBA+AEJ3Y6fj7yomFGKnVr42gG9dzu+gfrcAvwikSBJD9yABfcRQdN1V6LW8GLITNh2mX4idc6LAU8Z5zyqUGJMBMfBli0V2Y3AQZV/zQk2xUPpT1YrsmSeiEQ0m+qUOGg7C3fVxgTHHqNy/4xlDpyshPkDhQHm34UaAb6jhj1oWwWCvFrj7R8k6F0W7uSKsD3irUeU7wBKoBfygyAOFi6GIvaEcGIaWEBKYLehBPKBTfNfVZDfBMQ/GA2hGfINWhefebQpYNLBJcDAeA4/bDBkdcEGWCZBaIVAxfPKFVBQLZPwalascSSSBIaIDhCUEUOZaJY+WJAA/APwRvCMAtRgz1L0KgUkYfNCK8doKIMQcIWQCSYNdQZAB1A6HyKQH3GGLeCYJ/bFC8WITqeEGIfEGIdkfAPQmqGIXkfkRAUoLIQobqcYTwGICkKkNsSwz0GIWkQYGIU/Awf4GhWiN8HhJ2BcIhQoX1fwygTJBPHgjaO9RVEiBNJ5FMTwUI+qMRQ+P/CXY4VAQhLAj4ZeKgMQZAMjU4B1Cgv/B2V9XhBQ8g9pDtBEDhK8fOIiZzFwdATefAbed4JIskKuHqY8OuOUMkUokGBINIRQ73Z1AIdNH0RVGpVUa0J4ZAYve8CDPgLImHcoQCUUSKOeW6YsQ9ddGqWFQkasVdM4ZUZCU1HNPwRYtItlQSDlJvcsBaQPKfewM0bwCAgAdRcC6ggPNQ5F3wYkcHhgFAT2YNFCUHzjKyDjJDJW4XkXOUKAdydwOlYM+woDpGZU4lvxUPG3ITYT+C9EWKQJqlIOhzymDVnHohyUpGRExOr2xOdQE0qQoU0N2mQ30WXHQAoEd09x6JOABRCj7WnjIjG0bROBuOQEiJQHyG8UYP0kWBgg6EGGEjSEtH2OzCjFLErXUFkESXHj4ImhIDLRvwXhBACBaHyjuH4GCi+B8DERHzHXYE+Sr19H9EDAlJpAwGLQEQelFFWLfyrRqlrW1LOJnXU3nUUKXRBhOPXXkMeCByz34D3RqEPWPX0WkHPUgHNRuXL0eNY1oVTTL2FE8BdJrzryZ20zOxc1y2F2x1FzH2kC50G1kx5Aj1Gzi3+3zNKxsgvzQC4BkzQGAFLK8WKL0ArNO2c300uyhTrKJglKbOkyG1bIU1iyUwmy7JoO9HpIAF4tzXBM1hBgAaCDZMk9BIAAAyUHLk7qLgUs2vRQhHcc9Hc7NnNza7MTecyTZsqLZDAHeiBM4EabV43UW8ziJ8lnF8oXGcyjT88zCLJcvMh9ACygmyL4pkeEUC6vcCgXSCms6CsDWCmDeClsxCgnX7FCsIP4/ATC1wbCqsqc9nAihsvML8xcki38rs5CtTIE8wmi2QOiyczHN8kXOc5ihcyLOTDipCwnZtGzakxwzgPc/i47ZnHC6s6ckDWcwiujNin8ljaS8itTNEukPigSlbPCzSmCsSyTFuGXXnMAIwfnImIg7wTpWgSWREiPDQIQQmaXWXDueXE2IOJXAeS4MRdXM/dXAkaebZahRYpQCSCDQkK4MJafMGNyhgAUIDGSAiXiSc6ozpWo/SWcIcMRR8CkO5VBLg/SAJKIRUvkqRNwigIpHKoYyEDkrAKU82ciVKCHFyPcF8A+HgB6NaO8SaQBO8fSBKgIfcYhMUzwVPPVREBCg2CqrqTCJqhQHKo4K9Y0JgVqtyIgC3SUu4nEjiifHFGUeiQwiAnQlkCAswhGAEpGe8ZsIRf89EwYUE7efE0UXvHIY3DhKkz7X6f4eSJSaACA6mEgIgY1EYCApEBoCAteOaWG1KeGjAlG7oNG1IWgMARGugBPGYq0MVfawYH/AQ2cTPciNK5BDKrKsm3Kw3PTDBVAQ4/kK6aq9VTyx3aEt8JoEdcYLo8pIiYFVcpGI6Y5OUeE1o9oo5Vq3eUgT3NcP4JxTwaAdKlycVSVPaHtbgadfyudU2cg1SWcFdGVPmjdA2AC3dLwA9PcNM8QDMqTXakGRKiYBgTIfSnbGyRm5AXcsKYuAIfmGIFy+mhmP2mINWFHFSyssOlyDyk6LynynS12xmtqkYk2qSgwSAXQAsk6fAFq/Se3CPZE4harNHAjeOkYROh3byplHOvO+iO6ogIu7mpO3m/Yiu1S5yzWmukuogeu2DXOqAKBWEcwtuygpEy22O07au9ygeoexu0epqyegesut8buuOvuhenmpe2y89PnSutShi4S2cwrUISXPy9uLuHuRXfuaOVXLCb8Z27wokjCaodQTwDhSk8Ix03hKUlvWKhyUtYg/OznDKTQWjOZT+mgRaILREZEfJNESB4CLrZoTyYKKcTCTog+T0f05cHU+071HIaNEpQcZYxhJa46V/Y5TwgARiOAAComH5LkRIAbA9ZKB4QWGuBjxTobJUGLJ6YUAPwSGOC3ylinYCjBgZqBUVbwSjSFKiiJIAbcJaopQKpkYzkgQRxKtg1STDsuSFrKxHEwlNIGBtI/kjB1hmGmHcF2COGuH6peHjhPB2gaH0IbIv7gq3hYBjVI8vR2gwgDZyqjTKrGFMxfMDUlHaTfVV7fUW6ICbrl9x6nq1ZVIS6zoMGUUQ0ZFCjM4FU/Hx5ZGb8JUpVV1PoXl9YwlfkwgAkqwkYswVHkIABmOx+wOpTh5Ynhphvhk4VWigKKDCOuNAc6U5EpxrT5RYJsV8PgX6zhOE3hURLRZAfmGJmktsB6t4+iNWfELBjILR3JzOMxO0OBgJPRFaGVKsQieeeVaYdyowAAFjsfgKzNL33zylcf4dShyFFFQdlRudLUXAPCyCIYNhAhz3gN4l+rVrydIaOE7lyFmlihycwmjxDyke/wUHRP2gN2zEugXkPUDAGnkWngj1IAIgMgFFkixHImwloGyHxCyfOgahomJY+HejCFQACDJiVDHEeYMCWDsZfjaAIFYhfmueQH+HudmsKG+ZOGGLvFQbIDqlOU8gXjFZYGlUCTmDGfHhAlhkKYedNvEbIbBH2e4cOa6W0fHilZIUfHaF4ThaSNeTwA5k0CMGzUBW8DzV+A/uLjgb8nNNVbuANyYGCmKaxeSJkjJgYH2ZiR8hGAHSGjqV0SEDFd5PxAxZWWiuUCwMFGzEsbF1Yn7XYWKrNf/s9aMENrbGNsXWHxOHNrXUUOtpkp6uTPtqPTWido10vVELrctreEbeXVEAttjM3Xbdtv3VTJ7eNwgkaN0VrGolQFhPzAPvsscuPvoqErWy0ussvsbilwPpvsCt7kClCqHjVwNKMDfvUlQaSikU/MPbx0Aa2Vnh2RJUwkySrfNzOEbKzO5Z7GVUNHMLCsgbJGJt7bFAEPogAAEIOWQwBVRaZLgiYowxB0ls9mxIH6I5lH3KCiCN0xDuHqxG1Qh5DuTMB6F6BcaOhZwYgf2XTT1ShSTYYD1ZJugjhPt2iMIkw0xibz4rwWhvBVHAjsj6IkjSzeIOjnGcHpAJJbhlDBjswDn5BZxHWyAS0DY6C4Q+V0JLlsxCoSBeSL4zatJPc5lIGZHgoqG2g5wIUaDBRniZQ2G2x8RgLfB8C/h4D/gRhoD8RjLBh8Q0Kup8QqLg0eKEZuPePfaUWf8K2SncXJCkD8Q2ABQ95qpswc23xNOjj0AaW4Q8SCCHTNzFgZxQg6BWC0A4gbkeORQWk3SNHKw0XPwohMBixxPdJv5dQZhauOgwSvGmpKITOAVo3ogRMYPtXZwslM1rUDab7B2J2ozm2h3J3ftp2UyHa53nb+25wIyTaVux2W2Ta22Nv9ZO3Z3JhoOF3PA/xl35jFn12ecIAHKM5t2cOSBa6vLmAcRj3W5T277gqH6Vdwqb3Ndn6cJorW8OaQJ2TQW/AAkCSozrrWSGEXAqB5BYXUqI3lBSAikO6N6sB5rIBsEv6hvdbiwjh7BFO9ddR9RMJ4jaEYhBLXy93KMmHvMYgWlZhnVzr1j9k5wOkEu+AqbND+g3wDZoBpCSB7B5saoAApewAADTFGIOQBkjchAcpNZ6gsso01lDkfpK7QFMy8T2RTVAyN/AqT5X2IfxGCjfDQdTBUfb8c8jYXNCNYEj1GtHdWKr49N9ID9YCCaHZtJsD7u4CjpntngeWkHSBBkcazV8LgW9nSW8jJHejPHdbfjKnYu7tqu5PV28gAvQHYO4bY0mO7W7O+RU267cdvnbHERCXbM8LCe78t51Tk9iDH1keCpCNgV2B+Mk9y4CoEjkvfkFjiqmdjUETndhTgMG79onUAAH1xhEAV+hUjQ6AV/3cjItZF+dZIAVgJQAAOAATnoYYFafWDiHP42FP/FFadWHofP+FHoZWFP5IHoYADZ1g0BT+AAdhWCADaArTWgAICPQewj+ogJYM/3P4MBIBJAc/k8wgFPMVgcAlYB/x/4MB6GXCAQIAJq6n8lgl/EgEsFP4rA4gp/ZOIYG75PN/+yAggU83oZLA4g6wZgWgHAEMAUBtAOID/xICn8GAIAwAYAPP5rw4gKwXgSgKWA0DD+EASAKwIED8Dz+5/SkMQNYGn8nmTzU/vQy0ECA4ggA4gdoNaYMAf+tAMwSsCeY/80AgA+hvQ1kFyCoAy/XAGv1oAb8t+EEWgCvz1g0CgAA=== -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=formbricks/formbricks&utm_content=5002):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: use-tusk[bot]
created_at: 2025-03-27T12:11:04Z
body: ## ⏩ Already incorporated tests (5fb6e99fa855f8448144bf785483c6d6d046a711) [View output ↗](https://github.com/formbricks/formbricks/pull/5002/checks?check_run_id=39514498047)

> [!TIP]
> New to Tusk? Learn more [here](https://docs.usetusk.ai/automated-tests/overview).

<details><summary>View check history</summary>
<p>

| Commit | Status | Output | Tests | Passed | Failed | Error |
|--------|--------|--------|--------|--------|--------|--------|
| 0ea891c32f9028fe35019ac108e162a8707d3dbc | ✅ Generated 5 tests - 5 passed | [Tests](https://app.usetusk.ai/app/testing-commit-check/87e014d0-f14e-48e6-b50b-7fa3e0093450?client=ed5e7af5-f810-424e-a528-cd1b708b51aa) | 5 | 5 | 0 | 0 |
| 6709030b81b1e811731bd1a7422e0529cbd15c0c | ✅ Generated 5 tests - 5 passed | [Tests](https://app.usetusk.ai/app/testing-commit-check/dd493a89-1e9b-48c6-9d91-8042283117eb?client=ed5e7af5-f810-424e-a528-cd1b708b51aa) | 5 | 5 | 0 | 0 |
| ec5359cbce94db40530106c1ffb7af8591e580f8 | ✅ Generated 5 tests - 5 passed | [Tests](https://app.usetusk.ai/app/testing-commit-check/73238308-0783-44fe-be50-177c2ce007fb?client=ed5e7af5-f810-424e-a528-cd1b708b51aa) | 5 | 5 | 0 | 0 |
| 42ae9b7415f241a3dc94df6e8c07779baf0df945 | ⏩ Commit incorporated tests | [Output](https://app.usetusk.ai/app/testing-commit-check/cc25f4f3-7bb0-452c-8dba-e5876575010c?client=ed5e7af5-f810-424e-a528-cd1b708b51aa) |  |  |  |  |
| 5fb6e99fa855f8448144bf785483c6d6d046a711 | ⏩ Already incorporated tests | [Output](https://app.usetusk.ai/app/testing-commit-check/873c33c3-1bdc-4ef5-8e01-07f3d155777e?client=ed5e7af5-f810-424e-a528-cd1b708b51aa) |  |  |  |  |

</p>
</details>
author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-03-27T12:34:41Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=formbricks_formbricks&pullRequest=5002) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [5 New issues](https://sonarcloud.io/project/issues?id=formbricks_formbricks&pullRequest=5002&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=formbricks_formbricks&pullRequest=5002&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=formbricks_formbricks&pullRequest=5002&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [99.7% Coverage on New Code](https://sonarcloud.io/component_measures?id=formbricks_formbricks&pullRequest=5002&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=formbricks_formbricks&pullRequest=5002&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=formbricks_formbricks&pullRequest=5002)


author_association: NONE