@eox/pages-theme-eox 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitlab-ci.yml +8 -0
- package/CHANGELOG.md +10 -0
- package/README.md +54 -0
- package/cypress/support/mocks/helpers.js +46 -0
- package/package.json +4 -1
- package/src/components/PricingTable.vue +10 -7
- package/src/helpers.js +52 -0
- package/src/style.css +9 -0
package/.gitlab-ci.yml
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
image: cypress/base:20.14.0
|
|
2
2
|
|
|
3
3
|
stages:
|
|
4
|
+
- security
|
|
4
5
|
- test
|
|
5
6
|
|
|
6
7
|
cache:
|
|
@@ -13,6 +14,13 @@ cache:
|
|
|
13
14
|
variables:
|
|
14
15
|
npm_config_cache: "$CI_PROJECT_DIR/.npm"
|
|
15
16
|
|
|
17
|
+
security:
|
|
18
|
+
stage: security
|
|
19
|
+
image: node:24-alpine
|
|
20
|
+
script:
|
|
21
|
+
- npm ci
|
|
22
|
+
- npm audit --audit-level=critical
|
|
23
|
+
|
|
16
24
|
format:
|
|
17
25
|
stage: test
|
|
18
26
|
script:
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
# [1.8.0](https://gitlab.eox.at/eox/hub/eoxhub-portal/compare/v1.7.0...v1.8.0) (2026-06-08)
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
- **style:** z-index for sidebar/footer ([6326866](https://gitlab.eox.at/eox/hub/eoxhub-portal/commit/632686679cd4db43a50e8e97b4557eb072409fd1))
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- price formatting helper ([287a28a](https://gitlab.eox.at/eox/hub/eoxhub-portal/commit/287a28abe3c8611d36685bae2efb75973e94d6c1))
|
|
12
|
+
|
|
3
13
|
# [1.7.0](https://gitlab.eox.at/eox/hub/eoxhub-portal/compare/v1.6.0...v1.7.0) (2026-05-11)
|
|
4
14
|
|
|
5
15
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -483,3 +483,57 @@ feedback form can be disabled on individual pages via frontmatter configuration
|
|
|
483
483
|
feedback: false
|
|
484
484
|
---
|
|
485
485
|
```
|
|
486
|
+
|
|
487
|
+
## Helper Functions
|
|
488
|
+
|
|
489
|
+
The theme exports a set of shared utility and helper functions from its helpers module. You can import and use these helpers in your custom VitePress components, checkout pages, or inline script setups:
|
|
490
|
+
|
|
491
|
+
```javascript
|
|
492
|
+
import { formatPrice, trackEvent, t } from "@eox/pages-theme-eox/src/helpers";
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Available Helpers
|
|
496
|
+
|
|
497
|
+
#### `formatPrice(price, lang)`
|
|
498
|
+
|
|
499
|
+
Formats a numeric or string-based price following either British/American style or Continental European style based on the active language code.
|
|
500
|
+
|
|
501
|
+
- **Parameters**:
|
|
502
|
+
- `price` (`number|string`): The numeric price (or numeric string) to format.
|
|
503
|
+
- `lang` (`string`): The active language prefix (defaults to `'en'`).
|
|
504
|
+
- **Returns**: `string` - Formatted price string.
|
|
505
|
+
- **Behavior**:
|
|
506
|
+
- **English (`'en'`)**: Uses commas as thousands separators, dots as decimals, and adds a `.-` suffix for whole numbers (e.g., `1500` formatted to `€ 1,500.-`, `1500.50` formatted to `€ 1,500.50`).
|
|
507
|
+
- **Other languages (e.g., German/Continental)**: Uses dots as thousands separators, commas as decimals, and adds a `,-` suffix for whole numbers (e.g., `1500` formatted to `€ 1.500,-`, `1500.5` formatted to `€ 1.500,50`).
|
|
508
|
+
|
|
509
|
+
##### Example Usage:
|
|
510
|
+
|
|
511
|
+
```javascript
|
|
512
|
+
import { formatPrice } from "@eox/pages-theme-eox/src/helpers";
|
|
513
|
+
|
|
514
|
+
formatPrice(12000, "en"); // "€ 12,000.-"
|
|
515
|
+
formatPrice(49.99, "de"); // "€ 49,99"
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
#### `trackEvent(eventDetails)`
|
|
519
|
+
|
|
520
|
+
Sends a custom tracking event to Matomo Analytics.
|
|
521
|
+
|
|
522
|
+
- **Parameters**:
|
|
523
|
+
- `eventDetails` (`Array<string>`): An array representing the event payload: `[Category, Action, Name?, Value?]`.
|
|
524
|
+
|
|
525
|
+
##### Example Usage:
|
|
526
|
+
|
|
527
|
+
```javascript
|
|
528
|
+
import { trackEvent } from "@eox/pages-theme-eox/src/helpers";
|
|
529
|
+
|
|
530
|
+
trackEvent(["CTA", "Click", "Technical Specifications", "Viewing Ready"]);
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
#### `t(key, i18n)`
|
|
534
|
+
|
|
535
|
+
Retrieves localized translation strings from an i18n mapping object using dot-notation. Falls back to the key itself if no translation is defined.
|
|
536
|
+
|
|
537
|
+
- **Parameters**:
|
|
538
|
+
- `key` (`string`): The lookup key (supports dot notation, e.g., `'legal.privacy'`).
|
|
539
|
+
- `i18n` (`object`): The translation object/table.
|
|
@@ -47,6 +47,50 @@ window.__helpersMock = {
|
|
|
47
47
|
}
|
|
48
48
|
return result ? result : key;
|
|
49
49
|
},
|
|
50
|
+
formatPrice: (price, lang = "en") => {
|
|
51
|
+
if (typeof price === "string") {
|
|
52
|
+
const trimmed = price.trim();
|
|
53
|
+
if (/^\d+(\.\d+)?$/.test(trimmed)) {
|
|
54
|
+
price = Number(trimmed);
|
|
55
|
+
} else {
|
|
56
|
+
return price;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (typeof price === "number" && !isNaN(price)) {
|
|
60
|
+
const isEnglish = !lang || lang.startsWith("en");
|
|
61
|
+
const hasCents = price % 1 !== 0;
|
|
62
|
+
if (isEnglish) {
|
|
63
|
+
if (hasCents) {
|
|
64
|
+
const formatted = new Intl.NumberFormat("en-US", {
|
|
65
|
+
minimumFractionDigits: 2,
|
|
66
|
+
maximumFractionDigits: 2,
|
|
67
|
+
}).format(price);
|
|
68
|
+
return `€ ${formatted}`;
|
|
69
|
+
} else {
|
|
70
|
+
const formatted = new Intl.NumberFormat("en-US", {
|
|
71
|
+
minimumFractionDigits: 0,
|
|
72
|
+
maximumFractionDigits: 0,
|
|
73
|
+
}).format(price);
|
|
74
|
+
return `€ ${formatted}.-`;
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
if (hasCents) {
|
|
78
|
+
const formatted = new Intl.NumberFormat("de-DE", {
|
|
79
|
+
minimumFractionDigits: 2,
|
|
80
|
+
maximumFractionDigits: 2,
|
|
81
|
+
}).format(price);
|
|
82
|
+
return `€ ${formatted}`;
|
|
83
|
+
} else {
|
|
84
|
+
const formatted = new Intl.NumberFormat("de-DE", {
|
|
85
|
+
minimumFractionDigits: 0,
|
|
86
|
+
maximumFractionDigits: 0,
|
|
87
|
+
}).format(price);
|
|
88
|
+
return `€ ${formatted},-`;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return price;
|
|
93
|
+
},
|
|
50
94
|
};
|
|
51
95
|
|
|
52
96
|
export const trackEvent = (...args) => window.__helpersMock.trackEvent(...args);
|
|
@@ -61,3 +105,5 @@ export const isActive = (...args) => window.__helpersMock.isActive(...args);
|
|
|
61
105
|
export const getFlatList = (...args) =>
|
|
62
106
|
window.__helpersMock.getFlatList(...args);
|
|
63
107
|
export const t = (...args) => window.__helpersMock.t(...args);
|
|
108
|
+
export const formatPrice = (...args) =>
|
|
109
|
+
window.__helpersMock.formatPrice(...args);
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eox/pages-theme-eox",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vitepress Theme with EOX branding",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"author": "EOX",
|
|
8
8
|
"license": "MIT",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=24.0.0"
|
|
11
|
+
},
|
|
9
12
|
"scripts": {
|
|
10
13
|
"format": "npx prettier --write .",
|
|
11
14
|
"format:check": "npx prettier --check .",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
{{ t(plan.basePrice, theme.i18n) }}
|
|
98
98
|
</h6>
|
|
99
99
|
<h4 v-else class="primary-text bold" :style="dynamicPriceStyleH4">
|
|
100
|
-
|
|
100
|
+
{{ formatPrice(calculatePrice(plan)) }}
|
|
101
101
|
</h4>
|
|
102
102
|
<span
|
|
103
103
|
v-if="
|
|
@@ -236,7 +236,7 @@
|
|
|
236
236
|
class="primary-text bold"
|
|
237
237
|
:style="dynamicPriceStyleH6"
|
|
238
238
|
>
|
|
239
|
-
+
|
|
239
|
+
+ {{ formatPrice(plan.additionalPrice) }}
|
|
240
240
|
</h6>
|
|
241
241
|
</div>
|
|
242
242
|
<template
|
|
@@ -285,14 +285,14 @@
|
|
|
285
285
|
</template>
|
|
286
286
|
|
|
287
287
|
<script>
|
|
288
|
-
import { t } from "../helpers.js";
|
|
288
|
+
import { t, formatPrice } from "../helpers.js";
|
|
289
289
|
import { useData } from "vitepress";
|
|
290
290
|
|
|
291
291
|
export default {
|
|
292
292
|
name: "PricingTable",
|
|
293
293
|
setup() {
|
|
294
|
-
const { theme } = useData();
|
|
295
|
-
return { theme, t };
|
|
294
|
+
const { theme, lang } = useData();
|
|
295
|
+
return { theme, lang, t };
|
|
296
296
|
},
|
|
297
297
|
props: {
|
|
298
298
|
config: {
|
|
@@ -346,7 +346,7 @@ export default {
|
|
|
346
346
|
if (typeof plan.basePrice === "string") {
|
|
347
347
|
text = this.t(plan.basePrice, this.theme.i18n) || "";
|
|
348
348
|
} else {
|
|
349
|
-
text =
|
|
349
|
+
text = this.formatPrice(this.calculatePrice(plan));
|
|
350
350
|
}
|
|
351
351
|
if (text.length > maxLen) maxLen = text.length;
|
|
352
352
|
});
|
|
@@ -354,7 +354,7 @@ export default {
|
|
|
354
354
|
if (detail.plans) {
|
|
355
355
|
detail.plans.forEach((plan) => {
|
|
356
356
|
if (plan.additionalPrice) {
|
|
357
|
-
const text = `+
|
|
357
|
+
const text = `+ ${this.formatPrice(plan.additionalPrice)}`;
|
|
358
358
|
if (text.length > maxLen) maxLen = text.length;
|
|
359
359
|
}
|
|
360
360
|
});
|
|
@@ -433,6 +433,9 @@ export default {
|
|
|
433
433
|
}
|
|
434
434
|
return basePrice + additionalOptionPrice + additionalPlanModifier;
|
|
435
435
|
},
|
|
436
|
+
formatPrice(price) {
|
|
437
|
+
return formatPrice(price, this.lang);
|
|
438
|
+
},
|
|
436
439
|
getRowKeys(plansArray) {
|
|
437
440
|
const keys = new Set();
|
|
438
441
|
plansArray.forEach((plan) => {
|
package/src/helpers.js
CHANGED
|
@@ -198,3 +198,55 @@ export const t = (key, i18n) => {
|
|
|
198
198
|
}
|
|
199
199
|
return typeof obj === "string" ? obj : key;
|
|
200
200
|
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Format price according to localized English (American/British) or European (German) styles.
|
|
204
|
+
*
|
|
205
|
+
* @param {number|string} price - Price to format
|
|
206
|
+
* @param {string} lang - Language code
|
|
207
|
+
* @returns {string} Formatted price with correct separators and suffixes
|
|
208
|
+
*/
|
|
209
|
+
export const formatPrice = (price, lang = "en") => {
|
|
210
|
+
if (typeof price === "string") {
|
|
211
|
+
const trimmed = price.trim();
|
|
212
|
+
if (/^\d+(\.\d+)?$/.test(trimmed)) {
|
|
213
|
+
price = Number(trimmed);
|
|
214
|
+
} else {
|
|
215
|
+
return price;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (typeof price === "number" && !isNaN(price)) {
|
|
219
|
+
const isEnglish = !lang || lang.startsWith("en");
|
|
220
|
+
const hasCents = price % 1 !== 0;
|
|
221
|
+
if (isEnglish) {
|
|
222
|
+
if (hasCents) {
|
|
223
|
+
const formatted = new Intl.NumberFormat("en-US", {
|
|
224
|
+
minimumFractionDigits: 2,
|
|
225
|
+
maximumFractionDigits: 2,
|
|
226
|
+
}).format(price);
|
|
227
|
+
return `€ ${formatted}`;
|
|
228
|
+
} else {
|
|
229
|
+
const formatted = new Intl.NumberFormat("en-US", {
|
|
230
|
+
minimumFractionDigits: 0,
|
|
231
|
+
maximumFractionDigits: 0,
|
|
232
|
+
}).format(price);
|
|
233
|
+
return `€ ${formatted}.-`;
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
if (hasCents) {
|
|
237
|
+
const formatted = new Intl.NumberFormat("de-DE", {
|
|
238
|
+
minimumFractionDigits: 2,
|
|
239
|
+
maximumFractionDigits: 2,
|
|
240
|
+
}).format(price);
|
|
241
|
+
return `€ ${formatted}`;
|
|
242
|
+
} else {
|
|
243
|
+
const formatted = new Intl.NumberFormat("de-DE", {
|
|
244
|
+
minimumFractionDigits: 0,
|
|
245
|
+
maximumFractionDigits: 0,
|
|
246
|
+
}).format(price);
|
|
247
|
+
return `€ ${formatted},-`;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return price;
|
|
252
|
+
};
|
package/src/style.css
CHANGED
|
@@ -336,3 +336,12 @@ details[open] > summary > i.mdi-chevron-down {
|
|
|
336
336
|
eox-feedback-button {
|
|
337
337
|
z-index: calc(var(--vp-z-index-footer) + 1);
|
|
338
338
|
}
|
|
339
|
+
|
|
340
|
+
@media (min-width: 960px) {
|
|
341
|
+
footer {
|
|
342
|
+
z-index: calc(var(--vp-z-index-sidebar) + 1);
|
|
343
|
+
}
|
|
344
|
+
eox-feedback-button {
|
|
345
|
+
z-index: calc(var(--vp-z-index-sidebar) + 2);
|
|
346
|
+
}
|
|
347
|
+
}
|