fermmap-shared 0.1.0

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.
@@ -0,0 +1,693 @@
1
+ import { ButtonProps as _ButtonProps1, TextFieldProps as _TextFieldProps1, Selection, CheckboxProps as _CheckboxProps1, Key, SortDescriptor, DisclosureGroupProps, LinkProps, RadioGroupProps as _RadioGroupProps1, SelectProps, SwitchProps as _SwitchProps1, TabListProps, TabPanelProps, TabProps, TabsProps } from "react-aria-components";
2
+ import { JSX } from "react/jsx-runtime";
3
+ import { ReactNode } from "react";
4
+ interface ButtonProps extends _ButtonProps1 {
5
+ children?: React.ReactNode;
6
+ variant?: 'primary' | 'secondary' | 'danger' | 'navigation';
7
+ size?: 'S' | 'M' | 'L';
8
+ }
9
+ export const Button: ({ children, variant, size, className, ...props }: ButtonProps) => JSX.Element;
10
+ export const textFieldInputStyles: () => {
11
+ readonly padding: 12;
12
+ readonly paddingX: 16;
13
+ readonly borderWidth: 1;
14
+ readonly borderStyle: "solid";
15
+ readonly borderColor: {
16
+ readonly default: "--plum-300";
17
+ readonly isHovered: "--plum-400";
18
+ readonly isFocusVisible: "--plum-500";
19
+ readonly isInvalid: "--berry-500";
20
+ };
21
+ readonly borderRadius: "lg";
22
+ readonly font: "body";
23
+ readonly backgroundColor: "layer-1";
24
+ readonly color: "neutral";
25
+ readonly outline: "none";
26
+ };
27
+ interface TextFieldProps extends Omit<_TextFieldProps1, 'children'> {
28
+ label: string;
29
+ type?: 'text' | 'password' | 'email' | 'tel' | 'url' | 'number';
30
+ error?: string;
31
+ description?: string;
32
+ placeholder?: string;
33
+ }
34
+ export function TextField({ label, type, error, description, placeholder, ...props }: TextFieldProps): JSX.Element;
35
+ interface AlertDialogProps {
36
+ isOpen: boolean;
37
+ onClose: () => void;
38
+ title: string;
39
+ message: string;
40
+ variant?: 'danger' | 'warning' | 'info' | 'success';
41
+ confirmLabel?: string;
42
+ cancelLabel?: string;
43
+ onConfirm: () => void | Promise<void>;
44
+ showCancel?: boolean;
45
+ children?: React.ReactNode;
46
+ }
47
+ export function AlertDialog({ isOpen, onClose, title, message, variant, confirmLabel, cancelLabel, onConfirm, showCancel, children }: AlertDialogProps): JSX.Element;
48
+ interface PromptDialogProps {
49
+ isOpen: boolean;
50
+ onClose: () => void;
51
+ title: string;
52
+ message: string;
53
+ inputLabel: string;
54
+ inputPlaceholder?: string;
55
+ confirmLabel?: string;
56
+ cancelLabel?: string;
57
+ onConfirm: (value: string) => void | Promise<void>;
58
+ validator?: (value: string) => string | null;
59
+ }
60
+ export function PromptDialog({ isOpen, onClose, title, message, inputLabel, inputPlaceholder, confirmLabel, cancelLabel, onConfirm, validator }: PromptDialogProps): JSX.Element;
61
+ export interface AutocompleteItem {
62
+ id: string | number;
63
+ name: string;
64
+ [key: string]: any;
65
+ }
66
+ export interface AutocompleteProps<T extends AutocompleteItem> {
67
+ items: T[];
68
+ selectedKeys?: Selection;
69
+ onSelectionChange?: (keys: Selection) => void;
70
+ selectionMode?: 'none' | 'single' | 'multiple';
71
+ label: string;
72
+ placeholder?: string;
73
+ ariaLabel?: string;
74
+ error?: string;
75
+ getItemText?: (item: T) => string;
76
+ }
77
+ /**
78
+ * Autocomplete component using React Aria SearchField + ListBox
79
+ *
80
+ * Features:
81
+ * - SearchField for filtering items
82
+ * - Optional checkboxes for multi-selection
83
+ * - Shows both selected and unselected items when filtered
84
+ * - Selected items highlighted with background color
85
+ * - Keyboard navigation (arrow keys, enter, escape)
86
+ * - Accessible (ARIA labels, focus management)
87
+ *
88
+ * @example
89
+ * ```tsx
90
+ * // Single select
91
+ * <Autocomplete
92
+ * items={sources}
93
+ * selectedKeys={selectedKey}
94
+ * onSelectionChange={setSelectedKey}
95
+ * selectionMode="single"
96
+ * label="Select Source"
97
+ * placeholder="Search sources..."
98
+ * />
99
+ *
100
+ * // Multi select
101
+ * <Autocomplete
102
+ * items={sources}
103
+ * selectedKeys={selectedKeys}
104
+ * onSelectionChange={setSelectedKeys}
105
+ * selectionMode="multiple"
106
+ * label="Select Sources"
107
+ * placeholder="Search sources..."
108
+ * />
109
+ *
110
+ * // No selection (just filtering)
111
+ * <Autocomplete
112
+ * items={sources}
113
+ * selectionMode="none"
114
+ * label="Search Sources"
115
+ * placeholder="Search sources..."
116
+ * />
117
+ * ```
118
+ */
119
+ export function Autocomplete<T extends AutocompleteItem>({ items, selectedKeys, onSelectionChange, selectionMode, label, placeholder, ariaLabel, error, getItemText, }: AutocompleteProps<T>): JSX.Element;
120
+ export interface CheckboxProps extends Omit<_CheckboxProps1, 'className' | 'style'> {
121
+ /**
122
+ * The size of the Checkbox.
123
+ * @default 'M'
124
+ */
125
+ size?: 'S' | 'M' | 'L';
126
+ }
127
+ /**
128
+ * Checkbox component for selection within tables and forms.
129
+ * Simplified version based on React Spectrum S2 Checkbox.
130
+ *
131
+ * @example
132
+ * ```tsx
133
+ * <Checkbox isSelected={selected} onChange={setSelected}>
134
+ * Subscribe to newsletter
135
+ * </Checkbox>
136
+ *
137
+ * // In table header/cell (no label)
138
+ * <Checkbox slot="selection" />
139
+ * ```
140
+ */
141
+ export function Checkbox({ size, children, ...props }: CheckboxProps): JSX.Element;
142
+ interface ProgressBarProps {
143
+ label?: string;
144
+ value?: number;
145
+ maxValue?: number;
146
+ isIndeterminate?: boolean;
147
+ showValueLabel?: boolean;
148
+ 'aria-label'?: string;
149
+ }
150
+ /**
151
+ * ProgressBar Component
152
+ *
153
+ * Accessible progress indicator using React Aria Components.
154
+ * Supports both determinate (with value) and indeterminate (loading) states.
155
+ *
156
+ * @example
157
+ * // Determinate progress
158
+ * <ProgressBar label="Loading..." value={50} maxValue={100} />
159
+ *
160
+ * @example
161
+ * // Indeterminate progress
162
+ * <ProgressBar label="Loading..." isIndeterminate />
163
+ */
164
+ export function ProgressBar({ label, value, maxValue, isIndeterminate, showValueLabel, 'aria-label': ariaLabel, }: ProgressBarProps): JSX.Element;
165
+ export interface ColumnDef<T> {
166
+ key: string;
167
+ label: string;
168
+ renderCell: (item: T) => ReactNode;
169
+ width?: string;
170
+ isRowHeader?: boolean;
171
+ align?: 'start' | 'center' | 'end';
172
+ }
173
+ export interface TableProps<T> {
174
+ data: T[];
175
+ columns: ColumnDef<T>[];
176
+ onRowAction?: (key: Key) => void;
177
+ ariaLabel: string;
178
+ sortDescriptor?: SortDescriptor;
179
+ onSortChange?: (descriptor: SortDescriptor) => void;
180
+ selectionMode?: 'none' | 'single' | 'multiple';
181
+ selectedKeys?: Set<Key>;
182
+ onSelectionChange?: (keys: Set<Key>) => void;
183
+ onLoadMore?: () => void;
184
+ isLoadingMore?: boolean;
185
+ }
186
+ /**
187
+ * Reusable Table component using React Aria Components
188
+ *
189
+ * Features:
190
+ * - Collection-based API for better performance
191
+ * - Built-in virtualization support (via RAC)
192
+ * - Keyboard navigation
193
+ * - Screen reader support
194
+ * - Optional sorting
195
+ * - Optional row selection
196
+ * - Optional row actions (clickable rows)
197
+ * - Checkbox column for multiple selection
198
+ * - Hover and selected states
199
+ *
200
+ * @example
201
+ * ```tsx
202
+ * const columns: ColumnDef<Run>[] = [
203
+ * { key: 'id', label: 'ID', renderCell: (run) => `#${run.id}` },
204
+ * { key: 'status', label: 'Status', renderCell: (run) => <Badge variant={run.status}>{run.status}</Badge> }
205
+ * ];
206
+ *
207
+ * <Table
208
+ * data={runs}
209
+ * columns={columns}
210
+ * ariaLabel="Scraper runs"
211
+ * selectionMode="multiple"
212
+ * selectedKeys={selectedIds}
213
+ * onSelectionChange={setSelectedIds}
214
+ * />
215
+ * ```
216
+ */
217
+ export function Table<T extends {
218
+ id: number | string;
219
+ }>({ data, columns, onRowAction, ariaLabel, sortDescriptor, onSortChange, selectionMode, selectedKeys, onSelectionChange, onLoadMore, isLoadingMore }: TableProps<T>): JSX.Element;
220
+ export interface AutocompleteTableProps<T extends AutocompleteItem> {
221
+ allItems: T[];
222
+ selectedKeys: Selection;
223
+ onSelectionChange: (keys: Selection) => void;
224
+ selectionMode?: 'single' | 'multiple';
225
+ searchLabel: string;
226
+ searchPlaceholder?: string;
227
+ getItemText?: (item: T) => string;
228
+ columns: ColumnDef<T>[];
229
+ tableLabel: string;
230
+ }
231
+ /**
232
+ * Composite component combining Autocomplete and Table
233
+ * Useful for search-and-select workflows where selected items are displayed in a table
234
+ *
235
+ * Features:
236
+ * - Autocomplete search field at top
237
+ * - Table below showing selected items
238
+ * - Filters all items in autocomplete
239
+ * - Shows only selected items in table
240
+ * - Scrollable table with max height of 384px
241
+ *
242
+ * @example
243
+ * ```tsx
244
+ * <AutocompleteTable
245
+ * allItems={allSources}
246
+ * selectedKeys={selectedSourceIds}
247
+ * onSelectionChange={setSelectedSourceIds}
248
+ * selectionMode="multiple"
249
+ * searchLabel="Search Sources"
250
+ * searchPlaceholder="Search by name or URL..."
251
+ * columns={[
252
+ * { key: 'name', label: 'Name', isRowHeader: true },
253
+ * { key: 'domain', label: 'Domain' },
254
+ * { key: 'actions', label: 'Actions', align: 'end' },
255
+ * ]}
256
+ * tableLabel="Selected Sources"
257
+ * />
258
+ * ```
259
+ */
260
+ export function AutocompleteTable<T extends AutocompleteItem>({ allItems, selectedKeys, onSelectionChange, selectionMode, searchLabel, searchPlaceholder, getItemText, columns, tableLabel, }: AutocompleteTableProps<T>): JSX.Element;
261
+ export type BadgeVariant = 'completed' | 'running' | 'failed' | 'cancelled' | 'pending' | 'plum' | 'cucumber' | 'berry' | 'ginger' | 'peach';
262
+ interface BadgeProps {
263
+ variant?: BadgeVariant;
264
+ children: React.ReactNode;
265
+ }
266
+ /**
267
+ * Badge component for displaying status or category with conditional colors
268
+ *
269
+ * @example
270
+ * <Badge variant="completed">Completed</Badge>
271
+ * <Badge variant="cucumber">Vegetable</Badge>
272
+ */
273
+ export function Badge({ variant, children }: BadgeProps): JSX.Element;
274
+ type LocaleMessages = Record<string, Record<string, string>>;
275
+ interface I18nProviderProps {
276
+ children: ReactNode;
277
+ messages: LocaleMessages;
278
+ }
279
+ /**
280
+ * Unified i18n provider that can be used by both frontend and admin UI.
281
+ *
282
+ * @param messages - Locale messages object (e.g., from src/i18n/index.ts or admin-ui/src/i18n/index.ts)
283
+ *
284
+ * @example
285
+ * // Frontend usage
286
+ * import { localeMessages } from '@/i18n';
287
+ * <I18nProvider messages={localeMessages}>{children}</I18nProvider>
288
+ *
289
+ * @example
290
+ * // Admin UI usage
291
+ * import { localeMessages } from '@admin/i18n';
292
+ * <I18nProvider messages={localeMessages}>{children}</I18nProvider>
293
+ */
294
+ export function I18nProvider({ children, messages }: I18nProviderProps): JSX.Element;
295
+ interface CloseButtonProps extends Omit<_ButtonProps1, 'onPress'> {
296
+ onClose: () => void;
297
+ }
298
+ export const CloseButton: ({ onClose, ...props }: CloseButtonProps) => JSX.Element;
299
+ export interface ComboBoxItem {
300
+ id: Key;
301
+ label: string;
302
+ textValue?: string;
303
+ }
304
+ export interface ComboBoxProps {
305
+ label?: string;
306
+ description?: string;
307
+ errorMessage?: string;
308
+ isRequired?: boolean;
309
+ isDisabled?: boolean;
310
+ selectedKey?: Key | null;
311
+ onSelectionChange?: (key: Key | null) => void;
312
+ items: ComboBoxItem[];
313
+ placeholder?: string;
314
+ 'aria-label'?: string;
315
+ inputValue?: string;
316
+ onInputChange?: (value: string) => void;
317
+ }
318
+ /**
319
+ * Generic ComboBox component for searchable single-selection.
320
+ *
321
+ * Uses React Aria Components ComboBox for accessible selection with filtering.
322
+ * Reusable for any data type - countries, users, categories, etc.
323
+ *
324
+ * @example
325
+ * ```tsx
326
+ * const items = [
327
+ * { id: 1, label: 'Apple' },
328
+ * { id: 2, label: 'Banana' },
329
+ * { id: 3, label: 'Orange' },
330
+ * ];
331
+ *
332
+ * <ComboBox
333
+ * label="Fruit"
334
+ * items={items}
335
+ * selectedKey={selectedId}
336
+ * onSelectionChange={setSelectedId}
337
+ * placeholder="Select a fruit"
338
+ * />
339
+ * ```
340
+ */
341
+ export function ComboBox({ label, description, errorMessage, isRequired, isDisabled, selectedKey, onSelectionChange, items, placeholder, 'aria-label': ariaLabel, inputValue: controlledInputValue, onInputChange: controlledOnInputChange, }: ComboBoxProps): JSX.Element;
342
+ export interface DisclosureItem {
343
+ id: string;
344
+ title: string;
345
+ content: React.ReactNode;
346
+ onPress?: () => void;
347
+ }
348
+ interface CustomDisclosureGroupProps extends Omit<DisclosureGroupProps, 'children'> {
349
+ items: DisclosureItem[];
350
+ 'aria-label'?: string;
351
+ }
352
+ /**
353
+ * Reusable DisclosureGroup component
354
+ * Displays collapsible sections with consistent styling
355
+ *
356
+ * @example
357
+ * <DisclosureGroup
358
+ * items={[
359
+ * {
360
+ * id: 'section1',
361
+ * title: 'Section 1',
362
+ * content: <div>Content goes here</div>
363
+ * }
364
+ * ]}
365
+ * />
366
+ */
367
+ export function DisclosureGroup({ items, ...props }: CustomDisclosureGroupProps): JSX.Element;
368
+ interface FilterOption {
369
+ value: string;
370
+ label: string;
371
+ }
372
+ interface FilterTabsProps {
373
+ value: string;
374
+ onChange: (value: string) => void;
375
+ options: FilterOption[];
376
+ ariaLabel?: string;
377
+ }
378
+ /**
379
+ * FilterTabs component for status filtering
380
+ * Uses React Aria ToggleButtonGroup with SelectionIndicator
381
+ *
382
+ * @example
383
+ * <FilterTabs
384
+ * value={statusFilter}
385
+ * onChange={setStatusFilter}
386
+ * options={[
387
+ * { value: 'all', label: 'All' },
388
+ * { value: 'running', label: 'Running' }
389
+ * ]}
390
+ * ariaLabel="Filter by status"
391
+ * />
392
+ */
393
+ export function FilterTabs({ value, onChange, options, ariaLabel }: FilterTabsProps): JSX.Element;
394
+ interface LabeledValueProps {
395
+ label: string;
396
+ error?: string;
397
+ description?: string;
398
+ children: React.ReactNode;
399
+ }
400
+ export function LabeledValue({ label, children, error, description, ...props }: LabeledValueProps): JSX.Element;
401
+ interface CustomLinkProps extends Omit<LinkProps, 'className'> {
402
+ children: ReactNode;
403
+ isExternal?: boolean;
404
+ className?: string | ((renderProps: any) => string);
405
+ }
406
+ /**
407
+ * Link Component
408
+ *
409
+ * Wraps React Aria Components Link with S2 styling and proper a11y.
410
+ * Automatically handles focus states and keyboard navigation.
411
+ * External links automatically get target="_blank" and rel="noopener noreferrer".
412
+ *
413
+ * @example
414
+ * <Link href="/ferments">Browse Ferments</Link>
415
+ *
416
+ * @example
417
+ * <Link href="https://example.com" isExternal>External Link</Link>
418
+ *
419
+ * @example
420
+ * <Link href="/" className={customStyles}>Custom Styled Link</Link>
421
+ */
422
+ export function Link({ children, href, isExternal, className, ...props }: CustomLinkProps): JSX.Element;
423
+ interface RadioGroupProps extends Omit<_RadioGroupProps1, 'children'> {
424
+ label: string;
425
+ options: Array<{
426
+ id: string;
427
+ label: string;
428
+ value: string;
429
+ }>;
430
+ error?: string;
431
+ }
432
+ export function RadioGroup({ label, options, error, ...props }: RadioGroupProps): JSX.Element;
433
+ interface SearchFieldProps {
434
+ value: string;
435
+ onChange: (value: string) => void;
436
+ label: string;
437
+ placeholder: string;
438
+ ariaLabel: string;
439
+ }
440
+ /**
441
+ * Reusable SearchField component using React Aria Components
442
+ * Styled with S2 design tokens and includes search icon
443
+ *
444
+ * @example
445
+ * <SearchField
446
+ * value={searchQuery}
447
+ * onChange={setSearchTerm}
448
+ * label="Search"
449
+ * placeholder="Search ferments..."
450
+ * ariaLabel="Search ferments by name or description"
451
+ * />
452
+ */
453
+ export function SearchField({ value, onChange, label, placeholder, ariaLabel, }: SearchFieldProps): JSX.Element;
454
+ interface SegmentedControlProps {
455
+ value: 'map' | 'list';
456
+ }
457
+ /**
458
+ * SegmentedControl for Map/List view toggle
459
+ * Uses RAC ToggleButtonGroup for accessible segmented control
460
+ */
461
+ export function SegmentedControl({ value }: SegmentedControlProps): JSX.Element;
462
+ export interface SelectOption {
463
+ id: string;
464
+ label: string;
465
+ value: string | null;
466
+ isDisabled?: boolean;
467
+ }
468
+ interface CustomSelectProps extends Omit<SelectProps<SelectOption>, 'children'> {
469
+ label: string;
470
+ options: SelectOption[];
471
+ placeholder?: string;
472
+ }
473
+ /**
474
+ * Reusable Select component following RAC pattern
475
+ * Label is inside the Select wrapper for proper a11y
476
+ */
477
+ export function Select({ label, options, placeholder, ...props }: CustomSelectProps): JSX.Element;
478
+ interface StatCardProps {
479
+ title: string;
480
+ value: ReactNode;
481
+ description?: string;
482
+ }
483
+ /**
484
+ * StatCard component for displaying dashboard statistics
485
+ *
486
+ * @example
487
+ * <StatCard
488
+ * title="Total Sources"
489
+ * value={42}
490
+ * description="Active scraping sources"
491
+ * />
492
+ *
493
+ * @example
494
+ * // With icon as value
495
+ * <StatCard
496
+ * title="System Status"
497
+ * value={<CheckmarkCircle />}
498
+ * description="All systems operational"
499
+ * />
500
+ */
501
+ export function StatCard({ title, value, description }: StatCardProps): JSX.Element;
502
+ interface SwitchProps extends Omit<_SwitchProps1, 'children'> {
503
+ children?: React.ReactNode;
504
+ }
505
+ export const Switch: ({ children, ...props }: SwitchProps) => JSX.Element;
506
+ export function Tabs({ children, ...props }: TabsProps): JSX.Element;
507
+ export function TabList<T extends object>({ children, ...props }: TabListProps<T>): JSX.Element;
508
+ export function Tab({ children, ...props }: TabProps): JSX.Element;
509
+ export function TabPanel({ children, ...props }: TabPanelProps): JSX.Element;
510
+ interface TextAreaProps extends Omit<TextFieldProps, 'type'> {
511
+ }
512
+ export function TextArea({ label, error, placeholder, value, ...props }: TextAreaProps): JSX.Element;
513
+ type ColorScheme = 'light' | 'dark';
514
+ interface ThemeContextType {
515
+ colorScheme: ColorScheme;
516
+ toggleColorScheme: () => void;
517
+ }
518
+ export function ThemeProvider({ children }: {
519
+ children: ReactNode;
520
+ }): JSX.Element;
521
+ export function useTheme(): ThemeContextType;
522
+ /**
523
+ * Type definitions for FermMap
524
+ *
525
+ * Shared types used across the application
526
+ */
527
+ export interface FilterState {
528
+ category: string | null;
529
+ country: string | null;
530
+ searchQuery: string;
531
+ }
532
+ export interface CategoryColors {
533
+ marker: string;
534
+ badge: BadgeVariant;
535
+ }
536
+ /**
537
+ * Type definitions for FermMap
538
+ *
539
+ * Shared types used across the application
540
+ */
541
+ export interface Ferment {
542
+ id: number;
543
+ name: string;
544
+ slug: string;
545
+ description: string;
546
+ country: string;
547
+ countryCode: string;
548
+ region: string;
549
+ regionName?: string | null;
550
+ category: string;
551
+ ingredients: string[] | null;
552
+ imageUrl: string | null;
553
+ sourceUrl: string | null;
554
+ latitude: number | null;
555
+ longitude: number | null;
556
+ relatedFerments?: RelatedFerment[];
557
+ }
558
+ export interface RelatedFerment {
559
+ id: number;
560
+ name: string;
561
+ slug: string;
562
+ category?: string;
563
+ imageUrl?: string | null;
564
+ country: string;
565
+ countryCode?: string;
566
+ }
567
+ export interface Country {
568
+ code: string;
569
+ name: string;
570
+ region: string;
571
+ hasFerments: boolean;
572
+ }
573
+ export interface Category {
574
+ name: string;
575
+ category: string;
576
+ }
577
+ /**
578
+ * Filter context type with state and update methods
579
+ */
580
+ interface FilterContextType {
581
+ filters: FilterState;
582
+ updateFilter: <K extends keyof FilterState>(key: K, value: FilterState[K]) => void;
583
+ clearFilters: () => void;
584
+ }
585
+ /**
586
+ * Filter Provider Component
587
+ * Manages filter state for the application
588
+ *
589
+ * @example
590
+ * <FilterProvider>
591
+ * <App />
592
+ * </FilterProvider>
593
+ */
594
+ export function FilterProvider({ children }: {
595
+ children: ReactNode;
596
+ }): JSX.Element;
597
+ /**
598
+ * Hook to access filter context
599
+ * Must be used within FilterProvider
600
+ *
601
+ * @example
602
+ * const { filters, updateFilter, clearFilters } = useFilters();
603
+ * updateFilter('category', 'dairy');
604
+ */
605
+ export function useFilters(): FilterContextType;
606
+ export function AxeDevTools(): null;
607
+ /**
608
+ * Default filter state
609
+ */
610
+ export const DEFAULT_FILTERS: FilterState;
611
+ /**
612
+ * Check if any filters are active
613
+ */
614
+ export function hasActiveFilters(filters: FilterState): boolean;
615
+ /**
616
+ * Clear all filters, returning default state
617
+ */
618
+ export function clearFilters(): FilterState;
619
+ /**
620
+ * Update a single filter field
621
+ */
622
+ export function updateFilter(filters: FilterState, field: keyof FilterState, value: string | null): FilterState;
623
+ /**
624
+ * Convert filter state to API query parameters
625
+ * Only includes non-empty filters
626
+ */
627
+ export function filtersToQueryParams(filters: FilterState): Record<string, string>;
628
+ /**
629
+ * Get colors for a ferment category
630
+ * Returns marker color (for map), badge background, and badge text color
631
+ *
632
+ * @example
633
+ * const colors = getCategoryColors('dairy');
634
+ * // { marker: 'var(--plum-500)', badge: 'plum' }
635
+ */
636
+ export function getCategoryColors(category: string): CategoryColors;
637
+ export interface FermentsResponse {
638
+ ferments: Ferment[];
639
+ count: number;
640
+ filters?: {
641
+ category?: string;
642
+ country?: string;
643
+ };
644
+ }
645
+ export interface FermentDetailResponse extends Ferment {
646
+ relatedFerments: Array<RelatedFerment>;
647
+ }
648
+ export interface SearchResponse {
649
+ results: Ferment[];
650
+ query: string;
651
+ count: number;
652
+ }
653
+ export interface FilterOptions {
654
+ category?: string;
655
+ country?: string;
656
+ limit?: number;
657
+ offset?: number;
658
+ }
659
+ /**
660
+ * API client object with all endpoint methods
661
+ */
662
+ export const api: {
663
+ /**
664
+ * Get list of ferments with optional filters
665
+ */
666
+ getFerments(filters?: FilterOptions): Promise<FermentsResponse>;
667
+ /**
668
+ * Get optimized data for map markers
669
+ */
670
+ getFermentsForMap(): Promise<{
671
+ ferments: Array<Pick<Ferment, "id" | "name" | "latitude" | "longitude" | "category" | "country">>;
672
+ }>;
673
+ /**
674
+ * Search ferments by name or description
675
+ */
676
+ searchFerments(query: string): Promise<SearchResponse>;
677
+ /**
678
+ * Get list of countries with hasFerments flag
679
+ * Optionally filtered by category
680
+ */
681
+ getCountries(category?: string): Promise<{
682
+ countries: Country[];
683
+ }>;
684
+ /**
685
+ * Get list of categories
686
+ * Optionally filtered by country
687
+ */
688
+ getCategories(country?: string): Promise<{
689
+ categories: Category[];
690
+ }>;
691
+ };
692
+
693
+ //# sourceMappingURL=types.d.ts.map