@shop-prompter/react 1.1.1 → 1.2.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/index.mjs CHANGED
@@ -650,6 +650,57 @@ var ShopPrompterService = class {
650
650
  }
651
651
  };
652
652
 
653
+ // src/translations.ts
654
+ var DEFAULT_LOCALE = "en";
655
+ var TRANSLATIONS = {
656
+ en: {
657
+ newChat: "New Chat",
658
+ welcomeToShopPrompter: "Welcome",
659
+ welcomeDescription: "I am your personal AI shopping assistant. Ask me what you would like to know!",
660
+ askMeAnything: "Ask me anything...",
661
+ thinking: "Thinking...",
662
+ feedbackError: "We are sorry, but we could not process your feedback. Please try again later.",
663
+ guestLimitReached: "We are sorry, the chat is no longer available for a guest user. Please log in to continue chatting.",
664
+ sessionExpired: "We are sorry. It looks like the session just expired. Please refresh the page to continue chatting.",
665
+ agentExperienceError: "We are sorry. The session seems to be expired, or the connection is wrongly configured. Please reload the page.",
666
+ apiKeyWarning: "API Key is only for development purposes",
667
+ configErrorTitle: "Configuration Error",
668
+ configErrorMessage: "The initialization requires certain parameters to be present\u201A. Please check your setup.",
669
+ configMissingParams: "Missing Parameters:",
670
+ configMissingToken: "- Valid token (not found)",
671
+ configMissingApiKey: "- Valid API Key. (not found)",
672
+ disclaimer: "AI content may be inaccurate.",
673
+ loadingProducts: "Loading products...",
674
+ productFetchError: "We are unable to fetch products.",
675
+ predefinedPromptDisclaimer: "Click on a prompt, to start the conversation"
676
+ },
677
+ de: {
678
+ newChat: "Neuer Chat",
679
+ welcomeToShopPrompter: "Willkommen",
680
+ welcomeDescription: "Ich bin Ihr pers\xF6nlicher KI-Einkaufsassistent. Fragen Sie mich alles \xFCber was Sie wissen m\xF6chten!",
681
+ askMeAnything: "Fragen Sie mich etwas...",
682
+ thinking: "Einen Moment...",
683
+ feedbackError: "Es tut uns leid, aber wir konnten Ihr Feedback nicht verarbeiten. Bitte versuchen Sie es sp\xE4ter erneut.",
684
+ guestLimitReached: "Es tut uns leid, der Chat steht f\xFCr Gastbenutzer nicht mehr zur Verf\xFCgung. Bitte melden Sie sich an, um weiter zu chatten.",
685
+ sessionExpired: "Es tut uns leid. Die Sitzung scheint abgelaufen zu sein. Bitte laden Sie die Seite neu, um weiter zu chatten.",
686
+ agentExperienceError: "Es tut uns leid. Die Sitzung scheint abgelaufen zu sein oder die Verbindung ist falsch konfiguriert. Bitte laden Sie die Seite neu.",
687
+ apiKeyWarning: "API Key ist nur f\xFCr Entwicklungszwecke",
688
+ configErrorTitle: "Konfigurationsfehler",
689
+ configErrorMessage: "Die Initialsierung ben\xF6tigt gewisse Parameter, die nicht vorhanden sind. Bitte \xFCberpr\xFCfen Sie Ihre Einrichtung.",
690
+ configMissingParams: "Fehlende Parameter:",
691
+ configMissingToken: "- G\xFCltiger Token (nicht gefunden)",
692
+ configMissingApiKey: "- API Key (nicht gefunden)",
693
+ disclaimer: "KI-Inhalte k\xF6nnen Fehler beinhalten.",
694
+ loadingProducts: "Produkte werden geladen...",
695
+ productFetchError: "Die Produkte konnten leider nicht geladen werden.",
696
+ predefinedPromptDisclaimer: "Klicke auf einen Text, um die Unterhaltung zu starten"
697
+ }
698
+ };
699
+ function getTranslations(locale) {
700
+ const resolved = locale || DEFAULT_LOCALE;
701
+ return TRANSLATIONS[resolved] || TRANSLATIONS[DEFAULT_LOCALE];
702
+ }
703
+
653
704
  // src/thumbs-up-icon.component.jsx
654
705
  import * as React from "react";
655
706
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -797,12 +848,23 @@ var styles = {
797
848
  maxWidth: "100%",
798
849
  boxSizing: "border-box",
799
850
  overflow: "hidden"
851
+ },
852
+ errorContainer: {
853
+ padding: "0.75rem",
854
+ backgroundColor: "#fef2f2",
855
+ border: "1px solid #fecaca",
856
+ color: "#dc2626",
857
+ fontSize: "0.75rem",
858
+ borderRadius: "0.5rem"
800
859
  }
801
860
  };
802
861
  function ProductCardList(props) {
803
862
  const [products, setProducts] = useState(() => []);
804
863
  const [loading, setLoading] = useState(() => false);
805
864
  const [error, setError] = useState(() => null);
865
+ function t() {
866
+ return getTranslations(props.defaultLocale);
867
+ }
806
868
  function fetchProducts() {
807
869
  let skusList = props.skus;
808
870
  if (typeof skusList === "string") {
@@ -826,13 +888,13 @@ function ProductCardList(props) {
826
888
  setLoading(false);
827
889
  }).catch((err) => {
828
890
  console.error("Failed to fetch products:", err);
829
- setError((err == null ? void 0 : err.message) || "Failed to load products");
891
+ setError(t().productFetchError);
830
892
  setLoading(false);
831
893
  });
832
894
  }
833
895
  function getProductName(product) {
834
896
  var _a;
835
- const locale = props.defaultLocale || "de";
897
+ const locale = props.defaultLocale;
836
898
  const localized = ((_a = product.localization) == null ? void 0 : _a[locale]) || Object.values(product.localization || {})[0];
837
899
  return (localized == null ? void 0 : localized.name) || product.sku;
838
900
  }
@@ -856,7 +918,7 @@ function ProductCardList(props) {
856
918
  }
857
919
  function getDynamicField(product, field) {
858
920
  var _a;
859
- const locale = props.defaultLocale || "de";
921
+ const locale = props.defaultLocale;
860
922
  const localized = ((_a = product.localization) == null ? void 0 : _a[locale]) || Object.values(product.localization || {})[0];
861
923
  if (!localized) return void 0;
862
924
  const parts = field.split(".");
@@ -888,11 +950,8 @@ function ProductCardList(props) {
888
950
  props.skus ? Array.isArray(props.skus) ? props.skus.join(",") : props.skus : ""
889
951
  ]);
890
952
  return /* @__PURE__ */ jsx3(Fragment, { children: props.skus && props.skus.length > 0 ? /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsxs3("div", { style: styles.wrapper, children: [
891
- loading ? /* @__PURE__ */ jsx3("div", { style: styles.statusContainer, children: /* @__PURE__ */ jsx3("span", { children: "Loading products..." }) }) : null,
892
- !loading && error ? /* @__PURE__ */ jsx3("div", { style: styles.statusContainer, children: /* @__PURE__ */ jsxs3("span", { style: styles.errorText, children: [
893
- "Error: ",
894
- error
895
- ] }) }) : null,
953
+ loading ? /* @__PURE__ */ jsx3("div", { style: styles.statusContainer, children: /* @__PURE__ */ jsx3("span", { children: t().loadingProducts }) }) : null,
954
+ !loading && error ? /* @__PURE__ */ jsx3("div", { style: styles.errorContainer, children: t().productFetchError }) : null,
896
955
  !loading && products && products.length > 0 ? /* @__PURE__ */ jsx3("div", { style: styles.productCardsContainer, children: products == null ? void 0 : products.map((product) => {
897
956
  var _a;
898
957
  return /* @__PURE__ */ jsxs3("div", { style: styles.productCard, children: [
@@ -968,7 +1027,7 @@ var styles2 = {
968
1027
  },
969
1028
  button: {
970
1029
  padding: "0.625rem 1rem",
971
- border: "1px solid #e5e7eb",
1030
+ border: "2px solid #e5e7eb",
972
1031
  borderRadius: "0.75rem",
973
1032
  backgroundColor: "transparent",
974
1033
  transition: "background-color 0.2s, color 0.2s",
@@ -1011,15 +1070,16 @@ var styles2 = {
1011
1070
  fontFamily: 'var(--font-space-grotesk), "Space Grotesk", sans-serif',
1012
1071
  fontWeight: 500,
1013
1072
  fontStyle: "Medium",
1014
- fontSize: "32px",
1073
+ fontSize: "28px",
1015
1074
  leadingTrim: "NONE",
1016
1075
  lineHeight: "100%",
1017
1076
  letterSpacing: "0%",
1077
+ paddingBottom: "1rem",
1018
1078
  textAlign: "center"
1019
1079
  },
1020
1080
  welcomeDescription: {
1021
1081
  color: "#4b5563",
1022
- maxWidth: "20rem",
1082
+ maxWidth: "32rem",
1023
1083
  fontFamily: 'var(--font-space-grotesk), "Space Grotesk", sans-serif',
1024
1084
  fontWeight: 400,
1025
1085
  fontStyle: "Regular",
@@ -1034,7 +1094,7 @@ var styles2 = {
1034
1094
  display: "flex",
1035
1095
  flexWrap: "wrap",
1036
1096
  justifyContent: "center",
1037
- gap: "0.5rem"
1097
+ gap: "1rem"
1038
1098
  },
1039
1099
  messageContainerUser: {
1040
1100
  display: "flex",
@@ -1179,6 +1239,13 @@ var styles2 = {
1179
1239
  textAlign: "center",
1180
1240
  marginTop: "0.75rem"
1181
1241
  },
1242
+ predefinedPromptDisclaimer: {
1243
+ fontSize: "0.75rem",
1244
+ color: "#6b7280",
1245
+ textAlign: "center",
1246
+ marginTop: "2rem",
1247
+ width: "100%"
1248
+ },
1182
1249
  backdrop: {
1183
1250
  position: "fixed",
1184
1251
  top: 0,
@@ -1333,14 +1400,6 @@ var ANIMATION_STYLES = `
1333
1400
  border-color: #d1d5db !important;
1334
1401
  }
1335
1402
  `;
1336
- var TRANSLATIONS = {
1337
- newChat: "New Chat",
1338
- welcomeToShopPrompter: "Welcome to ShopPrompter",
1339
- welcomeDescription: "I am your personal AI shopping assistant. Ask me anything about our products!",
1340
- askMeAnything: "Ask me anything...",
1341
- thinking: "Thinking...",
1342
- feedbackError: "We are sorry, but we could not process your feedback. Please try again later."
1343
- };
1344
1403
  var ICONS = {
1345
1404
  chat: "https://cdn.jsdelivr.net/gh/lucide-icons/lucide@latest/icons/message-square.svg",
1346
1405
  shopprompter: "https://cdn.jsdelivr.net/gh/lucide-icons/lucide@latest/icons/bot.svg",
@@ -1363,6 +1422,20 @@ function ShopPrompter(props) {
1363
1422
  const [backdropAnimationClass, setBackdropAnimationClass] = useState2(
1364
1423
  () => ""
1365
1424
  );
1425
+ function t() {
1426
+ return getTranslations(props.defaultLocale);
1427
+ }
1428
+ function requireClickOnPredefinedPromptVal() {
1429
+ const flag = props.requireClickOnPredefinedPrompt;
1430
+ return typeof flag === "string" ? flag !== "false" : flag != null ? flag : true;
1431
+ }
1432
+ function shouldHideInput() {
1433
+ const requireClick = requireClickOnPredefinedPromptVal();
1434
+ if (!requireClick) return false;
1435
+ if (chatMessages.length > 0) return false;
1436
+ if (prompts().length === 0) return false;
1437
+ return true;
1438
+ }
1366
1439
  function hasToken() {
1367
1440
  var _a2;
1368
1441
  const token = props.shopPrompterToken || ((_a2 = apiConfigObj == null ? void 0 : apiConfigObj()) == null ? void 0 : _a2.shopPrompterToken);
@@ -1522,7 +1595,13 @@ function ShopPrompter(props) {
1522
1595
  setIsLoading(loading);
1523
1596
  },
1524
1597
  onError: (err) => {
1525
- setError(err);
1598
+ if (err.includes("429")) {
1599
+ setError(t().guestLimitReached);
1600
+ } else if (err.includes("401") || err.includes("403")) {
1601
+ setError(t().sessionExpired);
1602
+ } else {
1603
+ setError(err);
1604
+ }
1526
1605
  },
1527
1606
  onCartOperation: (ops) => {
1528
1607
  if (props.onCartOperation) {
@@ -1659,6 +1738,10 @@ function ShopPrompter(props) {
1659
1738
  return;
1660
1739
  }
1661
1740
  const response = await (service == null ? void 0 : service.getAgentExperience());
1741
+ if (!response) {
1742
+ setError(t().agentExperienceError);
1743
+ return;
1744
+ }
1662
1745
  setAgentExperience(response);
1663
1746
  })();
1664
1747
  }
@@ -1682,7 +1765,7 @@ function ShopPrompter(props) {
1682
1765
  setError(null);
1683
1766
  service == null ? void 0 : service.sendFeedback(messageId, messageContent, targetFeedback).catch((err) => {
1684
1767
  setChatMessages(oldMessages);
1685
- setError((err == null ? void 0 : err.message) || TRANSLATIONS.feedbackError);
1768
+ setError((err == null ? void 0 : err.message) || t().feedbackError);
1686
1769
  });
1687
1770
  }
1688
1771
  function canShowFeedback(message) {
@@ -1820,7 +1903,7 @@ function ShopPrompter(props) {
1820
1903
  ) : null,
1821
1904
  (agentExperience == null ? void 0 : agentExperience.agentName) ? /* @__PURE__ */ jsx4("span", { style: styles2.headerText, children: agentExperience.agentName }) : null
1822
1905
  ] }) : /* @__PURE__ */ jsx4(Fragment2, { children: /* @__PURE__ */ jsx4("div", { style: styles2.subHeaderContainer }) }),
1823
- !hasToken() && ((_a = apiConfigObj == null ? void 0 : apiConfigObj()) == null ? void 0 : _a.apiKey) ? /* @__PURE__ */ jsx4("div", { style: styles2.warningBanner, children: "API Key is only for development purposes" }) : null,
1906
+ !hasToken() && ((_a = apiConfigObj == null ? void 0 : apiConfigObj()) == null ? void 0 : _a.apiKey) ? /* @__PURE__ */ jsx4("div", { style: styles2.warningBanner, children: t().apiKeyWarning }) : null,
1824
1907
  /* @__PURE__ */ jsxs4("div", { style: styles2.subHeaderContainer, children: [
1825
1908
  /* @__PURE__ */ jsx4(
1826
1909
  "button",
@@ -1828,7 +1911,7 @@ function ShopPrompter(props) {
1828
1911
  className: "sh-header-btn",
1829
1912
  onClick: (e) => handleNewChat(),
1830
1913
  style: styles2.headerButton,
1831
- children: TRANSLATIONS.newChat
1914
+ children: t().newChat
1832
1915
  }
1833
1916
  ),
1834
1917
  props.chatPosition === "classic-chatbot" || props.chatPosition === "right-panel" || props.chatPosition === "dialog" ? /* @__PURE__ */ jsx4(
@@ -1871,7 +1954,7 @@ function ShopPrompter(props) {
1871
1954
  ...styles2.insufficientConfigTitle,
1872
1955
  fontFamily: themeObj().fontFamily || void 0
1873
1956
  },
1874
- children: "Configuration Error"
1957
+ children: t().configErrorTitle
1875
1958
  }
1876
1959
  ),
1877
1960
  /* @__PURE__ */ jsx4(
@@ -1881,7 +1964,7 @@ function ShopPrompter(props) {
1881
1964
  ...styles2.insufficientConfigMessage,
1882
1965
  fontFamily: themeObj().fontFamily || void 0
1883
1966
  },
1884
- children: "ShopPrompter requires a secure token or API key to initialize. Please check your setup."
1967
+ children: t().configErrorMessage
1885
1968
  }
1886
1969
  ),
1887
1970
  /* @__PURE__ */ jsxs4(
@@ -1899,7 +1982,7 @@ function ShopPrompter(props) {
1899
1982
  fontWeight: 600,
1900
1983
  marginBottom: "0.25rem"
1901
1984
  },
1902
- children: "Missing Parameters:"
1985
+ children: t().configMissingParams
1903
1986
  }
1904
1987
  ),
1905
1988
  /* @__PURE__ */ jsxs4(
@@ -1919,7 +2002,7 @@ function ShopPrompter(props) {
1919
2002
  style: {
1920
2003
  color: "#ef4444"
1921
2004
  },
1922
- children: "\u2717 shopPrompterToken (not found)"
2005
+ children: t().configMissingToken
1923
2006
  }
1924
2007
  ),
1925
2008
  /* @__PURE__ */ jsx4(
@@ -1928,7 +2011,7 @@ function ShopPrompter(props) {
1928
2011
  style: {
1929
2012
  color: "#ef4444"
1930
2013
  },
1931
- children: "\u2717 apiConfig.apiKey (not found)"
2014
+ children: t().configMissingApiKey
1932
2015
  }
1933
2016
  )
1934
2017
  ]
@@ -1964,8 +2047,8 @@ function ShopPrompter(props) {
1964
2047
  alt: "ShopPrompter",
1965
2048
  src: (agentExperience == null ? void 0 : agentExperience.agentLogoUrl) ? agentExperience.agentLogoUrl : ICONS.shopprompter,
1966
2049
  style: {
1967
- width: "64px",
1968
- height: "64px",
2050
+ width: "96px",
2051
+ height: "96px",
1969
2052
  marginBottom: "24px",
1970
2053
  borderRadius: "12px",
1971
2054
  objectFit: "cover"
@@ -1979,7 +2062,9 @@ function ShopPrompter(props) {
1979
2062
  ...styles2.welcomeTitle,
1980
2063
  fontFamily: themeObj().fontFamily || styles2.welcomeTitle.fontFamily
1981
2064
  },
1982
- children: ((_b = agentExperience == null ? void 0 : agentExperience.greeting) == null ? void 0 : _b.title) || TRANSLATIONS.welcomeToShopPrompter
2065
+ dangerouslySetInnerHTML: {
2066
+ __html: ((_b = agentExperience == null ? void 0 : agentExperience.greeting) == null ? void 0 : _b.title) || t().welcomeToShopPrompter
2067
+ }
1983
2068
  }
1984
2069
  ),
1985
2070
  /* @__PURE__ */ jsx4(
@@ -1989,13 +2074,19 @@ function ShopPrompter(props) {
1989
2074
  ...styles2.welcomeDescription,
1990
2075
  fontFamily: themeObj().fontFamily || styles2.welcomeDescription.fontFamily
1991
2076
  },
1992
- children: ((_c = agentExperience == null ? void 0 : agentExperience.greeting) == null ? void 0 : _c.message) || TRANSLATIONS.welcomeDescription
2077
+ dangerouslySetInnerHTML: {
2078
+ __html: ((_c = agentExperience == null ? void 0 : agentExperience.greeting) == null ? void 0 : _c.message) || t().welcomeDescription
2079
+ }
1993
2080
  }
1994
2081
  ),
1995
2082
  /* @__PURE__ */ jsx4("div", { style: styles2.buttonContainer, children: (_d = prompts()) == null ? void 0 : _d.map((item) => /* @__PURE__ */ jsx4(
1996
2083
  "button",
1997
2084
  {
1998
- style: styles2.button,
2085
+ style: {
2086
+ ...styles2.button,
2087
+ borderColor: themeObj().actionColor || "#e5e7eb",
2088
+ outlineColor: themeObj().actionColor || void 0
2089
+ },
1999
2090
  onClick: (event) => {
2000
2091
  setInput(item.prompt);
2001
2092
  handleSend(item.prompt);
@@ -2003,7 +2094,17 @@ function ShopPrompter(props) {
2003
2094
  children: item.prompt
2004
2095
  },
2005
2096
  item.prompt
2006
- )) })
2097
+ )) }),
2098
+ requireClickOnPredefinedPromptVal() && prompts().length > 0 ? /* @__PURE__ */ jsx4(
2099
+ "div",
2100
+ {
2101
+ style: {
2102
+ ...styles2.predefinedPromptDisclaimer,
2103
+ fontFamily: themeObj().fontFamily || void 0
2104
+ },
2105
+ children: t().predefinedPromptDisclaimer
2106
+ }
2107
+ ) : null
2007
2108
  ] }) : null,
2008
2109
  !showWelcome() ? /* @__PURE__ */ jsx4(
2009
2110
  "div",
@@ -2129,7 +2230,7 @@ function ShopPrompter(props) {
2129
2230
  product_card_list_component_default,
2130
2231
  {
2131
2232
  skus: message.productSkus,
2132
- defaultLocale: props.defaultLocale,
2233
+ defaultLocale: props.defaultLocale || DEFAULT_LOCALE,
2133
2234
  showPriceIfEmpty: showPriceIfEmptyVal(),
2134
2235
  visibleFields: visibleFieldsList()
2135
2236
  }
@@ -2138,19 +2239,20 @@ function ShopPrompter(props) {
2138
2239
  ] }, message.id))
2139
2240
  }
2140
2241
  ) : null,
2141
- error ? /* @__PURE__ */ jsxs4("div", { style: styles2.errorContainer, children: [
2142
- "Error: ",
2143
- error
2144
- ] }) : null
2242
+ error ? /* @__PURE__ */ jsx4("div", { style: styles2.errorContainer, children: error }) : null
2145
2243
  ]
2146
2244
  }
2147
2245
  ),
2148
- /* @__PURE__ */ jsxs4(
2246
+ !shouldHideInput() ? /* @__PURE__ */ jsxs4(
2149
2247
  "div",
2150
2248
  {
2151
2249
  style: {
2152
2250
  ...styles2.inputSection,
2153
- backgroundColor: themeObj().mainBackgroundColor || styles2.inputSection.backgroundColor
2251
+ backgroundColor: themeObj().mainBackgroundColor || styles2.inputSection.backgroundColor,
2252
+ ...error ? {
2253
+ opacity: "0.5",
2254
+ pointerEvents: "none"
2255
+ } : {}
2154
2256
  },
2155
2257
  children: [
2156
2258
  /* @__PURE__ */ jsxs4("div", { style: styles2.inputWrapper, children: [
@@ -2160,10 +2262,11 @@ function ShopPrompter(props) {
2160
2262
  id: "shop-prompter-input",
2161
2263
  type: "text",
2162
2264
  style: styles2.input,
2163
- placeholder: (agentExperience == null ? void 0 : agentExperience.hintText) || TRANSLATIONS.askMeAnything,
2265
+ placeholder: (agentExperience == null ? void 0 : agentExperience.hintText) || t().askMeAnything,
2164
2266
  value: input,
2165
2267
  onInput: (e) => onInputChange(e),
2166
- onKeyDown: (e) => onInputKeyDown(e)
2268
+ onKeyDown: (e) => onInputKeyDown(e),
2269
+ disabled: !!error
2167
2270
  }
2168
2271
  ),
2169
2272
  /* @__PURE__ */ jsx4(
@@ -2188,10 +2291,10 @@ function ShopPrompter(props) {
2188
2291
  }
2189
2292
  )
2190
2293
  ] }),
2191
- /* @__PURE__ */ jsx4("p", { style: styles2.disclaimer, children: "AI content may be inaccurate." })
2294
+ /* @__PURE__ */ jsx4("p", { style: styles2.disclaimer, children: t().disclaimer })
2192
2295
  ]
2193
2296
  }
2194
- )
2297
+ ) : null
2195
2298
  ]
2196
2299
  }
2197
2300
  ) : null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shop-prompter/react",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -2,6 +2,7 @@
2
2
  import * as React from "react";
3
3
  import { useState, useEffect } from "react";
4
4
  import { productApi } from "./api/product-api";
5
+ import { getTranslations } from "./translations";
5
6
  const styles = {
6
7
  productCardsContainer: {
7
8
  marginTop: "0.75rem",
@@ -91,6 +92,14 @@ const styles = {
91
92
  boxSizing: "border-box",
92
93
  overflow: "hidden",
93
94
  },
95
+ errorContainer: {
96
+ padding: "0.75rem",
97
+ backgroundColor: "#fef2f2",
98
+ border: "1px solid #fecaca",
99
+ color: "#dc2626",
100
+ fontSize: "0.75rem",
101
+ borderRadius: "0.5rem",
102
+ },
94
103
  };
95
104
 
96
105
  function ProductCardList(props) {
@@ -100,6 +109,10 @@ function ProductCardList(props) {
100
109
 
101
110
  const [error, setError] = useState(() => null);
102
111
 
112
+ function t() {
113
+ return getTranslations(props.defaultLocale);
114
+ }
115
+
103
116
  function fetchProducts() {
104
117
  let skusList = props.skus;
105
118
  if (typeof skusList === "string") {
@@ -129,13 +142,13 @@ function ProductCardList(props) {
129
142
  })
130
143
  .catch((err) => {
131
144
  console.error("Failed to fetch products:", err);
132
- setError(err?.message || "Failed to load products");
145
+ setError(t().productFetchError);
133
146
  setLoading(false);
134
147
  });
135
148
  }
136
149
 
137
150
  function getProductName(product) {
138
- const locale = props.defaultLocale || "de";
151
+ const locale = props.defaultLocale;
139
152
  const localized =
140
153
  product.localization?.[locale] ||
141
154
  Object.values(product.localization || {})[0];
@@ -166,7 +179,7 @@ function ProductCardList(props) {
166
179
  }
167
180
 
168
181
  function getDynamicField(product, field) {
169
- const locale = props.defaultLocale || "de";
182
+ const locale = props.defaultLocale;
170
183
  const localized =
171
184
  product.localization?.[locale] ||
172
185
  Object.values(product.localization || {})[0];
@@ -215,13 +228,11 @@ function ProductCardList(props) {
215
228
  <div style={styles.wrapper}>
216
229
  {loading ? (
217
230
  <div style={styles.statusContainer}>
218
- <span>Loading products...</span>
231
+ <span>{t().loadingProducts}</span>
219
232
  </div>
220
233
  ) : null}
221
234
  {!loading && error ? (
222
- <div style={styles.statusContainer}>
223
- <span style={styles.errorText}>Error: {error}</span>
224
- </div>
235
+ <div style={styles.errorContainer}>{t().productFetchError}</div>
225
236
  ) : null}
226
237
  {!loading && products && products.length > 0 ? (
227
238
  <div style={styles.productCardsContainer}>