Enhance Form Builder and Management Features
- Updated the form builder to allow users to create new forms with additional fields for category and group, improving organization and usability. - Introduced an empty state in the form builder to guide users in creating new forms. - Enhanced the management page with new filters for category and group, allowing for better form organization and retrieval. - Updated the database schema to include new fields for form category, tags, and group, along with corresponding API adjustments for form creation and updates. - Improved the user interface with better handling of form descriptions and added visual indicators for categories and groups in the forms table.
This commit is contained in:
parent
8f84b00a9e
commit
c43d9b6849
@ -7,7 +7,7 @@
|
||||
<!-- Left section - Logo and navigation -->
|
||||
<div class="flex items-center gap-4">
|
||||
<Icon
|
||||
@click="navigateTo('/', { external: true })"
|
||||
@click="navigateTo('/form-builder/manage')"
|
||||
name="ph:arrow-circle-left-duotone"
|
||||
class="cursor-pointer w-6 h-6 hover:text-gray-600 text-gray-500"
|
||||
/>
|
||||
@ -91,17 +91,13 @@
|
||||
<Icon name="material-symbols:code" class="mr-2 w-4 h-4" />
|
||||
<span>Form Settings</span>
|
||||
</button>
|
||||
<button @click="navigateToManage(); showDropdown = false" class="w-full text-left px-4 py-2 hover:bg-gray-100 flex items-center">
|
||||
<Icon name="material-symbols:settings" class="mr-2 w-4 h-4" />
|
||||
<span>Manage Forms</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<div class="flex-1 flex overflow-hidden">
|
||||
<div v-if="hasCurrentForm" class="flex-1 flex overflow-hidden">
|
||||
<!-- Left Panel - Components Sidebar -->
|
||||
<div
|
||||
v-if="!isPreview"
|
||||
@ -553,7 +549,63 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty state - No form selected -->
|
||||
<div v-else class="flex-1 flex items-center justify-center bg-gray-50">
|
||||
<div class="text-center p-8 max-w-md">
|
||||
<Icon name="material-symbols:description" class="w-16 h-16 mx-auto mb-4 text-gray-400" />
|
||||
<h2 class="text-xl font-semibold text-gray-800 mb-2">Create a New Form</h2>
|
||||
<p class="text-gray-600 mb-6">Get started by creating a new form or navigate back to manage your existing forms.</p>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div class="mb-4">
|
||||
<FormKit
|
||||
v-model="newFormName"
|
||||
type="text"
|
||||
label="Form Name"
|
||||
placeholder="Enter a name for your new form"
|
||||
validation="required"
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
v-model="newFormDescription"
|
||||
type="textarea"
|
||||
label="Description (Optional)"
|
||||
placeholder="Enter a description"
|
||||
:rows="3"
|
||||
/>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<FormKit
|
||||
v-model="newFormCategory"
|
||||
type="select"
|
||||
label="Category (Optional)"
|
||||
placeholder="Select a category"
|
||||
:options="categoryOptions"
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
v-model="newFormGroup"
|
||||
type="text"
|
||||
label="Group (Optional)"
|
||||
placeholder="Enter a group name"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center gap-4">
|
||||
<RsButton @click="createNewForm" variant="primary" :disabled="!newFormName.trim()">
|
||||
<Icon name="material-symbols:add" class="mr-1" />
|
||||
Create Form
|
||||
</RsButton>
|
||||
|
||||
<RsButton @click="navigateToManage" variant="tertiary">
|
||||
<Icon name="material-symbols:arrow-back" class="mr-1" />
|
||||
Back to Forms
|
||||
</RsButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Form Settings Modal -->
|
||||
<RsModal v-model="showFormSettings" title="Form Settings & Scripts" size="xl" position="center">
|
||||
@ -577,6 +629,23 @@
|
||||
help="Brief description of what this form is for"
|
||||
rows="3"
|
||||
/>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<FormKit
|
||||
type="select"
|
||||
label="Category"
|
||||
v-model="formStore.formCategory"
|
||||
:options="categoryOptions"
|
||||
help="Categorize your form for better organization"
|
||||
/>
|
||||
|
||||
<FormKit
|
||||
type="text"
|
||||
label="Group"
|
||||
v-model="formStore.formGroup"
|
||||
help="Group related forms together"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1067,6 +1136,13 @@ const router = useRouter();
|
||||
const formStore = useFormBuilderStore();
|
||||
let toast;
|
||||
|
||||
// Track if we're creating a new form
|
||||
const isCreatingForm = ref(false);
|
||||
const newFormName = ref('');
|
||||
const newFormDescription = ref('');
|
||||
const newFormCategory = ref('');
|
||||
const newFormGroup = ref('');
|
||||
|
||||
// Try to use the toast composable if available
|
||||
try {
|
||||
toast = useToast();
|
||||
@ -1166,6 +1242,30 @@ const formName = computed({
|
||||
}
|
||||
});
|
||||
|
||||
// Category options for form creation
|
||||
const categoryOptions = [
|
||||
{ label: 'Select Category', value: '' },
|
||||
{ label: 'Forms', value: 'forms' },
|
||||
{ label: 'Surveys', value: 'surveys' },
|
||||
{ label: 'Applications', value: 'applications' },
|
||||
{ label: 'Feedback', value: 'feedback' },
|
||||
{ label: 'Registration', value: 'registration' },
|
||||
{ label: 'Other', value: 'other' }
|
||||
];
|
||||
|
||||
// Computed to check if we have a current form loaded
|
||||
const hasCurrentForm = computed(() => {
|
||||
// Consider a form loaded if any of these conditions are true:
|
||||
// 1. Form has components (user has added fields)
|
||||
// 2. Form has an ID (saved form)
|
||||
// 3. Form name has been changed from default (form creation completed)
|
||||
return formStore.formComponents.length > 0 ||
|
||||
formStore.currentFormId ||
|
||||
(formStore.formName &&
|
||||
formStore.formName.trim() !== '' &&
|
||||
formStore.formName !== 'New Form');
|
||||
});
|
||||
|
||||
// Form JSON representation for developer view
|
||||
const formJson = computed(() => {
|
||||
return {
|
||||
@ -1705,6 +1805,9 @@ onMounted(async () => {
|
||||
console.error('Error loading form from ID:', error);
|
||||
toast.error(`Failed to load form: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
} else {
|
||||
// No form ID provided, ensure we start with a clean slate
|
||||
formStore.clearForm();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error initializing form builder:', error);
|
||||
@ -2199,6 +2302,54 @@ const navigateToManage = () => {
|
||||
navigationTarget.value = "/form-builder/manage";
|
||||
};
|
||||
|
||||
// Handle creating a new form
|
||||
const createNewForm = async () => {
|
||||
if (!newFormName.value.trim()) {
|
||||
toast.error('Please enter a form name');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Set the form name and description in the store
|
||||
formStore.setFormName(newFormName.value.trim());
|
||||
formStore.formDescription = newFormDescription.value.trim();
|
||||
formStore.formCategory = newFormCategory.value.trim() || null;
|
||||
formStore.formGroup = newFormGroup.value.trim() || null;
|
||||
|
||||
// Clear existing form components to start fresh
|
||||
formStore.formComponents = [];
|
||||
formStore.currentFormId = null; // This ensures we create a new form instead of updating existing
|
||||
|
||||
// Save the form to the database immediately
|
||||
const savedForm = await formStore.saveForm();
|
||||
|
||||
if (savedForm) {
|
||||
// Update URL to include the form ID
|
||||
const newPath = `/form-builder?id=${savedForm.formUUID}`;
|
||||
window.history.replaceState({}, '', newPath);
|
||||
|
||||
// Reset navigation confirmation
|
||||
navigationConfirmed.value = false;
|
||||
|
||||
// Reset form variables
|
||||
isCreatingForm.value = false;
|
||||
newFormName.value = '';
|
||||
newFormDescription.value = '';
|
||||
newFormCategory.value = '';
|
||||
newFormGroup.value = '';
|
||||
|
||||
// Show success message
|
||||
toast.success(`Form "${formStore.formName}" created and saved successfully`);
|
||||
} else {
|
||||
toast.error('Failed to save form to database. Please try again.');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error creating form:', error);
|
||||
toast.error(`Failed to create form: ${error.message || 'Unknown error'}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOptimizeLayout = () => {
|
||||
formStore.optimizeGridLayout();
|
||||
};
|
||||
|
@ -52,7 +52,7 @@
|
||||
<FormKit
|
||||
v-model="searchQuery"
|
||||
type="text"
|
||||
placeholder="Search forms..."
|
||||
placeholder="Search forms by name or description..."
|
||||
:classes="{
|
||||
outer: 'mb-0',
|
||||
wrapper: 'relative',
|
||||
@ -68,7 +68,35 @@
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<RsButton @click="clearFilters" variant="secondary" size="sm" :disabled="loading" v-if="searchQuery">
|
||||
<!-- Category Filter -->
|
||||
<div class="relative">
|
||||
<FormKit
|
||||
v-model="selectedCategory"
|
||||
type="select"
|
||||
placeholder="All Categories"
|
||||
:options="categoryOptions"
|
||||
:classes="{
|
||||
outer: 'mb-0',
|
||||
input: 'min-w-[140px] text-sm'
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Group Filter -->
|
||||
<div class="relative">
|
||||
<FormKit
|
||||
v-model="selectedGroup"
|
||||
type="select"
|
||||
placeholder="All Groups"
|
||||
:options="groupOptions"
|
||||
:classes="{
|
||||
outer: 'mb-0',
|
||||
input: 'min-w-[140px] text-sm'
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<RsButton @click="clearFilters" variant="secondary" size="sm" :disabled="loading" v-if="hasActiveFilters">
|
||||
<Icon name="material-symbols:filter-alt-off" class="mr-1" />
|
||||
Clear Filters
|
||||
</RsButton>
|
||||
@ -95,7 +123,8 @@
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Components</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Category</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Group</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Created</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Last Updated</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
|
||||
@ -103,7 +132,7 @@
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
<tr v-if="filteredForms.length === 0">
|
||||
<td colspan="6" class="px-6 py-12 text-center text-gray-500">
|
||||
<td colspan="7" class="px-6 py-12 text-center text-gray-500">
|
||||
<div class="flex flex-col items-center">
|
||||
<Icon name="material-symbols:description-outline" class="w-12 h-12 text-gray-300 mb-2" />
|
||||
<p class="text-lg font-medium mb-1">
|
||||
@ -119,11 +148,11 @@
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<RsButton v-if="searchQuery" @click="clearFilters" variant="secondary" size="sm">
|
||||
<RsButton v-if="hasActiveFilters" @click="clearFilters" variant="secondary" size="sm">
|
||||
<Icon name="material-symbols:filter-alt-off" class="mr-1" />
|
||||
Clear Filters
|
||||
</RsButton>
|
||||
<RsButton v-if="!searchQuery" @click="createNewForm" variant="primary" size="sm">
|
||||
<RsButton v-if="!hasActiveFilters" @click="createNewForm" variant="primary" size="sm">
|
||||
<Icon name="material-symbols:add" class="mr-1" />
|
||||
Create New Form
|
||||
</RsButton>
|
||||
@ -137,14 +166,34 @@
|
||||
<div class="text-sm text-gray-500">ID: {{ form.id }}</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="text-sm text-gray-700 max-w-xs truncate">
|
||||
{{ form.description || 'No description' }}
|
||||
<div class="text-sm text-gray-700 max-w-xs">
|
||||
<div
|
||||
v-if="form.description && form.description.trim()"
|
||||
class="line-clamp-2 cursor-help"
|
||||
:title="form.description.length > 100 ? form.description : ''"
|
||||
>
|
||||
{{ form.description }}
|
||||
</div>
|
||||
<div v-else class="text-gray-400 italic">
|
||||
No description
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="text-sm text-gray-700">
|
||||
{{ form.components ? form.components.length : 0 }} components
|
||||
<div v-if="form.category" class="text-sm">
|
||||
<RsBadge :variant="getCategoryColor(form.category)" size="sm">
|
||||
{{ form.category }}
|
||||
</RsBadge>
|
||||
</div>
|
||||
<div v-else class="text-sm text-gray-400 italic">No category</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div v-if="form.group" class="text-sm">
|
||||
<RsBadge variant="secondary" size="sm">
|
||||
{{ form.group }}
|
||||
</RsBadge>
|
||||
</div>
|
||||
<div v-else class="text-sm text-gray-400 italic">No group</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="text-sm text-gray-500">{{ formatDate(form.createdAt) }}</div>
|
||||
@ -385,6 +434,8 @@ try {
|
||||
|
||||
// State
|
||||
const searchQuery = ref('');
|
||||
const selectedCategory = ref('');
|
||||
const selectedGroup = ref('');
|
||||
const loading = ref(false);
|
||||
const showDeleteConfirm = ref(false);
|
||||
const formToDelete = ref(null);
|
||||
@ -398,6 +449,27 @@ const jsonContent = ref('');
|
||||
const jsonValidationMessage = ref('');
|
||||
const jsonIsValid = ref(false);
|
||||
|
||||
// Computed properties for grouping
|
||||
const categoryOptions = computed(() => {
|
||||
const categories = [...new Set(formStore.savedForms.map(form => form.category).filter(Boolean))];
|
||||
return [
|
||||
{ label: 'All Categories', value: '' },
|
||||
...categories.map(cat => ({ label: cat, value: cat }))
|
||||
];
|
||||
});
|
||||
|
||||
const groupOptions = computed(() => {
|
||||
const groups = [...new Set(formStore.savedForms.map(form => form.group).filter(Boolean))];
|
||||
return [
|
||||
{ label: 'All Groups', value: '' },
|
||||
...groups.map(group => ({ label: group, value: group }))
|
||||
];
|
||||
});
|
||||
|
||||
const hasActiveFilters = computed(() => {
|
||||
return searchQuery.value || selectedCategory.value || selectedGroup.value;
|
||||
});
|
||||
|
||||
// Filtered forms
|
||||
const filteredForms = computed(() => {
|
||||
let filtered = formStore.savedForms;
|
||||
@ -412,6 +484,16 @@ const filteredForms = computed(() => {
|
||||
);
|
||||
}
|
||||
|
||||
// Filter by category
|
||||
if (selectedCategory.value) {
|
||||
filtered = filtered.filter(form => form.category === selectedCategory.value);
|
||||
}
|
||||
|
||||
// Filter by group
|
||||
if (selectedGroup.value) {
|
||||
filtered = filtered.filter(form => form.group === selectedGroup.value);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
});
|
||||
|
||||
@ -552,8 +634,8 @@ onMounted(async () => {
|
||||
await loadForms();
|
||||
});
|
||||
|
||||
// Watch for changes in search and reload forms
|
||||
watch([searchQuery], () => {
|
||||
// Watch for changes in search and filters
|
||||
watch([searchQuery, selectedCategory, selectedGroup], () => {
|
||||
// Debounce the search to avoid too many API calls
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(() => {
|
||||
@ -566,9 +648,24 @@ let searchTimeout = null;
|
||||
// Clear all filters
|
||||
const clearFilters = () => {
|
||||
searchQuery.value = '';
|
||||
selectedCategory.value = '';
|
||||
selectedGroup.value = '';
|
||||
// loadForms will be called automatically by the watcher
|
||||
};
|
||||
|
||||
// Helper function to get category color
|
||||
const getCategoryColor = (category) => {
|
||||
const colors = {
|
||||
'forms': 'primary',
|
||||
'surveys': 'success',
|
||||
'applications': 'info',
|
||||
'feedback': 'warning',
|
||||
'registration': 'danger',
|
||||
'default': 'secondary'
|
||||
};
|
||||
return colors[category?.toLowerCase()] || colors.default;
|
||||
};
|
||||
|
||||
// Clean up the search timeout on component unmount
|
||||
onUnmounted(() => {
|
||||
clearTimeout(searchTimeout);
|
||||
@ -835,4 +932,13 @@ button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Line clamp utility for description text */
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
|
@ -3003,10 +3003,6 @@ const sendToBack = () => {
|
||||
<Icon name="material-symbols:tune" class="mr-2 w-4 h-4" />
|
||||
<span>Process Settings</span>
|
||||
</button>
|
||||
<button @click="confirmNavigation('/process-builder/manage'); showDropdown = false" class="w-full text-left px-4 py-2 hover:bg-gray-100 flex items-center">
|
||||
<Icon name="material-symbols:settings" class="mr-2 w-4 h-4" />
|
||||
<span>Manage Processes</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -208,6 +208,28 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"formCategory": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"formTags": {
|
||||
"type": [
|
||||
"number",
|
||||
"string",
|
||||
"boolean",
|
||||
"object",
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"formGroup": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"creator": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
9
prisma/migrations/20241201_add_form_grouping.sql
Normal file
9
prisma/migrations/20241201_add_form_grouping.sql
Normal file
@ -0,0 +1,9 @@
|
||||
-- Add form grouping fields
|
||||
ALTER TABLE `form` ADD COLUMN `formCategory` VARCHAR(100) NULL;
|
||||
ALTER TABLE `form` ADD COLUMN `formTags` JSON NULL;
|
||||
ALTER TABLE `form` ADD COLUMN `formGroup` VARCHAR(100) NULL;
|
||||
|
||||
-- Add indexes for better performance
|
||||
CREATE INDEX `IDX_form_category` ON `form`(`formCategory`);
|
||||
CREATE INDEX `IDX_form_group` ON `form`(`formGroup`);
|
||||
CREATE INDEX `IDX_form_status` ON `form`(`formStatus`);
|
@ -65,11 +65,17 @@ model form {
|
||||
formEvents Json?
|
||||
scriptMode String? @default("safe") @db.VarChar(20)
|
||||
submitButton Json?
|
||||
formCategory String? @db.VarChar(100)
|
||||
formTags Json?
|
||||
formGroup String? @db.VarChar(100)
|
||||
creator user? @relation(fields: [formCreatedBy], references: [userID])
|
||||
formHistory formHistory[]
|
||||
task task[]
|
||||
|
||||
@@index([formCreatedBy], map: "FK_form_creator")
|
||||
@@index([formCategory], map: "IDX_form_category")
|
||||
@@index([formGroup], map: "IDX_form_group")
|
||||
@@index([formStatus], map: "IDX_form_status")
|
||||
}
|
||||
|
||||
model formHistory {
|
||||
|
@ -61,7 +61,7 @@ export default defineEventHandler(async (event) => {
|
||||
submitButton: currentForm.submitButton,
|
||||
versionNumber: nextVersionNumber,
|
||||
changeDescription: body.changeDescription || null,
|
||||
savedBy: body.savedBy || currentForm.formCreatedBy,
|
||||
savedBy: event.context.user?.userID || currentForm.formCreatedBy,
|
||||
savedDate: currentForm.formModifiedDate || currentForm.formCreatedDate
|
||||
}
|
||||
});
|
||||
@ -110,6 +110,18 @@ export default defineEventHandler(async (event) => {
|
||||
updateData.submitButton = body.submitButton;
|
||||
}
|
||||
|
||||
if (body.formCategory !== undefined) {
|
||||
updateData.formCategory = body.formCategory && body.formCategory.trim() ? body.formCategory.trim() : null;
|
||||
}
|
||||
|
||||
if (body.formTags !== undefined) {
|
||||
updateData.formTags = body.formTags;
|
||||
}
|
||||
|
||||
if (body.formGroup !== undefined) {
|
||||
updateData.formGroup = body.formGroup && body.formGroup.trim() ? body.formGroup.trim() : null;
|
||||
}
|
||||
|
||||
// Try to update by UUID first
|
||||
let form;
|
||||
try {
|
||||
|
@ -25,12 +25,15 @@ export default defineEventHandler(async (event) => {
|
||||
formDescription: body.formDescription || null,
|
||||
formComponents: body.components || [],
|
||||
formStatus: body.status || 'active',
|
||||
formCreatedBy: body.createdBy || null, // In a real app, this would come from the authenticated user
|
||||
customScript: body.customScript || null,
|
||||
customCSS: body.customCSS || null,
|
||||
formEvents: body.formEvents || null,
|
||||
scriptMode: body.scriptMode || 'safe',
|
||||
submitButton: body.submitButton || null
|
||||
submitButton: body.submitButton || null,
|
||||
formCategory: body.formCategory && body.formCategory.trim() ? body.formCategory.trim() : null,
|
||||
formTags: body.formTags || null,
|
||||
formGroup: body.formGroup && body.formGroup.trim() ? body.formGroup.trim() : null,
|
||||
formCreatedBy: event.context.user?.userID || undefined,
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -21,6 +21,9 @@ export default defineEventHandler(async (event) => {
|
||||
formStatus: true,
|
||||
formCreatedDate: true,
|
||||
formModifiedDate: true,
|
||||
formCategory: true,
|
||||
formGroup: true,
|
||||
formTags: true,
|
||||
// Don't include the full components data to keep response size small
|
||||
creator: {
|
||||
select: {
|
||||
|
@ -39,6 +39,11 @@ export const useFormBuilderStore = defineStore('formBuilder', {
|
||||
|
||||
// Form history tracking
|
||||
lastChangeDescription: null,
|
||||
|
||||
// Form grouping
|
||||
formCategory: '',
|
||||
formTags: [],
|
||||
formGroup: '',
|
||||
}),
|
||||
|
||||
getters: {
|
||||
@ -652,6 +657,9 @@ export const useFormBuilderStore = defineStore('formBuilder', {
|
||||
name: form.formName,
|
||||
description: form.formDescription || '',
|
||||
components: form.formComponents || [],
|
||||
category: form.formCategory || '',
|
||||
group: form.formGroup || '',
|
||||
tags: form.formTags || [],
|
||||
createdAt: form.formCreatedDate,
|
||||
updatedAt: form.formModifiedDate
|
||||
}));
|
||||
@ -679,9 +687,12 @@ export const useFormBuilderStore = defineStore('formBuilder', {
|
||||
formEvents: this.formEvents,
|
||||
scriptMode: this.scriptMode,
|
||||
submitButton: this.submitButton,
|
||||
// Add user info and change description for history tracking
|
||||
savedBy: 1, // TODO: Get from authenticated user
|
||||
changeDescription: this.lastChangeDescription || null
|
||||
formCategory: this.formCategory,
|
||||
formTags: this.formTags,
|
||||
formGroup: this.formGroup,
|
||||
// Add change description for history tracking
|
||||
changeDescription: this.lastChangeDescription || null,
|
||||
// Note: savedBy will be handled by the server using the authenticated user from auth middleware
|
||||
};
|
||||
|
||||
// Determine if this is a new form or an update
|
||||
@ -760,6 +771,11 @@ export const useFormBuilderStore = defineStore('formBuilder', {
|
||||
this.formDescription = result.form.formDescription || '';
|
||||
this.currentFormId = result.form.formUUID;
|
||||
|
||||
// Load grouping data
|
||||
this.formCategory = result.form.formCategory || '';
|
||||
this.formTags = result.form.formTags || [];
|
||||
this.formGroup = result.form.formGroup || '';
|
||||
|
||||
// Load custom scripts and settings
|
||||
this.formCustomScript = result.form.customScript || '';
|
||||
this.formCustomCSS = result.form.customCSS || '';
|
||||
@ -840,6 +856,11 @@ export const useFormBuilderStore = defineStore('formBuilder', {
|
||||
this.currentFormId = null;
|
||||
this.hasUnsavedChanges = false;
|
||||
|
||||
// Reset grouping fields
|
||||
this.formCategory = '';
|
||||
this.formTags = [];
|
||||
this.formGroup = '';
|
||||
|
||||
// Reset custom scripts and settings
|
||||
this.formCustomScript = '';
|
||||
this.formCustomCSS = '';
|
||||
|
Loading…
x
Reference in New Issue
Block a user