@thefittingroom/shop-ui 5.0.11 → 5.0.17

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 (3) hide show
  1. package/README.md +135 -0
  2. package/dist/index.js +1124 -457
  3. package/package.json +12 -2
package/dist/index.js CHANGED
@@ -13896,6 +13896,265 @@ const createImpl = (createState) => {
13896
13896
  return useBoundStore;
13897
13897
  };
13898
13898
  const create = ((createState) => createState ? createImpl(createState) : createImpl);
13899
+ let debugSource = false;
13900
+ function _init$8(debug) {
13901
+ if (debug) {
13902
+ let escapeRegExp = function(string) {
13903
+ return "^" + string.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".") + "$";
13904
+ };
13905
+ if (debug === true) {
13906
+ debugSource = true;
13907
+ } else if (debug instanceof RegExp) {
13908
+ debugSource = debug;
13909
+ } else if (Array.isArray(debug)) {
13910
+ debugSource = new RegExp(debug.map((s) => escapeRegExp(s)).join("|"));
13911
+ } else if (typeof debug === "string") {
13912
+ debugSource = new RegExp(escapeRegExp(debug));
13913
+ }
13914
+ }
13915
+ }
13916
+ function isDebugSource(source) {
13917
+ if (typeof debugSource === "boolean") {
13918
+ return debugSource;
13919
+ }
13920
+ return debugSource.test(source);
13921
+ }
13922
+ function log(entry) {
13923
+ const {
13924
+ source,
13925
+ level,
13926
+ message,
13927
+ data,
13928
+ duration
13929
+ } = entry;
13930
+ if (level === "debug" && !isDebugSource(source)) {
13931
+ return;
13932
+ }
13933
+ let finalMessage = message;
13934
+ if (finalMessage.includes("{{")) {
13935
+ finalMessage = finalMessage.replace(/{{ts}}/g, (/* @__PURE__ */ new Date()).toISOString());
13936
+ }
13937
+ finalMessage = `[TFR ${source}][${level.toUpperCase()}] ${finalMessage}`;
13938
+ const logData = [finalMessage];
13939
+ if (duration !== void 0) {
13940
+ logData.push(`${duration.toFixed(2)} ms`);
13941
+ }
13942
+ if (data) {
13943
+ logData.push(data);
13944
+ }
13945
+ switch (level) {
13946
+ case "error":
13947
+ console.error(...logData);
13948
+ break;
13949
+ case "warn":
13950
+ console.warn(...logData);
13951
+ break;
13952
+ case "info":
13953
+ console.info(...logData);
13954
+ break;
13955
+ case "debug":
13956
+ console.debug(...logData);
13957
+ break;
13958
+ default:
13959
+ console.log(...logData);
13960
+ }
13961
+ }
13962
+ let Logger$2 = class Logger {
13963
+ constructor(source) {
13964
+ this.timers = {};
13965
+ this.source = source;
13966
+ }
13967
+ logError(message, data = null) {
13968
+ log({
13969
+ source: this.source,
13970
+ level: "error",
13971
+ message,
13972
+ data
13973
+ });
13974
+ }
13975
+ logWarn(message, data = null) {
13976
+ log({
13977
+ source: this.source,
13978
+ level: "warn",
13979
+ message,
13980
+ data
13981
+ });
13982
+ }
13983
+ logInfo(message, data = null) {
13984
+ log({
13985
+ source: this.source,
13986
+ level: "info",
13987
+ message,
13988
+ data
13989
+ });
13990
+ }
13991
+ logDebug(message, data = null) {
13992
+ log({
13993
+ source: this.source,
13994
+ level: "debug",
13995
+ message,
13996
+ data
13997
+ });
13998
+ }
13999
+ logTimer(timerName, message, data = null) {
14000
+ const duration = this.getTimerDuration(timerName);
14001
+ log({
14002
+ source: this.source,
14003
+ level: "debug",
14004
+ message,
14005
+ data,
14006
+ duration: duration ?? void 0
14007
+ });
14008
+ }
14009
+ clearTimers() {
14010
+ this.timers = {};
14011
+ }
14012
+ timerStart(name2) {
14013
+ this.timers[name2] = [performance.now(), null];
14014
+ }
14015
+ timerEnd(name2) {
14016
+ const timer = this.timers[name2];
14017
+ if (timer && timer[1] === null) {
14018
+ timer[1] = performance.now();
14019
+ }
14020
+ }
14021
+ getTimers() {
14022
+ const result = {};
14023
+ for (const name2 in this.timers) {
14024
+ result[name2] = this.getTimerDuration(name2);
14025
+ }
14026
+ return result;
14027
+ }
14028
+ getTimerDuration(name2) {
14029
+ const timer = this.timers[name2];
14030
+ if (timer && timer[1] !== null) {
14031
+ return timer[1] - timer[0];
14032
+ }
14033
+ return null;
14034
+ }
14035
+ isDebugEnabled() {
14036
+ return isDebugSource(this.source);
14037
+ }
14038
+ };
14039
+ function getLogger(source) {
14040
+ return new Logger$2(source);
14041
+ }
14042
+ function getSizeLabelFromSize(size) {
14043
+ if (size.label) {
14044
+ return size.label;
14045
+ }
14046
+ return size.size_value?.name ?? null;
14047
+ }
14048
+ function applyFrameBaseUrl(url, baseUrl2) {
14049
+ const cleanBase = baseUrl2.replace(/\/+$/, "");
14050
+ if (/^https?:\/\//i.test(url)) {
14051
+ const parsed = new URL(url);
14052
+ return `${cleanBase}${parsed.pathname}${parsed.search}${parsed.hash}`;
14053
+ }
14054
+ return `${cleanBase}${url.startsWith("/") ? "" : "/"}${url}`;
14055
+ }
14056
+ const STORAGE_KEY = "tfr:fitting-room:v1";
14057
+ const logger$c = getLogger("fitting-room");
14058
+ function readAll() {
14059
+ try {
14060
+ const raw = window.localStorage.getItem(STORAGE_KEY);
14061
+ if (!raw) {
14062
+ return {};
14063
+ }
14064
+ const parsed = JSON.parse(raw);
14065
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
14066
+ return {};
14067
+ }
14068
+ return parsed;
14069
+ } catch (error) {
14070
+ logger$c.logWarn("Failed to read fitting room from localStorage", {
14071
+ error
14072
+ });
14073
+ return {};
14074
+ }
14075
+ }
14076
+ function writeAll(all) {
14077
+ try {
14078
+ window.localStorage.setItem(STORAGE_KEY, JSON.stringify(all));
14079
+ } catch (error) {
14080
+ logger$c.logWarn("Failed to write fitting room to localStorage", {
14081
+ error
14082
+ });
14083
+ }
14084
+ }
14085
+ function readFittingRoom(brandId) {
14086
+ const all = readAll();
14087
+ const items = all[String(brandId)];
14088
+ return Array.isArray(items) ? items : [];
14089
+ }
14090
+ function writeFittingRoom(brandId, items) {
14091
+ const all = readAll();
14092
+ all[String(brandId)] = items;
14093
+ writeAll(all);
14094
+ }
14095
+ function _init$7() {
14096
+ const {
14097
+ brandId
14098
+ } = getStaticData();
14099
+ const items = readFittingRoom(brandId);
14100
+ useMainStore.setState({
14101
+ fittingRoom: items
14102
+ });
14103
+ }
14104
+ async function toggleFittingRoomItem(productId, isPdp) {
14105
+ const state = useMainStore.getState();
14106
+ const isInFittingRoom = state.fittingRoom.some((item) => item.externalId === productId);
14107
+ if (isInFittingRoom) {
14108
+ logger$c.logDebug("{{ts}} - Removing from fitting room", {
14109
+ productId
14110
+ });
14111
+ state.removeFromFittingRoom(productId);
14112
+ return;
14113
+ }
14114
+ logger$c.logDebug("{{ts}} - Adding to fitting room", {
14115
+ productId,
14116
+ isPdp
14117
+ });
14118
+ let size = null;
14119
+ let color = null;
14120
+ let colorwaySizeAssetId = null;
14121
+ if (isPdp) {
14122
+ const {
14123
+ currentProduct
14124
+ } = getStaticData();
14125
+ if (currentProduct) {
14126
+ try {
14127
+ const selection = await currentProduct.getSelectedOptions();
14128
+ size = selection.size || null;
14129
+ color = selection.color;
14130
+ } catch (error) {
14131
+ logger$c.logWarn("Failed to read selected options from currentProduct", {
14132
+ error
14133
+ });
14134
+ }
14135
+ const stored = state.productData[productId];
14136
+ if (stored && !("error" in stored) && size != null) {
14137
+ const sizeRec = stored.sizeFitRecommendation.available_sizes.find((s) => getSizeLabelFromSize(s) === size);
14138
+ if (sizeRec) {
14139
+ const csa = sizeRec.colorway_size_assets.find((c) => {
14140
+ const variant = currentProduct.variants.find((v) => v.sku === c.sku);
14141
+ return variant?.color === color;
14142
+ });
14143
+ if (csa) {
14144
+ colorwaySizeAssetId = csa.id;
14145
+ }
14146
+ }
14147
+ }
14148
+ }
14149
+ }
14150
+ state.addToFittingRoom({
14151
+ externalId: productId,
14152
+ size,
14153
+ color,
14154
+ colorwaySizeAssetId,
14155
+ addedAt: Date.now()
14156
+ });
14157
+ }
13899
14158
  const BROWSER_ALIASES_MAP = {
13900
14159
  AmazonBot: "amazonbot",
13901
14160
  "Amazon Silk": "amazon_silk",
@@ -16690,7 +16949,7 @@ const consoleLogger = {
16690
16949
  console?.[type]?.apply?.(console, args);
16691
16950
  }
16692
16951
  };
16693
- let Logger$2 = class Logger {
16952
+ let Logger$1 = class Logger2 {
16694
16953
  constructor(concreteLogger, options = {}) {
16695
16954
  this.init(concreteLogger, options);
16696
16955
  }
@@ -16718,7 +16977,7 @@ let Logger$2 = class Logger {
16718
16977
  return this.logger[lvl](args);
16719
16978
  }
16720
16979
  create(moduleName) {
16721
- return new Logger(this.logger, {
16980
+ return new Logger2(this.logger, {
16722
16981
  ...{
16723
16982
  prefix: `${this.prefix}:${moduleName}:`
16724
16983
  },
@@ -16728,10 +16987,10 @@ let Logger$2 = class Logger {
16728
16987
  clone(options) {
16729
16988
  options = options || this.options;
16730
16989
  options.prefix = options.prefix || this.prefix;
16731
- return new Logger(this.logger, options);
16990
+ return new Logger2(this.logger, options);
16732
16991
  }
16733
16992
  };
16734
- var baseLogger = new Logger$2();
16993
+ var baseLogger = new Logger$1();
16735
16994
  class EventEmitter {
16736
16995
  constructor() {
16737
16996
  this.observers = {};
@@ -19025,11 +19284,19 @@ const useTranslation = (ns, props = {}) => {
19025
19284
  const try_it_on$1 = "Try It On";
19026
19285
  const powered_by$1 = "Powered by";
19027
19286
  const loading$1 = "Loading...";
19287
+ const add_to_fitting_room$1 = "Add to Fitting Room";
19288
+ const added_to_fitting_room$1 = "In Fitting Room";
19289
+ const view_fitting_room$1 = "Fitting Room";
19290
+ const fitting_room$1 = { "title": "Fitting Room", "empty": "Your fitting room is empty.", "size_not_chosen": "Size not chosen — pick one before trying on.", "remove": "Remove" };
19028
19291
  const landing$1 = { "header": "Meet your mini me!", "description": "Loose on your waist but tight on your hips? We’ll show you.", "get_the_app": "Get the app", "already_have_account": "Already have an account?", "sign_in": "Sign in." };
19029
19292
  const en$1 = {
19030
19293
  try_it_on: try_it_on$1,
19031
19294
  powered_by: powered_by$1,
19032
19295
  loading: loading$1,
19296
+ add_to_fitting_room: add_to_fitting_room$1,
19297
+ added_to_fitting_room: added_to_fitting_room$1,
19298
+ view_fitting_room: view_fitting_room$1,
19299
+ fitting_room: fitting_room$1,
19033
19300
  landing: landing$1,
19034
19301
  "get-app": { "create_avatar": "Create your avatar" },
19035
19302
  "sign-in": { "email": "Email", "password": "Password", "forgot_password": "Forgot password?", "sign_in": "Sign in", "no_account": "Don’t have an account?", "download_app": "Download the app.", "invalid_email": "Please enter a valid email address.", "missing_password": "Please enter your password.", "login_failed": "Incorrect email or password." },
@@ -19041,11 +19308,19 @@ const en$1 = {
19041
19308
  const try_it_on = "Essayez-le";
19042
19309
  const powered_by = "Alimenté par";
19043
19310
  const loading = "Chargement...";
19311
+ const add_to_fitting_room = "Ajouter à la cabine d'essayage";
19312
+ const added_to_fitting_room = "Dans la cabine d'essayage";
19313
+ const view_fitting_room = "Cabine d'essayage";
19314
+ const fitting_room = { "title": "Cabine d'essayage", "empty": "Votre cabine d'essayage est vide.", "size_not_chosen": "Taille non choisie — choisissez-en une avant l'essayage.", "remove": "Supprimer" };
19044
19315
  const landing = { "header": "Rencontrez votre mini-moi !", "description": "Lâche à la taille mais serré aux hanches ? Nous allons vous montrer.", "get_the_app": "Obtenez l'application", "already_have_account": "Vous avez déjà un compte ?", "sign_in": "Connectez-vous." };
19045
19316
  const fr = {
19046
19317
  try_it_on,
19047
19318
  powered_by,
19048
19319
  loading,
19320
+ add_to_fitting_room,
19321
+ added_to_fitting_room,
19322
+ view_fitting_room,
19323
+ fitting_room,
19049
19324
  landing,
19050
19325
  "get-app": { "create_avatar": "Créez votre avatar" },
19051
19326
  "sign-in": { "email": "Email", "password": "Mot de passe", "forgot_password": "Mot de passe oublié ?", "sign_in": "Se connecter", "no_account": "Vous n'avez pas de compte ?", "download_app": "Téléchargez l'application.", "invalid_email": "Veuillez entrer une adresse e-mail valide.", "missing_password": "Veuillez entrer votre mot de passe.", "login_failed": "Email ou mot de passe incorrect." },
@@ -19189,57 +19464,6 @@ function ButtonT({
19189
19464
  const translatedText = translate(t, vars);
19190
19465
  return /* @__PURE__ */ jsx$1(Button, { ...props, children: translatedText });
19191
19466
  }
19192
- const Link = reactExports.forwardRef(({
19193
- children,
19194
- variant,
19195
- css: css2,
19196
- ...props
19197
- }, ref) => {
19198
- const variantCss = useVariantCss(variant, (theme) => ({
19199
- base: {
19200
- cursor: "pointer !important",
19201
- color: theme.color_fg_text,
19202
- fontFamily: theme.font_family,
19203
- fontSize: "14px",
19204
- textDecoration: "none"
19205
- },
19206
- brand: {
19207
- cursor: "pointer !important",
19208
- color: theme.color_fg_text,
19209
- fontFamily: theme.brand_font_family,
19210
- fontSize: "14px",
19211
- textDecoration: theme.brand_link_text_decoration
19212
- },
19213
- underline: {
19214
- cursor: "pointer !important",
19215
- color: theme.color_fg_text,
19216
- fontFamily: theme.font_family,
19217
- fontSize: "14px",
19218
- textDecoration: "underline"
19219
- },
19220
- semibold: {
19221
- cursor: "pointer !important",
19222
- color: theme.color_fg_text,
19223
- fontFamily: theme.font_family,
19224
- fontSize: "14px",
19225
- fontWeight: 600,
19226
- textDecoration: "none"
19227
- }
19228
- }));
19229
- return /* @__PURE__ */ jsx$1("a", { ref, css: [variantCss, css2, "", ""], ...props, children });
19230
- });
19231
- Link.displayName = "Link";
19232
- function LinkT({
19233
- t,
19234
- vars,
19235
- ...props
19236
- }) {
19237
- const {
19238
- t: translate
19239
- } = useTranslation();
19240
- const translatedText = translate(t, vars);
19241
- return /* @__PURE__ */ jsx$1(Link, { ...props, children: translatedText });
19242
- }
19243
19467
  var lib = { exports: {} };
19244
19468
  var Modal = {};
19245
19469
  var propTypes = { exports: {} };
@@ -20742,6 +20966,7 @@ const SvgChevronLeft = (props) => /* @__PURE__ */ reactExports.createElement("sv
20742
20966
  const SvgChevronRight = (props) => /* @__PURE__ */ reactExports.createElement("svg", { width: 48, height: 48, viewBox: "0 0 48 48", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props }, /* @__PURE__ */ reactExports.createElement("g", { opacity: 0.35 }, /* @__PURE__ */ reactExports.createElement("path", { d: "M18 36L30 24L18 12", stroke: "#1E1E1E", strokeWidth: 4, strokeLinecap: "round", strokeLinejoin: "round" })));
20743
20967
  const SvgCloseIcon = (props) => /* @__PURE__ */ reactExports.createElement("svg", { width: 15, height: 16, viewBox: "0 0 15 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props }, /* @__PURE__ */ reactExports.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M0.249026 1.13202C0.353455 1.02778 0.494976 0.969238 0.642526 0.969238C0.790076 0.969238 0.931598 1.02778 1.03603 1.13202L14.423 14.498C14.4747 14.5498 14.5157 14.6112 14.5436 14.6788C14.5716 14.7463 14.5859 14.8187 14.5859 14.8919C14.5858 14.965 14.5714 15.0374 14.5434 15.1049C14.5153 15.1725 14.4743 15.2338 14.4225 15.2855C14.3708 15.3372 14.3094 15.3782 14.2418 15.4061C14.1742 15.4341 14.1018 15.4484 14.0287 15.4484C13.9555 15.4483 13.8831 15.4339 13.8156 15.4058C13.7481 15.3778 13.6867 15.3368 13.635 15.285L0.250026 1.91802C0.198323 1.86646 0.157301 1.80521 0.129312 1.73777C0.101322 1.67034 0.0869141 1.59804 0.0869141 1.52502C0.0869141 1.45201 0.101322 1.37971 0.129312 1.31227C0.157301 1.24483 0.197323 1.18358 0.249026 1.13202Z", fill: "#21201F" }), /* @__PURE__ */ reactExports.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M0.249003 15.285C0.197149 15.2335 0.156 15.1722 0.127922 15.1046C0.0998429 15.0371 0.0853882 14.9647 0.0853882 14.8916C0.0853882 14.8184 0.0998429 14.746 0.127922 14.6785C0.156 14.6109 0.197149 14.5496 0.249003 14.498L13.635 1.13205C13.7407 1.03479 13.8798 0.982096 14.0234 0.984975C14.167 0.987855 14.3039 1.04608 14.4056 1.1475C14.5073 1.24893 14.5658 1.3857 14.5691 1.52928C14.5723 1.67286 14.52 1.81214 14.423 1.91805L1.036 15.285C0.931574 15.3893 0.790052 15.4478 0.642502 15.4478C0.494952 15.4478 0.353431 15.3893 0.249003 15.285Z", fill: "#21201F" }));
20744
20968
  const SvgDragHandle = (props) => /* @__PURE__ */ reactExports.createElement("svg", { width: 32, height: 4, viewBox: "0 0 32 4", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props }, /* @__PURE__ */ reactExports.createElement("rect", { width: 32, height: 4, rx: 2, fill: "black" }));
20969
+ const SvgFittingRoomIcon = (props) => /* @__PURE__ */ reactExports.createElement("svg", { width: 24, height: 24, viewBox: "0 0 11 9", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props }, /* @__PURE__ */ reactExports.createElement("path", { d: "M6.16963 1.44419C6.16963 1.16593 6.0591 0.89907 5.86234 0.702313C5.66558 0.505556 5.39872 0.39502 5.12047 0.39502C4.84221 0.39502 4.57535 0.505556 4.37859 0.702313C4.18184 0.89907 4.0713 1.16593 4.0713 1.44419C4.0713 2.31867 4.42277 3.01794 5.12047 3.54252H5.11627M5.11627 3.54252L9.29772 5.86537C9.46136 5.95625 9.59771 6.08925 9.69263 6.25058C9.78754 6.41191 9.83757 6.59569 9.83752 6.78287V7.2146C9.83752 7.49286 9.72698 7.75972 9.53023 7.95648C9.33347 8.15323 9.06661 8.26377 8.78835 8.26377H1.44419C1.16593 8.26377 0.89907 8.15323 0.702313 7.95648C0.505556 7.75972 0.39502 7.49286 0.39502 7.2146V6.78287C0.39497 6.59569 0.444998 6.41191 0.539913 6.25058C0.634829 6.08925 0.771177 5.95625 0.934816 5.86537L5.11627 3.54252Z", stroke: "currentColor", strokeWidth: 0.79, strokeLinecap: "round", strokeLinejoin: "round" }));
20745
20970
  const SvgInfoIcon = (props) => /* @__PURE__ */ reactExports.createElement("svg", { width: 14, height: 14, viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props }, /* @__PURE__ */ reactExports.createElement("path", { d: "M6.75 9.15V6.75M6.75 4.35H6.756M12.75 6.75C12.75 10.0637 10.0637 12.75 6.75 12.75C3.43629 12.75 0.75 10.0637 0.75 6.75C0.75 3.43629 3.43629 0.75 6.75 0.75C10.0637 0.75 12.75 3.43629 12.75 6.75Z", stroke: "#757575", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round" }));
20746
20971
  const SvgLoadingCircle = (props) => /* @__PURE__ */ reactExports.createElement("svg", { width: 108, height: 108, viewBox: "0 0 108 108", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props }, /* @__PURE__ */ reactExports.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M27.1325 75.9593C25.3424 77.0549 23.0032 76.4919 21.9077 74.7018L20.2636 72.0155L18.786 69.0476L17.5726 65.9622L16.6328 62.7828L15.9737 59.5336L15.6002 56.2393L15.5153 52.925L15.7196 49.6159L16.2115 46.3373L16.9485 43.2752C17.4396 41.2348 19.4918 39.9788 21.5322 40.4699C23.5726 40.961 24.8286 43.0132 24.3375 45.0536L23.6782 47.793L23.2847 50.4154L23.1213 53.0621L23.1893 55.713L23.4879 58.3479L24.0151 60.9467L24.7668 63.4897L25.7373 65.9574L26.9191 68.3312L28.39 70.7345C29.4855 72.5246 28.9225 74.8638 27.1325 75.9593Z", fill: "black" }), /* @__PURE__ */ reactExports.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M32.542 81.0995C33.5444 79.2557 35.8518 78.5736 37.6955 79.5761L40.171 80.922L42.6022 81.9807L45.1164 82.8236L47.6945 83.4441L50.3169 83.8375L52.9636 84.0009L55.6145 83.933L58.2494 83.6343L60.8482 83.1071L63.3912 82.3554L65.8589 81.385L68.2327 80.2031L70.4945 78.8189L72.627 77.2428L74.6141 75.4868L76.4405 73.5643L78.0924 71.49L79.5573 69.2796L80.8239 66.9499L81.8827 64.5187L82.7255 62.0044L83.346 59.4264L83.7395 56.8039L83.9029 54.1572L83.8349 51.5064L83.5363 48.8715L83.0091 46.2727L82.2574 43.7297L81.2869 41.2619L80.1051 38.8882L78.7208 36.6263L77.1447 34.4938L75.3888 32.5068L73.4662 30.6803L71.392 29.0284L69.1815 27.5636L66.8518 26.297L64.4206 25.2382L61.9064 24.3954L59.3283 23.7749L56.7059 23.3814L54.0592 23.218L51.4083 23.2859L48.7734 23.5846L46.1747 24.1118L43.6316 24.8635L41.1639 25.834L38.7901 27.0158L36.5283 28.4001L34.3958 29.9762L32.4088 31.7321L30.4681 33.7749C29.0225 35.2964 26.6173 35.358 25.0958 33.9125C23.5743 32.467 23.5127 30.0618 24.9582 28.5403L27.1274 26.2569L29.6118 24.0615L32.278 22.0909L35.1058 20.3603L38.0737 18.8826L41.1591 17.6693L44.3385 16.7295L47.5877 16.0704L50.882 15.6969L54.1963 15.612L57.5054 15.8163L60.7841 16.3082L64.0074 17.084L67.1509 18.1378L70.1905 19.4615L73.1032 21.0451L75.8669 22.8766L78.4603 24.942L80.8639 27.2255L83.0594 29.7098L85.0299 32.376L86.7606 35.2039L88.2382 38.1718L89.4516 41.2571L90.3914 44.4365L91.0505 47.6857L91.424 50.98L91.5089 54.2943L91.3046 57.6034L90.8127 60.8821L90.0369 64.1055L88.9831 67.2489L87.6594 70.2886L86.0757 73.2013L84.2443 75.9649L82.1789 78.5584L79.8954 80.962L77.4111 83.1574L74.7448 85.128L71.917 86.8587L68.9491 88.3363L65.8637 89.5496L62.6843 90.4894L59.4351 91.1486L56.1408 91.522L52.8265 91.6069L49.5175 91.4026L46.2388 90.9107L43.0154 90.1349L39.872 89.0811L36.8323 87.7574L34.0653 86.253C32.2216 85.2506 31.5395 82.9432 32.542 81.0995Z", fill: "black", fillOpacity: 0.45 }));
20747
20972
  const SvgTfrIcon = (props) => /* @__PURE__ */ reactExports.createElement("svg", { width: 12, height: 24, viewBox: "0 0 12 24", fill: "#21201F", xmlns: "http://www.w3.org/2000/svg", ...props }, /* @__PURE__ */ reactExports.createElement("path", { d: "M11.9082 4.0752V19.8516L0.161133 22.8545V1.05859L11.9082 4.0752ZM9.31836 10.8525C8.96951 10.8525 8.68661 11.206 8.68652 11.6416C8.68652 12.0773 8.96946 12.4307 9.31836 12.4307C9.6671 12.4304 9.9502 12.0771 9.9502 11.6416C9.95011 11.2062 9.66705 10.8528 9.31836 10.8525Z" }));
@@ -20760,11 +20985,13 @@ function ModalFrame({
20760
20985
  isOpen,
20761
20986
  onRequestClose,
20762
20987
  contentStyle,
20988
+ overlayStyle,
20763
20989
  children
20764
20990
  }) {
20765
20991
  const styleProp = {
20766
20992
  overlay: {
20767
- zIndex: 1e3
20993
+ zIndex: 1e3,
20994
+ ...overlayStyle
20768
20995
  },
20769
20996
  content: contentStyle
20770
20997
  };
@@ -20914,21 +21141,190 @@ function SidecarModalFrame({
20914
21141
  };
20915
21142
  return /* @__PURE__ */ jsx$1(ModalFrame, { isOpen: true, onRequestClose, contentStyle: applyContentStyle, children });
20916
21143
  }
20917
- var dayjs_min$1 = { exports: {} };
20918
- var dayjs_min = dayjs_min$1.exports;
20919
- var hasRequiredDayjs_min;
20920
- function requireDayjs_min() {
20921
- if (hasRequiredDayjs_min) return dayjs_min$1.exports;
20922
- hasRequiredDayjs_min = 1;
20923
- (function(module, exports$1) {
20924
- !(function(t, e) {
20925
- module.exports = e();
20926
- })(dayjs_min, (function() {
20927
- var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", c = "month", f = "quarter", h = "year", d = "date", l = "Invalid Date", $2 = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M2 = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), ordinal: function(t2) {
20928
- var e2 = ["th", "st", "nd", "rd"], n2 = t2 % 100;
20929
- return "[" + t2 + (e2[(n2 - 20) % 10] || e2[n2] || e2[0]) + "]";
20930
- } }, m = function(t2, e2, n2) {
20931
- var r2 = String(t2);
21144
+ function measureTopOffset() {
21145
+ const {
21146
+ getOverlayTopOffset
21147
+ } = getStaticData();
21148
+ if (!getOverlayTopOffset) {
21149
+ return 0;
21150
+ }
21151
+ try {
21152
+ const offset = getOverlayTopOffset();
21153
+ return Number.isFinite(offset) && offset > 0 ? offset : 0;
21154
+ } catch {
21155
+ return 0;
21156
+ }
21157
+ }
21158
+ function FittingRoomOverlay() {
21159
+ const {
21160
+ t
21161
+ } = useTranslation();
21162
+ const fittingRoom = useMainStore((state) => state.fittingRoom);
21163
+ const removeFromFittingRoom = useMainStore((state) => state.removeFromFittingRoom);
21164
+ const closeOverlay = useMainStore((state) => state.closeOverlay);
21165
+ const [topOffset, setTopOffset] = reactExports.useState(() => 0);
21166
+ reactExports.useEffect(() => {
21167
+ const savedScrollY = window.scrollY;
21168
+ if (savedScrollY > 0) {
21169
+ window.scrollTo(0, 0);
21170
+ }
21171
+ setTopOffset(measureTopOffset());
21172
+ const onResize = () => setTopOffset(measureTopOffset());
21173
+ window.addEventListener("resize", onResize);
21174
+ return () => {
21175
+ window.removeEventListener("resize", onResize);
21176
+ if (savedScrollY > 0) {
21177
+ window.scrollTo(0, savedScrollY);
21178
+ }
21179
+ };
21180
+ }, []);
21181
+ const css2 = useCss((theme) => ({
21182
+ body: {
21183
+ width: "100%",
21184
+ height: "100%",
21185
+ overflowY: "auto",
21186
+ padding: "24px",
21187
+ boxSizing: "border-box"
21188
+ },
21189
+ list: {
21190
+ display: "flex",
21191
+ flexDirection: "column",
21192
+ gap: "12px",
21193
+ width: "100%",
21194
+ maxWidth: "760px",
21195
+ margin: "0 auto"
21196
+ },
21197
+ item: {
21198
+ display: "flex",
21199
+ flexDirection: "column",
21200
+ gap: "4px",
21201
+ padding: "12px",
21202
+ borderColor: theme.color_fg_text,
21203
+ borderStyle: "solid",
21204
+ borderWidth: "1px",
21205
+ borderRadius: "8px"
21206
+ },
21207
+ itemHeader: {
21208
+ display: "flex",
21209
+ justifyContent: "space-between",
21210
+ alignItems: "center",
21211
+ gap: "8px"
21212
+ },
21213
+ itemId: {
21214
+ fontWeight: "bold",
21215
+ wordBreak: "break-all"
21216
+ },
21217
+ itemMeta: {
21218
+ fontSize: "13px",
21219
+ color: theme.color_fg_text
21220
+ },
21221
+ itemMissing: {
21222
+ fontSize: "13px",
21223
+ color: theme.color_danger
21224
+ },
21225
+ removeButton: {
21226
+ fontSize: "13px",
21227
+ textDecoration: "underline"
21228
+ },
21229
+ empty: {
21230
+ marginTop: "48px",
21231
+ textAlign: "center"
21232
+ }
21233
+ }));
21234
+ const overlayStyle = {
21235
+ top: `${topOffset}px`,
21236
+ left: 0,
21237
+ right: 0,
21238
+ bottom: 0,
21239
+ backgroundColor: "transparent"
21240
+ };
21241
+ const contentStyle = {
21242
+ position: "absolute",
21243
+ inset: 0,
21244
+ margin: 0,
21245
+ padding: 0,
21246
+ border: "none",
21247
+ borderRadius: 0,
21248
+ backgroundColor: "#FFFFFF",
21249
+ overflow: "hidden"
21250
+ };
21251
+ return /* @__PURE__ */ jsx$1(ModalFrame, { isOpen: true, onRequestClose: closeOverlay, overlayStyle, contentStyle, children: /* @__PURE__ */ jsx$1("div", { css: css2.body, children: fittingRoom.length === 0 ? /* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.empty, t: "fitting_room.empty" }) : /* @__PURE__ */ jsx$1("div", { css: css2.list, children: fittingRoom.map((item) => {
21252
+ const hasVariant = item.size != null;
21253
+ return /* @__PURE__ */ jsxs("div", { css: css2.item, children: [
21254
+ /* @__PURE__ */ jsxs("div", { css: css2.itemHeader, children: [
21255
+ /* @__PURE__ */ jsx$1(Text, { variant: "base", css: css2.itemId, children: item.externalId }),
21256
+ /* @__PURE__ */ jsx$1(Button, { variant: "base", css: css2.removeButton, onClick: () => removeFromFittingRoom(item.externalId), children: t("fitting_room.remove") })
21257
+ ] }),
21258
+ hasVariant ? /* @__PURE__ */ jsx$1(Text, { variant: "base", css: css2.itemMeta, children: [item.size, item.color].filter(Boolean).join(" / ") }) : /* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.itemMissing, t: "fitting_room.size_not_chosen" })
21259
+ ] }, item.externalId);
21260
+ }) }) }) });
21261
+ }
21262
+ const Link = reactExports.forwardRef(({
21263
+ children,
21264
+ variant,
21265
+ css: css2,
21266
+ ...props
21267
+ }, ref) => {
21268
+ const variantCss = useVariantCss(variant, (theme) => ({
21269
+ base: {
21270
+ cursor: "pointer !important",
21271
+ color: theme.color_fg_text,
21272
+ fontFamily: theme.font_family,
21273
+ fontSize: "14px",
21274
+ textDecoration: "none"
21275
+ },
21276
+ brand: {
21277
+ cursor: "pointer !important",
21278
+ color: theme.color_fg_text,
21279
+ fontFamily: theme.brand_font_family,
21280
+ fontSize: "14px",
21281
+ textDecoration: theme.brand_link_text_decoration
21282
+ },
21283
+ underline: {
21284
+ cursor: "pointer !important",
21285
+ color: theme.color_fg_text,
21286
+ fontFamily: theme.font_family,
21287
+ fontSize: "14px",
21288
+ textDecoration: "underline"
21289
+ },
21290
+ semibold: {
21291
+ cursor: "pointer !important",
21292
+ color: theme.color_fg_text,
21293
+ fontFamily: theme.font_family,
21294
+ fontSize: "14px",
21295
+ fontWeight: 600,
21296
+ textDecoration: "none"
21297
+ }
21298
+ }));
21299
+ return /* @__PURE__ */ jsx$1("a", { ref, css: [variantCss, css2, "", ""], ...props, children });
21300
+ });
21301
+ Link.displayName = "Link";
21302
+ function LinkT({
21303
+ t,
21304
+ vars,
21305
+ ...props
21306
+ }) {
21307
+ const {
21308
+ t: translate
21309
+ } = useTranslation();
21310
+ const translatedText = translate(t, vars);
21311
+ return /* @__PURE__ */ jsx$1(Link, { ...props, children: translatedText });
21312
+ }
21313
+ var dayjs_min$1 = { exports: {} };
21314
+ var dayjs_min = dayjs_min$1.exports;
21315
+ var hasRequiredDayjs_min;
21316
+ function requireDayjs_min() {
21317
+ if (hasRequiredDayjs_min) return dayjs_min$1.exports;
21318
+ hasRequiredDayjs_min = 1;
21319
+ (function(module, exports$1) {
21320
+ !(function(t, e) {
21321
+ module.exports = e();
21322
+ })(dayjs_min, (function() {
21323
+ var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", c = "month", f = "quarter", h = "year", d = "date", l = "Invalid Date", $2 = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M2 = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), ordinal: function(t2) {
21324
+ var e2 = ["th", "st", "nd", "rd"], n2 = t2 % 100;
21325
+ return "[" + t2 + (e2[(n2 - 20) % 10] || e2[n2] || e2[0]) + "]";
21326
+ } }, m = function(t2, e2, n2) {
21327
+ var r2 = String(t2);
20932
21328
  return !r2 || r2.length >= e2 ? t2 : "" + Array(e2 + 1 - r2.length).join(n2) + t2;
20933
21329
  }, v = { s: m, z: function(t2) {
20934
21330
  var e2 = -t2.utcOffset(), n2 = Math.abs(e2), r2 = Math.floor(n2 / 60), i2 = n2 % 60;
@@ -22385,7 +22781,7 @@ const defaultLogHandler = (instance2, logType, ...args) => {
22385
22781
  throw new Error(`Attempted to log a message with an invalid logType (value: ${logType})`);
22386
22782
  }
22387
22783
  };
22388
- let Logger$1 = class Logger2 {
22784
+ class Logger3 {
22389
22785
  /**
22390
22786
  * Gives you an instance of a Logger to capture messages according to
22391
22787
  * Firebase's logging scheme.
@@ -22449,7 +22845,7 @@ let Logger$1 = class Logger2 {
22449
22845
  this._userLogHandler && this._userLogHandler(this, LogLevel.ERROR, ...args);
22450
22846
  this._logHandler(this, LogLevel.ERROR, ...args);
22451
22847
  }
22452
- };
22848
+ }
22453
22849
  const instanceOfAny = (object, constructors) => constructors.some((c) => object instanceof c);
22454
22850
  let idbProxyableTypes;
22455
22851
  let cursorAdvanceMethods;
@@ -22677,7 +23073,7 @@ function isVersionServiceProvider(provider) {
22677
23073
  }
22678
23074
  const name$q = "@firebase/app";
22679
23075
  const version$1$1 = "0.14.5";
22680
- const logger$6 = new Logger$1("@firebase/app");
23076
+ const logger$b = new Logger3("@firebase/app");
22681
23077
  const name$p = "@firebase/app-compat";
22682
23078
  const name$o = "@firebase/analytics-compat";
22683
23079
  const name$n = "@firebase/analytics";
@@ -22744,13 +23140,13 @@ function _addComponent(app, component) {
22744
23140
  try {
22745
23141
  app.container.addComponent(component);
22746
23142
  } catch (e) {
22747
- logger$6.debug(`Component ${component.name} failed to register with FirebaseApp ${app.name}`, e);
23143
+ logger$b.debug(`Component ${component.name} failed to register with FirebaseApp ${app.name}`, e);
22748
23144
  }
22749
23145
  }
22750
23146
  function _registerComponent(component) {
22751
23147
  const componentName = component.name;
22752
23148
  if (_components.has(componentName)) {
22753
- logger$6.debug(`There were multiple attempts to register component ${componentName}.`);
23149
+ logger$b.debug(`There were multiple attempts to register component ${componentName}.`);
22754
23150
  return false;
22755
23151
  }
22756
23152
  _components.set(componentName, component);
@@ -22959,7 +23355,7 @@ function registerVersion(libraryKeyOrName, version2, variant) {
22959
23355
  if (versionMismatch) {
22960
23356
  warning.push(`version name "${version2}" contains illegal characters (whitespace or "/")`);
22961
23357
  }
22962
- logger$6.warn(warning.join(" "));
23358
+ logger$b.warn(warning.join(" "));
22963
23359
  return;
22964
23360
  }
22965
23361
  _registerComponent(new Component(
@@ -23003,12 +23399,12 @@ async function readHeartbeatsFromIndexedDB(app) {
23003
23399
  return result;
23004
23400
  } catch (e) {
23005
23401
  if (e instanceof FirebaseError) {
23006
- logger$6.warn(e.message);
23402
+ logger$b.warn(e.message);
23007
23403
  } else {
23008
23404
  const idbGetError = ERROR_FACTORY.create("idb-get", {
23009
23405
  originalErrorMessage: e?.message
23010
23406
  });
23011
- logger$6.warn(idbGetError.message);
23407
+ logger$b.warn(idbGetError.message);
23012
23408
  }
23013
23409
  }
23014
23410
  }
@@ -23021,12 +23417,12 @@ async function writeHeartbeatsToIndexedDB(app, heartbeatObject) {
23021
23417
  await tx.done;
23022
23418
  } catch (e) {
23023
23419
  if (e instanceof FirebaseError) {
23024
- logger$6.warn(e.message);
23420
+ logger$b.warn(e.message);
23025
23421
  } else {
23026
23422
  const idbGetError = ERROR_FACTORY.create("idb-set", {
23027
23423
  originalErrorMessage: e?.message
23028
23424
  });
23029
- logger$6.warn(idbGetError.message);
23425
+ logger$b.warn(idbGetError.message);
23030
23426
  }
23031
23427
  }
23032
23428
  }
@@ -23075,7 +23471,7 @@ class HeartbeatServiceImpl {
23075
23471
  }
23076
23472
  return this._storage.overwrite(this._heartbeatsCache);
23077
23473
  } catch (e) {
23078
- logger$6.warn(e);
23474
+ logger$b.warn(e);
23079
23475
  }
23080
23476
  }
23081
23477
  /**
@@ -23106,7 +23502,7 @@ class HeartbeatServiceImpl {
23106
23502
  }
23107
23503
  return headerString;
23108
23504
  } catch (e) {
23109
- logger$6.warn(e);
23505
+ logger$b.warn(e);
23110
23506
  return "";
23111
23507
  }
23112
23508
  }
@@ -25742,7 +26138,7 @@ User.UNAUTHENTICATED = new User(null), // TODO(mikelehen): Look into getting a p
25742
26138
  // non-FirebaseAuth providers.
25743
26139
  User.GOOGLE_CREDENTIALS = new User("google-credentials-uid"), User.FIRST_PARTY = new User("first-party-uid"), User.MOCK_USER = new User("mock-user");
25744
26140
  let x = "12.3.0";
25745
- const O = new Logger$1("@firebase/firestore");
26141
+ const O = new Logger3("@firebase/firestore");
25746
26142
  function __PRIVATE_getLogLevel() {
25747
26143
  return O.logLevel;
25748
26144
  }
@@ -35492,7 +35888,7 @@ function _prodErrorMap() {
35492
35888
  }
35493
35889
  const prodErrorMap = _prodErrorMap;
35494
35890
  const _DEFAULT_AUTH_ERROR_FACTORY = new ErrorFactory("auth", "Firebase", _prodErrorMap());
35495
- const logClient = new Logger$1("@firebase/auth");
35891
+ const logClient = new Logger3("@firebase/auth");
35496
35892
  function _logWarn(msg, ...args) {
35497
35893
  if (logClient.logLevel <= LogLevel.WARN) {
35498
35894
  logClient.warn(`Auth (${SDK_VERSION}): ${msg}`, ...args);
@@ -40670,218 +41066,87 @@ registerAuth(
40670
41066
  "Browser"
40671
41067
  /* ClientPlatform.BROWSER */
40672
41068
  );
40673
- let debugSource = false;
40674
- function _init$4(debug) {
40675
- if (debug) {
40676
- let escapeRegExp = function(string) {
40677
- return "^" + string.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".") + "$";
40678
- };
40679
- if (debug === true) {
40680
- debugSource = true;
40681
- } else if (debug instanceof RegExp) {
40682
- debugSource = debug;
40683
- } else if (Array.isArray(debug)) {
40684
- debugSource = new RegExp(debug.map((s) => escapeRegExp(s)).join("|"));
40685
- } else if (typeof debug === "string") {
40686
- debugSource = new RegExp(escapeRegExp(debug));
41069
+ const firebaseDateToDayjs = (date) => {
41070
+ return dayjs(date.seconds * 1e3);
41071
+ };
41072
+ const LOGIN_TRACKING_PERIOD_SECONDS = 1800;
41073
+ const logger$a = getLogger("firebase");
41074
+ let firebaseApp = null;
41075
+ class FirestoreManager {
41076
+ constructor(firestore) {
41077
+ this.firestore = firestore;
41078
+ }
41079
+ async getDocData(collectionName, docId) {
41080
+ const docRef = this.doc(collectionName, docId);
41081
+ const docSnap = await getDoc(docRef);
41082
+ if (docSnap.exists()) {
41083
+ return docSnap.data();
41084
+ } else {
41085
+ return null;
40687
41086
  }
40688
41087
  }
40689
- }
40690
- function isDebugSource(source) {
40691
- if (typeof debugSource === "boolean") {
40692
- return debugSource;
41088
+ async setDocData(collectionName, docId, data) {
41089
+ const docRef = this.doc(collectionName, docId);
41090
+ await setDoc(docRef, data);
40693
41091
  }
40694
- return debugSource.test(source);
40695
- }
40696
- function log(entry) {
40697
- const {
40698
- source,
40699
- level,
40700
- message,
40701
- data,
40702
- duration
40703
- } = entry;
40704
- if (level === "debug" && !isDebugSource(source)) {
40705
- return;
41092
+ async mergeDocData(collectionName, docId, data) {
41093
+ const docRef = this.doc(collectionName, docId);
41094
+ await setDoc(docRef, data, {
41095
+ merge: true
41096
+ });
40706
41097
  }
40707
- let finalMessage = message;
40708
- if (finalMessage.includes("{{")) {
40709
- finalMessage = finalMessage.replace(/{{ts}}/g, (/* @__PURE__ */ new Date()).toISOString());
41098
+ listenToDoc(collectionName, docId, callback) {
41099
+ const docRef = this.doc(collectionName, docId);
41100
+ const unsubscribe = onSnapshot(docRef, (docSnap) => {
41101
+ if (docSnap.exists()) {
41102
+ callback(docSnap.data());
41103
+ } else {
41104
+ callback(null);
41105
+ }
41106
+ });
41107
+ return unsubscribe;
40710
41108
  }
40711
- finalMessage = `[TFR ${source}][${level.toUpperCase()}] ${finalMessage}`;
40712
- const logData = [finalMessage];
40713
- if (duration !== void 0) {
40714
- logData.push(`${duration.toFixed(2)} ms`);
41109
+ // listenToSubDoc subscribes to <parent>/<parentID>/<sub>/<subID>. Used for
41110
+ // VTO compositions which live at users/{uid}/vto_compositions/{token}.
41111
+ listenToSubDoc(parent, parentID, sub, subID, callback) {
41112
+ const docRef = doc(this.firestore, parent, parentID, sub, subID);
41113
+ return onSnapshot(docRef, (snap) => {
41114
+ if (snap.exists()) {
41115
+ callback(snap.data());
41116
+ } else {
41117
+ callback(null);
41118
+ }
41119
+ });
40715
41120
  }
40716
- if (data) {
40717
- logData.push(data);
41121
+ async queryDocs(collectionName, constraints) {
41122
+ const q2 = query(this.collection(collectionName), ...constraints);
41123
+ return getDocs(q2);
40718
41124
  }
40719
- switch (level) {
40720
- case "error":
40721
- console.error(...logData);
40722
- break;
40723
- case "warn":
40724
- console.warn(...logData);
40725
- break;
40726
- case "info":
40727
- console.info(...logData);
40728
- break;
40729
- case "debug":
40730
- console.debug(...logData);
40731
- break;
40732
- default:
40733
- console.log(...logData);
41125
+ collection(collectionName) {
41126
+ return collection(this.firestore, collectionName);
40734
41127
  }
40735
- }
40736
- class Logger3 {
40737
- constructor(source) {
40738
- this.timers = {};
40739
- this.source = source;
41128
+ doc(collectionName, docId) {
41129
+ return doc(this.firestore, collectionName, docId);
40740
41130
  }
40741
- logError(message, data = null) {
40742
- log({
40743
- source: this.source,
40744
- level: "error",
40745
- message,
40746
- data
40747
- });
41131
+ }
41132
+ let firestoreManager = null;
41133
+ function getFirestoreManager() {
41134
+ if (!firestoreManager) {
41135
+ throw new Error("Firebase not initialized. Call _init first.");
40748
41136
  }
40749
- logWarn(message, data = null) {
40750
- log({
40751
- source: this.source,
40752
- level: "warn",
40753
- message,
40754
- data
40755
- });
40756
- }
40757
- logInfo(message, data = null) {
40758
- log({
40759
- source: this.source,
40760
- level: "info",
40761
- message,
40762
- data
40763
- });
40764
- }
40765
- logDebug(message, data = null) {
40766
- log({
40767
- source: this.source,
40768
- level: "debug",
40769
- message,
40770
- data
40771
- });
40772
- }
40773
- logTimer(timerName, message, data = null) {
40774
- const duration = this.getTimerDuration(timerName);
40775
- log({
40776
- source: this.source,
40777
- level: "debug",
40778
- message,
40779
- data,
40780
- duration: duration ?? void 0
40781
- });
40782
- }
40783
- clearTimers() {
40784
- this.timers = {};
40785
- }
40786
- timerStart(name2) {
40787
- this.timers[name2] = [performance.now(), null];
40788
- }
40789
- timerEnd(name2) {
40790
- const timer = this.timers[name2];
40791
- if (timer && timer[1] === null) {
40792
- timer[1] = performance.now();
40793
- }
40794
- }
40795
- getTimers() {
40796
- const result = {};
40797
- for (const name2 in this.timers) {
40798
- result[name2] = this.getTimerDuration(name2);
40799
- }
40800
- return result;
40801
- }
40802
- getTimerDuration(name2) {
40803
- const timer = this.timers[name2];
40804
- if (timer && timer[1] !== null) {
40805
- return timer[1] - timer[0];
40806
- }
40807
- return null;
40808
- }
40809
- isDebugEnabled() {
40810
- return isDebugSource(this.source);
40811
- }
40812
- }
40813
- function getLogger(source) {
40814
- return new Logger3(source);
40815
- }
40816
- const firebaseDateToDayjs = (date) => {
40817
- return dayjs(date.seconds * 1e3);
40818
- };
40819
- const LOGIN_TRACKING_PERIOD_SECONDS = 1800;
40820
- const logger$5 = getLogger("firebase");
40821
- let firebaseApp = null;
40822
- class FirestoreManager {
40823
- constructor(firestore) {
40824
- this.firestore = firestore;
40825
- }
40826
- async getDocData(collectionName, docId) {
40827
- const docRef = this.doc(collectionName, docId);
40828
- const docSnap = await getDoc(docRef);
40829
- if (docSnap.exists()) {
40830
- return docSnap.data();
40831
- } else {
40832
- return null;
40833
- }
40834
- }
40835
- async setDocData(collectionName, docId, data) {
40836
- const docRef = this.doc(collectionName, docId);
40837
- await setDoc(docRef, data);
40838
- }
40839
- async mergeDocData(collectionName, docId, data) {
40840
- const docRef = this.doc(collectionName, docId);
40841
- await setDoc(docRef, data, {
40842
- merge: true
40843
- });
40844
- }
40845
- listenToDoc(collectionName, docId, callback) {
40846
- const docRef = this.doc(collectionName, docId);
40847
- const unsubscribe = onSnapshot(docRef, (docSnap) => {
40848
- if (docSnap.exists()) {
40849
- callback(docSnap.data());
40850
- } else {
40851
- callback(null);
40852
- }
40853
- });
40854
- return unsubscribe;
40855
- }
40856
- async queryDocs(collectionName, constraints) {
40857
- const q2 = query(this.collection(collectionName), ...constraints);
40858
- return getDocs(q2);
40859
- }
40860
- collection(collectionName) {
40861
- return collection(this.firestore, collectionName);
40862
- }
40863
- doc(collectionName, docId) {
40864
- return doc(this.firestore, collectionName, docId);
40865
- }
40866
- }
40867
- let firestoreManager = null;
40868
- function getFirestoreManager() {
40869
- if (!firestoreManager) {
40870
- throw new Error("Firebase not initialized. Call _init first.");
40871
- }
40872
- return firestoreManager;
40873
- }
40874
- class AuthManager {
40875
- constructor(auth, brandId) {
40876
- this.userProfile = null;
40877
- this.authStateChangeListeners = /* @__PURE__ */ new Set();
40878
- this.userProfileChangeListeners = /* @__PURE__ */ new Set();
40879
- this.listenToUserProfileUnsub = null;
40880
- this.auth = auth;
40881
- this.brandId = brandId;
40882
- this.addAuthStateChangeListener((authUser) => this.handleAuthStateChanged(authUser));
40883
- onAuthStateChanged(this.auth, (authUser) => {
40884
- this.authStateChangeListeners.forEach((callback) => callback(authUser));
41137
+ return firestoreManager;
41138
+ }
41139
+ class AuthManager {
41140
+ constructor(auth, brandId) {
41141
+ this.userProfile = null;
41142
+ this.authStateChangeListeners = /* @__PURE__ */ new Set();
41143
+ this.userProfileChangeListeners = /* @__PURE__ */ new Set();
41144
+ this.listenToUserProfileUnsub = null;
41145
+ this.auth = auth;
41146
+ this.brandId = brandId;
41147
+ this.addAuthStateChangeListener((authUser) => this.handleAuthStateChanged(authUser));
41148
+ onAuthStateChanged(this.auth, (authUser) => {
41149
+ this.authStateChangeListeners.forEach((callback) => callback(authUser));
40885
41150
  });
40886
41151
  }
40887
41152
  addAuthStateChangeListener(callback) {
@@ -40945,7 +41210,7 @@ class AuthManager {
40945
41210
  this.listenToUserProfileUnsub = null;
40946
41211
  }
40947
41212
  if (authUser) {
40948
- logger$5.logDebug("User logged in:", {
41213
+ logger$a.logDebug("User logged in:", {
40949
41214
  uid: authUser.uid
40950
41215
  });
40951
41216
  this.listenToUserProfileUnsub = getFirestoreManager().listenToDoc("users", authUser.uid, (doc2) => {
@@ -40979,7 +41244,7 @@ class AuthManager {
40979
41244
  await firestore.mergeDocData("user_logging", userLoggingDocId, userLoggingData);
40980
41245
  }
40981
41246
  } catch (error) {
40982
- logger$5.logError("Error logging user login activity:", {
41247
+ logger$a.logError("Error logging user login activity:", {
40983
41248
  error
40984
41249
  });
40985
41250
  }
@@ -40997,7 +41262,7 @@ function getAuthManager() {
40997
41262
  }
40998
41263
  return authManager;
40999
41264
  }
41000
- async function _init$3() {
41265
+ async function _init$4() {
41001
41266
  const {
41002
41267
  brandId,
41003
41268
  config
@@ -41028,7 +41293,7 @@ async function _init$3() {
41028
41293
  }
41029
41294
  }
41030
41295
  const CONTACT_US_LINK = "mailto:info@thefittingroom.tech?subject=Forgot%20Password%20Assistance";
41031
- const logger$4 = getLogger("forgot-password");
41296
+ const logger$9 = getLogger("forgot-password");
41032
41297
  function ForgotPasswordOverlay({
41033
41298
  returnToOverlay
41034
41299
  }) {
@@ -41094,7 +41359,7 @@ function ForgotPasswordOverlay({
41094
41359
  await authManager2.sendPasswordResetEmail(email2);
41095
41360
  setLinkSent(true);
41096
41361
  } catch (error) {
41097
- logger$4.logError("Error sending password reset email:", {
41362
+ logger$9.logError("Error sending password reset email:", {
41098
41363
  error
41099
41364
  });
41100
41365
  }
@@ -41313,7 +41578,7 @@ function TfrTitle() {
41313
41578
  }));
41314
41579
  return /* @__PURE__ */ jsx$1("div", { css: css2.container, children: /* @__PURE__ */ jsx$1(SvgTfrName, { css: css2.nameIcon }) });
41315
41580
  }
41316
- const logger$3 = getLogger("sign-in");
41581
+ const logger$8 = getLogger("sign-in");
41317
41582
  function SignInOverlay({
41318
41583
  returnToOverlay
41319
41584
  }) {
@@ -41387,7 +41652,7 @@ function SignInOverlay({
41387
41652
  closeOverlay();
41388
41653
  }
41389
41654
  } catch (error) {
41390
- logger$3.logError("Login failed:", {
41655
+ logger$8.logError("Login failed:", {
41391
41656
  error
41392
41657
  });
41393
41658
  setEmailError(" ");
@@ -41783,7 +42048,7 @@ function Loading({
41783
42048
  }
41784
42049
  let baseUrl;
41785
42050
  let responseCache = {};
41786
- function _init$2() {
42051
+ function _init$3() {
41787
42052
  const {
41788
42053
  config
41789
42054
  } = getStaticData();
@@ -41851,13 +42116,14 @@ async function getSizeRecommendation(styleId) {
41851
42116
  endpoint: `/v1/styles/${styleId}/recommendation`
41852
42117
  });
41853
42118
  }
41854
- async function requestVtoSingle(colorwaySizeAssetId) {
41855
- await execApiRequest({
41856
- useCache: true,
41857
- // although this is a POST, we only want to send it once
42119
+ async function requestVto(items) {
42120
+ return await execApiRequest({
41858
42121
  useToken: true,
41859
42122
  method: "POST",
41860
- endpoint: `/v1/colorway-size-assets/${colorwaySizeAssetId}/frames`
42123
+ endpoint: "/v1/vto-compositions",
42124
+ body: {
42125
+ items
42126
+ }
41861
42127
  });
41862
42128
  }
41863
42129
  const recordCache = {};
@@ -41875,45 +42141,82 @@ async function getStyleByExternalId(brandId, externalId) {
41875
42141
  recordCache[cacheKey] = record;
41876
42142
  return record;
41877
42143
  }
41878
- async function getStyleGarmentCategoryById(styleGarmentCategoryId) {
41879
- const cacheKey = `getStyleGarmentCategoryById/${styleGarmentCategoryId}`;
41880
- if (recordCache[cacheKey]) {
41881
- return recordCache[cacheKey];
41882
- }
41883
- const firestore = getFirestoreManager();
41884
- const record = await firestore.getDocData("style_garment_categories", styleGarmentCategoryId.toString());
41885
- if (!record) {
41886
- return null;
42144
+ const logger$7 = getLogger("product");
42145
+ function _init$2() {
42146
+ useMainStore.subscribe((state, prevState) => {
42147
+ if (state.userHasAvatar && !prevState.userHasAvatar) {
42148
+ const {
42149
+ currentProduct
42150
+ } = getStaticData();
42151
+ if (currentProduct) {
42152
+ loadProductDataToStore(currentProduct.externalId);
42153
+ }
42154
+ }
42155
+ });
42156
+ }
42157
+ async function loadProductData(externalId) {
42158
+ const {
42159
+ brandId
42160
+ } = getStaticData();
42161
+ const style = await getStyleByExternalId(brandId, externalId);
42162
+ if (!style) {
42163
+ throw new Error(`Style not found for externalId: ${externalId}`);
41887
42164
  }
41888
- recordCache[cacheKey] = record;
41889
- return record;
42165
+ const sizeFitRecommendation = await getSizeRecommendation(style.id);
42166
+ return {
42167
+ externalId,
42168
+ style,
42169
+ sizeFitRecommendation
42170
+ };
41890
42171
  }
41891
- function getSizeLabelFromSize(size) {
41892
- if (size.label) {
41893
- return size.label;
42172
+ function loadProductDataToStore(externalId) {
42173
+ async function loadAndStore() {
42174
+ try {
42175
+ const productData2 = await loadProductData(externalId);
42176
+ useMainStore.getState().setProductData(productData2.externalId, productData2);
42177
+ logger$7.logDebug(`Loaded product data for externalId: ${externalId}`, {
42178
+ productData: productData2
42179
+ });
42180
+ } catch (error) {
42181
+ logger$7.logError(`Error loading product data for externalId: ${externalId}`, {
42182
+ error
42183
+ });
42184
+ }
41894
42185
  }
41895
- return size.size_value?.name ?? null;
42186
+ const {
42187
+ userIsLoggedIn,
42188
+ userHasAvatar,
42189
+ productData
42190
+ } = useMainStore.getState();
42191
+ if (productData[externalId] || !userIsLoggedIn || userHasAvatar === false) {
42192
+ return;
42193
+ }
42194
+ loadAndStore();
41896
42195
  }
42196
+ const NON_PRIORITY_VTO_REQUEST_DELAY_MS = 500;
41897
42197
  const AVATAR_IMAGE_ASPECT_RATIO = 2 / 3;
41898
42198
  const AVATAR_GUTTER_HEIGHT_PX = 100;
41899
42199
  const CONTENT_AREA_WIDTH_PX = 550;
41900
- const logger$2 = getLogger("overlays/vto-single");
42200
+ const logger$6 = getLogger("overlays/vto-single");
42201
+ function compositionKey(csaId, tucked) {
42202
+ return `${csaId}:${0}`;
42203
+ }
41901
42204
  function VtoSingleOverlay() {
41902
- const {
41903
- brandId
41904
- } = getStaticData();
41905
42205
  const userIsLoggedIn = useMainStore((state) => state.userIsLoggedIn);
41906
42206
  const userHasAvatar = useMainStore((state) => state.userHasAvatar);
41907
42207
  const userProfile = useMainStore((state) => state.userProfile);
42208
+ const storeProductData = useMainStore((state) => state.productData);
41908
42209
  const deviceLayout = useMainStore((state) => state.deviceLayout);
41909
42210
  const openOverlay = useMainStore((state) => state.openOverlay);
41910
42211
  const closeOverlay = useMainStore((state) => state.closeOverlay);
41911
- const [loadedProductData, setLoadedProductData] = reactExports.useState(null);
42212
+ const [vtoProductData, setVtoProductData] = reactExports.useState(null);
41912
42213
  const [selectedSizeLabel, setSelectedSizeLabel] = reactExports.useState(null);
41913
42214
  const [selectedColorLabel, setSelectedColorLabel] = reactExports.useState(null);
41914
42215
  const [modalStyle, setModalStyle] = reactExports.useState({});
41915
- const fetchedVtoSkus = reactExports.useRef(/* @__PURE__ */ new Set());
41916
- const readyVtoSkus = reactExports.useRef(/* @__PURE__ */ new Set());
42216
+ const compositionTokensRef = reactExports.useRef(/* @__PURE__ */ new Map());
42217
+ const [vtoDocsByToken, setVtoDocsByToken] = reactExports.useState({});
42218
+ const subscriptionsRef = reactExports.useRef(/* @__PURE__ */ new Map());
42219
+ const lastPriorityVtoRequestTimeRef = reactExports.useRef(null);
41917
42220
  reactExports.useEffect(() => {
41918
42221
  if (!userIsLoggedIn) {
41919
42222
  openOverlay(OverlayName.LANDING, {
@@ -41930,15 +42233,30 @@ function VtoSingleOverlay() {
41930
42233
  }
41931
42234
  }, [userIsLoggedIn, userHasAvatar, openOverlay]);
41932
42235
  reactExports.useEffect(() => {
41933
- async function fetchInitialData() {
42236
+ const {
42237
+ currentProduct
42238
+ } = getStaticData();
42239
+ if (!currentProduct) {
42240
+ return;
42241
+ }
42242
+ loadProductDataToStore(currentProduct.externalId);
42243
+ }, []);
42244
+ reactExports.useEffect(() => {
42245
+ async function setupInitialVtoData() {
41934
42246
  try {
41935
- logger$2.clearTimers();
41936
- logger$2.timerStart("fetchInitialData");
41937
- logger$2.logDebug("{{ts}} - Starting fetch of initial data");
41938
- logger$2.timerStart("fetchInitialData_1_getProductData");
41939
42247
  const {
41940
42248
  currentProduct
41941
42249
  } = getStaticData();
42250
+ if (!currentProduct) {
42251
+ return;
42252
+ }
42253
+ const storeProduct = storeProductData[currentProduct.externalId];
42254
+ if (!storeProduct) {
42255
+ return;
42256
+ }
42257
+ if ("error" in storeProduct) {
42258
+ throw storeProduct.error;
42259
+ }
41942
42260
  const {
41943
42261
  productName,
41944
42262
  productDescriptionHtml,
@@ -41947,26 +42265,15 @@ function VtoSingleOverlay() {
41947
42265
  const {
41948
42266
  color: selectedColor
41949
42267
  } = await currentProduct.getSelectedOptions();
41950
- logger$2.timerEnd("fetchInitialData_1_getProductData");
41951
- logger$2.timerStart("fetchInitialData_2_getStyleData");
41952
- const styleRec = await getStyleByExternalId(brandId, currentProduct.externalId);
41953
- if (!styleRec) {
41954
- throw new Error(`Style not found for externalId: ${currentProduct.externalId}`);
41955
- }
41956
- const styleGarmentCategoryRec = await getStyleGarmentCategoryById(styleRec.style_garment_category_id);
41957
- const styleCategoryLabel = styleGarmentCategoryRec?.style_category_label ?? null;
41958
- logger$2.timerEnd("fetchInitialData_2_getStyleData");
41959
- logger$2.timerStart("fetchInitialData_3_getSizeRecommendation");
41960
- const sizeRecommendationRecord = await getSizeRecommendation(styleRec.id);
41961
- logger$2.timerEnd("fetchInitialData_3_getSizeRecommendation");
41962
- logger$2.timerStart("fetchInitialData_4_assembleLoadedData");
42268
+ const styleCategoryLabel = storeProduct.style.style_category_label || null;
42269
+ const sizeRecommendationRecord = storeProduct.sizeFitRecommendation;
41963
42270
  {
41964
42271
  const recommendedSizeId = sizeRecommendationRecord.recommended_size.id || null;
41965
42272
  const recommendedSizeLabel = getSizeLabelFromSize(sizeRecommendationRecord.recommended_size);
41966
42273
  if (recommendedSizeId == null || recommendedSizeLabel == null) {
41967
42274
  throw new Error(`No recommended size found for externalId: ${currentProduct.externalId}`);
41968
42275
  }
41969
- let productData;
42276
+ let vtoProductData2;
41970
42277
  {
41971
42278
  const fitClassification = sizeRecommendationRecord.fit_classification;
41972
42279
  const sizes = [];
@@ -41987,7 +42294,7 @@ function VtoSingleOverlay() {
41987
42294
  const sku = csaRec.sku;
41988
42295
  const variant = variants.find((v) => v.sku === sku);
41989
42296
  if (!variant) {
41990
- logger$2.logWarn(`Variant not found for externalId: ${currentProduct.externalId} sizeId: ${sizeId} sku: ${sku}`);
42297
+ logger$6.logWarn(`Variant not found for externalId: ${currentProduct.externalId} sizeId: ${sizeId} sku: ${sku}`);
41991
42298
  continue;
41992
42299
  }
41993
42300
  const colorLabel = variant.color || null;
@@ -42000,7 +42307,7 @@ function VtoSingleOverlay() {
42000
42307
  });
42001
42308
  }
42002
42309
  if (!colors.length) {
42003
- logger$2.logWarn(`No valid colorways found for externalId: ${currentProduct.externalId} sizeId: ${sizeId}`);
42310
+ logger$6.logWarn(`No valid colorways found for externalId: ${currentProduct.externalId} sizeId: ${sizeId}`);
42004
42311
  continue;
42005
42312
  }
42006
42313
  sizes.push({
@@ -42014,7 +42321,7 @@ function VtoSingleOverlay() {
42014
42321
  if (!sizes.length) {
42015
42322
  throw new Error(`No valid sizes found for externalId: ${currentProduct.externalId}`);
42016
42323
  }
42017
- productData = {
42324
+ vtoProductData2 = {
42018
42325
  productName,
42019
42326
  productDescriptionHtml,
42020
42327
  fitClassification,
@@ -42026,7 +42333,7 @@ function VtoSingleOverlay() {
42026
42333
  }
42027
42334
  let recommendedColorLabel = null;
42028
42335
  {
42029
- const recommendedSizeRecord = productData.sizes.find((s) => s.isRecommended);
42336
+ const recommendedSizeRecord = vtoProductData2.sizes.find((s) => s.isRecommended);
42030
42337
  if (!recommendedSizeRecord) {
42031
42338
  throw new Error("Recommended size record not found");
42032
42339
  }
@@ -42038,41 +42345,35 @@ function VtoSingleOverlay() {
42038
42345
  }) || recommendedSizeRecord.colors[0];
42039
42346
  recommendedColorLabel = recommendedSizeColorRecord?.colorLabel ?? null;
42040
42347
  }
42041
- setLoadedProductData(productData);
42348
+ setVtoProductData(vtoProductData2);
42042
42349
  setSelectedSizeLabel(recommendedSizeLabel);
42043
42350
  setSelectedColorLabel(recommendedColorLabel);
42044
42351
  }
42045
- for (const sku in userProfile?.vto?.[brandId] ?? {}) {
42046
- fetchedVtoSkus.current.add(sku);
42047
- readyVtoSkus.current.add(sku);
42048
- }
42049
- logger$2.timerEnd("fetchInitialData_4_assembleLoadedData");
42050
- logger$2.timerEnd("fetchInitialData");
42051
- logger$2.logTimer("total", "{{ts}} - Completed fetch of initial data", logger$2.getTimers());
42052
42352
  } catch (error) {
42053
- logger$2.logError("Error fetching initial data:", {
42353
+ logger$6.logError("Error fetching initial data:", {
42054
42354
  error
42055
42355
  });
42056
- setLoadedProductData(false);
42356
+ setVtoProductData(false);
42057
42357
  setSelectedSizeLabel(null);
42058
42358
  setSelectedColorLabel(null);
42059
42359
  }
42060
42360
  }
42061
- if (userIsLoggedIn && userHasAvatar && userProfile && loadedProductData == null) {
42062
- fetchInitialData();
42361
+ if (vtoProductData !== null) {
42362
+ return;
42063
42363
  }
42064
- }, [userIsLoggedIn, userHasAvatar, userProfile, loadedProductData]);
42364
+ setupInitialVtoData();
42365
+ }, [storeProductData, vtoProductData, userProfile]);
42065
42366
  const {
42066
42367
  sizeColorRecord: selectedColorSizeRecord,
42067
42368
  availableColorLabels
42068
42369
  } = reactExports.useMemo(() => {
42069
- if (!loadedProductData) {
42370
+ if (!vtoProductData) {
42070
42371
  return {
42071
42372
  sizeColorRecord: null,
42072
42373
  availableColorLabels: []
42073
42374
  };
42074
42375
  }
42075
- const sizeRecord = loadedProductData.sizes.find((s) => s.sizeLabel === selectedSizeLabel);
42376
+ const sizeRecord = vtoProductData.sizes.find((s) => s.sizeLabel === selectedSizeLabel);
42076
42377
  if (!sizeRecord || !sizeRecord.colors.length) {
42077
42378
  return {
42078
42379
  sizeColorRecord: null,
@@ -42085,74 +42386,141 @@ function VtoSingleOverlay() {
42085
42386
  sizeColorRecord,
42086
42387
  availableColorLabels: availableColorLabels2
42087
42388
  };
42088
- }, [loadedProductData, selectedSizeLabel, selectedColorLabel]);
42089
- const requestVto = reactExports.useCallback((sizeColorRecord) => {
42090
- if (fetchedVtoSkus.current.has(sizeColorRecord.sku)) {
42389
+ }, [vtoProductData, selectedSizeLabel, selectedColorLabel]);
42390
+ const requestVto$1 = reactExports.useCallback((sizeColorRecord, priority) => {
42391
+ const csaId = sizeColorRecord.colorwaySizeAssetId;
42392
+ const key = compositionKey(csaId);
42393
+ if (compositionTokensRef.current.has(key)) {
42091
42394
  return;
42092
42395
  }
42093
- logger$2.timerStart(`requestVto_${sizeColorRecord.sku}`);
42094
- logger$2.logDebug(`{{ts}} - Requesting VTO for sku: ${sizeColorRecord.sku}`, {
42095
- sizeColorRecord
42096
- });
42097
- fetchedVtoSkus.current.add(sizeColorRecord.sku);
42098
- requestVtoSingle(sizeColorRecord.colorwaySizeAssetId).catch((error) => {
42099
- logger$2.logError("Error requesting VTO:", {
42100
- error,
42396
+ function executeRequest() {
42397
+ logger$6.timerStart(`requestVto_${sizeColorRecord.sku}`);
42398
+ logger$6.logDebug(`{{ts}} - Requesting VTO for sku: ${sizeColorRecord.sku}`, {
42399
+ priority,
42101
42400
  sizeColorRecord
42102
42401
  });
42402
+ compositionTokensRef.current.set(key, "");
42403
+ requestVto([{
42404
+ colorway_size_asset_id: csaId,
42405
+ tucked: false
42406
+ }]).then((resp) => {
42407
+ compositionTokensRef.current.set(key, resp.token);
42408
+ subscribeToCompositionToken(resp.token, sizeColorRecord.sku);
42409
+ }).catch((error) => {
42410
+ logger$6.logError(`Error requesting VTO for sku: ${sizeColorRecord.sku}`, {
42411
+ error,
42412
+ sizeColorRecord
42413
+ });
42414
+ compositionTokensRef.current.delete(key);
42415
+ });
42416
+ }
42417
+ {
42418
+ let delay = 0;
42419
+ if (priority) {
42420
+ lastPriorityVtoRequestTimeRef.current = Date.now();
42421
+ } else {
42422
+ const lastPriorityTime = lastPriorityVtoRequestTimeRef.current;
42423
+ if (lastPriorityTime) {
42424
+ const now = Date.now();
42425
+ const minNextRequestTime = lastPriorityTime + NON_PRIORITY_VTO_REQUEST_DELAY_MS;
42426
+ if (now < minNextRequestTime) {
42427
+ delay = minNextRequestTime - now;
42428
+ }
42429
+ }
42430
+ }
42431
+ if (delay) {
42432
+ setTimeout(executeRequest, delay);
42433
+ return;
42434
+ }
42435
+ }
42436
+ executeRequest();
42437
+ }, []);
42438
+ const subscribeToCompositionToken = reactExports.useCallback((token2, sku) => {
42439
+ if (subscriptionsRef.current.has(token2)) {
42440
+ return;
42441
+ }
42442
+ const authManager2 = getAuthManager();
42443
+ const uid = authManager2.getAuthUser()?.uid;
42444
+ if (!uid) {
42445
+ logger$6.logWarn(`subscribe to vto composition skipped: no uid`, {
42446
+ token: token2,
42447
+ sku
42448
+ });
42449
+ return;
42450
+ }
42451
+ const unsub = getFirestoreManager().listenToSubDoc("users", uid, "vto_compositions", token2, (data) => {
42452
+ if (data && data.frames && data.frames.length > 0) {
42453
+ logger$6.timerEnd(`requestVto_${sku}`);
42454
+ logger$6.logTimer(`requestVto_${sku}`, `{{ts}} - VTO data is loaded for sku: ${sku}`);
42455
+ }
42456
+ setVtoDocsByToken((prev2) => ({
42457
+ ...prev2,
42458
+ [token2]: data ?? {}
42459
+ }));
42103
42460
  });
42461
+ subscriptionsRef.current.set(token2, unsub);
42462
+ }, []);
42463
+ reactExports.useEffect(() => {
42464
+ return () => {
42465
+ subscriptionsRef.current.forEach((unsub) => {
42466
+ try {
42467
+ unsub();
42468
+ } catch (e) {
42469
+ logger$6.logError("Error unsubscribing from vto composition", {
42470
+ error: e
42471
+ });
42472
+ }
42473
+ });
42474
+ subscriptionsRef.current.clear();
42475
+ };
42104
42476
  }, []);
42105
42477
  reactExports.useEffect(() => {
42106
42478
  if (selectedColorSizeRecord) {
42107
- requestVto(selectedColorSizeRecord);
42479
+ requestVto$1(selectedColorSizeRecord, true);
42108
42480
  }
42109
- }, [requestVto, selectedColorSizeRecord]);
42481
+ }, [requestVto$1, selectedColorSizeRecord]);
42110
42482
  reactExports.useEffect(() => {
42111
- if (!loadedProductData) {
42483
+ if (!vtoProductData) {
42112
42484
  return;
42113
42485
  }
42114
- for (const sizeRecord of loadedProductData.sizes) {
42486
+ for (const sizeRecord of vtoProductData.sizes) {
42115
42487
  const sizeColorRecord = sizeRecord.colors.find((c) => c.colorLabel === selectedColorLabel) ?? sizeRecord.colors[0];
42116
42488
  if (sizeColorRecord) {
42117
- requestVto(sizeColorRecord);
42489
+ requestVto$1(sizeColorRecord, false);
42118
42490
  }
42119
42491
  }
42120
- }, [requestVto, loadedProductData, selectedColorLabel]);
42492
+ }, [requestVto$1, vtoProductData, selectedColorLabel]);
42121
42493
  const vtoData = reactExports.useMemo(() => {
42122
- if (!userProfile || !selectedColorSizeRecord) {
42494
+ if (!selectedColorSizeRecord) {
42123
42495
  return null;
42124
42496
  }
42125
- const availableSkuData = userProfile.vto?.[brandId];
42126
- if (!availableSkuData) {
42497
+ const key = compositionKey(selectedColorSizeRecord.colorwaySizeAssetId);
42498
+ const token2 = compositionTokensRef.current.get(key);
42499
+ if (!token2) {
42127
42500
  return null;
42128
42501
  }
42129
- if (logger$2.isDebugEnabled()) {
42130
- for (const sku of fetchedVtoSkus.current) {
42131
- if (!readyVtoSkus.current.has(sku) && availableSkuData[sku]) {
42132
- readyVtoSkus.current.add(sku);
42133
- logger$2.timerEnd(`requestVto_${sku}`);
42134
- logger$2.logTimer(`requestVto_${sku}`, `{{ts}} - VTO data is loaded for sku: ${sku}`);
42135
- }
42136
- }
42137
- }
42138
- const vtoData2 = availableSkuData[selectedColorSizeRecord.sku];
42139
- if (!vtoData2) {
42502
+ const doc2 = vtoDocsByToken[token2];
42503
+ if (!doc2 || !doc2.frames || doc2.frames.length === 0) {
42140
42504
  return null;
42141
42505
  }
42142
- if (vtoData2.frames) {
42143
- vtoData2.frames.forEach((frameUrl) => {
42144
- const img = new Image();
42145
- img.src = frameUrl;
42146
- });
42147
- }
42148
- return vtoData2;
42149
- }, [selectedColorSizeRecord, userProfile]);
42150
- const frameUrls = vtoData?.frames ?? null;
42506
+ logger$6.logDebug(`{{ts}} - Displaying VTO for sku: ${selectedColorSizeRecord.sku}`);
42507
+ return doc2;
42508
+ }, [selectedColorSizeRecord, vtoDocsByToken]);
42509
+ const frameUrls = reactExports.useMemo(() => {
42510
+ if (!vtoData?.frames) return null;
42511
+ const baseUrl2 = getStaticData().config.frames.baseUrl;
42512
+ const rewritten = vtoData.frames.map((u) => applyFrameBaseUrl(u, baseUrl2));
42513
+ rewritten.forEach((url) => {
42514
+ const img = new Image();
42515
+ img.src = url;
42516
+ });
42517
+ return rewritten;
42518
+ }, [vtoData]);
42151
42519
  const handleSignOutClick = reactExports.useCallback(() => {
42152
42520
  closeOverlay();
42153
42521
  const authManager2 = getAuthManager();
42154
42522
  authManager2.logout().catch((error) => {
42155
- logger$2.logError("Error during logout:", {
42523
+ logger$6.logError("Error during logout:", {
42156
42524
  error
42157
42525
  });
42158
42526
  });
@@ -42165,21 +42533,24 @@ function VtoSingleOverlay() {
42165
42533
  const {
42166
42534
  currentProduct
42167
42535
  } = getStaticData();
42536
+ if (!currentProduct) {
42537
+ return;
42538
+ }
42168
42539
  closeOverlay();
42169
42540
  await currentProduct.addToCart({
42170
42541
  size: selectedSizeLabel,
42171
42542
  color: selectedColorLabel
42172
42543
  });
42173
42544
  } catch (error) {
42174
- logger$2.logError("Error adding to cart:", {
42545
+ logger$6.logError("Error adding to cart:", {
42175
42546
  error
42176
42547
  });
42177
42548
  }
42178
42549
  }, [selectedColorLabel, selectedSizeLabel]);
42179
- if (loadedProductData === false) {
42550
+ if (vtoProductData === false) {
42180
42551
  return /* @__PURE__ */ jsx$1(SidecarModalFrame, { onRequestClose: closeOverlay, children: /* @__PURE__ */ jsx$1(NoFitLayout, { onClose: closeOverlay, onSignOut: handleSignOutClick }) });
42181
42552
  }
42182
- if (!userIsLoggedIn || !userHasAvatar || loadedProductData == null || !selectedColorSizeRecord) {
42553
+ if (!userIsLoggedIn || !userHasAvatar || vtoProductData == null || !selectedColorSizeRecord) {
42183
42554
  return /* @__PURE__ */ jsx$1(SidecarModalFrame, { onRequestClose: closeOverlay, children: /* @__PURE__ */ jsx$1(Loading, {}) });
42184
42555
  }
42185
42556
  let Layout;
@@ -42188,7 +42559,7 @@ function VtoSingleOverlay() {
42188
42559
  } else {
42189
42560
  Layout = DesktopLayout;
42190
42561
  }
42191
- return /* @__PURE__ */ jsx$1(SidecarModalFrame, { onRequestClose: closeOverlay, contentStyle: modalStyle, children: /* @__PURE__ */ jsx$1(Layout, { loadedProductData, selectedColorSizeRecord, availableColorLabels, selectedColorLabel, selectedSizeLabel, frameUrls, setModalStyle, onClose: closeOverlay, onChangeColor: setSelectedColorLabel, onChangeSize: setSelectedSizeLabel, onAddToCart: handleAddToCartClick, onSignOut: handleSignOutClick }) });
42562
+ return /* @__PURE__ */ jsx$1(SidecarModalFrame, { onRequestClose: closeOverlay, contentStyle: modalStyle, children: /* @__PURE__ */ jsx$1(Layout, { loadedProductData: vtoProductData, selectedColorSizeRecord, availableColorLabels, selectedColorLabel, selectedSizeLabel, frameUrls, setModalStyle, onClose: closeOverlay, onChangeColor: setSelectedColorLabel, onChangeSize: setSelectedSizeLabel, onAddToCart: handleAddToCartClick, onSignOut: handleSignOutClick }) });
42192
42563
  }
42193
42564
  function NoFitLayout({
42194
42565
  onClose,
@@ -43145,65 +43516,262 @@ function Footer({
43145
43516
  /* @__PURE__ */ jsx$1(SvgTfrName, { css: css2.tfrIcon })
43146
43517
  ] });
43147
43518
  }
43148
- function useSizeRecommendation(load) {
43149
- const [record, setRecord] = reactExports.useState(null);
43150
- const [isLoading, setIsLoading] = reactExports.useState(false);
43151
- const [error, setError] = reactExports.useState(null);
43519
+ const logger$5 = getLogger("widgets/add-to-fitting-room-compact");
43520
+ function AddToFittingRoomCompactWidget({
43521
+ attributes
43522
+ }) {
43152
43523
  const {
43153
- userHasAvatar
43154
- } = useMainStore();
43155
- reactExports.useEffect(() => {
43156
- if (!load || !userHasAvatar) {
43524
+ t
43525
+ } = useTranslation();
43526
+ const {
43527
+ currentProduct
43528
+ } = getStaticData();
43529
+ const attrProductId = attributes["product-id"];
43530
+ const productId = attrProductId || currentProduct?.externalId || null;
43531
+ const isPdp = productId != null && productId === currentProduct?.externalId;
43532
+ const isInFittingRoom = useMainStore((state) => productId == null ? false : state.fittingRoom.some((item) => item.externalId === productId));
43533
+ const css2 = useCss((theme) => ({
43534
+ button: {
43535
+ display: "inline-flex",
43536
+ alignItems: "center",
43537
+ justifyContent: "center",
43538
+ width: "36px",
43539
+ height: "36px",
43540
+ padding: 0,
43541
+ backgroundColor: "transparent",
43542
+ border: "none",
43543
+ borderRadius: "50%",
43544
+ cursor: "pointer"
43545
+ },
43546
+ buttonAdded: {
43547
+ backgroundColor: theme.color_fg_text
43548
+ },
43549
+ icon: {
43550
+ width: "24px",
43551
+ height: "24px",
43552
+ color: theme.color_fg_text
43553
+ },
43554
+ iconAdded: {
43555
+ color: "#FFFFFF"
43556
+ }
43557
+ }));
43558
+ if (productId == null) {
43559
+ return null;
43560
+ }
43561
+ const handleClick = () => {
43562
+ toggleFittingRoomItem(productId, isPdp).catch((error) => {
43563
+ logger$5.logError("toggleFittingRoomItem failed", {
43564
+ error
43565
+ });
43566
+ });
43567
+ };
43568
+ const ariaLabel = t(isInFittingRoom ? "added_to_fitting_room" : "add_to_fitting_room");
43569
+ return /* @__PURE__ */ jsx$1("button", { type: "button", onClick: handleClick, css: [css2.button, isInFittingRoom && css2.buttonAdded, "", ""], "aria-label": ariaLabel, "aria-pressed": isInFittingRoom, children: /* @__PURE__ */ jsx$1(SvgFittingRoomIcon, { css: [css2.icon, isInFittingRoom && css2.iconAdded, "", ""] }) });
43570
+ }
43571
+ const logger$4 = getLogger("widgets/add-to-fitting-room");
43572
+ function AddToFittingRoomWidget({
43573
+ attributes
43574
+ }) {
43575
+ const {
43576
+ currentProduct
43577
+ } = getStaticData();
43578
+ const attrProductId = attributes["product-id"];
43579
+ const productId = attrProductId || currentProduct?.externalId || null;
43580
+ const isPdp = productId != null && productId === currentProduct?.externalId;
43581
+ const isInFittingRoom = useMainStore((state) => productId == null ? false : state.fittingRoom.some((item) => item.externalId === productId));
43582
+ const css2 = useCss((theme) => ({
43583
+ button: {
43584
+ marginTop: "10px",
43585
+ marginBottom: "10px",
43586
+ width: "100%",
43587
+ maxWidth: "440px",
43588
+ display: "flex",
43589
+ alignItems: "center",
43590
+ justifyContent: "center",
43591
+ gap: "10px",
43592
+ padding: "13px",
43593
+ backgroundColor: "white",
43594
+ borderWidth: "1px",
43595
+ borderColor: theme.color_fg_text,
43596
+ borderStyle: "solid",
43597
+ borderRadius: "30px",
43598
+ cursor: "pointer"
43599
+ },
43600
+ icon: {
43601
+ color: theme.color_fg_text,
43602
+ width: "20px",
43603
+ height: "20px"
43604
+ },
43605
+ text: {
43606
+ fontSize: "14px",
43607
+ textTransform: "uppercase"
43608
+ }
43609
+ }));
43610
+ if (productId == null) {
43611
+ return null;
43612
+ }
43613
+ const handleClick = () => {
43614
+ toggleFittingRoomItem(productId, isPdp).catch((error) => {
43615
+ logger$4.logError("toggleFittingRoomItem failed", {
43616
+ error
43617
+ });
43618
+ });
43619
+ };
43620
+ return /* @__PURE__ */ jsxs("button", { type: "button", onClick: handleClick, css: css2.button, children: [
43621
+ /* @__PURE__ */ jsx$1(SvgFittingRoomIcon, { css: css2.icon }),
43622
+ /* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.text, t: isInFittingRoom ? "added_to_fitting_room" : "add_to_fitting_room" })
43623
+ ] });
43624
+ }
43625
+ const logger$3 = getLogger("widgets/fitting-room-icon");
43626
+ function FittingRoomIconWidget({}) {
43627
+ const {
43628
+ t
43629
+ } = useTranslation();
43630
+ const count = useMainStore((state) => state.fittingRoom.length);
43631
+ const isOpen = useMainStore((state) => state.activeOverlay === OverlayName.FITTING_ROOM);
43632
+ const openOverlay = useMainStore((state) => state.openOverlay);
43633
+ const closeOverlay = useMainStore((state) => state.closeOverlay);
43634
+ const css2 = useCss((theme) => ({
43635
+ button: {
43636
+ position: "relative",
43637
+ display: "inline-flex",
43638
+ alignItems: "center",
43639
+ justifyContent: "center",
43640
+ width: "44px",
43641
+ height: "44px",
43642
+ padding: 0,
43643
+ background: "transparent",
43644
+ border: "none",
43645
+ cursor: "pointer",
43646
+ color: "inherit"
43647
+ },
43648
+ icon: {
43649
+ width: "24px",
43650
+ height: "24px",
43651
+ color: theme.color_fg_text
43652
+ },
43653
+ badge: {
43654
+ position: "absolute",
43655
+ top: "4px",
43656
+ right: "4px",
43657
+ minWidth: "18px",
43658
+ height: "18px",
43659
+ padding: "0 5px",
43660
+ borderRadius: "9px",
43661
+ backgroundColor: theme.color_fg_text,
43662
+ color: "#FFFFFF",
43663
+ fontSize: "11px",
43664
+ fontWeight: "bold",
43665
+ lineHeight: "18px",
43666
+ textAlign: "center"
43667
+ }
43668
+ }));
43669
+ const handleClick = () => {
43670
+ if (isOpen) {
43671
+ logger$3.logDebug("{{ts}} - Closing fitting room overlay", {
43672
+ count
43673
+ });
43674
+ closeOverlay();
43157
43675
  return;
43158
43676
  }
43159
- async function fetchSizeRec() {
43160
- const {
43161
- brandId,
43162
- currentProduct
43163
- } = getStaticData();
43164
- try {
43165
- setRecord(null);
43166
- setIsLoading(true);
43167
- setError(null);
43168
- const style = await getStyleByExternalId(brandId, currentProduct.externalId);
43169
- if (!style) {
43170
- throw new Error("Style not found");
43171
- }
43172
- const sizeRecommendationRecord = await getSizeRecommendation(style.id);
43173
- setRecord(sizeRecommendationRecord);
43174
- } catch (error2) {
43175
- setError(error2);
43176
- } finally {
43177
- setIsLoading(false);
43178
- }
43677
+ logger$3.logDebug("{{ts}} - Opening fitting room overlay", {
43678
+ count
43679
+ });
43680
+ openOverlay(OverlayName.FITTING_ROOM);
43681
+ };
43682
+ return /* @__PURE__ */ jsxs("button", { type: "button", onClick: handleClick, css: css2.button, "aria-label": t("view_fitting_room"), children: [
43683
+ /* @__PURE__ */ jsx$1(SvgFittingRoomIcon, { css: css2.icon }),
43684
+ count > 0 && /* @__PURE__ */ jsx$1("span", { css: css2.badge, children: count })
43685
+ ] });
43686
+ }
43687
+ const logger$2 = getLogger("widgets/fitting-room");
43688
+ function FittingRoomWidget({}) {
43689
+ const count = useMainStore((state) => state.fittingRoom.length);
43690
+ const openOverlay = useMainStore((state) => state.openOverlay);
43691
+ const css2 = useCss((theme) => ({
43692
+ button: {
43693
+ marginTop: "10px",
43694
+ marginBottom: "10px",
43695
+ width: "100%",
43696
+ maxWidth: "440px",
43697
+ display: "flex",
43698
+ alignItems: "center",
43699
+ justifyContent: "center",
43700
+ gap: "10px",
43701
+ padding: "13px",
43702
+ backgroundColor: "white",
43703
+ borderWidth: "1px",
43704
+ borderColor: theme.color_fg_text,
43705
+ borderStyle: "solid",
43706
+ borderRadius: "30px",
43707
+ cursor: "pointer"
43708
+ },
43709
+ icon: {
43710
+ color: theme.color_fg_text,
43711
+ width: "20px",
43712
+ height: "20px"
43713
+ },
43714
+ text: {
43715
+ fontSize: "14px",
43716
+ textTransform: "uppercase"
43717
+ },
43718
+ badge: {
43719
+ fontSize: "14px",
43720
+ fontWeight: "bold",
43721
+ minWidth: "22px",
43722
+ height: "22px",
43723
+ padding: "0 6px",
43724
+ borderRadius: "11px",
43725
+ backgroundColor: theme.color_fg_text,
43726
+ color: "#FFFFFF",
43727
+ display: "inline-flex",
43728
+ alignItems: "center",
43729
+ justifyContent: "center"
43179
43730
  }
43180
- fetchSizeRec();
43181
- }, [load, userHasAvatar]);
43182
- return {
43183
- record,
43184
- isLoading,
43185
- error
43731
+ }));
43732
+ const handleClick = () => {
43733
+ logger$2.logDebug("{{ts}} - Opening fitting room overlay", {
43734
+ count
43735
+ });
43736
+ openOverlay(OverlayName.FITTING_ROOM);
43186
43737
  };
43738
+ return /* @__PURE__ */ jsxs("button", { type: "button", onClick: handleClick, css: css2.button, children: [
43739
+ /* @__PURE__ */ jsx$1(SvgFittingRoomIcon, { css: css2.icon }),
43740
+ /* @__PURE__ */ jsx$1(TextT, { variant: "base", css: css2.text, t: "view_fitting_room" }),
43741
+ count > 0 && /* @__PURE__ */ jsx$1(Text, { variant: "base", css: css2.badge, children: count })
43742
+ ] });
43187
43743
  }
43188
43744
  const logger$1 = getLogger("size-rec");
43189
43745
  function SizeRecWidget({}) {
43190
43746
  const openOverlay = useMainStore((state) => state.openOverlay);
43191
43747
  const openedOverlays = useMainStore((state) => state.openedOverlays);
43748
+ const storeProductData = useMainStore((state) => state.productData);
43192
43749
  const hasOpenedVtoSingleOverlay = openedOverlays.includes(OverlayName.VTO_SINGLE);
43193
- const {
43194
- record: sizeRecommendationRecord,
43195
- error: sizeRecommendationError
43196
- } = useSizeRecommendation(hasOpenedVtoSingleOverlay);
43197
- reactExports.useEffect(() => {
43198
- if (sizeRecommendationError) {
43750
+ const sizeRecommendationRecord = reactExports.useMemo(() => {
43751
+ const {
43752
+ currentProduct
43753
+ } = getStaticData();
43754
+ if (!currentProduct) {
43755
+ return null;
43756
+ }
43757
+ const {
43758
+ externalId
43759
+ } = currentProduct;
43760
+ const productData = storeProductData[externalId];
43761
+ if (!productData) {
43762
+ return null;
43763
+ }
43764
+ if ("error" in productData) {
43199
43765
  logger$1.logError("Error loading size recommendation:", {
43200
- error: sizeRecommendationError
43766
+ error: productData.error
43201
43767
  });
43768
+ return null;
43202
43769
  }
43203
- }, [sizeRecommendationError]);
43770
+ return productData.sizeFitRecommendation || null;
43771
+ }, [storeProductData]);
43204
43772
  const handleLinkClick = reactExports.useCallback(() => {
43205
43773
  openOverlay(OverlayName.VTO_SINGLE);
43206
- }, []);
43774
+ }, [openOverlay]);
43207
43775
  if (!sizeRecommendationRecord || !hasOpenedVtoSingleOverlay) {
43208
43776
  return null;
43209
43777
  }
@@ -43315,6 +43883,22 @@ function _init$1() {
43315
43883
  });
43316
43884
  }
43317
43885
  const WIDGETS = {
43886
+ [
43887
+ "add-to-fitting-room"
43888
+ /* ADD_TO_FITTING_ROOM */
43889
+ ]: AddToFittingRoomWidget,
43890
+ [
43891
+ "add-to-fitting-room-compact"
43892
+ /* ADD_TO_FITTING_ROOM_COMPACT */
43893
+ ]: AddToFittingRoomCompactWidget,
43894
+ [
43895
+ "fitting-room"
43896
+ /* FITTING_ROOM */
43897
+ ]: FittingRoomWidget,
43898
+ [
43899
+ "fitting-room-icon"
43900
+ /* FITTING_ROOM_ICON */
43901
+ ]: FittingRoomIconWidget,
43318
43902
  [
43319
43903
  "size-rec"
43320
43904
  /* SIZE_REC */
@@ -43325,6 +43909,7 @@ const WIDGETS = {
43325
43909
  ]: VtoButtonWidget
43326
43910
  };
43327
43911
  var OverlayName = /* @__PURE__ */ ((OverlayName2) => {
43912
+ OverlayName2["FITTING_ROOM"] = "fitting-room";
43328
43913
  OverlayName2["FORGOT_PASSWORD"] = "forgot-password";
43329
43914
  OverlayName2["GET_APP"] = "get-app";
43330
43915
  OverlayName2["LANDING"] = "landing";
@@ -43333,6 +43918,10 @@ var OverlayName = /* @__PURE__ */ ((OverlayName2) => {
43333
43918
  return OverlayName2;
43334
43919
  })(OverlayName || {});
43335
43920
  const OVERLAYS = {
43921
+ [
43922
+ "fitting-room"
43923
+ /* FITTING_ROOM */
43924
+ ]: FittingRoomOverlay,
43336
43925
  [
43337
43926
  "forgot-password"
43338
43927
  /* FORGOT_PASSWORD */
@@ -43392,6 +43981,47 @@ const useMainStore = create((set) => ({
43392
43981
  userHasAvatar
43393
43982
  });
43394
43983
  },
43984
+ // Product data:
43985
+ productData: {},
43986
+ setProductData: (externalId, data) => set((prevState) => ({
43987
+ productData: {
43988
+ ...prevState.productData,
43989
+ [externalId]: data
43990
+ }
43991
+ })),
43992
+ // Fitting room:
43993
+ fittingRoom: [],
43994
+ addToFittingRoom: (item) => set((prevState) => {
43995
+ const filtered = prevState.fittingRoom.filter((existing) => existing.externalId !== item.externalId);
43996
+ const next2 = [...filtered, item];
43997
+ writeFittingRoom(getStaticData().brandId, next2);
43998
+ return {
43999
+ fittingRoom: next2
44000
+ };
44001
+ }),
44002
+ removeFromFittingRoom: (externalId) => set((prevState) => {
44003
+ const next2 = prevState.fittingRoom.filter((existing) => existing.externalId !== externalId);
44004
+ writeFittingRoom(getStaticData().brandId, next2);
44005
+ return {
44006
+ fittingRoom: next2
44007
+ };
44008
+ }),
44009
+ updateFittingRoomItem: (externalId, patch) => set((prevState) => {
44010
+ const next2 = prevState.fittingRoom.map((existing) => existing.externalId === externalId ? {
44011
+ ...existing,
44012
+ ...patch
44013
+ } : existing);
44014
+ writeFittingRoom(getStaticData().brandId, next2);
44015
+ return {
44016
+ fittingRoom: next2
44017
+ };
44018
+ }),
44019
+ clearFittingRoom: () => set(() => {
44020
+ writeFittingRoom(getStaticData().brandId, []);
44021
+ return {
44022
+ fittingRoom: []
44023
+ };
44024
+ }),
43395
44025
  // UI state:
43396
44026
  activeOverlay: null,
43397
44027
  activeOverlayProps: null,
@@ -43438,6 +44068,7 @@ function Widget({
43438
44068
  var EnvName = /* @__PURE__ */ ((EnvName2) => {
43439
44069
  EnvName2["DEVELOPMENT"] = "development";
43440
44070
  EnvName2["PRODUCTION"] = "production";
44071
+ EnvName2["LOCAL"] = "local";
43441
44072
  return EnvName2;
43442
44073
  })(EnvName || {});
43443
44074
  const SHARED_CONFIG = {
@@ -43446,9 +44077,9 @@ const SHARED_CONFIG = {
43446
44077
  appGooglePlayUrl: "https://play.google.com/store/apps/details?id=com.thefittingroom.marketplace"
43447
44078
  },
43448
44079
  build: {
43449
- version: `${"5.0.11"}`,
43450
- commitHash: `${"77ac921"}`,
43451
- date: `${"2026-02-02T01:20:02.888Z"}`
44080
+ version: `${"5.0.17"}`,
44081
+ commitHash: `${"edbce9b"}`,
44082
+ date: `${"2026-05-10T18:19:05.275Z"}`
43452
44083
  }
43453
44084
  };
43454
44085
  const CONFIGS = {
@@ -43471,6 +44102,9 @@ const CONFIGS = {
43471
44102
  asset: {
43472
44103
  baseUrl: "https://assets.dev.thefittingroom.xyz/shop-sdk/assets/v5"
43473
44104
  },
44105
+ frames: {
44106
+ baseUrl: "https://assets.dev.thefittingroom.xyz"
44107
+ },
43474
44108
  ...SHARED_CONFIG
43475
44109
  },
43476
44110
  [
@@ -43492,6 +44126,33 @@ const CONFIGS = {
43492
44126
  asset: {
43493
44127
  baseUrl: "https://assets.p.thefittingroom.xyz/shop-sdk/assets/v5"
43494
44128
  },
44129
+ frames: {
44130
+ baseUrl: "https://assets.p.thefittingroom.xyz"
44131
+ },
44132
+ ...SHARED_CONFIG
44133
+ },
44134
+ [
44135
+ "local"
44136
+ /* LOCAL */
44137
+ ]: {
44138
+ firebase: {
44139
+ apiKey: "AIzaSyDfjBWzpmzb-mhGN8VSURxzLg6nkzmKUD8",
44140
+ authDomain: "fittingroom-dev-5d248.firebaseapp.com",
44141
+ projectId: "fittingroom-dev-5d248",
44142
+ storageBucket: "fittingroom-dev-5d248.appspot.com",
44143
+ messagingSenderId: "2298664147",
44144
+ appId: "1:2298664147:web:340bda75cd5d25f3997026",
44145
+ measurementId: "G-B7GDQ1Y9LL"
44146
+ },
44147
+ api: {
44148
+ baseUrl: "http://localhost:8080"
44149
+ },
44150
+ asset: {
44151
+ baseUrl: "http://localhost:9000/tfr-assets-dev/shop-sdk/assets/v5"
44152
+ },
44153
+ frames: {
44154
+ baseUrl: "http://localhost:9000/tfr-assets-dev"
44155
+ },
43495
44156
  ...SHARED_CONFIG
43496
44157
  }
43497
44158
  };
@@ -43518,18 +44179,20 @@ async function init(initParams) {
43518
44179
  environment,
43519
44180
  lang = null,
43520
44181
  theme = null,
43521
- debug
44182
+ debug,
44183
+ productLookup,
44184
+ getOverlayTopOffset
43522
44185
  } = initParams;
43523
44186
  if (!brandId || typeof brandId !== "number" || isNaN(brandId) || brandId <= 0) {
43524
44187
  throw new Error(`Invalid brandId "${brandId}"`);
43525
44188
  }
43526
- if (!currentProduct || typeof currentProduct.externalId !== "string") {
44189
+ if (currentProduct !== void 0 && typeof currentProduct.externalId !== "string") {
43527
44190
  throw new Error("Invalid currentProduct");
43528
44191
  }
43529
44192
  if (!Object.values(EnvName).includes(environment)) {
43530
44193
  throw new Error(`Invalid environment "${environment}"`);
43531
44194
  }
43532
- _init$4(debug);
44195
+ _init$8(debug);
43533
44196
  logger2.logDebug("Starting SDK initialization:", {
43534
44197
  initParams
43535
44198
  });
@@ -43539,14 +44202,17 @@ async function init(initParams) {
43539
44202
  }
43540
44203
  _init({
43541
44204
  brandId,
43542
- currentProduct,
44205
+ currentProduct: currentProduct ?? null,
43543
44206
  environment,
43544
- config
44207
+ config,
44208
+ productLookup: productLookup ?? null,
44209
+ getOverlayTopOffset: getOverlayTopOffset ?? null
43545
44210
  });
44211
+ _init$7();
43546
44212
  _init$5();
43547
44213
  _init$6(theme);
43548
44214
  _init$1();
43549
- await _init$3();
44215
+ await _init$4();
43550
44216
  const authManager2 = getAuthManager();
43551
44217
  authManager2.addAuthStateChangeListener((authUser) => {
43552
44218
  useMainStore.getState().setAuthUser(authUser);
@@ -43554,6 +44220,7 @@ async function init(initParams) {
43554
44220
  authManager2.addUserProfileChangeListener((userProfile) => {
43555
44221
  useMainStore.getState().setUserProfile(userProfile);
43556
44222
  });
44223
+ _init$3();
43557
44224
  _init$2();
43558
44225
  {
43559
44226
  const styleEl = document.createElement("style");