diff --git a/web/src/assets/svg/BusterFrameLogoWithText.tsx b/web/src/assets/svg/BusterFrameLogoWithText.tsx
deleted file mode 100644
index 6d9d9a5d0..000000000
--- a/web/src/assets/svg/BusterFrameLogoWithText.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-export const BusterFrameLogoWithText = ({ className }: { className?: string }) => {
- return (
-
- );
-};
diff --git a/web/src/assets/svg/BusterLogoNew.tsx b/web/src/assets/svg/BusterLogoNew.tsx
deleted file mode 100644
index 898235581..000000000
--- a/web/src/assets/svg/BusterLogoNew.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import React from 'react';
-
-export const BusterLogoNew: React.FC<{
- onClick?: () => void;
- className?: string;
- style?: React.CSSProperties;
- color?: string;
-}> = ({ color = 'currentColor', ...props }) => {
- return (
-
- );
-};
diff --git a/web/src/assets/svg/BusterLogoWithText.svg b/web/src/assets/svg/BusterLogoWithText.svg
deleted file mode 100644
index a982add98..000000000
--- a/web/src/assets/svg/BusterLogoWithText.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/web/src/assets/svg/BusterLogoWithText.tsx b/web/src/assets/svg/BusterLogoWithText.tsx
index 0a6b0ca04..c4ea9929a 100644
--- a/web/src/assets/svg/BusterLogoWithText.tsx
+++ b/web/src/assets/svg/BusterLogoWithText.tsx
@@ -1,30 +1,27 @@
-export const BusterLogoWithText = ({ className }: { className?: string }) => {
+import React from 'react';
+
+export const BusterLogoWithText: React.FC<{
+ onClick?: () => void;
+ className?: string;
+ style?: React.CSSProperties;
+ color?: string;
+}> = ({ color = 'currentColor', ...props }) => {
return (
);
diff --git a/web/src/assets/svg/BusterLogoWithTextBW.tsx b/web/src/assets/svg/BusterLogoWithTextBW.tsx
deleted file mode 100644
index d51256e67..000000000
--- a/web/src/assets/svg/BusterLogoWithTextBW.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import React from 'react';
-
-export const BusterLogoWithTextBW: React.FC<{
- className?: string;
-}> = ({ className = '' }) => {
- return (
-
- );
-};
diff --git a/web/src/assets/svg/BusterLogos.stories.tsx b/web/src/assets/svg/BusterLogos.stories.tsx
new file mode 100644
index 000000000..af518f430
--- /dev/null
+++ b/web/src/assets/svg/BusterLogos.stories.tsx
@@ -0,0 +1,34 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { BusterLogo } from './BusterLogo';
+import { BusterLogoWithText } from './BusterLogoWithText';
+
+const meta: Meta
= {
+ title: 'Base/Logos',
+ tags: ['autodocs'],
+ argTypes: {
+ className: { control: 'text' }
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const DefaultBusterLogo: Story = {
+ render: (args) =>
+};
+
+type BusterLogoNewStory = StoryObj;
+
+export const DefaultBusterLogoWithText: BusterLogoNewStory = {
+ render: (args) => ,
+ args: {
+ color: 'currentColor'
+ }
+};
+
+export const CustomColorBusterLogoWithText: BusterLogoNewStory = {
+ render: (args) => ,
+ args: {
+ color: '#FF0000'
+ }
+};
diff --git a/web/src/components/ui/buttons/Button.tsx b/web/src/components/ui/buttons/Button.tsx
index bc15f97c3..229755d52 100644
--- a/web/src/components/ui/buttons/Button.tsx
+++ b/web/src/components/ui/buttons/Button.tsx
@@ -36,7 +36,7 @@ const sizeVariants = {
};
export const buttonVariants = cva(
- 'inline-flex items-center overflow-hidden text-base justify-center gap-[5px] shadow-btn rounded transition-all duration-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:cursor-not-allowed data-[loading=true]:cursor-progress',
+ 'inline-flex items-center overflow-hidden text-base justify-center gap-[5px] shadow rounded transition-all duration-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:cursor-not-allowed data-[loading=true]:cursor-progress',
{
variants: {
buttonType: buttonTypeClasses,
diff --git a/web/src/components/ui/card/InfoCard.stories.tsx b/web/src/components/ui/card/InfoCard.stories.tsx
new file mode 100644
index 000000000..17515e1f5
--- /dev/null
+++ b/web/src/components/ui/card/InfoCard.stories.tsx
@@ -0,0 +1,86 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { InfoCard } from './InfoCard';
+import { BellOutlined } from '@ant-design/icons';
+import { faker } from '@faker-js/faker';
+
+const meta: Meta = {
+ title: 'Base/InfoCard',
+ component: InfoCard,
+ tags: ['autodocs'],
+ args: {
+ title: faker.lorem.sentence(),
+ description: faker.lorem.paragraph(),
+ icon: ,
+ onClick: () => {}
+ },
+ argTypes: {
+ variant: {
+ control: 'select',
+ options: ['default', 'gray', 'ghost']
+ },
+ size: {
+ control: 'select',
+ options: ['default']
+ },
+ iconPosition: {
+ control: 'select',
+ options: ['top', 'center', 'bottom', 'absolute-top']
+ },
+ title: {
+ control: 'text'
+ },
+ description: {
+ control: 'text'
+ },
+ selected: {
+ control: 'boolean'
+ }
+ }
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Default: Story = {
+ args: {
+ title: 'Notifications',
+ description: 'You have 3 unread messages',
+ icon: ,
+ iconPosition: 'center'
+ }
+};
+
+export const TopIcon: Story = {
+ args: {
+ title: 'System Status',
+ description: 'All systems are operational',
+ icon: ,
+ iconPosition: 'top'
+ }
+};
+
+export const BottomIcon: Story = {
+ args: {
+ title: 'Updates Available',
+ description: 'New version 2.0 is ready to install',
+ icon: ,
+ iconPosition: 'bottom'
+ }
+};
+
+export const AbsoluteTopIcon: Story = {
+ args: {
+ title: 'Important Notice',
+ description: 'Please review your account settings',
+ icon: ,
+ iconPosition: 'absolute-top'
+ }
+};
+
+export const NoIcon: Story = {
+ args: {
+ title: 'Simple Card',
+ description: 'A card without an icon',
+ icon: null
+ }
+};
diff --git a/web/src/components/ui/card/InfoCard.tsx b/web/src/components/ui/card/InfoCard.tsx
new file mode 100644
index 000000000..f5ca49faa
--- /dev/null
+++ b/web/src/components/ui/card/InfoCard.tsx
@@ -0,0 +1,102 @@
+import { cn } from '@/lib/classMerge';
+import { cva, type VariantProps } from 'class-variance-authority';
+import React from 'react';
+
+const infoCardVariants = cva('rounded relative flex gap-1 w-full h-full justify-between', {
+ variants: {
+ size: {
+ default: 'px-4 py-3.5'
+ },
+ variant: {
+ default: 'border bg-background shadow-sm',
+ gray: 'border bg-item-hover shadow-sm',
+ ghost: 'border-none bg-transparent shadow-none'
+ },
+ selectable: {
+ true: 'cursor-pointer',
+ false: ''
+ },
+ selected: {
+ true: '',
+ false: ''
+ }
+ },
+ compoundVariants: [
+ {
+ selectable: true,
+ variant: 'default',
+ className: 'hover:bg-item-hover'
+ },
+ {
+ selectable: true,
+ variant: 'gray',
+ className: 'hover:bg-item-active'
+ },
+ {
+ selected: true,
+ variant: 'default',
+ className: 'bg-item-hover'
+ },
+ {
+ selected: true,
+ variant: 'gray',
+ className: 'bg-item-active'
+ }
+ ]
+});
+
+interface InfoCardProps extends React.HTMLAttributes {
+ variant?: VariantProps['variant'];
+ size?: VariantProps['size'];
+ title: string;
+ description: string;
+ icon: React.ReactNode;
+ iconPosition?: 'top' | 'center' | 'bottom' | 'absolute-top';
+ onClick?: () => void;
+ selected?: boolean;
+}
+
+export const InfoCard: React.FC = React.memo(
+ ({
+ size = 'default',
+ variant = 'default',
+ title,
+ description,
+ icon,
+ iconPosition = 'center',
+ className,
+ onClick,
+ selected = false,
+ ...props
+ }) => {
+ return (
+
+
+
{title}
+
{description}
+
+ {icon && (
+
+ {icon}
+
+ )}
+
+ );
+ }
+);
+
+InfoCard.displayName = 'InfoCard';
diff --git a/web/src/components/ui/icons/customIcons/BuserLogoBW.svg b/web/src/components/ui/icons/customIcons/BuserLogoBW.svg
deleted file mode 100644
index 5293cbc8c..000000000
--- a/web/src/components/ui/icons/customIcons/BuserLogoBW.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/web/src/controllers/AppSidebar/AppSidebarTopItems.tsx b/web/src/controllers/AppSidebar/AppSidebarTopItems.tsx
index 4c37504b3..c3ee47e1a 100644
--- a/web/src/controllers/AppSidebar/AppSidebarTopItems.tsx
+++ b/web/src/controllers/AppSidebar/AppSidebarTopItems.tsx
@@ -3,7 +3,7 @@ import { Button, ConfigProvider } from 'antd';
import Link from 'next/link';
import { BusterRoutes, BusterRoutesWithArgsRoute } from '@/routes';
import { AppMaterialIcons } from '@/components/ui';
-import { BusterLogoNew } from '@/assets/svg/BusterLogoNew';
+import { BusterLogoNew } from '@/assets/svg/BusterLogoWithText';
import { AppTooltip } from '@/components/ui';
import { useHotkeys } from 'react-hotkeys-hook';
import { useAntToken } from '@/styles/useAntToken';
diff --git a/web/src/lib/imageGeneration.tsx b/web/src/lib/imageGeneration.tsx
index 815b7a6fa..5dfdab06c 100644
--- a/web/src/lib/imageGeneration.tsx
+++ b/web/src/lib/imageGeneration.tsx
@@ -2,7 +2,6 @@ import type { IBusterMetric } from '@/context/Metrics';
import { downloadImageData, exportElementToImage } from './exportUtils';
import { createRoot } from 'react-dom/client';
import { timeout } from './timeout';
-import { BusterLogoWithTextBW } from '@/assets/svg/BusterLogoWithTextBW';
import React, { useEffect, useRef } from 'react';
import { BusterChart } from '@/components/ui/charts';
import type { BusterMetricData } from '@/context/MetricData';
@@ -196,7 +195,7 @@ export const PreviewImageReactComponent: React.FC<{
? 'drop-shadow(0 0 8px rgba(255, 255, 255, 0.15))'
: 'drop-shadow(0 0 8px rgba(0, 0, 0, 0.1))'
}}>
-
+ <>>
);
diff --git a/web/src/styles/tailwind.css b/web/src/styles/tailwind.css
index 34f499d1c..946889108 100644
--- a/web/src/styles/tailwind.css
+++ b/web/src/styles/tailwind.css
@@ -10,7 +10,7 @@
--font-mono: Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
/* shadows */
- --shadow-btn: 0px 1px 4px 0px rgba(0, 0, 0, 0.05);
+ --shadow: 0px 1px 4px 0px rgba(0, 0, 0, 0.05);
/* border */
--default-border-width: 0.5px;