- Refinement of the chat client to have better visible clues for user vs chatbot messages

- Introduction of interview_phase and normal phase in TRAICIE_SELECTION_SPECIALIST to make interaction with bot more human.
- More and random humanised messages to TRAICIE_SELECTION_SPECIALIST
This commit is contained in:
Josako
2025-08-02 16:36:41 +02:00
parent 998ddf4c03
commit 9a88582fff
50 changed files with 2064 additions and 384 deletions

View File

@@ -21,6 +21,7 @@
@update:model-value="updateFieldValue(field.id || field.name, $event)"
@open-privacy-modal="openPrivacyModal"
@open-terms-modal="openTermsModal"
@keydown-enter="handleEnterKey"
/>
</template>
<template v-else-if="typeof formData.fields === 'object'">
@@ -33,29 +34,49 @@
@update:model-value="updateFieldValue(fieldId, $event)"
@open-privacy-modal="openPrivacyModal"
@open-terms-modal="openTermsModal"
@keydown-enter="handleEnterKey"
/>
</template>
</div>
<!-- Form actions (only show if not hidden and not read-only) -->
<div v-if="!hideActions && !readOnly" class="form-actions">
<button
type="button"
@click="handleCancel"
class="btn btn-secondary"
:disabled="isSubmitting"
>
Annuleren
</button>
<button
type="button"
@click="handleSubmit"
class="btn btn-primary"
:disabled="isSubmitting || !isFormValid"
>
<span v-if="isSubmitting">Verzenden...</span>
<span v-else>Versturen</span>
</button>
<div v-if="!hideActions && !readOnly" class="form-actions" :class="{ 'with-send-button': showSendButton }">
<!-- Send button mode (ChatInput styling) -->
<template v-if="showSendButton">
<button
type="button"
@click="handleSendSubmit"
class="send-btn"
:disabled="isSubmittingForm || !isFormValid"
:title="sendButtonText"
>
<span v-if="isSubmittingForm" class="loading-spinner"></span>
<svg v-else width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
</svg>
</button>
</template>
<!-- Standard buttons mode -->
<template v-else>
<button
type="button"
@click="handleCancel"
class="btn btn-secondary"
:disabled="isSubmitting"
>
Annuleren
</button>
<button
type="button"
@click="handleSubmit"
class="btn btn-primary"
:disabled="isSubmitting || !isFormValid"
>
<span v-if="isSubmitting">Verzenden...</span>
<span v-else>Versturen</span>
</button>
</template>
</div>
<!-- Read-only form display -->
@@ -66,8 +87,9 @@
class="form-field-readonly"
>
<div class="field-label">{{ field.name }}:</div>
<div class="field-value" :class="{'text-value': field.type === 'text'}">
{{ formatFieldValue(fieldId, field) }}
<div class="field-value" :class="{'text-value': field.type === 'text', 'boolean-value': field.type === 'boolean'}">
<span v-if="field.type === 'boolean'" v-html="formatFieldValue(fieldId, field)"></span>
<span v-else>{{ formatFieldValue(fieldId, field) }}</span>
</div>
</div>
</div>
@@ -87,12 +109,15 @@ export default {
'form-field': FormField
},
setup(props) {
const { watchIcon } = useIconManager();
const { watchIcon, loadIcons } = useIconManager();
const contentModal = injectContentModal();
// Watch formData.icon for automatic icon loading
watchIcon(() => props.formData?.icon);
// Preload boolean icons
loadIcons(['check_circle', 'cancel']);
return {
contentModal
};
@@ -149,9 +174,21 @@ export default {
apiPrefix: {
type: String,
required: true
},
showSendButton: {
type: Boolean,
default: false
},
sendButtonText: {
type: String,
default: 'Verstuur formulier'
},
isSubmittingForm: {
type: Boolean,
default: false
}
},
emits: ['submit', 'cancel', 'update:formValues'],
emits: ['submit', 'cancel', 'update:formValues', 'form-enter-pressed', 'form-send-submit'],
data() {
return {
localFormValues: { ...this.formValues }
@@ -259,6 +296,11 @@ export default {
mounted() {
// Proactief alle boolean velden initialiseren bij het laden
this.initializeBooleanFields();
// Auto-focus on first form field for better UX
this.$nextTick(() => {
this.focusFirstField();
});
},
methods: {
// Proactieve initialisatie van alle boolean velden
@@ -388,6 +430,16 @@ export default {
this.$emit('cancel');
},
handleSendSubmit() {
// Eerst proactief alle boolean velden corrigeren
this.initializeBooleanFields();
// Wacht tot updates zijn verwerkt, dan emit de form values
this.$nextTick(() => {
this.$emit('form-send-submit', this.localFormValues);
});
},
getFieldsForDisplay() {
// Voor read-only weergave
if (Array.isArray(this.formData.fields)) {
@@ -410,7 +462,15 @@ export default {
// Format different field types
if (field.type === 'boolean') {
return value ? true : false;
const iconName = value ? 'check_circle' : 'cancel';
const label = value ? 'Ja' : 'Nee';
const cssClass = value ? 'boolean-true' : 'boolean-false';
return `<span class="material-symbols-outlined boolean-icon ${cssClass}"
aria-label="${label}"
title="${label}">
${iconName}
</span>`;
} else if (field.type === 'enum' && !value && field.default) {
return field.default;
}
@@ -450,6 +510,26 @@ export default {
title: title,
contentUrl: contentUrl
});
},
// Handle Enter key press in form fields
handleEnterKey(event) {
console.log('DynamicForm: Enter event received, emitting form-enter-pressed');
// Prevent default form submission
event.preventDefault();
// Emit event to parent (ChatInput) to trigger send
this.$emit('form-enter-pressed');
},
// Focus management - auto-focus on first form field
focusFirstField() {
if (this.readOnly) return; // Don't focus in read-only mode
// Find the first focusable input element
const firstInput = this.$el.querySelector('input:not([type="hidden"]):not([type="radio"]):not([type="checkbox"]), textarea, select');
if (firstInput) {
firstInput.focus();
}
}
}
};
@@ -463,6 +543,8 @@ export default {
}
.dynamic-form {
background: var(--human-message-background);
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 15px rgba(0,0,0,0.1);
}
@@ -472,11 +554,11 @@ export default {
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid var(--active-text-color);
border-bottom: 1px solid var(--human-message-text-color);
}
.dynamic-form.readonly .form-header {
border-bottom: 1px solid var(--history-message-text-color);
border-bottom: 1px solid #777;
}
.form-icon {
@@ -486,21 +568,21 @@ export default {
display: flex;
align-items: center;
justify-content: center;
color: var(--active-text-color);
color: var(--human-message-text-color);
}
.dynamic-form.readonly .form-icon {
color: var(--history-message-text-color);
color: #777;
}
.form-title {
font-size: 1.2rem;
font-weight: 600;
color: var(--active-text-color);
color: var(--human-message-text-color);
}
.dynamic-form.readonly .form-title {
color: var(--history-message-text-color);
color: #777;
}
.form-fields {
@@ -650,7 +732,7 @@ export default {
}
.dynamic-form.readonly .form-field-readonly {
border-bottom: 1px solid var(--history-message-text-color);
border-bottom: 1px solid #777;
}
.field-label {
@@ -659,20 +741,73 @@ export default {
padding-right: 10px;
}
.dynamic-form.readonly .field-label {
color: var(--history-message-text-color);
}
.field-value {
flex: 1;
word-break: break-word;
}
.dynamic-form.readonly .field-value {
color: var(--history-message-text-color);
}
.text-value {
white-space: pre-wrap;
}
/* Boolean icon styling */
.boolean-icon {
font-size: 20px;
vertical-align: middle;
}
.boolean-true {
color: #4caf50; /* Groen voor true */
}
.boolean-false {
color: #f44336; /* Rood voor false */
}
.field-value.boolean-value {
display: flex;
align-items: center;
}
/* Send button styling (ChatInput consistency) */
.send-btn {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
background-color: var(--human-message-background);
color: var(--human-message-text-color);
border: 2px solid var(--human-message-text-color);
border-radius: 50%;
cursor: pointer;
transition: background-color 0.2s;
}
.send-btn:hover:not(:disabled) {
background-color: var(--human-message-background);
}
.send-btn:disabled {
background-color: #ccc;
cursor: not-allowed;
}
/* Loading spinner for send button */
.loading-spinner {
display: inline-block;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Flexbox layout for send button mode */
.form-actions.with-send-button {
display: flex;
justify-content: flex-end;
align-items: center;
}
</style>