- 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:
@@ -1,4 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
|
import copy
|
||||||
|
import re
|
||||||
from typing import Dict, Any, Optional
|
from typing import Dict, Any, Optional
|
||||||
|
|
||||||
from flask import session
|
from flask import session
|
||||||
@@ -50,8 +52,8 @@ class TranslationServices:
|
|||||||
if isinstance(config_data, str):
|
if isinstance(config_data, str):
|
||||||
config_data = json.loads(config_data)
|
config_data = json.loads(config_data)
|
||||||
|
|
||||||
# Maak een kopie van de originele data om te wijzigen
|
# Maak een deep copy van de originele data om te wijzigen en input-mutatie te vermijden
|
||||||
translated_config = config_data.copy()
|
translated_config = copy.deepcopy(config_data)
|
||||||
|
|
||||||
# Haal type en versie op voor de Business Event span
|
# Haal type en versie op voor de Business Event span
|
||||||
config_type = config_data.get('type', 'Unknown')
|
config_type = config_data.get('type', 'Unknown')
|
||||||
@@ -65,71 +67,124 @@ class TranslationServices:
|
|||||||
if not context and 'metadata' in config_data and 'description' in config_data['metadata']:
|
if not context and 'metadata' in config_data and 'description' in config_data['metadata']:
|
||||||
description_context = config_data['metadata']['description']
|
description_context = config_data['metadata']['description']
|
||||||
|
|
||||||
|
# Hulpfuncties
|
||||||
|
def is_nonempty_str(val: Any) -> bool:
|
||||||
|
return isinstance(val, str) and val.strip() != ''
|
||||||
|
|
||||||
|
def safe_translate(text: str, ctx: Optional[str]):
|
||||||
|
try:
|
||||||
|
res = cache_manager.translation_cache.get_translation(
|
||||||
|
text=text,
|
||||||
|
target_lang=target_language,
|
||||||
|
source_lang=source_language,
|
||||||
|
context=ctx
|
||||||
|
)
|
||||||
|
return res.translated_text if res else None
|
||||||
|
except Exception as e:
|
||||||
|
if current_event:
|
||||||
|
current_event.log_error('translation_error', {
|
||||||
|
'tenant_id': tenant_id,
|
||||||
|
'config_type': config_type,
|
||||||
|
'config_version': config_version,
|
||||||
|
'field_config': field_config,
|
||||||
|
'error': str(e)
|
||||||
|
})
|
||||||
|
return None
|
||||||
|
|
||||||
|
tag_pair_pattern = re.compile(r'<([a-zA-Z][\w-]*)>[\s\S]*?<\/\1>')
|
||||||
|
|
||||||
|
def extract_tag_counts(text: str) -> Dict[str, int]:
|
||||||
|
counts: Dict[str, int] = {}
|
||||||
|
for m in tag_pair_pattern.finditer(text or ''):
|
||||||
|
tag = m.group(1)
|
||||||
|
counts[tag] = counts.get(tag, 0) + 1
|
||||||
|
return counts
|
||||||
|
|
||||||
|
def tags_valid(source: str, translated: str) -> bool:
|
||||||
|
return extract_tag_counts(source) == extract_tag_counts(translated)
|
||||||
|
|
||||||
|
# Counters
|
||||||
|
meta_consentRich_translated_count = 0
|
||||||
|
meta_aria_translated_count = 0
|
||||||
|
meta_inline_tags_invalid_after_translation_count = 0
|
||||||
|
|
||||||
# Loop door elk veld in de configuratie
|
# Loop door elk veld in de configuratie
|
||||||
for field_name, field_data in fields.items():
|
for field_name, field_data in fields.items():
|
||||||
# Vertaal name als het bestaat en niet leeg is
|
# Vertaal name als het bestaat en niet leeg is (alleen strings)
|
||||||
if 'name' in field_data and field_data['name']:
|
if 'name' in field_data and is_nonempty_str(field_data['name']):
|
||||||
# Gebruik context indien opgegeven, anders description_context
|
|
||||||
field_context = context if context else description_context
|
field_context = context if context else description_context
|
||||||
translated_name = cache_manager.translation_cache.get_translation(
|
t = safe_translate(field_data['name'], field_context)
|
||||||
text=field_data['name'],
|
if t:
|
||||||
target_lang=target_language,
|
translated_config[field_config][field_name]['name'] = t
|
||||||
source_lang=source_language,
|
|
||||||
context=field_context
|
|
||||||
)
|
|
||||||
if translated_name:
|
|
||||||
translated_config[field_config][field_name]['name'] = translated_name.translated_text
|
|
||||||
|
|
||||||
if 'title' in field_data and field_data['title']:
|
if 'title' in field_data and is_nonempty_str(field_data.get('title')):
|
||||||
# Gebruik context indien opgegeven, anders description_context
|
|
||||||
field_context = context if context else description_context
|
field_context = context if context else description_context
|
||||||
translated_title = cache_manager.translation_cache.get_translation(
|
t = safe_translate(field_data['title'], field_context)
|
||||||
text=field_data['title'],
|
if t:
|
||||||
target_lang=target_language,
|
translated_config[field_config][field_name]['title'] = t
|
||||||
source_lang=source_language,
|
|
||||||
context=field_context
|
|
||||||
)
|
|
||||||
if translated_title:
|
|
||||||
translated_config[field_config][field_name]['title'] = translated_title.translated_text
|
|
||||||
|
|
||||||
# Vertaal description als het bestaat en niet leeg is
|
# Vertaal description als het bestaat en niet leeg is
|
||||||
if 'description' in field_data and field_data['description']:
|
if 'description' in field_data and is_nonempty_str(field_data.get('description')):
|
||||||
# Gebruik context indien opgegeven, anders description_context
|
|
||||||
field_context = context if context else description_context
|
field_context = context if context else description_context
|
||||||
translated_desc = cache_manager.translation_cache.get_translation(
|
t = safe_translate(field_data['description'], field_context)
|
||||||
text=field_data['description'],
|
if t:
|
||||||
target_lang=target_language,
|
translated_config[field_config][field_name]['description'] = t
|
||||||
source_lang=source_language,
|
|
||||||
context=field_context
|
|
||||||
)
|
|
||||||
if translated_desc:
|
|
||||||
translated_config[field_config][field_name]['description'] = translated_desc.translated_text
|
|
||||||
|
|
||||||
# Vertaal context als het bestaat en niet leeg is
|
# Vertaal context als het bestaat en niet leeg is
|
||||||
if 'context' in field_data and field_data['context']:
|
if 'context' in field_data and is_nonempty_str(field_data.get('context')):
|
||||||
translated_ctx = cache_manager.translation_cache.get_translation(
|
t = safe_translate(field_data['context'], context)
|
||||||
text=field_data['context'],
|
if t:
|
||||||
target_lang=target_language,
|
translated_config[field_config][field_name]['context'] = t
|
||||||
source_lang=source_language,
|
|
||||||
context=context
|
|
||||||
)
|
|
||||||
if translated_ctx:
|
|
||||||
translated_config[field_config][field_name]['context'] = translated_ctx.translated_text
|
|
||||||
|
|
||||||
# vertaal allowed values als het veld bestaat en de waarden niet leeg zijn.
|
# vertaal allowed_values als het veld bestaat en waarden niet leeg zijn (alleen string-items)
|
||||||
if 'allowed_values' in field_data and field_data['allowed_values']:
|
if 'allowed_values' in field_data and isinstance(field_data['allowed_values'], list) and field_data['allowed_values']:
|
||||||
translated_allowed_values = []
|
translated_allowed_values = []
|
||||||
for allowed_value in field_data['allowed_values']:
|
for allowed_value in field_data['allowed_values']:
|
||||||
translated_allowed_value = cache_manager.translation_cache.get_translation(
|
if is_nonempty_str(allowed_value):
|
||||||
text=allowed_value,
|
t = safe_translate(allowed_value, context)
|
||||||
target_lang=target_language,
|
translated_allowed_values.append(t if t else allowed_value)
|
||||||
source_lang=source_language,
|
else:
|
||||||
context=context
|
translated_allowed_values.append(allowed_value)
|
||||||
)
|
|
||||||
translated_allowed_values.append(translated_allowed_value.translated_text)
|
|
||||||
if translated_allowed_values:
|
if translated_allowed_values:
|
||||||
translated_config[field_config][field_name]['allowed_values'] = translated_allowed_values
|
translated_config[field_config][field_name]['allowed_values'] = translated_allowed_values
|
||||||
|
|
||||||
|
# Vertaal meta.consentRich en meta.aria*
|
||||||
|
meta = field_data.get('meta')
|
||||||
|
if isinstance(meta, dict):
|
||||||
|
# consentRich
|
||||||
|
if is_nonempty_str(meta.get('consentRich')):
|
||||||
|
consent_ctx = (context if context else description_context) or ''
|
||||||
|
consent_ctx = f"Consent rich text with inline tags. Keep tag names intact and translate only inner text. {consent_ctx}".strip()
|
||||||
|
t = safe_translate(meta['consentRich'], consent_ctx)
|
||||||
|
if t and tags_valid(meta['consentRich'], t):
|
||||||
|
translated_config[field_config][field_name].setdefault('meta', {})['consentRich'] = t
|
||||||
|
meta_consentRich_translated_count += 1
|
||||||
|
else:
|
||||||
|
if t and not tags_valid(meta['consentRich'], t) and current_event:
|
||||||
|
src_counts = extract_tag_counts(meta['consentRich'])
|
||||||
|
dst_counts = extract_tag_counts(t)
|
||||||
|
current_event.log_error('inline_tags_validation_failed', {
|
||||||
|
'tenant_id': tenant_id,
|
||||||
|
'config_type': config_type,
|
||||||
|
'config_version': config_version,
|
||||||
|
'field_config': field_config,
|
||||||
|
'field_name': field_name,
|
||||||
|
'target_language': target_language,
|
||||||
|
'source_tag_counts': src_counts,
|
||||||
|
'translated_tag_counts': dst_counts
|
||||||
|
})
|
||||||
|
meta_inline_tags_invalid_after_translation_count += 1
|
||||||
|
# fallback: keep original (already in deep copy)
|
||||||
|
# aria*
|
||||||
|
for k, v in list(meta.items()):
|
||||||
|
if isinstance(k, str) and k.startswith('aria') and is_nonempty_str(v):
|
||||||
|
aria_ctx = (context if context else description_context) or ''
|
||||||
|
aria_ctx = f"ARIA label for accessibility. Short, imperative, descriptive. Form '{config_type} {config_version}', field '{field_name}'. {aria_ctx}".strip()
|
||||||
|
t2 = safe_translate(v, aria_ctx)
|
||||||
|
if t2:
|
||||||
|
translated_config[field_config][field_name].setdefault('meta', {})[k] = t2
|
||||||
|
meta_aria_translated_count += 1
|
||||||
|
|
||||||
return translated_config
|
return translated_config
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -9,8 +9,11 @@ content: >
|
|||||||
|
|
||||||
'{context}'
|
'{context}'
|
||||||
|
|
||||||
Do not translate text in between double square brackets, as these are names or terms that need to remain intact.
|
These are best practices you should follow:
|
||||||
Remove the triple quotes in your translation!
|
|
||||||
|
- Do not translate text in between double square brackets, as these are names or terms that need to remain intact. Remove the square brackets in the translation!
|
||||||
|
- We use inline tags (Custom HTML/XML-like tags). Ensure the tags themself are not translated and remain intact in the translation. The text inbetween the tags should be translated. e.g. "<terms_and_conditions>Terms & Conditions</terms_and_conditions>" translates in Dutch to <terms_and_conditions>Gebruiksvoorwaarden</terms_and_conditions>
|
||||||
|
- Remove the triple quotes in your translation!
|
||||||
|
|
||||||
I only want you to return the translation. No explanation, no options. I need to be able to directly use your answer
|
I only want you to return the translation. No explanation, no options. I need to be able to directly use your answer
|
||||||
without further interpretation. If more than one option is available, present me with the most probable one.
|
without further interpretation. If more than one option is available, present me with the most probable one.
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ content: >
|
|||||||
|
|
||||||
into '{target_language}'.
|
into '{target_language}'.
|
||||||
|
|
||||||
Do not translate text in between double square brackets, as these are names or terms that need to remain intact.
|
These are best practices you should follow:
|
||||||
Remove the triple quotes in your translation!
|
|
||||||
|
- Do not translate text in between double square brackets, as these are names or terms that need to remain intact. Remove the square brackets in the translation!
|
||||||
|
- We use inline tags (Custom HTML/XML-like tags). Ensure the tags themself are not translated and remain intact in the translation. The text inbetween the tags should be translated. e.g. "<terms_and_conditions>Terms & Conditions</terms_and_conditions>" translates in Dutch to <terms_and_conditions>Gebruiksvoorwaarden</terms_and_conditions>
|
||||||
|
- Remove the triple quotes in your translation!
|
||||||
|
|
||||||
I only want you to return the translation. No explanation, no options. I need to be able to directly use your answer
|
I only want you to return the translation. No explanation, no options. I need to be able to directly use your answer
|
||||||
without further interpretation. If more than one option is available, present me with the most probable one.
|
without further interpretation. If more than one option is available, present me with the most probable one.
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ fields:
|
|||||||
type: "boolean"
|
type: "boolean"
|
||||||
description: "Consent"
|
description: "Consent"
|
||||||
required: true
|
required: true
|
||||||
|
meta:
|
||||||
|
kind: "consent"
|
||||||
|
consentRich: "Ik Agree with the <terms>Terms and Conditions</terms> and the <privacy>Privacy Statement</privacy> of Ask Eve AI"
|
||||||
|
ariaPrivacy: "Open privacyverklaring in a modal dialog"
|
||||||
|
ariaTerms: "Open algemene voorwaarden in a modal dialog"
|
||||||
metadata:
|
metadata:
|
||||||
author: "Josako"
|
author: "Josako"
|
||||||
date_added: "2025-07-29"
|
date_added: "2025-07-29"
|
||||||
|
|||||||
@@ -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 -->
|
<!-- Regular checkbox label -->
|
||||||
<span v-if="!isConsentField" class="checkbox-text">{{ field.name }}</span>
|
<span v-if="!isConsentField" class="checkbox-text">{{ field.name }}</span>
|
||||||
<!-- Consent field with privacy and terms links -->
|
<!-- Consent field with privacy and terms links (rich, multilingual) -->
|
||||||
<span v-else class="checkbox-text consent-text">
|
<ConsentRichText
|
||||||
{{ texts.consentPrefix }}
|
v-else
|
||||||
<a href="#" @click="openPrivacyModal" class="consent-link">{{ texts.privacyLink }}</a>
|
class="checkbox-text consent-text"
|
||||||
{{ texts.consentMiddle }}
|
:template="texts.consentRich"
|
||||||
<a href="#" @click="openTermsModal" class="consent-link">{{ texts.termsLink }}</a>
|
:aria-privacy="texts.ariaPrivacy || 'Open privacy statement in a dialog'"
|
||||||
{{ texts.consentSuffix }}
|
:aria-terms="texts.ariaTerms || 'Open terms and conditions in a dialog'"
|
||||||
</span>
|
@open-privacy="openPrivacyModal"
|
||||||
|
@open-terms="openTermsModal"
|
||||||
|
/>
|
||||||
<span v-if="field.required" class="required" style="color: #d93025; margin-left: 2px;">*</span>
|
<span v-if="field.required" class="required" style="color: #d93025; margin-left: 2px;">*</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -180,9 +182,11 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { useComponentTranslations } from '../js/services/LanguageProvider.js';
|
import { useComponentTranslations } from '../js/services/LanguageProvider.js';
|
||||||
|
import ConsentRichText from './ConsentRichText.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormField',
|
name: 'FormField',
|
||||||
|
components: { ConsentRichText },
|
||||||
props: {
|
props: {
|
||||||
field: {
|
field: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -201,13 +205,11 @@ export default {
|
|||||||
},
|
},
|
||||||
emits: ['update:modelValue', 'open-privacy-modal', 'open-terms-modal', 'keydown-enter'],
|
emits: ['update:modelValue', 'open-privacy-modal', 'open-terms-modal', 'keydown-enter'],
|
||||||
setup() {
|
setup() {
|
||||||
// Consent text constants (English base)
|
// Consent text constants (English base) - rich template
|
||||||
const consentTexts = {
|
const consentTexts = {
|
||||||
consentPrefix: "I agree with the",
|
consentRich: "I agree with the <privacy>privacy statement</privacy> and the <terms>terms and conditions</terms>",
|
||||||
consentMiddle: "and",
|
ariaPrivacy: 'Open privacy statement in a dialog',
|
||||||
consentSuffix: "of AskEveAI",
|
ariaTerms: 'Open terms and conditions in a dialog'
|
||||||
privacyLink: "privacy statement",
|
|
||||||
termsLink: "terms and conditions"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -230,24 +232,36 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
texts() {
|
texts() {
|
||||||
// Robust consent texts that always return valid values
|
// Validate that consentRich exists and includes both required tags; otherwise fallback to English base
|
||||||
// Use translated texts if available and valid, otherwise use fallback
|
const hasValidRich = (t) => t && typeof t.consentRich === 'string'
|
||||||
if (this.translatedTexts && typeof this.translatedTexts === 'object') {
|
&& /<privacy>[\s\S]*?<\/privacy>/.test(t.consentRich)
|
||||||
const translated = this.translatedTexts;
|
&& /<terms>[\s\S]*?<\/terms>/.test(t.consentRich);
|
||||||
// Check if translated texts have all required properties
|
|
||||||
if (translated.consentPrefix && translated.consentMiddle && translated.consentSuffix &&
|
// 1) Prefer backend-provided rich string on the field's meta (already localized)
|
||||||
translated.privacyLink && translated.termsLink) {
|
const meta = this.field && this.field.meta ? this.field.meta : (this.field.i18n || null);
|
||||||
return translated;
|
if (meta && typeof meta.consentRich === 'string' && hasValidRich(meta)) {
|
||||||
}
|
return {
|
||||||
|
consentRich: meta.consentRich,
|
||||||
|
ariaPrivacy: meta.ariaPrivacy || this.fallbackTexts.ariaPrivacy,
|
||||||
|
ariaTerms: meta.ariaTerms || this.fallbackTexts.ariaTerms
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to English texts
|
// 2) Otherwise, use client-side translated texts if available and valid
|
||||||
return this.fallbackTexts || {
|
if (this.translatedTexts && typeof this.translatedTexts === 'object' && hasValidRich(this.translatedTexts)) {
|
||||||
consentPrefix: "I agree with the",
|
return this.translatedTexts;
|
||||||
consentMiddle: "and",
|
}
|
||||||
consentSuffix: "of AskEveAI",
|
|
||||||
privacyLink: "privacy statement",
|
// 3) Fallback to English texts (rich template)
|
||||||
termsLink: "terms and conditions"
|
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: {
|
value: {
|
||||||
@@ -317,12 +331,10 @@ export default {
|
|||||||
this.value = file;
|
this.value = file;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openPrivacyModal(event) {
|
openPrivacyModal() {
|
||||||
event.preventDefault();
|
|
||||||
this.$emit('open-privacy-modal');
|
this.$emit('open-privacy-modal');
|
||||||
},
|
},
|
||||||
openTermsModal(event) {
|
openTermsModal() {
|
||||||
event.preventDefault();
|
|
||||||
this.$emit('open-terms-modal');
|
this.$emit('open-terms-modal');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
<script>
|
<script>
|
||||||
// Definieer chatConfig voordat componenten worden geladen
|
// Definieer chatConfig voordat componenten worden geladen
|
||||||
window.chatConfig = {
|
window.chatConfig = {
|
||||||
explanation: `{{ customisation.sidebar_markdown|default('') }}`,
|
explanation: {{ customisation.sidebar_markdown|default('')|tojson }},
|
||||||
progress_tracker_insights: `{{ customisation.progress_tracker_insights|default('No Information') }}`,
|
progress_tracker_insights: {{ customisation.progress_tracker_insights|default('No Information')|tojson }},
|
||||||
form_title_display: `{{ customisation.form_title_display|default('Full Title') }}`,
|
form_title_display: {{ customisation.form_title_display|default('Full Title')|tojson }},
|
||||||
conversationId: '{{ conversation_id|default("default") }}',
|
conversationId: {{ conversation_id|default('default')|tojson }},
|
||||||
messages: {{ messages|tojson|safe }},
|
messages: {{ messages|tojson|safe }},
|
||||||
settings: {
|
settings: {
|
||||||
maxMessageLength: {{ settings.max_message_length|default(2000) }},
|
maxMessageLength: {{ settings.max_message_length|default(2000) }},
|
||||||
@@ -22,15 +22,15 @@
|
|||||||
allowReactions: {{ settings.allow_reactions|default('true')|lower }}
|
allowReactions: {{ settings.allow_reactions|default('true')|lower }}
|
||||||
},
|
},
|
||||||
apiPrefix: '/chat-client/chat',
|
apiPrefix: '/chat-client/chat',
|
||||||
language: '{{ session.magic_link.specialist_args.language|default("en") }}',
|
language: {{ session.magic_link.specialist_args.language|default('en')|tojson }},
|
||||||
supportedLanguageDetails: {{ config.SUPPORTED_LANGUAGE_DETAILS|tojson|safe }},
|
supportedLanguageDetails: {{ config.SUPPORTED_LANGUAGE_DETAILS|tojson|safe }},
|
||||||
allowedLanguages: {{ tenant_make.allowed_languages|tojson|safe }},
|
allowedLanguages: {{ tenant_make.allowed_languages|tojson|safe }},
|
||||||
tenantMake: {
|
tenantMake: {{ {
|
||||||
name: "{{ tenant_make.name|default('EveAI') }}",
|
'name': tenant_make.name or 'EveAI',
|
||||||
logo_url: "{{ tenant_make.logo_url|default('') }}"
|
'logo_url': tenant_make.logo_url or ''
|
||||||
},
|
}|tojson|safe }},
|
||||||
// Environment-aware static base provided by Flask's overridden url_for
|
// Environment-aware static base provided by Flask's overridden url_for
|
||||||
staticBase: '{{ static_url }}'
|
staticBase: {{ static_url|tojson }}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Debug info om te controleren of chatConfig correct is ingesteld
|
// Debug info om te controleren of chatConfig correct is ingesteld
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
try { globalThis.staticUrl = window.staticUrl; } catch (e) {}
|
try { globalThis.staticUrl = window.staticUrl; } catch (e) {}
|
||||||
} else {
|
} else {
|
||||||
// Prefer runtime chatConfig.staticBase; else fallback to server-provided base or default
|
// Prefer runtime chatConfig.staticBase; else fallback to server-provided base or default
|
||||||
var serverStaticBase = '{{ static_url|default("") }}' || '';
|
var serverStaticBase = {{ static_url|default('')|tojson }} || '';
|
||||||
if (!serverStaticBase) { serverStaticBase = '{{ url_for("static", filename="") }}'; }
|
if (!serverStaticBase) { serverStaticBase = {{ url_for('static', filename='')|tojson }}; }
|
||||||
var base = (window.chatConfig && window.chatConfig.staticBase) ? window.chatConfig.staticBase : (serverStaticBase || '/static/');
|
var base = (window.chatConfig && window.chatConfig.staticBase) ? window.chatConfig.staticBase : (serverStaticBase || '/static/');
|
||||||
var normalizedBase = String(base).replace(/\/+$/, '/');
|
var normalizedBase = String(base).replace(/\/+$/, '/');
|
||||||
window.staticUrl = function(path) {
|
window.staticUrl = function(path) {
|
||||||
@@ -34,19 +34,19 @@
|
|||||||
window.chatConfig.supportedLanguages = [
|
window.chatConfig.supportedLanguages = [
|
||||||
{% for lang_code in config.SUPPORTED_LANGUAGES %}
|
{% for lang_code in config.SUPPORTED_LANGUAGES %}
|
||||||
{
|
{
|
||||||
code: "{{ lang_code }}",
|
code: {{ lang_code|tojson }},
|
||||||
name: "{{ config.SUPPORTED_LANGUAGE_DETAILS[config.SUPPORTED_LANGUAGES_FULL[loop.index0]]['iso 639-1'] }}",
|
name: {{ config.SUPPORTED_LANGUAGE_DETAILS[config.SUPPORTED_LANGUAGES_FULL[loop.index0]]['iso 639-1']|tojson }},
|
||||||
flag: "{{ config.SUPPORTED_LANGUAGE_DETAILS[config.SUPPORTED_LANGUAGES_FULL[loop.index0]]['flag'] }}"
|
flag: {{ config.SUPPORTED_LANGUAGE_DETAILS[config.SUPPORTED_LANGUAGES_FULL[loop.index0]]['flag']|tojson }}
|
||||||
}{% if not loop.last %},{% endif %}
|
}{% if not loop.last %},{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Voeg tenantMake toe aan chatConfig als die nog niet bestaat
|
// Voeg tenantMake toe aan chatConfig als die nog niet bestaat
|
||||||
if (!window.chatConfig.tenantMake) {
|
if (!window.chatConfig.tenantMake) {
|
||||||
window.chatConfig.tenantMake = {
|
window.chatConfig.tenantMake = {{ {
|
||||||
name: "{{ tenant_make.name|default('EveAI') }}",
|
'name': tenant_make.name or 'EveAI',
|
||||||
logo_url: "{{ tenant_make.logo_url|default('') }}"
|
'logo_url': tenant_make.logo_url or ''
|
||||||
};
|
}|tojson|safe }};
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Taalinstellingen toegevoegd aan chatConfig');
|
console.log('Taalinstellingen toegevoegd aan chatConfig');
|
||||||
|
|||||||
Reference in New Issue
Block a user