docling-studio/frontend/src/features/store/ui/Neo4jConfigForm.vue
Pier-Jean Malandrino 151505cdbf feat(#251): add Neo4j as a StoreKind
- domain : StoreKind.NEO4J ajouté à l'enum
- service : validation config par kind étendue (Neo4j requires non-empty index_name)
- frontend : Neo4jConfigForm (index_name + database optionnelle), dispatcher StoreConfigForm, dropdown kind, i18n FR/EN
- tests service : 2 cas Neo4j (create OK + missing index_name 422)

Auth (URI/user/password) reste pilotée par les variables d'environnement
globales (cf. infra/neo4j.py) — la config par store ne porte que les
paramètres routables (index, database).
2026-05-05 09:38:39 +02:00

109 lines
2.6 KiB
Vue

<template>
<div class="config-form">
<div class="field">
<label class="field-label" for="store-neo4j-index">{{
t('storeForm.neo4j.indexName')
}}</label>
<input
id="store-neo4j-index"
v-model="indexName"
class="field-input"
type="text"
placeholder="chunks-vec"
:aria-invalid="showError && !indexName.trim()"
@input="emitChange"
/>
<p v-if="showError && !indexName.trim()" class="field-error">
{{ t('storeForm.required') }}
</p>
<p v-else class="field-help">{{ t('storeForm.neo4j.indexNameHelp') }}</p>
</div>
<div class="field">
<label class="field-label" for="store-neo4j-db">{{ t('storeForm.neo4j.database') }}</label>
<input
id="store-neo4j-db"
v-model="database"
class="field-input"
type="text"
placeholder="neo4j"
@input="emitChange"
/>
<p class="field-help">{{ t('storeForm.neo4j.databaseHelp') }}</p>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useI18n } from '../../../shared/i18n'
const props = defineProps<{
modelValue: Record<string, unknown>
showError?: boolean
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: Record<string, unknown>): void
(e: 'valid', value: boolean): void
}>()
const { t } = useI18n()
const indexName = ref(String(props.modelValue.indexName ?? props.modelValue.index_name ?? ''))
const database = ref(String(props.modelValue.database ?? ''))
function emitChange() {
emit('update:modelValue', {
indexName: indexName.value,
database: database.value || undefined,
})
emit('valid', indexName.value.trim().length > 0)
}
watch(
() => props.modelValue,
(next) => {
const incomingIndex = String(next.indexName ?? next.index_name ?? '')
if (incomingIndex !== indexName.value) indexName.value = incomingIndex
const incomingDb = String(next.database ?? '')
if (incomingDb !== database.value) database.value = incomingDb
},
)
emit('valid', indexName.value.trim().length > 0)
</script>
<style scoped>
.config-form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.field {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.field-label {
font-weight: 500;
font-size: 0.875rem;
}
.field-input {
padding: 0.5rem 0.75rem;
border: 1px solid var(--color-border, #d1d5db);
border-radius: 0.375rem;
font-size: 0.875rem;
}
.field-input[aria-invalid='true'] {
border-color: #dc2626;
}
.field-help {
font-size: 0.75rem;
color: var(--color-text-muted, #6b7280);
}
.field-error {
font-size: 0.75rem;
color: #dc2626;
}
</style>