diff --git a/config/static-manifest/manifest.json b/config/static-manifest/manifest.json
index 3145502..4fc1101 100644
--- a/config/static-manifest/manifest.json
+++ b/config/static-manifest/manifest.json
@@ -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"
}
\ No newline at end of file
diff --git a/config/tasks/traicie/TRAICIE_AFFIRMATIVE_ANSWER_CHECK_TASK/1.0.0.yaml b/config/tasks/traicie/TRAICIE_AFFIRMATIVE_ANSWER_CHECK_TASK/1.0.0.yaml
index 0fd3038..e5126ae 100644
--- a/config/tasks/traicie/TRAICIE_AFFIRMATIVE_ANSWER_CHECK_TASK/1.0.0.yaml
+++ b/config/tasks/traicie/TRAICIE_AFFIRMATIVE_ANSWER_CHECK_TASK/1.0.0.yaml
@@ -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}!
diff --git a/eveai_chat_client/static/assets/vue-components/ConsentRichText.vue b/eveai_chat_client/static/assets/vue-components/ConsentRichText.vue
index 9d5f94d..092c27f 100644
--- a/eveai_chat_client/static/assets/vue-components/ConsentRichText.vue
+++ b/eveai_chat_client/static/assets/vue-components/ConsentRichText.vue
@@ -37,7 +37,7 @@ export default {
const source = (this.template || '');
// 2) parse only allowed tags ... and ...
- 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');
+ }
}
}
};
diff --git a/eveai_chat_client/static/assets/vue-components/DynamicForm.vue b/eveai_chat_client/static/assets/vue-components/DynamicForm.vue
index d92697f..a044fd1 100644
--- a/eveai_chat_client/static/assets/vue-components/DynamicForm.vue
+++ b/eveai_chat_client/static/assets/vue-components/DynamicForm.vue
@@ -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
diff --git a/eveai_chat_client/static/assets/vue-components/FormField.vue b/eveai_chat_client/static/assets/vue-components/FormField.vue
index 6de1755..22ae7a6 100644
--- a/eveai_chat_client/static/assets/vue-components/FormField.vue
+++ b/eveai_chat_client/static/assets/vue-components/FormField.vue
@@ -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"
/>
*
@@ -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'
- && /[\s\S]*?<\/privacy>/.test(t.consentRich)
+ && /[\s\S]*?<\/dpa>/.test(t.consentRich)
&& /[\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');
},
diff --git a/eveai_chat_client/views/chat_views.py b/eveai_chat_client/views/chat_views.py
index 8815957..e1614e3 100644
--- a/eveai_chat_client/views/chat_views.py
+++ b/eveai_chat_client/views/chat_views.py
@@ -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