fix(loyalty-admin): require at least one terms field on program form
Some checks failed
Some checks failed
If a merchant saves a loyalty program with both terms_text and terms_cms_page_slug empty, the storefront enrollment page renders the "Terms & Conditions" link as a non-clickable <span> (see enroll.html template branch) — customers can't read what they're agreeing to. Two changes to the shared program-form to make this impossible to ship by accident: 1. Yellow warning banner inside the Terms section, visible only when both fields are empty. Tells the admin what the storefront will look like and what to fix. 2. Save button is disabled until at least one of the two terms fields is filled. The button gets a localised :title tooltip explaining why it's disabled, and disabled:cursor-not-allowed so the disabled state is obvious on hover. Added three i18n keys (terms_required_warning, terms_text_hint, terms_required_tooltip) in en/fr/de/lb, plus a small "either this or the slug above is required" hint under the textarea so each field is self-explanatory in isolation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -411,6 +411,9 @@
|
||||
"terms_cms_page_hint": "Geben Sie einen CMS-Seiten-Slug ein (z.B. agb) um die vollständigen AGB aus dem CMS-Modul anzuzeigen",
|
||||
"terms_conditions": "Allgemeine Geschäftsbedingungen (Fallback)",
|
||||
"terms_fallback_hint": "Wird verwendet wenn kein CMS-Slug gesetzt ist",
|
||||
"terms_text_hint": "Klartext-Fallback. Entweder dieses Feld ODER der CMS-Seiten-Slug oben ist erforderlich, damit Kunden die AGB auf der Anmeldeseite lesen können.",
|
||||
"terms_required_warning": "Kunden müssen Ihre AGB lesen können, bevor sie diese akzeptieren. Füllen Sie entweder den CMS-Seiten-Slug oben oder den Klartext-Fallback unten aus — bis dahin ist der \"AGB\"-Link auf der Anmeldeseite des Storefronts nicht klickbar.",
|
||||
"terms_required_tooltip": "Füllen Sie den CMS-Slug oder den Klartext der AGB aus, bevor Sie speichern.",
|
||||
"privacy_policy_url": "Datenschutzrichtlinien-URL",
|
||||
"program_status": "Programmstatus",
|
||||
"program_active": "Programm aktiv",
|
||||
|
||||
@@ -415,6 +415,9 @@
|
||||
"terms_cms_page_hint": "Enter a CMS page slug (e.g. terms-and-conditions) to display full T&C from the CMS module",
|
||||
"terms_conditions": "Terms & Conditions (fallback)",
|
||||
"terms_fallback_hint": "Used when no CMS page slug is set",
|
||||
"terms_text_hint": "Plain-text fallback. Either this field OR the CMS page slug above is required so customers can read the terms on the enrollment page.",
|
||||
"terms_required_warning": "Customers must be able to read your terms before accepting them. Fill in either the CMS page slug above or the plain-text fallback below — until you do, the 'Terms & Conditions' link on the storefront enrollment page is not clickable.",
|
||||
"terms_required_tooltip": "Fill in either the CMS page slug or the plain-text terms before saving.",
|
||||
"privacy_policy_url": "Privacy Policy URL",
|
||||
"program_status": "Program Status",
|
||||
"program_active": "Program Active",
|
||||
|
||||
@@ -411,6 +411,9 @@
|
||||
"terms_cms_page_hint": "Entrez un slug de page CMS (ex. conditions-generales) pour afficher les CGV complètes depuis le module CMS",
|
||||
"terms_conditions": "Conditions Générales (secours)",
|
||||
"terms_fallback_hint": "Utilisé quand aucun slug CMS n'est défini",
|
||||
"terms_text_hint": "Texte de secours. Ce champ OU le slug CMS ci-dessus est requis afin que les clients puissent lire les conditions sur la page d'inscription.",
|
||||
"terms_required_warning": "Les clients doivent pouvoir lire vos conditions avant de les accepter. Remplissez soit le slug de page CMS ci-dessus, soit le texte de secours ci-dessous — tant que ce n'est pas fait, le lien « Conditions Générales » sur la page d'inscription du storefront n'est pas cliquable.",
|
||||
"terms_required_tooltip": "Remplissez le slug CMS ou le texte des conditions avant d'enregistrer.",
|
||||
"privacy_policy_url": "URL politique de confidentialité",
|
||||
"program_status": "Statut du programme",
|
||||
"program_active": "Programme actif",
|
||||
|
||||
@@ -411,6 +411,9 @@
|
||||
"terms_cms_page_hint": "Gitt e CMS-Säiten-Slug an (z.B. agb) fir déi komplett AGB vum CMS-Modul unzeweisen",
|
||||
"terms_conditions": "Allgemeng Geschäftsbedingungen (Fallback)",
|
||||
"terms_fallback_hint": "Gëtt benotzt wann keen CMS-Slug gesat ass",
|
||||
"terms_text_hint": "Plain-Text-Ersatz. Entweder dëst Feld ODER de CMS-Säit-Slug uewendriwwer ass erfuerderlech, fir datt Clienten d'AGB op der Aschreiwungssäit liesen kënnen.",
|
||||
"terms_required_warning": "Clienten musse fäeg sinn Är AGB ze liese ier se se akzeptéieren. Fëllt entweder de CMS-Säit-Slug uewendriwwer oder de Plain-Text-Ersatz hei drënner aus — bis dat gemaach ass, ass de \"AGB\"-Link op der Aschreiwungssäit vum Storefront net cliquéierbar.",
|
||||
"terms_required_tooltip": "Fëllt de CMS-Slug oder den AGB-Plain-Text aus ier Dir späichert.",
|
||||
"privacy_policy_url": "Dateschutzrichtlinn-URL",
|
||||
"program_status": "Programmstatus",
|
||||
"program_active": "Programm aktiv",
|
||||
|
||||
@@ -245,6 +245,22 @@
|
||||
<span x-html="$icon('document-text', 'inline w-5 h-5 mr-2')"></span>
|
||||
{{ _('loyalty.shared.program_form.terms_privacy') }}
|
||||
</h3>
|
||||
|
||||
{# Warning banner — visible only when neither terms field is filled.
|
||||
The storefront enrollment page renders the "Terms & Conditions" link
|
||||
as a non-clickable <span> in that case (see enroll.html:122-124),
|
||||
so customers can't read the terms before accepting. #}
|
||||
<template x-if="!settings.terms_text && !settings.terms_cms_page_slug">
|
||||
<div class="mb-4 px-4 py-3 rounded-lg bg-yellow-50 border border-yellow-200 dark:bg-yellow-900/20 dark:border-yellow-800">
|
||||
<div class="flex items-start gap-3">
|
||||
<span x-html="$icon('exclamation-triangle', 'w-5 h-5 text-yellow-500 mt-0.5 flex-shrink-0')"></span>
|
||||
<p class="text-sm text-yellow-800 dark:text-yellow-200">
|
||||
{{ _('loyalty.shared.program_form.terms_required_warning') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="grid gap-6 md:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">{{ _('loyalty.shared.program_form.terms_cms_page') }}</label>
|
||||
@@ -264,6 +280,7 @@
|
||||
<textarea x-model="settings.terms_text" rows="3"
|
||||
placeholder="{{ _('loyalty.shared.program_form.terms_fallback_hint') }}"
|
||||
class="w-full px-4 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg focus:border-purple-400 focus:outline-none dark:bg-gray-700 dark:text-gray-300"></textarea>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">{{ _('loyalty.shared.program_form.terms_text_hint') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -310,8 +327,10 @@
|
||||
class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 dark:hover:bg-gray-700">
|
||||
{{ _('loyalty.common.cancel') }}
|
||||
</a>
|
||||
<button type="submit" :disabled="saving"
|
||||
class="flex items-center px-6 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 disabled:opacity-50">
|
||||
<button type="submit"
|
||||
:disabled="saving || (!settings.terms_text && !settings.terms_cms_page_slug)"
|
||||
:title="(!settings.terms_text && !settings.terms_cms_page_slug) ? '{{ _('loyalty.shared.program_form.terms_required_tooltip')|replace("'", "\\'") }}' : ''"
|
||||
class="flex items-center px-6 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<span x-show="saving" x-html="$icon('spinner', 'w-4 h-4 mr-2 animate-spin')"></span>
|
||||
<span x-text="saving ? '{{ _('loyalty.common.saving')|replace("'", "\\'") }}' : (isNewProgram ? '{{ _('loyalty.shared.program_form.create_program')|replace("'", "\\'") }}' : '{{ _('loyalty.shared.program_form.save_changes')|replace("'", "\\'") }}')"></span>
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user