+
((params) => {
style={{ width: `${progress}%` }}
/>
-
+
{Math.round(progress)}%
diff --git a/src/components/AGGrid/index-enhanced.ts b/src/components/AGGrid/index-enhanced.ts
index 19f279b2..5e0315ca 100644
--- a/src/components/AGGrid/index-enhanced.ts
+++ b/src/components/AGGrid/index-enhanced.ts
@@ -1,100 +1,97 @@
// Main AG Grid Component with enhanced brand support
-export { AGGrid, AgGridReact } from './AGGrid';
export type {
+ AGColDef,
AGGridProps,
+ CellClickedEvent,
+ CellValueChangedEvent,
ColDef,
- AGColDef,
+ FilterChangedEvent,
+ FirstDataRenderedEvent,
GridApi,
GridReadyEvent,
RowClickedEvent,
- CellClickedEvent,
- CellValueChangedEvent,
+ RowSelectedEvent,
SelectionChangedEvent,
- FilterChangedEvent,
SortChangedEvent,
- RowSelectedEvent,
- FirstDataRenderedEvent,
} from './AGGrid';
+export { AGGrid, AgGridReact } from './AGGrid';
// Original Cell Renderers (backward compatibility)
+export type {
+ DateRendererProps,
+ ProgressRendererProps,
+ StatusBadgeRendererProps,
+ StatusConfig,
+} from './CellRenderers';
export {
- CellRenderers,
// Individual renderers
AvatarNameRenderer,
- StatusBadgeRenderer,
- EngagementScoreRenderer,
- EmailRenderer,
- PhoneRenderer,
- LinkedInRenderer,
- DomainRenderer,
- CurrencyRenderer,
- NumberRenderer,
- DateRenderer,
BooleanRenderer,
+ CellRenderers,
CompanyRenderer,
- ProgressRenderer,
- TagsRenderer,
+ CurrencyRenderer,
+ DateRenderer,
+ DomainRenderer,
+ EmailRenderer,
+ EngagementScoreRenderer,
+ // Utilities
+ formatPhoneDisplay,
+ LinkedInRenderer,
// Memoized renderers (recommended)
MemoizedAvatarNameRenderer,
- MemoizedStatusBadgeRenderer,
- MemoizedEngagementScoreRenderer,
+ MemoizedBooleanRenderer,
+ MemoizedCompanyRenderer,
+ MemoizedCurrencyRenderer,
+ MemoizedDateRenderer,
+ MemoizedDomainRenderer,
MemoizedEmailRenderer,
- MemoizedPhoneRenderer,
+ MemoizedEngagementScoreRenderer,
MemoizedLinkedInRenderer,
- MemoizedDomainRenderer,
- MemoizedCurrencyRenderer,
MemoizedNumberRenderer,
- MemoizedDateRenderer,
- MemoizedBooleanRenderer,
- MemoizedCompanyRenderer,
+ MemoizedPhoneRenderer,
MemoizedProgressRenderer,
+ MemoizedStatusBadgeRenderer,
MemoizedTagsRenderer,
- // Utilities
- formatPhoneDisplay,
+ NumberRenderer,
+ PhoneRenderer,
+ ProgressRenderer,
+ StatusBadgeRenderer,
statusColors,
-} from './CellRenderers';
-
-export type {
- StatusConfig,
- StatusBadgeRendererProps,
- DateRendererProps,
- ProgressRendererProps,
+ TagsRenderer,
} from './CellRenderers';
// Enhanced Cell Renderers with Design System Integration
+export type {
+ ActionsRendererProps,
+ EnhancedCellRendererType,
+} from './EnhancedCellRenderers';
export {
- EnhancedAvatarNameRenderer,
- EnhancedStatusBadgeRenderer,
EnhancedActionsRenderer,
+ EnhancedAvatarNameRenderer,
EnhancedBooleanRenderer,
+ enhancedCellRenderers,
EnhancedCurrencyRenderer,
EnhancedDateRenderer,
EnhancedProgressRenderer,
+ EnhancedStatusBadgeRenderer,
EnhancedTagsRenderer,
- enhancedCellRenderers,
-} from './EnhancedCellRenderers';
-
-export type {
- ActionsRendererProps,
- EnhancedCellRendererType,
} from './EnhancedCellRenderers';
// Brand Theme Utilities
+export type {
+ AGGridBrandName,
+ AGGridBrandTheme,
+ ResponsiveColumnOptions,
+ UseAGGridBrandThemeOptions,
+} from './brand-theme-utils';
export {
agGridBrandThemes,
- generateAGGridBrandCSS,
- generateAGGridDarkBrandCSS,
- useAGGridBrandTheme,
- injectAGGridBrandStyles,
- createBrandAwareColumnDef,
applyBrandThemeToColumns,
+ createBrandAwareColumnDef,
createResponsiveColumn,
+ generateAGGridBrandCSS,
+ generateAGGridDarkBrandCSS,
getBrandAwareGridOptions,
-} from './brand-theme-utils';
-
-export type {
- AGGridBrandName,
- AGGridBrandTheme,
- UseAGGridBrandThemeOptions,
- ResponsiveColumnOptions,
+ injectAGGridBrandStyles,
+ useAGGridBrandTheme,
} from './brand-theme-utils';
diff --git a/src/components/AGGrid/index.ts b/src/components/AGGrid/index.ts
index c73620ec..a2e7c8d7 100644
--- a/src/components/AGGrid/index.ts
+++ b/src/components/AGGrid/index.ts
@@ -1,61 +1,60 @@
-export { AGGrid, AgGridReact } from './AGGrid';
export type {
+ AGColDef,
AGGridProps,
+ CellClickedEvent,
+ CellValueChangedEvent,
ColDef,
- AGColDef,
+ FilterChangedEvent,
+ FirstDataRenderedEvent,
GridApi,
GridReadyEvent,
RowClickedEvent,
- CellClickedEvent,
- CellValueChangedEvent,
+ RowSelectedEvent,
SelectionChangedEvent,
- FilterChangedEvent,
SortChangedEvent,
- RowSelectedEvent,
- FirstDataRenderedEvent,
} from './AGGrid';
+export { AGGrid, AgGridReact } from './AGGrid';
// Cell Renderers
+export type {
+ DateRendererProps,
+ ProgressRendererProps,
+ StatusBadgeRendererProps,
+ StatusConfig,
+} from './CellRenderers';
export {
- CellRenderers,
// Individual renderers
AvatarNameRenderer,
- StatusBadgeRenderer,
- EngagementScoreRenderer,
- EmailRenderer,
- PhoneRenderer,
- LinkedInRenderer,
- DomainRenderer,
- CurrencyRenderer,
- NumberRenderer,
- DateRenderer,
BooleanRenderer,
+ CellRenderers,
CompanyRenderer,
- ProgressRenderer,
- TagsRenderer,
+ CurrencyRenderer,
+ DateRenderer,
+ DomainRenderer,
+ EmailRenderer,
+ EngagementScoreRenderer,
+ // Utilities
+ formatPhoneDisplay,
+ LinkedInRenderer,
// Memoized renderers (recommended)
MemoizedAvatarNameRenderer,
- MemoizedStatusBadgeRenderer,
- MemoizedEngagementScoreRenderer,
+ MemoizedBooleanRenderer,
+ MemoizedCompanyRenderer,
+ MemoizedCurrencyRenderer,
+ MemoizedDateRenderer,
+ MemoizedDomainRenderer,
MemoizedEmailRenderer,
- MemoizedPhoneRenderer,
+ MemoizedEngagementScoreRenderer,
MemoizedLinkedInRenderer,
- MemoizedDomainRenderer,
- MemoizedCurrencyRenderer,
MemoizedNumberRenderer,
- MemoizedDateRenderer,
- MemoizedBooleanRenderer,
- MemoizedCompanyRenderer,
+ MemoizedPhoneRenderer,
MemoizedProgressRenderer,
+ MemoizedStatusBadgeRenderer,
MemoizedTagsRenderer,
- // Utilities
- formatPhoneDisplay,
+ NumberRenderer,
+ PhoneRenderer,
+ ProgressRenderer,
+ StatusBadgeRenderer,
statusColors,
-} from './CellRenderers';
-
-export type {
- StatusConfig,
- StatusBadgeRendererProps,
- DateRendererProps,
- ProgressRendererProps,
+ TagsRenderer,
} from './CellRenderers';
diff --git a/src/components/AI/icons.tsx b/src/components/AI/icons.tsx
index e5dcd0b7..ac39ecd0 100644
--- a/src/components/AI/icons.tsx
+++ b/src/components/AI/icons.tsx
@@ -5,6 +5,7 @@
*/
import * as React from 'react';
+
import { cn } from '../../utils/cn';
// ============================================================================
diff --git a/src/components/AI/index.ts b/src/components/AI/index.ts
index c26cfd53..b43abb51 100644
--- a/src/components/AI/index.ts
+++ b/src/components/AI/index.ts
@@ -10,55 +10,55 @@ export * from './types';
// Icons
export {
- SparklesIcon,
AILogoIcon,
- CloseIcon,
- RefreshIcon,
- ChevronIcon,
- SendIcon,
- SpinnerIcon,
- type SparklesIconProps,
type AILogoIconProps,
+ ChevronIcon,
+ type ChevronIconProps,
+ CloseIcon,
type CloseIconProps,
+ RefreshIcon,
type RefreshIconProps,
- type ChevronIconProps,
+ SendIcon,
type SendIconProps,
+ SparklesIcon,
+ type SparklesIconProps,
+ SpinnerIcon,
type SpinnerIconProps,
} from './icons';
// MCP Tool Call Display
export {
- MCPToolCallDisplay,
- ResourceLink,
- ToolStatusIcon,
getToolIcon,
+ MCPToolCallDisplay,
type MCPToolCallDisplayProps,
+ ResourceLink,
type ResourceLinkProps,
+ ToolStatusIcon,
} from './MCPToolCall';
// AI Message Display
export {
AIMessageDisplay,
- MessageAvatar,
- AITypingIndicator,
type AIMessageDisplayProps,
+ AITypingIndicator,
+ MessageAvatar,
} from './AIMessage';
// AI Chat
export {
AIChat,
- SuggestedActions,
type AIChatProps,
+ SuggestedActions,
type SuggestedActionsProps,
} from './AIChat';
// AI Chat Modal
export {
AIChatModal,
- AIChatTrigger,
- FloatingAIChat,
type AIChatModalProps,
+ AIChatTrigger,
type AIChatTriggerProps,
+ FloatingAIChat,
type FloatingAIChatProps,
} from './AIChatModal';
diff --git a/src/components/Accordion/Accordion.tsx b/src/components/Accordion/Accordion.tsx
new file mode 100644
index 00000000..4fcd3a8a
--- /dev/null
+++ b/src/components/Accordion/Accordion.tsx
@@ -0,0 +1,482 @@
+import { cva, type VariantProps } from 'class-variance-authority';
+import * as React from 'react';
+
+import { cn } from '../../utils/cn';
+
+// =============================================================================
+// Accordion Context
+// =============================================================================
+
+interface AccordionContextValue {
+ expandedItems: Set
;
+ toggleItem: (id: string) => void;
+ allowMultiple: boolean;
+}
+
+const AccordionContext = React.createContext(
+ null
+);
+
+function useAccordionContext() {
+ const context = React.useContext(AccordionContext);
+ if (!context) {
+ throw new Error('Accordion components must be used within an Accordion');
+ }
+ return context;
+}
+
+// =============================================================================
+// Accordion Root
+// =============================================================================
+
+const accordionVariants = cva('w-full', {
+ variants: {
+ variant: {
+ default: 'divide-y divide-gray-200 dark:divide-gray-700',
+ bordered:
+ 'border border-gray-200 dark:border-gray-700 rounded-lg divide-y divide-gray-200 dark:divide-gray-700',
+ separated: 'space-y-2',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ },
+});
+
+export interface AccordionProps
+ extends React.HTMLAttributes,
+ VariantProps {
+ children: React.ReactNode;
+ /** Allow multiple items to be expanded at once */
+ allowMultiple?: boolean;
+ /** Default expanded item IDs */
+ defaultExpanded?: string[];
+}
+
+export function Accordion({
+ children,
+ variant,
+ allowMultiple = false,
+ defaultExpanded = [],
+ className,
+ ...props
+}: AccordionProps) {
+ const [expandedItems, setExpandedItems] = React.useState>(
+ new Set(defaultExpanded)
+ );
+
+ const toggleItem = React.useCallback(
+ (id: string) => {
+ setExpandedItems((prev) => {
+ const next = new Set(prev);
+ if (next.has(id)) {
+ next.delete(id);
+ } else {
+ if (!allowMultiple) {
+ next.clear();
+ }
+ next.add(id);
+ }
+ return next;
+ });
+ },
+ [allowMultiple]
+ );
+
+ const contextValue = React.useMemo(
+ () => ({ expandedItems, toggleItem, allowMultiple }),
+ [expandedItems, toggleItem, allowMultiple]
+ );
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+// =============================================================================
+// Accordion Item
+// =============================================================================
+
+interface AccordionItemContextValue {
+ itemId: string;
+ isExpanded: boolean;
+}
+
+const AccordionItemContext =
+ React.createContext(null);
+
+function useAccordionItemContext() {
+ const context = React.useContext(AccordionItemContext);
+ if (!context) {
+ throw new Error(
+ 'AccordionItem components must be used within an AccordionItem'
+ );
+ }
+ return context;
+}
+
+const accordionItemVariants = cva('', {
+ variants: {
+ variant: {
+ default: '',
+ bordered: 'first:rounded-t-lg last:rounded-b-lg',
+ separated:
+ 'border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ },
+});
+
+export interface AccordionItemProps
+ extends React.HTMLAttributes,
+ VariantProps {
+ children: React.ReactNode;
+ /** Unique identifier for this item */
+ id: string;
+}
+
+export function AccordionItem({
+ children,
+ id,
+ variant,
+ className,
+ ...props
+}: AccordionItemProps) {
+ const { expandedItems } = useAccordionContext();
+ const isExpanded = expandedItems.has(id);
+
+ const contextValue = React.useMemo(
+ () => ({ itemId: id, isExpanded }),
+ [id, isExpanded]
+ );
+
+ return (
+
+
+ {children}
+
+
+ );
+}
+
+// =============================================================================
+// Accordion Trigger
+// =============================================================================
+
+const accordionTriggerVariants = cva(
+ 'flex w-full items-center justify-between text-left font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2',
+ {
+ variants: {
+ size: {
+ sm: 'py-2 px-3 text-sm',
+ md: 'py-3 px-4 text-base',
+ lg: 'py-4 px-5 text-lg',
+ },
+ variant: {
+ default:
+ 'hover:bg-gray-50 dark:hover:bg-gray-800 text-gray-900 dark:text-white',
+ muted:
+ 'hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-200',
+ },
+ },
+ defaultVariants: {
+ size: 'md',
+ variant: 'default',
+ },
+ }
+);
+
+export interface AccordionTriggerProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ children: React.ReactNode;
+ /** Show chevron icon */
+ showChevron?: boolean;
+}
+
+export function AccordionTrigger({
+ children,
+ size,
+ variant,
+ showChevron = true,
+ className,
+ ...props
+}: AccordionTriggerProps) {
+ const { toggleItem } = useAccordionContext();
+ const { itemId, isExpanded } = useAccordionItemContext();
+
+ return (
+
+ );
+}
+
+// =============================================================================
+// Accordion Content
+// =============================================================================
+
+const accordionContentVariants = cva(
+ 'overflow-hidden transition-all duration-200 ease-in-out',
+ {
+ variants: {
+ size: {
+ sm: 'px-3 text-sm',
+ md: 'px-4 text-base',
+ lg: 'px-5 text-lg',
+ },
+ },
+ defaultVariants: {
+ size: 'md',
+ },
+ }
+);
+
+export interface AccordionContentProps
+ extends React.HTMLAttributes,
+ VariantProps {
+ children: React.ReactNode;
+}
+
+export function AccordionContent({
+ children,
+ size,
+ className,
+ ...props
+}: AccordionContentProps) {
+ const { itemId, isExpanded } = useAccordionItemContext();
+ const contentRef = React.useRef(null);
+ const [height, setHeight] = React.useState(
+ isExpanded ? undefined : 0
+ );
+
+ React.useEffect(() => {
+ if (!contentRef.current) return;
+ const resizeObserver = new ResizeObserver(() => {
+ if (isExpanded) {
+ setHeight(contentRef.current?.scrollHeight);
+ }
+ });
+ resizeObserver.observe(contentRef.current);
+ return () => resizeObserver.disconnect();
+ }, [isExpanded]);
+
+ React.useEffect(() => {
+ if (isExpanded) {
+ setHeight(contentRef.current?.scrollHeight);
+ } else {
+ setHeight(0);
+ }
+ }, [isExpanded]);
+
+ return (
+
+ );
+}
+
+// =============================================================================
+// FAQ Accordion (Specialized for Provider Pages)
+// =============================================================================
+
+export interface FAQItem {
+ id: string;
+ question: string;
+ answer: string | React.ReactNode;
+}
+
+export interface FAQAccordionProps {
+ items: FAQItem[];
+ className?: string;
+}
+
+export function FAQAccordion({ items, className }: FAQAccordionProps) {
+ return (
+
+ {items.map((item) => (
+
+ {item.question}
+ {item.answer}
+
+ ))}
+
+ );
+}
+
+// =============================================================================
+// Provider FAQ Generator
+// =============================================================================
+
+export interface ProviderFAQData {
+ name: string;
+ address: {
+ street1: string;
+ street2?: string;
+ city: string;
+ state: string;
+ postalCode: string;
+ };
+ phoneNumber?: string;
+ services?: { name: string }[];
+ website?: string;
+ locationType?: string;
+}
+
+export function generateProviderFAQs(provider: ProviderFAQData): FAQItem[] {
+ const faqs: FAQItem[] = [];
+ const { name, address, phoneNumber, services, website, locationType } =
+ provider;
+
+ // Location FAQ
+ faqs.push({
+ id: 'location',
+ question: `Where is ${name} located?`,
+ answer: (
+
+ {name} is located at{' '}
+
+ {address.street1}, {address.city}, {address.state}{' '}
+ {address.postalCode}
+
+ .
+
+ ),
+ });
+
+ // Phone FAQ
+ if (phoneNumber) {
+ faqs.push({
+ id: 'phone',
+ question: `What is the phone number for ${name}?`,
+ answer: (
+
+ You can contact {name} by calling{' '}
+
+ {phoneNumber}
+
+ .
+
+ ),
+ });
+ }
+
+ // Services FAQ
+ if (services && services.length > 0) {
+ const serviceNames = services.slice(0, 5).map((s) => s.name);
+ const hasMore = services.length > 5;
+ faqs.push({
+ id: 'services',
+ question: `What services does ${name} offer?`,
+ answer: `${name} offers services including ${serviceNames.join(', ')}${hasMore ? ` and ${services.length - 5} more services` : ''}.`,
+ });
+ }
+
+ // Website FAQ
+ if (website) {
+ faqs.push({
+ id: 'website',
+ question: `Does ${name} have a website?`,
+ answer: (
+
+ Yes, you can visit the {name} website at{' '}
+
+ {new URL(website).hostname}
+
+ .
+
+ ),
+ });
+ }
+
+ // Facility Type FAQ
+ if (locationType) {
+ faqs.push({
+ id: 'facility-type',
+ question: `What type of facility is ${name}?`,
+ answer: `${name} is classified as a ${locationType}.`,
+ });
+ }
+
+ // Booking FAQ
+ faqs.push({
+ id: 'booking',
+ question: `How do I book an appointment at ${name}?`,
+ answer: `You can book an appointment at ${name} by clicking the "Book Appointment" button on this page, calling the provider directly${phoneNumber ? ` at ${phoneNumber}` : ''}, or visiting their website${website ? ` at ${new URL(website).hostname}` : ''}.`,
+ });
+
+ return faqs;
+}
+
+// =============================================================================
+// Icon
+// =============================================================================
+
+function ChevronIcon({ className }: { className?: string }) {
+ return (
+
+ );
+}
+
+export default Accordion;
diff --git a/src/components/Accordion/index.ts b/src/components/Accordion/index.ts
new file mode 100644
index 00000000..edcc8680
--- /dev/null
+++ b/src/components/Accordion/index.ts
@@ -0,0 +1,15 @@
+export {
+ Accordion,
+ AccordionContent,
+ type AccordionContentProps,
+ AccordionItem,
+ type AccordionItemProps,
+ type AccordionProps,
+ AccordionTrigger,
+ type AccordionTriggerProps,
+ FAQAccordion,
+ type FAQAccordionProps,
+ type FAQItem,
+ generateProviderFAQs,
+ type ProviderFAQData,
+} from './Accordion';
diff --git a/src/components/ActivityFeed/ActivityFeed.stories.tsx b/src/components/ActivityFeed/ActivityFeed.stories.tsx
new file mode 100644
index 00000000..f643cf73
--- /dev/null
+++ b/src/components/ActivityFeed/ActivityFeed.stories.tsx
@@ -0,0 +1,70 @@
+import type { Meta, StoryObj } from '@storybook/react-vite';
+import React from 'react';
+
+import { ActivityFeed, type ActivityItem } from './ActivityFeed';
+
+const meta: Meta = {
+ title: 'Dashboard/ActivityFeed',
+ component: ActivityFeed,
+ tags: ['autodocs'],
+ parameters: { layout: 'padded' },
+};
+
+export default meta;
+
+type Story = StoryObj;
+
+const now = Date.now();
+const minutesAgo = (m: number) => new Date(now - m * 60_000).toISOString();
+
+const items: ActivityItem[] = [
+ {
+ id: '1',
+ kind: 'results_ready',
+ title: 'Results ready for Alex Rivera',
+ description: 'DOT Physical — Midwest Occ Health',
+ timestamp: minutesAgo(3),
+ onClick: () => {},
+ },
+ {
+ id: '2',
+ kind: 'order_accepted',
+ title: 'Order accepted',
+ description: 'BH-10235 — Jamie Chen',
+ timestamp: minutesAgo(14),
+ },
+ {
+ id: '3',
+ kind: 'employee_added',
+ title: 'New employee added',
+ description: 'Sam Patel',
+ actor: 'you',
+ timestamp: minutesAgo(60),
+ },
+ {
+ id: '4',
+ kind: 'order_completed',
+ title: 'Order completed',
+ description: 'BH-10232 — Taylor Park',
+ timestamp: minutesAgo(60 * 6),
+ },
+ {
+ id: '5',
+ kind: 'invoice_paid',
+ title: 'Invoice paid',
+ description: 'INV-221 — $1,240.00',
+ timestamp: minutesAgo(60 * 26),
+ },
+];
+
+export const Default: Story = {
+ args: { items },
+};
+
+export const Loading: Story = {
+ args: { items: [], loading: true },
+};
+
+export const Empty: Story = {
+ args: { items: [] },
+};
diff --git a/src/components/ActivityFeed/ActivityFeed.tsx b/src/components/ActivityFeed/ActivityFeed.tsx
new file mode 100644
index 00000000..0a9b669a
--- /dev/null
+++ b/src/components/ActivityFeed/ActivityFeed.tsx
@@ -0,0 +1,281 @@
+'use client';
+
+import * as React from 'react';
+
+import { cn } from '../../utils/cn';
+
+// =============================================================================
+// Types
+// =============================================================================
+
+export type ActivityKind =
+ | 'order_created'
+ | 'order_accepted'
+ | 'order_completed'
+ | 'order_refused'
+ | 'results_ready'
+ | 'employee_added'
+ | 'invoice_paid'
+ | 'message'
+ | 'system';
+
+export interface ActivityItem {
+ id: string;
+ kind: ActivityKind;
+ /** Primary title (e.g. "Order accepted by Midwest Occ Health"). */
+ title: string;
+ /** Optional secondary description (e.g. employee name, service). */
+ description?: string;
+ /** Actor (e.g. user or system that generated the event). */
+ actor?: string;
+ /** When the event happened. */
+ timestamp?: string | Date;
+ /** Optional click handler to drill into the related entity. */
+ onClick?: () => void;
+}
+
+export interface ActivityFeedProps {
+ items: ActivityItem[];
+ /** Loading state. */
+ loading?: boolean;
+ /** Empty state node. */
+ emptyState?: React.ReactNode;
+ /** Max items to show before scrolling. */
+ maxItems?: number;
+ /** Additional CSS classes. */
+ className?: string;
+}
+
+// =============================================================================
+// Icon + color per kind
+// =============================================================================
+
+function relativeTime(input?: string | Date): string {
+ if (!input) return '';
+ const d = input instanceof Date ? input : new Date(input);
+ const diff = Date.now() - d.getTime();
+ if (Number.isNaN(diff)) return '';
+ const s = Math.max(0, Math.floor(diff / 1000));
+ if (s < 60) return 'just now';
+ const m = Math.floor(s / 60);
+ if (m < 60) return `${m}m ago`;
+ const h = Math.floor(m / 60);
+ if (h < 24) return `${h}h ago`;
+ const d2 = Math.floor(h / 24);
+ if (d2 < 30) return `${d2}d ago`;
+ return d.toLocaleDateString();
+}
+
+const KIND_STYLE: Record<
+ ActivityKind,
+ { color: string; bg: string; icon: React.ReactNode }
+> = {
+ order_created: {
+ color: 'text-sky-700 dark:text-sky-300',
+ bg: 'bg-sky-100 dark:bg-sky-900/30',
+ icon: (
+
+ ),
+ },
+ order_accepted: {
+ color: 'text-violet-700 dark:text-violet-300',
+ bg: 'bg-violet-100 dark:bg-violet-900/30',
+ icon: (
+
+ ),
+ },
+ order_completed: {
+ color: 'text-neutral-700 dark:text-neutral-300',
+ bg: 'bg-neutral-100 dark:bg-neutral-800',
+ icon: (
+
+ ),
+ },
+ order_refused: {
+ color: 'text-red-700 dark:text-red-300',
+ bg: 'bg-red-100 dark:bg-red-900/30',
+ icon: (
+
+ ),
+ },
+ results_ready: {
+ color: 'text-green-700 dark:text-green-300',
+ bg: 'bg-green-100 dark:bg-green-900/30',
+ icon: (
+
+ ),
+ },
+ employee_added: {
+ color: 'text-primary-700 dark:text-primary-300',
+ bg: 'bg-primary-100 dark:bg-primary-900/30',
+ icon: (
+
+ ),
+ },
+ invoice_paid: {
+ color: 'text-amber-700 dark:text-amber-300',
+ bg: 'bg-amber-100 dark:bg-amber-900/30',
+ icon: (
+
+ ),
+ },
+ message: {
+ color: 'text-sky-700 dark:text-sky-300',
+ bg: 'bg-sky-100 dark:bg-sky-900/30',
+ icon: (
+
+ ),
+ },
+ system: {
+ color: 'text-neutral-700 dark:text-neutral-300',
+ bg: 'bg-neutral-100 dark:bg-neutral-800',
+ icon: (
+
+ ),
+ },
+};
+
+// =============================================================================
+// Component
+// =============================================================================
+
+/**
+ * ActivityFeed — compact vertical event list. Intended for dashboards to
+ * surface recent order/result/invoice/message activity.
+ */
+export function ActivityFeed({
+ items,
+ loading = false,
+ emptyState,
+ maxItems,
+ className,
+}: ActivityFeedProps): React.JSX.Element {
+ const visible = maxItems ? items.slice(0, maxItems) : items;
+
+ if (loading) {
+ return (
+
+ {[0, 1, 2, 3].map((i) => (
+
+ ))}
+
+ );
+ }
+
+ if (!items.length) {
+ return (
+
+ {emptyState ?? (
+
+ No recent activity
+
+ )}
+
+ );
+ }
+
+ return (
+
+ {visible.map((item, idx) => {
+ const style = KIND_STYLE[item.kind] ?? KIND_STYLE.system;
+ const isLast = idx === visible.length - 1;
+ const interactive = Boolean(item.onClick);
+ return (
+ -
+ {/* Timeline rail */}
+ {!isLast && (
+
+ )}
+
+ {style.icon}
+
+
{
+ if (!interactive) return;
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ item.onClick?.();
+ }
+ }}
+ >
+
+ {item.title}
+
+ {item.description && (
+
+ {item.description}
+
+ )}
+
+ {item.actor && {item.actor}}
+ {item.timestamp && (
+
+ {relativeTime(item.timestamp)}
+
+ )}
+
+
+
+ );
+ })}
+
+ );
+}
diff --git a/src/components/ActivityFeed/index.ts b/src/components/ActivityFeed/index.ts
new file mode 100644
index 00000000..a87c71a9
--- /dev/null
+++ b/src/components/ActivityFeed/index.ts
@@ -0,0 +1,6 @@
+export {
+ ActivityFeed,
+ type ActivityFeedProps,
+ type ActivityItem,
+ type ActivityKind,
+} from './ActivityFeed';
diff --git a/src/components/AddContactModal/AddContactModal.stories.tsx b/src/components/AddContactModal/AddContactModal.stories.tsx
index bc67239c..4ea6877c 100644
--- a/src/components/AddContactModal/AddContactModal.stories.tsx
+++ b/src/components/AddContactModal/AddContactModal.stories.tsx
@@ -1,7 +1,8 @@
import type { Meta, StoryObj } from '@storybook/react';
import { useEffect, useState } from 'react';
-import { AddContactModal, ContactFormData } from './AddContactModal';
+
import { Button } from '../Button/Button';
+import { AddContactModal, ContactFormData } from './AddContactModal';
const meta: Meta = {
title: 'Components/Forms & Inputs/AddContactModal',
diff --git a/src/components/AddContactModal/AddContactModal.tsx b/src/components/AddContactModal/AddContactModal.tsx
index 9f0f19f8..0dcc7ed1 100644
--- a/src/components/AddContactModal/AddContactModal.tsx
+++ b/src/components/AddContactModal/AddContactModal.tsx
@@ -1,18 +1,19 @@
'use client';
import * as React from 'react';
-import { useState, useEffect } from 'react';
+import { useEffect, useState } from 'react';
+
+import { cn } from '../../utils/cn';
+import { Button } from '../Button/Button';
+import { Input } from '../Input/Input';
import {
Modal,
- ModalHeader,
- ModalTitle,
ModalBody,
ModalFooter,
+ ModalHeader,
+ ModalTitle,
} from '../Modal/Modal';
-import { Button } from '../Button/Button';
-import { Input } from '../Input/Input';
import { Select } from '../Select/Select';
-import { cn } from '../../utils/cn';
// ============================================================================
// Constants
diff --git a/src/components/AddContactModal/index.ts b/src/components/AddContactModal/index.ts
index 8b6c2486..234244a3 100644
--- a/src/components/AddContactModal/index.ts
+++ b/src/components/AddContactModal/index.ts
@@ -1,7 +1,7 @@
-export { AddContactModal } from './AddContactModal';
export type {
AddContactModalProps,
- ContactFormData,
ContactAddress,
+ ContactFormData,
CustomField,
} from './AddContactModal';
+export { AddContactModal } from './AddContactModal';
diff --git a/src/components/AdditionalFields/AdditionalFields.tsx b/src/components/AdditionalFields/AdditionalFields.tsx
index 0f2b8192..46c4d526 100644
--- a/src/components/AdditionalFields/AdditionalFields.tsx
+++ b/src/components/AdditionalFields/AdditionalFields.tsx
@@ -1,4 +1,5 @@
import * as React from 'react';
+
import { cn } from '../../utils/cn';
import { Button } from '../Button';
import { ChevronDownIcon, PlusIcon, TrashIcon } from '../Icons';
diff --git a/src/components/AdditionalFields/index.ts b/src/components/AdditionalFields/index.ts
index d6db0435..17d42536 100644
--- a/src/components/AdditionalFields/index.ts
+++ b/src/components/AdditionalFields/index.ts
@@ -1,6 +1,6 @@
export {
AdditionalFields,
- generateId,
type AdditionalFieldsProps,
+ generateId,
type KeyValueEntry,
} from './AdditionalFields';
diff --git a/src/components/Address/Address.stories.tsx b/src/components/Address/Address.stories.tsx
index ea4f9159..4f57cba1 100644
--- a/src/components/Address/Address.stories.tsx
+++ b/src/components/Address/Address.stories.tsx
@@ -1,10 +1,11 @@
import type { Meta, StoryObj } from '@storybook/react';
+
import {
Address,
AddressCard,
- AddressInline,
AddressCompact,
type AddressData,
+ AddressInline,
} from './Address';
const meta: Meta = {
diff --git a/src/components/Address/index.ts b/src/components/Address/index.ts
index 12a925f9..f3db7385 100644
--- a/src/components/Address/index.ts
+++ b/src/components/Address/index.ts
@@ -1,25 +1,24 @@
export {
Address,
AddressCard,
- AddressInline,
+ type AddressCardProps,
AddressCompact,
+ type AddressCompactProps,
+ // Types
+ type AddressData,
+ AddressInline,
+ type AddressInlineProps,
+ type AddressProps,
+ formatAddressLines,
// Utility functions
formatAddressSingleLine,
- formatAddressLines,
- formatCityStateZip,
formatCityState,
- getGoogleMapsUrl,
+ formatCityStateZip,
getGoogleMapsSearchUrl,
- // Types
- type AddressData,
- type AddressProps,
- type AddressCardProps,
- type AddressInlineProps,
- type AddressCompactProps,
+ getGoogleMapsUrl,
} from './Address';
-
export {
AddressForm,
- type AddressFormProps,
type AddressFormData,
+ type AddressFormProps,
} from './AddressForm';
diff --git a/src/components/Alert/index.ts b/src/components/Alert/index.ts
index 9b6803ea..4ac7230c 100644
--- a/src/components/Alert/index.ts
+++ b/src/components/Alert/index.ts
@@ -1,7 +1,7 @@
export {
Alert,
- AlertTitle,
AlertDescription,
- alertVariants,
type AlertProps,
+ AlertTitle,
+ alertVariants,
} from './Alert';
diff --git a/src/components/AudioRecorder/index.ts b/src/components/AudioRecorder/index.ts
index 21064a76..988a1a00 100644
--- a/src/components/AudioRecorder/index.ts
+++ b/src/components/AudioRecorder/index.ts
@@ -1,10 +1,10 @@
export {
AudioRecorder,
+ type AudioRecorderControlsRenderProps,
+ type AudioRecorderProps,
+ type AudioRecorderState,
audioRecorderVariants,
- waveformContainerVariants,
controlButtonVariants,
formatTime,
- type AudioRecorderProps,
- type AudioRecorderState,
- type AudioRecorderControlsRenderProps,
+ waveformContainerVariants,
} from './AudioRecorder';
diff --git a/src/components/AuthDialog/index.ts b/src/components/AuthDialog/index.ts
index 2e295e9f..c99ad168 100644
--- a/src/components/AuthDialog/index.ts
+++ b/src/components/AuthDialog/index.ts
@@ -1,8 +1,8 @@
export {
AuthDialog,
- DEFAULT_SOCIAL_PROVIDERS,
type AuthDialogProps,
type AuthMode,
- type SocialProvider,
+ DEFAULT_SOCIAL_PROVIDERS,
type SignupData,
+ type SocialProvider,
} from './AuthDialog';
diff --git a/src/components/Avatar/Avatar.stories.tsx b/src/components/Avatar/Avatar.stories.tsx
index bafbc165..54796bff 100644
--- a/src/components/Avatar/Avatar.stories.tsx
+++ b/src/components/Avatar/Avatar.stories.tsx
@@ -1,4 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
+
import { Avatar, AvatarGroup } from './Avatar';
const meta: Meta = {
diff --git a/src/components/Avatar/Avatar.tsx b/src/components/Avatar/Avatar.tsx
index a49edab2..24df7c54 100644
--- a/src/components/Avatar/Avatar.tsx
+++ b/src/components/Avatar/Avatar.tsx
@@ -1,5 +1,6 @@
-import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
+import * as React from 'react';
+
import { cn } from '../../utils/cn';
// ============================================================================
@@ -34,8 +35,7 @@ const avatarVariants = cva(
);
export interface AvatarProps
- extends
- React.HTMLAttributes,
+ extends React.HTMLAttributes,
VariantProps {
/** Image URL for the avatar */
src?: string | null;
diff --git a/src/components/Badge/Badge.stories.tsx b/src/components/Badge/Badge.stories.tsx
index ef3e6f92..c33f5880 100644
--- a/src/components/Badge/Badge.stories.tsx
+++ b/src/components/Badge/Badge.stories.tsx
@@ -1,23 +1,24 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
+import type { LucideIcon } from 'lucide-react';
import React from 'react';
-import { Badge } from './Badge';
+
import {
- CheckIcon,
AlertCircleIcon,
- InfoIcon,
- StarIcon,
- HeartIcon,
BellIcon,
- TagIcon,
- ZapIcon,
- ShieldIcon,
+ CheckIcon,
ClockIcon,
- UserIcon,
+ HeartIcon,
+ InfoIcon,
MailIcon,
PlusIcon,
+ ShieldIcon,
SparklesIcon,
+ StarIcon,
+ TagIcon,
+ UserIcon,
+ ZapIcon,
} from '../Icons';
-import type { LucideIcon } from 'lucide-react';
+import { Badge } from './Badge';
// Map of available icons for the dropdown
const iconMap: Record = {
diff --git a/src/components/Badge/Badge.tsx b/src/components/Badge/Badge.tsx
index 2a21c065..1cb7e49e 100644
--- a/src/components/Badge/Badge.tsx
+++ b/src/components/Badge/Badge.tsx
@@ -1,5 +1,6 @@
-import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
+import * as React from 'react';
+
import { cn } from '../../utils/cn';
const badgeVariants = cva(
@@ -37,8 +38,7 @@ const badgeVariants = cva(
);
export interface BadgeProps
- extends
- React.HTMLAttributes,
+ extends React.HTMLAttributes,
VariantProps {
/** Optional icon before the text */
icon?: React.ReactNode;
diff --git a/src/components/Badge/index.ts b/src/components/Badge/index.ts
index 7385ca71..72c30950 100644
--- a/src/components/Badge/index.ts
+++ b/src/components/Badge/index.ts
@@ -1 +1 @@
-export { Badge, badgeVariants, type BadgeProps } from './Badge';
+export { Badge, type BadgeProps, badgeVariants } from './Badge';
diff --git a/src/components/BookingDialog/index.ts b/src/components/BookingDialog/index.ts
index 0eb4ec5a..0a4fcb70 100644
--- a/src/components/BookingDialog/index.ts
+++ b/src/components/BookingDialog/index.ts
@@ -1,21 +1,20 @@
export {
BookingDialog,
- FloatingInput,
- ServiceSelect,
- ConsentSwitch,
- DialogOverlay,
- InlineBookingForm,
- QuickBookCard,
- type BookingService,
- type BookingProvider,
- type BookingFormData,
type BookingDialogProps,
- type FloatingInputProps,
- type ServiceSelectProps,
+ type BookingFormData,
+ type BookingProvider,
+ type BookingService,
+ ConsentSwitch,
type ConsentSwitchProps,
+ DialogOverlay,
type DialogOverlayProps,
+ FloatingInput,
+ type FloatingInputProps,
+ InlineBookingForm,
type InlineBookingFormProps,
+ QuickBookCard,
type QuickBookCardProps,
+ ServiceSelect,
+ type ServiceSelectProps,
} from './BookingDialog';
-
export { default } from './BookingDialog';
diff --git a/src/components/Breadcrumb/index.ts b/src/components/Breadcrumb/index.ts
index d4cb6f6c..f8534f1d 100644
--- a/src/components/Breadcrumb/index.ts
+++ b/src/components/Breadcrumb/index.ts
@@ -1,6 +1,6 @@
export {
Breadcrumb,
- BreadcrumbSlash,
- type BreadcrumbProps,
type BreadcrumbItem,
+ type BreadcrumbProps,
+ BreadcrumbSlash,
} from './Breadcrumb';
diff --git a/src/components/BusinessHours/BusinessHours.stories.tsx b/src/components/BusinessHours/BusinessHours.stories.tsx
index 2dd4bf8c..66641827 100644
--- a/src/components/BusinessHours/BusinessHours.stories.tsx
+++ b/src/components/BusinessHours/BusinessHours.stories.tsx
@@ -1,10 +1,11 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
+
import {
BusinessHours,
+ type BusinessHoursSchedule,
CompactHours,
HoursSummary,
OpenStatusBadge,
- type BusinessHoursSchedule,
} from './BusinessHours';
// Sample data
diff --git a/src/components/BusinessHours/index.ts b/src/components/BusinessHours/index.ts
index 883951d2..768a8625 100644
--- a/src/components/BusinessHours/index.ts
+++ b/src/components/BusinessHours/index.ts
@@ -1,12 +1,12 @@
export {
BusinessHours,
- CompactHours,
- HoursSummary,
- OpenStatusBadge,
type BusinessHoursProps,
- type CompactHoursProps,
- type HoursSummaryProps,
type BusinessHoursSchedule,
+ CompactHours,
+ type CompactHoursProps,
type DayHours,
+ HoursSummary,
+ type HoursSummaryProps,
+ OpenStatusBadge,
type TimeRange,
} from './BusinessHours';
diff --git a/src/components/BusinessHoursEditor/BusinessHoursEditor.stories.tsx b/src/components/BusinessHoursEditor/BusinessHoursEditor.stories.tsx
index a329d52f..6bd3a76e 100644
--- a/src/components/BusinessHoursEditor/BusinessHoursEditor.stories.tsx
+++ b/src/components/BusinessHoursEditor/BusinessHoursEditor.stories.tsx
@@ -1,11 +1,12 @@
import type { Meta, StoryObj } from '@storybook/react';
import { useEffect, useState } from 'react';
+
import {
BusinessHoursEditor,
- DaySchedule,
- createDefaultSchedule,
create24HourSchedule,
+ createDefaultSchedule,
createWeekdaySchedule,
+ DaySchedule,
} from './BusinessHoursEditor';
const meta: Meta = {
diff --git a/src/components/BusinessHoursEditor/index.ts b/src/components/BusinessHoursEditor/index.ts
index 1a8fa246..931e6e28 100644
--- a/src/components/BusinessHoursEditor/index.ts
+++ b/src/components/BusinessHoursEditor/index.ts
@@ -1,11 +1,11 @@
-export {
- BusinessHoursEditor,
- createDefaultSchedule,
- create24HourSchedule,
- createWeekdaySchedule,
-} from './BusinessHoursEditor';
export type {
BusinessHoursEditorProps,
DaySchedule,
TimeSlot,
} from './BusinessHoursEditor';
+export {
+ BusinessHoursEditor,
+ create24HourSchedule,
+ createDefaultSchedule,
+ createWeekdaySchedule,
+} from './BusinessHoursEditor';
diff --git a/src/components/Button/Button.stories.tsx b/src/components/Button/Button.stories.tsx
index 3a29cf3c..038aa310 100644
--- a/src/components/Button/Button.stories.tsx
+++ b/src/components/Button/Button.stories.tsx
@@ -1,55 +1,56 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
-import { Button } from './Button';
import {
- Plus,
- Minus,
+ ArrowLeft,
+ ArrowRight,
+ Bell,
+ BellOff,
+ Calendar,
Check,
- X,
- ChevronRight,
- ChevronLeft,
ChevronDown,
+ ChevronLeft,
+ ChevronRight,
ChevronUp,
- ArrowRight,
- ArrowLeft,
- Search,
- Settings,
- User,
- Users,
- Mail,
- Phone,
- Calendar,
Clock,
- Heart,
- Star,
- Trash2,
- Edit,
Copy,
Download,
- Upload,
- Share,
- Send,
- Save,
- Loader2,
- RefreshCw,
+ Edit,
ExternalLink,
- Link as LinkIcon,
Eye,
EyeOff,
- Lock,
- Unlock,
- Bell,
- BellOff,
+ Filter,
+ Heart,
Home,
+ Link as LinkIcon,
+ Loader2,
+ Lock,
+ type LucideIcon,
+ Mail,
Menu,
+ Minus,
MoreHorizontal,
MoreVertical,
- Filter,
+ Phone,
+ Plus,
+ RefreshCw,
+ Save,
+ Search,
+ Send,
+ Settings,
+ Share,
SortAsc,
SortDesc,
+ Star,
+ Trash2,
+ Unlock,
+ Upload,
+ User,
+ Users,
+ X,
Zap,
- type LucideIcon,
} from 'lucide-react';
+import { Button } from './Button';
+
// Icon registry for Storybook controls
const iconRegistry: Record = {
None: undefined,
@@ -120,7 +121,15 @@ const meta: Meta = {
argTypes: {
variant: {
control: 'select',
- options: ['primary', 'secondary', 'ghost', 'outline', 'danger', 'link'],
+ options: [
+ 'primary',
+ 'brand',
+ 'secondary',
+ 'ghost',
+ 'outline',
+ 'danger',
+ 'link',
+ ],
},
size: {
control: 'select',
@@ -164,6 +173,22 @@ export const Primary: Story = {
},
};
+export const Brand: Story = {
+ args: {
+ children: 'Get started',
+ variant: 'brand',
+ size: 'lg',
+ },
+ parameters: {
+ docs: {
+ description: {
+ story:
+ 'High-emphasis brand action with a gradient fill, brand-tinted glow, and a subtle lift on hover. Use for hero CTAs and auth flows — not for routine actions.',
+ },
+ },
+ },
+};
+
export const Secondary: Story = {
args: {
children: 'Secondary Button',
diff --git a/src/components/Button/Button.test.tsx b/src/components/Button/Button.test.tsx
index a561c385..9ea26ceb 100644
--- a/src/components/Button/Button.test.tsx
+++ b/src/components/Button/Button.test.tsx
@@ -1,6 +1,7 @@
-import { describe, it, expect, vi } from 'vitest';
-import { screen, fireEvent } from '@testing-library/react';
+import { fireEvent, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
+import { describe, expect, it, vi } from 'vitest';
+
import { renderWithTheme } from '../../test/test-utils';
import { Button } from './Button';
diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx
index 1fedec40..899c4700 100644
--- a/src/components/Button/Button.tsx
+++ b/src/components/Button/Button.tsx
@@ -1,5 +1,6 @@
-import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
+import * as React from 'react';
+
import { cn } from '../../utils/cn';
const buttonVariants = cva(
@@ -19,6 +20,11 @@ const buttonVariants = cva(
'hover:bg-primary-900',
'active:bg-primary-950',
],
+ brand: [
+ 'bg-gradient-brand text-white shadow-glow',
+ 'hover:-translate-y-0.5 hover:shadow-glow-hover',
+ 'active:translate-y-0 active:shadow-glow',
+ ],
secondary: [
'bg-neutral-200 text-neutral-900',
'hover:bg-neutral-300',
@@ -94,6 +100,7 @@ export interface ButtonProps
* @example
* ```tsx
*
+ *
* }>Delete
*
* ```
diff --git a/src/components/Button/index.ts b/src/components/Button/index.ts
index ce295122..4116a61b 100644
--- a/src/components/Button/index.ts
+++ b/src/components/Button/index.ts
@@ -1 +1 @@
-export { Button, buttonVariants, type ButtonProps } from './Button';
+export { Button, type ButtonProps, buttonVariants } from './Button';
diff --git a/src/components/CSVColumnMapper/CSVColumnMapper.stories.tsx b/src/components/CSVColumnMapper/CSVColumnMapper.stories.tsx
index 549671c6..e45175d0 100644
--- a/src/components/CSVColumnMapper/CSVColumnMapper.stories.tsx
+++ b/src/components/CSVColumnMapper/CSVColumnMapper.stories.tsx
@@ -1,9 +1,10 @@
import type { Meta, StoryObj } from '@storybook/react';
import { useState } from 'react';
+
import {
+ type CSVColumn,
CSVColumnMapper,
CSVFileUpload,
- type CSVColumn,
} from './CSVColumnMapper';
const meta: Meta = {
@@ -155,7 +156,7 @@ function FileUploadWrapper() {
}}
/>
{file && (
-
+
Selected file: {file.name}
)}
diff --git a/src/components/CSVColumnMapper/index.ts b/src/components/CSVColumnMapper/index.ts
index 24c07fad..dcf56182 100644
--- a/src/components/CSVColumnMapper/index.ts
+++ b/src/components/CSVColumnMapper/index.ts
@@ -1,8 +1,8 @@
export {
+ type CSVColumn,
CSVColumnMapper,
- CSVFileUpload,
type CSVColumnMapperProps,
- type CSVColumn,
- type FieldOption,
+ CSVFileUpload,
type CSVFileUploadProps,
+ type FieldOption,
} from './CSVColumnMapper';
diff --git a/src/components/Card/Card.stories.tsx b/src/components/Card/Card.stories.tsx
index 0dbe4ae2..7e59cb93 100644
--- a/src/components/Card/Card.stories.tsx
+++ b/src/components/Card/Card.stories.tsx
@@ -1,20 +1,21 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { useState } from 'react';
+
+import { Button } from '../Button';
import {
Card,
- CardHeader,
- CardTitle,
- CardDescription,
+ CardActions,
+ CardBadge,
+ CardCollapsible,
CardContent,
+ CardDescription,
+ CardDivider,
CardFooter,
+ CardHeader,
CardMedia,
- CardBadge,
- CardActions,
- CardDivider,
- CardCollapsible,
CardStat,
+ CardTitle,
} from './Card';
-import { Button } from '../Button';
const meta: Meta = {
title: 'Components/Layout & Structure/Card',
@@ -149,7 +150,7 @@ export const NoPadding: Story = {
/>
Image Card
-
+
Card with no padding and an image.
@@ -180,31 +181,31 @@ export const Variants: Story = {
Default
- With shadow
+ With shadow
Elevated
- Larger shadow
+ Larger shadow
Outlined
- Thicker border
+ Thicker border
Ghost
- No background
+ No background
Filled
- Muted background
+ Muted background