- 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:
Josako
2025-12-02 12:15:50 +01:00
parent 9b86a220b1
commit 94b805e0eb
6 changed files with 86 additions and 19 deletions

View File

@@ -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"
}

View File

@@ -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 theres 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}!

View File

@@ -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');
}
}
}
};

View File

@@ -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

View File

@@ -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');
},

View File

@@ -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