@seekora-ai/ui-sdk-react 0.1.1
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/components/Breadcrumb.d.ts +43 -0
- package/dist/components/Breadcrumb.d.ts.map +1 -0
- package/dist/components/Breadcrumb.js +119 -0
- package/dist/components/ClearRefinements.d.ts +42 -0
- package/dist/components/ClearRefinements.d.ts.map +1 -0
- package/dist/components/ClearRefinements.js +80 -0
- package/dist/components/CurrentRefinements.d.ts +41 -0
- package/dist/components/CurrentRefinements.d.ts.map +1 -0
- package/dist/components/CurrentRefinements.js +83 -0
- package/dist/components/Facets.d.ts +53 -0
- package/dist/components/Facets.d.ts.map +1 -0
- package/dist/components/Facets.js +195 -0
- package/dist/components/FederatedDropdown.d.ts +92 -0
- package/dist/components/FederatedDropdown.d.ts.map +1 -0
- package/dist/components/FederatedDropdown.js +510 -0
- package/dist/components/HierarchicalMenu.d.ts +55 -0
- package/dist/components/HierarchicalMenu.d.ts.map +1 -0
- package/dist/components/HierarchicalMenu.js +168 -0
- package/dist/components/Highlight.d.ts +51 -0
- package/dist/components/Highlight.d.ts.map +1 -0
- package/dist/components/Highlight.js +155 -0
- package/dist/components/HitsPerPage.d.ts +41 -0
- package/dist/components/HitsPerPage.d.ts.map +1 -0
- package/dist/components/HitsPerPage.js +72 -0
- package/dist/components/InfiniteHits.d.ts +56 -0
- package/dist/components/InfiniteHits.d.ts.map +1 -0
- package/dist/components/InfiniteHits.js +181 -0
- package/dist/components/MobileFilters.d.ts +71 -0
- package/dist/components/MobileFilters.d.ts.map +1 -0
- package/dist/components/MobileFilters.js +242 -0
- package/dist/components/Pagination.d.ts +44 -0
- package/dist/components/Pagination.d.ts.map +1 -0
- package/dist/components/Pagination.js +142 -0
- package/dist/components/QuerySuggestions.d.ts +38 -0
- package/dist/components/QuerySuggestions.d.ts.map +1 -0
- package/dist/components/QuerySuggestions.js +86 -0
- package/dist/components/QuerySuggestionsDropdown.d.ts +86 -0
- package/dist/components/QuerySuggestionsDropdown.d.ts.map +1 -0
- package/dist/components/QuerySuggestionsDropdown.js +395 -0
- package/dist/components/RangeInput.d.ts +58 -0
- package/dist/components/RangeInput.d.ts.map +1 -0
- package/dist/components/RangeInput.js +203 -0
- package/dist/components/RangeSlider.d.ts +51 -0
- package/dist/components/RangeSlider.d.ts.map +1 -0
- package/dist/components/RangeSlider.js +193 -0
- package/dist/components/Recommendations.d.ts +90 -0
- package/dist/components/Recommendations.d.ts.map +1 -0
- package/dist/components/Recommendations.js +270 -0
- package/dist/components/RichQuerySuggestions.d.ts +77 -0
- package/dist/components/RichQuerySuggestions.d.ts.map +1 -0
- package/dist/components/RichQuerySuggestions.js +492 -0
- package/dist/components/SearchBar.d.ts +40 -0
- package/dist/components/SearchBar.d.ts.map +1 -0
- package/dist/components/SearchBar.js +217 -0
- package/dist/components/SearchBarWithSuggestions.d.ts +99 -0
- package/dist/components/SearchBarWithSuggestions.d.ts.map +1 -0
- package/dist/components/SearchBarWithSuggestions.js +275 -0
- package/dist/components/SearchLayout.d.ts +35 -0
- package/dist/components/SearchLayout.d.ts.map +1 -0
- package/dist/components/SearchLayout.js +56 -0
- package/dist/components/SearchProvider.d.ts +28 -0
- package/dist/components/SearchProvider.d.ts.map +1 -0
- package/dist/components/SearchProvider.js +43 -0
- package/dist/components/SearchResults.d.ts +51 -0
- package/dist/components/SearchResults.d.ts.map +1 -0
- package/dist/components/SearchResults.js +485 -0
- package/dist/components/SortBy.d.ts +44 -0
- package/dist/components/SortBy.d.ts.map +1 -0
- package/dist/components/SortBy.js +61 -0
- package/dist/components/Stats.d.ts +37 -0
- package/dist/components/Stats.d.ts.map +1 -0
- package/dist/components/Stats.js +52 -0
- package/dist/components/suggestions/AmazonDropdown.d.ts +30 -0
- package/dist/components/suggestions/AmazonDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/AmazonDropdown.js +529 -0
- package/dist/components/suggestions/GoogleDropdown.d.ts +31 -0
- package/dist/components/suggestions/GoogleDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/GoogleDropdown.js +370 -0
- package/dist/components/suggestions/MinimalDropdown.d.ts +24 -0
- package/dist/components/suggestions/MinimalDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/MinimalDropdown.js +314 -0
- package/dist/components/suggestions/MobileSheetDropdown.d.ts +31 -0
- package/dist/components/suggestions/MobileSheetDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/MobileSheetDropdown.js +485 -0
- package/dist/components/suggestions/PinterestDropdown.d.ts +29 -0
- package/dist/components/suggestions/PinterestDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/PinterestDropdown.js +450 -0
- package/dist/components/suggestions/ShopifyDropdown.d.ts +27 -0
- package/dist/components/suggestions/ShopifyDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/ShopifyDropdown.js +451 -0
- package/dist/components/suggestions/SpotlightDropdown.d.ts +33 -0
- package/dist/components/suggestions/SpotlightDropdown.d.ts.map +1 -0
- package/dist/components/suggestions/SpotlightDropdown.js +547 -0
- package/dist/components/suggestions/SuggestionSearchBar.d.ts +123 -0
- package/dist/components/suggestions/SuggestionSearchBar.d.ts.map +1 -0
- package/dist/components/suggestions/SuggestionSearchBar.js +652 -0
- package/dist/components/suggestions/index.d.ts +37 -0
- package/dist/components/suggestions/index.d.ts.map +1 -0
- package/dist/components/suggestions/index.js +59 -0
- package/dist/components/suggestions/styles/index.d.ts +11 -0
- package/dist/components/suggestions/styles/index.d.ts.map +1 -0
- package/dist/components/suggestions/styles/index.js +289 -0
- package/dist/components/suggestions/styles/responsive.d.ts +107 -0
- package/dist/components/suggestions/styles/responsive.d.ts.map +1 -0
- package/dist/components/suggestions/styles/responsive.js +237 -0
- package/dist/components/suggestions/types.d.ts +489 -0
- package/dist/components/suggestions/types.d.ts.map +1 -0
- package/dist/components/suggestions/types.js +6 -0
- package/dist/components/suggestions/utils.d.ts +213 -0
- package/dist/components/suggestions/utils.d.ts.map +1 -0
- package/dist/components/suggestions/utils.js +514 -0
- package/dist/hooks/useAnalytics.d.ts +20 -0
- package/dist/hooks/useAnalytics.d.ts.map +1 -0
- package/dist/hooks/useAnalytics.js +62 -0
- package/dist/hooks/useNaturalLanguageFilters.d.ts +48 -0
- package/dist/hooks/useNaturalLanguageFilters.d.ts.map +1 -0
- package/dist/hooks/useNaturalLanguageFilters.js +221 -0
- package/dist/hooks/useQuerySuggestions.d.ts +21 -0
- package/dist/hooks/useQuerySuggestions.d.ts.map +1 -0
- package/dist/hooks/useQuerySuggestions.js +68 -0
- package/dist/hooks/useQuerySuggestionsEnhanced.d.ts +114 -0
- package/dist/hooks/useQuerySuggestionsEnhanced.d.ts.map +1 -0
- package/dist/hooks/useQuerySuggestionsEnhanced.js +376 -0
- package/dist/hooks/useSearchState.d.ts +35 -0
- package/dist/hooks/useSearchState.d.ts.map +1 -0
- package/dist/hooks/useSearchState.js +68 -0
- package/dist/hooks/useSeekoraSearch.d.ts +20 -0
- package/dist/hooks/useSeekoraSearch.d.ts.map +1 -0
- package/dist/hooks/useSeekoraSearch.js +63 -0
- package/dist/hooks/useSmartSuggestions.d.ts +55 -0
- package/dist/hooks/useSmartSuggestions.d.ts.map +1 -0
- package/dist/hooks/useSmartSuggestions.js +236 -0
- package/dist/hooks/useSuggestionsAnalytics.d.ts +91 -0
- package/dist/hooks/useSuggestionsAnalytics.d.ts.map +1 -0
- package/dist/hooks/useSuggestionsAnalytics.js +226 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.umd.js +1 -0
- package/dist/src/index.d.ts +2849 -0
- package/dist/src/index.esm.js +11679 -0
- package/dist/src/index.esm.js.map +1 -0
- package/dist/src/index.js +11761 -0
- package/dist/src/index.js.map +1 -0
- package/dist/themes/createTheme.d.ts +8 -0
- package/dist/themes/createTheme.d.ts.map +1 -0
- package/dist/themes/createTheme.js +10 -0
- package/dist/themes/dark.d.ts +6 -0
- package/dist/themes/dark.d.ts.map +1 -0
- package/dist/themes/dark.js +34 -0
- package/dist/themes/default.d.ts +6 -0
- package/dist/themes/default.d.ts.map +1 -0
- package/dist/themes/default.js +71 -0
- package/dist/themes/mergeThemes.d.ts +7 -0
- package/dist/themes/mergeThemes.d.ts.map +1 -0
- package/dist/themes/mergeThemes.js +6 -0
- package/dist/themes/minimal.d.ts +6 -0
- package/dist/themes/minimal.d.ts.map +1 -0
- package/dist/themes/minimal.js +34 -0
- package/dist/themes/suggestions.d.ts +216 -0
- package/dist/themes/suggestions.d.ts.map +1 -0
- package/dist/themes/suggestions.js +546 -0
- package/dist/themes/types.d.ts +7 -0
- package/dist/themes/types.d.ts.map +1 -0
- package/dist/themes/types.js +6 -0
- package/dist/types/index.d.ts +33 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/package.json +65 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useNaturalLanguageFilters Hook
|
|
3
|
+
*
|
|
4
|
+
* Convert natural language queries to structured filters
|
|
5
|
+
* Examples:
|
|
6
|
+
* - "red shirts under $50" → color: red, category: shirts, price: <= 50
|
|
7
|
+
* - "size large blue jeans" → size: L, color: blue, category: jeans
|
|
8
|
+
*/
|
|
9
|
+
import { useMemo, useCallback } from 'react';
|
|
10
|
+
import { useSearchState } from './useSearchState';
|
|
11
|
+
// Default patterns for parsing natural language
|
|
12
|
+
const DEFAULT_PATTERNS = {
|
|
13
|
+
// Price patterns
|
|
14
|
+
price: [
|
|
15
|
+
{ pattern: /under\s*\$?(\d+(?:\.\d{2})?)/i, operator: '<=', field: 'price' },
|
|
16
|
+
{ pattern: /less than\s*\$?(\d+(?:\.\d{2})?)/i, operator: '<', field: 'price' },
|
|
17
|
+
{ pattern: /over\s*\$?(\d+(?:\.\d{2})?)/i, operator: '>=', field: 'price' },
|
|
18
|
+
{ pattern: /more than\s*\$?(\d+(?:\.\d{2})?)/i, operator: '>', field: 'price' },
|
|
19
|
+
{ pattern: /\$(\d+(?:\.\d{2})?)\s*-\s*\$?(\d+(?:\.\d{2})?)/i, operator: 'range', field: 'price' },
|
|
20
|
+
{ pattern: /between\s*\$?(\d+(?:\.\d{2})?)\s*and\s*\$?(\d+(?:\.\d{2})?)/i, operator: 'range', field: 'price' },
|
|
21
|
+
{ pattern: /\$(\d+(?:\.\d{2})?)/i, operator: '<=', field: 'price' },
|
|
22
|
+
],
|
|
23
|
+
// Color patterns
|
|
24
|
+
colors: ['red', 'blue', 'green', 'black', 'white', 'pink', 'purple', 'yellow', 'orange', 'brown', 'gray', 'grey', 'navy', 'beige', 'gold', 'silver'],
|
|
25
|
+
// Size patterns
|
|
26
|
+
sizes: {
|
|
27
|
+
'extra small': 'XS',
|
|
28
|
+
'xs': 'XS',
|
|
29
|
+
'small': 'S',
|
|
30
|
+
's': 'S',
|
|
31
|
+
'medium': 'M',
|
|
32
|
+
'm': 'M',
|
|
33
|
+
'large': 'L',
|
|
34
|
+
'l': 'L',
|
|
35
|
+
'extra large': 'XL',
|
|
36
|
+
'xl': 'XL',
|
|
37
|
+
'xxl': 'XXL',
|
|
38
|
+
'2xl': 'XXL',
|
|
39
|
+
'xxxl': 'XXXL',
|
|
40
|
+
'3xl': 'XXXL',
|
|
41
|
+
},
|
|
42
|
+
// Brand patterns (common examples)
|
|
43
|
+
brandIndicators: ['by', 'from', 'brand'],
|
|
44
|
+
// Rating patterns
|
|
45
|
+
ratings: [
|
|
46
|
+
{ pattern: /(\d+)\s*\+?\s*stars?/i, field: 'rating', operator: '>=' },
|
|
47
|
+
{ pattern: /rated\s*(\d+)/i, field: 'rating', operator: '>=' },
|
|
48
|
+
{ pattern: /top\s*rated/i, field: 'rating', value: '4', operator: '>=' },
|
|
49
|
+
],
|
|
50
|
+
// Availability patterns
|
|
51
|
+
availability: [
|
|
52
|
+
{ pattern: /in\s*stock/i, field: 'inStock', value: 'true' },
|
|
53
|
+
{ pattern: /available/i, field: 'inStock', value: 'true' },
|
|
54
|
+
{ pattern: /on\s*sale/i, field: 'onSale', value: 'true' },
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
export function useNaturalLanguageFilters(options = {}) {
|
|
58
|
+
const { fieldMappings = {}, valueMappings = {}, autoApply = false, currencySymbol = '$', } = options;
|
|
59
|
+
const { addRefinement, removeRefinement, clearRefinements, setQuery } = useSearchState();
|
|
60
|
+
// Merge default patterns with custom mappings
|
|
61
|
+
const patterns = useMemo(() => ({
|
|
62
|
+
...DEFAULT_PATTERNS,
|
|
63
|
+
colors: [...DEFAULT_PATTERNS.colors, ...(valueMappings.color ? Object.keys(valueMappings.color) : [])],
|
|
64
|
+
sizes: { ...DEFAULT_PATTERNS.sizes, ...(valueMappings.size || {}) },
|
|
65
|
+
}), [valueMappings]);
|
|
66
|
+
const parse = useCallback((query) => {
|
|
67
|
+
const filters = [];
|
|
68
|
+
let cleanedQuery = query;
|
|
69
|
+
// Parse price filters
|
|
70
|
+
for (const pricePattern of patterns.price) {
|
|
71
|
+
const match = query.match(pricePattern.pattern);
|
|
72
|
+
if (match) {
|
|
73
|
+
if (pricePattern.operator === 'range' && match[2]) {
|
|
74
|
+
// Handle price range
|
|
75
|
+
filters.push({
|
|
76
|
+
field: fieldMappings.price || 'price',
|
|
77
|
+
value: `>=${match[1]}`,
|
|
78
|
+
operator: '>=',
|
|
79
|
+
confidence: 0.95,
|
|
80
|
+
matchedText: match[0],
|
|
81
|
+
});
|
|
82
|
+
filters.push({
|
|
83
|
+
field: fieldMappings.price || 'price',
|
|
84
|
+
value: `<=${match[2]}`,
|
|
85
|
+
operator: '<=',
|
|
86
|
+
confidence: 0.95,
|
|
87
|
+
matchedText: match[0],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
else if (match[1]) {
|
|
91
|
+
filters.push({
|
|
92
|
+
field: fieldMappings.price || 'price',
|
|
93
|
+
value: `${pricePattern.operator}${match[1]}`,
|
|
94
|
+
operator: pricePattern.operator,
|
|
95
|
+
confidence: 0.95,
|
|
96
|
+
matchedText: match[0],
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
cleanedQuery = cleanedQuery.replace(match[0], ' ');
|
|
100
|
+
break; // Only match first price pattern
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Parse colors
|
|
104
|
+
const colorRegex = new RegExp(`\\b(${patterns.colors.join('|')})\\b`, 'gi');
|
|
105
|
+
const colorMatches = query.match(colorRegex);
|
|
106
|
+
if (colorMatches) {
|
|
107
|
+
colorMatches.forEach(color => {
|
|
108
|
+
const normalizedColor = valueMappings.color?.[color.toLowerCase()] || color.toLowerCase();
|
|
109
|
+
filters.push({
|
|
110
|
+
field: fieldMappings.color || 'color',
|
|
111
|
+
value: normalizedColor,
|
|
112
|
+
operator: '=',
|
|
113
|
+
confidence: 0.9,
|
|
114
|
+
matchedText: color,
|
|
115
|
+
});
|
|
116
|
+
cleanedQuery = cleanedQuery.replace(new RegExp(`\\b${color}\\b`, 'gi'), ' ');
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
// Parse sizes
|
|
120
|
+
const sizeEntries = Object.entries(patterns.sizes);
|
|
121
|
+
for (const [sizeText, sizeValue] of sizeEntries) {
|
|
122
|
+
const sizeRegex = new RegExp(`\\b${sizeText}\\b`, 'gi');
|
|
123
|
+
if (sizeRegex.test(query)) {
|
|
124
|
+
filters.push({
|
|
125
|
+
field: fieldMappings.size || 'size',
|
|
126
|
+
value: sizeValue,
|
|
127
|
+
operator: '=',
|
|
128
|
+
confidence: 0.85,
|
|
129
|
+
matchedText: sizeText,
|
|
130
|
+
});
|
|
131
|
+
cleanedQuery = cleanedQuery.replace(sizeRegex, ' ');
|
|
132
|
+
break; // Only match first size
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Parse ratings
|
|
136
|
+
for (const ratingPattern of patterns.ratings) {
|
|
137
|
+
if ('value' in ratingPattern) {
|
|
138
|
+
if (ratingPattern.pattern.test(query)) {
|
|
139
|
+
filters.push({
|
|
140
|
+
field: fieldMappings.rating || ratingPattern.field,
|
|
141
|
+
value: `${ratingPattern.operator}${ratingPattern.value}`,
|
|
142
|
+
operator: ratingPattern.operator,
|
|
143
|
+
confidence: 0.8,
|
|
144
|
+
matchedText: query.match(ratingPattern.pattern)?.[0] || '',
|
|
145
|
+
});
|
|
146
|
+
cleanedQuery = cleanedQuery.replace(ratingPattern.pattern, ' ');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const match = query.match(ratingPattern.pattern);
|
|
151
|
+
if (match && match[1]) {
|
|
152
|
+
filters.push({
|
|
153
|
+
field: fieldMappings.rating || ratingPattern.field,
|
|
154
|
+
value: `${ratingPattern.operator}${match[1]}`,
|
|
155
|
+
operator: ratingPattern.operator,
|
|
156
|
+
confidence: 0.85,
|
|
157
|
+
matchedText: match[0],
|
|
158
|
+
});
|
|
159
|
+
cleanedQuery = cleanedQuery.replace(match[0], ' ');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Parse availability
|
|
164
|
+
for (const availPattern of patterns.availability) {
|
|
165
|
+
if (availPattern.pattern.test(query)) {
|
|
166
|
+
filters.push({
|
|
167
|
+
field: fieldMappings[availPattern.field] || availPattern.field,
|
|
168
|
+
value: availPattern.value,
|
|
169
|
+
operator: '=',
|
|
170
|
+
confidence: 0.9,
|
|
171
|
+
matchedText: query.match(availPattern.pattern)?.[0] || '',
|
|
172
|
+
});
|
|
173
|
+
cleanedQuery = cleanedQuery.replace(availPattern.pattern, ' ');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Clean up the query
|
|
177
|
+
cleanedQuery = cleanedQuery
|
|
178
|
+
.replace(/\s+/g, ' ')
|
|
179
|
+
.trim();
|
|
180
|
+
return {
|
|
181
|
+
cleanedQuery,
|
|
182
|
+
filters,
|
|
183
|
+
originalQuery: query,
|
|
184
|
+
hasFilters: filters.length > 0,
|
|
185
|
+
};
|
|
186
|
+
}, [patterns, fieldMappings, valueMappings]);
|
|
187
|
+
const applyFilters = useCallback((filters) => {
|
|
188
|
+
filters.forEach(filter => {
|
|
189
|
+
addRefinement(filter.field, filter.value, false);
|
|
190
|
+
});
|
|
191
|
+
// Trigger search after all filters are added
|
|
192
|
+
if (filters.length > 0) {
|
|
193
|
+
// The last addRefinement with triggerSearch=true would be called
|
|
194
|
+
// But we add them all with false and rely on the query change to trigger
|
|
195
|
+
}
|
|
196
|
+
}, [addRefinement]);
|
|
197
|
+
const parseAndApply = useCallback((query) => {
|
|
198
|
+
const result = parse(query);
|
|
199
|
+
if (result.hasFilters) {
|
|
200
|
+
applyFilters(result.filters);
|
|
201
|
+
// Update the query to the cleaned version
|
|
202
|
+
if (result.cleanedQuery !== query) {
|
|
203
|
+
setQuery(result.cleanedQuery, true);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}, [parse, applyFilters, setQuery]);
|
|
208
|
+
return {
|
|
209
|
+
parse,
|
|
210
|
+
applyFilters,
|
|
211
|
+
parseAndApply,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Utility to format parsed filters for display
|
|
216
|
+
*/
|
|
217
|
+
export function formatParsedFilters(filters) {
|
|
218
|
+
return filters
|
|
219
|
+
.map(f => `${f.field}: ${f.value}`)
|
|
220
|
+
.join(', ');
|
|
221
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useQuerySuggestions Hook
|
|
3
|
+
*
|
|
4
|
+
* Hook for fetching query suggestions with debouncing
|
|
5
|
+
*/
|
|
6
|
+
import type { SeekoraClient } from '@seekora-ai/search-sdk';
|
|
7
|
+
import type { SuggestionItem } from '../types';
|
|
8
|
+
export interface UseQuerySuggestionsOptions {
|
|
9
|
+
client: SeekoraClient;
|
|
10
|
+
query: string;
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
debounceMs?: number;
|
|
13
|
+
maxSuggestions?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface UseQuerySuggestionsReturn {
|
|
16
|
+
suggestions: SuggestionItem[];
|
|
17
|
+
loading: boolean;
|
|
18
|
+
error: Error | null;
|
|
19
|
+
}
|
|
20
|
+
export declare const useQuerySuggestions: ({ client, query, enabled, debounceMs, maxSuggestions, }: UseQuerySuggestionsOptions) => UseQuerySuggestionsReturn;
|
|
21
|
+
//# sourceMappingURL=useQuerySuggestions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useQuerySuggestions.d.ts","sourceRoot":"","sources":["../../src/hooks/useQuerySuggestions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,eAAO,MAAM,mBAAmB,GAAI,yDAMjC,0BAA0B,KAAG,yBAqE/B,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useQuerySuggestions Hook
|
|
3
|
+
*
|
|
4
|
+
* Hook for fetching query suggestions with debouncing
|
|
5
|
+
*/
|
|
6
|
+
import { useState, useEffect, useRef } from 'react';
|
|
7
|
+
import { log } from '@seekora-ai/ui-sdk-core';
|
|
8
|
+
export const useQuerySuggestions = ({ client, query, enabled = true, debounceMs = 300, maxSuggestions = 10, }) => {
|
|
9
|
+
const [suggestions, setSuggestions] = useState([]);
|
|
10
|
+
const [loading, setLoading] = useState(false);
|
|
11
|
+
const [error, setError] = useState(null);
|
|
12
|
+
const debounceTimerRef = useRef(null);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// Clear previous timer
|
|
15
|
+
if (debounceTimerRef.current) {
|
|
16
|
+
clearTimeout(debounceTimerRef.current);
|
|
17
|
+
}
|
|
18
|
+
if (!enabled || !query.trim()) {
|
|
19
|
+
setSuggestions([]);
|
|
20
|
+
setLoading(false);
|
|
21
|
+
setError(null);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
setLoading(true);
|
|
25
|
+
setError(null);
|
|
26
|
+
debounceTimerRef.current = setTimeout(async () => {
|
|
27
|
+
try {
|
|
28
|
+
const response = await client.getSuggestions?.(query, maxSuggestions);
|
|
29
|
+
// Transform suggestions to SuggestionItem format
|
|
30
|
+
// getSuggestions returns an array of suggestion objects with structure:
|
|
31
|
+
// { query: string, popularity: number, objectID: string, ... }
|
|
32
|
+
const rawSuggestions = Array.isArray(response) ? response : [];
|
|
33
|
+
const suggestionItems = rawSuggestions.map((suggestion) => {
|
|
34
|
+
// Extract query - it should be in suggestion.query
|
|
35
|
+
const suggestionQuery = suggestion.query || suggestion.text || suggestion;
|
|
36
|
+
// Extract count/popularity
|
|
37
|
+
const count = suggestion.popularity !== undefined ? suggestion.popularity : (suggestion.count !== undefined ? suggestion.count : undefined);
|
|
38
|
+
const item = {
|
|
39
|
+
query: typeof suggestionQuery === 'string' ? suggestionQuery : String(suggestionQuery),
|
|
40
|
+
count: typeof count === 'number' ? count : undefined,
|
|
41
|
+
metadata: suggestion,
|
|
42
|
+
};
|
|
43
|
+
return item;
|
|
44
|
+
});
|
|
45
|
+
setSuggestions(suggestionItems);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
49
|
+
log.error('Error in useQuerySuggestions:', error);
|
|
50
|
+
setError(error);
|
|
51
|
+
setSuggestions([]);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
setLoading(false);
|
|
55
|
+
}
|
|
56
|
+
}, debounceMs);
|
|
57
|
+
return () => {
|
|
58
|
+
if (debounceTimerRef.current) {
|
|
59
|
+
clearTimeout(debounceTimerRef.current);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}, [client, query, enabled, debounceMs, maxSuggestions]);
|
|
63
|
+
return {
|
|
64
|
+
suggestions,
|
|
65
|
+
loading,
|
|
66
|
+
error,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Query Suggestions Hook
|
|
3
|
+
*
|
|
4
|
+
* Provides full support for query suggestions API including:
|
|
5
|
+
* - Basic suggestions
|
|
6
|
+
* - Dropdown recommendations (trending, top searches, related)
|
|
7
|
+
* - Filtered tabs with products
|
|
8
|
+
* - Recent searches (local storage)
|
|
9
|
+
* - Analytics tracking
|
|
10
|
+
*/
|
|
11
|
+
import type { SeekoraClient } from '@seekora-ai/search-sdk';
|
|
12
|
+
import type { SuggestionItem, ProductItem, FilteredTab, DropdownRecommendations, RecentSearch, QuerySuggestionsResponse, TrendingSearch, TopSearch, RelatedSearch, PopularBrand, SuggestionCategory } from '@seekora-ai/ui-sdk-types';
|
|
13
|
+
export interface UseQuerySuggestionsEnhancedOptions {
|
|
14
|
+
/** Seekora client instance */
|
|
15
|
+
client: SeekoraClient;
|
|
16
|
+
/** Current search query */
|
|
17
|
+
query: string;
|
|
18
|
+
/** Enable/disable the hook */
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
/** Debounce delay in ms */
|
|
21
|
+
debounceMs?: number;
|
|
22
|
+
/** Max suggestions to fetch */
|
|
23
|
+
maxSuggestions?: number;
|
|
24
|
+
/** Minimum query length to trigger search */
|
|
25
|
+
minQueryLength?: number;
|
|
26
|
+
/** Include dropdown recommendations (trending, products, etc.) */
|
|
27
|
+
includeDropdownRecommendations?: boolean;
|
|
28
|
+
/** Include categories in suggestions */
|
|
29
|
+
includeCategories?: boolean;
|
|
30
|
+
/** Include facets in suggestions */
|
|
31
|
+
includeFacets?: boolean;
|
|
32
|
+
/** Max categories per suggestion */
|
|
33
|
+
maxCategories?: number;
|
|
34
|
+
/** Max facets per suggestion */
|
|
35
|
+
maxFacets?: number;
|
|
36
|
+
/** Filtered tabs configuration */
|
|
37
|
+
filteredTabs?: Array<{
|
|
38
|
+
id?: string;
|
|
39
|
+
label: string;
|
|
40
|
+
filter: string;
|
|
41
|
+
}>;
|
|
42
|
+
/** Minimum popularity threshold */
|
|
43
|
+
minPopularity?: number;
|
|
44
|
+
/** Time range for analytics data */
|
|
45
|
+
timeRange?: '7d' | '30d' | '90d';
|
|
46
|
+
/** Disable typo tolerance */
|
|
47
|
+
disableTypoTolerance?: boolean;
|
|
48
|
+
/** Analytics tags */
|
|
49
|
+
analyticsTags?: string[];
|
|
50
|
+
/** Enable recent searches from localStorage */
|
|
51
|
+
enableRecentSearches?: boolean;
|
|
52
|
+
/** Max recent searches to store */
|
|
53
|
+
maxRecentSearches?: number;
|
|
54
|
+
/** Local storage key for recent searches */
|
|
55
|
+
recentSearchesKey?: string;
|
|
56
|
+
/** Callback when suggestions are loaded */
|
|
57
|
+
onSuggestionsLoaded?: (response: QuerySuggestionsResponse) => void;
|
|
58
|
+
/** Callback on error */
|
|
59
|
+
onError?: (error: Error) => void;
|
|
60
|
+
}
|
|
61
|
+
export interface UseQuerySuggestionsEnhancedReturn {
|
|
62
|
+
/** Basic suggestion items */
|
|
63
|
+
suggestions: SuggestionItem[];
|
|
64
|
+
/** Loading state */
|
|
65
|
+
loading: boolean;
|
|
66
|
+
/** Error state */
|
|
67
|
+
error: Error | null;
|
|
68
|
+
/** Dropdown recommendations */
|
|
69
|
+
dropdownRecommendations: DropdownRecommendations | null;
|
|
70
|
+
/** Trending searches */
|
|
71
|
+
trendingSearches: TrendingSearch[];
|
|
72
|
+
/** Top searches */
|
|
73
|
+
topSearches: TopSearch[];
|
|
74
|
+
/** Related searches */
|
|
75
|
+
relatedSearches: RelatedSearch[];
|
|
76
|
+
/** Popular brands */
|
|
77
|
+
popularBrands: PopularBrand[];
|
|
78
|
+
/** Filtered tabs with products */
|
|
79
|
+
filteredTabs: FilteredTab[];
|
|
80
|
+
/** Trending products */
|
|
81
|
+
trendingProducts: ProductItem[];
|
|
82
|
+
/** Recent searches (from localStorage) */
|
|
83
|
+
recentSearches: RecentSearch[];
|
|
84
|
+
/** Processing time in ms */
|
|
85
|
+
processingTimeMs: number | null;
|
|
86
|
+
/** Total hits count */
|
|
87
|
+
totalHits: number;
|
|
88
|
+
/** Current page */
|
|
89
|
+
currentPage: number;
|
|
90
|
+
/** Total pages */
|
|
91
|
+
totalPages: number;
|
|
92
|
+
/** Original query */
|
|
93
|
+
originalQuery: string;
|
|
94
|
+
/** Add a search to recent searches */
|
|
95
|
+
addRecentSearch: (query: string, resultsCount?: number) => void;
|
|
96
|
+
/** Remove a search from recent searches */
|
|
97
|
+
removeRecentSearch: (query: string) => void;
|
|
98
|
+
/** Clear all recent searches */
|
|
99
|
+
clearRecentSearches: () => void;
|
|
100
|
+
/** Manually refetch suggestions */
|
|
101
|
+
refetch: () => Promise<void>;
|
|
102
|
+
/** Check if dropdown has any content to show */
|
|
103
|
+
hasContent: boolean;
|
|
104
|
+
/** Get all items for keyboard navigation (flattened) */
|
|
105
|
+
getAllNavigableItems: () => NavigableItem[];
|
|
106
|
+
}
|
|
107
|
+
export interface NavigableItem {
|
|
108
|
+
type: 'suggestion' | 'recent' | 'trending' | 'product' | 'category' | 'brand' | 'tab';
|
|
109
|
+
index: number;
|
|
110
|
+
data: SuggestionItem | RecentSearch | TrendingSearch | ProductItem | SuggestionCategory | PopularBrand | FilteredTab;
|
|
111
|
+
}
|
|
112
|
+
export declare function useQuerySuggestionsEnhanced(options: UseQuerySuggestionsEnhancedOptions): UseQuerySuggestionsEnhancedReturn;
|
|
113
|
+
export default useQuerySuggestionsEnhanced;
|
|
114
|
+
//# sourceMappingURL=useQuerySuggestionsEnhanced.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useQuerySuggestionsEnhanced.d.ts","sourceRoot":"","sources":["../../src/hooks/useQuerySuggestionsEnhanced.ts"],"names":[],"mappings":"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"}
|