- 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.js": "dist/chat-client.825210dd.js",
|
||||||
"dist/chat-client.css": "dist/chat-client.00145e73.css",
|
"dist/chat-client.css": "dist/chat-client.568d7be7.css",
|
||||||
"dist/main.js": "dist/main.6a617099.js",
|
"dist/main.js": "dist/main.6a617099.js",
|
||||||
"dist/main.css": "dist/main.7182aac3.css"
|
"dist/main.css": "dist/main.7182aac3.css"
|
||||||
}
|
}
|
||||||
@@ -10,11 +10,22 @@ task_description: >
|
|||||||
€€€{history}€€€
|
€€€{history}€€€
|
||||||
|
|
||||||
(In this history, user interactions are preceded by 'HUMAN', and your interactions with 'AI'.)
|
(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:
|
Please note that this answer can be very short:
|
||||||
- Affirmative answers: e.g. Yes, OK, Sure, Of Course
|
- Affirmative answers: e.g. Yes, OK, Sure, Of Course
|
||||||
- Negative answers: e.g. No, not really, No, I'd rather not.
|
- 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}!
|
Please consider that the answer will be given in {language}!
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default {
|
|||||||
const source = (this.template || '');
|
const source = (this.template || '');
|
||||||
|
|
||||||
// 2) parse only allowed tags <dpa>...</dpa> and <terms>...</terms>
|
// 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 = [];
|
const out = [];
|
||||||
let lastIndex = 0;
|
let lastIndex = 0;
|
||||||
let match;
|
let match;
|
||||||
@@ -62,8 +62,17 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
emitClick(kind) {
|
emitClick(kind) {
|
||||||
if (kind === 'dpa') this.$emit('open-dpa');
|
// Debug logging to trace click events for consent links
|
||||||
if (kind === 'terms') this.$emit('open-terms');
|
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"
|
:field-id="field.id || field.name"
|
||||||
:model-value="localFormValues[field.id || field.name]"
|
:model-value="localFormValues[field.id || field.name]"
|
||||||
@update:model-value="updateFieldValue(field.id || field.name, $event)"
|
@update:model-value="updateFieldValue(field.id || field.name, $event)"
|
||||||
@open-privacy-modal="openPrivacyModal"
|
@open-dpa-modal="openDpaModal"
|
||||||
@open-terms-modal="openTermsModal"
|
@open-terms-modal="openTermsModal"
|
||||||
@keydown-enter="handleEnterKey"
|
@keydown-enter="handleEnterKey"
|
||||||
/>
|
/>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
:field-id="fieldId"
|
:field-id="fieldId"
|
||||||
:model-value="localFormValues[fieldId]"
|
:model-value="localFormValues[fieldId]"
|
||||||
@update:model-value="updateFieldValue(fieldId, $event)"
|
@update:model-value="updateFieldValue(fieldId, $event)"
|
||||||
@open-privacy-modal="openPrivacyModal"
|
@open-dpa-modal="openDpaModal"
|
||||||
@open-terms-modal="openTermsModal"
|
@open-terms-modal="openTermsModal"
|
||||||
@keydown-enter="handleEnterKey"
|
@keydown-enter="handleEnterKey"
|
||||||
/>
|
/>
|
||||||
@@ -199,12 +199,19 @@ export default {
|
|||||||
// Basic validation - check required fields
|
// Basic validation - check required fields
|
||||||
const missingFields = [];
|
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)) {
|
if (Array.isArray(this.formData.fields)) {
|
||||||
// Valideer array-gebaseerde velden
|
// Valideer array-gebaseerde velden
|
||||||
this.formData.fields.forEach(field => {
|
this.formData.fields.forEach(field => {
|
||||||
const fieldId = field.id || field.name;
|
const fieldId = field.id || field.name;
|
||||||
|
const value = this.localFormValues[fieldId];
|
||||||
|
|
||||||
|
// Basis required-validatie
|
||||||
if (field.required) {
|
if (field.required) {
|
||||||
const value = this.localFormValues[fieldId];
|
|
||||||
// Voor boolean velden is false een geldige waarde
|
// Voor boolean velden is false een geldige waarde
|
||||||
if (field.type === 'boolean') {
|
if (field.type === 'boolean') {
|
||||||
// Boolean velden zijn altijd geldig als ze een boolean waarde hebben
|
// 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 {
|
} else {
|
||||||
// Valideer object-gebaseerde velden
|
// Valideer object-gebaseerde velden
|
||||||
Object.entries(this.formData.fields).forEach(([fieldId, field]) => {
|
Object.entries(this.formData.fields).forEach(([fieldId, field]) => {
|
||||||
|
const value = this.localFormValues[fieldId];
|
||||||
|
|
||||||
if (field.required) {
|
if (field.required) {
|
||||||
const value = this.localFormValues[fieldId];
|
|
||||||
// Voor boolean velden is false een geldige waarde
|
// Voor boolean velden is false een geldige waarde
|
||||||
if (field.type === 'boolean') {
|
if (field.type === 'boolean') {
|
||||||
// Boolean velden zijn altijd geldig als ze een boolean waarde hebben
|
// 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
|
// Title display mode configuration
|
||||||
titleDisplayMode() {
|
titleDisplayMode() {
|
||||||
@@ -479,11 +512,13 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Modal handling methods
|
// Modal handling methods
|
||||||
openPrivacyModal() {
|
openDpaModal() {
|
||||||
|
console.log('[DynamicForm] openDpaModal called');
|
||||||
this.loadContent('dpa');
|
this.loadContent('dpa');
|
||||||
},
|
},
|
||||||
|
|
||||||
openTermsModal() {
|
openTermsModal() {
|
||||||
|
console.log('[DynamicForm] openTermsModal called');
|
||||||
this.loadContent('terms');
|
this.loadContent('terms');
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -504,6 +539,8 @@ export default {
|
|||||||
async loadContent(contentType) {
|
async loadContent(contentType) {
|
||||||
const title = contentType === 'dpa' ? 'Data Privacy Agreement' : 'Terms & Conditions';
|
const title = contentType === 'dpa' ? 'Data Privacy Agreement' : 'Terms & Conditions';
|
||||||
const contentUrl = `${this.apiPrefix}/${contentType}`;
|
const contentUrl = `${this.apiPrefix}/${contentType}`;
|
||||||
|
|
||||||
|
console.log('[DynamicForm] Loading content from:', contentUrl);
|
||||||
|
|
||||||
// Use the composable to show modal and load content
|
// Use the composable to show modal and load content
|
||||||
await this.contentModal.showModal({
|
await this.contentModal.showModal({
|
||||||
@@ -514,11 +551,19 @@ export default {
|
|||||||
|
|
||||||
// Handle Enter key press in form fields
|
// Handle Enter key press in form fields
|
||||||
handleEnterKey(event) {
|
handleEnterKey(event) {
|
||||||
console.log('DynamicForm: Enter event received, emitting form-enter-pressed');
|
console.log('DynamicForm: Enter event received');
|
||||||
// Prevent default form submission
|
// Prevent default form submission
|
||||||
event.preventDefault();
|
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
|
// Focus management - auto-focus on first form field
|
||||||
|
|||||||
@@ -111,7 +111,7 @@
|
|||||||
:template="texts.consentRich"
|
:template="texts.consentRich"
|
||||||
:aria-privacy="texts.ariaPrivacy || 'Open dpa statement in a dialog'"
|
:aria-privacy="texts.ariaPrivacy || 'Open dpa statement in a dialog'"
|
||||||
:aria-terms="texts.ariaTerms || 'Open terms and conditions in a dialog'"
|
:aria-terms="texts.ariaTerms || 'Open terms and conditions in a dialog'"
|
||||||
@open-privacy="openPrivacyModal"
|
@open-dpa="openDpaModal"
|
||||||
@open-terms="openTermsModal"
|
@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>
|
||||||
@@ -234,7 +234,7 @@ export default {
|
|||||||
texts() {
|
texts() {
|
||||||
// Validate that consentRich exists and includes both required tags; otherwise fallback to English base
|
// Validate that consentRich exists and includes both required tags; otherwise fallback to English base
|
||||||
const hasValidRich = (t) => t && typeof t.consentRich === 'string'
|
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);
|
&& /<terms>[\s\S]*?<\/terms>/.test(t.consentRich);
|
||||||
|
|
||||||
// 1) Prefer backend-provided rich string on the field's meta (already localized)
|
// 1) Prefer backend-provided rich string on the field's meta (already localized)
|
||||||
@@ -331,10 +331,12 @@ export default {
|
|||||||
this.value = file;
|
this.value = file;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openPrivacyModal() {
|
openDpaModal() {
|
||||||
|
console.log('[FormField] openDpaModal emitting open-dpa-modal');
|
||||||
this.$emit('open-dpa-modal');
|
this.$emit('open-dpa-modal');
|
||||||
},
|
},
|
||||||
openTermsModal() {
|
openTermsModal() {
|
||||||
|
console.log('[FormField] openTermsModal emitting open-terms-modal');
|
||||||
this.$emit('open-terms-modal');
|
this.$emit('open-terms-modal');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -385,7 +385,7 @@ def translate():
|
|||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
|
|
||||||
@chat_bp.route('/privacy', methods=['GET'])
|
@chat_bp.route('/dpa', methods=['GET'])
|
||||||
def privacy_statement():
|
def privacy_statement():
|
||||||
"""
|
"""
|
||||||
Public AJAX endpoint for dpa statement content
|
Public AJAX endpoint for dpa statement content
|
||||||
|
|||||||
Reference in New Issue
Block a user