Files
saas-builder/web/src/components/FormBuilder.vue

327 lines
9.3 KiB
Vue

<template>
<div class="min-h-screen bg-gray-100 flex flex-row items-start p-4">
<div class="flex-col flex-1 items-center justify-center p-4">
<h2 class="text-xl font-bold mb-4">Form Builder</h2>
<div>
<DynamicForm
class="space-y-2 p-6 bg-white shadow-lg rounded-lg max-w-md"
:formInputs="formBuilderPropertiesInputs"
@send="handleFormPropertiesSubmit"
form-title="Form Properties"
submit-label="Update Form"
/>
</div>
<div class="mt-6">
<DynamicForm
class="space-y-2 p-6 bg-white shadow-lg rounded-lg max-w-md"
:formInputs="formBuilderFieldInputs"
@send="handleFormBuilderSubmit"
form-title="Add a new field"
/>
</div>
</div>
<div v-if="generatedFormInputs.length" class="flex-col flex-1 items-center justify-center p-4">
<h2 class="text-xl font-bold mb-4">Generated Form</h2>
<DynamicForm
class="space-y-2 p-6 bg-white shadow-lg rounded-lg max-w-md"
:formInputs="generatedFormInputs"
:submitLabel="submitLabel"
:formTitle="formTitle"
@edit="handleEditRequest"
@delete="handleDeleteRequest"
@move-up="handleMoveUp"
@move-down="handleMoveDown"
:is-editing="showEditOption"
/>
<button
@click.prevent="showEditOption = !showEditOption"
class="px-2 py-1 bg-gray-200 text-black font-semibold rounded-md shadow-md hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-300 mt-4">preview: {{showEditOption ? 'off' : 'on'}}</button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import DynamicForm from './DynamicForm.vue';
import type {FormInput} from "@/components/DynamicForm.vue";
const formBuilderPropertiesInputs = ref<FormInput[]>([
{
id: 'formTitle',
label: 'Form Title',
type: 'text',
required: false
},
{
id: 'formDescription',
label: 'Form Description',
type: 'textarea',
required: false
},
{
id: 'submitLabel',
label: 'Submit Button Label',
type: 'text',
required: false
}
]);
const formTitle = ref('');
const formDescription = ref('');
const submitLabel = ref('');
const showEditOption = ref(true);
const formBuilderFieldInputs = ref<FormInput[]>([
{
id: 'fieldType',
label: 'Field Type',
type: 'select',
required: true,
options: [
{ label: 'Text', value: 'text' },
{ label: 'Email', value: 'email' },
{ label: 'Password', value: 'password' },
{ label: 'Number', value: 'number' },
{ label: 'Textarea', value: 'textarea' },
{ label: 'Select', value: 'select' },
{ label: 'Radio', value: 'radio' },
{ label: 'Checkbox', value: 'checkbox' },
{ label: 'Multi-Input', value: 'multi-input' },
{ label: 'URL', value: 'url' },
{ label: 'Telephone', value: 'tel' },
{ label: 'Search', value: 'search' },
{ label: 'Color', value: 'color' },
{ label: 'Date', value: 'date' },
{ label: 'Datetime-local', value: 'datetime-local' },
{ label: 'Month', value: 'month' },
{ label: 'Time', value: 'time' },
{ label: 'Week', value: 'week' },
{ label: 'Description', value: 'description' }
]
},
{
id: 'fieldLabel',
label: 'Field Label',
type: 'text',
required: true
},
{
id: 'fieldID',
label: 'Field ID',
type: 'text',
required: true
},
{
id: 'placeholder',
label: 'Placeholder',
type: 'text',
required: false
},
{
id: 'required',
label: 'Required',
type: 'checkbox',
required: false,
showLabel: false,
options: [{ label: 'Required', value: 'true' }]
},
{
id: 'showLabel',
label: 'Show Label',
type: 'checkbox',
required: false,
showLabel: false,
defaultValue: ['true'],
options: [{ label: 'Show Label', value: 'true' }]
},
{
id: 'defaultValue',
label: 'Default Value',
type: 'text',
required: false
},
{
id: 'min',
label: 'Min (for Number, Date, etc.)',
type: 'text',
required: false
},
{
id: 'max',
label: 'Max (for Number, Date, etc.)',
type: 'text',
required: false
},
{
id: 'options',
label: 'Options (for Select, Radio, Checkbox)',
type: 'multi-input',
required: false
}
]);
const defaultFormInput: FormInput = {
id: '',
label: '',
type: '',
required: false,
showLabel: true
};
const generatedFormInputs = ref<FormInput[]>([]);
const handleFormPropertiesSubmit = (formData: Record<string, any>) => {
formTitle.value = formData.formTitle;
formDescription.value = formData.formDescription;
submitLabel.value = formData.submitLabel;
};
const handleEditRequest = (inputToEdit: FormInput) => {
// set formBuilderFieldInputs to the input values
console.log(inputToEdit);
formBuilderFieldInputs.value.forEach((input: FormInput, i) => {
// fieldtype
if (input.id === 'fieldType') {
input.defaultValue = inputToEdit.type;
}
// fieldlabel
if (input.id === 'fieldLabel') {
input.defaultValue = inputToEdit.label;
}
// fieldid
if (input.id === 'fieldID') {
input.defaultValue = inputToEdit.id;
}
// placeholder
if (input.id === 'placeholder') {
input.defaultValue = inputToEdit.placeholder;
}
// required
if (input.id === 'required') {
input.defaultValue = inputToEdit.required ? ['true'] : [];
}
// showlabel
if (input.id === 'showLabel') {
input.defaultValue = inputToEdit.showLabel ? ['true'] : [];
}
// defaultvalue
if (input.id === 'defaultValue') {
input.defaultValue = inputToEdit.defaultValue;
}
// min
if (input.id === 'min') {
input.defaultValue = inputToEdit.min;
}
// max
if (input.id === 'max') {
input.defaultValue = inputToEdit.max;
}
// options
if (input.id === 'options') {
input.defaultValue = inputToEdit.options ? inputToEdit.options.map(opt => opt.value) : [];
}
});
};
const handleDeleteRequest = (inputID: string) => {
const index = generatedFormInputs.value.findIndex(input => input.id === inputID);
if (index > -1) {
generatedFormInputs.value.splice(index, 1);
}
};
const handleMoveUp = (inputID: string) => {
const index = generatedFormInputs.value.findIndex(input => input.id === inputID);
if (index > 0) {
const temp = generatedFormInputs.value[index];
generatedFormInputs.value[index] = generatedFormInputs.value[index - 1];
generatedFormInputs.value[index - 1] = temp;
}
};
const handleMoveDown = (inputID: string) => {
const index = generatedFormInputs.value.findIndex(input => input.id === inputID);
if (index < generatedFormInputs.value.length - 1) {
const temp = generatedFormInputs.value[index];
generatedFormInputs.value[index] = generatedFormInputs.value[index + 1];
generatedFormInputs.value[index + 1] = temp;
}
};
const handleFormBuilderSubmit = (formData: Record<string, any>) => {
const newField: FormInput = {
id: formData.fieldID,
label: formData.fieldLabel,
type: formData.fieldType,
placeholder: formData.placeholder || undefined,
required: formData.required && formData.required.includes('true'),
showLabel: formData.showLabel && formData.showLabel.includes('true'),
defaultValue: formData.defaultValue || undefined,
min: formData.min || undefined,
max: formData.max || undefined,
options: formData.options ? formData.options.map((opt: string) => ({ label: opt, value: opt })) : undefined
};
// if field id already exists, replace the previous field otherwise add the new field
const existingFieldIndex = generatedFormInputs.value.findIndex(field => field.id === newField.id);
if (existingFieldIndex > -1) {
generatedFormInputs.value.splice(existingFieldIndex, 1, newField);
} else {
generatedFormInputs.value.push(newField);
}
// Reset form builder fields
resetBuilderFields();
};
function resetBuilderFields() {
formBuilderFieldInputs.value.forEach((input: FormInput, i) => {
// fieldtype
if (input.id === 'fieldType') {
input.defaultValue = defaultFormInput.type;
}
// fieldlabel
if (input.id === 'fieldLabel') {
input.defaultValue = defaultFormInput.label;
}
// fieldid
if (input.id === 'fieldID') {
input.defaultValue = defaultFormInput.id;
}
// placeholder
if (input.id === 'placeholder') {
input.defaultValue = defaultFormInput.placeholder;
}
// required
if (input.id === 'required') {
input.defaultValue = defaultFormInput.required ? ['true'] : [];
}
// showlabel
if (input.id === 'showLabel') {
input.defaultValue = defaultFormInput.showLabel ? ['true'] : [];
}
// defaultvalue
if (input.id === 'defaultValue') {
input.defaultValue = defaultFormInput.defaultValue;
}
// min
if (input.id === 'min') {
input.defaultValue = defaultFormInput.min;
}
// max
if (input.id === 'max') {
input.defaultValue = defaultFormInput.max;
}
// options
if (input.id === 'options') {
input.defaultValue = defaultFormInput.options ? defaultFormInput.options.map(opt => opt.value) : [];
}
});
}
</script>
<style scoped>
/* Add any additional styling if needed */
</style>