@seekora-ai/ui-sdk-vanilla 0.2.14 → 0.2.16

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/index.js CHANGED
@@ -349,7 +349,7 @@ class SearchBar {
349
349
  }
350
350
  this.container = container;
351
351
  this.options = {
352
- placeholder: options.placeholder || 'Search...',
352
+ placeholder: options.placeholder || 'Powered by Seekora',
353
353
  showSuggestions: options.showSuggestions !== false,
354
354
  minQueryLength: options.minQueryLength || 1,
355
355
  maxSuggestions: options.maxSuggestions || 10,
@@ -361,8 +361,6 @@ class SearchBar {
361
361
  onBrandClick: options.onBrandClick,
362
362
  searchOptions: options.searchOptions,
363
363
  };
364
- // Cache credentials and environment from client for rich suggestions
365
- this.cacheClientCredentials();
366
364
  this.render();
367
365
  this.attachEventListeners();
368
366
  // Subscribe to state manager to sync input with query state
@@ -584,108 +582,58 @@ class SearchBar {
584
582
  }
585
583
  async fetchRichSuggestions(query) {
586
584
  try {
587
- // Use cached credentials first, then try to get from client
588
- let baseUrl = 'https://api.seekora.com';
589
- let storeId = this.cachedStoreId;
590
- let readSecret = this.cachedReadSecret;
591
- const env = this.cachedEnvironment;
592
- // If not cached, try to get from client
593
- const clientAny = this.client;
594
- if (!storeId || !readSecret) {
595
- if (clientAny.config) {
596
- storeId = storeId || clientAny.config.storeId;
597
- readSecret = readSecret || clientAny.config.readSecret;
598
- }
599
- else if (clientAny.storeId && clientAny.readSecret) {
600
- storeId = storeId || clientAny.storeId;
601
- readSecret = readSecret || clientAny.readSecret;
602
- }
603
- else if (clientAny._config) {
604
- storeId = storeId || clientAny._config.storeId;
605
- readSecret = readSecret || clientAny._config.readSecret;
606
- }
607
- else if (clientAny._storeId && clientAny._readSecret) {
608
- storeId = storeId || clientAny._storeId;
609
- readSecret = readSecret || clientAny._readSecret;
610
- }
611
- }
612
- // Determine environment and set baseUrl
613
- const environment = env || clientAny.environment || clientAny.config?.environment || clientAny._config?.environment;
614
- const isDevelopment = environment === 'development';
615
- // ALWAYS use localhost:3000 for development - FORCE IT
616
- baseUrl = 'http://localhost:3000'; // Force localhost for all requests in dev
617
- console.log('🌐 SearchBar: Setting baseUrl', {
618
- environment,
619
- isDevelopment,
620
- forcedBaseUrl: baseUrl,
621
- });
622
- if (!storeId || !readSecret) {
623
- uiSdkCore.log.warn('SearchBar: Missing store credentials, falling back to basic suggestions', {
624
- hasConfig: !!clientAny.config,
625
- hasDirectProps: !!(clientAny.storeId && clientAny.readSecret),
626
- hasPrivateConfig: !!clientAny._config,
627
- clientKeys: Object.keys(clientAny),
628
- });
629
- await this.fallbackToBasicSuggestions(query);
630
- return;
631
- }
632
- uiSdkCore.log.verbose('SearchBar: Fetching rich suggestions', {
633
- baseUrl,
634
- storeId: storeId ? storeId.substring(0, 8) + '...' : 'missing',
635
- query,
636
- environment,
637
- isDevelopment,
638
- credentialsFromCache: !!(this.cachedStoreId && this.cachedReadSecret),
585
+ uiSdkCore.log.verbose('SearchBar: Fetching rich suggestions via client.getSuggestions', { query });
586
+ const response = await this.client.getSuggestions(query, {
587
+ include_dropdown_recommendations: true,
588
+ hitsPerPage: this.options.maxSuggestions,
589
+ returnFullResponse: true,
639
590
  });
640
- // Build API URL with rich recommendations
641
- // Use 'query' parameter to match API expectations (also accepts 'q')
642
- // Empty query is allowed - will return trending products/brands
643
- const params = new URLSearchParams({
644
- include_dropdown_recommendations: 'true',
645
- hitsPerPage: String(this.options.maxSuggestions),
646
- });
647
- // Only add query parameter if it's not empty
648
- if (query && query.trim().length > 0) {
649
- params.append('query', query);
650
- }
651
- const url = `${baseUrl}/api/v1/suggestions/queries?${params.toString()}`;
652
- uiSdkCore.log.verbose('SearchBar: Making rich suggestions request', { url: url.replace(/secret=[^&]+/, 'secret=***') });
653
- const response = await fetch(url, {
654
- method: 'GET',
655
- headers: {
656
- 'x-storeid': storeId,
657
- 'x-storesecret': readSecret,
658
- 'Content-Type': 'application/json',
591
+ // response is QuerySuggestionsFullResponse when returnFullResponse is true
592
+ const suggestions = response.suggestions || [];
593
+ const extensions = response.extensions;
594
+ // Build RichQuerySuggestionsResponse from SDK response
595
+ const raw = response.raw || {};
596
+ const data = {
597
+ status: 'success',
598
+ data: {
599
+ hits: suggestions,
600
+ nbHits: suggestions.length,
601
+ page: raw.page || 0,
602
+ hitsPerPage: raw.hitsPerPage || this.options.maxSuggestions,
603
+ processingTimeMS: raw.processingTimeMS || 0,
604
+ query: query,
605
+ indexUsed: raw.indexUsed || '',
606
+ parsedQuery: raw.parsedQuery || query,
607
+ dropdown_recommendations: extensions ? {
608
+ trending_searches: extensions.trending_searches,
609
+ trending_products: extensions.trending_products,
610
+ popular_brands: extensions.popular_brands,
611
+ processing_time_ms: extensions.processing_time_ms || 0,
612
+ cached_at: extensions.cached_at,
613
+ } : undefined,
659
614
  },
660
- });
661
- if (!response.ok) {
662
- throw new Error(`API request failed: ${response.status} ${response.statusText}`);
663
- }
664
- const data = await response.json();
665
- if (data.status === 'error') {
666
- throw new Error('API returned error status');
667
- }
615
+ timestamp: raw.timestamp || new Date().toISOString(),
616
+ version: raw.version || '',
617
+ };
668
618
  this.richSuggestionsData = data;
669
619
  // Transform hits to SuggestionItem format
670
- this.suggestions = (data.data.hits || []).map((hit) => ({
620
+ this.suggestions = suggestions.map((hit) => ({
671
621
  query: hit.query,
672
622
  count: hit.popularity,
673
623
  metadata: hit,
674
624
  }));
675
- console.log('SearchBar: Rich suggestions loaded', {
625
+ uiSdkCore.log.verbose('SearchBar: Rich suggestions loaded', {
676
626
  query,
677
627
  suggestionsCount: this.suggestions.length,
678
628
  hasTrendingSearches: !!data.data.dropdown_recommendations?.trending_searches?.length,
679
629
  hasTrendingProducts: !!data.data.dropdown_recommendations?.trending_products?.length,
680
630
  hasPopularBrands: !!data.data.dropdown_recommendations?.popular_brands?.length,
681
- rawData: data.data.dropdown_recommendations,
682
631
  });
683
632
  }
684
633
  catch (err) {
685
634
  const error = err instanceof Error ? err : new Error(String(err));
686
635
  uiSdkCore.log.error('SearchBar: Failed to fetch rich suggestions, falling back to basic', {
687
636
  error: error.message,
688
- stack: error.stack,
689
637
  query,
690
638
  });
691
639
  await this.fallbackToBasicSuggestions(query);
@@ -714,50 +662,6 @@ class SearchBar {
714
662
  this.richSuggestionsData = null;
715
663
  }
716
664
  }
717
- cacheClientCredentials() {
718
- const clientAny = this.client;
719
- console.log('🔍 SearchBar: Attempting to cache credentials', {
720
- hasConfig: !!clientAny.config,
721
- hasDirectProps: !!(clientAny.storeId && clientAny.readSecret),
722
- hasPrivateConfig: !!clientAny._config,
723
- hasPrivateProps: !!(clientAny._storeId && clientAny._readSecret),
724
- clientKeys: Object.keys(clientAny).slice(0, 10),
725
- // Try to see actual values
726
- configStoreId: clientAny.config?.storeId,
727
- directStoreId: clientAny.storeId,
728
- });
729
- // Try multiple ways to extract credentials - check ALL properties
730
- if (clientAny.config && clientAny.config.storeId) {
731
- this.cachedStoreId = clientAny.config.storeId;
732
- this.cachedReadSecret = clientAny.config.readSecret;
733
- this.cachedEnvironment = clientAny.config.environment;
734
- console.log('📍 Extracted from config');
735
- }
736
- if (!this.cachedStoreId && clientAny.storeId) {
737
- this.cachedStoreId = clientAny.storeId;
738
- this.cachedReadSecret = clientAny.readSecret;
739
- this.cachedEnvironment = clientAny.environment;
740
- console.log('📍 Extracted from direct props');
741
- }
742
- if (!this.cachedStoreId && clientAny._config) {
743
- this.cachedStoreId = clientAny._config.storeId;
744
- this.cachedReadSecret = clientAny._config.readSecret;
745
- this.cachedEnvironment = clientAny._config.environment;
746
- console.log('📍 Extracted from _config');
747
- }
748
- if (!this.cachedStoreId && clientAny._storeId) {
749
- this.cachedStoreId = clientAny._storeId;
750
- this.cachedReadSecret = clientAny._readSecret;
751
- this.cachedEnvironment = clientAny._environment || clientAny.environment;
752
- console.log('📍 Extracted from _private props');
753
- }
754
- console.log('✅ SearchBar: Cached client credentials', {
755
- hasStoreId: !!this.cachedStoreId,
756
- storeIdPreview: this.cachedStoreId ? this.cachedStoreId.substring(0, 8) + '...' : 'MISSING',
757
- hasReadSecret: !!this.cachedReadSecret,
758
- environment: this.cachedEnvironment,
759
- });
760
- }
761
665
  async performSearch() {
762
666
  // Use empty string for empty queries
763
667
  const query = this.input.value.trim();
@@ -1242,7 +1146,6 @@ class SearchResults {
1242
1146
  onResultClick: options.onResultClick,
1243
1147
  renderResult: options.renderResult,
1244
1148
  renderEmpty: options.renderEmpty,
1245
- renderLoading: options.renderLoading,
1246
1149
  renderError: options.renderError,
1247
1150
  };
1248
1151
  // Attach event delegation listener to the persistent container for result clicks
@@ -1390,13 +1293,6 @@ class SearchResults {
1390
1293
  loading: this.options.loading,
1391
1294
  hasError: !!this.options.error,
1392
1295
  });
1393
- if (this.options.loading) {
1394
- const loadingEl = this.options.renderLoading
1395
- ? this.options.renderLoading()
1396
- : this.renderDefaultLoading();
1397
- this.container.appendChild(loadingEl);
1398
- return;
1399
- }
1400
1296
  if (this.options.error) {
1401
1297
  const errorEl = this.options.renderError
1402
1298
  ? this.options.renderError(this.options.error)
@@ -1491,12 +1387,6 @@ class SearchResults {
1491
1387
  };
1492
1388
  }
1493
1389
  }
1494
- renderDefaultLoading() {
1495
- const div = document.createElement('div');
1496
- div.style.cssText = this.getLoadingStyle();
1497
- div.textContent = 'Loading results...';
1498
- return div;
1499
- }
1500
1390
  renderDefaultError(error) {
1501
1391
  const div = document.createElement('div');
1502
1392
  div.style.cssText = this.getErrorStyle();
@@ -1616,13 +1506,6 @@ class SearchResults {
1616
1506
  get theme() {
1617
1507
  return this.provider.theme;
1618
1508
  }
1619
- getLoadingStyle() {
1620
- return `
1621
- padding: ${this.theme.spacing.large};
1622
- text-align: center;
1623
- color: ${this.theme.colors.text};
1624
- `;
1625
- }
1626
1509
  getErrorStyle() {
1627
1510
  return `
1628
1511
  padding: ${this.theme.spacing.large};
@@ -1800,9 +1683,6 @@ class Stats {
1800
1683
  }
1801
1684
  render() {
1802
1685
  this.container.innerHTML = '';
1803
- if (this.options.loading) {
1804
- return;
1805
- }
1806
1686
  const totalResults = this.getTotalResults();
1807
1687
  if (totalResults === null) {
1808
1688
  return;
@@ -3387,13 +3267,11 @@ class InfiniteHits {
3387
3267
  this.options = {
3388
3268
  renderHit: options.renderHit,
3389
3269
  renderEmpty: options.renderEmpty,
3390
- renderLoading: options.renderLoading,
3391
3270
  showMoreButton: options.showMoreButton !== false,
3392
3271
  useInfiniteScroll: options.useInfiniteScroll ?? false,
3393
3272
  scrollThreshold: options.scrollThreshold ?? 0.1,
3394
3273
  fieldMapping: options.fieldMapping,
3395
3274
  showMoreLabel: options.showMoreLabel ?? 'Show more',
3396
- loadingLabel: options.loadingLabel ?? 'Loading...',
3397
3275
  onHitClick: options.onHitClick,
3398
3276
  };
3399
3277
  // Subscribe to state manager
@@ -3458,10 +3336,6 @@ class InfiniteHits {
3458
3336
  const state = this.provider.stateManager.getState();
3459
3337
  // Initial loading state
3460
3338
  if (state.loading && this.accumulatedHits.length === 0) {
3461
- const loadingEl = this.options.renderLoading
3462
- ? this.options.renderLoading()
3463
- : this.createDefaultLoading();
3464
- this.container.appendChild(loadingEl);
3465
3339
  return;
3466
3340
  }
3467
3341
  // Empty state
@@ -3486,13 +3360,6 @@ class InfiniteHits {
3486
3360
  const button = this.createShowMoreButton();
3487
3361
  this.container.appendChild(button);
3488
3362
  }
3489
- // Loading more indicator
3490
- if (this.isLoadingMore) {
3491
- const loadingEl = this.options.renderLoading
3492
- ? this.options.renderLoading()
3493
- : this.createDefaultLoading();
3494
- this.container.appendChild(loadingEl);
3495
- }
3496
3363
  // Infinite scroll sentinel
3497
3364
  if (this.options.useInfiniteScroll && !this.isLastPage) {
3498
3365
  const sentinel = document.createElement('div');
@@ -3585,20 +3452,10 @@ class InfiniteHits {
3585
3452
  `;
3586
3453
  return empty;
3587
3454
  }
3588
- createDefaultLoading() {
3589
- const loading = document.createElement('div');
3590
- loading.textContent = this.options.loadingLabel;
3591
- loading.style.cssText = `
3592
- padding: ${this.theme.spacing.medium};
3593
- text-align: center;
3594
- color: ${this.theme.colors.textSecondary};
3595
- `;
3596
- return loading;
3597
- }
3598
3455
  createShowMoreButton() {
3599
3456
  const button = document.createElement('button');
3600
3457
  button.type = 'button';
3601
- button.textContent = this.isLoadingMore ? this.options.loadingLabel : this.options.showMoreLabel;
3458
+ button.textContent = this.options.showMoreLabel;
3602
3459
  button.disabled = this.isLoadingMore;
3603
3460
  button.style.cssText = this.getShowMoreButtonStyle();
3604
3461
  button.addEventListener('click', () => this.handleShowMore());
@@ -3903,7 +3760,6 @@ class QuerySuggestions {
3903
3760
  async loadSuggestions(query) {
3904
3761
  this.loading = true;
3905
3762
  this.error = null;
3906
- this.render();
3907
3763
  try {
3908
3764
  const response = await this.provider.client.getSuggestions(query, this.options.maxSuggestions);
3909
3765
  const rawSuggestions = Array.isArray(response) ? response : [];
@@ -3942,13 +3798,6 @@ class QuerySuggestions {
3942
3798
  if (!this.options.query || this.options.query.length < this.options.minQueryLength) {
3943
3799
  return;
3944
3800
  }
3945
- if (this.loading) {
3946
- const loadingDiv = document.createElement('div');
3947
- loadingDiv.style.cssText = this.getLoadingStyle();
3948
- loadingDiv.textContent = 'Loading suggestions...';
3949
- this.container.appendChild(loadingDiv);
3950
- return;
3951
- }
3952
3801
  if (this.error || this.suggestions.length === 0) {
3953
3802
  const emptyDiv = document.createElement('div');
3954
3803
  emptyDiv.style.cssText = this.getEmptyStyle();
@@ -4018,13 +3867,6 @@ class QuerySuggestions {
4018
3867
  color: ${this.theme.colors.text};
4019
3868
  `;
4020
3869
  }
4021
- getLoadingStyle() {
4022
- return `
4023
- padding: ${this.theme.spacing.medium};
4024
- text-align: center;
4025
- color: ${this.theme.colors.text};
4026
- `;
4027
- }
4028
3870
  getEmptyStyle() {
4029
3871
  return `
4030
3872
  padding: ${this.theme.spacing.medium};