fermmap-shared 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.cjs.map CHANGED
@@ -1 +1 @@
1
- {"mappings":"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","sources":["packages/shared/src/index.ts","packages/shared/src/components/index.ts","packages/shared/src/components/AlertDialog.tsx","packages/shared/src/components/Button.tsx","packages/shared/src/components/TextField.tsx","packages/shared/src/lib/i18n-provider.tsx","packages/shared/src/components/Autocomplete.tsx","packages/shared/src/components/AutocompleteTable.tsx","packages/shared/src/components/Table.tsx","packages/shared/src/components/Checkbox.tsx","packages/shared/src/components/ProgressBar.tsx","packages/shared/src/components/AvatarButton.tsx","packages/shared/src/components/Badge.tsx","packages/shared/src/components/CloseButton.tsx","packages/shared/src/components/ComboBox.tsx","packages/shared/src/components/CookieConsent.tsx","packages/shared/src/components/Link.tsx","packages/shared/src/components/DisclosureGroup.tsx","packages/shared/src/components/FermentTypeBadge.tsx","packages/shared/src/utils/category-colors.ts","packages/shared/src/components/FilterTabs.tsx","packages/shared/src/components/LabeledValue.tsx","packages/shared/src/components/LinkButton.tsx","packages/shared/src/components/Menu.tsx","packages/shared/src/components/PasswordStrength.tsx","packages/shared/src/components/Popover.tsx","packages/shared/src/components/RadioGroup.tsx","packages/shared/src/components/SearchField.tsx","packages/shared/src/components/SegmentedControl.tsx","packages/shared/src/components/SegmentedTabs.tsx","packages/shared/src/components/Select.tsx","packages/shared/src/components/SourceTypeBadge.tsx","packages/shared/src/utils/source-type-colors.ts","packages/shared/src/components/StatCard.tsx","packages/shared/src/components/Switch.tsx","packages/shared/src/components/Tabs.tsx","packages/shared/src/components/TextArea.tsx","packages/shared/src/utils/browser.ts","packages/shared/src/lib/index.ts","packages/shared/src/lib/theme-context.tsx","packages/shared/src/lib/filter-context.tsx","packages/shared/src/i18n/index.ts","packages/shared/src/i18n/en.json","packages/shared/src/hooks/index.ts","packages/shared/src/hooks/useFilterState.ts","packages/shared/src/hooks/useIsMobileDevice.ts","packages/shared/src/utils/index.ts","packages/shared/src/utils/styleUtils.ts","packages/shared/src/utils/select-options.ts","packages/shared/src/utils/api.ts","packages/shared/src/types/index.ts","packages/shared/src/types/types.ts"],"sourcesContent":["// Components\nexport * from './components';\n\n// Lib (contexts & providers)\nexport * from './lib';\n\n// i18n\nexport { localeMessages } from './i18n';\n\n// Hooks\nexport * from './hooks';\n\n// Utils\nexport * from './utils';\n\n// Types\nexport * from './types';\n","// Component exports\nexport { AlertDialog, PromptDialog } from './AlertDialog';\nexport { Autocomplete, type AutocompleteItem, type AutocompleteProps } from './Autocomplete';\nexport { AutocompleteTable, type AutocompleteTableProps } from './AutocompleteTable';\nexport { AvatarButton, type AvatarButtonProps } from './AvatarButton';\nexport { Badge, type BadgeVariant } from './Badge';\nexport { Button } from './Button';\nexport { Checkbox, type CheckboxProps } from './Checkbox';\nexport { CloseButton } from './CloseButton';\nexport { ComboBox, type ComboBoxItem, type ComboBoxProps } from './ComboBox';\nexport {\n CookieConsent,\n type CookieConsentProps,\n type ConsentLevel,\n getConsentLevel,\n canUseAnalytics,\n canUseEssentials,\n hasConsented\n} from './CookieConsent';\nexport { DisclosureGroup, type DisclosureItem } from './DisclosureGroup';\nexport { FermentTypeBadge, type FermentTypeBadgeProps } from './FermentTypeBadge';\nexport { FilterTabs } from './FilterTabs';\nexport { LabeledValue } from './LabeledValue';\nexport { Link } from './Link';\nexport { LinkButton, type LinkButtonProps } from './LinkButton';\nexport { Menu, type MenuItem, type MenuProps } from './Menu';\nexport { PasswordStrength, type PasswordStrengthProps, type PasswordValidation } from './PasswordStrength';\nexport { Popover, type PopoverProps } from './Popover';\nexport { ProgressBar } from './ProgressBar';\nexport { RadioGroup } from './RadioGroup';\nexport { SearchField } from './SearchField';\nexport { SegmentedControl } from './SegmentedControl';\nexport { SegmentedTabs, SegmentedTabList, SegmentedTab } from './SegmentedTabs';\nexport { Select } from './Select';\nexport { SourceCategoryBadge, type SourceCategoryBadgeProps } from './SourceTypeBadge';\nexport { StatCard } from './StatCard';\nexport { Switch } from './Switch';\nexport { Table, type ColumnDef, type TableProps } from './Table';\nexport { Tabs, TabList, Tab, TabPanel } from './Tabs';\nexport { TextArea } from './TextArea';\nexport { TextField } from './TextField';\n","import { Button } from './Button';\nimport { Dialog, Heading, Modal, ModalOverlay } from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { TextField } from './TextField';\nimport { useIntl } from '../lib/i18n-context';\nimport { useState } from 'react';\n\n/**\n * Helper to translate default i18n keys or return custom labels as-is\n */\nconst useTranslateLabel = () => {\n const intl = useIntl();\n return (label: string) => {\n // If it's one of our default i18n keys, translate it\n if (label === 'admin.common.ok' || label === 'admin.common.cancel') {\n return intl.formatMessage({ id: label });\n }\n // Otherwise return as-is (already translated by caller)\n return label;\n };\n};\n\nconst overlayStyles = style({\n position: 'fixed',\n inset: 0,\n backgroundColor: 'transparent-black-500',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n isolation: 'isolate',\n zIndex: {\n isFromPopover: 111111\n }\n});\n\nconst modalStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n boxShadow: 'elevated',\n padding: 24,\n width: {\n default: 'calc(100vw - 32px)',\n sm: 400,\n md: 500\n },\n maxWidth: 500,\n transform: {\n entering: 'scale(0.95)',\n default: 'scale(1)'\n },\n});\n\nconst headingStyles = style({\n font: 'heading-xl',\n color: 'heading',\n margin: 0,\n marginBottom: 12\n});\n\nconst messageStyles = style({\n font: 'body',\n color: '--plum-800',\n margin: 0,\n marginBottom: 24\n});\n\nconst buttonGroupStyles = style({\n display: 'flex',\n gap: 12,\n justifyContent: 'end'\n});\n\nexport interface AlertDialogProps {\n isFromPopover?: boolean;\n isOpen: boolean;\n onClose: () => void;\n title: string;\n message: string;\n variant?: 'danger' | 'warning' | 'info' | 'success';\n confirmLabel?: string;\n cancelLabel?: string;\n onConfirm: () => void | Promise<void>;\n showCancel?: boolean;\n children?: React.ReactNode; // Optional custom content (e.g., Switch, Checkbox)\n}\n\nexport function AlertDialog({\n isFromPopover = false,\n isOpen,\n onClose,\n title,\n message,\n variant = 'info',\n confirmLabel = 'admin.common.ok',\n cancelLabel = 'admin.common.cancel',\n onConfirm,\n showCancel = true,\n children\n}: AlertDialogProps) {\n const [isProcessing, setIsProcessing] = useState(false);\n const translateLabel = useTranslateLabel();\n const intl = useIntl();\n\n const handleConfirm = async () => {\n setIsProcessing(true);\n try {\n await onConfirm();\n onClose();\n } finally {\n setIsProcessing(false);\n }\n };\n\n // Map variant to button variant\n const buttonVariant = variant === 'danger' ? 'danger' :\n variant === 'success' ? 'primary' :\n 'primary';\n\n return (\n <ModalOverlay isOpen={isOpen} onOpenChange={(open) => !open && onClose()} className={overlayStyles({ isFromPopover: isFromPopover })}>\n <Modal className={modalStyles}>\n <Dialog>\n {({ close }) => (\n <>\n <Heading slot=\"title\" className={headingStyles}>\n {title}\n </Heading>\n <p className={messageStyles}>{message}</p>\n {children && (\n <div className={style({ marginBottom: 24 })}>\n {children}\n </div>\n )}\n <div className={buttonGroupStyles}>\n {showCancel && (\n <Button\n variant=\"secondary\"\n size=\"M\"\n onPress={close}\n isDisabled={isProcessing}\n >{translateLabel(cancelLabel)}\n </Button>\n )}\n <Button\n variant={buttonVariant}\n size=\"M\"\n onPress={handleConfirm}\n isDisabled={isProcessing}\n >\n {isProcessing ? intl.formatMessage({ id: 'admin.common.processing' }) : translateLabel(confirmLabel)}\n </Button>\n </div>\n </>\n )}\n </Dialog>\n </Modal>\n </ModalOverlay>\n );\n}\n\nexport interface PromptDialogProps {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n message: string;\n inputLabel: string;\n inputPlaceholder?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n onConfirm: (value: string) => void | Promise<void>;\n validator?: (value: string) => string | null;\n}\n\nexport function PromptDialog({\n isOpen,\n onClose,\n title,\n message,\n inputLabel,\n inputPlaceholder = '',\n confirmLabel = 'admin.common.ok',\n cancelLabel = 'admin.common.cancel',\n onConfirm,\n validator\n}: PromptDialogProps) {\n const [value, setValue] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [isProcessing, setIsProcessing] = useState(false);\n const translateLabel = useTranslateLabel();\n const intl = useIntl();\n\n const handleConfirm = async () => {\n // Validate\n if (validator) {\n const validationError = validator(value);\n if (validationError) {\n setError(validationError);\n return;\n }\n }\n\n setIsProcessing(true);\n try {\n await onConfirm(value);\n setValue('');\n setError(null);\n onClose();\n } finally {\n setIsProcessing(false);\n }\n };\n\n const handleClose = () => {\n setValue('');\n setError(null);\n onClose();\n };\n\n return (\n <ModalOverlay isOpen={isOpen} onOpenChange={(open) => !open && handleClose()} className={overlayStyles}>\n <Modal className={modalStyles}>\n <Dialog>\n {({ close }) => (\n <>\n <Heading slot=\"title\" className={headingStyles}>\n {title}\n </Heading>\n <p className={messageStyles}>{message}</p>\n <div className={style({ marginBottom: 24 })}>\n <TextField\n label={inputLabel}\n value={value}\n onChange={(newValue) => {\n setValue(newValue);\n setError(null);\n }}\n placeholder={inputPlaceholder}\n error={error || undefined}\n autoFocus\n />\n </div>\n <div className={buttonGroupStyles}>\n <Button\n variant=\"secondary\"\n size=\"M\"\n onPress={() => {\n setValue('');\n setError(null);\n close();\n }}\n isDisabled={isProcessing}\n >{translateLabel(cancelLabel)}\n </Button>\n <Button\n variant=\"primary\"\n size=\"M\"\n onPress={handleConfirm}\n isDisabled={isProcessing}\n >\n {isProcessing ? intl.formatMessage({ id: 'admin.common.processing' }) : translateLabel(confirmLabel)}\n </Button>\n </div>\n </>\n )}\n </Dialog>\n </Modal>\n </ModalOverlay>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\n// For props: https://react-spectrum.adobe.com/react-aria/Button.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Button.html#styling\n// For conditional styling: https://react-spectrum.adobe.com/beta/s2/styling.html#conditional-styles\n\nexport interface ButtonProps extends AriaButtonProps {\n children?: React.ReactNode;\n variant?: 'primary' | 'secondary' | 'danger' | 'navigation';\n size?: 'S' | 'M' | 'L';\n}\n\n// Primary variant styles\nconst buttonStyles = style({\n ...focusRing(),\n font: {\n default: 'body',\n size: {\n S: 'body-sm',\n L: 'body-lg',// or `body` too?\n },\n },\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: {\n default: 8,\n size: {\n S: 4,\n },\n },\n paddingY: {\n default: 12,\n size: {\n S: 8,\n L: 16,\n },\n variant: {\n navigation: {\n default: 4\n }\n }\n },\n paddingStart: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n navigation: {\n default: 8\n }\n }\n },\n paddingEnd: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n navigation: 12\n }\n },\n width: {\n variant: {\n navigation: 'fit'\n }\n },\n color: {\n variant: {\n primary: {\n default: '--plum-900',\n },\n secondary: {\n default: '--plum-800',\n },\n danger: {\n default: '--yogurt-0',\n },\n navigation: {\n default: '--plum-800',\n }\n },\n forcedColors: 'ButtonText',\n isDisabled: {\n default: 'disabled',\n forcedColors: 'GrayText',\n },\n },\n '--iconPrimary': {\n type: 'fill',\n value: 'currentColor'\n },\n backgroundColor: {\n default: '--plum-300',\n variant: {\n primary: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisible: '--plum-400',\n isPressed: '--plum-400',\n },\n secondary: {\n default: '--yogurt-200',\n isHovered: '--yogurt-300',\n isFocusVisible: '--yogurt-300',\n isPressed: '--yogurt-400',\n },\n navigation: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n isPressed: '--yogurt-300',\n },\n danger: {\n default: '--berry-700',\n isHovered: '--berry-800',\n isFocusVisible: '--berry-800',\n isPressed: '--berry-900',\n },\n },\n forcedColors: 'ButtonFace',\n isDisabled: {\n default: 'gray-100',\n forcedColors: 'GrayText',\n },\n },\n borderRadius: 'lg',\n borderStyle: {\n default: 'none',\n forcedColors: 'solid'\n },\n borderColor: {\n forcedColors: 'ButtonBorder'\n },\n borderWidth: {\n forcedColors: 1\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n }\n});\n\nexport const Button = ({\n children,\n variant = 'primary',\n size = 'M',\n className,\n ...props\n}: ButtonProps) => {\n return (\n <AriaButton\n {...props}\n className={renderProps => `${className || ''} ` + buttonStyles({ ...renderProps, size, variant })}\n >\n {children}\n </AriaButton>\n );\n};\n","import {\n TextField as AriaTextField,\n TextFieldProps as AriaTextFieldProps,\n Input,\n Label,\n Text,\n FieldError,\n} from 'react-aria-components';\nimport { focusRing, style, } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { textFieldInputStyles } from '../utils/styleUtils' with { type: 'macro' };\n\nexport interface TextFieldProps extends Omit<AriaTextFieldProps, 'children'> {\n label: string;\n type?: 'text' | 'password' | 'email' | 'tel' | 'url' | 'number';\n error?: string;\n description?: string;\n placeholder?: string;\n}\n\nexport const textFieldStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n});\n\nexport const textFieldLabelStyles = style({\n font: 'body',\n color: '--plum-800',\n});\n\nconst inputStyles = style({\n ...focusRing(),\n ...textFieldInputStyles(),\n});\n\nexport const textFieldErrorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nexport const textFieldDescriptionStyles = style({\n font: 'body-sm',\n color: '--plum-800',\n});\n\nexport function TextField({ label, type = 'text', error, description, placeholder, ...props }: TextFieldProps) {\n return (\n <AriaTextField className={textFieldStyles} isInvalid={!!error} {...props}>\n <Label className={textFieldLabelStyles}>{label}</Label>\n <Input className={inputStyles} type={type} placeholder={placeholder} />\n {description && !error && (\n <Text slot=\"description\" className={textFieldDescriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={textFieldErrorStyles}>{error}</FieldError>\n </AriaTextField>\n );\n}\n","import { createContext, useContext, ReactNode } from 'react';\nimport { IntlProvider, useIntl as useReactIntl } from 'react-intl';\n\ntype LocaleMessages = Record<string, Record<string, string>>;\n\ninterface I18nProviderProps {\n children: ReactNode;\n messages: LocaleMessages;\n}\n\ntype Locale = string;\n\ninterface I18nContextType {\n locale: Locale;\n setLocale: (locale: Locale) => void;\n}\n\nconst I18nContext = createContext<I18nContextType | undefined>(undefined);\n\n/**\n * Unified i18n provider that can be used by both frontend and admin UI.\n * \n * @param messages - Locale messages object (e.g., from src/i18n/index.ts or admin-ui/src/i18n/index.ts)\n * \n * @example\n * // Frontend usage\n * import { localeMessages } from '@/i18n';\n * <I18nProvider messages={localeMessages}>{children}</I18nProvider>\n * \n * @example\n * // Admin UI usage\n * import { localeMessages } from '@admin/i18n';\n * <I18nProvider messages={localeMessages}>{children}</I18nProvider>\n */\nexport function I18nProvider({ children, messages }: I18nProviderProps) {\n // For MVP, we'll start with just English\n // Future: Add locale state management and persistence\n const locale: Locale = 'en';\n\n const setLocale = (newLocale: Locale) => {\n // Future: Implement locale switching with localStorage\n console.log('Locale switching to:', newLocale);\n };\n\n // Defensive check: ensure messages are loaded\n const currentMessages = messages?.[locale] || {};\n\n if (!messages || Object.keys(currentMessages).length === 0) {\n console.warn('I18nProvider: No messages loaded for locale:', locale);\n }\n\n return (\n <I18nContext.Provider value={{ locale, setLocale }}>\n <IntlProvider\n messages={currentMessages}\n locale={locale}\n defaultLocale=\"en\"\n >\n {children}\n </IntlProvider>\n </I18nContext.Provider>\n );\n}\n\nexport function useI18n() {\n const context = useContext(I18nContext);\n if (context === undefined) {\n throw new Error('useI18n must be used within an I18nProvider');\n }\n return context;\n}\n\n/**\n * Hook to access react-intl's formatMessage and other intl methods.\n * Re-exported from react-intl for convenience.\n */\nexport function useIntl() {\n return useReactIntl();\n}\n","import { useState, useMemo } from 'react';\nimport {\n SearchField as AriaSearchField,\n Autocomplete as AriaAutocomplete,\n Input,\n Label,\n ListBox,\n ListBoxItem,\n useFilter,\n Popover,\n FieldError\n} from 'react-aria-components';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport Search from '@react-spectrum/s2/icons/Search';\nimport CheckmarkCircle from '@react-spectrum/s2/icons/CheckmarkCircle';\nimport type { Selection } from 'react-aria-components';\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n width: 'full',\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-800',\n});\n\nconst searchFieldStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nconst inputContainerStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst inputStyles = style({\n ...focusRing(),\n width: 'full',\n padding: 8,\n paddingStart: 40,\n font: 'body',\n color: {\n default: '--plum-800',\n '::placeholder': '--plum-400'\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n },\n});\n\nconst searchIconStyles = style({\n position: 'absolute',\n left: 12,\n top: '50%',\n transform: 'translateY(-50%)',\n pointerEvents: 'none',\n color: '--yogurt-600',\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 300,\n overflow: 'auto',\n isolation: 'isolate',\n // width: 'full',\n // minWidth: 300,\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none',\n maxHeight: 384,\n overflowY: 'auto'\n});\n\nconst listBoxItemStyles = style({\n ...focusRing(),\n font: 'body',\n paddingX: 8,\n paddingY: 8,\n borderRadius: 'sm',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocused: '--yogurt-200',\n isPressed: '--yogurt-300',\n },\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocused: '--plum-900',\n isPressed: '--plum-900',\n },\n outline: 'none',\n});\n\nconst selectedItemStyles = style({\n backgroundColor: {\n default: '--plum-100',\n isHovered: '--plum-200',\n isFocused: '--plum-200',\n },\n color: '--plum-900',\n});\n\nconst checkboxStyles = style({\n width: 16,\n height: 16,\n flexShrink: 0,\n});\n\nconst itemTextStyles = style({\n flex: 1,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nexport interface AutocompleteItem {\n id: string | number;\n name: string;\n [key: string]: any;\n}\n\nexport interface AutocompleteProps<T extends AutocompleteItem> {\n items: T[];\n selectedKeys?: Selection;\n onSelectionChange?: (keys: Selection) => void;\n selectionMode?: 'none' | 'single' | 'multiple';\n label: string;\n placeholder?: string;\n ariaLabel?: string;\n error?: string;\n getItemText?: (item: T) => string;\n onInputChange?: (value: string) => void; // Callback when search input changes\n isDisabled?: boolean;\n}\n\n/**\n * Autocomplete component using React Aria SearchField + ListBox\n * \n * Features:\n * - SearchField for filtering items\n * - Optional checkboxes for multi-selection\n * - Shows both selected and unselected items when filtered\n * - Selected items highlighted with background color\n * - Keyboard navigation (arrow keys, enter, escape)\n * - Accessible (ARIA labels, focus management)\n * \n * @example\n * ```tsx\n * // Single select\n * <Autocomplete\n * items={sources}\n * selectedKeys={selectedKey}\n * onSelectionChange={setSelectedKey}\n * selectionMode=\"single\"\n * label=\"Select Source\"\n * placeholder=\"Search sources...\"\n * />\n * \n * // Multi select\n * <Autocomplete\n * items={sources}\n * selectedKeys={selectedKeys}\n * onSelectionChange={setSelectedKeys}\n * selectionMode=\"multiple\"\n * label=\"Select Sources\"\n * placeholder=\"Search sources...\"\n * />\n * \n * // No selection (just filtering)\n * <Autocomplete\n * items={sources}\n * selectionMode=\"none\"\n * label=\"Search Sources\"\n * placeholder=\"Search sources...\"\n * />\n * ```\n */\nexport function Autocomplete<T extends AutocompleteItem>({\n items,\n selectedKeys,\n onSelectionChange,\n selectionMode = 'none',\n label,\n placeholder,\n ariaLabel,\n error,\n getItemText = (item) => item.name,\n onInputChange: externalOnInputChange,\n isDisabled = false,\n}: AutocompleteProps<T>) {\n const [inputValue, setInputValue] = useState('');\n const { contains } = useFilter({ sensitivity: 'base' });\n\n // Call external callback when input changes\n const handleInputChange = (value: string) => {\n setInputValue(value);\n externalOnInputChange?.(value);\n };\n\n // Filter items based on input value\n const filteredItems = useMemo(() => {\n if (!inputValue) return items;\n const searchLower = inputValue.toLowerCase();\n return items.filter((item) =>\n getItemText(item).toLowerCase().includes(searchLower)\n );\n }, [items, inputValue, getItemText]);\n\n // Sort to show selected items first when in selection mode\n const sortedItems = useMemo(() => {\n if (selectionMode === 'none' || !selectedKeys) return filteredItems;\n\n return [...filteredItems].sort((a, b) => {\n const aSelected = selectedKeys === 'all' || selectedKeys.has(String(a.id));\n const bSelected = selectedKeys === 'all' || selectedKeys.has(String(b.id));\n if (aSelected && !bSelected) return -1;\n if (!aSelected && bSelected) return 1;\n return 0;\n });\n }, [filteredItems, selectedKeys, selectionMode]);\n\n const showCheckboxes = selectionMode !== 'none';\n\n return (\n <div className={containerStyles}>\n <AriaAutocomplete\n filter={contains}\n inputValue={inputValue}\n onInputChange={handleInputChange}\n >\n <AriaSearchField\n className={searchFieldStyles}\n aria-label={ariaLabel || label}\n isDisabled={isDisabled}\n isInvalid={!!error}\n >\n <Label className={labelStyles}>{label}</Label>\n <div className={inputContainerStyles}>\n <div className={searchIconStyles}>\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <Search styles={iconStyle({ size: 'M', color: '--plum-800' })} />\n </div>\n <Input\n className={inputStyles}\n placeholder={placeholder}\n />\n </div>\n </AriaSearchField>\n\n <ListBox\n className={listBoxStyles}\n items={sortedItems}\n selectionMode={selectionMode}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange}\n aria-label={ariaLabel || `${label} options`}\n >\n {(item) => {\n const isSelected = selectedKeys === 'all' || (selectedKeys && selectedKeys.has(String(item.id)));\n return (\n <ListBoxItem\n key={item.id}\n id={String(item.id)}\n textValue={getItemText(item)}\n className={(renderProps) =>\n `${listBoxItemStyles(renderProps)} ${isSelected ? selectedItemStyles(renderProps) : ''}`\n }\n >\n {showCheckboxes && (\n <div className={checkboxStyles}>\n {isSelected && (\n // @ts-expect-error passing non-standard css value to icon color\n <CheckmarkCircle styles={iconStyle({ size: 'M', color: '--plum-800' })} />\n )}\n </div>\n )}\n <span className={itemTextStyles}>{getItemText(item)}</span>\n </ListBoxItem>\n );\n }}\n </ListBox>\n\n <FieldError className={errorStyles}>{error}</FieldError>\n </AriaAutocomplete>\n </div>\n );\n}\n","import { useMemo } from 'react';\nimport type { Selection } from 'react-aria-components';\nimport { Autocomplete, type AutocompleteItem } from './Autocomplete';\nimport { Table, type ColumnDef } from './Table';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 16,\n width: 'full',\n});\n\nconst tableContainerStyles = style({\n maxHeight: 384,\n overflow: 'auto',\n});\n\nexport interface AutocompleteTableProps<T extends AutocompleteItem> {\n // Autocomplete props\n allItems: T[];\n selectedKeys: Selection;\n onSelectionChange: (keys: Selection) => void;\n selectionMode?: 'single' | 'multiple';\n searchLabel: string;\n searchPlaceholder?: string;\n getItemText?: (item: T) => string;\n\n // Table props\n columns: ColumnDef<T>[];\n tableLabel: string;\n}\n\n/**\n * Composite component combining Autocomplete and Table\n * Useful for search-and-select workflows where selected items are displayed in a table\n * \n * Features:\n * - Autocomplete search field at top\n * - Table below showing selected items\n * - Filters all items in autocomplete\n * - Shows only selected items in table\n * - Scrollable table with max height of 384px\n * \n * @example\n * ```tsx\n * <AutocompleteTable\n * allItems={allSources}\n * selectedKeys={selectedSourceIds}\n * onSelectionChange={setSelectedSourceIds}\n * selectionMode=\"multiple\"\n * searchLabel=\"Search Sources\"\n * searchPlaceholder=\"Search by name or URL...\"\n * columns={[\n * { key: 'name', label: 'Name', isRowHeader: true },\n * { key: 'domain', label: 'Domain' },\n * { key: 'actions', label: 'Actions', align: 'end' },\n * ]}\n * tableLabel=\"Selected Sources\"\n * />\n * ```\n */\nexport function AutocompleteTable<T extends AutocompleteItem>({\n allItems,\n selectedKeys,\n onSelectionChange,\n selectionMode = 'multiple',\n searchLabel,\n searchPlaceholder,\n getItemText,\n columns,\n tableLabel,\n}: AutocompleteTableProps<T>) {\n // Filter to show only selected items in table\n const selectedItems = useMemo(() => {\n if (selectedKeys === 'all') return allItems;\n if (!selectedKeys || selectedKeys.size === 0) return [];\n\n return allItems.filter(item => selectedKeys.has(String(item.id)));\n }, [allItems, selectedKeys]);\n\n return (\n <div className={containerStyles}>\n <Autocomplete\n items={allItems}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange}\n selectionMode={selectionMode}\n label={searchLabel}\n placeholder={searchPlaceholder}\n getItemText={getItemText}\n />\n\n <div className={tableContainerStyles}>\n <Table\n columns={columns}\n data={selectedItems}\n ariaLabel={tableLabel}\n />\n </div>\n </div>\n );\n}\n","import {\n Cell,\n Collection,\n Column,\n Row,\n Table as AriaTable,\n TableBody,\n TableHeader,\n TableLoadMoreItem,\n type Key,\n type Selection,\n type SortDescriptor\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { Checkbox } from './Checkbox';\nimport { ProgressBar } from './ProgressBar';\nimport { ReactNode } from 'react';\nimport { useIntl } from '../lib/i18n-context';\n\nexport interface ColumnDef<T> {\n key: string;\n label: string;\n renderCell: (item: T) => ReactNode;\n width?: string;\n isRowHeader?: boolean;\n align?: 'start' | 'center' | 'end';\n}\n\nexport interface TableProps<T> {\n data: T[];\n columns: ColumnDef<T>[];\n onRowAction?: (key: Key) => void;\n ariaLabel: string;\n sortDescriptor?: SortDescriptor;\n onSortChange?: (descriptor: SortDescriptor) => void;\n selectionMode?: 'none' | 'single' | 'multiple';\n selectedKeys?: Selection;\n onSelectionChange?: (keys: Selection) => void;\n onLoadMore?: () => void;\n isLoadingMore?: boolean;\n isLoading?: boolean;\n emptyMessage?: string;\n}\n\nconst tableContainerStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-200',\n overflow: 'auto',\n maxHeight: {\n default: '80vh'\n }\n});\n\nconst tableStyles = style({\n width: 'full',\n borderCollapse: 'collapse'\n});\n\nconst tableHeaderStyles = style({\n backgroundColor: '--yogurt-200',\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-200',\n position: 'sticky',\n top: 0,\n zIndex: 1\n});\n\nconst columnHeaderStyles = style({\n padding: 16,\n textAlign: {\n default: 'start',\n align: {\n start: 'start',\n center: 'center',\n end: 'end',\n },\n },\n font: 'title-lg',\n color: '--plum-800',\n cursor: {\n allowsSorting: 'pointer'\n }\n});\n\nconst rowStyles = style({\n ...focusRing(),\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-100',\n isSelected: '--plum-100',\n isFocusVisible: '--yogurt-100'\n },\n cursor: {\n default: 'default',\n onRowAction: 'pointer',\n selectionMode: {\n single: 'pointer',\n multiple: 'pointer'\n }\n }\n});\n\nconst cellStyles = style({\n padding: 16,\n textAlign: {\n default: 'start',\n align: {\n start: 'start',\n center: 'center',\n end: 'end',\n },\n },\n font: 'body',\n color: '--plum-800',\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-100'\n});\n\nconst checkboxCellStyles = style({\n padding: 16,\n width: 48,\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-100'\n});\n\nconst emptyStateStyles = style({\n padding: 48,\n textAlign: 'center',\n font: 'body',\n color: '--plum-800',\n});\n\n/**\n * Reusable Table component using React Aria Components\n * \n * Features:\n * - Collection-based API for better performance\n * - Built-in virtualization support (via RAC)\n * - Keyboard navigation\n * - Screen reader support\n * - Optional sorting\n * - Optional row selection\n * - Optional row actions (clickable rows)\n * - Checkbox column for multiple selection\n * - Hover and selected states\n * \n * @example\n * ```tsx\n * const columns: ColumnDef<Run>[] = [\n * { key: 'id', label: 'ID', renderCell: (run) => `#${run.id}` },\n * { key: 'status', label: 'Status', renderCell: (run) => <Badge variant={run.status}>{run.status}</Badge> }\n * ];\n * \n * <Table\n * data={runs}\n * columns={columns}\n * ariaLabel=\"Scraper runs\"\n * selectionMode=\"multiple\"\n * selectedKeys={selectedIds}\n * onSelectionChange={setSelectedIds}\n * />\n * ```\n */\nexport function Table<T extends { id: number | string }>({\n data,\n columns,\n onRowAction,\n ariaLabel,\n sortDescriptor,\n onSortChange,\n selectionMode = 'none',\n selectedKeys,\n onSelectionChange,\n onLoadMore,\n isLoading = false,\n emptyMessage\n}: TableProps<T>) {\n const intl = useIntl();\n\n return (\n <div className={tableContainerStyles}>\n <AriaTable\n aria-label={ariaLabel}\n selectionMode={selectionMode}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange as any}\n onRowAction={onRowAction}\n sortDescriptor={sortDescriptor}\n onSortChange={onSortChange}\n className={tableStyles}\n >\n <TableHeader className={tableHeaderStyles}>\n {selectionMode === 'multiple' && (\n <Column className={columnHeaderStyles({ align: 'center', alignItems: 'center' })}>\n <Checkbox slot=\"selection\" size=\"S\" />\n </Column>\n )}\n {columns.map((column) => (\n <Column\n key={column.key}\n id={column.key}\n isRowHeader={column.isRowHeader}\n allowsSorting={!!onSortChange}\n className={columnHeaderStyles({ align: column.align })}\n >\n {column.label}\n </Column>\n ))}\n </TableHeader>\n <TableBody\n renderEmptyState={() => (\n <div className={emptyStateStyles}>\n {isLoading ? (\n <ProgressBar isIndeterminate label={intl.formatMessage({ id: 'table.common.loading' })} />\n ) : (\n emptyMessage || intl.formatMessage({ id: 'common.noData' })\n )}\n </div>\n )}\n >\n <Collection items={data}>\n {(item) => (\n <Row\n key={item.id}\n id={item.id}\n className={(renderProps) => rowStyles({\n ...renderProps,\n onRowAction: !!onRowAction,\n selectionMode\n })}\n >\n {selectionMode === 'multiple' && (\n <Cell className={checkboxCellStyles}>\n <Checkbox slot=\"selection\" size=\"S\" />\n </Cell>\n )}\n {columns.map((column) => (\n <Cell key={column.key} className={cellStyles({ align: column.align })}>\n {column.renderCell(item)}\n </Cell>\n ))}\n </Row>\n )}\n </Collection>\n <TableLoadMoreItem onLoadMore={onLoadMore}>\n <ProgressBar isIndeterminate label={intl.formatMessage({ id: 'table.common.loadingMore' })} />\n </TableLoadMoreItem>\n </TableBody>\n </AriaTable>\n </div>\n );\n}\n\n","import { Checkbox as AriaCheckbox, type CheckboxProps as AriaCheckboxProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport './Checkbox.css';\n\nexport interface CheckboxProps extends Omit<AriaCheckboxProps, 'className' | 'style'> {\n /**\n * The size of the Checkbox.\n * @default 'M'\n */\n size?: 'S' | 'M' | 'L';\n}\n\nconst checkboxStyles = style({\n display: 'flex',\n position: 'relative',\n alignItems: 'center',\n gap: 8,\n font: 'body',\n color: {\n default: '--plum-800',\n isDisabled: 'disabled'\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'default'\n },\n disableTapHighlight: true\n});\n\nconst boxStyles = style({\n ...focusRing(),\n size: {\n default: 20,\n size: {\n S: 16,\n M: 20,\n L: 24\n }\n },\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderStyle: 'solid',\n borderRadius: 'sm',\n transition: 'default',\n backgroundColor: {\n default: 'layer-1',\n isSelected: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isPressed: '--plum-900'\n },\n isDisabled: {\n default: 'gray-100',\n isSelected: 'disabled'\n }\n },\n borderColor: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isPressed: '--plum-900',\n isSelected: 'transparent',\n isDisabled: 'disabled'\n }\n});\n\nconst dashStyles = style({\n width: '60%',\n height: 2,\n backgroundColor: 'white',\n borderRadius: 'sm'\n});\n\n/**\n * Checkbox component for selection within tables and forms.\n * Simplified version based on React Spectrum S2 Checkbox.\n * \n * @example\n * ```tsx\n * <Checkbox isSelected={selected} onChange={setSelected}>\n * Subscribe to newsletter\n * </Checkbox>\n * \n * // In table header/cell (no label)\n * <Checkbox slot=\"selection\" />\n * ```\n */\nexport function Checkbox({ size = 'M', children, ...props }: CheckboxProps) {\n return (\n <AriaCheckbox\n {...props}\n className={(renderProps) => checkboxStyles(renderProps)}\n >\n {(renderProps) => {\n const { isSelected, isIndeterminate } = renderProps;\n\n return (\n <>\n <div className={boxStyles({ ...renderProps, isSelected: isSelected || isIndeterminate, size })}>\n {isIndeterminate ? (\n <div className={dashStyles} />\n ) : isSelected ? (\n <svg viewBox=\"0 0 18 18\" aria-hidden=\"true\" className=\"checkbox-checkmark\">\n <polyline points=\"2 9 7 14 16 4\" className=\"checkbox-checkmark-path checkbox-checkmark-selected\" />\n </svg>\n ) : null}\n </div>\n {children}\n </>\n );\n }}\n </AriaCheckbox>\n );\n}\n","import { ProgressBar as AriaProgressBar } from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport './ProgressBar.css';\n\nconst progressBarStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n width: 'full',\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-800',\n display: 'flex',\n justifyContent: 'space-between',\n});\n\nconst trackStyles = style({\n height: 6,\n backgroundColor: '--yogurt-200',\n borderRadius: 'full',\n overflow: 'hidden',\n position: 'relative',\n});\n\nconst fillStyles = style({\n height: 'full',\n backgroundColor: '--plum-500',\n borderRadius: 'full',\n});\n\nconst indeterminateFillStyles = style({\n height: 'full',\n backgroundColor: '--plum-500',\n borderRadius: 'full',\n width: '40%',\n});\n\ninterface ProgressBarProps {\n label?: string;\n value?: number;\n maxValue?: number;\n isIndeterminate?: boolean;\n showValueLabel?: boolean;\n 'aria-label'?: string;\n}\n\n/**\n * ProgressBar Component\n * \n * Accessible progress indicator using React Aria Components.\n * Supports both determinate (with value) and indeterminate (loading) states.\n * \n * @example\n * // Determinate progress\n * <ProgressBar label=\"Loading...\" value={50} maxValue={100} />\n * \n * @example\n * // Indeterminate progress\n * <ProgressBar label=\"Loading...\" isIndeterminate />\n */\nexport function ProgressBar({\n label,\n value = 0,\n maxValue = 100,\n isIndeterminate = false,\n showValueLabel = true,\n 'aria-label': ariaLabel,\n}: ProgressBarProps) {\n return (\n <AriaProgressBar\n value={isIndeterminate ? undefined : value}\n maxValue={maxValue}\n isIndeterminate={isIndeterminate}\n aria-label={ariaLabel || label}\n className={progressBarStyles}\n >\n {({ percentage, valueText }) => (\n <>\n {label && (\n <div className={labelStyles}>\n <span>{label}</span>\n {!isIndeterminate && showValueLabel && <span>{valueText}</span>}\n </div>\n )}\n <div className={trackStyles}>\n <div\n className={`${isIndeterminate ? 'progress-fill-indeterminate' : 'progress-fill'} ${isIndeterminate ? indeterminateFillStyles : fillStyles}`}\n style={isIndeterminate ? undefined : { width: `${percentage}%` }}\n />\n </div>\n </>\n )}\n </AriaProgressBar>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface AvatarButtonProps extends AriaButtonProps {\n initials: string;\n name?: string;\n showName?: boolean;\n}\n\nconst avatarButtonStyles = style({\n ...focusRing(),\n font: 'title-lg',\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n padding: 8,\n paddingX: 16,\n backgroundColor: {\n default: '--plum-100',\n isHovered: '--plum-200',\n isPressed: '--plum-300',\n isFocusVisible: '--plum-200'\n },\n color: '--plum-800',\n borderRadius: 'full',\n borderWidth: 2,\n borderStyle: 'solid',\n borderColor: '--plum-300',\n cursor: 'pointer'\n});\n\nconst avatarCircleStyles = style({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'full',\n backgroundColor: '--plum-800',\n font: 'title-lg',\n color: '--yogurt-0'\n});\n\nexport const AvatarButton = ({ initials, name, showName = true, ...props }: AvatarButtonProps) => {\n return (\n <AriaButton\n {...props}\n aria-label={!showName && name && !props['aria-label'] ? name : props['aria-label']}\n className={renderProps => avatarButtonStyles(renderProps)}\n >\n <div className={avatarCircleStyles}>\n {initials}\n </div>\n {showName && name && <span>{name}</span>}\n </AriaButton>\n );\n};\n","import { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport type BadgeVariant = 'completed' | 'running' | 'failed' | 'cancelled' | 'pending' | 'plum' | 'cucumber' | 'berry' | 'ginger' | 'peach' | 'yogurt';\n\nexport interface BadgeProps {\n variant?: BadgeVariant;\n children: React.ReactNode;\n}\n\nconst badgeStyles = style({\n paddingY: 4,\n paddingX: 12,\n borderRadius: 'lg',\n font: 'ui-sm',\n display: 'inline-block',\n backgroundColor: {\n variant: {\n completed: '--cucumber-100',\n running: '--ginger-100',\n failed: '--berry-100',\n cancelled: '--plum-100',\n pending: '--plum-100',\n plum: '--plum-100',\n cucumber: '--cucumber-100',\n berry: '--berry-100',\n ginger: '--ginger-100',\n peach: '--peach-100',\n yogurt: '--yogurt-100',\n }\n },\n color: {\n variant: {\n completed: '--cucumber-800',\n running: '--ginger-800',\n failed: '--berry-800',\n cancelled: '--plum-800',\n pending: '--plum-800',\n plum: '--plum-800',\n cucumber: '--cucumber-800',\n berry: '--berry-800',\n ginger: '--ginger-800',\n peach: '--peach-800',\n yogurt: '--yogurt-800',\n }\n }\n});\n\n/**\n * Badge component for displaying status or category with conditional colors\n * \n * @example\n * <Badge variant=\"completed\">Completed</Badge>\n * <Badge variant=\"cucumber\">Vegetable</Badge>\n */\nexport function Badge({ variant, children }: BadgeProps) {\n return (\n <span>{/* Wrapper span to prevent parent display flex's from affecting badge layout */}\n <span className={badgeStyles({ variant })}>\n {children}\n </span>\n </span>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport CloseCircle from '@react-spectrum/s2/icons/CloseCircle';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\n// For props: https://react-spectrum.adobe.com/react-aria/Button.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Button.html#styling\n\nexport interface CloseButtonProps extends Omit<AriaButtonProps, 'onPress'> {\n onClose: () => void;\n}\n\nconst closeButtonStyles = style({\n ...focusRing(),\n padding: 8,\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-100',\n isPressed: '--yogurt-200'\n },\n borderStyle: 'none',\n borderRadius: 'lg',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n forcedColors: {\n backgroundColor: 'ButtonFace',\n color: 'ButtonText',\n borderColor: 'ButtonBorder',\n borderWidth: 1,\n borderStyle: 'solid'\n }\n});\n\nexport const CloseButton = ({ onClose, ...props }: CloseButtonProps) => {\n const intl = useIntl();\n\n return (\n <AriaButton\n {...props}\n onPress={onClose}\n className={renderProps => closeButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'ferment.detail.close' })}\n >\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <CloseCircle styles={iconStyle({ size: 'XL', color: '--plum-800' })} />\n </AriaButton>\n );\n};\n","import { Button, ComboBox as AriaComboBox, ComboBoxRenderProps, FieldError, Group, Input, Label, ListBox, ListBoxItem, Popover, Text } from 'react-aria-components';\nimport type { Key } from 'react-aria-components';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useEffect, useState } from 'react';\nimport ChevronDown from '@react-spectrum/s2/icons/ChevronDown';\n\nconst comboBoxContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n minWidth: 192,\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-800',\n});\n\nconst inputWrapperStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst inputStyles = style({\n ...focusRing(),\n width: 'calc(100% - 44px)', // Padding (12px+32px) and border (1px+1px)\n font: 'body',\n flexGrow: 1,\n flexShrink: 1,\n paddingStart: 12,\n paddingEnd: 32,\n paddingY: 8,\n backgroundColor: {\n default: '--yogurt-100',\n isFocusVisible: '--yogurt-200',\n isDisabled: 'gray-50',\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: {\n default: '--yogurt-400',\n isFocusVisible: '--plum-800',\n isDisabled: 'gray-200',\n },\n color: {\n default: '--plum-800',\n '::placeholder': '--plum-400',\n isDisabled: 'disabled',\n },\n outlineStyle: 'none',\n});\n\nconst buttonStyles = style({\n ...focusRing(),\n verticalAlign: 'middle',\n marginStart: -36, // 32px for button + padding and 4px this padding\n padding: 4,\n borderRadius: 'sm',\n backgroundColor: {\n default: 'transparent',\n isHovered: 'gray-200',\n },\n borderStyle: 'none',\n color: {\n default: 'gray-600',\n isDisabled: 'disabled',\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'default',\n },\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 320,\n overflow: 'auto',\n isolation: 'isolate',\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none',\n});\n\nconst listBoxItemStyles = style({\n font: 'body',\n paddingX: 8,\n paddingY: 4,\n borderRadius: 'sm',\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed',\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isSelected: '--plum-200',\n isFocused: '--yogurt-200',\n isDisabled: 'gray-100',\n },\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocused: '--plum-900',\n isPressed: '--plum-900',\n isSelected: '--plum-900',\n isDisabled: 'disabled',\n },\n outline: 'none',\n});\n\nconst descriptionStyles = style({\n font: 'detail',\n color: '--plum-800',\n});\n\nconst errorStyles = style({\n font: 'detail',\n color: '--berry-700',\n});\n\nexport interface ComboBoxItem {\n id: Key;\n label: string;\n textValue?: string;\n}\n\nexport interface ComboBoxProps {\n label?: string;\n description?: string;\n errorMessage?: string;\n isRequired?: boolean;\n isDisabled?: boolean;\n selectedKey?: Key | null;\n onSelectionChange?: (key: Key | null) => void;\n items: ComboBoxItem[];\n placeholder?: string;\n 'aria-label'?: string;\n inputValue?: string;\n onInputChange?: (value: string) => void;\n}\n\n/**\n * Generic ComboBox component for searchable single-selection.\n * \n * Uses React Aria Components ComboBox for accessible selection with filtering.\n * Reusable for any data type - countries, users, categories, etc.\n * \n * @example\n * ```tsx\n * const items = [\n * { id: 1, label: 'Apple' },\n * { id: 2, label: 'Banana' },\n * { id: 3, label: 'Orange' },\n * ];\n * \n * <ComboBox\n * label=\"Fruit\"\n * items={items}\n * selectedKey={selectedId}\n * onSelectionChange={setSelectedId}\n * placeholder=\"Select a fruit\"\n * />\n * ```\n */\nexport function ComboBox({\n label,\n description,\n errorMessage,\n isRequired = false,\n isDisabled = false,\n selectedKey,\n onSelectionChange,\n items,\n placeholder,\n 'aria-label': ariaLabel,\n inputValue: controlledInputValue,\n onInputChange: controlledOnInputChange,\n}: ComboBoxProps) {\n const [internalInputValue, setInternalInputValue] = useState('');\n\n // Only sync input value with selected item when NOT using controlled input\n // (for async search, the parent controls inputValue)\n useEffect(() => {\n if (controlledInputValue !== undefined) {\n // When using controlled input (search mode), don't sync with selection\n return;\n }\n\n if (selectedKey) {\n const selected = items.find(item => item.id === selectedKey);\n if (selected) {\n setInternalInputValue(selected.label);\n }\n } else {\n setInternalInputValue('');\n }\n }, [selectedKey, items, controlledInputValue]);\n\n // Use controlled inputValue if provided, otherwise use internal state\n const effectiveInputValue = controlledInputValue !== undefined\n ? controlledInputValue\n : internalInputValue;\n\n const effectiveOnInputChange = controlledOnInputChange !== undefined\n ? controlledOnInputChange\n : setInternalInputValue;\n\n return (\n <AriaComboBox\n className={comboBoxContainerStyles}\n inputValue={effectiveInputValue}\n onInputChange={effectiveOnInputChange}\n selectedKey={selectedKey}\n onSelectionChange={onSelectionChange}\n isDisabled={isDisabled}\n isRequired={isRequired}\n isInvalid={!!errorMessage}\n aria-label={ariaLabel || label}\n >\n {label && <Label className={labelStyles}>{label}</Label>}\n {/* TODO - The focusRing is missing. */}\n <div className={inputWrapperStyles}>\n <Input className={inputStyles} placeholder={placeholder} />\n <Button className={buttonStyles}>\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <ChevronDown styles={iconStyle({ color: '--plum-800' })} />\n </Button>\n </div>\n {description && !errorMessage && (\n <Text slot=\"description\" className={descriptionStyles}>\n {description}\n </Text>\n )}\n {errorMessage && <FieldError className={errorStyles}>{errorMessage}</FieldError>}\n <Popover className={popoverStyles}>\n <ListBox className={listBoxStyles} items={items}>\n {(item) => (\n <ListBoxItem\n key={item.id}\n id={item.id}\n textValue={item.textValue || item.label}\n className={listBoxItemStyles}\n >\n {item.label}\n </ListBoxItem>\n )}\n </ListBox>\n </Popover>\n </AriaComboBox>\n );\n}\n","import { useEffect, useState } from 'react';\nimport { Button } from './Button';\nimport { Link } from './Link';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport type { IntlShape } from 'react-intl';\n\nconst bannerStyles = style({\n position: 'fixed',\n bottom: 0,\n left: 0,\n right: 0,\n backgroundColor: '--plum-900',\n color: 'white',\n padding: 16,\n paddingX: {\n default: 16,\n md: 24,\n },\n boxShadow: 'elevated',\n zIndex: 1000,\n display: 'flex',\n flexDirection: {\n default: 'column',\n md: 'row',\n },\n alignItems: {\n default: 'stretch',\n md: 'center',\n },\n justifyContent: 'space-between',\n gap: 16,\n});\n\nconst messageStyles = style({\n font: 'body',\n color: '--yogurt-50',\n margin: 0,\n flexGrow: 1,\n});\n\nconst actionsStyles = style({\n display: 'flex',\n gap: 8,\n alignItems: 'center',\n flexShrink: 0,\n flexWrap: 'wrap',\n});\n\nconst linkStyles = style({\n color: '--yogurt-200',\n textDecorationLine: {\n default: 'underline',\n isHovered: 'none',\n },\n});\n\nexport type ConsentLevel = 'accepted' | 'essential' | 'rejected';\n\nexport interface CookieConsentProps {\n /**\n * Storage key prefix for localStorage items\n * @example 'worldferments' or 'worldferments-admin'\n */\n storagePrefix: string;\n\n /**\n * Internationalization formatter\n */\n intl: IntlShape;\n\n /**\n * Privacy policy URL\n * @default '/privacy-policy'\n */\n privacyPolicyUrl?: string;\n\n /**\n * Additional essential localStorage keys to preserve with \"Essential Only\" consent.\n * Note: Only cookie-consent and {storagePrefix}-theme are preserved by default.\n * Use this for strictly necessary keys only (authentication, security, etc.)\n * @example ['session-token', 'csrf-token']\n */\n additionalEssentialKeys?: string[];\n\n /**\n * Callback when consent level changes\n */\n onConsentChange?: (level: ConsentLevel) => void;\n}\n\n/**\n * Cookie consent banner component\n * \n * Displays a banner at the bottom of the page informing users about cookie usage\n * with three consent levels:\n * \n * - **Accept All**: Allows all cookies including analytics (when implemented)\n * - **Essential Only**: Only theme preference + consent (removes analytics, Vercel cookies, etc.)\n * - **Reject All**: Removes ALL cookies and storage except the consent preference itself\n * \n * Required for GDPR/CCPA compliance. Stores user preference in localStorage.\n * \n * **What gets removed:**\n * - Essential Only: Analytics, Vercel platform cookies, non-essential storage\n * - Reject All: Everything except `cookie-consent` preference\n * \n * **What gets preserved:**\n * - Essential Only: `cookie-consent`, `{storagePrefix}-theme`, `additionalEssentialKeys`\n * - Reject All: Only `cookie-consent`\n * \n * @example\n * // Consumer UI\n * <CookieConsent \n * storagePrefix=\"worldferments\" \n * intl={intl}\n * />\n * \n * // Admin UI\n * <CookieConsent \n * storagePrefix=\"worldferments-admin\" \n * intl={intl}\n * privacyPolicyUrl=\"/admin/privacy-policy\"\n * />\n */\nexport function CookieConsent({\n storagePrefix,\n intl,\n privacyPolicyUrl = '/privacy-policy',\n additionalEssentialKeys = [],\n onConsentChange,\n}: CookieConsentProps) {\n const [isVisible, setIsVisible] = useState(false);\n const consentKey = 'cookie-consent';\n\n useEffect(() => {\n // Check if user has already made a choice\n const consent = localStorage.getItem(consentKey);\n if (consent === null) {\n setIsVisible(true);\n }\n }, []);\n\n const clearNonEssentialStorage = () => {\n // Essential keys that should never be removed (strictly necessary for site operation)\n const essentialKeys = [\n consentKey,\n `${storagePrefix}-theme`, // Theme preference is considered essential UX\n ...additionalEssentialKeys,\n ];\n\n // Remove all localStorage items except essential ones\n const keys = Object.keys(localStorage);\n keys.forEach(key => {\n const isEssential = essentialKeys.includes(key);\n if (!isEssential) {\n localStorage.removeItem(key);\n }\n });\n\n // Clear non-essential cookies (analytics, tracking, Vercel platform cookies)\n if (typeof document !== 'undefined') {\n document.cookie.split(';').forEach(cookie => {\n const name = cookie.split('=')[0].trim();\n // Clear analytics cookies\n if (name.startsWith('_ga') || name.startsWith('_gid')) {\n document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;\n }\n // Clear Vercel platform cookies (not essential for end users)\n if (name.startsWith('__vercel') || name.startsWith('_vercel') || name.startsWith('__prerender')) {\n document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;\n }\n });\n }\n };\n\n const clearAllStorage = () => {\n const consentValue = localStorage.getItem(consentKey);\n localStorage.clear();\n\n // Restore only the consent preference itself\n if (consentValue) {\n localStorage.setItem(consentKey, consentValue);\n }\n\n // Clear ALL cookies including Vercel platform cookies\n if (typeof document !== 'undefined') {\n document.cookie.split(';').forEach(cookie => {\n const name = cookie.split('=')[0].trim();\n // Remove all cookies - user has rejected everything\n document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;\n });\n }\n };\n\n const handleAccept = () => {\n localStorage.setItem(consentKey, 'accepted');\n setIsVisible(false);\n onConsentChange?.('accepted');\n // When analytics is implemented, initialize here\n // Example: initializeGoogleAnalytics();\n };\n\n const handleEssential = () => {\n localStorage.setItem(consentKey, 'essential');\n setIsVisible(false);\n clearNonEssentialStorage();\n onConsentChange?.('essential');\n // Ensure analytics is not initialized\n };\n\n const handleReject = () => {\n localStorage.setItem(consentKey, 'rejected');\n clearAllStorage();\n setIsVisible(false);\n onConsentChange?.('rejected');\n // Ensure analytics is not initialized\n };\n\n if (!isVisible) return null;\n\n return (\n <div className={bannerStyles}>\n <p className={messageStyles}>\n {intl.formatMessage({ id: 'cookieConsent.message' })}{' '}\n <Link href={privacyPolicyUrl} className={linkStyles}>\n {intl.formatMessage({ id: 'cookieConsent.learnMore' })}\n </Link>\n </p>\n <div className={actionsStyles}>\n <Button onPress={handleReject} variant=\"danger\">\n {intl.formatMessage({ id: 'cookieConsent.reject' })}\n </Button>\n <Button onPress={handleEssential} variant=\"secondary\">\n {intl.formatMessage({ id: 'cookieConsent.essential' })}\n </Button>\n <Button onPress={handleAccept} variant=\"primary\">\n {intl.formatMessage({ id: 'cookieConsent.accept' })}\n </Button>\n </div>\n </div>\n );\n}\n\n/**\n * Get the current consent level\n */\nexport function getConsentLevel(): ConsentLevel | null {\n if (typeof window === 'undefined') return null;\n\n const consent = localStorage.getItem('cookie-consent');\n if (consent === 'accepted' || consent === 'essential' || consent === 'rejected') {\n return consent;\n }\n return null;\n}\n\n/**\n * Check if analytics tracking is allowed\n * Analytics should only be enabled if user has accepted all cookies\n */\nexport function canUseAnalytics(): boolean {\n return getConsentLevel() === 'accepted';\n}\n\n/**\n * Check if essential features are allowed\n * Essential features include: authentication, theme preference, language\n */\nexport function canUseEssentials(): boolean {\n const level = getConsentLevel();\n return level === 'accepted' || level === 'essential';\n}\n\n/**\n * Check if any consent has been given\n */\nexport function hasConsented(): boolean {\n return getConsentLevel() !== null;\n}\n","import { Link as AriaLink, LinkProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { ReactNode } from 'react';\n\nconst linkStyles = style({\n ...focusRing(),\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isPressed: '--plum-900',\n colorScheme: {\n light: {\n default: '--yogurt-0',\n isHovered: '--yogurt-100',\n isPressed: '--yogurt-200'\n },\n dark: {\n default: '--plum-900',\n isHovered: '--plum-800',\n isPressed: '--plum-800'\n }\n }\n },\n textDecoration: {\n default: 'underline',\n isQuiet: 'none'\n },\n cursor: 'pointer',\n backgroundColor: {\n default: 'transparent',\n isHovered: 'transparent'\n }\n});\n\ninterface CustomLinkProps extends Omit<LinkProps, 'className'> {\n children: ReactNode;\n isExternal?: boolean;\n isQuiet?: boolean;\n colorScheme?: 'default' | 'light' | 'dark';\n className?: string | ((renderProps: any) => string);\n}\n\n/**\n * Link Component\n * \n * Wraps React Aria Components Link with S2 styling and proper a11y.\n * Automatically handles focus states and keyboard navigation.\n * External links automatically get target=\"_blank\" and rel=\"noopener noreferrer\".\n *\n * @example\n * <Link href=\"/ferments\">Browse ferments</Link>\n * \n * @example\n * <Link href=\"https://example.com\" isExternal>External link</Link>\n *\n * @example\n * // For light text on dark backgrounds (like plum)\n * <Link href=\"/\" colorScheme=\"light\">Light yogurt link</Link>\n *\n * @example\n * // For dark text on light backgrounds\n * <Link href=\"/\" colorScheme=\"dark\">Dark plum link</Link>\n *\n * @example\n * <Link href=\"/\" className={customStyles}>Custom styled link</Link>\n */\nexport function Link({ children, href, isExternal, isQuiet = false, colorScheme = 'default', className, ...props }: CustomLinkProps) {\n // Automatically detect external links if not specified\n const isExternalLink = isExternal ?? (href?.toString().startsWith('http://') || href?.toString().startsWith('https://'));\n\n const externalProps = isExternalLink ? {\n target: '_blank',\n rel: 'noopener noreferrer'\n } : {};\n\n return (\n <AriaLink\n {...props}\n {...externalProps}\n href={href}\n className={className || linkStyles({ isQuiet, colorScheme })}\n >\n {children}\n </AriaLink>\n );\n}\n","import {\n Button as AriaButton,\n Disclosure,\n DisclosureGroup as AriaDisclosureGroup,\n DisclosurePanel,\n DisclosureStateContext,\n Heading\n} from 'react-aria-components';\nimport ChevronRight from '@react-spectrum/s2/icons/ChevronRight';\nimport type { DisclosureGroupProps } from 'react-aria-components';\nimport { style, iconStyle, focusRing } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useContext } from 'react';\n\nimport './DisclosureGroup.css';\n\n// Disclosure container - very thin grey border, full width button appearance\nconst disclosureContainerStyles = style({\n borderStyle: 'none',\n borderWidth: 0,\n borderColor: 'transparent',\n borderRadius: 'lg',\n overflow: 'hidden',\n width: 'full',\n backgroundColor: '--yogurt-100'\n});\n\n// Disclosure trigger button - blends seamlessly with container\nconst disclosureTriggerStyles = style({\n ...focusRing(),\n font: 'title',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n '--iconPrimary': {\n type: 'color',\n value: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n },\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: 16,\n paddingX: 20,\n width: 'full',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isPressed: '--yogurt-300'\n },\n borderStyle: 'none',\n borderRadius: 'none',\n cursor: 'pointer'\n});\n\nconst disclosureHeaderStyles = style({\n margin: 0,\n});\n\n// Disclosure panel - white/light background, blends with button header\nconst disclosurePanelStyles = style({\n padding: {\n default: 0,\n isExpanded: 20,\n },\n backgroundColor: '--yogurt-50',\n borderTopWidth: 1,\n borderTopStyle: 'solid',\n borderColor: '--yogurt-300'\n});\n\nexport interface DisclosureItem {\n id: string;\n title: string;\n content: React.ReactNode;\n onPress?: () => void;\n}\n\ninterface CustomDisclosureGroupProps extends Omit<DisclosureGroupProps, 'children'> {\n items: DisclosureItem[];\n 'aria-label'?: string;\n}\n\n/**\n * Reusable DisclosureGroup component\n * Displays collapsible sections with consistent styling\n * \n * @example\n * <DisclosureGroup\n * items={[\n * {\n * id: 'section1',\n * title: 'Section 1',\n * content: <div>Content goes here</div>\n * }\n * ]}\n * />\n */\nexport function DisclosureGroup({ items, ...props }: CustomDisclosureGroupProps) {\n return (\n <AriaDisclosureGroup {...props} className={disclosureContainerStyles}>\n {items.map((item) => (\n <Disclosure key={item.id} id={item.id}>\n <DisclosureContents key={`${item.id}-contents`} {...item} />\n </Disclosure>\n ))}\n </AriaDisclosureGroup>\n );\n}\n\nconst DisclosureContents = (item: DisclosureItem) => {\n let { isExpanded } = useContext(DisclosureStateContext)!;\n\n return (\n <>\n <Heading className={disclosureHeaderStyles}>\n <AriaButton\n slot=\"trigger\"\n className={(renderProps) =>\n `disclosure-chevron ${disclosureTriggerStyles(renderProps)}`\n }\n onPress={item.onPress}\n >\n <ChevronRight styles={iconStyle({ size: 'S' })} />\n {item.title}\n </AriaButton>\n </Heading>\n <DisclosurePanel className={disclosurePanelStyles({ isExpanded })}>\n {item.content}\n </DisclosurePanel>\n </>\n );\n}\n","import { Badge } from './Badge';\nimport { getCategoryColors } from '../utils/category-colors';\n\nexport interface FermentTypeBadgeProps {\n category: string;\n}\n\n/**\n * Badge component specifically for displaying ferment category types\n * Maps ferment categories to appropriate color variants\n * \n * @example\n * <FermentTypeBadge category=\"dairy\" />\n */\nexport function FermentTypeBadge({ category }: FermentTypeBadgeProps) {\n const colors = getCategoryColors(category);\n \n return (\n <Badge variant={colors.badge}>\n {category}\n </Badge>\n );\n}\n","/**\n * Category Color Utilities\n * Provides consistent color mapping for ferment categories across Map and List views\n */\nimport type { CategoryColors } from '../types/types';\n\n/**\n * Get colors for a ferment category\n * Returns marker color (for map), badge background, and badge text color\n * \n * @example\n * const colors = getCategoryColors('dairy');\n * // { marker: 'var(--plum-500)', badge: 'plum' }\n */\nexport function getCategoryColors(category: string): CategoryColors {\n const colorMap: Record<string, CategoryColors> = {\n dairy: {\n marker: 'var(--yogurt-500)',\n badge: 'peach',\n },\n vegetable: {\n marker: 'var(--cucumber-500)',\n badge: 'cucumber',\n },\n beverage: {\n marker: 'var(--berry-500)',\n badge: 'berry',\n },\n grain: {\n marker: 'var(--ginger-500)',\n badge: 'ginger',\n },\n legume: {\n marker: 'var(--ginger-600)',\n badge: 'ginger',\n },\n protein: {\n marker: 'var(--berry-500)',\n badge: 'berry',\n },\n fruit: {\n marker: 'var(--peach-500)',\n badge: 'peach',\n },\n condiment: {\n marker: 'var(--plum-500)',\n badge: 'plum',\n },\n other: {\n marker: 'var(--yogurt-500)',\n badge: 'peach',\n },\n };\n\n // Default to yogurt colors for unknown categories\n return colorMap[category.toLowerCase()] || {\n marker: 'var(--yogurt-500)',\n badge: 'yogurt'\n };\n}\n","import {\n ToggleButton,\n ToggleButtonGroup,\n SelectionIndicator\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\nexport interface FilterOption {\n value: string;\n label: string;\n}\n\nexport interface FilterTabsProps {\n value: string;\n onChange: (value: string) => void;\n options: FilterOption[];\n ariaLabel?: string;\n}\n\nconst filterContainerStyles = style({\n display: 'flex',\n gap: 0,\n padding: 4,\n backgroundColor: '--yogurt-200',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400'\n});\n\nconst filterButtonStyles = style({\n ...focusRing(),\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n paddingX: 16,\n paddingY: 8,\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n zIndex: 1\n});\n\nconst selectionIndicatorStyles = style({\n position: 'absolute',\n inset: 0,\n borderRadius: 'default',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: 'gray-300',\n boxShadow: 'elevated',\n zIndex: -1,\n backgroundColor: {\n default: 'layer-1',\n isHovered: '--yogurt-300',\n isPressed: '--yogurt-400'\n }\n});\n\n/**\n * FilterTabs component for status filtering\n * Uses React Aria ToggleButtonGroup with SelectionIndicator\n * \n * @example\n * <FilterTabs\n * value={statusFilter}\n * onChange={setStatusFilter}\n * options={[\n * { value: 'all', label: 'All' },\n * { value: 'running', label: 'Running' }\n * ]}\n * ariaLabel=\"Filter by status\"\n * />\n */\nexport function FilterTabs({ value, onChange, options, ariaLabel }: FilterTabsProps) {\n const intl = useIntl();\n\n return (\n <ToggleButtonGroup\n selectionMode=\"single\"\n selectedKeys={new Set([value])}\n disallowEmptySelection\n onSelectionChange={(keys) => {\n const selected = Array.from(keys)[0] as string;\n onChange(selected);\n }}\n aria-label={ariaLabel || intl.formatMessage({ id: 'filterTabs.ariaLabel' })}\n className={filterContainerStyles}\n >\n {options.map((option) => (\n <ToggleButton\n key={option.value}\n id={option.value}\n className={filterButtonStyles}\n >\n {({ isSelected }) => (\n <>\n {option.label}\n {isSelected && <SelectionIndicator className={selectionIndicatorStyles} />}\n </>\n )}\n </ToggleButton>\n ))}\n </ToggleButtonGroup>\n );\n}\n\n","import {\n TextField as AriaTextField,\n Label,\n Text,\n FieldError,\n} from 'react-aria-components';\nimport { style, } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport {\n textFieldDescriptionStyles as labeledValueDescriptionStyles,\n textFieldErrorStyles as labeledValueErrorStyles,\n} from './TextField';\n\nexport interface LabeledValueProps {\n label: string;\n error?: string;\n description?: string;\n isBoldLabel?: boolean;\n labelPosition?: 'top' | 'side';\n children: React.ReactNode;\n}\n\nconst labeledValueStyles = style({\n font: 'body',\n color: '--plum-800',\n display: 'flex',\n flexDirection: {\n default: 'column',\n labelPosition: {\n side: 'row',\n },\n },\n gap: 8,\n alignItems: {\n labelPosition: {\n side: 'center',\n },\n },\n});\n\nconst labeledValueLabelStyles = style({\n font: {\n isBoldLabel: 'title',\n },\n color: '--plum-800',\n});\n\n\nexport function LabeledValue({ label, children, error, description, isBoldLabel = false, labelPosition = 'top', ...props }: LabeledValueProps) {\n return (\n <AriaTextField className={labeledValueStyles({ labelPosition })} isInvalid={!!error} {...props}>\n <Label className={labeledValueLabelStyles({ isBoldLabel })}>{label}{labelPosition === 'side' && ':'}</Label>\n <span>{children}</span>\n {description && !error && (\n <Text slot=\"description\" className={labeledValueDescriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={labeledValueErrorStyles}>{error}</FieldError>\n </AriaTextField>\n );\n}\n","import { Link } from './Link';\nimport type { LinkProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface LinkButtonProps extends Omit<LinkProps, 'className'> {\n variant?: 'primary' | 'secondary' | 'danger' | 'navigation' | 'inline';\n size?: 'S' | 'M' | 'L';\n children: React.ReactNode;\n}\n\nconst linkButtonStyles = style({\n ...focusRing(),\n font: {\n default: 'body',\n size: {\n S: 'body-sm',\n L: 'body-lg',\n },\n },\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: {\n default: 8,\n size: {\n S: 4,\n },\n },\n paddingY: {\n default: 12,\n size: {\n S: 8,\n L: 16,\n },\n variant: {\n navigation: 4,\n inline: 0\n }\n },\n paddingStart: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n inline: 0\n }\n },\n paddingEnd: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n inline: 0\n }\n },\n width: {\n variant: {\n navigation: 'fit'\n }\n },\n color: {\n variant: {\n primary: {\n default: '--plum-900',\n },\n secondary: {\n default: '--plum-800',\n },\n danger: {\n default: '--yogurt-0',\n },\n navigation: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isPressed: '--plum-900'\n },\n inline: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isPressed: '--plum-900'\n }\n },\n forcedColors: 'ButtonText',\n isDisabled: {\n default: 'disabled',\n forcedColors: 'GrayText',\n },\n },\n backgroundColor: {\n default: '--plum-300',\n variant: {\n primary: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisible: '--plum-400',\n isPressed: '--plum-400',\n },\n secondary: {\n default: '--yogurt-200',\n isHovered: '--yogurt-300',\n isFocusVisible: '--yogurt-300',\n isPressed: '--yogurt-400',\n },\n navigation: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n isPressed: '--yogurt-300',\n },\n danger: {\n default: '--berry-700',\n isHovered: '--berry-800',\n isFocusVisible: '--berry-800',\n isPressed: '--berry-900',\n },\n inline: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n isPressed: '--yogurt-300',\n }\n },\n forcedColors: 'ButtonFace',\n isDisabled: {\n default: 'gray-100',\n isDisabled: 'disabled',\n },\n },\n borderRadius: 'lg',\n borderStyle: {\n default: 'none',\n forcedColors: 'solid'\n },\n borderColor: {\n forcedColors: 'ButtonBorder'\n },\n borderWidth: {\n forcedColors: 1\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n },\n textDecorationLine: {\n default: 'none',\n variant: {\n navigation: 'underline',\n inline: 'underline'\n }\n },\n});\n\nexport function LinkButton({\n variant = 'primary',\n size = 'M',\n children,\n ...props\n}: LinkButtonProps) {\n return (\n <Link\n {...props}\n className={(renderProps) => linkButtonStyles({ ...renderProps, variant, size })}\n >\n {children}\n </Link>\n );\n}\n","import {\n MenuTrigger,\n Menu as AriaMenu,\n MenuItem as AriaMenuItem,\n Popover,\n Separator\n} from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { ReactNode } from 'react';\n\nconst menuStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n padding: 4,\n minWidth: 200,\n outline: 'none'\n});\n\nconst menuItemStyles = style({\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n paddingX: 12,\n paddingY: 8,\n borderRadius: 'lg',\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocused: '--plum-900',\n isPressed: '--plum-900'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocused: '--yogurt-200'\n },\n cursor: 'pointer',\n outline: 'none'\n});\n\nconst dividerStyles = style({\n height: 1,\n backgroundColor: '--yogurt-400',\n marginY: 4\n});\n\nexport interface MenuItem {\n id: string;\n label: string;\n icon?: ReactNode;\n onAction: () => void;\n}\n\nexport interface MenuProps {\n trigger: ReactNode;\n items: MenuItem[];\n placement?: 'bottom' | 'bottom start' | 'bottom end' | 'top' | 'top start' | 'top end';\n 'aria-label'?: string;\n}\n\n/**\n * Menu component styled to match Select component\n * Uses yogurt colors for consistency with Select\n */\nexport function Menu({ trigger, items, placement = 'bottom end', 'aria-label': ariaLabel }: MenuProps) {\n return (\n <MenuTrigger>\n {trigger}\n <Popover placement={placement}>\n <AriaMenu className={menuStyles} aria-label={ariaLabel}>\n {items.map((item, index) =>\n item.id === 'divider' ? (\n <Separator key={`divider-${index}`} className={dividerStyles} />\n ) : (\n <AriaMenuItem\n key={item.id}\n id={item.id}\n onAction={item.onAction}\n className={menuItemStyles}\n >\n {item.icon}\n {item.label}\n </AriaMenuItem>\n )\n )}\n </AriaMenu>\n </Popover>\n </MenuTrigger>\n );\n}\n","import { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\nexport interface PasswordValidation {\n minLength: boolean;\n hasLetter: boolean;\n hasNumber: boolean;\n hasSpecial: boolean;\n}\n\nexport interface PasswordStrengthProps {\n validation: PasswordValidation;\n}\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8\n});\n\nconst requirementStyles = style({\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n font: 'body-sm'\n});\n\nconst indicatorStyles = style({\n width: 16,\n height: 16,\n borderRadius: 'full',\n flexShrink: 0,\n backgroundColor: {\n default: '--gray-300',\n isValid: '--cucumber-500'\n },\n color: {\n default: 'inherit',\n isValid: '--cucumber-700'\n }\n});\n\n/**\n * Visual password strength indicator showing validation requirements\n * Displays checkmarks (green) for met requirements, circles (gray) for unmet\n */\nexport function PasswordStrength({ validation }: PasswordStrengthProps) {\n const intl = useIntl();\n\n const requirements = [\n {\n key: 'minLength',\n met: validation.minLength,\n label: intl.formatMessage({ id: 'validation.password.minLength' })\n },\n {\n key: 'hasLetter',\n met: validation.hasLetter,\n label: intl.formatMessage({ id: 'validation.password.letter' })\n },\n {\n key: 'hasNumber',\n met: validation.hasNumber,\n label: intl.formatMessage({ id: 'validation.password.number' })\n },\n {\n key: 'hasSpecial',\n met: validation.hasSpecial,\n label: intl.formatMessage({ id: 'validation.password.special' })\n }\n ];\n\n console.log('PasswordStrength requirements:', requirements, intl, intl.formatMessage({ id: 'validation.password.special' }));\n\n return (\n <div className={containerStyles}>\n {requirements.map(req => (\n <div key={req.key} className={requirementStyles}>\n <div\n className={indicatorStyles({ isValid: req.met })}\n />\n <span>{req.label}</span>\n </div>\n ))}\n </div>\n );\n}\n","import { Dialog, Heading, Popover as RACPopover } from 'react-aria-components';\nimport type { ReactNode } from 'react';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface PopoverProps {\n title: string;\n children: ReactNode;\n}\n\nconst popoverStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-300',\n padding: 24,\n maxWidth: 400,\n boxShadow: 'elevated'\n});\n\nconst dialogStyles = style({\n maxHeight: '33vh',\n overflow: 'auto'\n});\n\nconst headingStyles = style({\n font: 'heading',\n margin: 0,\n marginBottom: 16,\n color: '--plum-800'\n});\n\n/**\n * Popover component with list of items\n * \n * Uses React Aria Components Dialog, Popover, Dialog, and Heading\n * \n * @example\n * <Popover\n * title=\"Countries Without Ferments\"\n * items={countries}\n * />\n */\nexport function Popover({ title, children }: PopoverProps) {\n return (\n <RACPopover className={popoverStyles}>\n <Dialog className={dialogStyles}>\n <Heading slot=\"title\" className={headingStyles}>\n {title}\n </Heading>\n {children}\n </Dialog>\n </RACPopover>\n );\n}\n","import { Radio as AriaRadio, RadioGroup as AriaRadioGroup, Text, FieldError, type RadioGroupProps as AriaRadioGroupProps, type RadioProps as AriaRadioProps } from 'react-aria-components';\nimport { style, focusRing } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useId } from 'react';\n\nexport interface RadioGroupProps extends Omit<AriaRadioGroupProps, 'children'> {\n label: string;\n options: Array<{ id: string; label: string; value: string }>;\n error?: string;\n description?: string;\n}\n\nexport interface RadioProps extends AriaRadioProps {\n children: React.ReactNode;\n}\n\nconst radioGroupStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n});\n\nconst labelStyles = style({\n font: 'body',\n fontWeight: 'medium',\n color: '--plum-800',\n marginBottom: 8,\n});\n\nconst radioContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 12,\n});\n\nconst radioStyles = style({\n ...focusRing(),\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n font: 'body',\n color: {\n default: '--plum-800',\n isSelected: '--plum-900',\n isHovered: '--plum-900',\n isDisabled: 'disabled',\n },\n cursor: 'pointer',\n padding: 8,\n borderRadius: 'lg',\n backgroundColor: {\n default: 'transparent',\n isHovered: '--plum-50',\n },\n});\n\nconst radioButtonStyles = style({\n width: 20,\n height: 20,\n borderRadius: 'full',\n borderWidth: 2,\n borderStyle: 'solid',\n borderColor: {\n default: '--plum-400',\n isSelected: '--plum-500',\n isHovered: '--plum-300',\n isDisabled: 'disabled',\n },\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n backgroundColor: {\n default: 'layer-1',\n isDisabled: 'gray-100',\n },\n});\n\nconst radioInnerDotStyles = style({\n width: 10,\n height: 10,\n borderRadius: 'full',\n backgroundColor: '--plum-500',\n opacity: {\n default: 0,\n isSelected: 1,\n },\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nconst descriptionStyles = style({\n font: 'body-sm',\n color: '--plum-800',\n});\n\nexport function Radio({ children, ...props }: RadioProps) {\n return (\n <AriaRadio {...props} className={renderProps => radioStyles(renderProps)}>\n {renderProps => (\n <>\n <div className={radioButtonStyles(renderProps)}>\n <div className={radioInnerDotStyles(renderProps)} />\n </div>\n <span>{children}</span>\n </>\n )}\n </AriaRadio>\n );\n}\n\nexport function RadioGroup({ label, options, error, description, ...props }: RadioGroupProps) {\n let ariaLabelId = useId();\n\n return (\n <AriaRadioGroup {...props} isInvalid={!!error} className={radioGroupStyles} aria-labelledby={ariaLabelId}>\n <span id={ariaLabelId} className={labelStyles}>{label}</span>\n <div className={radioContainerStyles}>\n {options.map(option => (\n <Radio key={option.id} value={option.value}>\n {option.label}\n </Radio>\n ))}\n </div>\n {description && !error && (\n <Text slot=\"description\" className={descriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={errorStyles}>{error}</FieldError>\n </AriaRadioGroup>\n );\n}\n","import { SearchField as AriaSearchField, Button, Input, Label, Text, FieldError, } from 'react-aria-components';\nimport Close from '@react-spectrum/s2/icons/Close';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { raw } from '../utils/styleMacro' with { type: 'macro' };\nimport Search from '@react-spectrum/s2/icons/Search';\nimport { useIntl } from '../lib/i18n-context';\n\n\nconst searchFieldContainerStyles = style({\n flex: {\n default: 'none',\n md: 1,\n },\n});\n\nconst searchFieldStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nconst searchInputContainerStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst searchInputStyles = style({\n ...focusRing(),\n width: 'full',\n padding: 8,\n paddingStart: 40,\n font: 'body',\n color: {\n default: '--plum-800',\n '::placeholder': '--plum-400'\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n },\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-800',\n marginBottom: 4,\n});\n\nconst searchIconStyles = {\n position: 'absolute' as const,\n left: 12,\n top: '50%',\n transform: 'translateY(-50%)',\n pointerEvents: 'none' as const,\n color: '--yogurt-600',\n};\n\nconst clearButtonStyles = style({\n ...focusRing(),\n position: 'absolute',\n right: 8,\n top: '50%',\n transform: 'translateY(-50%)',\n padding: 4,\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n color: {\n default: '--yogurt-600',\n isHovered: '--yogurt-700',\n isPressed: '--yogurt-800',\n },\n});\n\nconst descriptionStyles = style({\n font: 'body-sm',\n color: '--plum-800',\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nexport interface SearchFieldProps {\n value: string;\n onChange: (value: string) => void;\n label: string;\n placeholder: string;\n ariaLabel: string;\n description?: string;\n error?: string;\n}\n\n/**\n * Reusable SearchField component using React Aria Components\n * Styled with S2 design tokens and includes search icon\n * \n * @example\n * <SearchField\n * value={searchQuery}\n * onChange={setSearchTerm}\n * label=\"Search\"\n * placeholder=\"Search ferments...\"\n * ariaLabel=\"Search ferments by name or description\"\n * />\n */\nexport function SearchField({\n value,\n onChange,\n label,\n placeholder,\n ariaLabel,\n description,\n error,\n}: SearchFieldProps) {\n const intl = useIntl();\n\n return (\n <div className={searchFieldContainerStyles}>\n <AriaSearchField\n value={value}\n onChange={onChange}\n className={searchFieldStyles}\n isInvalid={!!error}\n aria-label={ariaLabel}\n >\n {({ isEmpty }) => (\n <>\n <Label className={labelStyles}>{label}</Label>\n <div className={searchInputContainerStyles}>\n <div style={searchIconStyles}>\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <Search styles={iconStyle({ color: '--plum-800' })} />\n </div>\n <Input\n className={(renderProps) => searchInputStyles(renderProps) + ' ' + raw('&::-webkit-search-cancel-button { display: none }', 'custom_layer')}\n placeholder={placeholder}\n />\n {!isEmpty && <Button className={clearButtonStyles} aria-label={intl.formatMessage({ id: 'ferments.search.clear' })}>\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <Close styles={iconStyle({ size: 'S', color: '--plum-800' })} />\n </Button>}\n </div>\n {description && !error && (\n <Text slot=\"description\" className={descriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={errorStyles}>{error}</FieldError>\n </>\n )}\n </AriaSearchField>\n </div>\n );\n}\n","import { useRouter } from 'next/navigation';\nimport {\n ToggleButton,\n ToggleButtonGroup,\n SelectionIndicator\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\nimport GlobeGrid from '@react-spectrum/s2/icons/GlobeGrid';\nimport ListIcon from '@react-spectrum/s2/icons/ViewList';\n\nconst segmentedControlStyles = style({\n display: 'flex',\n gap: 0,\n padding: 4,\n backgroundColor: '--yogurt-200',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n});\n\nconst toggleButtonStyles = style({\n ...focusRing(),\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 8,\n paddingX: 16,\n paddingY: 8,\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n zIndex: 1,\n '--iconPrimary': {\n type: 'color',\n value: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n }\n});\n\nconst selectionIndicatorStyles = style({\n position: 'absolute',\n inset: 0,\n borderRadius: 'default',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-300',\n boxShadow: 'elevated',\n zIndex: -1,\n color: {\n isDisabled: 'disabled',\n forcedColors: 'ButtonText'\n },\n backgroundColor: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisible: '--plum-400',\n isPressed: '--plum-400',\n isDisabled: 'gray-100',\n forcedColors: 'ButtonFace'\n },\n});\n\ninterface SegmentedControlProps {\n value: 'map' | 'list';\n}\n\n/**\n * SegmentedControl for Map/List view toggle\n * Uses RAC ToggleButtonGroup for accessible segmented control\n */\nexport function SegmentedControl({ value }: SegmentedControlProps) {\n const intl = useIntl();\n const router = useRouter();\n\n const handleSelectionChange = (newValue: 'map' | 'list') => {\n if (newValue === 'map') {\n router.push('/');\n } else {\n router.push('/ferments');\n }\n };\n\n return (\n <ToggleButtonGroup\n selectionMode=\"single\"\n selectedKeys={new Set([value])}\n disallowEmptySelection\n onSelectionChange={(keys) => {\n const selected = Array.from(keys)[0] as 'map' | 'list';\n handleSelectionChange(selected);\n }}\n aria-label={intl.formatMessage({ id: 'nav.viewToggle.ariaLabel' })}\n className={segmentedControlStyles}\n >\n <ToggleButton\n id=\"map\"\n className={(renderProps) => toggleButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'nav.map' })}\n >\n {(renderProps) => (\n <>\n <SelectionIndicator className={selectionIndicatorStyles(renderProps)} />\n <GlobeGrid aria-hidden=\"true\" />\n <span>{intl.formatMessage({ id: 'nav.map' })}</span>\n </>\n )}\n </ToggleButton>\n\n <ToggleButton\n id=\"list\"\n className={(renderProps) => toggleButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'nav.list' })}\n >\n {(renderProps) => (\n <>\n <SelectionIndicator className={selectionIndicatorStyles(renderProps)} />\n <ListIcon aria-hidden=\"true\" />\n <span>{intl.formatMessage({ id: 'nav.list' })}</span>\n </>\n )}\n </ToggleButton>\n </ToggleButtonGroup>\n );\n}\n","import {\n Tab,\n TabList,\n TabPanel,\n Tabs,\n type TabListProps,\n type TabProps,\n type TabsProps\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport type { ReactNode } from 'react';\n\nconst tabListStyles = style({\n display: 'flex',\n gap: 0,\n padding: 4,\n backgroundColor: '--yogurt-200',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n marginBottom: 24\n});\n\nconst tabStyles = style({\n ...focusRing(),\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n paddingX: 16,\n paddingY: 8,\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n zIndex: 1\n});\n\nconst selectedIndicatorStyles = style({\n position: 'absolute',\n inset: 0,\n borderRadius: 'default',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: 'gray-300',\n boxShadow: 'elevated',\n zIndex: -1,\n backgroundColor: {\n default: 'layer-1',\n isHovered: '--yogurt-300',\n isPressed: '--yogurt-400'\n }\n});\n\nexport interface SegmentedTabsProps extends Omit<TabsProps, 'children'> {\n children: ReactNode;\n ariaLabel?: string;\n}\n\n/**\n * SegmentedTabs - Tabs with segmented control styling\n * \n * Usage:\n * ```tsx\n * <SegmentedTabs selectedKey={tab} onSelectionChange={setTab}>\n * <TabList aria-label=\"Options\">\n * <Tab id=\"one\">One</Tab>\n * <Tab id=\"two\">Two</Tab>\n * </TabList>\n * <TabPanel id=\"one\">Content 1</TabPanel>\n * <TabPanel id=\"two\">Content 2</TabPanel>\n * </SegmentedTabs>\n * ```\n */\nexport function SegmentedTabs({ children, ariaLabel, ...props }: SegmentedTabsProps) {\n return (\n <Tabs {...props}>\n {children}\n </Tabs>\n );\n}\n\nexport function SegmentedTabList({ children, ...props }: TabListProps<object>) {\n return (\n <TabList {...props} className={tabListStyles}>\n {children}\n </TabList>\n );\n}\n\nexport function SegmentedTab({ children, ...props }: TabProps) {\n return (\n <Tab {...props} className={tabStyles}>\n {({ isSelected }) => (\n <>\n {isSelected && <div className={selectedIndicatorStyles({ isSelected })} />}\n {children}\n </>\n )}\n </Tab>\n );\n}\n\n// Re-export TabPanel for convenience\nexport { TabPanel };\n","import {\n Select as AriaSelect,\n SelectProps,\n Label,\n Button,\n SelectValue,\n Popover,\n ListBox,\n ListBoxItem,\n Text,\n FieldError\n} from 'react-aria-components';\nimport type { SelectOption } from '../utils/select-options';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport ChevronDown from '@react-spectrum/s2/icons/ChevronDown';\n\nconst selectContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n minWidth: 200\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-800',\n});\n\nconst descriptionStyles = style({\n font: 'body-sm',\n color: '--plum-800',\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nconst buttonStyles = style({\n ...focusRing(),\n paddingX: 12,\n paddingY: 8,\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n isPressed: '--yogurt-300'\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocused: '--plum-900',\n isPressed: '--plum-900',\n '::placeholder': '--plum-400'\n },\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8,\n minWidth: 200\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 300,\n overflow: 'auto',\n isolation: 'isolate',\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none'\n});\n\nconst listBoxItemStyles = style({\n font: 'body',\n paddingX: 8,\n paddingY: 4,\n borderRadius: 'sm',\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isSelected: '--plum-200',\n isFocused: '--yogurt-200',\n isDisabled: 'gray-100'\n },\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocused: '--plum-900',\n isPressed: '--plum-900',\n isSelected: '--plum-900',\n isDisabled: 'disabled'\n },\n outline: 'none'\n});\n\ninterface CustomSelectProps extends Omit<SelectProps<SelectOption<string | null>>, 'children'> {\n label: string;\n options: SelectOption<string | null>[];\n placeholder?: string;\n description?: string;\n error?: string;\n}\n\n/**\n * Reusable Select component following RAC pattern\n * Label is inside the Select wrapper for proper a11y\n */\nexport function Select({ label, options, placeholder, description, error, ...props }: CustomSelectProps) {\n return (\n <AriaSelect {...props} isInvalid={!!error} className={selectContainerStyles}>\n <Label className={labelStyles}>{label}</Label>\n <Button className={buttonStyles}>\n <SelectValue />\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <ChevronDown aria-hidden=\"true\" styles={iconStyle({ color: '--plum-800' })} />\n </Button>\n {description && !error && (\n <Text slot=\"description\" className={descriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={errorStyles}>{error}</FieldError>\n <Popover className={popoverStyles}>\n <ListBox className={listBoxStyles} items={options}>\n {(item) => (\n <ListBoxItem\n key={item.id}\n id={item.id}\n textValue={item.label}\n isDisabled={item.isDisabled}\n className={listBoxItemStyles}\n >\n {item.label}\n </ListBoxItem>\n )}\n </ListBox>\n </Popover>\n </AriaSelect>\n );\n}\n","import { Badge } from './Badge';\nimport { getSourceCategoryColors } from '../utils/source-type-colors';\nimport type { SourceCategory } from '../types/types';\n\nexport interface SourceCategoryBadgeProps {\n sourceCategory: SourceCategory;\n}\n\n/**\n * Badge component specifically for displaying ferment source categories\n * Maps source categories (recipes, history, tools, etc.) to appropriate color variants\n * \n * @example\n * <SourceCategoryBadge sourceCategory=\"recipes\" />\n */\nexport function SourceCategoryBadge({ sourceCategory }: SourceCategoryBadgeProps) {\n const colors = getSourceCategoryColors(sourceCategory);\n\n return (\n <Badge variant={colors.badge}>\n {sourceCategory}\n </Badge>\n );\n}\n","/**\n * Source Category Color Utilities\n * Provides consistent color mapping for ferment source categories\n */\nimport type { BadgeVariant } from '../components/Badge';\nimport type { SourceCategory } from '../types/types';\n\nexport interface SourceCategoryColors {\n badge: BadgeVariant;\n}\n\n/**\n * Get badge color for a ferment source category\n * Returns badge variant for consistent color scheme\n * \n * @example\n * const colors = getSourceCategoryColors('recipes');\n * // { badge: 'ginger' }\n */\nexport function getSourceCategoryColors(sourceCategory: SourceCategory): SourceCategoryColors {\n const colorMap: Record<SourceCategory, SourceCategoryColors> = {\n about: {\n badge: 'cucumber', // Information - green\n },\n history: {\n badge: 'peach', // Heritage - orange\n },\n recipes: {\n badge: 'ginger', // Cooking - warm spice\n },\n tools: {\n badge: 'yogurt', // Equipment - neutral\n },\n starters: {\n badge: 'ginger', // Living cultures - warm spice\n },\n culture: {\n badge: 'peach', // Traditions - orange\n },\n scientific: {\n badge: 'berry', // Research - red\n },\n commercial: {\n badge: 'yogurt', // Business - neutral\n },\n course: {\n badge: 'plum', // Education - purple\n },\n general: {\n badge: 'cucumber', // Default - green\n },\n };\n\n return colorMap[sourceCategory] || { badge: 'yogurt' };\n}\n","import { Button } from './Button';\nimport { DialogTrigger, ListBox, ListBoxItem } from 'react-aria-components';\nimport { Key, ReactNode } from 'react';\nimport { Popover } from './Popover';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\nexport interface StatCardProps {\n title: string;\n value: ReactNode;\n description?: string;\n list?: Array<{ id: string | number | Key; name: string }>;\n listTitle?: string;\n actions?: ReactNode;\n listItemActions?: (item: { id: string | number | Key; name: string }) => ReactNode;\n}\n\nconst cardStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n padding: 24,\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-200',\n display: 'flex',\n flexDirection: 'column',\n gap: 16,\n alignItems: 'center',\n textAlign: 'center'\n});\n\nconst cardTitleStyles = style({\n font: 'title-lg',\n color: '--plum-800',\n margin: 0\n});\n\nconst cardValueStyles = style({\n font: 'heading-2xl',\n color: '--plum-800',\n});\n\nconst cardDescStyles = style({\n font: 'body',\n color: '--plum-800',\n margin: 0\n});\n\nconst listPreviewStyles = style({\n display: 'flex',\n flexDirection: 'row',\n gap: 16,\n alignItems: 'center',\n font: 'body-sm',\n color: '--plum-800',\n margin: 0,\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none'\n});\n\nconst listBoxItemStyles = style({\n font: 'body',\n paddingX: 8,\n paddingY: 4,\n borderRadius: 'sm',\n backgroundColor: 'transparent',\n color: '--plum-800',\n outline: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8\n});\n\nconst actionsContainerStyles = style({\n display: 'flex',\n gap: 8,\n justifyContent: 'center'\n});\n\n/**\n * StatCard component for displaying dashboard statistics\n *\n * @example\n * <StatCard \n * title=\"Total Sources\"\n * value={42}\n * description=\"Active scraping sources\"\n * />\n *\n * @example\n * // With icon as value\n * <StatCard \n * title=\"System Status\"\n * value={<CheckmarkCircle />}\n * description=\"All systems operational\"\n * />\n *\n * @example\n * // With list and popover\n * <StatCard\n * title=\"Countries Without Ferments\"\n * value={5}\n * list={countries}\n * listTitle=\"Countries Without Ferments\"\n * />\n */\nexport function StatCard({ title, value, description, list, listTitle, actions, listItemActions }: StatCardProps) {\n const intl = useIntl();\n\n return (\n <div className={cardStyles}>\n <h3 className={cardTitleStyles}>{title}</h3>\n <div className={cardValueStyles}>{value}</div>\n {description && <p className={cardDescStyles}>{description}</p>}\n {list && list.length > 0 && (\n <div className={listPreviewStyles}>\n <DialogTrigger>\n <Button variant=\"navigation\">{intl.formatMessage({ id: 'statCard.viewAll' })}</Button>\n <Popover\n title={listTitle || title}>\n <ListBox className={listBoxStyles}>\n {list.map(item => (\n <ListBoxItem key={item.id} className={listBoxItemStyles}>\n <span>{item.name}</span>\n {listItemActions?.(item)}\n </ListBoxItem>\n ))}\n </ListBox>\n </Popover>\n </DialogTrigger>\n {actions && (\n <div className={actionsContainerStyles}>\n {actions}\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","import { Switch as AriaSwitch, SwitchProps as AriaSwitchProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\n// For props: https://react-spectrum.adobe.com/react-aria/Switch.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Switch.html#styling\n\nconst switchWrapperStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nconst switchContainerStyles = style({\n ...focusRing(),\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n paddingEnd: 2,\n cursor: 'pointer',\n font: {\n default: 'ui',\n size: {\n S: 'ui-sm',\n M: 'ui',\n L: 'ui-lg'\n }\n },\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900',\n isDisabled: '--plum-400'\n },\n borderRadius: 'full'\n});\n\nconst switchTrackStyles = style({\n width: {\n default: 44,\n size: {\n S: 36,\n M: 44,\n L: 52\n }\n },\n height: {\n default: 24,\n size: {\n S: 20,\n M: 24,\n L: 28\n }\n },\n borderRadius: 'full',\n padding: 2,\n display: 'flex',\n alignItems: 'center',\n backgroundColor: {\n default: '--yogurt-400',\n isSelected: '--plum-500',\n isDisabled: 'disabled'\n },\n borderStyle: {\n forcedColors: 'solid'\n },\n borderColor: {\n forcedColors: 'ButtonBorder'\n },\n borderWidth: {\n forcedColors: 1\n }\n});\n\nconst switchThumbStyles = style({\n width: {\n default: 20,\n size: {\n S: 16,\n M: 20,\n L: 24\n }\n },\n height: {\n default: 20,\n size: {\n S: 16,\n M: 20,\n L: 24\n }\n },\n borderRadius: 'full',\n backgroundColor: {\n default: '--yogurt-50',\n forcedColors: 'ButtonText'\n },\n transform: {\n default: 'translateX(0)',\n isSelected: {\n default: 'translateX(22px)',\n size: {\n S: 'translateX(16px)',\n M: 'translateX(22px)',\n L: 'translateX(24px)'\n }\n }\n }\n});\n\nconst errorStyles = style({\n font: {\n default: 'ui-sm',\n size: {\n S: 'ui-xs',\n M: 'ui-sm',\n L: 'ui'\n }\n },\n color: '--berry-600',\n});\n\nconst descriptionStyles = style({\n font: {\n default: 'ui-sm',\n size: {\n S: 'ui-xs',\n M: 'ui-sm',\n L: 'ui'\n }\n },\n color: '--plum-800',\n});\n\nexport interface SwitchProps extends Omit<AriaSwitchProps, 'children'> {\n children?: React.ReactNode;\n size?: 'S' | 'M' | 'L';\n description?: string;\n error?: string;\n}\n\nexport const Switch = ({ children, size = 'M', description, isDisabled, error, ...props }: SwitchProps) => {\n return (\n <div className={switchWrapperStyles}>\n <AriaSwitch\n isDisabled={isDisabled}\n {...props}\n className={renderProps => switchContainerStyles({ ...renderProps, size, isDisabled })}\n >\n {renderProps => (\n <>\n <div className={switchTrackStyles({ ...renderProps, size, isDisabled })}>\n <div className={switchThumbStyles({ ...renderProps, size, isDisabled })} />\n </div>\n {children}\n </>\n )}\n </AriaSwitch>\n {error && isDisabled && (\n <div className={errorStyles({ size })}>{error}</div>\n )}\n {description && (\n <div className={descriptionStyles({ size })}>{description}</div>\n )}\n </div>\n );\n};","import { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { Tabs as AriaTabs, TabList as AriaTabList, Tab as AriaTab, TabPanel as AriaTabPanel } from 'react-aria-components';\nimport type { TabListProps, TabPanelProps, TabProps, TabsProps } from 'react-aria-components';\n\nconst tabsStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 0\n});\n\nconst tabListStyles = style({\n display: 'flex',\n gap: 4,\n borderBottomWidth: 2,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-200',\n marginBottom: 24,\n overflowX: 'auto',\n overflowY: 'hidden',\n WebkitOverflowScrolling: 'touch'\n});\n\nconst tabStyles = style({\n ...focusRing(),\n paddingX: 20,\n paddingY: 12,\n font: 'body',\n fontWeight: {\n isSelected: 'bold'\n },\n color: {\n default: '--plum-800',\n isSelected: '--plum-900',\n isHovered: '--plum-900',\n isDisabled: 'disabled'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--plum-50',\n isSelected: '--plum-100'\n },\n borderWidth: 0,\n borderBottomWidth: 2,\n borderStyle: 'solid',\n borderColor: {\n default: 'transparent',\n isSelected: '--plum-800',\n isHovered: '--plum-900'\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n },\n textDecoration: 'none',\n transitionProperty: 'all',\n transitionDuration: '200ms'\n});\n\nconst tabPanelStyles = style({\n paddingY: 0,\n outlineStyle: 'none'\n});\n\nexport function Tabs({ children, ...props }: TabsProps) {\n return (\n <AriaTabs className={tabsStyles} {...props}>\n {children}\n </AriaTabs>\n );\n}\n\nexport function TabList<T extends object>({ children, ...props }: TabListProps<T>) {\n return (\n <AriaTabList className={tabListStyles} {...props}>\n {children}\n </AriaTabList>\n );\n}\n\nexport function Tab({ children, ...props }: TabProps) {\n return (\n <AriaTab className={tabStyles} {...props}>\n {children}\n </AriaTab>\n );\n}\n\nexport function TabPanel({ children, ...props }: TabPanelProps) {\n return (\n <AriaTabPanel className={tabPanelStyles} {...props}>\n {children}\n </AriaTabPanel>\n );\n}\n","import {\n TextArea as AriaTextArea,\n TextField as AriaTextField,\n Label,\n Text,\n FieldError,\n} from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { textFieldErrorStyles, textFieldLabelStyles, textFieldDescriptionStyles, type TextFieldProps, textFieldStyles } from './TextField';\nimport { textFieldInputStyles } from '../utils/styleUtils' with { type: 'macro' };\nimport { useRef, useEffect } from 'react';\nimport { safeWindow } from '../utils/browser';\n\nexport interface TextAreaProps extends Omit<TextFieldProps, 'type'> { }\n\nconst textareaStyles = style({\n ...textFieldInputStyles(),\n resize: 'none',\n minHeight: 75, // ~3 lines\n maxHeight: '75vh',\n overflowY: 'auto',\n});\n\nexport function TextArea({ label, error, description, placeholder, value, ...props }: TextAreaProps) {\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n // Auto-grow on value change\n useEffect(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n // Reset height to recalculate\n textarea.style.height = '75px';\n\n // Set to scrollHeight (content height)\n const newHeight = Math.min(textarea.scrollHeight, safeWindow.innerHeight * 0.75 || 64);\n textarea.style.height = `${newHeight}px`;\n }, [value]);\n\n return (\n <AriaTextField className={textFieldStyles} isInvalid={!!error} {...props} value={value}>\n <Label className={textFieldLabelStyles}>{label}</Label>\n <AriaTextArea\n ref={textareaRef}\n className={textareaStyles}\n placeholder={placeholder}\n />\n {description && !error && (\n <Text slot=\"description\" className={textFieldDescriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={textFieldErrorStyles}>{error}</FieldError>\n </AriaTextField>\n );\n}\n","/**\n * Safe browser API wrappers that work in SSR\n */\n\nexport const isBrowser = typeof window !== 'undefined';\nexport const isDocument = typeof document !== 'undefined';\n\nexport const safeWindow = {\n get innerHeight() {\n return isBrowser ? window.innerHeight : 0;\n },\n\n get location() {\n return isBrowser ? window.location : null;\n },\n\n matchMedia: (query: string) => {\n if (!isBrowser) {\n return { matches: false, media: query, addEventListener: () => { }, removeEventListener: () => { } };\n }\n return window.matchMedia(query);\n },\n\n reload: () => {\n if (isBrowser) window.location.reload();\n }\n};\n\nexport const safeDocument = {\n get documentElement() {\n return isDocument ? document.documentElement : null;\n },\n\n get referrer() {\n return isDocument ? document.referrer : '';\n },\n\n createElement: (tag: string) => {\n return isDocument ? document.createElement(tag) : HTMLUnknownElement.prototype;\n }\n};\n\nexport const safeLocalStorage = {\n getItem: (key: string): string | null => {\n if (!isBrowser) return null;\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n },\n\n setItem: (key: string, value: string): void => {\n if (!isBrowser) return;\n try {\n localStorage.setItem(key, value);\n } catch {\n // Silent fail\n }\n }\n};\n","// Contexts & Providers\nexport { ThemeProvider, useTheme } from './theme-context';\nexport { I18nProvider } from './i18n-provider';\nexport { useIntl } from './i18n-context';\nexport { FilterProvider, useFilters } from './filter-context';\n","import { createContext, useContext, useState, useEffect, ReactNode } from 'react';\nimport { isBrowser, safeWindow, safeDocument, safeLocalStorage } from '../utils/browser';\n\ntype ColorScheme = 'light' | 'dark';\n\ninterface ThemeContextType {\n colorScheme: ColorScheme;\n toggleColorScheme: () => void;\n}\n\nconst ThemeContext = createContext<ThemeContextType | undefined>(undefined);\n\nconst THEME_STORAGE_KEY = 'worldferments-theme';\n\n// Get initial theme synchronously to prevent flicker\nfunction getInitialTheme(): ColorScheme {\n if (!isBrowser) return 'light';\n\n // First check if blocking script already set the attribute\n const htmlElement = safeDocument.documentElement;\n if (htmlElement) {\n const htmlAttribute = htmlElement.getAttribute('data-color-scheme') as ColorScheme | null;\n if (htmlAttribute && (htmlAttribute === 'light' || htmlAttribute === 'dark')) {\n return htmlAttribute;\n }\n }\n\n // Fallback to localStorage\n const stored = safeLocalStorage.getItem(THEME_STORAGE_KEY) as ColorScheme | null;\n if (stored && (stored === 'light' || stored === 'dark')) {\n return stored;\n }\n\n // Final fallback to system preference\n return safeWindow.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nexport function ThemeProvider({ children }: { children: ReactNode }) {\n // Always start with 'light' to match server render\n // Will update to actual theme after mount\n const [colorScheme, setColorScheme] = useState<ColorScheme>('light');\n const [mounted, setMounted] = useState(false);\n\n // Set mounted flag and sync to actual theme on client\n useEffect(() => {\n setMounted(true);\n const actual = getInitialTheme();\n setColorScheme(actual);\n }, []);\n\n // Update html attribute and localStorage when scheme changes\n useEffect(() => {\n if (!mounted || !isBrowser) return;\n\n const htmlElement = safeDocument.documentElement;\n if (htmlElement) {\n htmlElement.setAttribute('data-color-scheme', colorScheme);\n }\n safeLocalStorage.setItem(THEME_STORAGE_KEY, colorScheme);\n }, [colorScheme, mounted]);\n\n const toggleColorScheme = () => {\n setColorScheme(prev => prev === 'light' ? 'dark' : 'light');\n };\n\n return (\n <ThemeContext.Provider value={{ colorScheme, toggleColorScheme }}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\nexport function useTheme() {\n const context = useContext(ThemeContext);\n if (context === undefined) {\n throw new Error('useTheme must be used within a ThemeProvider');\n }\n return context;\n}\n","import { createContext, useContext, useState, ReactNode } from 'react';\nimport type { FilterState } from '../types/types';\n\n\n/**\n * Filter context type with state and update methods\n */\ninterface FilterContextType {\n filters: FilterState;\n updateFilter: <K extends keyof FilterState>(key: K, value: FilterState[K]) => void;\n clearFilters: () => void;\n}\n\nconst initialFilterState: FilterState = {\n category: null,\n country: null,\n searchQuery: '',\n};\n\nconst FilterContext = createContext<FilterContextType | undefined>(undefined);\n\n/**\n * Filter Provider Component\n * Manages filter state for the application\n * \n * @example\n * <FilterProvider>\n * <App />\n * </FilterProvider>\n */\nexport function FilterProvider({ children }: { children: ReactNode }) {\n const [filters, setFilters] = useState<FilterState>(initialFilterState);\n\n /**\n * Update a single filter\n */\n const updateFilter = <K extends keyof FilterState>(\n key: K,\n value: FilterState[K]\n ) => {\n setFilters((prev) => ({\n ...prev,\n [key]: value,\n }));\n };\n\n /**\n * Clear all filters back to initial state\n */\n const clearFilters = () => {\n setFilters(initialFilterState);\n };\n\n return (\n <FilterContext.Provider value={{ filters, updateFilter, clearFilters }}>\n {children}\n </FilterContext.Provider>\n );\n}\n\n/**\n * Hook to access filter context\n * Must be used within FilterProvider\n * \n * @example\n * const { filters, updateFilter, clearFilters } = useFilters();\n * updateFilter('category', 'dairy');\n */\nexport function useFilters(): FilterContextType {\n const context = useContext(FilterContext);\n\n if (context === undefined) {\n throw new Error('useFilters must be used within a FilterProvider');\n }\n\n return context;\n}\n","// Export all locale message files for i18n\nimport enMessages from './en.json';\n\nexport const localeMessages = {\n en: enMessages\n} as const;\n\nexport type SupportedLocale = keyof typeof localeMessages;\n","{\n \"app.title\": \"World Ferments\",\n \"app.subtitle\": \"Explore Fermented Foods Around the World\",\n \"home.hello\": \"Hello World!\",\n \"home.description\": \"Next.js is running successfully with React Aria Components and React Spectrum S2. The foundation is set!\",\n \"home.checkHealth\": \"Check API Health\",\n \"home.healthChecking\": \"Checking health...\",\n \"home.healthClickPrompt\": \"Click the button to check API health\",\n \"health.status\": \"Status\",\n \"health.database\": \"Database\",\n \"health.time\": \"Time\",\n \"health.error\": \"Error\",\n \"health.siteUrls\": \"Site URLs\",\n \"health.apiEndpoints\": \"API Endpoints\",\n \"theme.toggle.label\": \"Dark Mode\",\n \"theme.toggle.ariaLabel\": \"Toggle dark mode\",\n \"features.map\": \"Coming soon: Interactive map with fermented foods\",\n \"features.browse\": \"Browse by region, category, and culture\",\n \"ferments.subtitle\": \"Explore and discover fermented foods from around the world\",\n \"ferments.loading\": \"Loading...\",\n \"ferments.count\": \"{count, plural, =0 {No ferments found} one {# ferment found} other {# ferments found}}\",\n \"ferments.filter.category\": \"Category\",\n \"ferments.filter.country\": \"Country\",\n \"ferments.filter.allCategories\": \"All Categories\",\n \"ferments.filter.allCountries\": \"All Countries\",\n \"ferments.filter.all\": \"All\",\n \"ferments.filter.alphabetLabel\": \"Filter by first letter\",\n \"ferments.filter.clear\": \"Clear Filters\",\n \"ferments.filter.loading\": \"Loading filters...\",\n \"ferments.filter.category.ariaLabel\": \"Filter by category\",\n \"ferments.filter.country.ariaLabel\": \"Filter by country\",\n \"ferments.search.placeholder\": \"Search ferments...\",\n \"ferments.search.label\": \"Search\",\n \"ferments.search.ariaLabel\": \"Search ferments by name or description\",\n \"ferments.search.clear\": \"Clear\",\n \"ferment.detail.backToBrowse\": \"Back to Browse\",\n \"ferment.detail.loading\": \"Loading ferment details...\",\n \"ferment.detail.notFound\": \"Ferment not found\",\n \"ferment.detail.error\": \"Failed to load ferment details\",\n \"ferment.detail.country\": \"Country\",\n \"ferment.detail.region\": \"Region\",\n \"ferment.detail.category\": \"Category\",\n \"ferment.detail.ingredients\": \"Ingredients\",\n \"ferment.detail.learnMore\": \"Learn More\",\n \"ferment.detail.viewSource\": \"View Source\",\n \"ferment.detail.location\": \"Location\",\n \"ferment.detail.coordinates\": \"Coordinates\",\n \"ferment.detail.relatedFerments\": \"Related Ferments\",\n \"ferment.detail.close\": \"Close\",\n \"map.loading\": \"Loading map...\",\n \"map.error.title\": \"Map Error\",\n \"map.error.load\": \"Failed to load ferments for map\",\n \"map.error.noToken\": \"Mapbox token not configured. Please set NEXT_PUBLIC_MAPBOX_TOKEN in your environment.\",\n \"map.error.init\": \"Failed to initialize map\",\n \"map.ariaLabel\": \"Interactive map showing fermented foods around the world\",\n \"map.marker.label\": \"{name} from {country}\",\n \"map.popup.viewDetails\": \"View details\",\n \"nav.map\": \"Map\",\n \"nav.list\": \"List\",\n \"nav.viewToggle.ariaLabel\": \"Switch between map and list views\",\n \"nav.about\": \"About\",\n \"common.back\": \"Back\",\n \"common.loading\": \"Loading...\",\n \"common.saving\": \"Saving...\",\n \"common.noData\": \"No data available\",\n \"table.common.loading\": \"Loading data...\",\n \"table.common.loadingMore\": \"Loading more data...\",\n \"filterTabs.ariaLabel\": \"Filter options\",\n \"statCard.viewAll\": \"View all\",\n \"ferments.category.all\": \"All Categories\",\n \"ferments.category.vegetable\": \"Vegetable\",\n \"ferments.category.dairy\": \"Dairy\",\n \"ferments.category.grain\": \"Grain\",\n \"ferments.category.legume\": \"Legume\",\n \"ferments.category.beverage\": \"Beverage\",\n \"ferments.category.condiment\": \"Condiment\",\n \"ferments.category.protein\": \"Protein\",\n \"ferments.category.fruit\": \"Fruit\",\n \"ferments.category.other\": \"Other\",\n \"sources.content_type.about\": \"About\",\n \"sources.content_type.history\": \"History\",\n \"sources.content_type.recipes\": \"Recipes\",\n \"sources.content_type.tools\": \"Tools & Equipment\",\n \"sources.content_type.starters\": \"Starters & Cultures\",\n \"sources.content_type.culture\": \"Cultural Context\",\n \"sources.content_type.scientific\": \"Scientific Research\",\n \"sources.content_type.commercial\": \"Commercial Products\",\n \"sources.content_type.course\": \"Courses & Tutorials\",\n \"sources.content_type.general\": \"General Information\"\n}","// Hooks\nexport * from './useFilterState';\nexport * from './useIsMobileDevice';\n","/**\n * Filter State Hook\n * Custom hook for managing ferment filter state with type-safe utilities\n */\nimport type { FilterState } from '../types/types';\n\n/**\n * Default filter state\n */\nexport const DEFAULT_FILTERS: FilterState = {\n searchQuery: '',\n category: null,\n country: null,\n letterFilter: null,\n};\n\n/**\n * Check if any filters are active\n */\nexport function hasActiveFilters(filters: FilterState): boolean {\n return (\n filters.searchQuery.trim() !== '' ||\n filters.category !== null ||\n filters.country !== null ||\n (filters.letterFilter !== null && filters.letterFilter !== 'all')\n );\n}\n\n/**\n * Clear all filters, returning default state\n */\nexport function clearFilters(): FilterState {\n return { ...DEFAULT_FILTERS };\n}\n\n/**\n * Update a single filter field\n */\nexport function updateFilter(\n filters: FilterState,\n field: keyof FilterState,\n value: string | null\n): FilterState {\n return {\n ...filters,\n [field]: value,\n };\n}\n\n/**\n * Convert filter state to API query parameters\n * Only includes non-empty filters\n */\nexport function filtersToQueryParams(filters: FilterState): Record<string, string> {\n const params: Record<string, string> = {};\n\n if (filters.category) {\n params.category = filters.category;\n }\n\n if (filters.country) {\n params.country = filters.country;\n }\n\n if (filters.letterFilter && filters.letterFilter !== 'all') {\n params.letter = filters.letterFilter;\n }\n\n return params;\n}\n","import { useMediaQuery } from '@react-spectrum/utils';\n\n/**\n * Hook to detect if the current viewport is a mobile device\n * \n * Uses the 640px breakpoint (S2's small breakpoint) to determine mobile\n * \n * @returns true if viewport width is 640px or less\n * \n * @example\n * const isMobile = useIsMobileDevice();\n * \n * return (\n * <Button>\n * {isMobile ? 'Save' : 'Save Changes'}\n * </Button>\n * );\n */\nexport function useIsMobileDevice(): boolean {\n return useMediaQuery('(max-width: 640px)');\n}\n","// Utilities\nexport { textFieldInputStyles } from './styleUtils';\nexport { getCategoryColors } from './category-colors';\nexport { getSourceCategoryColors } from './source-type-colors';\nexport {\n getCategoryOptions,\n getSourceCategoryOptions,\n FERMENT_CATEGORIES,\n SOURCE_CATEGORIES,\n} from './select-options';\nexport type { CategoryFilterType, FermentCategory, SelectOption } from './select-options';\nexport { api } from './api';\nexport type { FermentsResponse, FermentDetailResponse, SearchResponse, FilterOptions } from './api';\nexport { isBrowser, isDocument, safeWindow, safeDocument, safeLocalStorage } from './browser';\n","\nexport const textFieldInputStyles = () => ({\n padding: 12,\n paddingX: 16,\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisible: '--plum-500',\n isInvalid: '--berry-500',\n },\n borderRadius: 'lg',\n font: 'body',\n backgroundColor: 'layer-1',\n color: {\n default: '--plum-800',\n '::placeholder': '--plum-400'\n },\n outline: 'none',\n} as const);\n","/**\n * Select Options Utilities\n * Provides consistent select options for categories and source categories\n * Used across admin-ui and consumer-ui for dropdowns/filters\n */\nimport type { SourceCategory } from '../types/types';\n\nexport interface SelectOption<T = string> {\n id: string;\n label: string;\n value: T;\n isDisabled?: boolean;\n}\n\n/**\n * Ferment category values\n * Sorted alphabetically with 'other' last\n */\nexport const FERMENT_CATEGORIES = [\n 'beverage',\n 'condiment',\n 'dairy',\n 'fruit',\n 'grain',\n 'legume',\n 'protein',\n 'vegetable',\n 'other',\n] as const;\n\n/**\n * Ferment category type\n */\nexport type FermentCategory = typeof FERMENT_CATEGORIES[number];\n\n/**\n * Category filter type for frontend filtering\n * Includes 'all' option for showing all categories\n */\nexport type CategoryFilterType = FermentCategory | 'all';\n\n/**\n * Source category values\n */\nexport const SOURCE_CATEGORIES: SourceCategory[] = [\n 'about',\n 'history',\n 'recipes',\n 'tools',\n 'starters',\n 'culture',\n 'scientific',\n 'commercial',\n 'course',\n 'general',\n];\n\n/**\n * Get ferment category options for Select components\n * Returns i18n keys that need to be formatted with intl.formatMessage\n * \n * @param includeAll - Whether to include \"All\" option (for filters)\n * @returns Array of select options with i18n keys\n * \n * @example\n * const options = getCategoryOptions().map(opt => ({\n * ...opt,\n * label: intl.formatMessage({ id: opt.label })\n * }));\n */\nexport function getCategoryOptions(includeAll = false): SelectOption<FermentCategory | null>[] {\n const options: SelectOption<FermentCategory | null>[] = [];\n\n if (includeAll) {\n options.push({\n id: 'all',\n label: 'ferments.category.all', // i18n key\n value: null,\n });\n }\n\n FERMENT_CATEGORIES.forEach(category => {\n options.push({\n id: category,\n label: `ferments.category.${category}`, // i18n key\n value: category,\n });\n });\n\n return options;\n}\n\n/**\n * Get source category options for Select components\n * Returns i18n keys that need to be formatted with intl.formatMessage\n * \n * @returns Array of select options with i18n keys\n * \n * @example\n * const options = getSourceCategoryOptions().map(opt => ({\n * ...opt,\n * label: intl.formatMessage({ id: opt.label })\n * }));\n */\nexport function getSourceCategoryOptions(): SelectOption<SourceCategory>[] {\n return SOURCE_CATEGORIES.map(category => ({\n id: category,\n label: `sources.content_type.${category}`, // i18n key\n value: category,\n }));\n}\n","/**\n * API Client for GlobalFerments\n * \n * Centralized functions for all backend API calls\n * with error handling and type safety\n */\n\nimport type { Category, Country, Ferment, RelatedFerment } from '../types';\n\nexport interface FermentsResponse {\n ferments: Ferment[];\n count: number;\n filters?: {\n category?: string;\n country?: string;\n };\n}\n\nexport interface FermentDetailResponse extends Ferment {\n relatedFerments: Array<RelatedFerment>;\n}\n\nexport interface SearchResponse {\n results: Ferment[];\n query: string;\n count: number;\n}\n\nexport interface FilterOptions {\n category?: string;\n country?: string;\n limit?: number;\n offset?: number;\n}\n\n/**\n * API client object with all endpoint methods\n */\nexport const api = {\n /**\n * Get list of ferments with optional filters\n */\n async getFerments(filters?: FilterOptions): Promise<FermentsResponse> {\n const params = new URLSearchParams();\n\n if (filters?.category) params.append('category', filters.category);\n if (filters?.country) params.append('country', filters.country);\n if (filters?.limit) params.append('limit', filters.limit.toString());\n if (filters?.offset) params.append('offset', filters.offset.toString());\n\n const url = `/api/ferments${params.toString() ? `?${params}` : ''}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch ferments: ${response.statusText}`);\n }\n\n return response.json();\n },\n\n /**\n * Get optimized data for map markers\n */\n async getFermentsForMap(): Promise<{ ferments: Array<Pick<Ferment, 'id' | 'name' | 'latitude' | 'longitude' | 'category' | 'country'>> }> {\n const response = await fetch('/api/ferments/map');\n\n if (!response.ok) {\n throw new Error(`Failed to fetch map data: ${response.statusText}`);\n }\n\n return response.json();\n },\n\n /**\n * Search ferments by name or description\n */\n async searchFerments(query: string): Promise<SearchResponse> {\n if (!query || query.length < 2) {\n return { results: [], query, count: 0 };\n }\n\n const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);\n\n if (!response.ok) {\n throw new Error(`Search failed: ${response.statusText}`);\n }\n\n return response.json();\n },\n\n /**\n * Get list of countries with hasFerments flag\n * Optionally filtered by category\n */\n async getCountries(category?: string): Promise<{ countries: Country[] }> {\n const url = category\n ? `/api/countries?category=${encodeURIComponent(category)}`\n : '/api/countries';\n\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch countries: ${response.statusText}`);\n }\n\n return response.json();\n },\n\n /**\n * Get list of categories\n * Optionally filtered by country\n */\n async getCategories(country?: string): Promise<{ categories: Category[] }> {\n const url = country\n ? `/api/categories?country=${encodeURIComponent(country)}`\n : '/api/categories';\n\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch categories: ${response.statusText}`);\n }\n\n return response.json();\n },\n};\n","\nexport * from './types';\n","import type { BadgeVariant } from '../components/Badge';\n\n/**\n * Type definitions for GlobalFerments\n *\n * Shared types used across the application\n */\n\n/**\n * License types for content attribution\n * Used across admin-ui, consumer-ui, and backend\n */\nexport type LicenseType =\n | 'CC0'\n | 'CC-BY'\n | 'CC-BY-SA'\n | 'CC-BY-NC'\n | 'CC-BY-NC-SA'\n | 'CC-BY-ND'\n | 'CC-BY-NC-ND'\n | 'MIT'\n | 'Apache-2.0'\n | 'GPL'\n | 'All Rights Reserved'\n | 'Public Domain'\n | 'Unknown'\n | 'Other'\n | 'Unsplash'\n | 'Pexels'\n | 'Pixabay';\n\n/**\n * Data source extraction methods\n */\nexport type DataSourceType =\n | 'html_meta_tags'\n | 'structured_data'\n | 'page_content'\n | 'mixed'\n | 'manual'\n | 'unknown';\n\n/**\n * Source content category\n * Categorizes the type of information a ferment source provides\n */\nexport type SourceCategory =\n | 'about' // General information about the ferment\n | 'history' // Historical background and cultural context\n | 'recipes' // Recipes and preparation methods\n | 'tools' // Equipment and tools needed\n | 'starters' // Starter cultures and ingredients\n | 'culture' // Cultural significance and traditions\n | 'scientific' // Scientific research and studies\n | 'commercial' // Commercial products and vendors\n | 'course' // Classes, tutorials, and educational content\n | 'general'; // General/uncategorized sources\n\nexport interface FilterState {\n category: string | null;\n country: string | null;\n searchQuery: string;\n letterFilter: string | null;\n}\n\nexport interface CategoryColors {\n marker: string; // CSS variable for map marker color\n badge: BadgeVariant; // plain english name that maps to badge color scheme\n}\n\n/**\n * Type definitions for GlobalFerments\n *\n * Shared types used across the application\n */\n\nexport interface Ferment {\n id: number;\n name: string;\n slug: string;\n description: string;\n country: string;\n countryCode: string;\n region: string;\n regionName?: string | null;\n category: string;\n ingredients: string[] | null;\n imageUrl: string | null;\n sourceUrl: string | null;\n latitude: number | null;\n longitude: number | null;\n relatedFerments?: RelatedFerment[];\n}\n\nexport interface RelatedFerment {\n id: number;\n name: string;\n slug: string;\n category?: string;\n imageUrl?: string | null;\n country: string;\n countryCode?: string;\n}\n\nexport interface Country {\n code: string;\n name: string;\n region: string;\n hasFerments: boolean; // True if this country has ferments\n}\n\nexport interface Category {\n name: string; // Primary field name\n category: string; // Deprecated (backward compatibility)\n}\n"],"names":[],"version":3,"file":"main.cjs.map"}
1
+ {"mappings":"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","sources":["packages/shared/src/index.ts","packages/shared/src/components/index.ts","packages/shared/src/components/AlertDialog.tsx","packages/shared/src/components/Button.tsx","packages/shared/src/components/TextField.tsx","packages/shared/src/lib/i18n-provider.tsx","packages/shared/src/components/Autocomplete.tsx","packages/shared/src/components/AutocompleteTable.tsx","packages/shared/src/components/Table.tsx","packages/shared/src/components/Checkbox.tsx","packages/shared/src/components/ProgressBar.tsx","packages/shared/src/components/AvatarButton.tsx","packages/shared/src/components/Badge.tsx","packages/shared/src/components/CloseButton.tsx","packages/shared/src/components/ComboBox.tsx","packages/shared/src/components/CookieConsent.tsx","packages/shared/src/components/Link.tsx","packages/shared/src/components/DisclosureGroup.tsx","packages/shared/src/components/FermentTypeBadge.tsx","packages/shared/src/utils/category-colors.ts","packages/shared/src/components/FilterTabs.tsx","packages/shared/src/components/LabeledValue.tsx","packages/shared/src/components/LinkButton.tsx","packages/shared/src/components/Menu.tsx","packages/shared/src/components/PasswordStrength.tsx","packages/shared/src/components/Popover.tsx","packages/shared/src/components/RadioGroup.tsx","packages/shared/src/components/SearchField.tsx","packages/shared/src/components/SegmentedControl.tsx","packages/shared/src/components/SegmentedTabs.tsx","packages/shared/src/components/Select.tsx","packages/shared/src/components/SourceTypeBadge.tsx","packages/shared/src/utils/source-type-colors.ts","packages/shared/src/components/StatCard.tsx","packages/shared/src/components/Switch.tsx","packages/shared/src/components/Tabs.tsx","packages/shared/src/components/TextArea.tsx","packages/shared/src/utils/browser.ts","packages/shared/src/lib/index.ts","packages/shared/src/lib/theme-context.tsx","packages/shared/src/lib/filter-context.tsx","packages/shared/src/i18n/index.ts","packages/shared/src/i18n/en.json","packages/shared/src/hooks/index.ts","packages/shared/src/hooks/useFilterState.ts","packages/shared/src/hooks/useIsMobileDevice.ts","packages/shared/src/utils/index.ts","packages/shared/src/utils/styleUtils.ts","packages/shared/src/utils/select-options.ts","packages/shared/src/utils/api.ts","packages/shared/src/types/index.ts","packages/shared/src/types/types.ts"],"sourcesContent":["// Components\nexport * from './components';\n\n// Lib (contexts & providers)\nexport * from './lib';\n\n// i18n\nexport { localeMessages } from './i18n';\n\n// Hooks\nexport * from './hooks';\n\n// Utils\nexport * from './utils';\n\n// Types\nexport * from './types';\n","// Component exports\nexport { AlertDialog, PromptDialog } from './AlertDialog';\nexport { Autocomplete, type AutocompleteItem, type AutocompleteProps } from './Autocomplete';\nexport { AutocompleteTable, type AutocompleteTableProps } from './AutocompleteTable';\nexport { AvatarButton, type AvatarButtonProps } from './AvatarButton';\nexport { Badge, type BadgeVariant } from './Badge';\nexport { Button } from './Button';\nexport { Checkbox, type CheckboxProps } from './Checkbox';\nexport { CloseButton } from './CloseButton';\nexport { ComboBox, type ComboBoxItem, type ComboBoxProps } from './ComboBox';\nexport {\n CookieConsent,\n type CookieConsentProps,\n type ConsentLevel,\n getConsentLevel,\n canUseAnalytics,\n canUseEssentials,\n hasConsented\n} from './CookieConsent';\nexport { DisclosureGroup, type DisclosureItem } from './DisclosureGroup';\nexport { FermentTypeBadge, type FermentTypeBadgeProps } from './FermentTypeBadge';\nexport { FilterTabs } from './FilterTabs';\nexport { LabeledValue } from './LabeledValue';\nexport { Link } from './Link';\nexport { LinkButton, type LinkButtonProps } from './LinkButton';\nexport { Menu, type MenuItem, type MenuProps } from './Menu';\nexport { PasswordStrength, type PasswordStrengthProps, type PasswordValidation } from './PasswordStrength';\nexport { Popover, type PopoverProps } from './Popover';\nexport { ProgressBar } from './ProgressBar';\nexport { RadioGroup } from './RadioGroup';\nexport { SearchField } from './SearchField';\nexport { SegmentedControl } from './SegmentedControl';\nexport { SegmentedTabs, SegmentedTabList, SegmentedTab } from './SegmentedTabs';\nexport { Select } from './Select';\nexport { SourceCategoryBadge, type SourceCategoryBadgeProps } from './SourceTypeBadge';\nexport { StatCard } from './StatCard';\nexport { Switch } from './Switch';\nexport { Table, type ColumnDef, type TableProps } from './Table';\nexport { Tabs, TabList, Tab, TabPanel } from './Tabs';\nexport { TextArea } from './TextArea';\nexport { TextField } from './TextField';\n","import { Button } from './Button';\nimport { Dialog, Heading, Modal, ModalOverlay } from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { TextField } from './TextField';\nimport { useIntl } from '../lib/i18n-context';\nimport { useState } from 'react';\n\n/**\n * Helper to translate default i18n keys or return custom labels as-is\n */\nconst useTranslateLabel = () => {\n const intl = useIntl();\n return (label: string) => {\n // If it's one of our default i18n keys, translate it\n if (label === 'admin.common.ok' || label === 'admin.common.cancel') {\n return intl.formatMessage({ id: label });\n }\n // Otherwise return as-is (already translated by caller)\n return label;\n };\n};\n\nconst overlayStyles = style({\n position: 'fixed',\n inset: 0,\n backgroundColor: 'transparent-black-500',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n isolation: 'isolate',\n zIndex: {\n isFromPopover: 111111\n }\n});\n\nconst modalStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n boxShadow: 'elevated',\n padding: 24,\n width: {\n default: 'calc(100vw - 32px)',\n sm: 400,\n md: 500\n },\n maxWidth: 500,\n transform: {\n entering: 'scale(0.95)',\n default: 'scale(1)'\n },\n});\n\nconst headingStyles = style({\n font: 'heading-xl',\n color: 'heading',\n margin: 0,\n marginBottom: 12\n});\n\nconst messageStyles = style({\n font: 'body',\n color: '--plum-800',\n margin: 0,\n marginBottom: 24\n});\n\nconst buttonGroupStyles = style({\n display: 'flex',\n gap: 12,\n justifyContent: 'end'\n});\n\nexport interface AlertDialogProps {\n isFromPopover?: boolean;\n isOpen: boolean;\n onClose: () => void;\n title: string;\n message: string;\n variant?: 'danger' | 'warning' | 'info' | 'success';\n confirmLabel?: string;\n cancelLabel?: string;\n onConfirm: () => void | Promise<void>;\n showCancel?: boolean;\n children?: React.ReactNode; // Optional custom content (e.g., Switch, Checkbox)\n}\n\nexport function AlertDialog({\n isFromPopover = false,\n isOpen,\n onClose,\n title,\n message,\n variant = 'info',\n confirmLabel = 'admin.common.ok',\n cancelLabel = 'admin.common.cancel',\n onConfirm,\n showCancel = true,\n children\n}: AlertDialogProps) {\n const [isProcessing, setIsProcessing] = useState(false);\n const translateLabel = useTranslateLabel();\n const intl = useIntl();\n\n const handleConfirm = async () => {\n setIsProcessing(true);\n try {\n await onConfirm();\n onClose();\n } finally {\n setIsProcessing(false);\n }\n };\n\n // Map variant to button variant\n const buttonVariant = variant === 'danger' ? 'danger' :\n variant === 'success' ? 'primary' :\n 'primary';\n\n return (\n <ModalOverlay isOpen={isOpen} onOpenChange={(open) => !open && onClose()} className={overlayStyles({ isFromPopover: isFromPopover })}>\n <Modal className={modalStyles}>\n <Dialog>\n {({ close }) => (\n <>\n <Heading slot=\"title\" className={headingStyles}>\n {title}\n </Heading>\n <p className={messageStyles}>{message}</p>\n {children && (\n <div className={style({ marginBottom: 24 })}>\n {children}\n </div>\n )}\n <div className={buttonGroupStyles}>\n {showCancel && (\n <Button\n variant=\"secondary\"\n size=\"M\"\n onPress={close}\n isDisabled={isProcessing}\n >{translateLabel(cancelLabel)}\n </Button>\n )}\n <Button\n variant={buttonVariant}\n size=\"M\"\n onPress={handleConfirm}\n isDisabled={isProcessing}\n >\n {isProcessing ? intl.formatMessage({ id: 'admin.common.processing' }) : translateLabel(confirmLabel)}\n </Button>\n </div>\n </>\n )}\n </Dialog>\n </Modal>\n </ModalOverlay>\n );\n}\n\nexport interface PromptDialogProps {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n message: string;\n inputLabel: string;\n inputPlaceholder?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n onConfirm: (value: string) => void | Promise<void>;\n validator?: (value: string) => string | null;\n}\n\nexport function PromptDialog({\n isOpen,\n onClose,\n title,\n message,\n inputLabel,\n inputPlaceholder = '',\n confirmLabel = 'admin.common.ok',\n cancelLabel = 'admin.common.cancel',\n onConfirm,\n validator\n}: PromptDialogProps) {\n const [value, setValue] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [isProcessing, setIsProcessing] = useState(false);\n const translateLabel = useTranslateLabel();\n const intl = useIntl();\n\n const handleConfirm = async () => {\n // Validate\n if (validator) {\n const validationError = validator(value);\n if (validationError) {\n setError(validationError);\n return;\n }\n }\n\n setIsProcessing(true);\n try {\n await onConfirm(value);\n setValue('');\n setError(null);\n onClose();\n } finally {\n setIsProcessing(false);\n }\n };\n\n const handleClose = () => {\n setValue('');\n setError(null);\n onClose();\n };\n\n return (\n <ModalOverlay isOpen={isOpen} onOpenChange={(open) => !open && handleClose()} className={overlayStyles}>\n <Modal className={modalStyles}>\n <Dialog>\n {({ close }) => (\n <>\n <Heading slot=\"title\" className={headingStyles}>\n {title}\n </Heading>\n <p className={messageStyles}>{message}</p>\n <div className={style({ marginBottom: 24 })}>\n <TextField\n label={inputLabel}\n value={value}\n onChange={(newValue) => {\n setValue(newValue);\n setError(null);\n }}\n placeholder={inputPlaceholder}\n error={error || undefined}\n autoFocus\n />\n </div>\n <div className={buttonGroupStyles}>\n <Button\n variant=\"secondary\"\n size=\"M\"\n onPress={() => {\n setValue('');\n setError(null);\n close();\n }}\n isDisabled={isProcessing}\n >{translateLabel(cancelLabel)}\n </Button>\n <Button\n variant=\"primary\"\n size=\"M\"\n onPress={handleConfirm}\n isDisabled={isProcessing}\n >\n {isProcessing ? intl.formatMessage({ id: 'admin.common.processing' }) : translateLabel(confirmLabel)}\n </Button>\n </div>\n </>\n )}\n </Dialog>\n </Modal>\n </ModalOverlay>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\n// For props: https://react-spectrum.adobe.com/react-aria/Button.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Button.html#styling\n// For conditional styling: https://react-spectrum.adobe.com/beta/s2/styling.html#conditional-styles\n\nexport interface ButtonProps extends AriaButtonProps {\n children?: React.ReactNode;\n variant?: 'primary' | 'secondary' | 'danger' | 'navigation';\n size?: 'S' | 'M' | 'L';\n}\n\n// Primary variant styles\nconst buttonStyles = style({\n ...focusRing(),\n font: {\n default: 'body',\n size: {\n S: 'body-sm',\n L: 'body-lg',// or `body` too?\n },\n },\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: {\n default: 8,\n size: {\n S: 4,\n },\n },\n paddingY: {\n default: 12,\n size: {\n S: 8,\n L: 16,\n },\n variant: {\n navigation: {\n default: 4\n }\n }\n },\n paddingStart: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n navigation: {\n default: 8\n }\n }\n },\n paddingEnd: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n navigation: 12\n }\n },\n width: {\n variant: {\n navigation: 'fit'\n }\n },\n color: {\n variant: {\n primary: {\n default: '--plum-900',\n },\n secondary: {\n default: '--plum-800',\n },\n danger: {\n default: '--yogurt-0',\n },\n navigation: {\n default: '--plum-800',\n }\n },\n forcedColors: 'ButtonText',\n isDisabled: {\n default: 'disabled',\n forcedColors: 'GrayText',\n },\n },\n '--iconPrimary': {\n type: 'fill',\n value: 'currentColor'\n },\n backgroundColor: {\n default: '--plum-300',\n variant: {\n primary: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisible: '--plum-400',\n isPressed: '--plum-400',\n },\n secondary: {\n default: '--yogurt-200',\n isHovered: '--yogurt-300',\n isFocusVisible: '--yogurt-300',\n isPressed: '--yogurt-400',\n },\n navigation: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n isPressed: '--yogurt-300',\n },\n danger: {\n default: '--berry-700',\n isHovered: '--berry-800',\n isFocusVisible: '--berry-800',\n isPressed: '--berry-900',\n },\n },\n forcedColors: 'ButtonFace',\n isDisabled: {\n default: 'gray-100',\n forcedColors: 'GrayText',\n },\n },\n borderRadius: 'lg',\n borderStyle: {\n default: 'none',\n forcedColors: 'solid'\n },\n borderColor: {\n forcedColors: 'ButtonBorder'\n },\n borderWidth: {\n forcedColors: 1\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n }\n});\n\nexport const Button = ({\n children,\n variant = 'primary',\n size = 'M',\n className,\n ...props\n}: ButtonProps) => {\n return (\n <AriaButton\n {...props}\n className={renderProps => `${className || ''} ` + buttonStyles({ ...renderProps, size, variant })}\n >\n {children}\n </AriaButton>\n );\n};\n","import {\n TextField as AriaTextField,\n TextFieldProps as AriaTextFieldProps,\n Input,\n Label,\n Text,\n FieldError,\n} from 'react-aria-components';\nimport { focusRing, style, } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { textFieldInputStyles } from '../utils/styleUtils' with { type: 'macro' };\n\nexport interface TextFieldProps extends Omit<AriaTextFieldProps, 'children'> {\n label: string;\n type?: 'text' | 'password' | 'email' | 'tel' | 'url' | 'number';\n error?: string;\n description?: string;\n placeholder?: string;\n}\n\nexport const textFieldStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n});\n\nexport const textFieldLabelStyles = style({\n font: 'body',\n color: '--plum-800',\n});\n\nconst inputStyles = style({\n ...focusRing(),\n ...textFieldInputStyles(),\n});\n\nexport const textFieldErrorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nexport const textFieldDescriptionStyles = style({\n font: 'body-sm',\n color: '--plum-800',\n});\n\nexport function TextField({ label, type = 'text', error, description, placeholder, ...props }: TextFieldProps) {\n return (\n <AriaTextField className={textFieldStyles} isInvalid={!!error} {...props}>\n <Label className={textFieldLabelStyles}>{label}</Label>\n <Input className={inputStyles} type={type} placeholder={placeholder} />\n {description && !error && (\n <Text slot=\"description\" className={textFieldDescriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={textFieldErrorStyles}>{error}</FieldError>\n </AriaTextField>\n );\n}\n","import { createContext, useContext, ReactNode } from 'react';\nimport { IntlProvider, useIntl as useReactIntl } from 'react-intl';\n\ntype LocaleMessages = Record<string, Record<string, string>>;\n\ninterface I18nProviderProps {\n children: ReactNode;\n messages: LocaleMessages;\n}\n\ntype Locale = string;\n\ninterface I18nContextType {\n locale: Locale;\n setLocale: (locale: Locale) => void;\n}\n\nconst I18nContext = createContext<I18nContextType | undefined>(undefined);\n\n/**\n * Unified i18n provider that can be used by both frontend and admin UI.\n * \n * @param messages - Locale messages object (e.g., from src/i18n/index.ts or admin-ui/src/i18n/index.ts)\n * \n * @example\n * // Frontend usage\n * import { localeMessages } from '@/i18n';\n * <I18nProvider messages={localeMessages}>{children}</I18nProvider>\n * \n * @example\n * // Admin UI usage\n * import { localeMessages } from '@admin/i18n';\n * <I18nProvider messages={localeMessages}>{children}</I18nProvider>\n */\nexport function I18nProvider({ children, messages }: I18nProviderProps) {\n // For MVP, we'll start with just English\n // Future: Add locale state management and persistence\n const locale: Locale = 'en';\n\n const setLocale = (newLocale: Locale) => {\n // Future: Implement locale switching with localStorage\n console.log('Locale switching to:', newLocale);\n };\n\n // Defensive check: ensure messages are loaded\n const currentMessages = messages?.[locale] || {};\n\n if (!messages || Object.keys(currentMessages).length === 0) {\n console.warn('I18nProvider: No messages loaded for locale:', locale);\n }\n\n return (\n <I18nContext.Provider value={{ locale, setLocale }}>\n <IntlProvider\n messages={currentMessages}\n locale={locale}\n defaultLocale=\"en\"\n >\n {children}\n </IntlProvider>\n </I18nContext.Provider>\n );\n}\n\nexport function useI18n() {\n const context = useContext(I18nContext);\n if (context === undefined) {\n throw new Error('useI18n must be used within an I18nProvider');\n }\n return context;\n}\n\n/**\n * Hook to access react-intl's formatMessage and other intl methods.\n * Re-exported from react-intl for convenience.\n */\nexport function useIntl() {\n return useReactIntl();\n}\n","import { useState, useMemo } from 'react';\nimport {\n SearchField as AriaSearchField,\n Autocomplete as AriaAutocomplete,\n Input,\n Label,\n ListBox,\n ListBoxItem,\n useFilter,\n Popover,\n FieldError\n} from 'react-aria-components';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport Search from '@react-spectrum/s2/icons/Search';\nimport CheckmarkCircle from '@react-spectrum/s2/icons/CheckmarkCircle';\nimport type { Selection } from 'react-aria-components';\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n width: 'full',\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-800',\n});\n\nconst searchFieldStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nconst inputContainerStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst inputStyles = style({\n ...focusRing(),\n width: 'full',\n padding: 8,\n paddingStart: 40,\n font: 'body',\n color: {\n default: '--plum-800',\n '::placeholder': '--plum-400'\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n },\n});\n\nconst searchIconStyles = style({\n position: 'absolute',\n left: 12,\n top: '50%',\n transform: 'translateY(-50%)',\n pointerEvents: 'none',\n color: '--yogurt-600',\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 300,\n overflow: 'auto',\n isolation: 'isolate',\n // width: 'full',\n // minWidth: 300,\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none',\n maxHeight: 384,\n overflowY: 'auto'\n});\n\nconst listBoxItemStyles = style({\n ...focusRing(),\n font: 'body',\n paddingX: 8,\n paddingY: 8,\n borderRadius: 'sm',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocused: '--yogurt-200',\n isPressed: '--yogurt-300',\n },\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocused: '--plum-900',\n isPressed: '--plum-900',\n },\n outline: 'none',\n});\n\nconst selectedItemStyles = style({\n backgroundColor: {\n default: '--plum-100',\n isHovered: '--plum-200',\n isFocused: '--plum-200',\n },\n color: '--plum-900',\n});\n\nconst checkboxStyles = style({\n width: 16,\n height: 16,\n flexShrink: 0,\n});\n\nconst itemTextStyles = style({\n flex: 1,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n whiteSpace: 'nowrap',\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nexport interface AutocompleteItem {\n id: string | number;\n name: string;\n [key: string]: any;\n}\n\nexport interface AutocompleteProps<T extends AutocompleteItem> {\n items: T[];\n selectedKeys?: Selection;\n onSelectionChange?: (keys: Selection) => void;\n selectionMode?: 'none' | 'single' | 'multiple';\n label: string;\n placeholder?: string;\n ariaLabel?: string;\n error?: string;\n getItemText?: (item: T) => string;\n onInputChange?: (value: string) => void; // Callback when search input changes\n isDisabled?: boolean;\n}\n\n/**\n * Autocomplete component using React Aria SearchField + ListBox\n * \n * Features:\n * - SearchField for filtering items\n * - Optional checkboxes for multi-selection\n * - Shows both selected and unselected items when filtered\n * - Selected items highlighted with background color\n * - Keyboard navigation (arrow keys, enter, escape)\n * - Accessible (ARIA labels, focus management)\n * \n * @example\n * ```tsx\n * // Single select\n * <Autocomplete\n * items={sources}\n * selectedKeys={selectedKey}\n * onSelectionChange={setSelectedKey}\n * selectionMode=\"single\"\n * label=\"Select Source\"\n * placeholder=\"Search sources...\"\n * />\n * \n * // Multi select\n * <Autocomplete\n * items={sources}\n * selectedKeys={selectedKeys}\n * onSelectionChange={setSelectedKeys}\n * selectionMode=\"multiple\"\n * label=\"Select Sources\"\n * placeholder=\"Search sources...\"\n * />\n * \n * // No selection (just filtering)\n * <Autocomplete\n * items={sources}\n * selectionMode=\"none\"\n * label=\"Search Sources\"\n * placeholder=\"Search sources...\"\n * />\n * ```\n */\nexport function Autocomplete<T extends AutocompleteItem>({\n items,\n selectedKeys,\n onSelectionChange,\n selectionMode = 'none',\n label,\n placeholder,\n ariaLabel,\n error,\n getItemText = (item) => item.name,\n onInputChange: externalOnInputChange,\n isDisabled = false,\n}: AutocompleteProps<T>) {\n const [inputValue, setInputValue] = useState('');\n const { contains } = useFilter({ sensitivity: 'base' });\n\n // Call external callback when input changes\n const handleInputChange = (value: string) => {\n setInputValue(value);\n externalOnInputChange?.(value);\n };\n\n // Filter items based on input value\n const filteredItems = useMemo(() => {\n if (!inputValue) return items;\n const searchLower = inputValue.toLowerCase();\n return items.filter((item) =>\n getItemText(item).toLowerCase().includes(searchLower)\n );\n }, [items, inputValue, getItemText]);\n\n // Sort to show selected items first when in selection mode\n const sortedItems = useMemo(() => {\n if (selectionMode === 'none' || !selectedKeys) return filteredItems;\n\n return [...filteredItems].sort((a, b) => {\n const aSelected = selectedKeys === 'all' || selectedKeys.has(String(a.id));\n const bSelected = selectedKeys === 'all' || selectedKeys.has(String(b.id));\n if (aSelected && !bSelected) return -1;\n if (!aSelected && bSelected) return 1;\n return 0;\n });\n }, [filteredItems, selectedKeys, selectionMode]);\n\n const showCheckboxes = selectionMode !== 'none';\n\n return (\n <div className={containerStyles}>\n <AriaAutocomplete\n filter={contains}\n inputValue={inputValue}\n onInputChange={handleInputChange}\n >\n <AriaSearchField\n className={searchFieldStyles}\n aria-label={ariaLabel || label}\n isDisabled={isDisabled}\n isInvalid={!!error}\n >\n <Label className={labelStyles}>{label}</Label>\n <div className={inputContainerStyles}>\n <div className={searchIconStyles}>\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <Search styles={iconStyle({ size: 'M', color: '--plum-800' })} />\n </div>\n <Input\n className={inputStyles}\n placeholder={placeholder}\n />\n </div>\n </AriaSearchField>\n\n <ListBox\n className={listBoxStyles}\n items={sortedItems}\n selectionMode={selectionMode}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange}\n aria-label={ariaLabel || `${label} options`}\n >\n {(item) => {\n const isSelected = selectedKeys === 'all' || (selectedKeys && selectedKeys.has(String(item.id)));\n return (\n <ListBoxItem\n key={item.id}\n id={String(item.id)}\n textValue={getItemText(item)}\n className={(renderProps) =>\n `${listBoxItemStyles(renderProps)} ${isSelected ? selectedItemStyles(renderProps) : ''}`\n }\n >\n {showCheckboxes && (\n <div className={checkboxStyles}>\n {isSelected && (\n // @ts-expect-error passing non-standard css value to icon color\n <CheckmarkCircle styles={iconStyle({ size: 'M', color: '--plum-800' })} />\n )}\n </div>\n )}\n <span className={itemTextStyles}>{getItemText(item)}</span>\n </ListBoxItem>\n );\n }}\n </ListBox>\n\n <FieldError className={errorStyles}>{error}</FieldError>\n </AriaAutocomplete>\n </div>\n );\n}\n","import { useMemo } from 'react';\nimport type { Selection } from 'react-aria-components';\nimport { Autocomplete, type AutocompleteItem } from './Autocomplete';\nimport { Table, type ColumnDef } from './Table';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 16,\n width: 'full',\n});\n\nconst tableContainerStyles = style({\n maxHeight: 384,\n overflow: 'auto',\n});\n\nexport interface AutocompleteTableProps<T extends AutocompleteItem> {\n // Autocomplete props\n allItems: T[];\n selectedKeys: Selection;\n onSelectionChange: (keys: Selection) => void;\n selectionMode?: 'single' | 'multiple';\n searchLabel: string;\n searchPlaceholder?: string;\n getItemText?: (item: T) => string;\n\n // Table props\n columns: ColumnDef<T>[];\n tableLabel: string;\n}\n\n/**\n * Composite component combining Autocomplete and Table\n * Useful for search-and-select workflows where selected items are displayed in a table\n * \n * Features:\n * - Autocomplete search field at top\n * - Table below showing selected items\n * - Filters all items in autocomplete\n * - Shows only selected items in table\n * - Scrollable table with max height of 384px\n * \n * @example\n * ```tsx\n * <AutocompleteTable\n * allItems={allSources}\n * selectedKeys={selectedSourceIds}\n * onSelectionChange={setSelectedSourceIds}\n * selectionMode=\"multiple\"\n * searchLabel=\"Search Sources\"\n * searchPlaceholder=\"Search by name or URL...\"\n * columns={[\n * { key: 'name', label: 'Name', isRowHeader: true },\n * { key: 'domain', label: 'Domain' },\n * { key: 'actions', label: 'Actions', align: 'end' },\n * ]}\n * tableLabel=\"Selected Sources\"\n * />\n * ```\n */\nexport function AutocompleteTable<T extends AutocompleteItem>({\n allItems,\n selectedKeys,\n onSelectionChange,\n selectionMode = 'multiple',\n searchLabel,\n searchPlaceholder,\n getItemText,\n columns,\n tableLabel,\n}: AutocompleteTableProps<T>) {\n // Filter to show only selected items in table\n const selectedItems = useMemo(() => {\n if (selectedKeys === 'all') return allItems;\n if (!selectedKeys || selectedKeys.size === 0) return [];\n\n return allItems.filter(item => selectedKeys.has(String(item.id)));\n }, [allItems, selectedKeys]);\n\n return (\n <div className={containerStyles}>\n <Autocomplete\n items={allItems}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange}\n selectionMode={selectionMode}\n label={searchLabel}\n placeholder={searchPlaceholder}\n getItemText={getItemText}\n />\n\n <div className={tableContainerStyles}>\n <Table\n columns={columns}\n data={selectedItems}\n ariaLabel={tableLabel}\n />\n </div>\n </div>\n );\n}\n","import {\n Cell,\n Collection,\n Column,\n Row,\n Table as AriaTable,\n TableBody,\n TableHeader,\n TableLoadMoreItem,\n type Key,\n type Selection,\n type SortDescriptor\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { Checkbox } from './Checkbox';\nimport { ProgressBar } from './ProgressBar';\nimport { ReactNode } from 'react';\nimport { useIntl } from '../lib/i18n-context';\n\nexport interface ColumnDef<T> {\n key: string;\n label: string;\n renderCell: (item: T) => ReactNode;\n width?: string;\n isRowHeader?: boolean;\n align?: 'start' | 'center' | 'end';\n}\n\nexport interface TableProps<T> {\n data: T[];\n columns: ColumnDef<T>[];\n onRowAction?: (key: Key) => void;\n ariaLabel: string;\n sortDescriptor?: SortDescriptor;\n onSortChange?: (descriptor: SortDescriptor) => void;\n selectionMode?: 'none' | 'single' | 'multiple';\n selectedKeys?: Selection;\n onSelectionChange?: (keys: Selection) => void;\n onLoadMore?: () => void;\n isLoadingMore?: boolean;\n isLoading?: boolean;\n emptyMessage?: string;\n}\n\nconst tableContainerStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-200',\n overflow: 'auto',\n maxHeight: {\n default: '80vh'\n }\n});\n\nconst tableStyles = style({\n width: 'full',\n borderCollapse: 'collapse'\n});\n\nconst tableHeaderStyles = style({\n backgroundColor: '--yogurt-200',\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-200',\n position: 'sticky',\n top: 0,\n zIndex: 1\n});\n\nconst columnHeaderStyles = style({\n padding: 16,\n textAlign: {\n default: 'start',\n align: {\n start: 'start',\n center: 'center',\n end: 'end',\n },\n },\n font: 'title-lg',\n color: '--plum-800',\n cursor: {\n allowsSorting: 'pointer'\n }\n});\n\nconst rowStyles = style({\n ...focusRing(),\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-100',\n isSelected: '--plum-100',\n isFocusVisible: '--yogurt-100'\n },\n cursor: {\n default: 'default',\n onRowAction: 'pointer',\n selectionMode: {\n single: 'pointer',\n multiple: 'pointer'\n }\n }\n});\n\nconst cellStyles = style({\n padding: 16,\n textAlign: {\n default: 'start',\n align: {\n start: 'start',\n center: 'center',\n end: 'end',\n },\n },\n font: 'body',\n color: '--plum-800',\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-100'\n});\n\nconst checkboxCellStyles = style({\n padding: 16,\n width: 48,\n borderBottomWidth: 1,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-100'\n});\n\nconst emptyStateStyles = style({\n padding: 48,\n textAlign: 'center',\n font: 'body',\n color: '--plum-800',\n});\n\n/**\n * Reusable Table component using React Aria Components\n * \n * Features:\n * - Collection-based API for better performance\n * - Built-in virtualization support (via RAC)\n * - Keyboard navigation\n * - Screen reader support\n * - Optional sorting\n * - Optional row selection\n * - Optional row actions (clickable rows)\n * - Checkbox column for multiple selection\n * - Hover and selected states\n * \n * @example\n * ```tsx\n * const columns: ColumnDef<Run>[] = [\n * { key: 'id', label: 'ID', renderCell: (run) => `#${run.id}` },\n * { key: 'status', label: 'Status', renderCell: (run) => <Badge variant={run.status}>{run.status}</Badge> }\n * ];\n * \n * <Table\n * data={runs}\n * columns={columns}\n * ariaLabel=\"Scraper runs\"\n * selectionMode=\"multiple\"\n * selectedKeys={selectedIds}\n * onSelectionChange={setSelectedIds}\n * />\n * ```\n */\nexport function Table<T extends { id: number | string }>({\n data,\n columns,\n onRowAction,\n ariaLabel,\n sortDescriptor,\n onSortChange,\n selectionMode = 'none',\n selectedKeys,\n onSelectionChange,\n onLoadMore,\n isLoading = false,\n emptyMessage\n}: TableProps<T>) {\n const intl = useIntl();\n\n return (\n <div className={tableContainerStyles}>\n <AriaTable\n aria-label={ariaLabel}\n selectionMode={selectionMode}\n selectedKeys={selectedKeys}\n onSelectionChange={onSelectionChange as any}\n onRowAction={onRowAction}\n sortDescriptor={sortDescriptor}\n onSortChange={onSortChange}\n className={tableStyles}\n >\n <TableHeader className={tableHeaderStyles}>\n {selectionMode === 'multiple' && (\n <Column className={columnHeaderStyles({ align: 'center', alignItems: 'center' })}>\n <Checkbox slot=\"selection\" size=\"S\" />\n </Column>\n )}\n {columns.map((column) => (\n <Column\n key={column.key}\n id={column.key}\n isRowHeader={column.isRowHeader}\n allowsSorting={!!onSortChange}\n className={columnHeaderStyles({ align: column.align })}\n >\n {column.label}\n </Column>\n ))}\n </TableHeader>\n <TableBody\n renderEmptyState={() => (\n <div className={emptyStateStyles}>\n {isLoading ? (\n <ProgressBar isIndeterminate label={intl.formatMessage({ id: 'table.common.loading' })} />\n ) : (\n emptyMessage || intl.formatMessage({ id: 'common.noData' })\n )}\n </div>\n )}\n >\n <Collection items={data}>\n {(item) => (\n <Row\n key={item.id}\n id={item.id}\n className={(renderProps) => rowStyles({\n ...renderProps,\n onRowAction: !!onRowAction,\n selectionMode\n })}\n >\n {selectionMode === 'multiple' && (\n <Cell className={checkboxCellStyles}>\n <Checkbox slot=\"selection\" size=\"S\" />\n </Cell>\n )}\n {columns.map((column) => (\n <Cell key={column.key} className={cellStyles({ align: column.align })}>\n {column.renderCell(item)}\n </Cell>\n ))}\n </Row>\n )}\n </Collection>\n <TableLoadMoreItem onLoadMore={onLoadMore}>\n <ProgressBar isIndeterminate label={intl.formatMessage({ id: 'table.common.loadingMore' })} />\n </TableLoadMoreItem>\n </TableBody>\n </AriaTable>\n </div>\n );\n}\n\n","import { Checkbox as AriaCheckbox, type CheckboxProps as AriaCheckboxProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport './Checkbox.css';\n\nexport interface CheckboxProps extends Omit<AriaCheckboxProps, 'className' | 'style'> {\n /**\n * The size of the Checkbox.\n * @default 'M'\n */\n size?: 'S' | 'M' | 'L';\n}\n\nconst checkboxStyles = style({\n display: 'flex',\n position: 'relative',\n alignItems: 'center',\n gap: 8,\n font: 'body',\n color: {\n default: '--plum-800',\n isDisabled: 'disabled'\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'default'\n },\n disableTapHighlight: true\n});\n\nconst boxStyles = style({\n ...focusRing(),\n size: {\n default: 20,\n size: {\n S: 16,\n M: 20,\n L: 24\n }\n },\n flexShrink: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderWidth: 2,\n borderStyle: 'solid',\n borderRadius: 'sm',\n transition: 'default',\n backgroundColor: {\n default: 'layer-1',\n isSelected: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isPressed: '--plum-900'\n },\n isDisabled: {\n default: 'gray-100',\n isSelected: 'disabled'\n }\n },\n borderColor: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isPressed: '--plum-900',\n isSelected: 'transparent',\n isDisabled: 'disabled'\n }\n});\n\nconst dashStyles = style({\n width: '60%',\n height: 2,\n backgroundColor: 'white',\n borderRadius: 'sm'\n});\n\n/**\n * Checkbox component for selection within tables and forms.\n * Simplified version based on React Spectrum S2 Checkbox.\n * \n * @example\n * ```tsx\n * <Checkbox isSelected={selected} onChange={setSelected}>\n * Subscribe to newsletter\n * </Checkbox>\n * \n * // In table header/cell (no label)\n * <Checkbox slot=\"selection\" />\n * ```\n */\nexport function Checkbox({ size = 'M', children, ...props }: CheckboxProps) {\n return (\n <AriaCheckbox\n {...props}\n className={(renderProps) => checkboxStyles(renderProps)}\n >\n {(renderProps) => {\n const { isSelected, isIndeterminate } = renderProps;\n\n return (\n <>\n <div className={boxStyles({ ...renderProps, isSelected: isSelected || isIndeterminate, size })}>\n {isIndeterminate ? (\n <div className={dashStyles} />\n ) : isSelected ? (\n <svg viewBox=\"0 0 18 18\" aria-hidden=\"true\" className=\"checkbox-checkmark\">\n <polyline points=\"2 9 7 14 16 4\" className=\"checkbox-checkmark-path checkbox-checkmark-selected\" />\n </svg>\n ) : null}\n </div>\n {children}\n </>\n );\n }}\n </AriaCheckbox>\n );\n}\n","import { ProgressBar as AriaProgressBar } from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport './ProgressBar.css';\n\nconst progressBarStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n width: 'full',\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-800',\n display: 'flex',\n justifyContent: 'space-between',\n});\n\nconst trackStyles = style({\n height: 6,\n backgroundColor: '--yogurt-200',\n borderRadius: 'full',\n overflow: 'hidden',\n position: 'relative',\n});\n\nconst fillStyles = style({\n height: 'full',\n backgroundColor: '--plum-500',\n borderRadius: 'full',\n});\n\nconst indeterminateFillStyles = style({\n height: 'full',\n backgroundColor: '--plum-500',\n borderRadius: 'full',\n width: '40%',\n});\n\ninterface ProgressBarProps {\n label?: string;\n value?: number;\n maxValue?: number;\n isIndeterminate?: boolean;\n showValueLabel?: boolean;\n 'aria-label'?: string;\n}\n\n/**\n * ProgressBar Component\n * \n * Accessible progress indicator using React Aria Components.\n * Supports both determinate (with value) and indeterminate (loading) states.\n * \n * @example\n * // Determinate progress\n * <ProgressBar label=\"Loading...\" value={50} maxValue={100} />\n * \n * @example\n * // Indeterminate progress\n * <ProgressBar label=\"Loading...\" isIndeterminate />\n */\nexport function ProgressBar({\n label,\n value = 0,\n maxValue = 100,\n isIndeterminate = false,\n showValueLabel = true,\n 'aria-label': ariaLabel,\n}: ProgressBarProps) {\n return (\n <AriaProgressBar\n value={isIndeterminate ? undefined : value}\n maxValue={maxValue}\n isIndeterminate={isIndeterminate}\n aria-label={ariaLabel || label}\n className={progressBarStyles}\n >\n {({ percentage, valueText }) => (\n <>\n {label && (\n <div className={labelStyles}>\n <span>{label}</span>\n {!isIndeterminate && showValueLabel && <span>{valueText}</span>}\n </div>\n )}\n <div className={trackStyles}>\n <div\n className={`${isIndeterminate ? 'progress-fill-indeterminate' : 'progress-fill'} ${isIndeterminate ? indeterminateFillStyles : fillStyles}`}\n style={isIndeterminate ? undefined : { width: `${percentage}%` }}\n />\n </div>\n </>\n )}\n </AriaProgressBar>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface AvatarButtonProps extends AriaButtonProps {\n initials: string;\n name?: string;\n showName?: boolean;\n}\n\nconst avatarButtonStyles = style({\n ...focusRing(),\n font: 'title-lg',\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n padding: 8,\n paddingX: 16,\n backgroundColor: {\n default: '--plum-100',\n isHovered: '--plum-200',\n isPressed: '--plum-300',\n isFocusVisible: '--plum-200'\n },\n color: '--plum-800',\n borderRadius: 'full',\n borderWidth: 2,\n borderStyle: 'solid',\n borderColor: '--plum-300',\n cursor: 'pointer'\n});\n\nconst avatarCircleStyles = style({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'full',\n backgroundColor: '--plum-800',\n font: 'title-lg',\n color: '--yogurt-0'\n});\n\nexport const AvatarButton = ({ initials, name, showName = true, ...props }: AvatarButtonProps) => {\n return (\n <AriaButton\n {...props}\n aria-label={!showName && name && !props['aria-label'] ? name : props['aria-label']}\n className={renderProps => avatarButtonStyles(renderProps)}\n >\n <div className={avatarCircleStyles}>\n {initials}\n </div>\n {showName && name && <span>{name}</span>}\n </AriaButton>\n );\n};\n","import { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport type BadgeVariant = 'completed' | 'running' | 'failed' | 'cancelled' | 'pending' | 'plum' | 'cucumber' | 'berry' | 'ginger' | 'peach' | 'yogurt';\n\nexport interface BadgeProps {\n variant?: BadgeVariant;\n children: React.ReactNode;\n}\n\nconst badgeStyles = style({\n paddingY: 4,\n paddingX: 12,\n borderRadius: 'lg',\n font: 'ui-sm',\n display: 'inline-block',\n backgroundColor: {\n variant: {\n completed: '--cucumber-100',\n running: '--ginger-100',\n failed: '--berry-100',\n cancelled: '--plum-100',\n pending: '--plum-100',\n plum: '--plum-100',\n cucumber: '--cucumber-100',\n berry: '--berry-100',\n ginger: '--ginger-100',\n peach: '--peach-100',\n yogurt: '--yogurt-100',\n }\n },\n color: {\n variant: {\n completed: '--cucumber-800',\n running: '--ginger-800',\n failed: '--berry-800',\n cancelled: '--plum-800',\n pending: '--plum-800',\n plum: '--plum-800',\n cucumber: '--cucumber-800',\n berry: '--berry-800',\n ginger: '--ginger-800',\n peach: '--peach-800',\n yogurt: '--yogurt-800',\n }\n }\n});\n\n/**\n * Badge component for displaying status or category with conditional colors\n * \n * @example\n * <Badge variant=\"completed\">Completed</Badge>\n * <Badge variant=\"cucumber\">Vegetable</Badge>\n */\nexport function Badge({ variant, children }: BadgeProps) {\n return (\n <span>{/* Wrapper span to prevent parent display flex's from affecting badge layout */}\n <span className={badgeStyles({ variant })}>\n {children}\n </span>\n </span>\n );\n}\n","import { Button as AriaButton, ButtonProps as AriaButtonProps } from 'react-aria-components';\nimport CloseCircle from '@react-spectrum/s2/icons/CloseCircle';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\n// For props: https://react-spectrum.adobe.com/react-aria/Button.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Button.html#styling\n\nexport interface CloseButtonProps extends Omit<AriaButtonProps, 'onPress'> {\n onClose: () => void;\n}\n\nconst closeButtonStyles = style({\n ...focusRing(),\n padding: 8,\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-100',\n isPressed: '--yogurt-200'\n },\n borderStyle: 'none',\n borderRadius: 'lg',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n forcedColors: {\n backgroundColor: 'ButtonFace',\n color: 'ButtonText',\n borderColor: 'ButtonBorder',\n borderWidth: 1,\n borderStyle: 'solid'\n }\n});\n\nexport const CloseButton = ({ onClose, ...props }: CloseButtonProps) => {\n const intl = useIntl();\n\n return (\n <AriaButton\n {...props}\n onPress={onClose}\n className={renderProps => closeButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'ferment.detail.close' })}\n >\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <CloseCircle styles={iconStyle({ size: 'XL', color: '--plum-800' })} />\n </AriaButton>\n );\n};\n","import { Button, ComboBox as AriaComboBox, ComboBoxRenderProps, FieldError, Group, Input, Label, ListBox, ListBoxItem, Popover, Text } from 'react-aria-components';\nimport type { Key } from 'react-aria-components';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useEffect, useState } from 'react';\nimport ChevronDown from '@react-spectrum/s2/icons/ChevronDown';\n\nconst comboBoxContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n minWidth: 192,\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-800',\n});\n\nconst inputWrapperStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst inputStyles = style({\n ...focusRing(),\n width: 'calc(100% - 44px)', // Padding (12px+32px) and border (1px+1px)\n font: 'body',\n flexGrow: 1,\n flexShrink: 1,\n paddingStart: 12,\n paddingEnd: 32,\n paddingY: 8,\n backgroundColor: {\n default: '--yogurt-100',\n isFocusVisible: '--yogurt-200',\n isDisabled: 'gray-50',\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: {\n default: '--yogurt-400',\n isFocusVisible: '--plum-800',\n isDisabled: 'gray-200',\n },\n color: {\n default: '--plum-800',\n '::placeholder': '--plum-400',\n isDisabled: 'disabled',\n },\n outlineStyle: 'none',\n});\n\nconst buttonStyles = style({\n ...focusRing(),\n verticalAlign: 'middle',\n marginStart: -36, // 32px for button + padding and 4px this padding\n padding: 4,\n borderRadius: 'sm',\n backgroundColor: {\n default: 'transparent',\n isHovered: 'gray-200',\n },\n borderStyle: 'none',\n color: {\n default: 'gray-600',\n isDisabled: 'disabled',\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'default',\n },\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 320,\n overflow: 'auto',\n isolation: 'isolate',\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none',\n});\n\nconst listBoxItemStyles = style({\n font: 'body',\n paddingX: 8,\n paddingY: 4,\n borderRadius: 'sm',\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed',\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isSelected: '--plum-200',\n isFocused: '--yogurt-200',\n isDisabled: 'gray-100',\n },\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocused: '--plum-900',\n isPressed: '--plum-900',\n isSelected: '--plum-900',\n isDisabled: 'disabled',\n },\n outline: 'none',\n});\n\nconst descriptionStyles = style({\n font: 'detail',\n color: '--plum-800',\n});\n\nconst errorStyles = style({\n font: 'detail',\n color: '--berry-700',\n});\n\nexport interface ComboBoxItem {\n id: Key;\n label: string;\n textValue?: string;\n}\n\nexport interface ComboBoxProps {\n label?: string;\n description?: string;\n errorMessage?: string;\n isRequired?: boolean;\n isDisabled?: boolean;\n selectedKey?: Key | null;\n onSelectionChange?: (key: Key | null) => void;\n items: ComboBoxItem[];\n placeholder?: string;\n 'aria-label'?: string;\n inputValue?: string;\n onInputChange?: (value: string) => void;\n}\n\n/**\n * Generic ComboBox component for searchable single-selection.\n * \n * Uses React Aria Components ComboBox for accessible selection with filtering.\n * Reusable for any data type - countries, users, categories, etc.\n * \n * @example\n * ```tsx\n * const items = [\n * { id: 1, label: 'Apple' },\n * { id: 2, label: 'Banana' },\n * { id: 3, label: 'Orange' },\n * ];\n * \n * <ComboBox\n * label=\"Fruit\"\n * items={items}\n * selectedKey={selectedId}\n * onSelectionChange={setSelectedId}\n * placeholder=\"Select a fruit\"\n * />\n * ```\n */\nexport function ComboBox({\n label,\n description,\n errorMessage,\n isRequired = false,\n isDisabled = false,\n selectedKey,\n onSelectionChange,\n items,\n placeholder,\n 'aria-label': ariaLabel,\n inputValue: controlledInputValue,\n onInputChange: controlledOnInputChange,\n}: ComboBoxProps) {\n const [internalInputValue, setInternalInputValue] = useState('');\n\n // Only sync input value with selected item when NOT using controlled input\n // (for async search, the parent controls inputValue)\n useEffect(() => {\n if (controlledInputValue !== undefined) {\n // When using controlled input (search mode), don't sync with selection\n return;\n }\n\n if (selectedKey) {\n const selected = items.find(item => item.id === selectedKey);\n if (selected) {\n setInternalInputValue(selected.label);\n }\n } else {\n setInternalInputValue('');\n }\n }, [selectedKey, items, controlledInputValue]);\n\n // Use controlled inputValue if provided, otherwise use internal state\n const effectiveInputValue = controlledInputValue !== undefined\n ? controlledInputValue\n : internalInputValue;\n\n const effectiveOnInputChange = controlledOnInputChange !== undefined\n ? controlledOnInputChange\n : setInternalInputValue;\n\n return (\n <AriaComboBox\n className={comboBoxContainerStyles}\n inputValue={effectiveInputValue}\n onInputChange={effectiveOnInputChange}\n selectedKey={selectedKey}\n onSelectionChange={onSelectionChange}\n isDisabled={isDisabled}\n isRequired={isRequired}\n isInvalid={!!errorMessage}\n aria-label={ariaLabel || label}\n >\n {label && <Label className={labelStyles}>{label}</Label>}\n {/* TODO - The focusRing is missing. */}\n <div className={inputWrapperStyles}>\n <Input className={inputStyles} placeholder={placeholder} />\n <Button className={buttonStyles}>\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <ChevronDown styles={iconStyle({ color: '--plum-800' })} />\n </Button>\n </div>\n {description && !errorMessage && (\n <Text slot=\"description\" className={descriptionStyles}>\n {description}\n </Text>\n )}\n {errorMessage && <FieldError className={errorStyles}>{errorMessage}</FieldError>}\n <Popover className={popoverStyles}>\n <ListBox className={listBoxStyles} items={items}>\n {(item) => (\n <ListBoxItem\n key={item.id}\n id={item.id}\n textValue={item.textValue || item.label}\n className={listBoxItemStyles}\n >\n {item.label}\n </ListBoxItem>\n )}\n </ListBox>\n </Popover>\n </AriaComboBox>\n );\n}\n","import { useEffect, useState } from 'react';\nimport { Button } from './Button';\nimport { Link } from './Link';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport type { IntlShape } from 'react-intl';\n\nconst bannerStyles = style({\n position: 'fixed',\n bottom: 0,\n left: 0,\n right: 0,\n backgroundColor: '--plum-900',\n color: 'white',\n padding: 16,\n paddingX: {\n default: 16,\n md: 24,\n },\n boxShadow: 'elevated',\n zIndex: 1000,\n display: 'flex',\n flexDirection: {\n default: 'column',\n md: 'row',\n },\n alignItems: {\n default: 'stretch',\n md: 'center',\n },\n justifyContent: 'space-between',\n gap: 16,\n});\n\nconst messageStyles = style({\n font: 'body',\n color: '--yogurt-50',\n margin: 0,\n flexGrow: 1,\n});\n\nconst actionsStyles = style({\n display: 'flex',\n gap: 8,\n alignItems: 'center',\n flexShrink: 0,\n flexWrap: 'wrap',\n});\n\nconst linkStyles = style({\n color: '--yogurt-200',\n textDecorationLine: {\n default: 'underline',\n isHovered: 'none',\n },\n});\n\nexport type ConsentLevel = 'accepted' | 'essential' | 'rejected';\n\nexport interface CookieConsentProps {\n /**\n * Storage key prefix for localStorage items\n * @example 'worldferments' or 'worldferments-admin'\n */\n storagePrefix: string;\n\n /**\n * Internationalization formatter\n */\n intl: IntlShape;\n\n /**\n * Privacy policy URL\n * @default '/privacy-policy'\n */\n privacyPolicyUrl?: string;\n\n /**\n * Additional essential localStorage keys to preserve with \"Essential Only\" consent.\n * Note: Only cookie-consent and {storagePrefix}-theme are preserved by default.\n * Use this for strictly necessary keys only (authentication, security, etc.)\n * @example ['session-token', 'csrf-token']\n */\n additionalEssentialKeys?: string[];\n\n /**\n * Callback when consent level changes\n */\n onConsentChange?: (level: ConsentLevel) => void;\n}\n\n/**\n * Cookie consent banner component\n * \n * Displays a banner at the bottom of the page informing users about cookie usage\n * with three consent levels:\n * \n * - **Accept All**: Allows all cookies including analytics (when implemented)\n * - **Essential Only**: Only theme preference + consent (removes analytics, Vercel cookies, etc.)\n * - **Reject All**: Removes ALL cookies and storage except the consent preference itself\n * \n * Required for GDPR/CCPA compliance. Stores user preference in localStorage.\n * \n * **What gets removed:**\n * - Essential Only: Analytics, Vercel platform cookies, non-essential storage\n * - Reject All: Everything except `cookie-consent` preference\n * \n * **What gets preserved:**\n * - Essential Only: `cookie-consent`, `{storagePrefix}-theme`, `additionalEssentialKeys`\n * - Reject All: Only `cookie-consent`\n * \n * @example\n * // Consumer UI\n * <CookieConsent \n * storagePrefix=\"worldferments\" \n * intl={intl}\n * />\n * \n * // Admin UI\n * <CookieConsent \n * storagePrefix=\"worldferments-admin\" \n * intl={intl}\n * privacyPolicyUrl=\"/admin/privacy-policy\"\n * />\n */\nexport function CookieConsent({\n storagePrefix,\n intl,\n privacyPolicyUrl = '/privacy-policy',\n additionalEssentialKeys = [],\n onConsentChange,\n}: CookieConsentProps) {\n const [isVisible, setIsVisible] = useState(false);\n const consentKey = 'cookie-consent';\n\n useEffect(() => {\n // Check if user has already made a choice\n const consent = localStorage.getItem(consentKey);\n if (consent === null) {\n setIsVisible(true);\n }\n }, []);\n\n const clearNonEssentialStorage = () => {\n // Essential keys that should never be removed (strictly necessary for site operation)\n const essentialKeys = [\n consentKey,\n `${storagePrefix}-theme`, // Theme preference is considered essential UX\n ...additionalEssentialKeys,\n ];\n\n // Remove all localStorage items except essential ones\n const keys = Object.keys(localStorage);\n keys.forEach(key => {\n const isEssential = essentialKeys.includes(key);\n if (!isEssential) {\n localStorage.removeItem(key);\n }\n });\n\n // Clear non-essential cookies (analytics, tracking, Vercel platform cookies)\n if (typeof document !== 'undefined') {\n document.cookie.split(';').forEach(cookie => {\n const name = cookie.split('=')[0].trim();\n // Clear analytics cookies\n if (name.startsWith('_ga') || name.startsWith('_gid')) {\n document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;\n }\n // Clear Vercel platform cookies (not essential for end users)\n if (name.startsWith('__vercel') || name.startsWith('_vercel') || name.startsWith('__prerender')) {\n document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;\n }\n });\n }\n };\n\n const clearAllStorage = () => {\n const consentValue = localStorage.getItem(consentKey);\n localStorage.clear();\n\n // Restore only the consent preference itself\n if (consentValue) {\n localStorage.setItem(consentKey, consentValue);\n }\n\n // Clear ALL cookies including Vercel platform cookies\n if (typeof document !== 'undefined') {\n document.cookie.split(';').forEach(cookie => {\n const name = cookie.split('=')[0].trim();\n // Remove all cookies - user has rejected everything\n document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;\n });\n }\n };\n\n const handleAccept = () => {\n localStorage.setItem(consentKey, 'accepted');\n setIsVisible(false);\n onConsentChange?.('accepted');\n // When analytics is implemented, initialize here\n // Example: initializeGoogleAnalytics();\n };\n\n const handleEssential = () => {\n localStorage.setItem(consentKey, 'essential');\n setIsVisible(false);\n clearNonEssentialStorage();\n onConsentChange?.('essential');\n // Ensure analytics is not initialized\n };\n\n const handleReject = () => {\n localStorage.setItem(consentKey, 'rejected');\n clearAllStorage();\n setIsVisible(false);\n onConsentChange?.('rejected');\n // Ensure analytics is not initialized\n };\n\n if (!isVisible) return null;\n\n return (\n <div className={bannerStyles}>\n <p className={messageStyles}>\n {intl.formatMessage({ id: 'cookieConsent.message' })}{' '}\n <Link href={privacyPolicyUrl} className={linkStyles}>\n {intl.formatMessage({ id: 'cookieConsent.learnMore' })}\n </Link>\n </p>\n <div className={actionsStyles}>\n <Button onPress={handleReject} variant=\"danger\">\n {intl.formatMessage({ id: 'cookieConsent.reject' })}\n </Button>\n <Button onPress={handleEssential} variant=\"secondary\">\n {intl.formatMessage({ id: 'cookieConsent.essential' })}\n </Button>\n <Button onPress={handleAccept} variant=\"primary\">\n {intl.formatMessage({ id: 'cookieConsent.accept' })}\n </Button>\n </div>\n </div>\n );\n}\n\n/**\n * Get the current consent level\n */\nexport function getConsentLevel(): ConsentLevel | null {\n if (typeof window === 'undefined') return null;\n\n const consent = localStorage.getItem('cookie-consent');\n if (consent === 'accepted' || consent === 'essential' || consent === 'rejected') {\n return consent;\n }\n return null;\n}\n\n/**\n * Check if analytics tracking is allowed\n * Analytics should only be enabled if user has accepted all cookies\n */\nexport function canUseAnalytics(): boolean {\n return getConsentLevel() === 'accepted';\n}\n\n/**\n * Check if essential features are allowed\n * Essential features include: authentication, theme preference, language\n */\nexport function canUseEssentials(): boolean {\n const level = getConsentLevel();\n return level === 'accepted' || level === 'essential';\n}\n\n/**\n * Check if any consent has been given\n */\nexport function hasConsented(): boolean {\n return getConsentLevel() !== null;\n}\n","import { Link as AriaLink, LinkProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { ReactNode } from 'react';\n\nconst linkStyles = style({\n ...focusRing(),\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isPressed: '--plum-900',\n colorScheme: {\n light: {\n default: '--yogurt-0',\n isHovered: '--yogurt-100',\n isPressed: '--yogurt-200'\n },\n dark: {\n default: '--plum-900',\n isHovered: '--plum-800',\n isPressed: '--plum-800'\n }\n }\n },\n textDecoration: {\n default: 'underline',\n isQuiet: 'none'\n },\n cursor: 'pointer',\n backgroundColor: {\n default: 'transparent',\n isHovered: 'transparent'\n }\n});\n\ninterface CustomLinkProps extends Omit<LinkProps, 'className'> {\n children: ReactNode;\n isExternal?: boolean;\n isQuiet?: boolean;\n colorScheme?: 'default' | 'light' | 'dark';\n className?: string | ((renderProps: any) => string);\n}\n\n/**\n * Link Component\n * \n * Wraps React Aria Components Link with S2 styling and proper a11y.\n * Automatically handles focus states and keyboard navigation.\n * External links automatically get target=\"_blank\" and rel=\"noopener noreferrer\".\n *\n * @example\n * <Link href=\"/ferments\">Browse ferments</Link>\n * \n * @example\n * <Link href=\"https://example.com\" isExternal>External link</Link>\n *\n * @example\n * // For light text on dark backgrounds (like plum)\n * <Link href=\"/\" colorScheme=\"light\">Light yogurt link</Link>\n *\n * @example\n * // For dark text on light backgrounds\n * <Link href=\"/\" colorScheme=\"dark\">Dark plum link</Link>\n *\n * @example\n * <Link href=\"/\" className={customStyles}>Custom styled link</Link>\n */\nexport function Link({ children, href, isExternal, isQuiet = false, colorScheme = 'default', className, ...props }: CustomLinkProps) {\n // Automatically detect external links if not specified\n const isExternalLink = isExternal ?? (href?.toString().startsWith('http://') || href?.toString().startsWith('https://'));\n\n const externalProps = isExternalLink ? {\n target: '_blank',\n rel: 'noopener noreferrer'\n } : {};\n\n return (\n <AriaLink\n {...props}\n {...externalProps}\n href={href}\n className={className || linkStyles({ isQuiet, colorScheme })}\n >\n {children}\n </AriaLink>\n );\n}\n","import {\n Button as AriaButton,\n Disclosure,\n DisclosureGroup as AriaDisclosureGroup,\n DisclosurePanel,\n DisclosureStateContext,\n Heading\n} from 'react-aria-components';\nimport ChevronRight from '@react-spectrum/s2/icons/ChevronRight';\nimport type { DisclosureGroupProps } from 'react-aria-components';\nimport { style, iconStyle, focusRing } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useContext } from 'react';\n\nimport './DisclosureGroup.css';\n\n// Disclosure container - very thin grey border, full width button appearance\nconst disclosureContainerStyles = style({\n borderStyle: 'none',\n borderWidth: 0,\n borderColor: 'transparent',\n borderRadius: 'lg',\n overflow: 'hidden',\n width: 'full',\n backgroundColor: '--yogurt-100'\n});\n\n// Disclosure trigger button - blends seamlessly with container\nconst disclosureTriggerStyles = style({\n ...focusRing(),\n font: 'title',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n '--iconPrimary': {\n type: 'color',\n value: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n },\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n padding: 16,\n paddingX: 20,\n width: 'full',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isPressed: '--yogurt-300'\n },\n borderStyle: 'none',\n borderRadius: 'none',\n cursor: 'pointer'\n});\n\nconst disclosureHeaderStyles = style({\n margin: 0,\n});\n\n// Disclosure panel - white/light background, blends with button header\nconst disclosurePanelStyles = style({\n padding: {\n default: 0,\n isExpanded: 20,\n },\n backgroundColor: '--yogurt-50',\n borderTopWidth: 1,\n borderTopStyle: 'solid',\n borderColor: '--yogurt-300'\n});\n\nexport interface DisclosureItem {\n id: string;\n title: string;\n content: React.ReactNode;\n onPress?: () => void;\n}\n\ninterface CustomDisclosureGroupProps extends Omit<DisclosureGroupProps, 'children'> {\n items: DisclosureItem[];\n 'aria-label'?: string;\n}\n\n/**\n * Reusable DisclosureGroup component\n * Displays collapsible sections with consistent styling\n * \n * @example\n * <DisclosureGroup\n * items={[\n * {\n * id: 'section1',\n * title: 'Section 1',\n * content: <div>Content goes here</div>\n * }\n * ]}\n * />\n */\nexport function DisclosureGroup({ items, ...props }: CustomDisclosureGroupProps) {\n return (\n <AriaDisclosureGroup {...props} className={disclosureContainerStyles}>\n {items.map((item) => (\n <Disclosure key={item.id} id={item.id}>\n <DisclosureContents key={`${item.id}-contents`} {...item} />\n </Disclosure>\n ))}\n </AriaDisclosureGroup>\n );\n}\n\nconst DisclosureContents = (item: DisclosureItem) => {\n let { isExpanded } = useContext(DisclosureStateContext)!;\n\n return (\n <>\n <Heading className={disclosureHeaderStyles}>\n <AriaButton\n slot=\"trigger\"\n className={(renderProps) =>\n `disclosure-chevron ${disclosureTriggerStyles(renderProps)}`\n }\n onPress={item.onPress}\n >\n <ChevronRight styles={iconStyle({ size: 'S' })} />\n {item.title}\n </AriaButton>\n </Heading>\n <DisclosurePanel className={disclosurePanelStyles({ isExpanded })}>\n {item.content}\n </DisclosurePanel>\n </>\n );\n}\n","import { Badge } from './Badge';\nimport { getCategoryColors } from '../utils/category-colors';\n\nexport interface FermentTypeBadgeProps {\n category: string;\n}\n\n/**\n * Badge component specifically for displaying ferment category types\n * Maps ferment categories to appropriate color variants\n * \n * @example\n * <FermentTypeBadge category=\"dairy\" />\n */\nexport function FermentTypeBadge({ category }: FermentTypeBadgeProps) {\n const colors = getCategoryColors(category);\n \n return (\n <Badge variant={colors.badge}>\n {category}\n </Badge>\n );\n}\n","/**\n * Category Color Utilities\n * Provides consistent color mapping for ferment categories across Map and List views\n */\nimport type { CategoryColors } from '../types/types';\n\n/**\n * Get colors for a ferment category\n * Returns marker color (for map), badge background, and badge text color\n * \n * @example\n * const colors = getCategoryColors('dairy');\n * // { marker: 'var(--plum-500)', badge: 'plum' }\n */\nexport function getCategoryColors(category: string): CategoryColors {\n const colorMap: Record<string, CategoryColors> = {\n dairy: {\n marker: 'var(--yogurt-500)',\n badge: 'peach',\n },\n vegetable: {\n marker: 'var(--cucumber-500)',\n badge: 'cucumber',\n },\n beverage: {\n marker: 'var(--berry-500)',\n badge: 'berry',\n },\n grain: {\n marker: 'var(--ginger-500)',\n badge: 'ginger',\n },\n legume: {\n marker: 'var(--ginger-600)',\n badge: 'ginger',\n },\n protein: {\n marker: 'var(--berry-500)',\n badge: 'berry',\n },\n fruit: {\n marker: 'var(--peach-500)',\n badge: 'peach',\n },\n condiment: {\n marker: 'var(--plum-500)',\n badge: 'plum',\n },\n other: {\n marker: 'var(--yogurt-500)',\n badge: 'peach',\n },\n };\n\n // Default to yogurt colors for unknown categories\n return colorMap[category.toLowerCase()] || {\n marker: 'var(--yogurt-500)',\n badge: 'yogurt'\n };\n}\n","import {\n ToggleButton,\n ToggleButtonGroup,\n SelectionIndicator\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\nexport interface FilterOption {\n value: string;\n label: string;\n}\n\nexport interface FilterTabsProps {\n value: string;\n onChange: (value: string) => void;\n options: FilterOption[];\n ariaLabel?: string;\n}\n\nconst filterContainerStyles = style({\n display: 'flex',\n gap: 0,\n padding: 4,\n backgroundColor: '--yogurt-200',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400'\n});\n\nconst filterButtonStyles = style({\n ...focusRing(),\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n paddingX: 16,\n paddingY: 8,\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n zIndex: 1\n});\n\nconst selectionIndicatorStyles = style({\n position: 'absolute',\n inset: 0,\n borderRadius: 'default',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: 'gray-300',\n boxShadow: 'elevated',\n zIndex: -1,\n backgroundColor: {\n default: 'layer-1',\n isHovered: '--yogurt-300',\n isPressed: '--yogurt-400'\n }\n});\n\n/**\n * FilterTabs component for status filtering\n * Uses React Aria ToggleButtonGroup with SelectionIndicator\n * \n * @example\n * <FilterTabs\n * value={statusFilter}\n * onChange={setStatusFilter}\n * options={[\n * { value: 'all', label: 'All' },\n * { value: 'running', label: 'Running' }\n * ]}\n * ariaLabel=\"Filter by status\"\n * />\n */\nexport function FilterTabs({ value, onChange, options, ariaLabel }: FilterTabsProps) {\n const intl = useIntl();\n\n return (\n <ToggleButtonGroup\n selectionMode=\"single\"\n selectedKeys={new Set([value])}\n disallowEmptySelection\n onSelectionChange={(keys) => {\n const selected = Array.from(keys)[0] as string;\n onChange(selected);\n }}\n aria-label={ariaLabel || intl.formatMessage({ id: 'filterTabs.ariaLabel' })}\n className={filterContainerStyles}\n >\n {options.map((option) => (\n <ToggleButton\n key={option.value}\n id={option.value}\n className={filterButtonStyles}\n >\n {({ isSelected }) => (\n <>\n {option.label}\n {isSelected && <SelectionIndicator className={selectionIndicatorStyles} />}\n </>\n )}\n </ToggleButton>\n ))}\n </ToggleButtonGroup>\n );\n}\n\n","import {\n TextField as AriaTextField,\n Label,\n Text,\n FieldError,\n} from 'react-aria-components';\nimport { style, } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport {\n textFieldDescriptionStyles as labeledValueDescriptionStyles,\n textFieldErrorStyles as labeledValueErrorStyles,\n} from './TextField';\n\nexport interface LabeledValueProps {\n label: string;\n error?: string;\n description?: string;\n isBoldLabel?: boolean;\n labelPosition?: 'top' | 'side';\n children: React.ReactNode;\n}\n\nconst labeledValueStyles = style({\n font: 'body',\n color: '--plum-800',\n display: 'flex',\n flexDirection: {\n default: 'column',\n labelPosition: {\n side: 'row',\n },\n },\n gap: 8,\n alignItems: {\n labelPosition: {\n side: 'center',\n },\n },\n});\n\nconst labeledValueLabelStyles = style({\n font: {\n isBoldLabel: 'title',\n },\n color: '--plum-800',\n});\n\n\nexport function LabeledValue({ label, children, error, description, isBoldLabel = false, labelPosition = 'top', ...props }: LabeledValueProps) {\n return (\n <AriaTextField className={labeledValueStyles({ labelPosition })} isInvalid={!!error} {...props}>\n <Label className={labeledValueLabelStyles({ isBoldLabel })}>{label}{labelPosition === 'side' && ':'}</Label>\n <span>{children}</span>\n {description && !error && (\n <Text slot=\"description\" className={labeledValueDescriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={labeledValueErrorStyles}>{error}</FieldError>\n </AriaTextField>\n );\n}\n","import { Link } from './Link';\nimport type { LinkProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface LinkButtonProps extends Omit<LinkProps, 'className'> {\n variant?: 'primary' | 'secondary' | 'danger' | 'navigation' | 'inline';\n size?: 'S' | 'M' | 'L';\n children: React.ReactNode;\n}\n\nconst linkButtonStyles = style({\n ...focusRing(),\n font: {\n default: 'body',\n size: {\n S: 'body-sm',\n L: 'body-lg',\n },\n },\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: {\n default: 8,\n size: {\n S: 4,\n },\n },\n paddingY: {\n default: 12,\n size: {\n S: 8,\n L: 16,\n },\n variant: {\n navigation: 4,\n inline: 0\n }\n },\n paddingStart: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n inline: 0\n }\n },\n paddingEnd: {\n default: 16,\n size: {\n S: 12,\n L: 24,\n },\n variant: {\n inline: 0\n }\n },\n width: {\n variant: {\n navigation: 'fit'\n }\n },\n color: {\n variant: {\n primary: {\n default: '--plum-900',\n },\n secondary: {\n default: '--plum-800',\n },\n danger: {\n default: '--yogurt-0',\n },\n navigation: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isPressed: '--plum-900'\n },\n inline: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isPressed: '--plum-900'\n }\n },\n forcedColors: 'ButtonText',\n isDisabled: {\n default: 'disabled',\n forcedColors: 'GrayText',\n },\n },\n backgroundColor: {\n default: '--plum-300',\n variant: {\n primary: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisible: '--plum-400',\n isPressed: '--plum-400',\n },\n secondary: {\n default: '--yogurt-200',\n isHovered: '--yogurt-300',\n isFocusVisible: '--yogurt-300',\n isPressed: '--yogurt-400',\n },\n navigation: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n isPressed: '--yogurt-300',\n },\n danger: {\n default: '--berry-700',\n isHovered: '--berry-800',\n isFocusVisible: '--berry-800',\n isPressed: '--berry-900',\n },\n inline: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n isPressed: '--yogurt-300',\n }\n },\n forcedColors: 'ButtonFace',\n isDisabled: {\n default: 'gray-100',\n isDisabled: 'disabled',\n },\n },\n borderRadius: 'lg',\n borderStyle: {\n default: 'none',\n forcedColors: 'solid'\n },\n borderColor: {\n forcedColors: 'ButtonBorder'\n },\n borderWidth: {\n forcedColors: 1\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n },\n textDecorationLine: {\n default: 'none',\n variant: {\n navigation: 'underline',\n inline: 'underline'\n }\n },\n});\n\nexport function LinkButton({\n variant = 'primary',\n size = 'M',\n children,\n ...props\n}: LinkButtonProps) {\n return (\n <Link\n {...props}\n className={(renderProps) => linkButtonStyles({ ...renderProps, variant, size })}\n >\n {children}\n </Link>\n );\n}\n","import ExportTo from '@react-spectrum/s2/icons/ExportTo';\nimport { iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { Link } from './';\nimport {\n Menu as AriaMenu,\n MenuItem as AriaMenuItem,\n MenuTrigger,\n Popover,\n Separator\n} from 'react-aria-components';\nimport { ReactNode } from 'react';\n\nconst menuStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n padding: 4,\n minWidth: 200,\n outline: 'none'\n});\n\nconst menuItemStyles = style({\n display: 'flex',\n alignItems: 'center',\n gap: 12,\n paddingX: 12,\n paddingY: 8,\n borderRadius: 'lg',\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocused: '--plum-900',\n isPressed: '--plum-900'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isFocused: '--yogurt-200'\n },\n textDecoration: 'none',\n cursor: 'pointer',\n outline: 'none'\n});\n\nconst dividerStyles = style({\n height: 1,\n backgroundColor: '--yogurt-400',\n marginY: 4\n});\n\nconst linkOutStyles = style({\n display: 'inline-flex',\n alignItems: 'center',\n marginStart: 'auto'\n});\n\nexport interface MenuItem {\n id: string;\n label: string;\n icon?: ReactNode;\n href?: string;\n onAction?: () => void;\n}\n\nexport interface MenuProps {\n trigger: ReactNode;\n items: MenuItem[];\n placement?: 'bottom' | 'bottom start' | 'bottom end' | 'top' | 'top start' | 'top end';\n 'aria-label'?: string;\n}\n\n/**\n * Menu component styled to match Select component\n * Uses yogurt colors for consistency with Select\n */\nexport function Menu({ trigger, items, placement = 'bottom end', 'aria-label': ariaLabel }: MenuProps) {\n return (\n <MenuTrigger>\n {trigger}\n <Popover placement={placement}>\n <AriaMenu className={menuStyles} aria-label={ariaLabel}>\n {items.map((item, index) =>\n item.id.startsWith('divider') ? (\n <Separator key={`divider-${index}`} className={dividerStyles} />\n ) : (\n <AriaMenuItem\n key={item.id}\n id={item.id}\n href={item.href}\n target={item.href ? '_blank' : undefined}\n rel={item.href ? 'noopener noreferrer' : undefined}\n onAction={item.onAction}\n className={menuItemStyles({ ishref: !!item.href })}\n >\n {item.icon}\n {item.label}\n {item.href && <span className={linkOutStyles}><ExportTo styles={iconStyle({ size: 'S' })} /></span>}\n </AriaMenuItem>\n )\n )}\n </AriaMenu>\n </Popover>\n </MenuTrigger>\n );\n}\n","import { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\nexport interface PasswordValidation {\n minLength: boolean;\n hasLetter: boolean;\n hasNumber: boolean;\n hasSpecial: boolean;\n}\n\nexport interface PasswordStrengthProps {\n validation: PasswordValidation;\n}\n\nconst containerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8\n});\n\nconst requirementStyles = style({\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n font: 'body-sm'\n});\n\nconst indicatorStyles = style({\n width: 16,\n height: 16,\n borderRadius: 'full',\n flexShrink: 0,\n backgroundColor: {\n default: '--gray-300',\n isValid: '--cucumber-500'\n },\n color: {\n default: 'inherit',\n isValid: '--cucumber-700'\n }\n});\n\n/**\n * Visual password strength indicator showing validation requirements\n * Displays checkmarks (green) for met requirements, circles (gray) for unmet\n */\nexport function PasswordStrength({ validation }: PasswordStrengthProps) {\n const intl = useIntl();\n\n const requirements = [\n {\n key: 'minLength',\n met: validation.minLength,\n label: intl.formatMessage({ id: 'validation.password.minLength' })\n },\n {\n key: 'hasLetter',\n met: validation.hasLetter,\n label: intl.formatMessage({ id: 'validation.password.letter' })\n },\n {\n key: 'hasNumber',\n met: validation.hasNumber,\n label: intl.formatMessage({ id: 'validation.password.number' })\n },\n {\n key: 'hasSpecial',\n met: validation.hasSpecial,\n label: intl.formatMessage({ id: 'validation.password.special' })\n }\n ];\n\n console.log('PasswordStrength requirements:', requirements, intl, intl.formatMessage({ id: 'validation.password.special' }));\n\n return (\n <div className={containerStyles}>\n {requirements.map(req => (\n <div key={req.key} className={requirementStyles}>\n <div\n className={indicatorStyles({ isValid: req.met })}\n />\n <span>{req.label}</span>\n </div>\n ))}\n </div>\n );\n}\n","import { Dialog, Heading, Popover as RACPopover } from 'react-aria-components';\nimport type { ReactNode } from 'react';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\nexport interface PopoverProps {\n title: string;\n children: ReactNode;\n}\n\nconst popoverStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-300',\n padding: 24,\n maxWidth: 400,\n boxShadow: 'elevated'\n});\n\nconst dialogStyles = style({\n maxHeight: '33vh',\n overflow: 'auto'\n});\n\nconst headingStyles = style({\n font: 'heading',\n margin: 0,\n marginBottom: 16,\n color: '--plum-800'\n});\n\n/**\n * Popover component with list of items\n * \n * Uses React Aria Components Dialog, Popover, Dialog, and Heading\n * \n * @example\n * <Popover\n * title=\"Countries Without Ferments\"\n * items={countries}\n * />\n */\nexport function Popover({ title, children }: PopoverProps) {\n return (\n <RACPopover className={popoverStyles}>\n <Dialog className={dialogStyles}>\n <Heading slot=\"title\" className={headingStyles}>\n {title}\n </Heading>\n {children}\n </Dialog>\n </RACPopover>\n );\n}\n","import { Radio as AriaRadio, RadioGroup as AriaRadioGroup, Text, FieldError, type RadioGroupProps as AriaRadioGroupProps, type RadioProps as AriaRadioProps } from 'react-aria-components';\nimport { style, focusRing } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useId } from 'react';\n\nexport interface RadioGroupProps extends Omit<AriaRadioGroupProps, 'children'> {\n label: string;\n options: Array<{ id: string; label: string; value: string }>;\n error?: string;\n description?: string;\n}\n\nexport interface RadioProps extends AriaRadioProps {\n children: React.ReactNode;\n}\n\nconst radioGroupStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 8,\n});\n\nconst labelStyles = style({\n font: 'body',\n fontWeight: 'medium',\n color: '--plum-800',\n marginBottom: 8,\n});\n\nconst radioContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 12,\n});\n\nconst radioStyles = style({\n ...focusRing(),\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n font: 'body',\n color: {\n default: '--plum-800',\n isSelected: '--plum-900',\n isHovered: '--plum-900',\n isDisabled: 'disabled',\n },\n cursor: 'pointer',\n padding: 8,\n borderRadius: 'lg',\n backgroundColor: {\n default: 'transparent',\n isHovered: '--plum-50',\n },\n});\n\nconst radioButtonStyles = style({\n width: 20,\n height: 20,\n borderRadius: 'full',\n borderWidth: 2,\n borderStyle: 'solid',\n borderColor: {\n default: '--plum-400',\n isSelected: '--plum-500',\n isHovered: '--plum-300',\n isDisabled: 'disabled',\n },\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n backgroundColor: {\n default: 'layer-1',\n isDisabled: 'gray-100',\n },\n});\n\nconst radioInnerDotStyles = style({\n width: 10,\n height: 10,\n borderRadius: 'full',\n backgroundColor: '--plum-500',\n opacity: {\n default: 0,\n isSelected: 1,\n },\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nconst descriptionStyles = style({\n font: 'body-sm',\n color: '--plum-800',\n});\n\nexport function Radio({ children, ...props }: RadioProps) {\n return (\n <AriaRadio {...props} className={renderProps => radioStyles(renderProps)}>\n {renderProps => (\n <>\n <div className={radioButtonStyles(renderProps)}>\n <div className={radioInnerDotStyles(renderProps)} />\n </div>\n <span>{children}</span>\n </>\n )}\n </AriaRadio>\n );\n}\n\nexport function RadioGroup({ label, options, error, description, ...props }: RadioGroupProps) {\n let ariaLabelId = useId();\n\n return (\n <AriaRadioGroup {...props} isInvalid={!!error} className={radioGroupStyles} aria-labelledby={ariaLabelId}>\n <span id={ariaLabelId} className={labelStyles}>{label}</span>\n <div className={radioContainerStyles}>\n {options.map(option => (\n <Radio key={option.id} value={option.value}>\n {option.label}\n </Radio>\n ))}\n </div>\n {description && !error && (\n <Text slot=\"description\" className={descriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={errorStyles}>{error}</FieldError>\n </AriaRadioGroup>\n );\n}\n","import { SearchField as AriaSearchField, Button, Input, Label, Text, FieldError, } from 'react-aria-components';\nimport Close from '@react-spectrum/s2/icons/Close';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { raw } from '../utils/styleMacro' with { type: 'macro' };\nimport Search from '@react-spectrum/s2/icons/Search';\nimport { useIntl } from '../lib/i18n-context';\n\n\nconst searchFieldContainerStyles = style({\n flex: {\n default: 'none',\n md: 1,\n },\n});\n\nconst searchFieldStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nconst searchInputContainerStyles = style({\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n});\n\nconst searchInputStyles = style({\n ...focusRing(),\n width: 'full',\n padding: 8,\n paddingStart: 40,\n font: 'body',\n color: {\n default: '--plum-800',\n '::placeholder': '--plum-400'\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n },\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-800',\n marginBottom: 4,\n});\n\nconst searchIconStyles = {\n position: 'absolute' as const,\n left: 12,\n top: '50%',\n transform: 'translateY(-50%)',\n pointerEvents: 'none' as const,\n color: '--yogurt-600',\n};\n\nconst clearButtonStyles = style({\n ...focusRing(),\n position: 'absolute',\n right: 8,\n top: '50%',\n transform: 'translateY(-50%)',\n padding: 4,\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n color: {\n default: '--yogurt-600',\n isHovered: '--yogurt-700',\n isPressed: '--yogurt-800',\n },\n});\n\nconst descriptionStyles = style({\n font: 'body-sm',\n color: '--plum-800',\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nexport interface SearchFieldProps {\n value: string;\n onChange: (value: string) => void;\n label: string;\n placeholder: string;\n ariaLabel: string;\n description?: string;\n error?: string;\n}\n\n/**\n * Reusable SearchField component using React Aria Components\n * Styled with S2 design tokens and includes search icon\n * \n * @example\n * <SearchField\n * value={searchQuery}\n * onChange={setSearchTerm}\n * label=\"Search\"\n * placeholder=\"Search ferments...\"\n * ariaLabel=\"Search ferments by name or description\"\n * />\n */\nexport function SearchField({\n value,\n onChange,\n label,\n placeholder,\n ariaLabel,\n description,\n error,\n}: SearchFieldProps) {\n const intl = useIntl();\n\n return (\n <div className={searchFieldContainerStyles}>\n <AriaSearchField\n value={value}\n onChange={onChange}\n className={searchFieldStyles}\n isInvalid={!!error}\n aria-label={ariaLabel}\n >\n {({ isEmpty }) => (\n <>\n <Label className={labelStyles}>{label}</Label>\n <div className={searchInputContainerStyles}>\n <div style={searchIconStyles}>\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <Search styles={iconStyle({ color: '--plum-800' })} />\n </div>\n <Input\n className={(renderProps) => searchInputStyles(renderProps) + ' ' + raw('&::-webkit-search-cancel-button { display: none }', 'custom_layer')}\n placeholder={placeholder}\n />\n {!isEmpty && <Button className={clearButtonStyles} aria-label={intl.formatMessage({ id: 'ferments.search.clear' })}>\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <Close styles={iconStyle({ size: 'S', color: '--plum-800' })} />\n </Button>}\n </div>\n {description && !error && (\n <Text slot=\"description\" className={descriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={errorStyles}>{error}</FieldError>\n </>\n )}\n </AriaSearchField>\n </div>\n );\n}\n","import { useRouter } from 'next/navigation';\nimport {\n ToggleButton,\n ToggleButtonGroup,\n SelectionIndicator\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\nimport GlobeGrid from '@react-spectrum/s2/icons/GlobeGrid';\nimport ListIcon from '@react-spectrum/s2/icons/ViewList';\n\nconst segmentedControlStyles = style({\n display: 'flex',\n gap: 0,\n padding: 4,\n backgroundColor: '--yogurt-200',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n});\n\nconst toggleButtonStyles = style({\n ...focusRing(),\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 8,\n paddingX: 16,\n paddingY: 8,\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n zIndex: 1,\n '--iconPrimary': {\n type: 'color',\n value: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n }\n});\n\nconst selectionIndicatorStyles = style({\n position: 'absolute',\n inset: 0,\n borderRadius: 'default',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-300',\n boxShadow: 'elevated',\n zIndex: -1,\n color: {\n isDisabled: 'disabled',\n forcedColors: 'ButtonText'\n },\n backgroundColor: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisible: '--plum-400',\n isPressed: '--plum-400',\n isDisabled: 'gray-100',\n forcedColors: 'ButtonFace'\n },\n});\n\ninterface SegmentedControlProps {\n value: 'map' | 'list';\n}\n\n/**\n * SegmentedControl for Map/List view toggle\n * Uses RAC ToggleButtonGroup for accessible segmented control\n */\nexport function SegmentedControl({ value }: SegmentedControlProps) {\n const intl = useIntl();\n const router = useRouter();\n\n const handleSelectionChange = (newValue: 'map' | 'list') => {\n if (newValue === 'map') {\n router.push('/');\n } else {\n router.push('/ferments');\n }\n };\n\n return (\n <ToggleButtonGroup\n selectionMode=\"single\"\n selectedKeys={new Set([value])}\n disallowEmptySelection\n onSelectionChange={(keys) => {\n const selected = Array.from(keys)[0] as 'map' | 'list';\n handleSelectionChange(selected);\n }}\n aria-label={intl.formatMessage({ id: 'nav.viewToggle.ariaLabel' })}\n className={segmentedControlStyles}\n >\n <ToggleButton\n id=\"map\"\n className={(renderProps) => toggleButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'nav.map' })}\n >\n {(renderProps) => (\n <>\n <SelectionIndicator className={selectionIndicatorStyles(renderProps)} />\n <GlobeGrid aria-hidden=\"true\" />\n <span>{intl.formatMessage({ id: 'nav.map' })}</span>\n </>\n )}\n </ToggleButton>\n\n <ToggleButton\n id=\"list\"\n className={(renderProps) => toggleButtonStyles(renderProps)}\n aria-label={intl.formatMessage({ id: 'nav.list' })}\n >\n {(renderProps) => (\n <>\n <SelectionIndicator className={selectionIndicatorStyles(renderProps)} />\n <ListIcon aria-hidden=\"true\" />\n <span>{intl.formatMessage({ id: 'nav.list' })}</span>\n </>\n )}\n </ToggleButton>\n </ToggleButtonGroup>\n );\n}\n","import {\n Tab,\n TabList,\n TabPanel,\n Tabs,\n type TabListProps,\n type TabProps,\n type TabsProps\n} from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport type { ReactNode } from 'react';\n\nconst tabListStyles = style({\n display: 'flex',\n gap: 0,\n padding: 4,\n backgroundColor: '--yogurt-200',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n marginBottom: 24\n});\n\nconst tabStyles = style({\n ...focusRing(),\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n paddingX: 16,\n paddingY: 8,\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900'\n },\n backgroundColor: 'transparent',\n borderStyle: 'none',\n borderRadius: 'default',\n cursor: 'pointer',\n zIndex: 1\n});\n\nconst selectedIndicatorStyles = style({\n position: 'absolute',\n inset: 0,\n borderRadius: 'default',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: 'gray-300',\n boxShadow: 'elevated',\n zIndex: -1,\n backgroundColor: {\n default: 'layer-1',\n isHovered: '--yogurt-300',\n isPressed: '--yogurt-400'\n }\n});\n\nexport interface SegmentedTabsProps extends Omit<TabsProps, 'children'> {\n children: ReactNode;\n ariaLabel?: string;\n}\n\n/**\n * SegmentedTabs - Tabs with segmented control styling\n * \n * Usage:\n * ```tsx\n * <SegmentedTabs selectedKey={tab} onSelectionChange={setTab}>\n * <TabList aria-label=\"Options\">\n * <Tab id=\"one\">One</Tab>\n * <Tab id=\"two\">Two</Tab>\n * </TabList>\n * <TabPanel id=\"one\">Content 1</TabPanel>\n * <TabPanel id=\"two\">Content 2</TabPanel>\n * </SegmentedTabs>\n * ```\n */\nexport function SegmentedTabs({ children, ariaLabel, ...props }: SegmentedTabsProps) {\n return (\n <Tabs {...props}>\n {children}\n </Tabs>\n );\n}\n\nexport function SegmentedTabList({ children, ...props }: TabListProps<object>) {\n return (\n <TabList {...props} className={tabListStyles}>\n {children}\n </TabList>\n );\n}\n\nexport function SegmentedTab({ children, ...props }: TabProps) {\n return (\n <Tab {...props} className={tabStyles}>\n {({ isSelected }) => (\n <>\n {isSelected && <div className={selectedIndicatorStyles({ isSelected })} />}\n {children}\n </>\n )}\n </Tab>\n );\n}\n\n// Re-export TabPanel for convenience\nexport { TabPanel };\n","import {\n Select as AriaSelect,\n SelectProps,\n Label,\n Button,\n SelectValue,\n Popover,\n ListBox,\n ListBoxItem,\n Text,\n FieldError\n} from 'react-aria-components';\nimport type { SelectOption } from '../utils/select-options';\nimport { focusRing, iconStyle, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport ChevronDown from '@react-spectrum/s2/icons/ChevronDown';\n\nconst selectContainerStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n minWidth: 200\n});\n\nconst labelStyles = style({\n font: 'body',\n color: '--plum-800',\n});\n\nconst descriptionStyles = style({\n font: 'body-sm',\n color: '--plum-800',\n});\n\nconst errorStyles = style({\n font: 'body-sm',\n color: '--berry-600',\n});\n\nconst buttonStyles = style({\n ...focusRing(),\n paddingX: 12,\n paddingY: 8,\n backgroundColor: {\n default: '--yogurt-100',\n isHovered: '--yogurt-200',\n isFocusVisible: '--yogurt-200',\n isPressed: '--yogurt-300'\n },\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n font: 'body',\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocused: '--plum-900',\n isPressed: '--plum-900',\n '::placeholder': '--plum-400'\n },\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8,\n minWidth: 200\n});\n\nconst popoverStyles = style({\n backgroundColor: '--yogurt-50',\n borderRadius: 'lg',\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--yogurt-400',\n boxShadow: 'elevated',\n marginTop: 4,\n maxHeight: 300,\n overflow: 'auto',\n isolation: 'isolate',\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none'\n});\n\nconst listBoxItemStyles = style({\n font: 'body',\n paddingX: 8,\n paddingY: 4,\n borderRadius: 'sm',\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--yogurt-200',\n isSelected: '--plum-200',\n isFocused: '--yogurt-200',\n isDisabled: 'gray-100'\n },\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocused: '--plum-900',\n isPressed: '--plum-900',\n isSelected: '--plum-900',\n isDisabled: 'disabled'\n },\n outline: 'none'\n});\n\ninterface CustomSelectProps extends Omit<SelectProps<SelectOption<string | null>>, 'children'> {\n label: string;\n options: SelectOption<string | null>[];\n placeholder?: string;\n description?: string;\n error?: string;\n}\n\n/**\n * Reusable Select component following RAC pattern\n * Label is inside the Select wrapper for proper a11y\n */\nexport function Select({ label, options, placeholder, description, error, ...props }: CustomSelectProps) {\n return (\n <AriaSelect {...props} isInvalid={!!error} className={selectContainerStyles}>\n <Label className={labelStyles}>{label}</Label>\n <Button className={buttonStyles}>\n <SelectValue />\n {/* @ts-expect-error passing non-standard css value to icon color */}\n <ChevronDown aria-hidden=\"true\" styles={iconStyle({ color: '--plum-800' })} />\n </Button>\n {description && !error && (\n <Text slot=\"description\" className={descriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={errorStyles}>{error}</FieldError>\n <Popover className={popoverStyles}>\n <ListBox className={listBoxStyles} items={options}>\n {(item) => (\n <ListBoxItem\n key={item.id}\n id={item.id}\n textValue={item.label}\n isDisabled={item.isDisabled}\n className={listBoxItemStyles}\n >\n {item.label}\n </ListBoxItem>\n )}\n </ListBox>\n </Popover>\n </AriaSelect>\n );\n}\n","import { Badge } from './Badge';\nimport { getSourceCategoryColors } from '../utils/source-type-colors';\nimport type { SourceCategory } from '../types/types';\n\nexport interface SourceCategoryBadgeProps {\n sourceCategory: SourceCategory;\n}\n\n/**\n * Badge component specifically for displaying ferment source categories\n * Maps source categories (recipes, history, tools, etc.) to appropriate color variants\n * \n * @example\n * <SourceCategoryBadge sourceCategory=\"recipes\" />\n */\nexport function SourceCategoryBadge({ sourceCategory }: SourceCategoryBadgeProps) {\n const colors = getSourceCategoryColors(sourceCategory);\n\n return (\n <Badge variant={colors.badge}>\n {sourceCategory}\n </Badge>\n );\n}\n","/**\n * Source Category Color Utilities\n * Provides consistent color mapping for ferment source categories\n */\nimport type { BadgeVariant } from '../components/Badge';\nimport type { SourceCategory } from '../types/types';\n\nexport interface SourceCategoryColors {\n badge: BadgeVariant;\n}\n\n/**\n * Get badge color for a ferment source category\n * Returns badge variant for consistent color scheme\n * \n * @example\n * const colors = getSourceCategoryColors('recipes');\n * // { badge: 'ginger' }\n */\nexport function getSourceCategoryColors(sourceCategory: SourceCategory): SourceCategoryColors {\n const colorMap: Record<SourceCategory, SourceCategoryColors> = {\n about: {\n badge: 'cucumber', // Information - green\n },\n history: {\n badge: 'peach', // Heritage - orange\n },\n recipes: {\n badge: 'ginger', // Cooking - warm spice\n },\n tools: {\n badge: 'yogurt', // Equipment - neutral\n },\n starters: {\n badge: 'ginger', // Living cultures - warm spice\n },\n culture: {\n badge: 'peach', // Traditions - orange\n },\n scientific: {\n badge: 'berry', // Research - red\n },\n commercial: {\n badge: 'yogurt', // Business - neutral\n },\n course: {\n badge: 'plum', // Education - purple\n },\n general: {\n badge: 'cucumber', // Default - green\n },\n };\n\n return colorMap[sourceCategory] || { badge: 'yogurt' };\n}\n","import { Button } from './Button';\nimport { DialogTrigger, ListBox, ListBoxItem } from 'react-aria-components';\nimport { Key, ReactNode } from 'react';\nimport { Popover } from './Popover';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { useIntl } from '../lib/i18n-context';\n\nexport interface StatCardProps {\n title: string;\n value: ReactNode;\n description?: string;\n list?: Array<{ id: string | number | Key; name: string }>;\n listTitle?: string;\n actions?: ReactNode;\n listItemActions?: (item: { id: string | number | Key; name: string }) => ReactNode;\n}\n\nconst cardStyles = style({\n backgroundColor: 'layer-1',\n borderRadius: 'xl',\n padding: 24,\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: '--plum-200',\n display: 'flex',\n flexDirection: 'column',\n gap: 16,\n alignItems: 'center',\n textAlign: 'center'\n});\n\nconst cardTitleStyles = style({\n font: 'title-lg',\n color: '--plum-800',\n margin: 0\n});\n\nconst cardValueStyles = style({\n font: 'heading-2xl',\n color: '--plum-800',\n});\n\nconst cardDescStyles = style({\n font: 'body',\n color: '--plum-800',\n margin: 0\n});\n\nconst listPreviewStyles = style({\n display: 'flex',\n flexDirection: 'row',\n gap: 16,\n alignItems: 'center',\n font: 'body-sm',\n color: '--plum-800',\n margin: 0,\n});\n\nconst listBoxStyles = style({\n padding: 4,\n outline: 'none'\n});\n\nconst listBoxItemStyles = style({\n font: 'body',\n paddingX: 8,\n paddingY: 4,\n borderRadius: 'sm',\n backgroundColor: 'transparent',\n color: '--plum-800',\n outline: 'none',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 8\n});\n\nconst actionsContainerStyles = style({\n display: 'flex',\n gap: 8,\n justifyContent: 'center'\n});\n\n/**\n * StatCard component for displaying dashboard statistics\n *\n * @example\n * <StatCard \n * title=\"Total Sources\"\n * value={42}\n * description=\"Active scraping sources\"\n * />\n *\n * @example\n * // With icon as value\n * <StatCard \n * title=\"System Status\"\n * value={<CheckmarkCircle />}\n * description=\"All systems operational\"\n * />\n *\n * @example\n * // With list and popover\n * <StatCard\n * title=\"Countries Without Ferments\"\n * value={5}\n * list={countries}\n * listTitle=\"Countries Without Ferments\"\n * />\n */\nexport function StatCard({ title, value, description, list, listTitle, actions, listItemActions }: StatCardProps) {\n const intl = useIntl();\n\n return (\n <div className={cardStyles}>\n <h3 className={cardTitleStyles}>{title}</h3>\n <div className={cardValueStyles}>{value}</div>\n {description && <p className={cardDescStyles}>{description}</p>}\n {list && list.length > 0 && (\n <div className={listPreviewStyles}>\n <DialogTrigger>\n <Button variant=\"navigation\">{intl.formatMessage({ id: 'statCard.viewAll' })}</Button>\n <Popover\n title={listTitle || title}>\n <ListBox className={listBoxStyles}>\n {list.map(item => (\n <ListBoxItem key={item.id} className={listBoxItemStyles}>\n <span>{item.name}</span>\n {listItemActions?.(item)}\n </ListBoxItem>\n ))}\n </ListBox>\n </Popover>\n </DialogTrigger>\n {actions && (\n <div className={actionsContainerStyles}>\n {actions}\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n","import { Switch as AriaSwitch, SwitchProps as AriaSwitchProps } from 'react-aria-components';\nimport { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\n\n// For props: https://react-spectrum.adobe.com/react-aria/Switch.html#props\n// For styling: https://react-spectrum.adobe.com/react-aria/Switch.html#styling\n\nconst switchWrapperStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 4,\n});\n\nconst switchContainerStyles = style({\n ...focusRing(),\n display: 'flex',\n alignItems: 'center',\n gap: 8,\n paddingEnd: 2,\n cursor: 'pointer',\n font: {\n default: 'ui',\n size: {\n S: 'ui-sm',\n M: 'ui',\n L: 'ui-lg'\n }\n },\n color: {\n default: '--plum-800',\n isHovered: '--plum-900',\n isFocusVisible: '--plum-900',\n isPressed: '--plum-900',\n isDisabled: '--plum-400'\n },\n borderRadius: 'full'\n});\n\nconst switchTrackStyles = style({\n width: {\n default: 44,\n size: {\n S: 36,\n M: 44,\n L: 52\n }\n },\n height: {\n default: 24,\n size: {\n S: 20,\n M: 24,\n L: 28\n }\n },\n borderRadius: 'full',\n padding: 2,\n display: 'flex',\n alignItems: 'center',\n backgroundColor: {\n default: '--yogurt-400',\n isSelected: '--plum-500',\n isDisabled: 'disabled'\n },\n borderStyle: {\n forcedColors: 'solid'\n },\n borderColor: {\n forcedColors: 'ButtonBorder'\n },\n borderWidth: {\n forcedColors: 1\n }\n});\n\nconst switchThumbStyles = style({\n width: {\n default: 20,\n size: {\n S: 16,\n M: 20,\n L: 24\n }\n },\n height: {\n default: 20,\n size: {\n S: 16,\n M: 20,\n L: 24\n }\n },\n borderRadius: 'full',\n backgroundColor: {\n default: '--yogurt-50',\n forcedColors: 'ButtonText'\n },\n transform: {\n default: 'translateX(0)',\n isSelected: {\n default: 'translateX(22px)',\n size: {\n S: 'translateX(16px)',\n M: 'translateX(22px)',\n L: 'translateX(24px)'\n }\n }\n }\n});\n\nconst errorStyles = style({\n font: {\n default: 'ui-sm',\n size: {\n S: 'ui-xs',\n M: 'ui-sm',\n L: 'ui'\n }\n },\n color: '--berry-600',\n});\n\nconst descriptionStyles = style({\n font: {\n default: 'ui-sm',\n size: {\n S: 'ui-xs',\n M: 'ui-sm',\n L: 'ui'\n }\n },\n color: '--plum-800',\n});\n\nexport interface SwitchProps extends Omit<AriaSwitchProps, 'children'> {\n children?: React.ReactNode;\n size?: 'S' | 'M' | 'L';\n description?: string;\n error?: string;\n}\n\nexport const Switch = ({ children, size = 'M', description, isDisabled, error, ...props }: SwitchProps) => {\n return (\n <div className={switchWrapperStyles}>\n <AriaSwitch\n isDisabled={isDisabled}\n {...props}\n className={renderProps => switchContainerStyles({ ...renderProps, size, isDisabled })}\n >\n {renderProps => (\n <>\n <div className={switchTrackStyles({ ...renderProps, size, isDisabled })}>\n <div className={switchThumbStyles({ ...renderProps, size, isDisabled })} />\n </div>\n {children}\n </>\n )}\n </AriaSwitch>\n {error && isDisabled && (\n <div className={errorStyles({ size })}>{error}</div>\n )}\n {description && (\n <div className={descriptionStyles({ size })}>{description}</div>\n )}\n </div>\n );\n};","import { focusRing, style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { Tabs as AriaTabs, TabList as AriaTabList, Tab as AriaTab, TabPanel as AriaTabPanel } from 'react-aria-components';\nimport type { TabListProps, TabPanelProps, TabProps, TabsProps } from 'react-aria-components';\n\nconst tabsStyles = style({\n display: 'flex',\n flexDirection: 'column',\n gap: 0\n});\n\nconst tabListStyles = style({\n display: 'flex',\n gap: 4,\n borderBottomWidth: 2,\n borderBottomStyle: 'solid',\n borderBottomColor: '--plum-200',\n marginBottom: 24,\n overflowX: 'auto',\n overflowY: 'hidden',\n WebkitOverflowScrolling: 'touch'\n});\n\nconst tabStyles = style({\n ...focusRing(),\n paddingX: 20,\n paddingY: 12,\n font: 'body',\n fontWeight: {\n isSelected: 'bold'\n },\n color: {\n default: '--plum-800',\n isSelected: '--plum-900',\n isHovered: '--plum-900',\n isDisabled: 'disabled'\n },\n backgroundColor: {\n default: 'transparent',\n isHovered: '--plum-50',\n isSelected: '--plum-100'\n },\n borderWidth: 0,\n borderBottomWidth: 2,\n borderStyle: 'solid',\n borderColor: {\n default: 'transparent',\n isSelected: '--plum-800',\n isHovered: '--plum-900'\n },\n cursor: {\n default: 'pointer',\n isDisabled: 'not-allowed'\n },\n textDecoration: 'none',\n transitionProperty: 'all',\n transitionDuration: '200ms'\n});\n\nconst tabPanelStyles = style({\n paddingY: 0,\n outlineStyle: 'none'\n});\n\nexport function Tabs({ children, ...props }: TabsProps) {\n return (\n <AriaTabs className={tabsStyles} {...props}>\n {children}\n </AriaTabs>\n );\n}\n\nexport function TabList<T extends object>({ children, ...props }: TabListProps<T>) {\n return (\n <AriaTabList className={tabListStyles} {...props}>\n {children}\n </AriaTabList>\n );\n}\n\nexport function Tab({ children, ...props }: TabProps) {\n return (\n <AriaTab className={tabStyles} {...props}>\n {children}\n </AriaTab>\n );\n}\n\nexport function TabPanel({ children, ...props }: TabPanelProps) {\n return (\n <AriaTabPanel className={tabPanelStyles} {...props}>\n {children}\n </AriaTabPanel>\n );\n}\n","import {\n TextArea as AriaTextArea,\n TextField as AriaTextField,\n Label,\n Text,\n FieldError,\n} from 'react-aria-components';\nimport { style } from '@react-spectrum/s2/style' with { type: 'macro' };\nimport { textFieldErrorStyles, textFieldLabelStyles, textFieldDescriptionStyles, type TextFieldProps, textFieldStyles } from './TextField';\nimport { textFieldInputStyles } from '../utils/styleUtils' with { type: 'macro' };\nimport { useRef, useEffect } from 'react';\nimport { safeWindow } from '../utils/browser';\n\nexport interface TextAreaProps extends Omit<TextFieldProps, 'type'> { }\n\nconst textareaStyles = style({\n ...textFieldInputStyles(),\n resize: 'none',\n minHeight: 75, // ~3 lines\n maxHeight: '75vh',\n overflowY: 'auto',\n});\n\nexport function TextArea({ label, error, description, placeholder, value, ...props }: TextAreaProps) {\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n // Auto-grow on value change\n useEffect(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n // Reset height to recalculate\n textarea.style.height = '75px';\n\n // Set to scrollHeight (content height)\n const newHeight = Math.min(textarea.scrollHeight, safeWindow.innerHeight * 0.75 || 64);\n textarea.style.height = `${newHeight}px`;\n }, [value]);\n\n return (\n <AriaTextField className={textFieldStyles} isInvalid={!!error} {...props} value={value}>\n <Label className={textFieldLabelStyles}>{label}</Label>\n <AriaTextArea\n ref={textareaRef}\n className={textareaStyles}\n placeholder={placeholder}\n />\n {description && !error && (\n <Text slot=\"description\" className={textFieldDescriptionStyles}>\n {description}\n </Text>\n )}\n <FieldError className={textFieldErrorStyles}>{error}</FieldError>\n </AriaTextField>\n );\n}\n","/**\n * Safe browser API wrappers that work in SSR\n */\n\nexport const isBrowser = typeof window !== 'undefined';\nexport const isDocument = typeof document !== 'undefined';\n\nexport const safeWindow = {\n get innerHeight() {\n return isBrowser ? window.innerHeight : 0;\n },\n\n get location() {\n return isBrowser ? window.location : null;\n },\n\n matchMedia: (query: string) => {\n if (!isBrowser) {\n return { matches: false, media: query, addEventListener: () => { }, removeEventListener: () => { } };\n }\n return window.matchMedia(query);\n },\n\n reload: () => {\n if (isBrowser) window.location.reload();\n }\n};\n\nexport const safeDocument = {\n get documentElement() {\n return isDocument ? document.documentElement : null;\n },\n\n get referrer() {\n return isDocument ? document.referrer : '';\n },\n\n createElement: (tag: string) => {\n return isDocument ? document.createElement(tag) : HTMLUnknownElement.prototype;\n }\n};\n\nexport const safeLocalStorage = {\n getItem: (key: string): string | null => {\n if (!isBrowser) return null;\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n },\n\n setItem: (key: string, value: string): void => {\n if (!isBrowser) return;\n try {\n localStorage.setItem(key, value);\n } catch {\n // Silent fail\n }\n }\n};\n","// Contexts & Providers\nexport { ThemeProvider, useTheme } from './theme-context';\nexport { I18nProvider } from './i18n-provider';\nexport { useIntl } from './i18n-context';\nexport { FilterProvider, useFilters } from './filter-context';\n","import { createContext, useContext, useState, useEffect, ReactNode } from 'react';\nimport { isBrowser, safeWindow, safeDocument, safeLocalStorage } from '../utils/browser';\n\ntype ColorScheme = 'light' | 'dark';\n\ninterface ThemeContextType {\n colorScheme: ColorScheme;\n toggleColorScheme: () => void;\n}\n\nconst ThemeContext = createContext<ThemeContextType | undefined>(undefined);\n\nconst THEME_STORAGE_KEY = 'worldferments-theme';\n\n// Get initial theme synchronously to prevent flicker\nfunction getInitialTheme(): ColorScheme {\n if (!isBrowser) return 'light';\n\n // First check if blocking script already set the attribute\n const htmlElement = safeDocument.documentElement;\n if (htmlElement) {\n const htmlAttribute = htmlElement.getAttribute('data-color-scheme') as ColorScheme | null;\n if (htmlAttribute && (htmlAttribute === 'light' || htmlAttribute === 'dark')) {\n return htmlAttribute;\n }\n }\n\n // Fallback to localStorage\n const stored = safeLocalStorage.getItem(THEME_STORAGE_KEY) as ColorScheme | null;\n if (stored && (stored === 'light' || stored === 'dark')) {\n return stored;\n }\n\n // Final fallback to system preference\n return safeWindow.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nexport function ThemeProvider({ children }: { children: ReactNode }) {\n // Always start with 'light' to match server render\n // Will update to actual theme after mount\n const [colorScheme, setColorScheme] = useState<ColorScheme>('light');\n const [mounted, setMounted] = useState(false);\n\n // Set mounted flag and sync to actual theme on client\n useEffect(() => {\n setMounted(true);\n const actual = getInitialTheme();\n setColorScheme(actual);\n }, []);\n\n // Update html attribute and localStorage when scheme changes\n useEffect(() => {\n if (!mounted || !isBrowser) return;\n\n const htmlElement = safeDocument.documentElement;\n if (htmlElement) {\n htmlElement.setAttribute('data-color-scheme', colorScheme);\n }\n safeLocalStorage.setItem(THEME_STORAGE_KEY, colorScheme);\n }, [colorScheme, mounted]);\n\n const toggleColorScheme = () => {\n setColorScheme(prev => prev === 'light' ? 'dark' : 'light');\n };\n\n return (\n <ThemeContext.Provider value={{ colorScheme, toggleColorScheme }}>\n {children}\n </ThemeContext.Provider>\n );\n}\n\nexport function useTheme() {\n const context = useContext(ThemeContext);\n if (context === undefined) {\n throw new Error('useTheme must be used within a ThemeProvider');\n }\n return context;\n}\n","import { createContext, useContext, useState, ReactNode } from 'react';\nimport type { FilterState } from '../types/types';\n\n\n/**\n * Filter context type with state and update methods\n */\ninterface FilterContextType {\n filters: FilterState;\n updateFilter: <K extends keyof FilterState>(key: K, value: FilterState[K]) => void;\n clearFilters: () => void;\n}\n\nconst initialFilterState: FilterState = {\n category: null,\n country: null,\n searchQuery: '',\n letterFilter: null,\n};\n\nconst FilterContext = createContext<FilterContextType | undefined>(undefined);\n\n/**\n * Filter Provider Component\n * Manages filter state for the application\n * \n * @example\n * <FilterProvider>\n * <App />\n * </FilterProvider>\n */\nexport function FilterProvider({ children }: { children: ReactNode }) {\n const [filters, setFilters] = useState<FilterState>(initialFilterState);\n\n /**\n * Update a single filter\n */\n const updateFilter = <K extends keyof FilterState>(\n key: K,\n value: FilterState[K]\n ) => {\n setFilters((prev) => ({\n ...prev,\n [key]: value,\n }));\n };\n\n /**\n * Clear all filters back to initial state\n */\n const clearFilters = () => {\n setFilters(initialFilterState);\n };\n\n return (\n <FilterContext.Provider value={{ filters, updateFilter, clearFilters }}>\n {children}\n </FilterContext.Provider>\n );\n}\n\n/**\n * Hook to access filter context\n * Must be used within FilterProvider\n * \n * @example\n * const { filters, updateFilter, clearFilters } = useFilters();\n * updateFilter('category', 'dairy');\n */\nexport function useFilters(): FilterContextType {\n const context = useContext(FilterContext);\n\n if (context === undefined) {\n throw new Error('useFilters must be used within a FilterProvider');\n }\n\n return context;\n}\n","// Export all locale message files for i18n\nimport enMessages from './en.json';\n\nexport const localeMessages = {\n en: enMessages\n} as const;\n\nexport type SupportedLocale = keyof typeof localeMessages;\n","{\n \"app.title\": \"World Ferments\",\n \"app.subtitle\": \"Explore Fermented Foods Around the World\",\n \"home.hello\": \"Hello World!\",\n \"home.description\": \"Next.js is running successfully with React Aria Components and React Spectrum S2. The foundation is set!\",\n \"home.checkHealth\": \"Check API Health\",\n \"home.healthChecking\": \"Checking health...\",\n \"home.healthClickPrompt\": \"Click the button to check API health\",\n \"health.status\": \"Status\",\n \"health.database\": \"Database\",\n \"health.time\": \"Time\",\n \"health.error\": \"Error\",\n \"health.siteUrls\": \"Site URLs\",\n \"health.apiEndpoints\": \"API Endpoints\",\n \"theme.toggle.label\": \"Dark Mode\",\n \"theme.toggle.ariaLabel\": \"Toggle dark mode\",\n \"features.map\": \"Coming soon: Interactive map with fermented foods\",\n \"features.browse\": \"Browse by region, category, and culture\",\n \"ferments.subtitle\": \"Explore and discover fermented foods from around the world\",\n \"ferments.loading\": \"Loading...\",\n \"ferments.count\": \"{count, plural, =0 {No ferments found} one {# ferment found} other {# ferments found}}\",\n \"ferments.filter.category\": \"Category\",\n \"ferments.filter.country\": \"Country\",\n \"ferments.filter.allCategories\": \"All Categories\",\n \"ferments.filter.allCountries\": \"All Countries\",\n \"ferments.filter.all\": \"All\",\n \"ferments.filter.alphabetLabel\": \"Filter by first letter\",\n \"ferments.filter.clear\": \"Clear Filters\",\n \"ferments.filter.loading\": \"Loading filters...\",\n \"ferments.filter.category.ariaLabel\": \"Filter by category\",\n \"ferments.filter.country.ariaLabel\": \"Filter by country\",\n \"ferments.search.placeholder\": \"Search ferments...\",\n \"ferments.search.label\": \"Search\",\n \"ferments.search.ariaLabel\": \"Search ferments by name or description\",\n \"ferments.search.clear\": \"Clear\",\n \"ferment.detail.backToBrowse\": \"Back to Browse\",\n \"ferment.detail.loading\": \"Loading ferment details...\",\n \"ferment.detail.notFound\": \"Ferment not found\",\n \"ferment.detail.error\": \"Failed to load ferment details\",\n \"ferment.detail.country\": \"Country\",\n \"ferment.detail.region\": \"Region\",\n \"ferment.detail.category\": \"Category\",\n \"ferment.detail.ingredients\": \"Ingredients\",\n \"ferment.detail.learnMore\": \"Learn More\",\n \"ferment.detail.viewSource\": \"View Source\",\n \"ferment.detail.location\": \"Location\",\n \"ferment.detail.coordinates\": \"Coordinates\",\n \"ferment.detail.relatedFerments\": \"Related Ferments\",\n \"ferment.detail.close\": \"Close\",\n \"map.loading\": \"Loading map...\",\n \"map.error.title\": \"Map Error\",\n \"map.error.load\": \"Failed to load ferments for map\",\n \"map.error.noToken\": \"Mapbox token not configured. Please set NEXT_PUBLIC_MAPBOX_TOKEN in your environment.\",\n \"map.error.init\": \"Failed to initialize map\",\n \"map.ariaLabel\": \"Interactive map showing fermented foods around the world\",\n \"map.marker.label\": \"{name} from {country}\",\n \"map.popup.viewDetails\": \"View details\",\n \"nav.map\": \"Map\",\n \"nav.list\": \"List\",\n \"nav.viewToggle.ariaLabel\": \"Switch between map and list views\",\n \"nav.about\": \"About\",\n \"common.back\": \"Back\",\n \"common.loading\": \"Loading...\",\n \"common.saving\": \"Saving...\",\n \"common.noData\": \"No data available\",\n \"table.common.loading\": \"Loading data...\",\n \"table.common.loadingMore\": \"Loading more data...\",\n \"filterTabs.ariaLabel\": \"Filter options\",\n \"statCard.viewAll\": \"View all\",\n \"ferments.category.all\": \"All Categories\",\n \"ferments.category.vegetable\": \"Vegetable\",\n \"ferments.category.dairy\": \"Dairy\",\n \"ferments.category.grain\": \"Grain\",\n \"ferments.category.legume\": \"Legume\",\n \"ferments.category.beverage\": \"Beverage\",\n \"ferments.category.condiment\": \"Condiment\",\n \"ferments.category.protein\": \"Protein\",\n \"ferments.category.fruit\": \"Fruit\",\n \"ferments.category.other\": \"Other\",\n \"sources.content_type.about\": \"About\",\n \"sources.content_type.history\": \"History\",\n \"sources.content_type.recipes\": \"Recipes\",\n \"sources.content_type.tools\": \"Tools & Equipment\",\n \"sources.content_type.starters\": \"Starters & Cultures\",\n \"sources.content_type.culture\": \"Cultural Context\",\n \"sources.content_type.scientific\": \"Scientific Research\",\n \"sources.content_type.commercial\": \"Commercial Products\",\n \"sources.content_type.course\": \"Courses & Tutorials\",\n \"sources.content_type.general\": \"General Information\"\n}","// Hooks\nexport * from './useFilterState';\nexport * from './useIsMobileDevice';\n","/**\n * Filter State Hook\n * Custom hook for managing ferment filter state with type-safe utilities\n */\nimport type { FilterState } from '../types/types';\n\n/**\n * Default filter state\n */\nexport const DEFAULT_FILTERS: FilterState = {\n searchQuery: '',\n category: null,\n country: null,\n letterFilter: null,\n};\n\n/**\n * Check if any filters are active\n */\nexport function hasActiveFilters(filters: FilterState): boolean {\n return (\n filters.searchQuery.trim() !== '' ||\n filters.category !== null ||\n filters.country !== null ||\n (filters.letterFilter !== null && filters.letterFilter !== 'all')\n );\n}\n\n/**\n * Clear all filters, returning default state\n */\nexport function clearFilters(): FilterState {\n return { ...DEFAULT_FILTERS };\n}\n\n/**\n * Update a single filter field\n */\nexport function updateFilter(\n filters: FilterState,\n field: keyof FilterState,\n value: string | null\n): FilterState {\n return {\n ...filters,\n [field]: value,\n };\n}\n\n/**\n * Convert filter state to API query parameters\n * Only includes non-empty filters\n */\nexport function filtersToQueryParams(filters: FilterState): Record<string, string> {\n const params: Record<string, string> = {};\n\n if (filters.category) {\n params.category = filters.category;\n }\n\n if (filters.country) {\n params.country = filters.country;\n }\n\n if (filters.letterFilter && filters.letterFilter !== 'all') {\n params.letter = filters.letterFilter;\n }\n\n return params;\n}\n","import { useMediaQuery } from '@react-spectrum/utils';\n\n/**\n * Hook to detect if the current viewport is a mobile device\n * \n * Uses the 640px breakpoint (S2's small breakpoint) to determine mobile\n * \n * @returns true if viewport width is 640px or less\n * \n * @example\n * const isMobile = useIsMobileDevice();\n * \n * return (\n * <Button>\n * {isMobile ? 'Save' : 'Save Changes'}\n * </Button>\n * );\n */\nexport function useIsMobileDevice(): boolean {\n return useMediaQuery('(max-width: 640px)');\n}\n","// Utilities\nexport { textFieldInputStyles } from './styleUtils';\nexport { getCategoryColors } from './category-colors';\nexport { getSourceCategoryColors } from './source-type-colors';\nexport {\n getCategoryOptions,\n getSourceCategoryOptions,\n FERMENT_CATEGORIES,\n SOURCE_CATEGORIES,\n} from './select-options';\nexport type { CategoryFilterType, FermentCategory, SelectOption } from './select-options';\nexport { api } from './api';\nexport type { FermentsResponse, FermentDetailResponse, SearchResponse, FilterOptions } from './api';\nexport { isBrowser, isDocument, safeWindow, safeDocument, safeLocalStorage } from './browser';\n","\nexport const textFieldInputStyles = () => ({\n padding: 12,\n paddingX: 16,\n borderWidth: 1,\n borderStyle: 'solid',\n borderColor: {\n default: '--plum-300',\n isHovered: '--plum-400',\n isFocusVisible: '--plum-500',\n isInvalid: '--berry-500',\n },\n borderRadius: 'lg',\n font: 'body',\n backgroundColor: 'layer-1',\n color: {\n default: '--plum-800',\n '::placeholder': '--plum-400'\n },\n outline: 'none',\n} as const);\n","/**\n * Select Options Utilities\n * Provides consistent select options for categories and source categories\n * Used across admin-ui and consumer-ui for dropdowns/filters\n */\nimport type { SourceCategory } from '../types/types';\n\nexport interface SelectOption<T = string> {\n id: string;\n label: string;\n value: T;\n isDisabled?: boolean;\n}\n\n/**\n * Ferment category values\n * Sorted alphabetically with 'other' last\n */\nexport const FERMENT_CATEGORIES = [\n 'beverage',\n 'condiment',\n 'dairy',\n 'fruit',\n 'grain',\n 'legume',\n 'protein',\n 'vegetable',\n 'other',\n] as const;\n\n/**\n * Ferment category type\n */\nexport type FermentCategory = typeof FERMENT_CATEGORIES[number];\n\n/**\n * Category filter type for frontend filtering\n * Includes 'all' option for showing all categories\n */\nexport type CategoryFilterType = FermentCategory | 'all';\n\n/**\n * Source category values\n */\nexport const SOURCE_CATEGORIES: SourceCategory[] = [\n 'about',\n 'history',\n 'recipes',\n 'tools',\n 'starters',\n 'culture',\n 'scientific',\n 'commercial',\n 'course',\n 'general',\n];\n\n/**\n * Get ferment category options for Select components\n * Returns i18n keys that need to be formatted with intl.formatMessage\n * \n * @param includeAll - Whether to include \"All\" option (for filters)\n * @returns Array of select options with i18n keys\n * \n * @example\n * const options = getCategoryOptions().map(opt => ({\n * ...opt,\n * label: intl.formatMessage({ id: opt.label })\n * }));\n */\nexport function getCategoryOptions(includeAll = false): SelectOption<FermentCategory | null>[] {\n const options: SelectOption<FermentCategory | null>[] = [];\n\n if (includeAll) {\n options.push({\n id: 'all',\n label: 'ferments.category.all', // i18n key\n value: null,\n });\n }\n\n FERMENT_CATEGORIES.forEach(category => {\n options.push({\n id: category,\n label: `ferments.category.${category}`, // i18n key\n value: category,\n });\n });\n\n return options;\n}\n\n/**\n * Get source category options for Select components\n * Returns i18n keys that need to be formatted with intl.formatMessage\n * \n * @returns Array of select options with i18n keys\n * \n * @example\n * const options = getSourceCategoryOptions().map(opt => ({\n * ...opt,\n * label: intl.formatMessage({ id: opt.label })\n * }));\n */\nexport function getSourceCategoryOptions(): SelectOption<SourceCategory>[] {\n return SOURCE_CATEGORIES.map(category => ({\n id: category,\n label: `sources.content_type.${category}`, // i18n key\n value: category,\n }));\n}\n","/**\n * API Client for GlobalFerments\n * \n * Centralized functions for all backend API calls\n * with error handling and type safety\n */\n\nimport type { Category, Country, Ferment, RelatedFerment } from '../types';\n\nexport interface FermentsResponse {\n ferments: Ferment[];\n count: number;\n filters?: {\n category?: string;\n country?: string;\n };\n}\n\nexport interface FermentDetailResponse extends Ferment {\n relatedFerments: Array<RelatedFerment>;\n}\n\nexport interface SearchResponse {\n results: Ferment[];\n query: string;\n count: number;\n}\n\nexport interface FilterOptions {\n category?: string;\n country?: string;\n limit?: number;\n offset?: number;\n}\n\n/**\n * API client object with all endpoint methods\n */\nexport const api = {\n /**\n * Get list of ferments with optional filters\n */\n async getFerments(filters?: FilterOptions): Promise<FermentsResponse> {\n const params = new URLSearchParams();\n\n if (filters?.category) params.append('category', filters.category);\n if (filters?.country) params.append('country', filters.country);\n if (filters?.limit) params.append('limit', filters.limit.toString());\n if (filters?.offset) params.append('offset', filters.offset.toString());\n\n const url = `/api/ferments${params.toString() ? `?${params}` : ''}`;\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch ferments: ${response.statusText}`);\n }\n\n return response.json();\n },\n\n /**\n * Get optimized data for map markers\n */\n async getFermentsForMap(): Promise<{ ferments: Array<Pick<Ferment, 'id' | 'name' | 'latitude' | 'longitude' | 'category' | 'country'>> }> {\n const response = await fetch('/api/ferments/map');\n\n if (!response.ok) {\n throw new Error(`Failed to fetch map data: ${response.statusText}`);\n }\n\n return response.json();\n },\n\n /**\n * Search ferments by name or description\n */\n async searchFerments(query: string): Promise<SearchResponse> {\n if (!query || query.length < 2) {\n return { results: [], query, count: 0 };\n }\n\n const response = await fetch(`/api/search?q=${encodeURIComponent(query)}`);\n\n if (!response.ok) {\n throw new Error(`Search failed: ${response.statusText}`);\n }\n\n return response.json();\n },\n\n /**\n * Get list of countries with hasFerments flag\n * Optionally filtered by category\n */\n async getCountries(category?: string): Promise<{ countries: Country[] }> {\n const url = category\n ? `/api/countries?category=${encodeURIComponent(category)}`\n : '/api/countries';\n\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch countries: ${response.statusText}`);\n }\n\n return response.json();\n },\n\n /**\n * Get list of categories\n * Optionally filtered by country\n */\n async getCategories(country?: string): Promise<{ categories: Category[] }> {\n const url = country\n ? `/api/categories?country=${encodeURIComponent(country)}`\n : '/api/categories';\n\n const response = await fetch(url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch categories: ${response.statusText}`);\n }\n\n return response.json();\n },\n};\n","\nexport * from './types';\n","import type { BadgeVariant } from '../components/Badge';\n\n/**\n * Type definitions for GlobalFerments\n *\n * Shared types used across the application\n */\n\n/**\n * License types for content attribution\n * Used across admin-ui, consumer-ui, and backend\n */\nexport type LicenseType =\n | 'CC0'\n | 'CC-BY'\n | 'CC-BY-SA'\n | 'CC-BY-NC'\n | 'CC-BY-NC-SA'\n | 'CC-BY-ND'\n | 'CC-BY-NC-ND'\n | 'MIT'\n | 'Apache-2.0'\n | 'GPL'\n | 'All Rights Reserved'\n | 'Public Domain'\n | 'Unknown'\n | 'Other'\n | 'Unsplash'\n | 'Pexels'\n | 'Pixabay';\n\n/**\n * Data source extraction methods\n */\nexport type DataSourceType =\n | 'html_meta_tags'\n | 'structured_data'\n | 'page_content'\n | 'mixed'\n | 'manual'\n | 'unknown';\n\n/**\n * Source content category\n * Categorizes the type of information a ferment source provides\n */\nexport type SourceCategory =\n | 'about' // General information about the ferment\n | 'history' // Historical background and cultural context\n | 'recipes' // Recipes and preparation methods\n | 'tools' // Equipment and tools needed\n | 'starters' // Starter cultures and ingredients\n | 'culture' // Cultural significance and traditions\n | 'scientific' // Scientific research and studies\n | 'commercial' // Commercial products and vendors\n | 'course' // Classes, tutorials, and educational content\n | 'general'; // General/uncategorized sources\n\nexport interface FilterState {\n category: string | null;\n country: string | null;\n searchQuery: string;\n letterFilter: string | null;\n}\n\nexport interface CategoryColors {\n marker: string; // CSS variable for map marker color\n badge: BadgeVariant; // plain english name that maps to badge color scheme\n}\n\n/**\n * Type definitions for GlobalFerments\n *\n * Shared types used across the application\n */\n\nexport interface Ferment {\n id: number;\n name: string;\n slug: string;\n description: string;\n country: string;\n countryCode: string;\n region: string;\n regionName?: string | null;\n category: string;\n ingredients: string[] | null;\n imageUrl: string | null;\n sourceUrl: string | null;\n latitude: number | null;\n longitude: number | null;\n relatedFerments?: RelatedFerment[];\n}\n\nexport interface RelatedFerment {\n id: number;\n name: string;\n slug: string;\n category?: string;\n imageUrl?: string | null;\n country: string;\n countryCode?: string;\n}\n\nexport interface Country {\n code: string;\n name: string;\n region: string;\n hasFerments: boolean; // True if this country has ferments\n}\n\nexport interface Category {\n name: string; // Primary field name\n category: string; // Deprecated (backward compatibility)\n}\n"],"names":[],"version":3,"file":"main.cjs.map"}