- TRA-76 - Send Button color changes implemented
- TRA-72 - Translation of privacy statement and T&C - TRA-73 - Strange characters in Tenant Make Name - Addition of meta information in Specialist Form Fields
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<span class="consent-rich-text">
|
||||
<template v-for="(node, idx) in nodes" :key="idx">
|
||||
<component
|
||||
v-if="node.type !== 'text'"
|
||||
:is="linkTag"
|
||||
:href="linkTag === 'a' ? '#' : undefined"
|
||||
class="consent-link"
|
||||
:aria-label="node.aria"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click.prevent="emitClick(node.type)"
|
||||
@keydown.enter.prevent="emitClick(node.type)"
|
||||
@keydown.space.prevent="emitClick(node.type)"
|
||||
>{{ node.label }}</component>
|
||||
<span v-else>{{ node.text }}</span>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ConsentRichText',
|
||||
props: {
|
||||
template: { type: String, required: true },
|
||||
asButton: { type: Boolean, default: false },
|
||||
ariaPrivacy: { type: String, default: 'Open privacy statement in a dialog' },
|
||||
ariaTerms: { type: String, default: 'Open terms and conditions in a dialog' }
|
||||
},
|
||||
emits: ['open-privacy', 'open-terms'],
|
||||
computed: {
|
||||
linkTag() {
|
||||
return this.asButton ? 'button' : 'a';
|
||||
},
|
||||
nodes() {
|
||||
// Parse only allowed tags <privacy>...</privacy> and <terms>...</terms>
|
||||
const source = (this.template || '');
|
||||
|
||||
// 2) parse only allowed tags <privacy>...</privacy> and <terms>...</terms>
|
||||
const pattern = /<(privacy|terms)>([\s\S]*?)<\/\1>/gi;
|
||||
const out = [];
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
while ((match = pattern.exec(source)) !== null) {
|
||||
const [full, tag, label] = match;
|
||||
const start = match.index;
|
||||
if (start > lastIndex) {
|
||||
out.push({ type: 'text', text: source.slice(lastIndex, start) });
|
||||
}
|
||||
out.push({
|
||||
type: tag, // 'privacy' | 'terms'
|
||||
label: (label || '').trim(),
|
||||
aria: tag === 'privacy' ? this.ariaPrivacy : this.ariaTerms
|
||||
});
|
||||
lastIndex = start + full.length;
|
||||
}
|
||||
if (lastIndex < source.length) {
|
||||
out.push({ type: 'text', text: source.slice(lastIndex) });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
emitClick(kind) {
|
||||
if (kind === 'privacy') this.$emit('open-privacy');
|
||||
if (kind === 'terms') this.$emit('open-terms');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.consent-link {
|
||||
color: #007bff;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
.consent-link:hover {
|
||||
color: #0056b3;
|
||||
}
|
||||
.consent-link:focus {
|
||||
outline: 2px solid #007bff;
|
||||
outline-offset: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
button.consent-link {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
color: #007bff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@@ -104,14 +104,16 @@
|
||||
>
|
||||
<!-- Regular checkbox label -->
|
||||
<span v-if="!isConsentField" class="checkbox-text">{{ field.name }}</span>
|
||||
<!-- Consent field with privacy and terms links -->
|
||||
<span v-else class="checkbox-text consent-text">
|
||||
{{ texts.consentPrefix }}
|
||||
<a href="#" @click="openPrivacyModal" class="consent-link">{{ texts.privacyLink }}</a>
|
||||
{{ texts.consentMiddle }}
|
||||
<a href="#" @click="openTermsModal" class="consent-link">{{ texts.termsLink }}</a>
|
||||
{{ texts.consentSuffix }}
|
||||
</span>
|
||||
<!-- Consent field with privacy and terms links (rich, multilingual) -->
|
||||
<ConsentRichText
|
||||
v-else
|
||||
class="checkbox-text consent-text"
|
||||
:template="texts.consentRich"
|
||||
:aria-privacy="texts.ariaPrivacy || 'Open privacy statement in a dialog'"
|
||||
:aria-terms="texts.ariaTerms || 'Open terms and conditions in a dialog'"
|
||||
@open-privacy="openPrivacyModal"
|
||||
@open-terms="openTermsModal"
|
||||
/>
|
||||
<span v-if="field.required" class="required" style="color: #d93025; margin-left: 2px;">*</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -180,9 +182,11 @@
|
||||
|
||||
<script>
|
||||
import { useComponentTranslations } from '../js/services/LanguageProvider.js';
|
||||
import ConsentRichText from './ConsentRichText.vue';
|
||||
|
||||
export default {
|
||||
name: 'FormField',
|
||||
components: { ConsentRichText },
|
||||
props: {
|
||||
field: {
|
||||
type: Object,
|
||||
@@ -201,13 +205,11 @@ export default {
|
||||
},
|
||||
emits: ['update:modelValue', 'open-privacy-modal', 'open-terms-modal', 'keydown-enter'],
|
||||
setup() {
|
||||
// Consent text constants (English base)
|
||||
// Consent text constants (English base) - rich template
|
||||
const consentTexts = {
|
||||
consentPrefix: "I agree with the",
|
||||
consentMiddle: "and",
|
||||
consentSuffix: "of AskEveAI",
|
||||
privacyLink: "privacy statement",
|
||||
termsLink: "terms and conditions"
|
||||
consentRich: "I agree with the <privacy>privacy statement</privacy> and the <terms>terms and conditions</terms>",
|
||||
ariaPrivacy: 'Open privacy statement in a dialog',
|
||||
ariaTerms: 'Open terms and conditions in a dialog'
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -230,24 +232,36 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
texts() {
|
||||
// Robust consent texts that always return valid values
|
||||
// Use translated texts if available and valid, otherwise use fallback
|
||||
if (this.translatedTexts && typeof this.translatedTexts === 'object') {
|
||||
const translated = this.translatedTexts;
|
||||
// Check if translated texts have all required properties
|
||||
if (translated.consentPrefix && translated.consentMiddle && translated.consentSuffix &&
|
||||
translated.privacyLink && translated.termsLink) {
|
||||
return translated;
|
||||
}
|
||||
// Validate that consentRich exists and includes both required tags; otherwise fallback to English base
|
||||
const hasValidRich = (t) => t && typeof t.consentRich === 'string'
|
||||
&& /<privacy>[\s\S]*?<\/privacy>/.test(t.consentRich)
|
||||
&& /<terms>[\s\S]*?<\/terms>/.test(t.consentRich);
|
||||
|
||||
// 1) Prefer backend-provided rich string on the field's meta (already localized)
|
||||
const meta = this.field && this.field.meta ? this.field.meta : (this.field.i18n || null);
|
||||
if (meta && typeof meta.consentRich === 'string' && hasValidRich(meta)) {
|
||||
return {
|
||||
consentRich: meta.consentRich,
|
||||
ariaPrivacy: meta.ariaPrivacy || this.fallbackTexts.ariaPrivacy,
|
||||
ariaTerms: meta.ariaTerms || this.fallbackTexts.ariaTerms
|
||||
};
|
||||
}
|
||||
|
||||
// 2) Otherwise, use client-side translated texts if available and valid
|
||||
if (this.translatedTexts && typeof this.translatedTexts === 'object' && hasValidRich(this.translatedTexts)) {
|
||||
return this.translatedTexts;
|
||||
}
|
||||
|
||||
// Fallback to English texts
|
||||
return this.fallbackTexts || {
|
||||
consentPrefix: "I agree with the",
|
||||
consentMiddle: "and",
|
||||
consentSuffix: "of AskEveAI",
|
||||
privacyLink: "privacy statement",
|
||||
termsLink: "terms and conditions"
|
||||
// 3) Fallback to English texts (rich template)
|
||||
if (this.fallbackTexts && hasValidRich(this.fallbackTexts)) {
|
||||
return this.fallbackTexts;
|
||||
}
|
||||
|
||||
// 4) Ultimate fallback (should not happen): provide a safe default
|
||||
return {
|
||||
consentRich: "I agree with the <privacy>privacy statement</privacy> and the <terms>terms and conditions</terms>",
|
||||
ariaPrivacy: 'Open privacy statement in a dialog',
|
||||
ariaTerms: 'Open terms and conditions in a dialog'
|
||||
};
|
||||
},
|
||||
value: {
|
||||
@@ -317,12 +331,10 @@ export default {
|
||||
this.value = file;
|
||||
}
|
||||
},
|
||||
openPrivacyModal(event) {
|
||||
event.preventDefault();
|
||||
openPrivacyModal() {
|
||||
this.$emit('open-privacy-modal');
|
||||
},
|
||||
openTermsModal(event) {
|
||||
event.preventDefault();
|
||||
openTermsModal() {
|
||||
this.$emit('open-terms-modal');
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user