@eox/pages-theme-eox 1.2.1 → 1.4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ # [1.4.0](https://gitlab.eox.at/eox/hub/eoxhub-portal/compare/v1.3.0...v1.4.0) (2026-04-23)
4
+
5
+ ### Features
6
+
7
+ - support strings for baseprice ([1fd6df2](https://gitlab.eox.at/eox/hub/eoxhub-portal/commit/1fd6df2a226ff79824e0a570312030f9747eacd0))
8
+
9
+ # [1.3.0](https://gitlab.eox.at/eox/hub/eoxhub-portal/compare/v1.2.2...v1.3.0) (2026-04-14)
10
+
11
+ ### Features
12
+
13
+ - integration of feedback form ([a5d384f](https://gitlab.eox.at/eox/hub/eoxhub-portal/commit/a5d384ff972b08562bedc368f42f0598e7656601))
14
+
15
+ ## [1.2.2](https://gitlab.eox.at/eox/hub/eoxhub-portal/compare/v1.2.1...v1.2.2) (2026-04-14)
16
+
17
+ ### Bug Fixes
18
+
19
+ - handle t&c array of objects ([27a56c7](https://gitlab.eox.at/eox/hub/eoxhub-portal/commit/27a56c7bcba6e00f13c26cd3eb24ec8af6599983))
20
+
3
21
  ## [1.2.1](https://gitlab.eox.at/eox/hub/eoxhub-portal/compare/v1.2.0...v1.2.1) (2026-03-23)
4
22
 
5
23
  ### Bug Fixes
package/README.md CHANGED
@@ -370,3 +370,108 @@ An image-overlay card where a background image fills the card and text is render
370
370
  ]"
371
371
  />
372
372
  ```
373
+
374
+ #### Feedback Form
375
+
376
+ **Feedback Form** for collecting user feedback can be enabled by configuring the theme:
377
+
378
+ ```javascript
379
+ // themeConfig
380
+ {
381
+ feedback: {
382
+ endpoint: "https://example.com/create-issue?repo=github.com/org/repo" // or repo can be passed in request body by being included in schema such as a hidden field with default value
383
+ schema: {
384
+ "type": "object",
385
+ "title": "Feedback contains URL of current page automatically. Let us know your feedback or issue type.",
386
+ "options": {
387
+ "remove_empty_properties": true,
388
+ "remove_additional_properties": true
389
+ },
390
+ "oneOf": [
391
+ {
392
+ "title": "Feature Request",
393
+ "properties": {
394
+ "type_feature": {
395
+ "type": "string",
396
+ "default": "feature",
397
+ "options": {
398
+ "hidden": true
399
+ }
400
+ },
401
+ "title": {
402
+ "type": "string",
403
+ "title": "Title"
404
+ },
405
+ "Expected behavior": {
406
+ "type": "string",
407
+ "format": "textarea",
408
+ "minLength": 1,
409
+ "options": {
410
+ "inputAttributes": {
411
+ "placeholder": "Describe the new functionality and how it would help you."
412
+ }
413
+ }
414
+ },
415
+ "Contact information or additional comments (optional)": {
416
+ "type": "string"
417
+ }
418
+ },
419
+ "required": [
420
+ "type_feature"
421
+ ],
422
+ "additionalProperties": false
423
+ },
424
+ {
425
+ "title": "Issue or Question",
426
+ "properties": {
427
+ "type_catalog": {
428
+ "type": "string",
429
+ "default": "catalog",
430
+ "options": {
431
+ "hidden": true
432
+ }
433
+ },
434
+ "title": {
435
+ "type": "string",
436
+ "title": "Title"
437
+ },
438
+ "Steps to reproduce": {
439
+ "type": "string",
440
+ "format": "textarea",
441
+ "minLength": 1,
442
+ "options": {
443
+ "inputAttributes": {
444
+ "placeholder": "Describe what you tried to do."
445
+ }
446
+ }
447
+ },
448
+ "Expected content": {
449
+ "type": "string",
450
+ "format": "textarea",
451
+ "minLength": 1,
452
+ "options": {
453
+ "inputAttributes": {
454
+ "placeholder": "What would you like to see?"
455
+ }
456
+ }
457
+ }
458
+ },
459
+ "required": [
460
+ "type_catalog"
461
+ ],
462
+ "additionalProperties": false
463
+ },
464
+ ]
465
+ }, // optional - feedback has a default single text field
466
+ position: "bottom-right", // can be any of bottom-left, top-left, bottom-right, top-right
467
+ }
468
+ }
469
+ ```
470
+
471
+ feedback form can be disabled on individual pages via frontmatter configuration
472
+
473
+ ```yaml
474
+ ---
475
+ feedback: false
476
+ ---
477
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eox/pages-theme-eox",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "type": "module",
5
5
  "description": "Vitepress Theme with EOX branding",
6
6
  "main": "src/index.js",
@@ -14,14 +14,15 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "@eox/eslint-config": "^2.0.0",
17
+ "@eox/feedback": "^1.1.1",
17
18
  "@eox/ui": "^1.0.1",
18
19
  "vitepress": "^1.6.3"
19
20
  },
20
21
  "devDependencies": {
21
- "@release-it/conventional-changelog": "^10.0.4",
22
+ "@release-it/conventional-changelog": "^10.0.6",
22
23
  "@vitejs/plugin-vue": "^6.0.3",
23
24
  "cypress": "^15.9.0",
24
- "release-it": "^19.2.3",
25
+ "release-it": "^19.2.4",
25
26
  "vue": "^3.5.26"
26
27
  }
27
28
  }
package/src/Layout.vue CHANGED
@@ -5,32 +5,6 @@
5
5
  <NavBar />
6
6
  <NewsBanner v-if="theme.newsBanner" :content="theme.newsBanner" />
7
7
  <HeroSection />
8
- <!-- <nav
9
- v-if="frontmatter.layout !== 'home'"
10
- class="no-space"
11
- style="max-width: 1200px; padding: 12px 24px; margin: auto; width: 100%; position: sticky; top: var(--vp-nav-height); z-index: 1;"
12
- >
13
- <template
14
- v-for="(segment, index) in router.route.path.split('/')"
15
- >
16
- <p
17
- v-if="index === router.route.path.split('/').length - 1"
18
- class="capitalize surface"
19
- >
20
- {{ segment === '' ? 'Home' : (index === router.route.path.split('/').length - 1 ? page.title || segment.replaceAll('-', ' ') : segment.replaceAll('-', ' ')) }}
21
- </p>
22
- <template v-else>
23
- <a
24
-
25
- class="capitalize link surface"
26
- :href="`${router.route.path.substring(0, (router.route.path.indexOf(segment) + segment.length) || 1)}`"
27
- >
28
- {{ segment === '' ? 'Home' : (index === router.route.path.split('/').length - 1 ? page.title || segment.replaceAll('-', ' ') : segment.replaceAll('-', ' ')) }}
29
- </a>
30
- <i class="mdi mdi-chevron-right small"></i>
31
- </template>
32
- </template>
33
- </nav> -->
34
8
  </template>
35
9
  <template #not-found>
36
10
  <ClientOnly v-if="router.route.path === '/cookie-settings'">
@@ -44,6 +18,13 @@
44
18
  ></FeaturesGallery>
45
19
  </template>
46
20
  <template #layout-bottom>
21
+ <ClientOnly v-if="theme.feedback && frontmatter?.feedback !== false">
22
+ <eox-feedback-button
23
+ :endpoint="theme.feedback?.endpoint"
24
+ .schema="theme.feedback?.schema"
25
+ :position="theme.feedback?.position"
26
+ />
27
+ </ClientOnly>
47
28
  <Footer v-if="frontmatter.footer !== false" />
48
29
  <CookieBanner v-if="theme.theme.brandConfig?.analytics"></CookieBanner>
49
30
  <slot name="layout-bottom"></slot>
@@ -98,10 +98,10 @@ describe("<FeaturesGallery />", () => {
98
98
  },
99
99
  });
100
100
 
101
- cy.get("a.link-card")
101
+ cy.get(".link-card a.link-overlay")
102
102
  .should("have.attr", "href", "/card-2")
103
103
  .and("have.attr", "target", "_self");
104
- cy.get("a.link-card article").should("exist");
104
+ cy.get(".link-card article").should("exist");
105
105
  });
106
106
 
107
107
  it("does not wrap card when link has text", () => {
@@ -67,16 +67,10 @@
67
67
  >{{ t("About", theme.i18n) }}</a
68
68
  >
69
69
  </p>
70
- <p>
71
- <a
72
- :href="
73
- theme.theme.brandConfig?.legal?.termsAndConditions ||
74
- 'https://eox.at/impressum'
75
- "
76
- target="_blank"
77
- class="link"
78
- >{{ t("Terms & Conditions", theme.i18n) }}</a
79
- >
70
+ <p v-for="(term, index) in termsLinks" :key="term.id || index">
71
+ <a :href="term.url" target="_blank" class="link">
72
+ {{ term.title }}
73
+ </a>
80
74
  </p>
81
75
  <p>
82
76
  <a
@@ -105,8 +99,27 @@
105
99
 
106
100
  <script setup>
107
101
  import { useData, withBase } from "vitepress";
102
+ import { computed } from "vue";
108
103
  import { trackEvent, getFlatList, t } from "../helpers";
109
104
  const { site, theme } = useData();
105
+
106
+ const termsLinks = computed(() => {
107
+ const rawTerms = theme.value.theme.brandConfig?.legal?.termsAndConditions;
108
+ const fallbackUrl = "https://eox.at/impressum";
109
+
110
+ if (Array.isArray(rawTerms)) {
111
+ return rawTerms.map((term) => ({
112
+ ...term,
113
+ title: t(term.title, theme.value.i18n),
114
+ }));
115
+ }
116
+ return [
117
+ {
118
+ title: t("Terms & Conditions", theme.value.i18n),
119
+ url: rawTerms || fallbackUrl,
120
+ },
121
+ ];
122
+ });
110
123
  </script>
111
124
 
112
125
  <style>
@@ -81,7 +81,15 @@
81
81
  :class="`cell orig-col-${index + 2}`"
82
82
  >
83
83
  <div class="small-round surface-container medium-padding">
84
- <h4 class="primary-text bold">€ {{ calculatePrice(plan) }},-</h4>
84
+ <h4
85
+ v-if="typeof plan.basePrice === 'string'"
86
+ class="primary-text bold"
87
+ >
88
+ {{ t(plan.basePrice, theme.i18n) }}
89
+ </h4>
90
+ <h4 v-else class="primary-text bold">
91
+ € {{ calculatePrice(plan) }},-
92
+ </h4>
85
93
  </div>
86
94
  </div>
87
95
 
@@ -301,7 +309,10 @@ export default {
301
309
  },
302
310
  methods: {
303
311
  calculatePrice(plan) {
304
- const basePrice = plan.basePrice;
312
+ // Prevent Math operations from running if a string was passed
313
+ if (typeof plan.basePrice === "string") return 0;
314
+
315
+ const basePrice = plan.basePrice || 0;
305
316
  const additionalOptionPrice = this.localOptions
306
317
  .filter((option) => option.checked)
307
318
  .reduce((sum, option) => sum + (option.modifier || 0), 0);
package/src/index.js CHANGED
@@ -43,6 +43,7 @@ export default {
43
43
 
44
44
  if (!import.meta.env.SSR) {
45
45
  import("@eox/ui");
46
+ import("@eox/feedback");
46
47
 
47
48
  document.body.classList.remove("dark");
48
49
  document.body.classList.add("light");
package/src/style.css CHANGED
@@ -142,7 +142,7 @@ body {
142
142
  footer {
143
143
  margin-top: 0 !important;
144
144
  position: relative;
145
- z-index: 50;
145
+ z-index: var(--vp-z-index-footer);
146
146
  background: var(--surface-container-low);
147
147
  }
148
148
 
@@ -291,3 +291,6 @@ details[open] > summary > i.mdi-chevron-down {
291
291
  text-decoration: underline;
292
292
  color: var(--primary);
293
293
  }
294
+ eox-feedback-button {
295
+ z-index: calc(var(--vp-z-index-footer) + 1);
296
+ }
@@ -102,6 +102,7 @@ export const generate = (brandConfig) => {
102
102
  brandConfig,
103
103
  },
104
104
  logo: { light: brandConfig.logo, dark: brandConfig.logo_dark },
105
+ feedback: null,
105
106
  footer: {
106
107
  message:
107
108
  brandConfig.theme?.footer?.message ||