Enhance default field values and block type configuration

- Refactor field options to use default values from blocks_types.json
- Add default values for various form input types in blocks_types.json
- Improve default field initialization logic in FieldOptions and working_form store
- Add camera switching functionality for mobile devices in CameraUpload component
This commit is contained in:
Julien Nahum 2025-02-19 12:25:13 +01:00
parent 83ef18f453
commit efd31133cc
4 changed files with 178 additions and 90 deletions

View File

@ -1,4 +1,3 @@
<template> <template>
<div class="relative border"> <div class="relative border">
<video <video
@ -44,6 +43,16 @@
class="w-8 h-8" class="w-8 h-8"
/> />
</span> </span>
<span
v-if="isMobileDevice"
class="text-white cursor-pointer"
@click="switchCamera"
>
<Icon
name="heroicons:arrow-path"
class="w-8 h-8"
/>
</span>
</div> </div>
</div> </div>
<div <div
@ -134,7 +143,9 @@ export default {
isCapturing: false, isCapturing: false,
capturedImage: null, capturedImage: null,
cameraPermissionStatus: "loading", cameraPermissionStatus: "loading",
quaggaInitialized: false quaggaInitialized: false,
currentFacingMode: 'user',
mediaStream: null
}), }),
computed: { computed: {
videoDisplay() { videoDisplay() {
@ -143,6 +154,9 @@ export default {
canvasDisplay() { canvasDisplay() {
return !this.isCapturing && this.capturedImage ? "" : "hidden" return !this.isCapturing && this.capturedImage ? "" : "hidden"
}, },
isMobileDevice() {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
}
}, },
mounted() { mounted() {
const webcamElement = document.getElementById("webcam") const webcamElement = document.getElementById("webcam")
@ -152,39 +166,74 @@ export default {
}, },
methods: { methods: {
async cleanupCurrentStream() {
if (this.quaggaInitialized) {
Quagga.stop()
this.quaggaInitialized = false
}
if (this.mediaStream) {
this.mediaStream.getTracks().forEach(track => track.stop())
this.mediaStream = null
}
if (this.webcam) {
this.webcam.stop()
}
const webcamElement = document.getElementById("webcam")
if (webcamElement && webcamElement.srcObject) {
webcamElement.srcObject = null
}
},
async switchCamera() {
try {
// Stop current camera
if (this.quaggaInitialized) {
Quagga.stop()
this.quaggaInitialized = false
}
this.webcam.stop()
// Toggle facing mode considering barcode mode
this.currentFacingMode = this.isBarcodeMode ? 'environment' :
(this.currentFacingMode === 'user' ? 'environment' : 'user')
// Restart camera
await this.openCameraUpload()
} catch (error) {
console.error('Error switching camera:', error)
this.cameraPermissionStatus = "unknown"
}
},
async openCameraUpload() { async openCameraUpload() {
this.isCapturing = true this.isCapturing = true
this.capturedImage = null this.capturedImage = null
try { try {
// Get video element
const webcamElement = document.getElementById("webcam") const webcamElement = document.getElementById("webcam")
const canvasElement = document.getElementById("canvas") const canvasElement = document.getElementById("canvas")
// iOS specific constraints
const constraints = { const constraints = {
audio: false, audio: false,
video: { video: {
facingMode: this.isBarcodeMode ? 'environment' : 'user', facingMode: this.isBarcodeMode ? 'environment' : this.currentFacingMode,
width: { ideal: 1280 }, width: { ideal: 1280 },
height: { ideal: 720 } height: { ideal: 720 }
} }
} }
// Try getting the stream directly first
const stream = await navigator.mediaDevices.getUserMedia(constraints) const stream = await navigator.mediaDevices.getUserMedia(constraints)
// Attach stream to video element
webcamElement.srcObject = stream webcamElement.srcObject = stream
// Create webcam instance with the stream
this.webcam = new Webcam( this.webcam = new Webcam(
webcamElement, webcamElement,
this.isBarcodeMode ? 'environment' : 'user', this.isBarcodeMode ? 'environment' : this.currentFacingMode,
canvasElement canvasElement
) )
// Wait for video to be ready
await new Promise((resolve) => { await new Promise((resolve) => {
webcamElement.onloadedmetadata = () => { webcamElement.onloadedmetadata = () => {
webcamElement.play() webcamElement.play()
@ -266,10 +315,8 @@ export default {
byteArrays.push(byteArray) byteArrays.push(byteArray)
} }
// Create Blob from binary data
const blob = new Blob(byteArrays, { type: "image/png" }) const blob = new Blob(byteArrays, { type: "image/png" })
const filename = Date.now() const filename = Date.now()
// Create a File object from the Blob
const file = new File([blob], `${filename}.png`, { type: "image/png" }) const file = new File([blob], `${filename}.png`, { type: "image/png" })
this.$emit("uploadImage", file) this.$emit("uploadImage", file)
}, },

View File

@ -609,6 +609,7 @@ import HiddenRequiredDisabled from './HiddenRequiredDisabled.vue'
import EditorSectionHeader from '~/components/open/forms/components/form-components/EditorSectionHeader.vue' import EditorSectionHeader from '~/components/open/forms/components/form-components/EditorSectionHeader.vue'
import { format } from 'date-fns' import { format } from 'date-fns'
import { default as _has } from 'lodash/has' import { default as _has } from 'lodash/has'
import blocksTypes from '~/data/blocks_types.json'
export default { export default {
name: 'FieldOptions', name: 'FieldOptions',
@ -821,57 +822,43 @@ export default {
}, },
setDefaultFieldValues() { setDefaultFieldValues() {
const defaultFieldValues = { const defaultFieldValues = {
slider: {
slider_min_value: 0,
slider_max_value: 100,
slider_step_value: 1
},
scale: {
scale_min_value: 1,
scale_max_value: 5,
scale_step_value: 1
},
rating: {
rating_max_value: 5
},
files: { files: {
max_file_size: Math.min((this.field.max_file_size ?? this.mbLimit), this.mbLimit) max_file_size: Math.min((this.field.max_file_size ?? this.mbLimit), this.mbLimit)
}, },
text: {
multi_lines: false,
max_char_limit: 2000
},
rich_text: {
max_char_limit: 2000
},
email: {
max_char_limit: 2000
},
url: {
max_char_limit: 2000
},
date: { date: {
date_format: this.dateFormatOptions[0].value, date_format: this.dateFormatOptions[0].value,
time_format: this.timeFormatOptions[0].value time_format: this.timeFormatOptions[0].value
},
matrix: {
rows:['Row 1'],
columns: [1 ,2 ,3],
selection_data:{
'Row 1': null
}
},
barcode: {
decoders: ['ean_reader', 'upc_reader']
} }
} }
// Apply type-specific defaults from blocks_types.json if available
if (this.field.type in blocksTypes && blocksTypes[this.field.type]?.default_values) {
Object.assign(this.field, blocksTypes[this.field.type].default_values)
}
// Apply additional defaults from defaultFieldValues if needed
if (this.field.type in defaultFieldValues) { if (this.field.type in defaultFieldValues) {
Object.keys(defaultFieldValues[this.field.type]).forEach(key => { Object.keys(defaultFieldValues[this.field.type]).forEach(key => {
if (!_has(this.field,key)) { if (!_has(this.field, key)) {
this.field[key] = defaultFieldValues[this.field.type][key] this.field[key] = defaultFieldValues[this.field.type][key]
} }
}) })
} }
// Ensure critical defaults for specific types
if (this.field.type === "rating" && !this.field.rating_max_value) {
this.field.rating_max_value = 5
} else if (this.field.type === "scale" && (!this.field.scale_min_value || !this.field.scale_max_value || !this.field.scale_step_value)) {
this.field.scale_min_value = 1
this.field.scale_max_value = 5
this.field.scale_step_value = 1
} else if (this.field.type === "slider" && (!this.field.slider_min_value || !this.field.slider_max_value || !this.field.slider_step_value)) {
this.field.slider_min_value = 0
this.field.slider_max_value = 50
this.field.slider_step_value = 1
} else if (["select", "multi_select"].includes(this.field.type) && !this.field[this.field.type]?.options) {
this.field[this.field.type] = { options: [] }
}
}, },
updateMatrixField(newField) { updateMatrixField(newField) {
this.field = newField this.field = newField

View File

@ -6,7 +6,11 @@
"default_block_name": "Your name", "default_block_name": "Your name",
"bg_class": "bg-blue-100", "bg_class": "bg-blue-100",
"text_class": "text-blue-900", "text_class": "text-blue-900",
"is_input": true "is_input": true,
"default_values": {
"multi_lines": false,
"max_char_limit": 2000
}
}, },
"rich_text": { "rich_text": {
"name": "rich_text", "name": "rich_text",
@ -15,7 +19,10 @@
"default_block_name": "Description", "default_block_name": "Description",
"bg_class": "bg-blue-100", "bg_class": "bg-blue-100",
"text_class": "text-blue-900", "text_class": "text-blue-900",
"is_input": true "is_input": true,
"default_values": {
"max_char_limit": 2000
}
}, },
"date": { "date": {
"name": "date", "name": "date",
@ -24,7 +31,8 @@
"default_block_name": "Date", "default_block_name": "Date",
"bg_class": "bg-green-100", "bg_class": "bg-green-100",
"text_class": "text-green-900", "text_class": "text-green-900",
"is_input": true "is_input": true,
"default_values": {}
}, },
"url": { "url": {
"name": "url", "name": "url",
@ -33,7 +41,10 @@
"default_block_name": "Link", "default_block_name": "Link",
"bg_class": "bg-blue-100", "bg_class": "bg-blue-100",
"text_class": "text-blue-900", "text_class": "text-blue-900",
"is_input": true "is_input": true,
"default_values": {
"max_char_limit": 2000
}
}, },
"phone_number": { "phone_number": {
"name": "phone_number", "name": "phone_number",
@ -42,7 +53,8 @@
"default_block_name": "Phone Number", "default_block_name": "Phone Number",
"bg_class": "bg-blue-100", "bg_class": "bg-blue-100",
"text_class": "text-blue-900", "text_class": "text-blue-900",
"is_input": true "is_input": true,
"default_values": {}
}, },
"email": { "email": {
"name": "email", "name": "email",
@ -51,7 +63,10 @@
"default_block_name": "Email", "default_block_name": "Email",
"bg_class": "bg-blue-100", "bg_class": "bg-blue-100",
"text_class": "text-blue-900", "text_class": "text-blue-900",
"is_input": true "is_input": true,
"default_values": {
"max_char_limit": 2000
}
}, },
"checkbox": { "checkbox": {
"name": "checkbox", "name": "checkbox",
@ -60,7 +75,8 @@
"default_block_name": "Checkbox", "default_block_name": "Checkbox",
"bg_class": "bg-red-100", "bg_class": "bg-red-100",
"text_class": "text-red-900", "text_class": "text-red-900",
"is_input": true "is_input": true,
"default_values": {}
}, },
"select": { "select": {
"name": "select", "name": "select",
@ -69,7 +85,15 @@
"default_block_name": "Select", "default_block_name": "Select",
"bg_class": "bg-red-100", "bg_class": "bg-red-100",
"text_class": "text-red-900", "text_class": "text-red-900",
"is_input": true "is_input": true,
"default_values": {
"select": {
"options": [
{ "name": "Option 1", "id": "Option 1" },
{ "name": "Option 2", "id": "Option 2" }
]
}
}
}, },
"multi_select": { "multi_select": {
"name": "multi_select", "name": "multi_select",
@ -78,16 +102,31 @@
"default_block_name": "Multi Select", "default_block_name": "Multi Select",
"bg_class": "bg-red-100", "bg_class": "bg-red-100",
"text_class": "text-red-900", "text_class": "text-red-900",
"is_input": true "is_input": true,
"default_values": {
"multi_select": {
"options": [
{ "name": "Option 1", "id": "Option 1" },
{ "name": "Option 2", "id": "Option 2" }
]
}
}
}, },
"matrix": { "matrix": {
"name": "matrix", "name": "matrix",
"title": "Matrix Input", "title": "Matrix Input",
"icon": "i-heroicons-table-cells-20-solid", "icon": "i-heroicons-table-cells-20-solid",
"default_block_name": "Matrix", "default_block_name": "Matrix",
"bg_class": "bg-red-100", "bg_class": "bg-red-100",
"text_class": "text-red-900", "text_class": "text-red-900",
"is_input": true "is_input": true,
"default_values": {
"rows": ["Row 1"],
"columns": [1, 2, 3],
"selection_data": {
"Row 1": null
}
}
}, },
"number": { "number": {
"name": "number", "name": "number",
@ -96,16 +135,20 @@
"default_block_name": "Number", "default_block_name": "Number",
"bg_class": "bg-purple-100", "bg_class": "bg-purple-100",
"text_class": "text-purple-900", "text_class": "text-purple-900",
"is_input": true "is_input": true,
"default_values": {}
}, },
"rating": { "rating": {
"name": "rating", "name": "rating",
"title": "Rating Input", "title": "Rating Input",
"icon": "i-heroicons-star", "icon": "i-heroicons-star",
"default_block_name": "Rating", "default_block_name": "Rating",
"bg_class": "bg-purple-100", "bg_class": "bg-purple-100",
"text_class": "text-purple-900", "text_class": "text-purple-900",
"is_input": true "is_input": true,
"default_values": {
"rating_max_value": 5
}
}, },
"scale": { "scale": {
"name": "scale", "name": "scale",
@ -114,7 +157,12 @@
"default_block_name": "Scale", "default_block_name": "Scale",
"bg_class": "bg-purple-100", "bg_class": "bg-purple-100",
"text_class": "text-purple-900", "text_class": "text-purple-900",
"is_input": true "is_input": true,
"default_values": {
"scale_min_value": 1,
"scale_max_value": 5,
"scale_step_value": 1
}
}, },
"slider": { "slider": {
"name": "slider", "name": "slider",
@ -123,7 +171,12 @@
"default_block_name": "Slider", "default_block_name": "Slider",
"bg_class": "bg-purple-100", "bg_class": "bg-purple-100",
"text_class": "text-purple-900", "text_class": "text-purple-900",
"is_input": true "is_input": true,
"default_values": {
"slider_min_value": 0,
"slider_max_value": 50,
"slider_step_value": 1
}
}, },
"files": { "files": {
"name": "files", "name": "files",
@ -132,7 +185,8 @@
"default_block_name": "Files", "default_block_name": "Files",
"bg_class": "bg-pink-100", "bg_class": "bg-pink-100",
"text_class": "text-pink-900", "text_class": "text-pink-900",
"is_input": true "is_input": true,
"default_values": {}
}, },
"signature": { "signature": {
"name": "signature", "name": "signature",
@ -141,7 +195,8 @@
"default_block_name": "Signature", "default_block_name": "Signature",
"bg_class": "bg-pink-100", "bg_class": "bg-pink-100",
"text_class": "text-pink-900", "text_class": "text-pink-900",
"is_input": true "is_input": true,
"default_values": {}
}, },
"barcode": { "barcode": {
"name": "barcode", "name": "barcode",
@ -150,7 +205,10 @@
"default_block_name": "Scan Barcode", "default_block_name": "Scan Barcode",
"bg_class": "bg-pink-100", "bg_class": "bg-pink-100",
"text_class": "text-pink-900", "text_class": "text-pink-900",
"is_input": true "is_input": true,
"default_values": {
"decoders": ["ean_reader", "ean_8_reader"]
}
}, },
"nf-text": { "nf-text": {
"name": "nf-text", "name": "nf-text",
@ -159,7 +217,8 @@
"default_block_name": "Text", "default_block_name": "Text",
"bg_class": "bg-yellow-100", "bg_class": "bg-yellow-100",
"text_class": "text-yellow-900", "text_class": "text-yellow-900",
"is_input": false "is_input": false,
"default_values": {}
}, },
"nf-page-break": { "nf-page-break": {
"name": "nf-page-break", "name": "nf-page-break",
@ -168,7 +227,8 @@
"default_block_name": "Page Break", "default_block_name": "Page Break",
"bg_class": "bg-gray-100", "bg_class": "bg-gray-100",
"text_class": "text-gray-900", "text_class": "text-gray-900",
"is_input": false "is_input": false,
"default_values": {}
}, },
"nf-divider": { "nf-divider": {
"name": "nf-divider", "name": "nf-divider",
@ -177,7 +237,8 @@
"default_block_name": "Divider", "default_block_name": "Divider",
"bg_class": "bg-gray-100", "bg_class": "bg-gray-100",
"text_class": "text-gray-900", "text_class": "text-gray-900",
"is_input": false "is_input": false,
"default_values": {}
}, },
"nf-image": { "nf-image": {
"name": "nf-image", "name": "nf-image",
@ -186,7 +247,8 @@
"default_block_name": "Image", "default_block_name": "Image",
"bg_class": "bg-yellow-100", "bg_class": "bg-yellow-100",
"text_class": "text-yellow-900", "text_class": "text-yellow-900",
"is_input": false "is_input": false,
"default_values": {}
}, },
"nf-code": { "nf-code": {
"name": "nf-code", "name": "nf-code",
@ -195,6 +257,7 @@
"default_block_name": "Code Block", "default_block_name": "Code Block",
"bg_class": "bg-yellow-100", "bg_class": "bg-yellow-100",
"text_class": "text-yellow-900", "text_class": "text-yellow-900",
"is_input": false "is_input": false,
"default_values": {}
} }
} }

View File

@ -101,23 +101,14 @@ export const useWorkingFormStore = defineStore("working_form", {
const newBlock = this.prefillDefault(this.blockForm.data()) const newBlock = this.prefillDefault(this.blockForm.data())
newBlock.id = generateUUID() newBlock.id = generateUUID()
newBlock.hidden = false newBlock.hidden = false
if (["select", "multi_select"].includes(this.blockForm.type)) {
newBlock[this.blockForm.type] = { options: [] }
}
if (this.blockForm.type === "rating") {
newBlock.rating_max_value = 5
}
if (this.blockForm.type === "scale") {
newBlock.scale_min_value = 1
newBlock.scale_max_value = 5
newBlock.scale_step_value = 1
}
if (this.blockForm.type === "slider") {
newBlock.slider_min_value = 0
newBlock.slider_max_value = 50
newBlock.slider_step_value = 1
}
newBlock.help_position = "below_input" newBlock.help_position = "below_input"
// Apply default values from blocks_types.json if they exist
if (blocksTypes[type]?.default_values) {
Object.assign(newBlock, blocksTypes[type].default_values)
}
// Insert in right position
if ( if (
(this.selectedFieldIndex === null || this.selectedFieldIndex === undefined) && (this.selectedFieldIndex === null || this.selectedFieldIndex === undefined) &&
(index === null || index === undefined) (index === null || index === undefined)