gatsby-core-theme 44.15.0 → 44.15.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,39 @@
1
+ ## [44.15.2](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.15.1...v44.15.2) (2026-02-26)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * add jackpot on pick keys for games ([ad29abd](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/ad29abdb66c06d33d4ea0c2dc8a2d1dcc24c592e))
7
+ * block crawling for develop environment ([ca986f6](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/ca986f649be3f90dac3b4bdc181415e5aa05bf56))
8
+ * clean data for pages that contain archive module ([3a2aaf0](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/3a2aaf005042d1a1c1649fa286b86fc1fc5a6a86))
9
+ * update isMeutimao constant ([b888337](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/b888337b2530e90e5a12eb1e8feccf26115c4164))
10
+ * update logic on gatsby node to have all the operators ([39f07ed](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/39f07edacac4c57b813d689b144a17ebba9a5c64))
11
+ * update module introduction to accept short codes ([acdf3df](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/acdf3dfa9325c63a230a92a9f9037b8d1adde68f))
12
+ * update modules function ([5851809](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/58518099f23e1774da43de91c38e8f384b47d562))
13
+ * update name ([a36746a](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/a36746a5f06d49217bef0bf3900ac461ded8d640))
14
+ * update regex and naming ([f0f08b5](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/f0f08b5e80b94865d2fdfb717212cc1f596c55a1))
15
+
16
+
17
+ * Merge branch 'en-221-update-logic-modules' into 'master' ([b888646](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/b88864634144e41667d8da50bf9d3bc964114e07))
18
+ * Merge branch 'en-330-jackpot-data' into 'master' ([dff9e12](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/dff9e12a7242c7ef1768109ba55ab81f7f62903a))
19
+ * Merge branch 'EN-371' into 'master' ([537296e](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/537296e0ff9db0aa02d3c75575e45d6056d86474))
20
+ * Merge branch 'EN-359' into 'master' ([0f6f3e3](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/0f6f3e307e1f5c007b73be2b4eb8f8aedfa3f6a7))
21
+ * Merge branch 'en-221-update-logic' into 'master' ([7641052](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/7641052265fbf88550073ff0b4300c60e03592ee))
22
+ * Merge branch 'en-372-short-codes-module-intro' into 'master' ([bac2830](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/bac283079b3bf2e99d8e49e373bbb5e706ec7860))
23
+
24
+ ## [44.15.1](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.15.0...v44.15.1) (2026-02-19)
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * add validation for api ([75a44d6](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/75a44d6aacc3a4d7702773d040edcc23b271d298))
30
+ * imporve test ([a2ef17b](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/a2ef17b491f4130a08769fd2f5982d2825743234))
31
+ * remove unnacessary use effects block, remove unnacessary function from dependency array ([fbda456](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/fbda456a83e61d044a722ba031f61f7f532df763))
32
+
33
+
34
+ * Merge branch 'EN/fix-scroll-behaviour' into 'master' ([1065b8e](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/1065b8e457790256826258d4b520b4340fa14600))
35
+ * Merge branch 'em-367-api-handle' into 'master' ([e6289c8](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/commit/e6289c858230ebc9c5efea9f9c67189d7d396723))
36
+
1
37
  # [44.15.0](https://gitlab.com/g2m-gentoo/team-floyd/themes/gatsby-themes/compare/v44.14.0...v44.15.0) (2026-02-11)
2
38
 
3
39
 
package/gatsby-node.mjs CHANGED
@@ -76,6 +76,21 @@ export const createPages = async (
76
76
  preconnectLinks = themeOptions.preconnectLinks || [];
77
77
  console.log(chalk.magenta("info") + chalk.whiteBright(" starting processor"));
78
78
 
79
+ const includeAllOperators = process.env.ENABLE_SHORT_CODES_OPERATOR;
80
+ const operatorsByShortName = includeAllOperators
81
+ ? Object.values(operatorData || {}).reduce((acc, operator) => {
82
+ if (operator?.short_name) {
83
+ acc[operator.short_name.toLowerCase()] = {
84
+ min_deposit: operator.min_deposit,
85
+ max_deposit: operator.max_deposit,
86
+ min_withdrawal: operator.min_withdrawal,
87
+ max_withdrawal: operator.max_withdrawal,
88
+ };
89
+ }
90
+ return acc;
91
+ }, {})
92
+ : {};
93
+
79
94
  // add data to modules
80
95
  const processed = processor.run(
81
96
  {
@@ -87,6 +102,7 @@ export const createPages = async (
87
102
  toplists: toplistData,
88
103
  content: contentData,
89
104
  ribbons: ribbonsData,
105
+ ...(includeAllOperators && { operators: operatorsByShortName }),
90
106
  relations: {
91
107
  operator: operatorData,
92
108
  payment_method: paymentData,
@@ -102,7 +118,7 @@ export const createPages = async (
102
118
  },
103
119
  themeOptions,
104
120
  fs,
105
- translationsData
121
+ translationsData,
106
122
  );
107
123
  pages = processed.pages;
108
124
 
@@ -112,12 +128,14 @@ export const createPages = async (
112
128
  htmlSitemapPages = processSitemapPages(pages, processed.site_markets);
113
129
 
114
130
  // create robots.txt file
115
- const robotsTxtContent = siteGeneralData.robots_txt
116
- ? siteGeneralData.robots_txt
117
- : "";
131
+ // block all crawlers to prevent indexing for non-production environments
132
+ const isProduction = process.env.GATSBY_ACTIVE_ENV === "production";
133
+ const robotsTxtContent = isProduction
134
+ ? (siteGeneralData.robots_txt || "")
135
+ : "User-agent: *\nDisallow: /\n";
118
136
  const streamRobotsTxt = fs.createWriteStream("./static/robots.txt");
119
137
  console.log(
120
- chalk.magenta("info") + chalk.whiteBright(" creating robots.txt file")
138
+ chalk.magenta("info") + chalk.whiteBright(` creating robots.txt file (${isProduction ? "production" : "blocking"})`)
121
139
  );
122
140
  streamRobotsTxt.write(robotsTxtContent);
123
141
  streamRobotsTxt.end();
@@ -140,7 +158,7 @@ export const createPages = async (
140
158
 
141
159
  siteSchema = schemaData[page["market_id"]];
142
160
  authors = siteSettingsData.authors;
143
-
161
+
144
162
 
145
163
  page.siteInfo = siteGeneralData;
146
164
  page.siteSchema = siteSchema;
@@ -158,6 +176,26 @@ export const createPages = async (
158
176
  __dirname
159
177
  );
160
178
 
179
+ const template = templatesData[page.template_id];
180
+ const autogenerated = template && template.autogenerated_content;
181
+
182
+ archivePages.forEach((archivePage) => {
183
+ archivePage.context = clean({
184
+ ...archivePage.context,
185
+ marketSections: removeUnwantedSections(
186
+ marketSection,
187
+ page.type,
188
+ ribbonsData
189
+ ),
190
+ authors,
191
+ autogenerated,
192
+ siteSchema,
193
+ comments: commentsData?.[page?.id],
194
+ lang: page.language,
195
+ ...themeOptions,
196
+ });
197
+ });
198
+
161
199
  pagesToCreate.push(...archivePages);
162
200
  return;
163
201
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gatsby-core-theme",
3
- "version": "44.15.0",
3
+ "version": "44.15.2",
4
4
  "description": "Gatsby Theme NPM Package",
5
5
  "author": "",
6
6
  "license": "ISC",
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable import/no-named-as-default */
2
- import React, { useRef, useContext } from "react";
2
+ import React, { useRef, useContext, useEffect } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { Context } from "~context/MainProvider.js";
5
5
  import Search from "~organisms/search";
@@ -59,7 +59,9 @@ const Navigation = ({
59
59
  <img alt={useTranslate('nav_site_logo', logoAlt)} src={logo} width={logoWidth} height={logoHeight} />
60
60
  );
61
61
 
62
- if (stopScrollOnOpen) toggleScroll("", true);
62
+ useEffect(() => {
63
+ if (stopScrollOnOpen) toggleScroll("", true);
64
+ }, [stopScrollOnOpen]);
63
65
 
64
66
  const activeMarket =
65
67
  pageContext?.allMarkets && pageContext?.allMarkets[pageContext.page.market];
@@ -1,7 +1,8 @@
1
1
  import { months } from "./common.mjs";
2
2
 
3
3
  const generatorsPlaceholderRegex =
4
- /\[MONTH\]|\[YEAR\]|\[currentyear\]|\[sitename\]|\[currentmonth\]|\[title\]|\[currentdate\]|\[operator_name]|\[author_name]|\[link\]/gi;
4
+ /\[[a-z0-9_]+\.(min_deposit|max_deposit|min_withdrawal|max_withdrawal)\]|\[MONTH\]|\[YEAR\]|\[currentyear\]|\[sitename\]|\[currentmonth\]|\[title\]|\[currentdate\]|\[operator_name\]|\[author_name\]|\[link\]/gi;
5
+
5
6
 
6
7
  const generatorsPlaceholderReplaceObject = (
7
8
  data,
@@ -116,6 +116,7 @@ export const pickRelationKeys = {
116
116
  "withdrawal_method",
117
117
  "support_email",
118
118
  "min_deposit",
119
+ "max_deposit",
119
120
  "max_withdrawal",
120
121
  "min_withdrawal",
121
122
  "license_objects",
@@ -179,7 +180,9 @@ export const pickRelationKeys = {
179
180
  "extra_fields",
180
181
  "game_categories",
181
182
  'maximum_bet',
182
- "game_themes"
183
+ "game_themes",
184
+ 'jackpot'
185
+
183
186
  ],
184
187
  software_provider: [
185
188
  "logo_filename_object",
@@ -39,24 +39,40 @@ export function generateTrackerLink(operator, trackerType, provider = false) {
39
39
  }
40
40
  }
41
41
 
42
- export function generatePlaceholderString(string = "", translations, data) {
42
+ export function generatePlaceholderString(
43
+ string = "",
44
+ translations,
45
+ data,
46
+ operators,
47
+ ) {
48
+
49
+
43
50
  const date = new Date();
44
51
  const day = String(date.getDate()).padStart(2, "0");
45
52
  const month = String(date.getMonth() + 1).padStart(2, "0");
46
53
  const year = date.getFullYear();
47
54
  const regex = generatorsConstant.generatorsPlaceholderRegex;
48
55
 
49
- return string.replace(
50
- regex,
51
- (match) =>
56
+ return string.replace(regex, (match) => {
57
+ const key = match.toLowerCase();
58
+ if (key.includes(".")) {
59
+ const [operatorKey, field] = key
60
+ .slice(1, -1)
61
+ .split(".");
62
+ const value = operators?.[operatorKey]?.[field];
63
+ return value ?? "";
64
+ }
65
+
66
+ return (
52
67
  generatorsConstant.generatorsPlaceholderReplaceObject(
53
68
  data,
54
69
  translations,
55
70
  month,
56
71
  day,
57
- year
58
- )[match.toLowerCase()]
59
- );
72
+ year,
73
+ )[key] ?? ""
74
+ );
75
+ });
60
76
  }
61
77
 
62
78
  export function generateArray(obj) {
@@ -169,9 +169,26 @@ export async function getAffiliateLink(operator, path, page, headers, url) {
169
169
  headers: headers ? filterCfKeys(Object.fromEntries(headers)) : {},
170
170
  });
171
171
 
172
- // Parse the response
173
- const data = res.ok ? await res.json() : { success: false };
174
- return { props: data };
172
+ // Check if response is ok and content-type is JSON
173
+ if (!res.ok) {
174
+ console.error(`Tracking API error: ${res.status} ${res.statusText}`);
175
+ return { props: { success: false } };
176
+ }
177
+
178
+ const contentType = res.headers.get("content-type");
179
+ if (!contentType || !contentType.includes("application/json")) {
180
+ console.error(`Tracking API returned non-JSON content-type: ${contentType}`);
181
+ return { props: { success: false } };
182
+ }
183
+
184
+ // Try to parse JSON safely
185
+ try {
186
+ const data = await res.json();
187
+ return { props: data };
188
+ } catch (jsonError) {
189
+ console.error("Failed to parse tracking API response as JSON:", jsonError);
190
+ return { props: { success: false } };
191
+ }
175
192
  } catch (error) {
176
193
  console.error("Error fetching affiliate link:", error);
177
194
  return { props: { success: false } };
@@ -96,9 +96,14 @@ describe("Tracker Helper", () => {
96
96
 
97
97
  global.fetch = jest.fn(() =>
98
98
  Promise.resolve({
99
+ ok: true,
100
+ headers: {
101
+ get: (key) => (key === "content-type" ? "application/json" : null),
102
+ },
99
103
  json: () => Promise.resolve({ success: true }),
100
104
  })
101
105
  );
106
+
102
107
  beforeEach(() => {
103
108
  fetch.mockClear();
104
109
  });
@@ -153,6 +158,162 @@ describe("Tracker Helper", () => {
153
158
  );
154
159
  });
155
160
 
161
+ test("getAffiliateLink handles successful response", async () => {
162
+ fetch.mockImplementationOnce(() =>
163
+ Promise.resolve({
164
+ ok: true,
165
+ headers: {
166
+ get: (key) => (key === "content-type" ? "application/json" : null),
167
+ },
168
+ json: () => Promise.resolve({ success: true, result: { link: "https://example.com" } }),
169
+ })
170
+ );
171
+
172
+ const result = await Tracker.getAffiliateLink(
173
+ operator,
174
+ "/no/visit/rizk",
175
+ { template: "default" },
176
+ null
177
+ );
178
+
179
+ expect(result).toEqual({ props: { success: true, result: { link: "https://example.com" } } });
180
+ });
181
+
182
+ test("getAffiliateLink handles failed HTTP response", async () => {
183
+ const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
184
+
185
+ fetch.mockImplementationOnce(() =>
186
+ Promise.resolve({
187
+ ok: false,
188
+ status: 500,
189
+ statusText: "Internal Server Error",
190
+ headers: {
191
+ get: () => null,
192
+ },
193
+ })
194
+ );
195
+
196
+ const result = await Tracker.getAffiliateLink(
197
+ operator,
198
+ "/no/visit/rizk",
199
+ { template: "default" },
200
+ null
201
+ );
202
+
203
+ expect(result).toEqual({ props: { success: false } });
204
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
205
+ "Tracking API error: 500 Internal Server Error"
206
+ );
207
+
208
+ consoleErrorSpy.mockRestore();
209
+ });
210
+
211
+ test("getAffiliateLink handles non-JSON content-type (HTML error page)", async () => {
212
+ const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
213
+
214
+ fetch.mockImplementationOnce(() =>
215
+ Promise.resolve({
216
+ ok: true,
217
+ headers: {
218
+ get: (key) => (key === "content-type" ? "text/html" : null),
219
+ },
220
+ })
221
+ );
222
+
223
+ const result = await Tracker.getAffiliateLink(
224
+ operator,
225
+ "/no/visit/rizk",
226
+ { template: "default" },
227
+ null
228
+ );
229
+
230
+ expect(result).toEqual({ props: { success: false } });
231
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
232
+ "Tracking API returned non-JSON content-type: text/html"
233
+ );
234
+
235
+ consoleErrorSpy.mockRestore();
236
+ });
237
+
238
+ test("getAffiliateLink handles missing content-type", async () => {
239
+ const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
240
+
241
+ fetch.mockImplementationOnce(() =>
242
+ Promise.resolve({
243
+ ok: true,
244
+ headers: {
245
+ get: () => null,
246
+ },
247
+ })
248
+ );
249
+
250
+ const result = await Tracker.getAffiliateLink(
251
+ operator,
252
+ "/no/visit/rizk",
253
+ { template: "default" },
254
+ null
255
+ );
256
+
257
+ expect(result).toEqual({ props: { success: false } });
258
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
259
+ "Tracking API returned non-JSON content-type: null"
260
+ );
261
+
262
+ consoleErrorSpy.mockRestore();
263
+ });
264
+
265
+ test("getAffiliateLink handles JSON parsing error", async () => {
266
+ const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
267
+
268
+ fetch.mockImplementationOnce(() =>
269
+ Promise.resolve({
270
+ ok: true,
271
+ headers: {
272
+ get: (key) => (key === "content-type" ? "application/json" : null),
273
+ },
274
+ json: () => Promise.reject(new Error("Unexpected token < in JSON at position 0")),
275
+ })
276
+ );
277
+
278
+ const result = await Tracker.getAffiliateLink(
279
+ operator,
280
+ "/no/visit/rizk",
281
+ { template: "default" },
282
+ null
283
+ );
284
+
285
+ expect(result).toEqual({ props: { success: false } });
286
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
287
+ "Failed to parse tracking API response as JSON:",
288
+ expect.any(Error)
289
+ );
290
+
291
+ consoleErrorSpy.mockRestore();
292
+ });
293
+
294
+ test("getAffiliateLink handles network error", async () => {
295
+ const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
296
+
297
+ fetch.mockImplementationOnce(() =>
298
+ Promise.reject(new Error("Network error"))
299
+ );
300
+
301
+ const result = await Tracker.getAffiliateLink(
302
+ operator,
303
+ "/no/visit/rizk",
304
+ { template: "default" },
305
+ null
306
+ );
307
+
308
+ expect(result).toEqual({ props: { success: false } });
309
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
310
+ "Error fetching affiliate link:",
311
+ expect.any(Error)
312
+ );
313
+
314
+ consoleErrorSpy.mockRestore();
315
+ });
316
+
156
317
  test("getTrackingAPIParams tests", async () => {
157
318
  const params = Tracker.getTrackingAPIParams(
158
319
  "temlateValue",
@@ -6,62 +6,47 @@ import PropTypes from "prop-types";
6
6
  import { ModalContext } from "./modalContext";
7
7
  import styles from "./modal.module.scss";
8
8
  import useTranslate from "~hooks/useTranslate/useTranslate";
9
+ import { toggleScroll } from "~helpers/scroll";
9
10
 
10
11
  const ModalContent = ({ closeIcon, customCss, children }) => {
11
12
  const { showModal, setShowModal } = useContext(ModalContext);
12
13
  const modal = useRef(null);
13
14
 
14
- // eslint-disable-next-line react-hooks/exhaustive-deps
15
- const close = () => {
16
- document.body.style.overflow = "auto";
17
- setShowModal(false);
18
- };
19
-
20
15
  useEffect(() => {
21
- if (showModal) {
22
- modal.current.classList.toggle(styles.activeModal);
23
- } else {
24
- modal.current.classList.remove(styles.activeModal);
25
- }
26
- }, [showModal]);
16
+ if (!showModal) return;
17
+
18
+ toggleScroll("modal");
27
19
 
28
- // Close on esc
29
- useEffect(() => {
30
20
  const handleEsc = (event) => {
31
21
  if (event.keyCode === 27) {
32
- close();
22
+ setShowModal(false);
33
23
  }
34
24
  };
35
- if (typeof window !== "undefined")
36
- window.addEventListener("keydown", handleEsc);
37
- return () => {
38
- if (typeof window !== "undefined")
39
- window.removeEventListener("keydown", handleEsc);
40
- };
41
- }, [close]);
42
25
 
43
- // Close on outside click
44
- useEffect(() => {
45
26
  const handleOutsideClick = (event) => {
46
27
  if (
47
28
  modal.current &&
48
29
  !modal.current.contains(event.target) &&
49
- !modal.current.previousElementSibling.contains(event.target)
30
+ !modal.current.previousElementSibling?.contains(event.target)
50
31
  ) {
51
- close();
32
+ setShowModal(false);
52
33
  }
53
34
  };
54
- if (typeof window !== "undefined")
55
- window.addEventListener("mousedown", handleOutsideClick);
35
+
36
+ window.addEventListener("keydown", handleEsc);
37
+ window.addEventListener("mousedown", handleOutsideClick);
38
+
56
39
  return () => {
57
- if (typeof window !== "undefined")
58
- window.removeEventListener("mousedown", handleOutsideClick);
40
+ window.removeEventListener("keydown", handleEsc);
41
+ window.removeEventListener("mousedown", handleOutsideClick);
42
+ toggleScroll("modal");
59
43
  };
60
- }, [close]);
44
+ }, [showModal, setShowModal]);
45
+
61
46
  const closeIconAriaLabel = useTranslate("ariaLabel-closeIcon", "Close Icon");
62
47
  return (
63
- <div className={styles.modalInner || ""} ref={modal}>
64
- <div className={styles.modalOverlay || ""} onClick={close} />
48
+ <div className={`${styles.modalInner || ""} ${showModal ? styles.activeModal || "" : ""}`} ref={modal}>
49
+ <div className={styles.modalOverlay || ""} onClick={() => setShowModal(false)} />
65
50
  <div
66
51
  className={`${styles.modalContent || ""} ${
67
52
  customCss ? customCss || "" : ""
@@ -72,7 +57,7 @@ const ModalContent = ({ closeIcon, customCss, children }) => {
72
57
  className={`${styles.closeIcon || ""} modal-gtm btn-cta`}
73
58
  aria-label={closeIconAriaLabel}
74
59
  type="button"
75
- onClick={close}
60
+ onClick={() => setShowModal(false)}
76
61
  >
77
62
  {closeIcon}
78
63
  </button>
@@ -150,7 +150,8 @@ export function processSections(
150
150
  data,
151
151
  toplists,
152
152
  content,
153
- previewPageID
153
+ previewPageID,
154
+ operators = {}
154
155
  ) {
155
156
  // pageId we will use it just on operator review pages
156
157
  const pageId = page ? page.id : null;
@@ -225,7 +226,8 @@ export function processSections(
225
226
  toplists,
226
227
  content,
227
228
  prefilledMarketModules,
228
- previewPageID
229
+ previewPageID,
230
+ operators
229
231
  );
230
232
  sections[sectionKey].modules[key] = module;
231
233
  if (module?.filters) {
@@ -334,13 +336,15 @@ export function processExtraFields(
334
336
  });
335
337
  }
336
338
 
337
- function updatePlaceholders(page, data, translationsData) {
339
+ function updatePlaceholders(page, data, translationsData, operators = {}) {
340
+
338
341
  page.title =
339
342
  page.title &&
340
343
  generatePlaceholderString(page.title, translationsData, {
341
344
  siteName: data.site_name,
342
345
  pageTitle: page.title,
343
346
  market: page.market,
347
+ operators
344
348
  });
345
349
 
346
350
  page.meta_title =
@@ -357,6 +361,7 @@ function updatePlaceholders(page, data, translationsData) {
357
361
  siteName: data.site_name,
358
362
  pageTitle: page.title,
359
363
  market: page.market,
364
+ operators
360
365
  });
361
366
  }
362
367
 
@@ -425,7 +430,7 @@ export default {
425
430
  relations,
426
431
  data.content
427
432
  );
428
- updatePlaceholders(page, generalData, translations);
433
+ updatePlaceholders(page, generalData, translations, data.operators);
429
434
 
430
435
  // Set Affiliate relations
431
436
  processRelations(
@@ -477,7 +482,8 @@ export default {
477
482
  siteName: generalData.site_name,
478
483
  pageTitle: page.title,
479
484
  market: page.market,
480
- }
485
+ },
486
+ data.operators
481
487
  );
482
488
  }
483
489
 
@@ -651,7 +657,8 @@ export default {
651
657
  null,
652
658
  data.toplists,
653
659
  data.content,
654
- previewPageID
660
+ previewPageID,
661
+ data.operators || {}
655
662
  );
656
663
  }
657
664
 
@@ -676,7 +683,8 @@ export default {
676
683
  data,
677
684
  data.toplists,
678
685
  data.content,
679
- previewPageID
686
+ previewPageID,
687
+ data.operators || {}
680
688
  ),
681
689
  });
682
690
  }
@@ -515,7 +515,8 @@ export function processContentModule(
515
515
  translations,
516
516
  relationData,
517
517
  content,
518
- previewPageID
518
+ previewPageID,
519
+ operators,
519
520
  ) {
520
521
  module.value = previewPageID
521
522
  ? module.value
@@ -524,7 +525,8 @@ export function processContentModule(
524
525
  module.value = generatePlaceholderString(
525
526
  module.value,
526
527
  translations,
527
- relationData
528
+ relationData,
529
+ operators
528
530
  );
529
531
 
530
532
  module.value = trailingSlash(module.value);
@@ -536,7 +538,17 @@ export function processContentModule(
536
538
  module.show_more_content = generatePlaceholderString(
537
539
  module.show_more_content,
538
540
  translations,
539
- relationData
541
+ relationData,
542
+ operators
543
+ );
544
+ }
545
+
546
+ if (module.module_introduction && Object.keys(operators).length) {
547
+ module.module_introduction = generatePlaceholderString(
548
+ module.module_introduction,
549
+ translations,
550
+ relationData,
551
+ operators,
540
552
  );
541
553
  }
542
554
  }
@@ -552,14 +564,14 @@ export function shouldSavePrefilled(module = {}, siteName) {
552
564
  );
553
565
  }
554
566
 
555
- export function processSpotlightModule(module = {}, content, previewPageID) {
567
+ export function processSpotlightModule(module = {}, content, previewPageID, translations, relationData, operators) {
556
568
  module.items.forEach((item) => {
557
569
  item.content = trailingSlash(
558
570
  previewPageID ? item.content : (content && content[item.content]) || ""
559
571
  );
560
- item.text = trailingSlash(
561
- previewPageID ? item.text : (content && content[item.text]) || ""
562
- );
572
+ item.text = generatePlaceholderString(trailingSlash(
573
+ previewPageID ? item.text : (content && content[item.text]) || "",
574
+ ), translations, relationData, operators);
563
575
  });
564
576
 
565
577
  return module;
@@ -572,30 +584,32 @@ export function processCarouselModule(module = {}, content) {
572
584
  return module;
573
585
  }
574
586
 
575
- export function processFaq(module = {}, content, relationData, previewPageID) {
587
+ export function processFaq(module = {}, content, relationData, previewPageID, operators) {
576
588
  // eslint-disable-next-line no-unused-expressions
577
589
  module.items &&
578
590
  // eslint-disable-next-line array-callback-return
579
591
  module.items.map((item) => {
580
592
  item.question = trailingSlash(
581
593
  previewPageID
582
- ? generatePlaceholderString(item.question, null, relationData)
594
+ ? generatePlaceholderString(item.question, null, relationData, operators)
583
595
  : (content
584
596
  ? generatePlaceholderString(
585
597
  content[item.question],
586
598
  null,
587
- relationData
599
+ relationData,
600
+ operators,
588
601
  )
589
602
  : "") || ""
590
603
  );
591
604
  item.answer = trailingSlash(
592
605
  previewPageID
593
- ? generatePlaceholderString(item.answer, null, relationData)
606
+ ? generatePlaceholderString(item.answer, null, relationData, operators)
594
607
  : (content &&
595
608
  generatePlaceholderString(
596
609
  content[item.answer],
597
610
  null,
598
- relationData
611
+ relationData,
612
+ operators
599
613
  )) ||
600
614
  ""
601
615
  );
@@ -632,19 +646,29 @@ export function processModule(
632
646
  toplists,
633
647
  content,
634
648
  prefilledMarketModules,
635
- previewPageID
649
+ previewPageID,
650
+ operators
636
651
  ) {
637
652
  module.module_title =
638
653
  module.module_title &&
639
- generatePlaceholderString(module.module_title, translations, relationData);
654
+ generatePlaceholderString(module.module_title, translations, relationData, operators);
640
655
  module.title =
641
656
  module.title &&
642
- generatePlaceholderString(module.title, translations, relationData);
657
+ generatePlaceholderString(module.title, translations, relationData, operators);
643
658
 
644
659
  module.anchor_slug =
645
660
  module.anchor_slug &&
646
661
  generatePlaceholderString(module.anchor_slug, translations, relationData);
647
662
 
663
+ module.module_introduction =
664
+ module.module_introduction &&
665
+ generatePlaceholderString(
666
+ module.module_introduction,
667
+ translations,
668
+ relationData,
669
+ operators,
670
+ );
671
+
648
672
  // See more link
649
673
  if (
650
674
  module.see_more_link?.value &&
@@ -671,7 +695,8 @@ export function processModule(
671
695
  translations,
672
696
  relationData,
673
697
  content,
674
- previewPageID
698
+ previewPageID,
699
+ operators,
675
700
  );
676
701
  } else if (module.name === "bonus") {
677
702
  processBonus(module, relations, data);
@@ -686,11 +711,11 @@ export function processModule(
686
711
  translations
687
712
  );
688
713
  } else if (module.name === "faq") {
689
- processFaq(module, content, relationData, previewPageID);
714
+ processFaq(module, content, relationData, previewPageID, operators);
690
715
  } else if (module.name === "anchor") {
691
716
  processAnchor(module, relationData, translations);
692
717
  } else if (module.name === "spotlights") {
693
- processSpotlightModule(module, content, previewPageID);
718
+ processSpotlightModule(module, content, previewPageID, translations, relationData, operators);
694
719
  } else if (module.name === "menu" && menus && menus[module.menu_id]) {
695
720
  module = Object.assign(module, menus[module.menu_id]);
696
721
  } else if (module.name === "statistics_counter") {