From 654935f7583de9f64a6db9c6e56f3886b231a58e Mon Sep 17 00:00:00 2001 From: MoritzWeber Date: Fri, 21 Nov 2025 18:00:33 +0100 Subject: [PATCH 1/5] feat: Render FIP Validity from params --- archetypes/operator/index.en.md | 16 ++-- assets/sass/fipValidity.scss | 44 +++++++++++ assets/sass/main.scss | 1 + content/operator/sncf/index.de.md | 10 ++- content/operator/sncf/index.en.md | 10 ++- content/operator/sncf/index.fr.md | 10 ++- i18n/de.yaml | 5 ++ i18n/en.yaml | 5 ++ i18n/fr.yaml | 5 ++ layouts/shortcodes/fip-validity.html | 108 +++++++++++++++++++++++++++ 10 files changed, 196 insertions(+), 18 deletions(-) create mode 100644 assets/sass/fipValidity.scss create mode 100644 layouts/shortcodes/fip-validity.html diff --git a/archetypes/operator/index.en.md b/archetypes/operator/index.en.md index 31fe5d270..e99b24e25 100644 --- a/archetypes/operator/index.en.md +++ b/archetypes/operator/index.en.md @@ -8,6 +8,11 @@ country: - "country2" - "country3" operator: "{{ .File.ContentBaseName }}" +Params: + fip_coupon: true # + fip_coupon_relatives: false # + fip_50_ticket: true # + fip_global_fare: true # --- @@ -30,15 +35,14 @@ operator: "{{ .File.ContentBaseName }}" ## Validity of FIP Tickets +{{< fip-validity >}}{{< /fip-validity >}} + -FIP Coupon: <✅/⛔> \ -FIP Coupon for relatives: <✅/⛔> \ -FIP 50 Tickets: <✅/⛔> \ -FIP Global Fare: <✅/⛔> - diff --git a/assets/sass/fipValidity.scss b/assets/sass/fipValidity.scss new file mode 100644 index 000000000..55250f3a8 --- /dev/null +++ b/assets/sass/fipValidity.scss @@ -0,0 +1,44 @@ +.o-fip-validity { + display: flex; + flex-direction: column; + gap: 0.8rem; + margin-bottom: 1.2rem; + + &__tags { + display: flex; + flex-wrap: wrap; + gap: 0.6rem; + } + + &__footnotes { + display: flex; + flex-direction: column; + gap: 0.4rem; + font-size: 0.9em; + } + + &__footnote { + display: flex; + gap: 0.4rem; + align-items: baseline; + } + + &__footnote-number { + font-weight: 600; + flex-shrink: 0; + } + + &__footnote-text { + p { + margin: 0; + display: inline; + } + } + + &__note { + p { + margin: 0; + display: inline; + } + } +} diff --git a/assets/sass/main.scss b/assets/sass/main.scss index 639c39d78..a280e7801 100644 --- a/assets/sass/main.scss +++ b/assets/sass/main.scss @@ -17,3 +17,4 @@ @import "startpage.scss"; @import "interactiveMap.scss"; @import "dropdown.scss"; +@import "fipValidity.scss"; diff --git a/content/operator/sncf/index.de.md b/content/operator/sncf/index.de.md index 22fe6bd45..a3099f245 100644 --- a/content/operator/sncf/index.de.md +++ b/content/operator/sncf/index.de.md @@ -10,6 +10,11 @@ country: - "belgium" - "luxembourg" operator: "sncf" +Params: + fip_coupon: true + fip_coupon_relatives: false + fip_50_ticket: true + fip_global_fare: true --- Die SNCF (Société Nationale des Chemins de fer Français) ist die französische Staatsbahn und die wichtigste Bahngesellschaft in Frankreich. Sie betreibt fast alle Fern- und Regionalzüge in Frankreich. @@ -24,10 +29,7 @@ Die SNCF (Société Nationale des Chemins de fer Français) ist die französisch ## Gültigkeit FIP Tickets -FIP Freifahrtschein: ✅ \ -FIP Freifahrt Angehörige: ⛔ \ -FIP 50 Tickets: ✅ \ -FIP Globalpreis: ✅ (Für internationale `TGV` Züge, siehe [Grenzüberschreitende TGV inOui / ICE Züge](#grenzüberschreitende-tgv-inoui--ice-züge)) +{{< fip-validity fip_global_fare_footnote="Für internationale `TGV` Züge, siehe [Grenzüberschreitende TGV inOui / ICE Züge](#grenzüberschreitende-tgv-inoui--ice-züge)">}} FIP Freifahrtscheine und FIP 50 Tickets sind auf Verbindungen der SNCF gültig. Bei grenzüberschreitenden Fahrten im Nahverkehr muss entweder ein durchgängiges FIP 50 Ticket oder FIP Freifahrtscheine beider Länder vorhanden sein. Auf internationalen Fernverkehrsverbindungen mittels `TGV` oder `ICE` gelten jedoch Globalpreise, siehe [Grenzüberschreitende TGV inOui / ICE Züge](#grenzüberschreitende-tgv-inoui--ice-züge). diff --git a/content/operator/sncf/index.en.md b/content/operator/sncf/index.en.md index 96bfe07a4..7a8cc8e75 100644 --- a/content/operator/sncf/index.en.md +++ b/content/operator/sncf/index.en.md @@ -10,6 +10,11 @@ country: - "belgium" - "luxembourg" operator: "sncf" +Params: + fip_coupon: true + fip_coupon_relatives: false + fip_50_ticket: true + fip_global_fare: true --- SNCF (Société Nationale des Chemins de fer Français) is the French national railway company and the main rail operator in France. It operates almost all long-distance and regional trains in France. @@ -24,10 +29,7 @@ SNCF (Société Nationale des Chemins de fer Français) is the French national r ## Validity of FIP Tickets -FIP Coupon: ✅ \ -FIP Coupon for relatives: ⛔ \ -FIP 50 Ticket: ✅ \ -FIP Global Fare: ✅ (For international `TGV` trains, see [International TGV inOui / ICE trains](#international-tgv-inoui--ice-trains)) +{{< fip-validity fip_global_fare_footnote="For international `TGV` trains, see [International TGV inOui / ICE trains](#international-tgv-inoui--ice-trains)">}} FIP Coupons and FIP 50 Tickets are valid on SNCF services. For cross-border journeys on local trains, either a continuous FIP 50 Ticket or FIP Coupons for both countries are required. For international long-distance services (`TGV` or `ICE`), global fares apply (see [International TGV inOui / ICE trains](#international-tgv-inoui--ice-trains)). diff --git a/content/operator/sncf/index.fr.md b/content/operator/sncf/index.fr.md index 987479c25..8c84104cf 100644 --- a/content/operator/sncf/index.fr.md +++ b/content/operator/sncf/index.fr.md @@ -10,6 +10,11 @@ country: - "belgium" - "luxembourg" operator: "sncf" +Params: + fip_coupon: true + fip_coupon_relatives: false + fip_50_ticket: true + fip_global_fare: true --- La SNCF (Société Nationale des Chemins de fer Français) est la compagnie ferroviaire nationale française et le principal opérateur ferroviaire en France. Elle exploite la quasi-totalité des trains grandes lignes et régionaux du pays. @@ -24,10 +29,7 @@ La SNCF (Société Nationale des Chemins de fer Français) est la compagnie ferr ## Validité des Billets FIP -Coupon FIP : ✅ \ -Coupon FIP accompagnant : ⛔ \ -Billet FIP 50 : ✅ \ -Tarif Global FIP : ✅ (pour les trains internationaux `TGV`, voir [Trains TGV inOui / ICE internationaux](#trains-tgv-inoui--ice-internationaux)) +{{< fip-validity fip_global_fare_footnote="pour les trains internationaux `TGV`, voir [Trains TGV inOui / ICE internationaux](#trains-tgv-inoui--ice-internationaux)">}} Les Coupons FIP et Billets FIP 50 sont valables sur les services SNCF. Pour les trajets transfrontaliers en trains régionaux, il faut soit un Billet FIP 50 continu, soit des Coupons FIP valables dans chaque pays. Sur les trains grandes lignes internationaux (`TGV` ou `ICE`), des Tarifs Globaux s’appliquent (voir [Trains TGV inOui / ICE internationaux](#trains-tgv-inoui--ice-internationaux)). diff --git a/i18n/de.yaml b/i18n/de.yaml index 084ce82af..5a7eee2c8 100644 --- a/i18n/de.yaml +++ b/i18n/de.yaml @@ -27,6 +27,11 @@ country: other: Länder discord: FIP Guide Community editPage: Seite bearbeiten +fipValidity: + fip-50-ticket: FIP 50 Ticket + fip-coupon: FIP Freifahrtschein + fip-coupon-relatives: FIP Freifahrt Angehörige + fip-global-fare: FIP Globalpreis footer-love: aria-label: Made with love in Europe text: Made with ♥️ in Europe diff --git a/i18n/en.yaml b/i18n/en.yaml index 46bfa1ab7..868ced393 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -26,6 +26,11 @@ country: other: countries discord: FIP Guide Community editPage: Edit page +fipValidity: + fip-50-ticket: FIP 50 Ticket + fip-coupon: FIP Coupon + fip-coupon-relatives: FIP Coupon for relatives + fip-global-fare: FIP Global Fare footer-love: aria-label: Made with love in Europe text: Made with ♥️ in Europe diff --git a/i18n/fr.yaml b/i18n/fr.yaml index fba8e28ab..041b9a2ca 100644 --- a/i18n/fr.yaml +++ b/i18n/fr.yaml @@ -27,6 +27,11 @@ country: countryselection: Choisir un pays discord: Communauté FIP Guide editPage: Modifier la page +fipValidity: + fip-50-ticket: Billet FIP 50 + fip-coupon: Coupon FIP + fip-coupon-relatives: Coupon FIP accompagnant + fip-global-fare: Tarif Global FIP footer-love: aria-label: Fait avec amour en Europe text: Fait avec ♥️ en Europe diff --git a/layouts/shortcodes/fip-validity.html b/layouts/shortcodes/fip-validity.html new file mode 100644 index 000000000..4a611d25c --- /dev/null +++ b/layouts/shortcodes/fip-validity.html @@ -0,0 +1,108 @@ +{{- $page := .Page -}} +{{- $footnotes := dict -}} + +{{- with .Get "fip_coupon_footnote" -}} + {{- $footnotes = merge $footnotes (dict "fip_coupon" .) -}} +{{- end -}} +{{- with .Get "fip_coupon_relatives_footnote" -}} + {{- $footnotes = merge $footnotes (dict "fip_coupon_relatives" .) -}} +{{- end -}} +{{- with .Get "fip_50_ticket_footnote" -}} + {{- $footnotes = merge $footnotes (dict "fip_50_ticket" .) -}} +{{- end -}} +{{- with .Get "fip_global_fare_footnote" -}} + {{- $footnotes = merge $footnotes (dict "fip_global_fare" .) -}} +{{- end -}} + +{{- $footnoteCounter := 0 -}} +{{- $footnoteMap := dict -}} + + +
+
+ {{- if isset $page.Params "fip_coupon" -}} + {{- $accepted := $page.Params.fip_coupon -}} + {{- $footnoteNum := "" -}} + {{- if isset $footnotes "fip_coupon" -}} + {{- $footnoteCounter = add $footnoteCounter 1 -}} + {{- $footnoteNum = printf "%d)" $footnoteCounter -}} + {{- $footnoteMap = merge $footnoteMap (dict (string $footnoteCounter) (index $footnotes "fip_coupon")) -}} + {{- end -}} + {{- partial "tag" ( + dict + "Icon" (cond $accepted "check_circle" "cancel") + "Text" "fipValidity.fip-coupon" + "Type" (cond $accepted "success" "error") + "Footnote" $footnoteNum + ) + }} + {{- end -}} + + {{- if isset $page.Params "fip_coupon_relatives" -}} + {{- $accepted := $page.Params.fip_coupon_relatives -}} + {{- $footnoteNum := "" -}} + {{- if isset $footnotes "fip_coupon_relatives" -}} + {{- $footnoteCounter = add $footnoteCounter 1 -}} + {{- $footnoteNum = printf "%d)" $footnoteCounter -}} + {{- $footnoteMap = merge $footnoteMap (dict (string $footnoteCounter) (index $footnotes "fip_coupon_relatives")) -}} + {{- end -}} + {{- partial "tag" ( + dict + "Icon" (cond $accepted "check_circle" "cancel") + "Text" "fipValidity.fip-coupon-relatives" + "Type" (cond $accepted "success" "error") + "Footnote" $footnoteNum + ) + }} + {{- end -}} + + {{- if isset $page.Params "fip_50_ticket" -}} + {{- $accepted := $page.Params.fip_50_ticket -}} + {{- $footnoteNum := "" -}} + {{- if isset $footnotes "fip_50_ticket" -}} + {{- $footnoteCounter = add $footnoteCounter 1 -}} + {{- $footnoteNum = printf "%d)" $footnoteCounter -}} + {{- $footnoteMap = merge $footnoteMap (dict (string $footnoteCounter) (index $footnotes "fip_50_ticket")) -}} + {{- end -}} + {{- partial "tag" ( + dict + "Icon" (cond $accepted "check_circle" "cancel") + "Text" "fipValidity.fip-50-ticket" + "Type" (cond $accepted "success" "error") + "Footnote" $footnoteNum + ) + }} + {{- end -}} + + {{- if isset $page.Params "fip_global_fare" -}} + {{- $accepted := $page.Params.fip_global_fare -}} + {{- $footnoteNum := "" -}} + {{- if isset $footnotes "fip_global_fare" -}} + {{- $footnoteCounter = add $footnoteCounter 1 -}} + {{- $footnoteNum = printf "%d)" $footnoteCounter -}} + {{- $footnoteMap = merge $footnoteMap (dict (string $footnoteCounter) (index $footnotes "fip_global_fare")) -}} + {{- end -}} + {{- partial "tag" ( + dict + "Icon" (cond $accepted "check_circle" "cancel") + "Text" "fipValidity.fip-global-fare" + "Type" (cond $accepted "success" "error") + "Footnote" $footnoteNum + ) + }} + {{- end -}} +
+ + {{- if gt (len $footnoteMap) 0 -}} +
+ {{- range $index, $text := $footnoteMap -}} +
+ {{ $index }}) + {{ $text | $page.RenderString }} +
+ {{- end -}} +
+ {{- end -}} +
From 68f6006a1b6ff633bc94848171b9f14d0b2369ba Mon Sep 17 00:00:00 2001 From: MoritzWeber0 Date: Fri, 21 Nov 2025 23:03:05 +0100 Subject: [PATCH 2/5] feat: Try another design for fip validity --- assets/sass/fipValidity.scss | 34 +---------- assets/sass/fipValidityItem.scss | 31 ++++++++++ assets/sass/main.scss | 1 + content/operator/sncf/index.en.md | 2 +- layouts/partials/fip-validity-item.html | 9 +++ layouts/shortcodes/fip-validity.html | 78 +++++++------------------ 6 files changed, 63 insertions(+), 92 deletions(-) create mode 100644 assets/sass/fipValidityItem.scss create mode 100644 layouts/partials/fip-validity-item.html diff --git a/assets/sass/fipValidity.scss b/assets/sass/fipValidity.scss index 55250f3a8..7b29ceb2a 100644 --- a/assets/sass/fipValidity.scss +++ b/assets/sass/fipValidity.scss @@ -5,40 +5,8 @@ margin-bottom: 1.2rem; &__tags { - display: flex; - flex-wrap: wrap; - gap: 0.6rem; - } - - &__footnotes { display: flex; flex-direction: column; - gap: 0.4rem; - font-size: 0.9em; - } - - &__footnote { - display: flex; - gap: 0.4rem; - align-items: baseline; - } - - &__footnote-number { - font-weight: 600; - flex-shrink: 0; - } - - &__footnote-text { - p { - margin: 0; - display: inline; - } - } - - &__note { - p { - margin: 0; - display: inline; - } + gap: 0.8rem; } } diff --git a/assets/sass/fipValidityItem.scss b/assets/sass/fipValidityItem.scss new file mode 100644 index 000000000..1cca40766 --- /dev/null +++ b/assets/sass/fipValidityItem.scss @@ -0,0 +1,31 @@ +.a-fip-validity-item { + display: flex; + align-items: flex-start; + gap: 0.5rem; + font-size: 0.95em; + font-weight: 400; + line-height: 1.2em; + + @each $name, $color in $tag-colors { + &--#{$name} { + color: var(--tag-#{$name}-color); + } + } + + &__content { + display: flex; + flex-direction: column; + gap: 0.2rem; + } + + &__note { + font-size: 0.8em; + color: var(--color-body); + font-weight: 400; + + p { + margin: 0; + display: inline; + } + } +} diff --git a/assets/sass/main.scss b/assets/sass/main.scss index a280e7801..fdbd07718 100644 --- a/assets/sass/main.scss +++ b/assets/sass/main.scss @@ -18,3 +18,4 @@ @import "interactiveMap.scss"; @import "dropdown.scss"; @import "fipValidity.scss"; +@import "fipValidityItem.scss"; diff --git a/content/operator/sncf/index.en.md b/content/operator/sncf/index.en.md index 7a8cc8e75..14bcb38a8 100644 --- a/content/operator/sncf/index.en.md +++ b/content/operator/sncf/index.en.md @@ -29,7 +29,7 @@ SNCF (Société Nationale des Chemins de fer Français) is the French national r ## Validity of FIP Tickets -{{< fip-validity fip_global_fare_footnote="For international `TGV` trains, see [International TGV inOui / ICE trains](#international-tgv-inoui--ice-trains)">}} +{{< fip-validity fip_global_fare_footnote="Only for international `TGV` trains, see [International TGV inOui / ICE trains](#international-tgv-inoui--ice-trains)">}} FIP Coupons and FIP 50 Tickets are valid on SNCF services. For cross-border journeys on local trains, either a continuous FIP 50 Ticket or FIP Coupons for both countries are required. For international long-distance services (`TGV` or `ICE`), global fares apply (see [International TGV inOui / ICE trains](#international-tgv-inoui--ice-trains)). diff --git a/layouts/partials/fip-validity-item.html b/layouts/partials/fip-validity-item.html new file mode 100644 index 000000000..8d06d68b0 --- /dev/null +++ b/layouts/partials/fip-validity-item.html @@ -0,0 +1,9 @@ +
+ {{ partial "icon" .Icon }} +
+ {{ i18n .Text }} + {{- with .Note -}} + {{ . }} + {{- end -}} +
+
diff --git a/layouts/shortcodes/fip-validity.html b/layouts/shortcodes/fip-validity.html index 4a611d25c..df5ebf542 100644 --- a/layouts/shortcodes/fip-validity.html +++ b/layouts/shortcodes/fip-validity.html @@ -1,108 +1,70 @@ {{- $page := .Page -}} -{{- $footnotes := dict -}} - -{{- with .Get "fip_coupon_footnote" -}} - {{- $footnotes = merge $footnotes (dict "fip_coupon" .) -}} -{{- end -}} -{{- with .Get "fip_coupon_relatives_footnote" -}} - {{- $footnotes = merge $footnotes (dict "fip_coupon_relatives" .) -}} -{{- end -}} -{{- with .Get "fip_50_ticket_footnote" -}} - {{- $footnotes = merge $footnotes (dict "fip_50_ticket" .) -}} -{{- end -}} -{{- with .Get "fip_global_fare_footnote" -}} - {{- $footnotes = merge $footnotes (dict "fip_global_fare" .) -}} -{{- end -}} - -{{- $footnoteCounter := 0 -}} -{{- $footnoteMap := dict -}}
{{- if isset $page.Params "fip_coupon" -}} {{- $accepted := $page.Params.fip_coupon -}} - {{- $footnoteNum := "" -}} - {{- if isset $footnotes "fip_coupon" -}} - {{- $footnoteCounter = add $footnoteCounter 1 -}} - {{- $footnoteNum = printf "%d)" $footnoteCounter -}} - {{- $footnoteMap = merge $footnoteMap (dict (string $footnoteCounter) (index $footnotes "fip_coupon")) -}} + {{- $note := "" -}} + {{- with .Get "fip_coupon_footnote" -}} + {{- $note = $page.RenderString . -}} {{- end -}} - {{- partial "tag" ( + {{- partial "fip-validity-item" ( dict "Icon" (cond $accepted "check_circle" "cancel") "Text" "fipValidity.fip-coupon" "Type" (cond $accepted "success" "error") - "Footnote" $footnoteNum + "Note" $note ) }} {{- end -}} {{- if isset $page.Params "fip_coupon_relatives" -}} {{- $accepted := $page.Params.fip_coupon_relatives -}} - {{- $footnoteNum := "" -}} - {{- if isset $footnotes "fip_coupon_relatives" -}} - {{- $footnoteCounter = add $footnoteCounter 1 -}} - {{- $footnoteNum = printf "%d)" $footnoteCounter -}} - {{- $footnoteMap = merge $footnoteMap (dict (string $footnoteCounter) (index $footnotes "fip_coupon_relatives")) -}} + {{- $note := "" -}} + {{- with .Get "fip_coupon_relatives_footnote" -}} + {{- $note = $page.RenderString . -}} {{- end -}} - {{- partial "tag" ( + {{- partial "fip-validity-item" ( dict "Icon" (cond $accepted "check_circle" "cancel") "Text" "fipValidity.fip-coupon-relatives" "Type" (cond $accepted "success" "error") - "Footnote" $footnoteNum + "Note" $note ) }} {{- end -}} {{- if isset $page.Params "fip_50_ticket" -}} {{- $accepted := $page.Params.fip_50_ticket -}} - {{- $footnoteNum := "" -}} - {{- if isset $footnotes "fip_50_ticket" -}} - {{- $footnoteCounter = add $footnoteCounter 1 -}} - {{- $footnoteNum = printf "%d)" $footnoteCounter -}} - {{- $footnoteMap = merge $footnoteMap (dict (string $footnoteCounter) (index $footnotes "fip_50_ticket")) -}} + {{- $note := "" -}} + {{- with .Get "fip_50_ticket_footnote" -}} + {{- $note = $page.RenderString . -}} {{- end -}} - {{- partial "tag" ( + {{- partial "fip-validity-item" ( dict "Icon" (cond $accepted "check_circle" "cancel") "Text" "fipValidity.fip-50-ticket" "Type" (cond $accepted "success" "error") - "Footnote" $footnoteNum + "Note" $note ) }} {{- end -}} {{- if isset $page.Params "fip_global_fare" -}} {{- $accepted := $page.Params.fip_global_fare -}} - {{- $footnoteNum := "" -}} - {{- if isset $footnotes "fip_global_fare" -}} - {{- $footnoteCounter = add $footnoteCounter 1 -}} - {{- $footnoteNum = printf "%d)" $footnoteCounter -}} - {{- $footnoteMap = merge $footnoteMap (dict (string $footnoteCounter) (index $footnotes "fip_global_fare")) -}} + {{- $note := "" -}} + {{- with .Get "fip_global_fare_footnote" -}} + {{- $note = $page.RenderString . -}} {{- end -}} - {{- partial "tag" ( + {{- partial "fip-validity-item" ( dict "Icon" (cond $accepted "check_circle" "cancel") "Text" "fipValidity.fip-global-fare" "Type" (cond $accepted "success" "error") - "Footnote" $footnoteNum + "Note" $note ) }} {{- end -}}
- - {{- if gt (len $footnoteMap) 0 -}} -
- {{- range $index, $text := $footnoteMap -}} -
- {{ $index }}) - {{ $text | $page.RenderString }} -
- {{- end -}} -
- {{- end -}}
From eb7392a5234ba1a7b54fd2acaa3ed91e84100e53 Mon Sep 17 00:00:00 2001 From: MoritzWeber0 Date: Sat, 22 Nov 2025 22:07:28 +0100 Subject: [PATCH 3/5] feat: Show FIP coupon for relatives for each operator --- assets/js/main.js | 1 + assets/js/modal.js | 50 +++++++++ assets/sass/_variables.scss | 14 +++ assets/sass/fipValidityItem.scss | 48 +++++++- assets/sass/main.scss | 1 + assets/sass/modal.scss | 139 ++++++++++++++++++++++++ content/operator/sncf/index.de.md | 4 +- content/operator/sncf/index.en.md | 4 +- content/operator/sncf/index.fr.md | 4 +- i18n/de.yaml | 11 ++ i18n/en.yaml | 11 ++ i18n/fr.yaml | 11 ++ layouts/partials/fip-validity-item.html | 117 +++++++++++++++++++- layouts/shortcodes/fip-validity.html | 9 +- 14 files changed, 413 insertions(+), 11 deletions(-) create mode 100644 assets/js/modal.js create mode 100644 assets/sass/modal.scss diff --git a/assets/js/main.js b/assets/js/main.js index b2b525c62..143019654 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -8,3 +8,4 @@ import "./dropdown.js"; import "./darkmode.js"; import "./search.js"; import "./interactiveMap.js"; +import "./modal.js"; diff --git a/assets/js/modal.js b/assets/js/modal.js new file mode 100644 index 000000000..19901ff1f --- /dev/null +++ b/assets/js/modal.js @@ -0,0 +1,50 @@ +document.addEventListener("DOMContentLoaded", () => { + const modalTriggers = document.querySelectorAll("[data-modal-trigger]"); + const modalCloseButtons = document.querySelectorAll("[data-modal-close]"); + + modalTriggers.forEach((trigger) => { + trigger.addEventListener("click", (e) => { + e.preventDefault(); + const modalId = trigger.getAttribute("data-modal-trigger"); + const modal = document.getElementById(modalId); + if (modal) { + openModal(modal); + } + }); + }); + + modalCloseButtons.forEach((button) => { + button.addEventListener("click", () => { + const modal = button.closest(".o-modal"); + if (modal) { + closeModal(modal); + } + }); + }); + + document.addEventListener("keydown", (e) => { + if (e.key === "Escape") { + const openModal = document.querySelector('.o-modal[aria-hidden="false"]'); + if (openModal) { + closeModal(openModal); + } + } + }); + + function openModal(modal) { + modal.setAttribute("aria-hidden", "false"); + document.body.style.overflow = "hidden"; + + const firstFocusable = modal.querySelector( + 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])', + ); + if (firstFocusable) { + firstFocusable.focus(); + } + } + + function closeModal(modal) { + modal.setAttribute("aria-hidden", "true"); + document.body.style.overflow = ""; + } +}); diff --git a/assets/sass/_variables.scss b/assets/sass/_variables.scss index 5a55b60b3..7f5bc397c 100644 --- a/assets/sass/_variables.scss +++ b/assets/sass/_variables.scss @@ -14,6 +14,14 @@ $bg-code: #fff284; $color-onLight: #000000; $color-table-border: #5b5b5b; +$fip-validity-accepted: #155724; +$fip-validity-rejected: #b70000; +$fip-validity-warning: #b64900; + +$fip-validity-accepted-dark: #a8d5ba; +$fip-validity-rejected-dark: #f5a9ae; +$fip-validity-warning-dark: #f0d98d; + html { --pagefind-ui-scale: 1 !important; --pagefind-ui-text: #000; @@ -27,6 +35,9 @@ html { --color-onLight: #{$color-onLight}; --color-table-border: #{$color-table-border}; --color-body: rgb(33, 37, 41); + --fip-validity-accepted: #{$fip-validity-accepted}; + --fip-validity-rejected: #{$fip-validity-rejected}; + --fip-validity-warning: #{$fip-validity-warning}; --border-radius-s: 0.4rem; --border-radius-m: 0.8rem; --border-radius-l: 1.6rem; @@ -57,6 +68,9 @@ html[data-theme="dark"] { --color-onLight: #ffffff; --color-table-border: #555; --color-body: #e0e0e0; + --fip-validity-accepted: #{$fip-validity-accepted-dark}; + --fip-validity-rejected: #{$fip-validity-rejected-dark}; + --fip-validity-warning: #{$fip-validity-warning-dark}; --pagefind-ui-border: #555; --box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.5); --box-shadow-light: 0.4rem 0.4rem 0.4rem rgba(0, 0, 0, 0.3); diff --git a/assets/sass/fipValidityItem.scss b/assets/sass/fipValidityItem.scss index 1cca40766..24cf6b7cc 100644 --- a/assets/sass/fipValidityItem.scss +++ b/assets/sass/fipValidityItem.scss @@ -6,10 +6,21 @@ font-weight: 400; line-height: 1.2em; - @each $name, $color in $tag-colors { - &--#{$name} { - color: var(--tag-#{$name}-color); - } + svg { + margin-top: 0.15em; + } + + &--success { + color: var(--fip-validity-accepted); + } + + &--error { + color: var(--fip-validity-rejected); + } + + &--info { + color: var(--color-body); + opacity: 0.85; } &__content { @@ -18,6 +29,35 @@ gap: 0.2rem; } + &__text { + display: flex; + align-items: center; + gap: 0.3rem; + } + + &__info-button { + background: none; + border: none; + padding: 0; + cursor: pointer; + color: var(--color-body); + display: inline-flex; + align-items: center; + justify-content: center; + opacity: 0.7; + transition: opacity 0.2s ease; + + &:hover { + opacity: 1; + } + + svg { + width: 1.2em; + height: 1.2em; + margin-top: 0; + } + } + &__note { font-size: 0.8em; color: var(--color-body); diff --git a/assets/sass/main.scss b/assets/sass/main.scss index fdbd07718..c8323ba16 100644 --- a/assets/sass/main.scss +++ b/assets/sass/main.scss @@ -19,3 +19,4 @@ @import "dropdown.scss"; @import "fipValidity.scss"; @import "fipValidityItem.scss"; +@import "modal.scss"; diff --git a/assets/sass/modal.scss b/assets/sass/modal.scss new file mode 100644 index 000000000..b9a464cfa --- /dev/null +++ b/assets/sass/modal.scss @@ -0,0 +1,139 @@ +.o-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + opacity: 0; + pointer-events: none; + transition: opacity 0.3s ease; + + &[aria-hidden="false"] { + opacity: 1; + pointer-events: auto; + } + + &__overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + } + + &__container { + position: relative; + background: var(--bg-default); + border-radius: var(--border-radius-m); + box-shadow: var(--box-shadow); + max-width: 90vw; + max-height: 90vh; + width: 60rem; + display: flex; + flex-direction: column; + z-index: 1; + } + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1.5rem; + border-bottom: var(--border); + } + + &__title { + margin: 0; + font-size: 1.5rem; + font-weight: 600; + } + + &__close { + background: none; + border: none; + padding: 0.5rem; + cursor: pointer; + color: var(--color-body); + display: flex; + align-items: center; + justify-content: center; + border-radius: var(--border-radius-s); + transition: background 0.2s ease; + + &:hover { + background: var(--bg-neutral); + } + + svg { + width: 1.5rem; + height: 1.5rem; + } + } + + &__content { + padding: 0 1.5rem 1.5rem 1.5rem; + overflow: hidden; + display: flex; + flex-direction: column; + } + + &__description { + margin-bottom: 1.5rem; + color: var(--color-body); + flex-shrink: 0; + } + + &__table-wrapper { + overflow-y: auto; + flex: 1; + min-height: 0; + } + + &__table { + width: 100%; + margin-left: 0; + margin-bottom: 0; + + &--body { + margin-top: 0; + } + + a { + color: var(--link-default); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + } + + &__status { + display: flex; + align-items: center; + gap: 0.5rem; + + svg { + width: 1.2rem; + height: 1.2rem; + } + + &--yes { + color: var(--fip-validity-accepted); + } + + &--no { + color: var(--fip-validity-rejected); + } + + &--unknown { + color: var(--color-body); + opacity: 0.6; + } + } +} diff --git a/content/operator/sncf/index.de.md b/content/operator/sncf/index.de.md index a3099f245..f0acc5175 100644 --- a/content/operator/sncf/index.de.md +++ b/content/operator/sncf/index.de.md @@ -12,7 +12,9 @@ country: operator: "sncf" Params: fip_coupon: true - fip_coupon_relatives: false + fip_coupon_relatives: + oebb: true + renfe: false fip_50_ticket: true fip_global_fare: true --- diff --git a/content/operator/sncf/index.en.md b/content/operator/sncf/index.en.md index 14bcb38a8..0efcdeca9 100644 --- a/content/operator/sncf/index.en.md +++ b/content/operator/sncf/index.en.md @@ -12,7 +12,9 @@ country: operator: "sncf" Params: fip_coupon: true - fip_coupon_relatives: false + fip_coupon_relatives: + oebb: true + renfe: false fip_50_ticket: true fip_global_fare: true --- diff --git a/content/operator/sncf/index.fr.md b/content/operator/sncf/index.fr.md index 8c84104cf..ef59f9ff4 100644 --- a/content/operator/sncf/index.fr.md +++ b/content/operator/sncf/index.fr.md @@ -12,7 +12,9 @@ country: operator: "sncf" Params: fip_coupon: true - fip_coupon_relatives: false + fip_coupon_relatives: + oebb: true + renfe: false fip_50_ticket: true fip_global_fare: true --- diff --git a/i18n/de.yaml b/i18n/de.yaml index 5a7eee2c8..b424d0c19 100644 --- a/i18n/de.yaml +++ b/i18n/de.yaml @@ -28,10 +28,21 @@ country: discord: FIP Guide Community editPage: Seite bearbeiten fipValidity: + close-modal: Schließen fip-50-ticket: FIP 50 Ticket fip-coupon: FIP Freifahrtschein fip-coupon-relatives: FIP Freifahrt Angehörige fip-global-fare: FIP Globalpreis + issuer: Aussteller des FIP Ausweises + relatives-available: FIP Freifahrt Angehörige verfügbar + relatives-modal-description: >- + Die Verfügbarkeit von FIP Freifahrt Angehörige hängt von der + Bahngesellschaft ab, die deinen FIP Ausweis ausgestellt hat. + relatives-modal-title: FIP Freifahrt Angehörige nach Betreiber + show-relatives-info: Informationen zu FIP Freifahrt Angehörige anzeigen + status-no: Nicht akzeptiert + status-unknown: Unbekannt + status-yes: Akzeptiert footer-love: aria-label: Made with love in Europe text: Made with ♥️ in Europe diff --git a/i18n/en.yaml b/i18n/en.yaml index 868ced393..2453a2174 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -27,10 +27,21 @@ country: discord: FIP Guide Community editPage: Edit page fipValidity: + close-modal: Close fip-50-ticket: FIP 50 Ticket fip-coupon: FIP Coupon fip-coupon-relatives: FIP Coupon for relatives fip-global-fare: FIP Global Fare + issuer: Issuer of the FIP card + relatives-available: FIP Coupon for relatives available + relatives-modal-description: >- + The availability of FIP Coupon for relatives depends on the railway company + that issued your FIP card. + relatives-modal-title: FIP Coupon for relatives by Operator + show-relatives-info: Show information about FIP Coupon for relatives + status-no: Not accepted + status-unknown: Unknown + status-yes: Accepted footer-love: aria-label: Made with love in Europe text: Made with ♥️ in Europe diff --git a/i18n/fr.yaml b/i18n/fr.yaml index 041b9a2ca..709114ede 100644 --- a/i18n/fr.yaml +++ b/i18n/fr.yaml @@ -28,10 +28,21 @@ countryselection: Choisir un pays discord: Communauté FIP Guide editPage: Modifier la page fipValidity: + close-modal: Fermer fip-50-ticket: Billet FIP 50 fip-coupon: Coupon FIP fip-coupon-relatives: Coupon FIP accompagnant fip-global-fare: Tarif Global FIP + issuer: Émetteur de la carte FIP + relatives-available: Coupon FIP accompagnant disponible + relatives-modal-description: >- + La disponibilité du Coupon FIP accompagnant dépend de la compagnie + ferroviaire qui a émis votre carte FIP. + relatives-modal-title: Coupon FIP accompagnant par opérateur + show-relatives-info: Afficher les informations sur le Coupon FIP accompagnant + status-no: Non accepté + status-unknown: Inconnu + status-yes: Accepté footer-love: aria-label: Fait avec amour en Europe text: Fait avec ♥️ en Europe diff --git a/layouts/partials/fip-validity-item.html b/layouts/partials/fip-validity-item.html index 8d06d68b0..d77e98dba 100644 --- a/layouts/partials/fip-validity-item.html +++ b/layouts/partials/fip-validity-item.html @@ -1,9 +1,124 @@
{{ partial "icon" .Icon }}
- {{ i18n .Text }} + + {{ i18n .Text }} + {{- if .ShowRelativesInfo -}} + + {{- end -}} + {{- with .Note -}} {{ . }} {{- end -}}
+ +{{- if .ShowRelativesInfo -}} + +{{- end -}} diff --git a/layouts/shortcodes/fip-validity.html b/layouts/shortcodes/fip-validity.html index df5ebf542..50fc8ae20 100644 --- a/layouts/shortcodes/fip-validity.html +++ b/layouts/shortcodes/fip-validity.html @@ -20,17 +20,20 @@ {{- end -}} {{- if isset $page.Params "fip_coupon_relatives" -}} - {{- $accepted := $page.Params.fip_coupon_relatives -}} {{- $note := "" -}} + {{- with .Get "fip_coupon_relatives_footnote" -}} {{- $note = $page.RenderString . -}} {{- end -}} + {{- partial "fip-validity-item" ( dict - "Icon" (cond $accepted "check_circle" "cancel") + "Icon" "help" "Text" "fipValidity.fip-coupon-relatives" - "Type" (cond $accepted "success" "error") + "Type" "info" "Note" $note + "ShowRelativesInfo" true + "Page" $page ) }} {{- end -}} From 0d13efc9aa102a73d1255ed7895bb76899b65269 Mon Sep 17 00:00:00 2001 From: MoritzWeber0 Date: Sun, 23 Nov 2025 16:32:19 +0100 Subject: [PATCH 4/5] feat: Add modals for FIP 50 ticket and FIP coupon --- content/operator/sncf/index.de.md | 6 + content/operator/sncf/index.en.md | 6 + content/operator/sncf/index.fr.md | 6 + i18n/de.yaml | 13 ++ i18n/en.yaml | 13 ++ i18n/fr.yaml | 13 ++ layouts/partials/fip-validity-item.html | 200 ++++++++++++++++++++++++ layouts/shortcodes/fip-validity.html | 4 + 8 files changed, 261 insertions(+) diff --git a/content/operator/sncf/index.de.md b/content/operator/sncf/index.de.md index f0acc5175..956ad0159 100644 --- a/content/operator/sncf/index.de.md +++ b/content/operator/sncf/index.de.md @@ -12,10 +12,16 @@ country: operator: "sncf" Params: fip_coupon: true + fip_coupon_fields: + oebb: 4 + renfe: 2 fip_coupon_relatives: oebb: true renfe: false fip_50_ticket: true + fip_50_ticket_discount: + oebb: 25 + renfe: 75 fip_global_fare: true --- diff --git a/content/operator/sncf/index.en.md b/content/operator/sncf/index.en.md index 0efcdeca9..6ff0efacb 100644 --- a/content/operator/sncf/index.en.md +++ b/content/operator/sncf/index.en.md @@ -12,10 +12,16 @@ country: operator: "sncf" Params: fip_coupon: true + fip_coupon_fields: + oebb: 4 + renfe: 2 fip_coupon_relatives: oebb: true renfe: false fip_50_ticket: true + fip_50_ticket_discount: + oebb: 25 + renfe: 75 fip_global_fare: true --- diff --git a/content/operator/sncf/index.fr.md b/content/operator/sncf/index.fr.md index ef59f9ff4..801913c4a 100644 --- a/content/operator/sncf/index.fr.md +++ b/content/operator/sncf/index.fr.md @@ -12,10 +12,16 @@ country: operator: "sncf" Params: fip_coupon: true + fip_coupon_fields: + oebb: 4 + renfe: 2 fip_coupon_relatives: oebb: true renfe: false fip_50_ticket: true + fip_50_ticket_discount: + oebb: 25 + renfe: 75 fip_global_fare: true --- diff --git a/i18n/de.yaml b/i18n/de.yaml index b424d0c19..c8753a72c 100644 --- a/i18n/de.yaml +++ b/i18n/de.yaml @@ -29,8 +29,19 @@ discord: FIP Guide Community editPage: Seite bearbeiten fipValidity: close-modal: Schließen + discount: Rabatt + fields: Felder fip-50-ticket: FIP 50 Ticket + fip-50-ticket-discount-modal-description: >- + Der Rabatt auf den regulären Fahrpreis für FIP 50 Tickets hängt von der + Bahngesellschaft ab, die deinen FIP Ausweis ausgestellt hat. + fip-50-ticket-discount-modal-title: FIP 50 Ticket Rabatt nach Betreiber fip-coupon: FIP Freifahrtschein + fip-coupon-fields-modal-description: >- + Die maximale Anzahl der Felder auf dem FIP Freifahrtschein hängt von der + Bahngesellschaft ab, die deinen FIP Ausweis ausgestellt hat. + fip-coupon-fields-modal-title: FIP Freifahrtschein Felder nach Betreiber + fip-coupon-fields-value: '%d Felder' fip-coupon-relatives: FIP Freifahrt Angehörige fip-global-fare: FIP Globalpreis issuer: Aussteller des FIP Ausweises @@ -39,6 +50,8 @@ fipValidity: Die Verfügbarkeit von FIP Freifahrt Angehörige hängt von der Bahngesellschaft ab, die deinen FIP Ausweis ausgestellt hat. relatives-modal-title: FIP Freifahrt Angehörige nach Betreiber + show-50-ticket-discount-info: Informationen zu FIP 50 Ticket Rabatten anzeigen + show-coupon-fields-info: Informationen zu FIP Freifahrtschein Feldern anzeigen show-relatives-info: Informationen zu FIP Freifahrt Angehörige anzeigen status-no: Nicht akzeptiert status-unknown: Unbekannt diff --git a/i18n/en.yaml b/i18n/en.yaml index 2453a2174..34808d6f1 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -28,8 +28,19 @@ discord: FIP Guide Community editPage: Edit page fipValidity: close-modal: Close + discount: Discount + fields: Fields fip-50-ticket: FIP 50 Ticket + fip-50-ticket-discount-modal-description: >- + The discount on the regular fare for FIP 50 Tickets depends on the railway + company that issued your FIP card. + fip-50-ticket-discount-modal-title: FIP 50 Ticket Discount by Operator fip-coupon: FIP Coupon + fip-coupon-fields-modal-description: >- + The maximum number of fields on the FIP Coupon depends on the railway + company that issued your FIP card. + fip-coupon-fields-modal-title: FIP Coupon Fields by Operator + fip-coupon-fields-value: '%d fields' fip-coupon-relatives: FIP Coupon for relatives fip-global-fare: FIP Global Fare issuer: Issuer of the FIP card @@ -38,6 +49,8 @@ fipValidity: The availability of FIP Coupon for relatives depends on the railway company that issued your FIP card. relatives-modal-title: FIP Coupon for relatives by Operator + show-50-ticket-discount-info: Show information about FIP 50 Ticket discounts + show-coupon-fields-info: Show information about FIP Coupon fields show-relatives-info: Show information about FIP Coupon for relatives status-no: Not accepted status-unknown: Unknown diff --git a/i18n/fr.yaml b/i18n/fr.yaml index 709114ede..e4d6d4415 100644 --- a/i18n/fr.yaml +++ b/i18n/fr.yaml @@ -29,8 +29,19 @@ discord: Communauté FIP Guide editPage: Modifier la page fipValidity: close-modal: Fermer + discount: Réduction + fields: Champs fip-50-ticket: Billet FIP 50 + fip-50-ticket-discount-modal-description: >- + La réduction sur le tarif régulier pour les Billets FIP 50 dépend de la + compagnie ferroviaire qui a émis votre carte FIP. + fip-50-ticket-discount-modal-title: Réduction Billet FIP 50 par opérateur fip-coupon: Coupon FIP + fip-coupon-fields-modal-description: >- + Le nombre maximum de champs sur le Coupon FIP dépend de la compagnie + ferroviaire qui a émis votre carte FIP. + fip-coupon-fields-modal-title: Champs du Coupon FIP par opérateur + fip-coupon-fields-value: '%d champs' fip-coupon-relatives: Coupon FIP accompagnant fip-global-fare: Tarif Global FIP issuer: Émetteur de la carte FIP @@ -39,6 +50,8 @@ fipValidity: La disponibilité du Coupon FIP accompagnant dépend de la compagnie ferroviaire qui a émis votre carte FIP. relatives-modal-title: Coupon FIP accompagnant par opérateur + show-50-ticket-discount-info: Afficher les informations sur les réductions Billet FIP 50 + show-coupon-fields-info: Afficher les informations sur les champs du Coupon FIP show-relatives-info: Afficher les informations sur le Coupon FIP accompagnant status-no: Non accepté status-unknown: Inconnu diff --git a/layouts/partials/fip-validity-item.html b/layouts/partials/fip-validity-item.html index d77e98dba..6e5df3f4c 100644 --- a/layouts/partials/fip-validity-item.html +++ b/layouts/partials/fip-validity-item.html @@ -3,6 +3,24 @@
{{ i18n .Text }} + {{- if .ShowCouponFieldsInfo -}} + + {{- end -}} + {{- if .Show50TicketDiscountInfo -}} + + {{- end -}} {{- if .ShowRelativesInfo -}}
+{{- if .ShowCouponFieldsInfo -}} + +{{- end -}} + +{{- if .Show50TicketDiscountInfo -}} + +{{- end -}} + {{- if .ShowRelativesInfo -}}