@seekora-ai/ui-sdk-react 1.0.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.
Files changed (169) hide show
  1. package/dist/components/Breadcrumb.d.ts +43 -0
  2. package/dist/components/Breadcrumb.d.ts.map +1 -0
  3. package/dist/components/Breadcrumb.js +119 -0
  4. package/dist/components/ClearRefinements.d.ts +42 -0
  5. package/dist/components/ClearRefinements.d.ts.map +1 -0
  6. package/dist/components/ClearRefinements.js +80 -0
  7. package/dist/components/CurrentRefinements.d.ts +41 -0
  8. package/dist/components/CurrentRefinements.d.ts.map +1 -0
  9. package/dist/components/CurrentRefinements.js +83 -0
  10. package/dist/components/Facets.d.ts +53 -0
  11. package/dist/components/Facets.d.ts.map +1 -0
  12. package/dist/components/Facets.js +195 -0
  13. package/dist/components/FederatedDropdown.d.ts +92 -0
  14. package/dist/components/FederatedDropdown.d.ts.map +1 -0
  15. package/dist/components/FederatedDropdown.js +510 -0
  16. package/dist/components/HierarchicalMenu.d.ts +55 -0
  17. package/dist/components/HierarchicalMenu.d.ts.map +1 -0
  18. package/dist/components/HierarchicalMenu.js +168 -0
  19. package/dist/components/Highlight.d.ts +51 -0
  20. package/dist/components/Highlight.d.ts.map +1 -0
  21. package/dist/components/Highlight.js +155 -0
  22. package/dist/components/HitsPerPage.d.ts +41 -0
  23. package/dist/components/HitsPerPage.d.ts.map +1 -0
  24. package/dist/components/HitsPerPage.js +72 -0
  25. package/dist/components/InfiniteHits.d.ts +56 -0
  26. package/dist/components/InfiniteHits.d.ts.map +1 -0
  27. package/dist/components/InfiniteHits.js +181 -0
  28. package/dist/components/MobileFilters.d.ts +71 -0
  29. package/dist/components/MobileFilters.d.ts.map +1 -0
  30. package/dist/components/MobileFilters.js +242 -0
  31. package/dist/components/Pagination.d.ts +44 -0
  32. package/dist/components/Pagination.d.ts.map +1 -0
  33. package/dist/components/Pagination.js +142 -0
  34. package/dist/components/QuerySuggestions.d.ts +38 -0
  35. package/dist/components/QuerySuggestions.d.ts.map +1 -0
  36. package/dist/components/QuerySuggestions.js +86 -0
  37. package/dist/components/QuerySuggestionsDropdown.d.ts +86 -0
  38. package/dist/components/QuerySuggestionsDropdown.d.ts.map +1 -0
  39. package/dist/components/QuerySuggestionsDropdown.js +395 -0
  40. package/dist/components/RangeInput.d.ts +58 -0
  41. package/dist/components/RangeInput.d.ts.map +1 -0
  42. package/dist/components/RangeInput.js +203 -0
  43. package/dist/components/RangeSlider.d.ts +51 -0
  44. package/dist/components/RangeSlider.d.ts.map +1 -0
  45. package/dist/components/RangeSlider.js +193 -0
  46. package/dist/components/Recommendations.d.ts +90 -0
  47. package/dist/components/Recommendations.d.ts.map +1 -0
  48. package/dist/components/Recommendations.js +270 -0
  49. package/dist/components/RichQuerySuggestions.d.ts +77 -0
  50. package/dist/components/RichQuerySuggestions.d.ts.map +1 -0
  51. package/dist/components/RichQuerySuggestions.js +492 -0
  52. package/dist/components/SearchBar.d.ts +40 -0
  53. package/dist/components/SearchBar.d.ts.map +1 -0
  54. package/dist/components/SearchBar.js +217 -0
  55. package/dist/components/SearchBarWithSuggestions.d.ts +99 -0
  56. package/dist/components/SearchBarWithSuggestions.d.ts.map +1 -0
  57. package/dist/components/SearchBarWithSuggestions.js +275 -0
  58. package/dist/components/SearchLayout.d.ts +35 -0
  59. package/dist/components/SearchLayout.d.ts.map +1 -0
  60. package/dist/components/SearchLayout.js +56 -0
  61. package/dist/components/SearchProvider.d.ts +28 -0
  62. package/dist/components/SearchProvider.d.ts.map +1 -0
  63. package/dist/components/SearchProvider.js +43 -0
  64. package/dist/components/SearchResults.d.ts +51 -0
  65. package/dist/components/SearchResults.d.ts.map +1 -0
  66. package/dist/components/SearchResults.js +485 -0
  67. package/dist/components/SortBy.d.ts +44 -0
  68. package/dist/components/SortBy.d.ts.map +1 -0
  69. package/dist/components/SortBy.js +61 -0
  70. package/dist/components/Stats.d.ts +37 -0
  71. package/dist/components/Stats.d.ts.map +1 -0
  72. package/dist/components/Stats.js +52 -0
  73. package/dist/components/suggestions/AmazonDropdown.d.ts +30 -0
  74. package/dist/components/suggestions/AmazonDropdown.d.ts.map +1 -0
  75. package/dist/components/suggestions/AmazonDropdown.js +529 -0
  76. package/dist/components/suggestions/GoogleDropdown.d.ts +31 -0
  77. package/dist/components/suggestions/GoogleDropdown.d.ts.map +1 -0
  78. package/dist/components/suggestions/GoogleDropdown.js +370 -0
  79. package/dist/components/suggestions/MinimalDropdown.d.ts +24 -0
  80. package/dist/components/suggestions/MinimalDropdown.d.ts.map +1 -0
  81. package/dist/components/suggestions/MinimalDropdown.js +314 -0
  82. package/dist/components/suggestions/MobileSheetDropdown.d.ts +31 -0
  83. package/dist/components/suggestions/MobileSheetDropdown.d.ts.map +1 -0
  84. package/dist/components/suggestions/MobileSheetDropdown.js +485 -0
  85. package/dist/components/suggestions/PinterestDropdown.d.ts +29 -0
  86. package/dist/components/suggestions/PinterestDropdown.d.ts.map +1 -0
  87. package/dist/components/suggestions/PinterestDropdown.js +450 -0
  88. package/dist/components/suggestions/ShopifyDropdown.d.ts +27 -0
  89. package/dist/components/suggestions/ShopifyDropdown.d.ts.map +1 -0
  90. package/dist/components/suggestions/ShopifyDropdown.js +451 -0
  91. package/dist/components/suggestions/SpotlightDropdown.d.ts +33 -0
  92. package/dist/components/suggestions/SpotlightDropdown.d.ts.map +1 -0
  93. package/dist/components/suggestions/SpotlightDropdown.js +547 -0
  94. package/dist/components/suggestions/SuggestionSearchBar.d.ts +123 -0
  95. package/dist/components/suggestions/SuggestionSearchBar.d.ts.map +1 -0
  96. package/dist/components/suggestions/SuggestionSearchBar.js +652 -0
  97. package/dist/components/suggestions/index.d.ts +37 -0
  98. package/dist/components/suggestions/index.d.ts.map +1 -0
  99. package/dist/components/suggestions/index.js +59 -0
  100. package/dist/components/suggestions/styles/index.d.ts +11 -0
  101. package/dist/components/suggestions/styles/index.d.ts.map +1 -0
  102. package/dist/components/suggestions/styles/index.js +289 -0
  103. package/dist/components/suggestions/styles/responsive.d.ts +107 -0
  104. package/dist/components/suggestions/styles/responsive.d.ts.map +1 -0
  105. package/dist/components/suggestions/styles/responsive.js +237 -0
  106. package/dist/components/suggestions/types.d.ts +489 -0
  107. package/dist/components/suggestions/types.d.ts.map +1 -0
  108. package/dist/components/suggestions/types.js +6 -0
  109. package/dist/components/suggestions/utils.d.ts +213 -0
  110. package/dist/components/suggestions/utils.d.ts.map +1 -0
  111. package/dist/components/suggestions/utils.js +514 -0
  112. package/dist/hooks/useAnalytics.d.ts +20 -0
  113. package/dist/hooks/useAnalytics.d.ts.map +1 -0
  114. package/dist/hooks/useAnalytics.js +62 -0
  115. package/dist/hooks/useNaturalLanguageFilters.d.ts +48 -0
  116. package/dist/hooks/useNaturalLanguageFilters.d.ts.map +1 -0
  117. package/dist/hooks/useNaturalLanguageFilters.js +221 -0
  118. package/dist/hooks/useQuerySuggestions.d.ts +21 -0
  119. package/dist/hooks/useQuerySuggestions.d.ts.map +1 -0
  120. package/dist/hooks/useQuerySuggestions.js +68 -0
  121. package/dist/hooks/useQuerySuggestionsEnhanced.d.ts +114 -0
  122. package/dist/hooks/useQuerySuggestionsEnhanced.d.ts.map +1 -0
  123. package/dist/hooks/useQuerySuggestionsEnhanced.js +376 -0
  124. package/dist/hooks/useSearchState.d.ts +35 -0
  125. package/dist/hooks/useSearchState.d.ts.map +1 -0
  126. package/dist/hooks/useSearchState.js +68 -0
  127. package/dist/hooks/useSeekoraSearch.d.ts +20 -0
  128. package/dist/hooks/useSeekoraSearch.d.ts.map +1 -0
  129. package/dist/hooks/useSeekoraSearch.js +63 -0
  130. package/dist/hooks/useSmartSuggestions.d.ts +55 -0
  131. package/dist/hooks/useSmartSuggestions.d.ts.map +1 -0
  132. package/dist/hooks/useSmartSuggestions.js +236 -0
  133. package/dist/hooks/useSuggestionsAnalytics.d.ts +91 -0
  134. package/dist/hooks/useSuggestionsAnalytics.d.ts.map +1 -0
  135. package/dist/hooks/useSuggestionsAnalytics.js +226 -0
  136. package/dist/index.d.ts +80 -0
  137. package/dist/index.d.ts.map +1 -0
  138. package/dist/index.js +86 -0
  139. package/dist/index.umd.js +1 -0
  140. package/dist/src/index.d.ts +2849 -0
  141. package/dist/src/index.esm.js +11679 -0
  142. package/dist/src/index.esm.js.map +1 -0
  143. package/dist/src/index.js +11761 -0
  144. package/dist/src/index.js.map +1 -0
  145. package/dist/themes/createTheme.d.ts +8 -0
  146. package/dist/themes/createTheme.d.ts.map +1 -0
  147. package/dist/themes/createTheme.js +10 -0
  148. package/dist/themes/dark.d.ts +6 -0
  149. package/dist/themes/dark.d.ts.map +1 -0
  150. package/dist/themes/dark.js +34 -0
  151. package/dist/themes/default.d.ts +6 -0
  152. package/dist/themes/default.d.ts.map +1 -0
  153. package/dist/themes/default.js +71 -0
  154. package/dist/themes/mergeThemes.d.ts +7 -0
  155. package/dist/themes/mergeThemes.d.ts.map +1 -0
  156. package/dist/themes/mergeThemes.js +6 -0
  157. package/dist/themes/minimal.d.ts +6 -0
  158. package/dist/themes/minimal.d.ts.map +1 -0
  159. package/dist/themes/minimal.js +34 -0
  160. package/dist/themes/suggestions.d.ts +216 -0
  161. package/dist/themes/suggestions.d.ts.map +1 -0
  162. package/dist/themes/suggestions.js +546 -0
  163. package/dist/themes/types.d.ts +7 -0
  164. package/dist/themes/types.d.ts.map +1 -0
  165. package/dist/themes/types.js +6 -0
  166. package/dist/types/index.d.ts +33 -0
  167. package/dist/types/index.d.ts.map +1 -0
  168. package/dist/types/index.js +4 -0
  169. package/package.json +65 -0
@@ -0,0 +1,376 @@
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 { useState, useEffect, useRef, useCallback, useMemo } from 'react';
12
+ import { log } from '@seekora-ai/ui-sdk-core';
13
+ // ============================================================================
14
+ // Local Storage Helpers
15
+ // ============================================================================
16
+ const RECENT_SEARCHES_DEFAULT_KEY = 'seekora_recent_searches';
17
+ const MAX_RECENT_SEARCHES_DEFAULT = 10;
18
+ function getRecentSearchesFromStorage(key) {
19
+ if (typeof window === 'undefined')
20
+ return [];
21
+ try {
22
+ const stored = localStorage.getItem(key);
23
+ if (!stored)
24
+ return [];
25
+ return JSON.parse(stored);
26
+ }
27
+ catch {
28
+ return [];
29
+ }
30
+ }
31
+ function saveRecentSearchesToStorage(key, searches) {
32
+ if (typeof window === 'undefined')
33
+ return;
34
+ try {
35
+ localStorage.setItem(key, JSON.stringify(searches));
36
+ }
37
+ catch (error) {
38
+ log.warn('Failed to save recent searches to localStorage', { error });
39
+ }
40
+ }
41
+ // ============================================================================
42
+ // Transform API Response
43
+ // ============================================================================
44
+ function transformSuggestion(raw) {
45
+ const query = raw.query || raw.text || String(raw);
46
+ const highlighted = raw._highlightResult?.query?.value;
47
+ return {
48
+ query,
49
+ popularity: raw.popularity,
50
+ count: raw.instant_search?.exact_nb_hits ?? raw.popularity ?? raw.count,
51
+ objectID: raw.objectID,
52
+ highlightedQuery: highlighted ? parseHighlight(highlighted) : undefined,
53
+ categories: raw.categories?.map((c) => ({
54
+ value: c.value || c.name,
55
+ count: c.count,
56
+ path: c.path,
57
+ })),
58
+ facets: raw.instant_search?.facets || raw.facets,
59
+ metadata: raw,
60
+ };
61
+ }
62
+ function parseHighlight(highlighted) {
63
+ // Convert __ais-highlight__ markers to HTML
64
+ return highlighted
65
+ .replace(/__ais-highlight__/g, '<mark>')
66
+ .replace(/__\/ais-highlight__/g, '</mark>');
67
+ }
68
+ function transformProduct(raw) {
69
+ return {
70
+ id: raw.id || raw.objectID,
71
+ objectID: raw.objectID || raw.id,
72
+ title: raw.title || raw.name || raw.productName,
73
+ name: raw.name || raw.title,
74
+ image: raw.image || raw.imageUrl || raw.metadata?.image,
75
+ price: raw.price || raw.sellPrice || raw.metadata?.sellPrice,
76
+ currency: raw.currency || raw.metadata?.currency || '',
77
+ url: raw.url || raw.productId || raw.metadata?.url,
78
+ clicks: raw.clicks,
79
+ conversions: raw.conversions,
80
+ revenue: raw.revenue,
81
+ trend_score: raw.trend_score,
82
+ metadata: raw.metadata || raw,
83
+ };
84
+ }
85
+ function transformFilteredTab(raw) {
86
+ return {
87
+ id: raw.id,
88
+ label: raw.label,
89
+ filter: raw.filter,
90
+ products: raw.products?.map(transformProduct) || [],
91
+ nb_hits: raw.nb_hits,
92
+ processing_time_ms: raw.processing_time_ms,
93
+ };
94
+ }
95
+ // ============================================================================
96
+ // Main Hook
97
+ // ============================================================================
98
+ export function useQuerySuggestionsEnhanced(options) {
99
+ const { client, query, enabled = true, debounceMs = 200, maxSuggestions = 10, minQueryLength = 1, includeDropdownRecommendations = false, includeCategories = false, includeFacets = false, maxCategories = 3, maxFacets = 5, filteredTabs, minPopularity, timeRange, disableTypoTolerance, analyticsTags, enableRecentSearches = true, maxRecentSearches = MAX_RECENT_SEARCHES_DEFAULT, recentSearchesKey = RECENT_SEARCHES_DEFAULT_KEY, onSuggestionsLoaded, onError, } = options;
100
+ // State
101
+ const [suggestions, setSuggestions] = useState([]);
102
+ const [loading, setLoading] = useState(false);
103
+ const [error, setError] = useState(null);
104
+ const [dropdownRecommendations, setDropdownRecommendations] = useState(null);
105
+ const [recentSearches, setRecentSearches] = useState([]);
106
+ const [processingTimeMs, setProcessingTimeMs] = useState(null);
107
+ const [totalHits, setTotalHits] = useState(0);
108
+ const [currentPage, setCurrentPage] = useState(0);
109
+ const [totalPages, setTotalPages] = useState(0);
110
+ const [originalQuery, setOriginalQuery] = useState('');
111
+ // Refs
112
+ const debounceTimerRef = useRef(null);
113
+ const abortControllerRef = useRef(null);
114
+ const mountedRef = useRef(true);
115
+ // Load recent searches on mount
116
+ useEffect(() => {
117
+ if (enableRecentSearches) {
118
+ const stored = getRecentSearchesFromStorage(recentSearchesKey);
119
+ setRecentSearches(stored.slice(0, maxRecentSearches));
120
+ }
121
+ return () => {
122
+ mountedRef.current = false;
123
+ };
124
+ }, [enableRecentSearches, recentSearchesKey, maxRecentSearches]);
125
+ // Fetch suggestions
126
+ const fetchSuggestions = useCallback(async (searchQuery) => {
127
+ if (!client || !searchQuery.trim()) {
128
+ setSuggestions([]);
129
+ setDropdownRecommendations(null);
130
+ return;
131
+ }
132
+ // Cancel previous request
133
+ if (abortControllerRef.current) {
134
+ abortControllerRef.current.abort();
135
+ }
136
+ abortControllerRef.current = new AbortController();
137
+ setLoading(true);
138
+ setError(null);
139
+ try {
140
+ const response = await client.getSuggestions?.(searchQuery, {
141
+ hitsPerPage: maxSuggestions,
142
+ include_dropdown_recommendations: includeDropdownRecommendations || (filteredTabs && filteredTabs.length > 0),
143
+ include_categories: includeCategories,
144
+ include_facets: includeFacets,
145
+ max_categories: maxCategories,
146
+ max_facets: maxFacets,
147
+ filtered_tabs: filteredTabs,
148
+ min_popularity: minPopularity,
149
+ time_range: timeRange,
150
+ disable_typo_tolerance: disableTypoTolerance,
151
+ analytics_tags: analyticsTags,
152
+ returnFullResponse: true,
153
+ });
154
+ if (!mountedRef.current)
155
+ return;
156
+ // Transform suggestions
157
+ const rawSuggestions = response.suggestions || [];
158
+ const transformedSuggestions = rawSuggestions.map(transformSuggestion);
159
+ setSuggestions(transformedSuggestions);
160
+ // Extract dropdown recommendations from extensions
161
+ const extensions = (response.extensions || {});
162
+ const recommendations = {
163
+ trending_searches: Array.isArray(extensions.trending_searches) ? extensions.trending_searches : [],
164
+ top_searches: Array.isArray(extensions.top_searches) ? extensions.top_searches : [],
165
+ related_searches: Array.isArray(extensions.related_searches) ? extensions.related_searches : [],
166
+ trending_products: Array.isArray(extensions.trending_products) ? extensions.trending_products.map(transformProduct) : [],
167
+ item_recommendations: Array.isArray(extensions.item_recommendations) ? extensions.item_recommendations.map(transformProduct) : [],
168
+ popular_brands: Array.isArray(extensions.popular_brands) ? extensions.popular_brands : [],
169
+ filtered_tabs: Array.isArray(extensions.filtered_tabs) ? extensions.filtered_tabs.map(transformFilteredTab) : [],
170
+ processing_time_ms: typeof extensions.processing_time_ms === 'number' ? extensions.processing_time_ms : undefined,
171
+ cached_at: typeof extensions.cached_at === 'string' ? extensions.cached_at : undefined,
172
+ };
173
+ setDropdownRecommendations(recommendations);
174
+ // Extract metadata from raw response
175
+ const raw = response.raw;
176
+ const rawResultsArr = raw?.results;
177
+ const rawResult = (rawResultsArr?.[0] ?? raw ?? {});
178
+ const num = (v) => (typeof v === 'number' && !Number.isNaN(v) ? v : 0);
179
+ const str = (v) => (typeof v === 'string' ? v : '');
180
+ setProcessingTimeMs(typeof rawResult.processingTimeMS === 'number' ? rawResult.processingTimeMS : null);
181
+ setTotalHits(num(rawResult.nbHits) || transformedSuggestions.length);
182
+ setCurrentPage(num(rawResult.page) || 0);
183
+ setTotalPages(num(rawResult.nbPages) || 1);
184
+ setOriginalQuery(str(rawResult.query) || searchQuery);
185
+ // Callback
186
+ if (onSuggestionsLoaded) {
187
+ onSuggestionsLoaded({
188
+ suggestions: transformedSuggestions,
189
+ nbHits: num(rawResult.nbHits) || transformedSuggestions.length,
190
+ page: num(rawResult.page) || 0,
191
+ nbPages: num(rawResult.nbPages) || 1,
192
+ hitsPerPage: num(rawResult.hitsPerPage) || maxSuggestions,
193
+ processingTimeMS: typeof rawResult.processingTimeMS === 'number' ? rawResult.processingTimeMS : undefined,
194
+ query: str(rawResult.query) || searchQuery,
195
+ dropdownRecommendations: recommendations,
196
+ results: Array.isArray(response.results) ? response.results : undefined,
197
+ extensions,
198
+ });
199
+ }
200
+ log.verbose('Query suggestions loaded', {
201
+ query: searchQuery,
202
+ suggestionsCount: transformedSuggestions.length,
203
+ hasRecommendations: Object.keys(recommendations).some(k => Array.isArray(recommendations[k]) && recommendations[k].length > 0),
204
+ });
205
+ }
206
+ catch (err) {
207
+ if (!mountedRef.current)
208
+ return;
209
+ const error = err instanceof Error ? err : new Error(String(err));
210
+ if (error.name === 'AbortError')
211
+ return;
212
+ log.error('Failed to fetch query suggestions', { query: searchQuery, error: error.message });
213
+ setError(error);
214
+ setSuggestions([]);
215
+ setDropdownRecommendations(null);
216
+ if (onError) {
217
+ onError(error);
218
+ }
219
+ }
220
+ finally {
221
+ if (mountedRef.current) {
222
+ setLoading(false);
223
+ }
224
+ }
225
+ }, [
226
+ client,
227
+ maxSuggestions,
228
+ includeDropdownRecommendations,
229
+ includeCategories,
230
+ includeFacets,
231
+ maxCategories,
232
+ maxFacets,
233
+ filteredTabs,
234
+ minPopularity,
235
+ timeRange,
236
+ disableTypoTolerance,
237
+ analyticsTags,
238
+ onSuggestionsLoaded,
239
+ onError,
240
+ ]);
241
+ // Debounced fetch effect
242
+ useEffect(() => {
243
+ if (debounceTimerRef.current) {
244
+ clearTimeout(debounceTimerRef.current);
245
+ }
246
+ if (!enabled || query.length < minQueryLength) {
247
+ setSuggestions([]);
248
+ setLoading(false);
249
+ setError(null);
250
+ return;
251
+ }
252
+ setLoading(true);
253
+ debounceTimerRef.current = setTimeout(() => {
254
+ fetchSuggestions(query);
255
+ }, debounceMs);
256
+ return () => {
257
+ if (debounceTimerRef.current) {
258
+ clearTimeout(debounceTimerRef.current);
259
+ }
260
+ };
261
+ }, [query, enabled, minQueryLength, debounceMs, fetchSuggestions]);
262
+ // Recent search management
263
+ const addRecentSearch = useCallback((searchQuery, resultsCount) => {
264
+ if (!enableRecentSearches || !searchQuery.trim())
265
+ return;
266
+ setRecentSearches(prev => {
267
+ // Remove existing entry for same query
268
+ const filtered = prev.filter(s => s.query.toLowerCase() !== searchQuery.toLowerCase());
269
+ // Add new entry at the beginning
270
+ const newSearch = {
271
+ query: searchQuery.trim(),
272
+ timestamp: Date.now(),
273
+ resultsCount,
274
+ };
275
+ const updated = [newSearch, ...filtered].slice(0, maxRecentSearches);
276
+ saveRecentSearchesToStorage(recentSearchesKey, updated);
277
+ return updated;
278
+ });
279
+ }, [enableRecentSearches, maxRecentSearches, recentSearchesKey]);
280
+ const removeRecentSearch = useCallback((searchQuery) => {
281
+ setRecentSearches(prev => {
282
+ const updated = prev.filter(s => s.query !== searchQuery);
283
+ saveRecentSearchesToStorage(recentSearchesKey, updated);
284
+ return updated;
285
+ });
286
+ }, [recentSearchesKey]);
287
+ const clearRecentSearches = useCallback(() => {
288
+ setRecentSearches([]);
289
+ saveRecentSearchesToStorage(recentSearchesKey, []);
290
+ }, [recentSearchesKey]);
291
+ // Manual refetch
292
+ const refetch = useCallback(async () => {
293
+ if (query.length >= minQueryLength) {
294
+ await fetchSuggestions(query);
295
+ }
296
+ }, [query, minQueryLength, fetchSuggestions]);
297
+ // Derived values
298
+ const trendingSearches = dropdownRecommendations?.trending_searches || [];
299
+ const topSearches = dropdownRecommendations?.top_searches || [];
300
+ const relatedSearches = dropdownRecommendations?.related_searches || [];
301
+ const popularBrands = dropdownRecommendations?.popular_brands || [];
302
+ const filteredTabsResult = dropdownRecommendations?.filtered_tabs || [];
303
+ const trendingProducts = dropdownRecommendations?.trending_products || [];
304
+ const hasContent = useMemo(() => {
305
+ return (suggestions.length > 0 ||
306
+ recentSearches.length > 0 ||
307
+ trendingSearches.length > 0 ||
308
+ topSearches.length > 0 ||
309
+ relatedSearches.length > 0 ||
310
+ popularBrands.length > 0 ||
311
+ filteredTabsResult.length > 0 ||
312
+ trendingProducts.length > 0);
313
+ }, [
314
+ suggestions,
315
+ recentSearches,
316
+ trendingSearches,
317
+ topSearches,
318
+ relatedSearches,
319
+ popularBrands,
320
+ filteredTabsResult,
321
+ trendingProducts,
322
+ ]);
323
+ // Get all navigable items for keyboard navigation
324
+ const getAllNavigableItems = useCallback(() => {
325
+ const items = [];
326
+ let index = 0;
327
+ // Recent searches first (when query is empty)
328
+ if (query.length === 0 && recentSearches.length > 0) {
329
+ recentSearches.forEach(search => {
330
+ items.push({ type: 'recent', index: index++, data: search });
331
+ });
332
+ }
333
+ // Suggestions
334
+ suggestions.forEach(suggestion => {
335
+ items.push({ type: 'suggestion', index: index++, data: suggestion });
336
+ });
337
+ // Trending searches
338
+ trendingSearches.forEach(trending => {
339
+ items.push({ type: 'trending', index: index++, data: trending });
340
+ });
341
+ // Products
342
+ trendingProducts.forEach(product => {
343
+ items.push({ type: 'product', index: index++, data: product });
344
+ });
345
+ // Brands
346
+ popularBrands.forEach(brand => {
347
+ items.push({ type: 'brand', index: index++, data: brand });
348
+ });
349
+ return items;
350
+ }, [query, recentSearches, suggestions, trendingSearches, trendingProducts, popularBrands]);
351
+ return {
352
+ suggestions,
353
+ loading,
354
+ error,
355
+ dropdownRecommendations,
356
+ trendingSearches,
357
+ topSearches,
358
+ relatedSearches,
359
+ popularBrands,
360
+ filteredTabs: filteredTabsResult,
361
+ trendingProducts,
362
+ recentSearches,
363
+ processingTimeMs,
364
+ totalHits,
365
+ currentPage,
366
+ totalPages,
367
+ originalQuery,
368
+ addRecentSearch,
369
+ removeRecentSearch,
370
+ clearRecentSearches,
371
+ refetch,
372
+ hasContent,
373
+ getAllNavigableItems,
374
+ };
375
+ }
376
+ export default useQuerySuggestionsEnhanced;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * useSearchState Hook
3
+ *
4
+ * React hook for accessing and managing search state via SearchStateManager
5
+ * Provides reactive state and methods to update search parameters
6
+ */
7
+ import type { SearchResponse } from '@seekora-ai/search-sdk';
8
+ import { SearchStateManager, type SearchState } from '@seekora-ai/ui-sdk-core';
9
+ export interface UseSearchStateReturn {
10
+ query: string;
11
+ refinements: Array<{
12
+ field: string;
13
+ value: string;
14
+ }>;
15
+ currentPage: number;
16
+ itemsPerPage: number;
17
+ sortBy?: string;
18
+ results: SearchState['results'];
19
+ loading: boolean;
20
+ error: Error | null;
21
+ setQuery: (query: string, triggerSearch?: boolean) => void;
22
+ addRefinement: (field: string, value: string, triggerSearch?: boolean) => void;
23
+ removeRefinement: (field: string, value: string, triggerSearch?: boolean) => void;
24
+ clearRefinements: (triggerSearch?: boolean) => void;
25
+ setPage: (page: number, triggerSearch?: boolean) => void;
26
+ setSortBy: (sortBy: string, triggerSearch?: boolean) => void;
27
+ search: (additionalOptions?: Parameters<SearchStateManager['search']>[0]) => Promise<SearchResponse | null>;
28
+ clear: () => void;
29
+ }
30
+ /**
31
+ * Hook to access and manage search state
32
+ * Automatically subscribes to state changes and re-renders when state updates
33
+ */
34
+ export declare const useSearchState: () => UseSearchStateReturn;
35
+ //# sourceMappingURL=useSearchState.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSearchState.d.ts","sourceRoot":"","sources":["../../src/hooks/useSearchState.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG/E,MAAM,WAAW,oBAAoB;IAEnC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAGpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3D,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/E,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAClF,gBAAgB,EAAE,CAAC,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACzD,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7D,MAAM,EAAE,CAAC,iBAAiB,CAAC,EAAE,UAAU,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC5G,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,QAAO,oBAkEjC,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * useSearchState Hook
3
+ *
4
+ * React hook for accessing and managing search state via SearchStateManager
5
+ * Provides reactive state and methods to update search parameters
6
+ */
7
+ import { useState, useEffect, useCallback } from 'react';
8
+ import { useSearchContext } from '../components/SearchProvider';
9
+ /**
10
+ * Hook to access and manage search state
11
+ * Automatically subscribes to state changes and re-renders when state updates
12
+ */
13
+ export const useSearchState = () => {
14
+ const { stateManager } = useSearchContext();
15
+ const [state, setState] = useState(stateManager.getState());
16
+ // Subscribe to state changes
17
+ useEffect(() => {
18
+ const unsubscribe = stateManager.subscribe((newState) => {
19
+ setState(newState);
20
+ });
21
+ return unsubscribe;
22
+ }, [stateManager]);
23
+ // Memoized action methods
24
+ const setQuery = useCallback((query, triggerSearch = true) => {
25
+ stateManager.setQuery(query, triggerSearch);
26
+ }, [stateManager]);
27
+ const addRefinement = useCallback((field, value, triggerSearch = true) => {
28
+ stateManager.addRefinement(field, value, triggerSearch);
29
+ }, [stateManager]);
30
+ const removeRefinement = useCallback((field, value, triggerSearch = true) => {
31
+ stateManager.removeRefinement(field, value, triggerSearch);
32
+ }, [stateManager]);
33
+ const clearRefinements = useCallback((triggerSearch = true) => {
34
+ stateManager.clearRefinements(triggerSearch);
35
+ }, [stateManager]);
36
+ const setPage = useCallback((page, triggerSearch = true) => {
37
+ stateManager.setPage(page, triggerSearch);
38
+ }, [stateManager]);
39
+ const setSortBy = useCallback((sortBy, triggerSearch = true) => {
40
+ stateManager.setSortBy(sortBy, triggerSearch);
41
+ }, [stateManager]);
42
+ const search = useCallback((additionalOptions) => {
43
+ return stateManager.search(additionalOptions);
44
+ }, [stateManager]);
45
+ const clear = useCallback(() => {
46
+ stateManager.clear();
47
+ }, [stateManager]);
48
+ return {
49
+ // State
50
+ query: state.query,
51
+ refinements: state.refinements,
52
+ currentPage: state.currentPage,
53
+ itemsPerPage: state.itemsPerPage,
54
+ sortBy: state.sortBy,
55
+ results: state.results,
56
+ loading: state.loading,
57
+ error: state.error,
58
+ // Actions
59
+ setQuery,
60
+ addRefinement,
61
+ removeRefinement,
62
+ clearRefinements,
63
+ setPage,
64
+ setSortBy,
65
+ search,
66
+ clear,
67
+ };
68
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * useSeekoraSearch Hook
3
+ *
4
+ * Hook for performing searches with the Seekora SDK
5
+ */
6
+ import type { SeekoraClient, SearchOptions, SearchResponse, SearchContext } from '@seekora-ai/search-sdk';
7
+ export interface UseSeekoraSearchOptions {
8
+ client: SeekoraClient;
9
+ autoTrack?: boolean;
10
+ }
11
+ export interface UseSeekoraSearchReturn {
12
+ search: (options: SearchOptions) => Promise<SearchResponse | null>;
13
+ results: SearchResponse | null;
14
+ loading: boolean;
15
+ error: Error | null;
16
+ context: SearchContext | null;
17
+ clearResults: () => void;
18
+ }
19
+ export declare const useSeekoraSearch: ({ client, autoTrack, }: UseSeekoraSearchOptions) => UseSeekoraSearchReturn;
20
+ //# sourceMappingURL=useSeekoraSearch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSeekoraSearch.d.ts","sourceRoot":"","sources":["../../src/hooks/useSeekoraSearch.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAG1G,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACnE,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED,eAAO,MAAM,gBAAgB,GAAI,wBAG9B,uBAAuB,KAAG,sBA8D5B,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * useSeekoraSearch Hook
3
+ *
4
+ * Hook for performing searches with the Seekora SDK
5
+ */
6
+ import { useState, useCallback } from 'react';
7
+ import { log } from '@seekora-ai/ui-sdk-core';
8
+ export const useSeekoraSearch = ({ client, autoTrack = true, }) => {
9
+ const [results, setResults] = useState(null);
10
+ const [loading, setLoading] = useState(false);
11
+ const [error, setError] = useState(null);
12
+ const [context, setContext] = useState(null);
13
+ const search = useCallback(async (options) => {
14
+ setLoading(true);
15
+ setError(null);
16
+ const query = options.q || '';
17
+ log.verbose('useSeekoraSearch: Starting search', { query, options });
18
+ try {
19
+ const { q, ...restOptions } = options;
20
+ const query = typeof q === 'string' ? q : '';
21
+ const response = await client.search(query, restOptions);
22
+ log.info('useSeekoraSearch: Search completed', {
23
+ query,
24
+ resultsCount: Array.isArray(response?.results) ? response.results.length : 0,
25
+ hasContext: !!response?.context,
26
+ });
27
+ setResults(response);
28
+ // Extract context from response
29
+ if (response.context != null) {
30
+ setContext(response.context);
31
+ }
32
+ return response;
33
+ }
34
+ catch (err) {
35
+ const error = err instanceof Error ? err : new Error(String(err));
36
+ log.error('useSeekoraSearch: Search failed', {
37
+ query,
38
+ error: error.message,
39
+ stack: error.stack,
40
+ });
41
+ setError(error);
42
+ setResults(null);
43
+ setContext(null);
44
+ return null;
45
+ }
46
+ finally {
47
+ setLoading(false);
48
+ }
49
+ }, [client]);
50
+ const clearResults = useCallback(() => {
51
+ setResults(null);
52
+ setError(null);
53
+ setContext(null);
54
+ }, []);
55
+ return {
56
+ search,
57
+ results,
58
+ loading,
59
+ error,
60
+ context,
61
+ clearResults,
62
+ };
63
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * useSmartSuggestions Hook
3
+ *
4
+ * AI-enhanced query suggestions with intent understanding
5
+ * Provides context-aware suggestions based on user behavior and query patterns
6
+ */
7
+ export interface SmartSuggestion {
8
+ text: string;
9
+ type: 'query' | 'correction' | 'expansion' | 'related' | 'trending';
10
+ confidence: number;
11
+ metadata?: {
12
+ intent?: string;
13
+ category?: string;
14
+ correctedFrom?: string;
15
+ expandedTo?: string[];
16
+ };
17
+ }
18
+ export interface SmartSuggestionsOptions {
19
+ /** Minimum query length */
20
+ minQueryLength?: number;
21
+ /** Maximum suggestions to return */
22
+ maxSuggestions?: number;
23
+ /** Debounce delay in ms */
24
+ debounceMs?: number;
25
+ /** Enable spell correction */
26
+ enableSpellCorrection?: boolean;
27
+ /** Enable query expansion */
28
+ enableQueryExpansion?: boolean;
29
+ /** Enable intent detection */
30
+ enableIntentDetection?: boolean;
31
+ /** Include trending queries when query is empty */
32
+ includeTrending?: boolean;
33
+ /** User context for personalization */
34
+ userContext?: {
35
+ recentSearches?: string[];
36
+ preferences?: Record<string, any>;
37
+ };
38
+ }
39
+ export interface UseSmartSuggestionsReturn {
40
+ suggestions: SmartSuggestion[];
41
+ loading: boolean;
42
+ error: Error | null;
43
+ /** Detected intent from the query */
44
+ detectedIntent: string | null;
45
+ /** Suggested corrections if query has typos */
46
+ corrections: string[];
47
+ /** Expanded query terms */
48
+ expansions: string[];
49
+ /** Process a query for smart suggestions */
50
+ getSuggestions: (query: string) => Promise<SmartSuggestion[]>;
51
+ /** Clear current suggestions */
52
+ clear: () => void;
53
+ }
54
+ export declare function useSmartSuggestions(query: string, options?: SmartSuggestionsOptions): UseSmartSuggestionsReturn;
55
+ //# sourceMappingURL=useSmartSuggestions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSmartSuggestions.d.ts","sourceRoot":"","sources":["../../src/hooks/useSmartSuggestions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC;IACpE,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;CACH;AAED,MAAM,WAAW,uBAAuB;IACtC,2BAA2B;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oCAAoC;IACpC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2BAA2B;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,6BAA6B;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,8BAA8B;IAC9B,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,mDAAmD;IACnD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uCAAuC;IACvC,WAAW,CAAC,EAAE;QACZ,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KACnC,CAAC;CACH;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,eAAe,EAAE,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,qCAAqC;IACrC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,+CAA+C;IAC/C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,4CAA4C;IAC5C,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC9D,gCAAgC;IAChC,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB;AAsHD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,uBAA4B,GACpC,yBAAyB,CA6J3B"}