- TRA-89 - Problem solved where connection could get lost in sync between client and backend
- TRA-98 - End user could continue without accepting dpa & terms - TRA-96 - Multiple-choice questions in mobile client not scrolling -> Solved by introducing new client layout - TRA-101 - DPA-link was not working - TRA-102 - Wrong responses when looking for affirmative answers.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"dist/chat-client.js": "dist/chat-client.be407684.js",
|
||||
"dist/chat-client.css": "dist/chat-client.00145e73.css",
|
||||
"dist/chat-client.js": "dist/chat-client.825210dd.js",
|
||||
"dist/chat-client.css": "dist/chat-client.568d7be7.css",
|
||||
"dist/main.js": "dist/main.6a617099.js",
|
||||
"dist/main.css": "dist/main.7182aac3.css"
|
||||
}
|
||||
@@ -10,11 +10,22 @@ task_description: >
|
||||
€€€{history}€€€
|
||||
|
||||
(In this history, user interactions are preceded by 'HUMAN', and your interactions with 'AI'.)
|
||||
Take into account the last question asked by the you, the AI.
|
||||
|
||||
Check if the user has given an affirmative answer or not.
|
||||
Check if the user has given an affirmative answer to that last question or not.
|
||||
Please note that this answer can be very short:
|
||||
- Affirmative answers: e.g. Yes, OK, Sure, Of Course
|
||||
- Negative answers: e.g. No, not really, No, I'd rather not.
|
||||
Also note that users may use emoticons, emojis, or other symbols to express their affirmative answers.
|
||||
- Affirmative answers: e.g. 👍🏼 , 👌🏼 , ☺️
|
||||
- Negative answers: e.g. 👎🏼 , 🙅🏼 , 😒
|
||||
Finally, users may use a direct answer to the last question asked:
|
||||
Example 1:
|
||||
- Question: "Do you have any other questions, or shall we start the interview to see if there’s a match with the job?"
|
||||
- Affirmative Answer: "Start the interview" or "Start please"
|
||||
Example 2:
|
||||
- Question: "Is there anything still on your mind, or shall we begin the conversation to explore the match?"
|
||||
- Affirmative Answer: "Let's start exploring" or "Let's go"
|
||||
|
||||
Please consider that the answer will be given in {language}!
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ export default {
|
||||
const source = (this.template || '');
|
||||
|
||||
// 2) parse only allowed tags <dpa>...</dpa> and <terms>...</terms>
|
||||
const pattern = /<(privacy|terms)>([\s\S]*?)<\/\1>/gi;
|
||||
const pattern = /<(dpa|terms)>([\s\S]*?)<\/\1>/gi;
|
||||
const out = [];
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
@@ -62,8 +62,17 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
emitClick(kind) {
|
||||
if (kind === 'dpa') this.$emit('open-dpa');
|
||||
if (kind === 'terms') this.$emit('open-terms');
|
||||
// Debug logging to trace click events for consent links
|
||||
console.log('[ConsentRichText] emitClick called with kind =', kind);
|
||||
|
||||
if (kind === 'dpa') {
|
||||
console.log('[ConsentRichText] Emitting open-dpa event');
|
||||
this.$emit('open-dpa');
|
||||
}
|
||||
if (kind === 'terms') {
|
||||
console.log('[ConsentRichText] Emitting open-terms event');
|
||||
this.$emit('open-terms');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
:field-id="field.id || field.name"
|
||||
:model-value="localFormValues[field.id || field.name]"
|
||||
@update:model-value="updateFieldValue(field.id || field.name, $event)"
|
||||
@open-privacy-modal="openPrivacyModal"
|
||||
@open-dpa-modal="openDpaModal"
|
||||
@open-terms-modal="openTermsModal"
|
||||
@keydown-enter="handleEnterKey"
|
||||
/>
|
||||
@@ -32,7 +32,7 @@
|
||||
:field-id="fieldId"
|
||||
:model-value="localFormValues[fieldId]"
|
||||
@update:model-value="updateFieldValue(fieldId, $event)"
|
||||
@open-privacy-modal="openPrivacyModal"
|
||||
@open-dpa-modal="openDpaModal"
|
||||
@open-terms-modal="openTermsModal"
|
||||
@keydown-enter="handleEnterKey"
|
||||
/>
|
||||
@@ -199,12 +199,19 @@ export default {
|
||||
// Basic validation - check required fields
|
||||
const missingFields = [];
|
||||
|
||||
// Extra consent-validatie: detecteer consent velden en controleer of alle consents geaccepteerd zijn.
|
||||
// We maken dit toekomstvast voor meerdere consent-velden.
|
||||
let hasConsentField = false;
|
||||
let allConsentsAccepted = true;
|
||||
|
||||
if (Array.isArray(this.formData.fields)) {
|
||||
// Valideer array-gebaseerde velden
|
||||
this.formData.fields.forEach(field => {
|
||||
const fieldId = field.id || field.name;
|
||||
const value = this.localFormValues[fieldId];
|
||||
|
||||
// Basis required-validatie
|
||||
if (field.required) {
|
||||
const value = this.localFormValues[fieldId];
|
||||
// Voor boolean velden is false een geldige waarde
|
||||
if (field.type === 'boolean') {
|
||||
// Boolean velden zijn altijd geldig als ze een boolean waarde hebben
|
||||
@@ -220,12 +227,21 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Consent-detectie en -validatie (ongeacht required-vlag)
|
||||
if (field.type === 'boolean' && field.meta && field.meta.kind === 'consent') {
|
||||
hasConsentField = true;
|
||||
if (value !== true) {
|
||||
allConsentsAccepted = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Valideer object-gebaseerde velden
|
||||
Object.entries(this.formData.fields).forEach(([fieldId, field]) => {
|
||||
const value = this.localFormValues[fieldId];
|
||||
|
||||
if (field.required) {
|
||||
const value = this.localFormValues[fieldId];
|
||||
// Voor boolean velden is false een geldige waarde
|
||||
if (field.type === 'boolean') {
|
||||
// Boolean velden zijn altijd geldig als ze een boolean waarde hebben
|
||||
@@ -241,10 +257,27 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Consent-detectie en -validatie (ongeacht required-vlag)
|
||||
if (field.type === 'boolean' && field.meta && field.meta.kind === 'consent') {
|
||||
hasConsentField = true;
|
||||
if (value !== true) {
|
||||
allConsentsAccepted = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return missingFields.length === 0;
|
||||
const isBaseValid = missingFields.length === 0;
|
||||
|
||||
if (!hasConsentField) {
|
||||
// Geen speciale consentvelden: behoud bestaand gedrag
|
||||
return isBaseValid;
|
||||
}
|
||||
|
||||
// Als er één of meer consentvelden zijn, zijn we alleen geldig als
|
||||
// zowel de basisvalidatie als alle consents geaccepteerd zijn.
|
||||
return isBaseValid && allConsentsAccepted;
|
||||
},
|
||||
// Title display mode configuration
|
||||
titleDisplayMode() {
|
||||
@@ -479,11 +512,13 @@ export default {
|
||||
},
|
||||
|
||||
// Modal handling methods
|
||||
openPrivacyModal() {
|
||||
openDpaModal() {
|
||||
console.log('[DynamicForm] openDpaModal called');
|
||||
this.loadContent('dpa');
|
||||
},
|
||||
|
||||
openTermsModal() {
|
||||
console.log('[DynamicForm] openTermsModal called');
|
||||
this.loadContent('terms');
|
||||
},
|
||||
|
||||
@@ -504,6 +539,8 @@ export default {
|
||||
async loadContent(contentType) {
|
||||
const title = contentType === 'dpa' ? 'Data Privacy Agreement' : 'Terms & Conditions';
|
||||
const contentUrl = `${this.apiPrefix}/${contentType}`;
|
||||
|
||||
console.log('[DynamicForm] Loading content from:', contentUrl);
|
||||
|
||||
// Use the composable to show modal and load content
|
||||
await this.contentModal.showModal({
|
||||
@@ -514,11 +551,19 @@ export default {
|
||||
|
||||
// Handle Enter key press in form fields
|
||||
handleEnterKey(event) {
|
||||
console.log('DynamicForm: Enter event received, emitting form-enter-pressed');
|
||||
console.log('DynamicForm: Enter event received');
|
||||
// Prevent default form submission
|
||||
event.preventDefault();
|
||||
// Emit event to parent (ChatInput) to trigger send
|
||||
this.$emit('form-enter-pressed');
|
||||
|
||||
// Alleen submit toelaten als het formulier (inclusief consentvelden)
|
||||
// geldig is. Hiermee worden keyboard-shortcuts uitgeschakeld zolang
|
||||
// consent niet is gegeven of andere vereiste velden ontbreken.
|
||||
if (this.isFormValid && !this.isSubmittingForm && !this.isSubmitting) {
|
||||
// Emit event to parent (ChatInput) to trigger send
|
||||
this.$emit('form-enter-pressed');
|
||||
} else {
|
||||
console.log('DynamicForm: Enter ignored because form is not valid or is submitting');
|
||||
}
|
||||
},
|
||||
|
||||
// Focus management - auto-focus on first form field
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
:template="texts.consentRich"
|
||||
:aria-privacy="texts.ariaPrivacy || 'Open dpa statement in a dialog'"
|
||||
:aria-terms="texts.ariaTerms || 'Open terms and conditions in a dialog'"
|
||||
@open-privacy="openPrivacyModal"
|
||||
@open-dpa="openDpaModal"
|
||||
@open-terms="openTermsModal"
|
||||
/>
|
||||
<span v-if="field.required" class="required" style="color: #d93025; margin-left: 2px;">*</span>
|
||||
@@ -234,7 +234,7 @@ export default {
|
||||
texts() {
|
||||
// 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)
|
||||
&& /<dpa>[\s\S]*?<\/dpa>/.test(t.consentRich)
|
||||
&& /<terms>[\s\S]*?<\/terms>/.test(t.consentRich);
|
||||
|
||||
// 1) Prefer backend-provided rich string on the field's meta (already localized)
|
||||
@@ -331,10 +331,12 @@ export default {
|
||||
this.value = file;
|
||||
}
|
||||
},
|
||||
openPrivacyModal() {
|
||||
openDpaModal() {
|
||||
console.log('[FormField] openDpaModal emitting open-dpa-modal');
|
||||
this.$emit('open-dpa-modal');
|
||||
},
|
||||
openTermsModal() {
|
||||
console.log('[FormField] openTermsModal emitting open-terms-modal');
|
||||
this.$emit('open-terms-modal');
|
||||
},
|
||||
|
||||
|
||||
@@ -385,7 +385,7 @@ def translate():
|
||||
}), 500
|
||||
|
||||
|
||||
@chat_bp.route('/privacy', methods=['GET'])
|
||||
@chat_bp.route('/dpa', methods=['GET'])
|
||||
def privacy_statement():
|
||||
"""
|
||||
Public AJAX endpoint for dpa statement content
|
||||
|
||||
Reference in New Issue
Block a user