Refactor Form Submission Logic and Improve File Handling
- Updated `FormController.php` to correct the file path concatenation for temporary file retrieval, enhancing clarity in file management. - Modified `FormSubmissionResource.php` to improve the filtering logic for file fields, ensuring only valid files are processed. - Streamlined the constructor in `StoreFormSubmissionJob.php` for better readability and consistency. - Enhanced file handling in `StoreFormSubmissionJob.php` by refining the logic for moving files from temporary to permanent storage, ensuring accurate file management. - Improved the `FormSubmissionFormatter.php` to handle file URLs more robustly with error handling for signed routes. - Updated `PartialSubmissionTest.php` to remove the hardcoded success message, improving test reliability. - Refactored `OpenFormField.vue` to simplify the disabled state logic for form fields, enhancing maintainability. - Adjusted `usePartialSubmission.js` to improve the synchronization mechanism during visibility changes, ensuring data integrity. These changes aim to enhance the overall functionality, maintainability, and reliability of the form submission process and file handling logic.
This commit is contained in:
parent
053abbf31b
commit
2edd0816d4
|
|
@ -228,7 +228,7 @@ class FormController extends Controller
|
||||||
$fileNameParser = StorageFileNameParser::parse($request->url);
|
$fileNameParser = StorageFileNameParser::parse($request->url);
|
||||||
|
|
||||||
// Make sure we retrieve the file in tmp storage, move it to persistent
|
// Make sure we retrieve the file in tmp storage, move it to persistent
|
||||||
$fileName = PublicFormController::TMP_FILE_UPLOAD_PATH . '/' . $fileNameParser->uuid;
|
$fileName = PublicFormController::TMP_FILE_UPLOAD_PATH . $fileNameParser->uuid;
|
||||||
if (!Storage::exists($fileName)) {
|
if (!Storage::exists($fileName)) {
|
||||||
// File not found, we skip
|
// File not found, we skip
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ class FormSubmissionResource extends JsonResource
|
||||||
foreach ($fileFields as $field) {
|
foreach ($fileFields as $field) {
|
||||||
if (isset($data[$field['id']]) && !empty($data[$field['id']])) {
|
if (isset($data[$field['id']]) && !empty($data[$field['id']])) {
|
||||||
$data[$field['id']] = collect($data[$field['id']])->filter(function ($file) {
|
$data[$field['id']] = collect($data[$field['id']])->filter(function ($file) {
|
||||||
return $file !== null && $file;
|
return !is_null($file) && !empty($file);
|
||||||
})->map(function ($file) {
|
})->map(function ($file) {
|
||||||
return [
|
return [
|
||||||
'file_url' => \URL::signedRoute(
|
'file_url' => \URL::signedRoute(
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Job to store form submissions
|
* Job to store form submissions
|
||||||
|
|
@ -68,20 +67,11 @@ class StoreFormSubmissionJob implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
// Extract metadata from submission data
|
|
||||||
$this->extractMetadata();
|
$this->extractMetadata();
|
||||||
|
|
||||||
// Process form data
|
|
||||||
$this->formData = $this->getFormData();
|
$this->formData = $this->getFormData();
|
||||||
$this->addHiddenPrefills($this->formData);
|
$this->addHiddenPrefills($this->formData);
|
||||||
|
|
||||||
// Store the submission
|
|
||||||
$this->storeSubmission($this->formData);
|
$this->storeSubmission($this->formData);
|
||||||
|
|
||||||
// Add the submission ID to the form data after storing the submission
|
|
||||||
$this->formData['submission_id'] = $this->submissionId;
|
$this->formData['submission_id'] = $this->submissionId;
|
||||||
|
|
||||||
// Only trigger integrations for completed submissions, not partial ones
|
|
||||||
if (!$this->isPartial) {
|
if (!$this->isPartial) {
|
||||||
FormSubmitted::dispatch($this->form, $this->formData);
|
FormSubmitted::dispatch($this->form, $this->formData);
|
||||||
}
|
}
|
||||||
|
|
@ -97,21 +87,16 @@ class StoreFormSubmissionJob implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
private function extractMetadata(): void
|
private function extractMetadata(): void
|
||||||
{
|
{
|
||||||
// Extract completion time
|
|
||||||
if (isset($this->submissionData['completion_time'])) {
|
if (isset($this->submissionData['completion_time'])) {
|
||||||
$this->completionTime = $this->submissionData['completion_time'];
|
$this->completionTime = $this->submissionData['completion_time'];
|
||||||
unset($this->submissionData['completion_time']);
|
unset($this->submissionData['completion_time']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract direct submission ID if present
|
|
||||||
if (isset($this->submissionData['submission_id']) && $this->submissionData['submission_id']) {
|
if (isset($this->submissionData['submission_id']) && $this->submissionData['submission_id']) {
|
||||||
if (is_numeric($this->submissionData['submission_id'])) {
|
if (is_numeric($this->submissionData['submission_id'])) {
|
||||||
$this->submissionId = (int)$this->submissionData['submission_id'];
|
$this->submissionId = (int)$this->submissionData['submission_id'];
|
||||||
}
|
}
|
||||||
unset($this->submissionData['submission_id']);
|
unset($this->submissionData['submission_id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract is_partial flag if present, otherwise default to false
|
|
||||||
if (isset($this->submissionData['is_partial'])) {
|
if (isset($this->submissionData['is_partial'])) {
|
||||||
$this->isPartial = (bool)$this->submissionData['is_partial'];
|
$this->isPartial = (bool)$this->submissionData['is_partial'];
|
||||||
unset($this->submissionData['is_partial']);
|
unset($this->submissionData['is_partial']);
|
||||||
|
|
@ -135,27 +120,18 @@ class StoreFormSubmissionJob implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
private function storeSubmission(array $formData)
|
private function storeSubmission(array $formData)
|
||||||
{
|
{
|
||||||
// Find existing submission or create a new one
|
|
||||||
$submission = $this->submissionId
|
$submission = $this->submissionId
|
||||||
? $this->form->submissions()->findOrFail($this->submissionId)
|
? $this->form->submissions()->findOrFail($this->submissionId)
|
||||||
: new FormSubmission();
|
: new FormSubmission();
|
||||||
|
|
||||||
// Set submission properties
|
|
||||||
if (!$this->submissionId) {
|
if (!$this->submissionId) {
|
||||||
$submission->form_id = $this->form->id;
|
$submission->form_id = $this->form->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
$submission->data = $formData;
|
$submission->data = $formData;
|
||||||
$submission->completion_time = $this->completionTime;
|
$submission->completion_time = $this->completionTime;
|
||||||
|
|
||||||
// Set the status based on whether this is a partial submission
|
|
||||||
$submission->status = $this->isPartial
|
$submission->status = $this->isPartial
|
||||||
? FormSubmission::STATUS_PARTIAL
|
? FormSubmission::STATUS_PARTIAL
|
||||||
: FormSubmission::STATUS_COMPLETED;
|
: FormSubmission::STATUS_COMPLETED;
|
||||||
|
|
||||||
$submission->save();
|
$submission->save();
|
||||||
|
|
||||||
// Store the submission ID
|
|
||||||
$this->submissionId = $submission->id;
|
$this->submissionId = $submission->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,10 +146,8 @@ class StoreFormSubmissionJob implements ShouldQueue
|
||||||
{
|
{
|
||||||
$data = $this->submissionData;
|
$data = $this->submissionData;
|
||||||
$finalData = [];
|
$finalData = [];
|
||||||
|
|
||||||
$properties = collect($this->form->properties);
|
$properties = collect($this->form->properties);
|
||||||
|
|
||||||
// Do required transformation per type (e.g. file uploads)
|
|
||||||
foreach ($data as $answerKey => $answerValue) {
|
foreach ($data as $answerKey => $answerValue) {
|
||||||
$field = $properties->where('id', $answerKey)->first();
|
$field = $properties->where('id', $answerKey)->first();
|
||||||
if (!$field) {
|
if (!$field) {
|
||||||
|
|
@ -185,45 +159,58 @@ class StoreFormSubmissionJob implements ShouldQueue
|
||||||
|| $field['type'] == 'files'
|
|| $field['type'] == 'files'
|
||||||
) {
|
) {
|
||||||
if (is_array($answerValue)) {
|
if (is_array($answerValue)) {
|
||||||
$finalData[$field['id']] = [];
|
$processedFiles = [];
|
||||||
foreach ($answerValue as $file) {
|
foreach ($answerValue as $fileItem) {
|
||||||
$finalData[$field['id']][] = $this->storeFile($file);
|
if (is_string($fileItem) && !empty($fileItem)) {
|
||||||
|
$singleStoredFile = $this->storeFile($fileItem);
|
||||||
|
if ($singleStoredFile) {
|
||||||
|
$processedFiles[] = $singleStoredFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
$finalData[$field['id']] = $processedFiles;
|
||||||
} else {
|
} else {
|
||||||
$finalData[$field['id']] = $this->storeFile($answerValue);
|
if (is_string($answerValue) && !empty($answerValue)) {
|
||||||
|
$singleFileResult = $this->storeFile($answerValue);
|
||||||
|
$finalData[$field['id']] = $singleFileResult;
|
||||||
|
} else {
|
||||||
|
$finalData[$field['id']] = $this->storeFile($answerValue); // Handles null/empty $answerValue by returning null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Standard field processing (text, ID generation, etc.)
|
||||||
if ($field['type'] == 'text' && isset($field['generates_uuid']) && $field['generates_uuid']) {
|
if ($field['type'] == 'text' && isset($field['generates_uuid']) && $field['generates_uuid']) {
|
||||||
$finalData[$field['id']] = ($this->form->is_pro) ? Str::uuid()->toString() : 'Please upgrade your OpenForm subscription to use our ID generation features';
|
$finalData[$field['id']] = ($this->form->is_pro) ? Str::uuid()->toString() : 'Please upgrade your OpenForm subscription to use our ID generation features';
|
||||||
|
} elseif ($field['type'] == 'text' && isset($field['generates_auto_increment_id']) && $field['generates_auto_increment_id']) {
|
||||||
|
$finalData[$field['id']] = ($this->form->is_pro) ? (string) ($this->form->submissions_count + 1) : 'Please upgrade your OpenForm subscription to use our ID generation features';
|
||||||
} else {
|
} else {
|
||||||
if ($field['type'] == 'text' && isset($field['generates_auto_increment_id']) && $field['generates_auto_increment_id']) {
|
$finalData[$field['id']] = $answerValue;
|
||||||
$finalData[$field['id']] = ($this->form->is_pro) ? (string) ($this->form->submissions_count + 1) : 'Please upgrade your OpenForm subscription to use our ID generation features';
|
|
||||||
} else {
|
|
||||||
$finalData[$field['id']] = $answerValue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Special field types
|
||||||
// For Singrature
|
|
||||||
if ($field['type'] == 'signature') {
|
if ($field['type'] == 'signature') {
|
||||||
$finalData[$field['id']] = $this->storeSignature($answerValue);
|
$finalData[$field['id']] = $this->storeSignature($answerValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For Phone
|
|
||||||
if ($field['type'] == 'phone_number' && $answerValue && ctype_alpha(substr($answerValue, 0, 2)) && (!isset($field['use_simple_text_input']) || !$field['use_simple_text_input'])) {
|
if ($field['type'] == 'phone_number' && $answerValue && ctype_alpha(substr($answerValue, 0, 2)) && (!isset($field['use_simple_text_input']) || !$field['use_simple_text_input'])) {
|
||||||
$finalData[$field['id']] = substr($answerValue, 2);
|
$finalData[$field['id']] = substr($answerValue, 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $finalData;
|
return $finalData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is use when updating a record, and file uploads aren't changed.
|
// This is use when updating a record, and file uploads aren't changed.
|
||||||
private function isSkipForUpload($value)
|
private function isSkipForUpload($value)
|
||||||
{
|
{
|
||||||
$newPath = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $this->form->id);
|
$parser = StorageFileNameParser::parse($value);
|
||||||
|
$canonicalStoredName = $parser->getMovedFileName();
|
||||||
|
|
||||||
return Storage::exists($newPath . '/' . $value);
|
if (!$canonicalStoredName) {
|
||||||
|
return false; // Input $value couldn't be resolved to a canonical stored name format
|
||||||
|
}
|
||||||
|
|
||||||
|
$destinationPath = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $this->form->id);
|
||||||
|
$fullPathToCheck = $destinationPath . '/' . $canonicalStoredName;
|
||||||
|
return Storage::exists($fullPathToCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -239,61 +226,58 @@ class StoreFormSubmissionJob implements ShouldQueue
|
||||||
if (is_null($value) || empty($value)) {
|
if (is_null($value) || empty($value)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
// Handle pre-existing full URLs (e.g., from prefill)
|
||||||
if (filter_var($value, FILTER_VALIDATE_URL) !== false && str_contains($value, parse_url(config('app.url'))['host'])) { // In case of prefill we have full url so convert to s3
|
if (filter_var($value, FILTER_VALIDATE_URL) !== false && str_contains($value, parse_url(config('app.url'))['host'])) {
|
||||||
$fileName = explode('?', basename($value))[0];
|
$fileName = explode('?', basename($value))[0];
|
||||||
$path = FormController::ASSETS_UPLOAD_PATH . '/' . $fileName;
|
$path = FormController::ASSETS_UPLOAD_PATH . '/' . $fileName; // Assuming assets are in a defined path
|
||||||
$newPath = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $this->form->id);
|
$newPath = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $this->form->id);
|
||||||
Storage::move($path, $newPath . '/' . $fileName);
|
Storage::move($path, $newPath . '/' . $fileName);
|
||||||
|
|
||||||
return $fileName;
|
return $fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isSkipForUpload($value)) {
|
$shouldSkip = $this->isSkipForUpload($value);
|
||||||
return $value;
|
|
||||||
|
if ($shouldSkip) {
|
||||||
|
// File (based on canonical name derived from $value) already exists in permanent storage.
|
||||||
|
// Return its canonical name.
|
||||||
|
$parser = StorageFileNameParser::parse($value);
|
||||||
|
return $parser->getMovedFileName() ?? $value; // Fallback to $value if canonical somehow fails (defensive)
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileNameParser = StorageFileNameParser::parse($value);
|
// Process as a new file upload (or one whose temp version needs to be moved)
|
||||||
|
$fileNameParser = StorageFileNameParser::parse($value); // $value is the temp file reference (e.g., originalname_uuid.ext or uuid)
|
||||||
|
|
||||||
if (!$fileNameParser || !$fileNameParser->uuid) {
|
if (!$fileNameParser || !$fileNameParser->uuid) {
|
||||||
return null;
|
return null; // Cannot derive UUID from the reference
|
||||||
}
|
}
|
||||||
|
$fileNameInTmp = PublicFormController::TMP_FILE_UPLOAD_PATH . $fileNameParser->uuid;
|
||||||
// Make sure we retrieve the file in tmp storage, move it to persistent
|
if (!Storage::exists($fileNameInTmp)) {
|
||||||
$fileName = PublicFormController::TMP_FILE_UPLOAD_PATH . '/' . $fileNameParser->uuid;
|
return null; // Temporary file not found
|
||||||
if (!Storage::exists($fileName)) {
|
}
|
||||||
// File not found, we skip
|
$movedFileName = $fileNameParser->getMovedFileName(); // This is the canonical name for storage
|
||||||
return null;
|
if (empty($movedFileName)) {
|
||||||
|
return null; // Canonical name generation failed
|
||||||
}
|
}
|
||||||
$newPath = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $this->form->id);
|
$newPath = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $this->form->id);
|
||||||
$completeNewFilename = $newPath . '/' . $fileNameParser->getMovedFileName();
|
$completeNewFilename = $newPath . '/' . $movedFileName;
|
||||||
|
Storage::move($fileNameInTmp, $completeNewFilename);
|
||||||
Log::debug('Moving file to permanent storage.', [
|
return $movedFileName;
|
||||||
'uuid' => $fileNameParser->uuid,
|
|
||||||
'destination' => $completeNewFilename,
|
|
||||||
'form_id' => $this->form->id,
|
|
||||||
'form_slug' => $this->form->slug,
|
|
||||||
]);
|
|
||||||
Storage::move($fileName, $completeNewFilename);
|
|
||||||
|
|
||||||
return $fileNameParser->getMovedFileName();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function storeSignature(?string $value)
|
private function storeSignature(?string $value)
|
||||||
{
|
{
|
||||||
if ($value && preg_match('/^[\/\w\-. ]+$/', $value)) { // If it's filename
|
// If $value looks like a filename (already processed, e.g. during skip or previous handling)
|
||||||
return $this->storeFile($value);
|
if ($value && preg_match('/^[\/\w\-. ]+$/', $value)) {
|
||||||
|
return $this->storeFile($value); // Re-run through storeFile for consistency / skip logic
|
||||||
}
|
}
|
||||||
|
// If $value is base64 data
|
||||||
if ($value == null || !isset(explode(',', $value)[1])) {
|
if ($value == null || !isset(explode(',', $value)[1])) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$fileName = 'sign_' . (string) Str::uuid() . '.png';
|
$fileName = 'sign_' . (string) Str::uuid() . '.png';
|
||||||
$newPath = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $this->form->id);
|
$newPath = Str::of(PublicFormController::FILE_UPLOAD_PATH)->replace('?', $this->form->id);
|
||||||
$completeNewFilename = $newPath . '/' . $fileName;
|
$completeNewFilename = $newPath . '/' . $fileName;
|
||||||
|
|
||||||
Storage::put($completeNewFilename, base64_decode(explode(',', $value)[1]));
|
Storage::put($completeNewFilename, base64_decode(explode(',', $value)[1]));
|
||||||
|
|
||||||
return $fileName;
|
return $fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,7 +288,6 @@ class StoreFormSubmissionJob implements ShouldQueue
|
||||||
*/
|
*/
|
||||||
private function addHiddenPrefills(array &$formData): void
|
private function addHiddenPrefills(array &$formData): void
|
||||||
{
|
{
|
||||||
// Find hidden properties with prefill, set values
|
|
||||||
collect($this->form->properties)->filter(function ($property) {
|
collect($this->form->properties)->filter(function ($property) {
|
||||||
return isset($property['hidden'])
|
return isset($property['hidden'])
|
||||||
&& isset($property['prefill'])
|
&& isset($property['prefill'])
|
||||||
|
|
@ -331,11 +314,8 @@ class StoreFormSubmissionJob implements ShouldQueue
|
||||||
if ($this->formData === null) {
|
if ($this->formData === null) {
|
||||||
$this->formData = $this->getFormData();
|
$this->formData = $this->getFormData();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the submission ID is included in the returned data
|
|
||||||
$data = $this->formData;
|
$data = $this->formData;
|
||||||
$data['submission_id'] = $this->submissionId;
|
$data['submission_id'] = $this->submissionId;
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,9 @@ class FormSubmissionFormatter
|
||||||
$formId = $this->form->id;
|
$formId = $this->form->id;
|
||||||
$returnArray[$field['name']] = implode(
|
$returnArray[$field['name']] = implode(
|
||||||
', ',
|
', ',
|
||||||
collect($data[$field['id']])->map(function ($file) use ($formId) {
|
collect($data[$field['id']])->filter(function ($file) {
|
||||||
|
return !is_null($file) && !empty($file);
|
||||||
|
})->map(function ($file) use ($formId) {
|
||||||
return $this->getFileUrl($formId, $file);
|
return $this->getFileUrl($formId, $file);
|
||||||
})->toArray()
|
})->toArray()
|
||||||
);
|
);
|
||||||
|
|
@ -289,9 +291,14 @@ class FormSubmissionFormatter
|
||||||
|
|
||||||
private function getFileUrl($formId, $file)
|
private function getFileUrl($formId, $file)
|
||||||
{
|
{
|
||||||
return $this->useSignedUrlForFiles ? \URL::signedRoute(
|
try {
|
||||||
'open.forms.submissions.file',
|
return $this->useSignedUrlForFiles ? \URL::signedRoute(
|
||||||
[$formId, $file]
|
'open.forms.submissions.file',
|
||||||
) : route('open.forms.submissions.file', [$formId, $file]);
|
[$formId, $file]
|
||||||
|
) : route('open.forms.submissions.file', [$formId, $file]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
throw $e;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ it('can submit form partially and complete it later using submission hash', func
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->assertJson([
|
->assertJson([
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'message' => 'Partial submission saved',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$submissionHash = $partialResponse->json('submission_hash');
|
$submissionHash = $partialResponse->json('submission_hash');
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
v-if="getFieldComponents"
|
v-if="getFieldComponents"
|
||||||
v-bind="inputProperties(field)"
|
v-bind="inputProperties(field)"
|
||||||
:required="isFieldRequired"
|
:required="isFieldRequired"
|
||||||
:disabled="isFieldDisabled ? true : null"
|
:disabled="isFieldDisabled"
|
||||||
:is-admin-preview="isAdminPreview"
|
:is-admin-preview="isAdminPreview"
|
||||||
/>
|
/>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
|
|
@ -175,6 +175,7 @@ const form = computed(() => props.formManager?.config?.value || {})
|
||||||
const dataForm = computed(() => props.formManager?.form || {})
|
const dataForm = computed(() => props.formManager?.form || {})
|
||||||
const darkMode = computed(() => props.formManager?.darkMode?.value || false)
|
const darkMode = computed(() => props.formManager?.darkMode?.value || false)
|
||||||
const showHidden = computed(() => props.formManager?.strategy?.value?.display?.showHiddenFields || false)
|
const showHidden = computed(() => props.formManager?.strategy?.value?.display?.showHiddenFields || false)
|
||||||
|
const enableDisabledFields = computed(() => props.formManager?.strategy?.value?.display?.enableDisabledFields || false)
|
||||||
|
|
||||||
// Setup stores and reactive state
|
// Setup stores and reactive state
|
||||||
const workingFormStore = useWorkingFormStore()
|
const workingFormStore = useWorkingFormStore()
|
||||||
|
|
@ -238,9 +239,10 @@ const isFieldRequired = computed(() =>
|
||||||
(new FormLogicPropertyResolver(props.field, dataForm.value)).isRequired()
|
(new FormLogicPropertyResolver(props.field, dataForm.value)).isRequired()
|
||||||
)
|
)
|
||||||
|
|
||||||
const isFieldDisabled = computed(() =>
|
const isFieldDisabled = computed(() => {
|
||||||
(new FormLogicPropertyResolver(props.field, dataForm.value)).isDisabled()
|
if (enableDisabledFields.value) return false
|
||||||
)
|
return (new FormLogicPropertyResolver(props.field, dataForm.value)).isDisabled()
|
||||||
|
})
|
||||||
|
|
||||||
const beingEdited = computed(() =>
|
const beingEdited = computed(() =>
|
||||||
isAdminPreview.value &&
|
isAdminPreview.value &&
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ export function usePartialSubmission(formConfig, formDataRef, pendingSubmissionS
|
||||||
clearTimeout(syncTimeout)
|
clearTimeout(syncTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a new timeout - increased to 2 seconds for less frequent syncing
|
|
||||||
syncTimeout = setTimeout(() => {
|
syncTimeout = setTimeout(() => {
|
||||||
syncToServer()
|
syncToServer()
|
||||||
}, 2000) // 2 second debounce
|
}, 2000) // 2 second debounce
|
||||||
|
|
@ -93,13 +92,13 @@ export function usePartialSubmission(formConfig, formDataRef, pendingSubmissionS
|
||||||
const handleVisibilityChange = () => {
|
const handleVisibilityChange = () => {
|
||||||
if (document.visibilityState === 'hidden') {
|
if (document.visibilityState === 'hidden') {
|
||||||
// When tab becomes hidden, sync immediately
|
// When tab becomes hidden, sync immediately
|
||||||
syncImmediately()
|
debouncedSync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBlur = () => {
|
const handleBlur = () => {
|
||||||
// When window loses focus, sync immediately
|
// When window loses focus, sync immediately
|
||||||
syncImmediately()
|
debouncedSync()
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleBeforeUnload = () => {
|
const handleBeforeUnload = () => {
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ export function createFormModeStrategy(mode) {
|
||||||
// Display behaviors
|
// Display behaviors
|
||||||
display: {
|
display: {
|
||||||
showHiddenFields: false,
|
showHiddenFields: false,
|
||||||
|
enableDisabledFields: false,
|
||||||
showFormCleanings: true,
|
showFormCleanings: true,
|
||||||
showFontLink: false
|
showFontLink: false
|
||||||
},
|
},
|
||||||
|
|
@ -71,6 +72,7 @@ export function createFormModeStrategy(mode) {
|
||||||
strategy.validation.performActualSubmission = false
|
strategy.validation.performActualSubmission = false
|
||||||
|
|
||||||
strategy.display.showHiddenFields = true
|
strategy.display.showHiddenFields = true
|
||||||
|
strategy.display.enableDisabledFields = true
|
||||||
break
|
break
|
||||||
|
|
||||||
case FormMode.EDIT:
|
case FormMode.EDIT:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue