detect fields

This commit is contained in:
Pete Matsyburka
2025-10-28 10:52:37 +02:00
parent b46ced2b6f
commit 1c8a7b6a7c
15 changed files with 921 additions and 8 deletions
@@ -449,6 +449,7 @@
:default-required-fields="defaultRequiredFields"
:field-types="fieldTypes"
:with-sticky-submitters="withStickySubmitters"
:with-fields-detection="withFieldsDetection"
:with-signature-id="withSignatureId"
:with-prefillable="withPrefillable"
:only-defined-fields="onlyDefinedFields"
@@ -618,6 +619,11 @@ export default {
required: false,
default: true
},
withFieldsDetection: {
type: Boolean,
required: false,
default: false
},
withAddPageButton: {
type: Boolean,
required: false,
+98 -2
View File
@@ -208,6 +208,34 @@
</li>
</ul>
</div>
<div
v-if="withFieldsDetection && editable && fields.length < 2"
class="mt-2"
>
<button
class="btn w-full"
:class="{ 'bg-base-300': fieldPagesLoaded !== null }"
@click="fieldPagesLoaded !== null ? null : detectFields()"
>
<template v-if="fieldPagesLoaded !== null">
<IconInnerShadowTop
width="22"
class="animate-spin"
/>
<span class="hidden md:inline">
{{ fieldPagesLoaded }} / {{ numberOfPages }} {{ t('processing_') }}
</span>
</template>
<template v-else>
<IconListSearch width="22" />
<span
class="hidden md:inline"
>
{{ t('autodetect_fields') }}
</span>
</template>
</button>
</div>
<div
v-show="fields.length < 4 && editable && withHelp && showTourStartForm"
class="rounded py-2 px-4 w-full border border-dashed border-base-300"
@@ -231,7 +259,7 @@
import Field from './field'
import FieldType from './field_type'
import FieldSubmitter from './field_submitter'
import { IconLock, IconCirclePlus } from '@tabler/icons-vue'
import { IconLock, IconCirclePlus, IconInnerShadowTop, IconListSearch } from '@tabler/icons-vue'
import IconDrag from './icon_drag'
export default {
@@ -240,11 +268,13 @@ export default {
Field,
FieldType,
IconCirclePlus,
IconListSearch,
IconInnerShadowTop,
FieldSubmitter,
IconDrag,
IconLock
},
inject: ['save', 'backgroundColor', 'withPhone', 'withVerification', 'withPayment', 't', 'fieldsDragFieldRef'],
inject: ['save', 'backgroundColor', 'withPhone', 'withVerification', 'withPayment', 't', 'fieldsDragFieldRef', 'baseFetch'],
props: {
fields: {
type: Array,
@@ -255,6 +285,11 @@ export default {
required: false,
default: null
},
withFieldsDetection: {
type: Boolean,
required: false,
default: false
},
withSignatureId: {
type: Boolean,
required: false,
@@ -331,12 +366,18 @@ export default {
emits: ['add-field', 'set-draw', 'set-draw-type', 'set-drag', 'drag-end', 'scroll-to-area', 'change-submitter', 'set-drag-placeholder'],
data () {
return {
fieldPagesLoaded: null,
defaultFieldsSearch: ''
}
},
computed: {
fieldNames: FieldType.computed.fieldNames,
fieldIcons: FieldType.computed.fieldIcons,
numberOfPages () {
return this.template.documents.reduce((acc, doc) => {
return acc + doc.metadata?.pdf?.number_of_pages || doc.preview_images.length
}, 0)
},
isShowFieldSearch () {
if (this.withFieldsSearch === false) {
return false
@@ -389,6 +430,61 @@ export default {
this.$emit('set-drag', field)
},
detectFields () {
const fields = []
this.fieldPagesLoaded = 0
this.baseFetch(`/templates/${this.template.id}/detect_fields`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}).then(async (response) => {
const reader = response.body.getReader()
const decoder = new TextDecoder('utf-8')
let buffer = ''
while (true) {
const { value, done } = await reader.read()
if (done) break
buffer += decoder.decode(value, { stream: true })
const lines = buffer.split('\n\n')
buffer = lines.pop()
for (const line of lines) {
if (line.startsWith('data: ')) {
const jsonStr = line.replace(/^data: /, '')
const data = JSON.parse(jsonStr)
if (data.completed) {
this.fieldPagesLoaded = null
this.template.fields = fields
break
} else if (data.fields) {
data.fields.forEach((f) => {
f.submitter_uuid = this.template.submitters[0].uuid
})
this.fieldPagesLoaded += 1
fields.push(...data.fields)
}
}
}
}
}).catch(error => {
console.error('Error in streaming message: ', error)
}).finally(() => {
this.fieldPagesLoaded = null
this.isFieldsLoading = false
})
},
setDragPlaceholder (event) {
this.$emit('set-drag-placeholder', {
offsetX: event.offsetX,
+1
View File
@@ -1,5 +1,6 @@
const en = {
view: 'View',
autodetect_fields: 'Autodetect fields',
payment_link: 'Payment link',
strikeout: 'Strikeout',
draw_strikethrough_the_document: 'Draw strikethrough the document',