- 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:
@@ -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>
|
||||
Reference in New Issue
Block a user