diff --git a/app/Filament/Forms/Resources/ElementTemplateManagementResource.php b/app/Filament/Forms/Resources/ElementTemplateManagementResource.php
index a9292757..38a68c65 100644
--- a/app/Filament/Forms/Resources/ElementTemplateManagementResource.php
+++ b/app/Filament/Forms/Resources/ElementTemplateManagementResource.php
@@ -71,21 +71,20 @@ public static function table(Table $table): Table
Tables\Columns\TextColumn::make('data_path_preview')->label('Data path')
->state(fn(FormElement $r) => optional($r->dataBindings->first())->path)
->copyable()->toggleable(isToggledHiddenByDefault: true),
- // hidden-by-default fields(some were above for displaying orders)
- Tables\Columns\IconColumn::make('visible_web')->label('Visible (Web)')->boolean()->sortable()->toggleable(isToggledHiddenByDefault: true),
- Tables\Columns\IconColumn::make('visible_pdf')->label('Visible (PDF)')->boolean()->sortable()->toggleable(isToggledHiddenByDefault: true),
- Tables\Columns\IconColumn::make('is_required')->label('Required')->boolean()->sortable()->toggleable(isToggledHiddenByDefault: true),
- Tables\Columns\IconColumn::make('is_read_only')->label('Read‑only')->boolean()->sortable()->toggleable(isToggledHiddenByDefault: true),
+ // hidden-by-default fields (some were above for displaying orders)
+ self::makeStatusColumn('visible_web', 'Visible (Web)'),
+ self::makeStatusColumn('visible_pdf', 'Visible (PDF)'),
+ self::makeStatusColumn('is_required', 'Required'),
+ self::makeStatusColumn('is_read_only', 'Read‑only'),
Tables\Columns\IconColumn::make('save_on_submit')->label('Save on Submit')->boolean()->sortable()->toggleable(isToggledHiddenByDefault: true),
// Tables\Columns\IconColumn::make('is_template')->label('Is template')->boolean()->sortable()->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('source_element_id')->label('Source element ID')->sortable()->toggleable(isToggledHiddenByDefault: true),
- Tables\Columns\TextColumn::make('custom_read_only')->label('Custom Read Only')->wrap()->toggleable(isToggledHiddenByDefault: true)->searchable(),
])
->filters([
SelectFilter::make('elementable_type')->label('Element type')->options(function () {
// Canonical label map from the model
$labels = FormElement::getAvailableElementTypes(); // [FQCN => 'Text Input', ...]
-
+
// Only show types that are present among templates
$typesInDb = FormElement::query()
->where('is_template', true)
@@ -107,18 +106,18 @@ public static function table(Table $table): Table
return $options; // [FQCN => 'Text Input', ...]
}),
SelectFilter::make('tags')->relationship('tags', 'name')->label('Tags')->multiple(),
- TernaryFilter::make('visible_web')->label('Visible on Web')->boolean(),
- TernaryFilter::make('visible_pdf')->label('Visible on PDF')->boolean(),
- TernaryFilter::make('is_required')->label('Required')->boolean(),
- TernaryFilter::make('is_read_only')->label('Read‑only')->boolean(),
+ self::makeStatusFilter('visible_web', 'Visible on Web'),
+ self::makeStatusFilter('visible_pdf', 'Visible on PDF'),
+ self::makeStatusFilter('is_required', 'Required'),
+ self::makeStatusFilter('is_read_only', 'Read‑only'),
TernaryFilter::make('save_on_submit')->label('Save on Submit')->boolean(),
- TernaryFilter::make('is_template')->label('Is Template')->boolean(),
+ // TernaryFilter::make('is_template')->label('Is Template')->boolean(),
])
// edit and untemplate action buttons
->actions([
Tables\Actions\Action::make('openBuilder')
->label('To Builder')
- ->icon('heroicon-o-cube-transparent')
+ ->icon('heroicon-o-wrench-screwdriver')
->visible(fn(FormElement $r) => filled($r->form_version_id))
->url(fn(FormElement $r) => url("/forms/form-versions/{$r->form_version_id}/build"))
->openUrlInNewTab(),
@@ -207,4 +206,34 @@ private static function getElementTypeLabels(): array
// Fallback: empty map; callers will fallback to class_basename
return [];
}
-}
+
+ /**
+ * Create a badge column for visibility/requirement/read-only fields.
+ */
+ private static function makeStatusColumn(string $field, string $label): Tables\Columns\TextColumn
+ {
+ return Tables\Columns\TextColumn::make($field)
+ ->label($label)
+ ->formatStateUsing(fn($state) => FormElement::getToggleButtonStates()[$state] ?? $state)
+ ->badge()
+ ->color(fn($state) => match ($state) {
+ 'always' => 'success',
+ 'icm' => 'info',
+ 'portal' => 'info',
+ 'never' => 'gray',
+ })
+ ->sortable()
+ ->toggleable(isToggledHiddenByDefault: true);
+ }
+
+ /**
+ * Create a filter for visibility/requirement/read-only fields.
+ */
+ private static function makeStatusFilter(string $field, string $label): SelectFilter
+ {
+ return SelectFilter::make($field)
+ ->label($label)
+ ->multiple()
+ ->options(FormElement::getToggleButtonStates());
+ }
+}
\ No newline at end of file
diff --git a/app/Filament/Forms/Resources/ElementTemplateManagementResource/Pages/ViewElementTemplateManagement.php b/app/Filament/Forms/Resources/ElementTemplateManagementResource/Pages/ViewElementTemplateManagement.php
index 0acab374..08fe020e 100644
--- a/app/Filament/Forms/Resources/ElementTemplateManagementResource/Pages/ViewElementTemplateManagement.php
+++ b/app/Filament/Forms/Resources/ElementTemplateManagementResource/Pages/ViewElementTemplateManagement.php
@@ -3,6 +3,7 @@
namespace App\Filament\Forms\Resources\ElementTemplateManagementResource\Pages;
use App\Filament\Forms\Resources\ElementTemplateManagementResource;
+use App\Filament\Forms\Resources\FormVersionResource;
use App\Helpers\GeneralTabHelper;
use App\Models\FormBuilding\FormElement;
use Filament\Actions;
@@ -15,6 +16,7 @@
use Filament\Forms\Components\Toggle;
use Filament\Resources\Pages\ViewRecord;
use Filament\Forms\Components\Actions\Action as FieldAction;
+use Illuminate\Support\Facades\Gate;
class ViewElementTemplateManagement extends ViewRecord
{
@@ -44,6 +46,14 @@ protected function getHeaderActions(): array
->icon('heroicon-o-pencil-square')
->url(ElementTemplateManagementResource::getUrl('edit', ['record' => $record]))
->visible(!$isParented),
+
+ Actions\Action::make('build')
+ ->label('To Builder')
+ ->icon('heroicon-o-wrench-screwdriver')
+ ->url(fn() => FormVersionResource::getUrl('build', ['record' => $this->record->form_version_id]))
+ ->color('primary')
+ ->outlined()
+ ->visible(fn() => Gate::allows('form-developer')),
];
}
diff --git a/app/Filament/Forms/Resources/FormResource.php b/app/Filament/Forms/Resources/FormResource.php
index e695501f..1dcde8fe 100644
--- a/app/Filament/Forms/Resources/FormResource.php
+++ b/app/Filament/Forms/Resources/FormResource.php
@@ -474,18 +474,25 @@ public static function table(Table $table): Table
Tables\Columns\TextColumn::make('decommissioned')
->label('Status')
->badge()
+ ->sortable()
+ ->searchable()
->formatStateUsing(fn(bool $state): string => $state ? 'Inactive' : 'Active')
->color(fn(bool $state): string => $state ? 'danger' : 'success'),
Tables\Columns\TextColumn::make('ministry.short_name')
->searchable()
->sortable()
+ ->toggleable()
->label('Ministry'),
Tables\Columns\TextColumn::make('businessAreas.name')
+ ->label('Business Areas')
->badge()
- ->label('Business Areas'),
+ ->toggleable()
+ ->sortable()
+ ->searchable(),
Tables\Columns\TextColumn::make('form_purpose')
->searchable()
->limit(30)
+ ->toggleable(isToggledHiddenByDefault: true)
->tooltip(function ($record): ?string {
$form_purpose = optional($record)->form_purpose ?? '';
return Str::length($form_purpose) > 30
@@ -494,8 +501,7 @@ public static function table(Table $table): Table
}),
Tables\Columns\TextColumn::make('notes')
->searchable()
- ->toggleable()
- ->toggledHiddenByDefault(true)
+ ->toggleable(isToggledHiddenByDefault: true)
->limit(30)
->tooltip(function ($record): ?string {
$notes = optional($record)->notes ?? '';
@@ -504,31 +510,41 @@ public static function table(Table $table): Table
: null;
}),
Tables\Columns\TextColumn::make('formFrequency.name')
- ->label('Usage Frequency'),
+ ->label('Usage Frequency')
+ ->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('formReach.name')
- ->label('Audience Size'),
+ ->label('Audience Size')
+ ->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('icm_generated')
+ ->label('ICM Generated')
->badge()
->formatStateUsing(fn($state) => $state ? 'Yes' : 'No')
->color(fn($state) => $state ? 'success' : 'danger')
- ->label('ICM Generated'),
+ ->toggleable()
+ ->sortable()
+ ->searchable(),
Tables\Columns\TextColumn::make('formSoftwareSources.name')
+ ->label('Software Sources')
->badge()
- ->label('Software Sources'),
+ ->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('formLocations.name')
+ ->label('Published Locations')
->badge()
- ->label('Published Locations'),
+ ->toggleable()
+ ->sortable()
+ ->searchable(),
Tables\Columns\TextColumn::make('formTags.name')
+ ->label('Tags')
->badge()
- ->label('Tags'),
+ ->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('dcv_material_number')
+ ->label('DCV Material Number')
->searchable()
- ->toggleable()
- ->toggledHiddenByDefault(true)
- ->label('DCV Material Number'),
+ ->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('migration2025_status')
->label('Migration 2025 Status')
->badge()
+ ->toggleable()
->getStateUsing(fn($record) => $record->migration2025_status)
->color(function ($state) {
return match ($state) {
diff --git a/app/Filament/Forms/Resources/FormResource/Pages/ViewForm.php b/app/Filament/Forms/Resources/FormResource/Pages/ViewForm.php
index 29499d3a..afa48d63 100644
--- a/app/Filament/Forms/Resources/FormResource/Pages/ViewForm.php
+++ b/app/Filament/Forms/Resources/FormResource/Pages/ViewForm.php
@@ -63,14 +63,22 @@ protected function getHeaderActions(): array
$actions = [];
if (Gate::allows('admin') || Gate::allows('form-developer')) {
- $actions[] = Actions\EditAction::make();
+ $actions[] = Actions\EditAction::make()
+ ->outlined()
+ ->icon('heroicon-s-pencil-square');
if ($hasVersions) {
$actions[] = Actions\Action::make('view_latest_version')
->label('View latest version')
- ->icon('heroicon-o-document-text')
+ ->icon('heroicon-s-eye')
->url(fn() => FormVersionResource::getUrl('view', ['record' => $latestVersion]))
+ ->color('gray')
->outlined();
+
+ $actions[] = Actions\Action::make('build_latest_version')
+ ->label('Build latest version')
+ ->icon('heroicon-s-wrench-screwdriver')
+ ->url(fn() => FormVersionResource::getUrl('build', ['record' => $latestVersion]));
}
} else {
// For regular users, show preview button if versions exist
diff --git a/app/Filament/Forms/Resources/FormResource/RelationManagers/FormVersionRelationManager.php b/app/Filament/Forms/Resources/FormResource/RelationManagers/FormVersionRelationManager.php
index 38f6c083..defc744c 100644
--- a/app/Filament/Forms/Resources/FormResource/RelationManagers/FormVersionRelationManager.php
+++ b/app/Filament/Forms/Resources/FormResource/RelationManagers/FormVersionRelationManager.php
@@ -7,15 +7,7 @@
use Filament\Tables;
use Illuminate\Support\Facades\Gate;
use App\Filament\Forms\Resources\FormVersionResource;
-use App\Helpers\FormVersionHelper;
use Filament\Forms\Components\DatePicker;
-use App\Models\FormBuilding\FormScript;
-use App\Models\FormBuilding\StyleSheet;
-use App\Models\FormBuilding\FormVersionFormDataSource;
-use App\Models\FormBuilding\FormElementDataBinding;
-use Illuminate\Support\Facades\Auth;
-use Filament\Tables\Actions\Action;
-use Illuminate\Support\Str;
class FormVersionRelationManager extends RelationManager
{
@@ -38,7 +30,10 @@ public function table(Tables\Table $table): Tables\Table
->sortable(),
Tables\Columns\TextColumn::make('formatted_status')
->label('Status')
- ->getStateUsing(fn($record) => $record->getFormattedStatusName()),
+ ->badge()
+ ->color(fn($state) => FormVersion::getStatusColour($state))
+ ->getStateUsing(fn($record) => $record->getFormattedStatusName())
+ ->sortable(),
Tables\Columns\TextColumn::make('formDeveloper.name')
->label('Developer')
->sortable(),
@@ -86,6 +81,10 @@ public function table(Tables\Table $table): Tables\Table
Tables\Actions\EditAction::make()
->url(fn(FormVersion $record) => FormVersionResource::getUrl('edit', ['record' => $record]))
->visible(fn($record) => (in_array($record->status, ['draft', 'testing'])) && Gate::allows('form-developer')),
+ Tables\Actions\Action::make('Build')
+ ->url(fn(FormVersion $record) => FormVersionResource::getUrl('build', ['record' => $record]))
+ ->visible(fn($record) => (in_array($record->status, ['draft', 'testing'])) && Gate::allows('form-developer'))
+ ->icon('heroicon-s-wrench-screwdriver'),
// Duplicate form version currently disabled due to ADO bugs 3302 and 3303
// Action::make('duplicate')
// ->label('Duplicate')
diff --git a/app/Filament/Forms/Resources/FormVersionResource.php b/app/Filament/Forms/Resources/FormVersionResource.php
index 16af2349..8cbec8fb 100644
--- a/app/Filament/Forms/Resources/FormVersionResource.php
+++ b/app/Filament/Forms/Resources/FormVersionResource.php
@@ -40,7 +40,7 @@ class FormVersionResource extends Resource
protected static ?string $navigationIcon = 'heroicon-o-inbox-stack';
- protected static bool $shouldRegisterNavigation = false;
+ protected static bool $shouldRegisterNavigation = true;
public static function getEloquentQuery(): Builder
{
@@ -130,8 +130,8 @@ public static function form(Form $form): Form
->itemLabel(
fn(array $state): ?string =>
isset($state['form_data_source_id'])
- ? FormDataSource::find($state['form_data_source_id'])?->name ?? 'New Data Source'
- : 'New Data Source'
+ ? FormDataSource::find($state['form_data_source_id'])?->name ?? 'New Data Source'
+ : 'New Data Source'
)
->addActionLabel('Add Data Source')
->collapsed()
@@ -315,13 +315,13 @@ public static function table(Table $table): Table
])
->filters([
SelectFilter::make('status')
- ->options([
- 'draft' => 'Draft',
- 'under_review' => 'Under Review',
- 'approved' => 'Approved',
- 'published' => 'Published',
- 'archived' => 'Archived',
- ]),
+ ->options([
+ 'draft' => 'Draft',
+ 'under_review' => 'Under Review',
+ 'approved' => 'Approved',
+ 'published' => 'Published',
+ 'archived' => 'Archived',
+ ]),
TrashedFilter::make()
->visible(fn() => Gate::allows('admin')),
])
@@ -329,102 +329,106 @@ public static function table(Table $table): Table
ViewAction::make(),
EditAction::make()
->visible(fn($record) => (in_array($record->status, ['draft', 'testing'])) && Gate::allows('form-developer')),
- Action::make('duplicate')
- ->label('Duplicate')
- ->icon('heroicon-o-document-duplicate')
- ->color('info')
+ Action::make('Build')
+ ->url(fn(FormVersion $record) => FormVersionResource::getUrl('build', ['record' => $record]))
->visible(fn($record) => (in_array($record->status, ['draft', 'testing'])) && Gate::allows('form-developer'))
- ->action(function ($record) {
- // Create a new version with incremented version number
- $newVersion = $record->replicate(['version_number', 'status', 'created_at', 'updated_at']);
- $newVersion->version_number = FormVersion::where('form_id', $record->form_id)->max('version_number') + 1;
- $newVersion->status = 'draft';
- $newVersion->form_developer_id = Auth::id();
- $newVersion->comments = 'Duplicated from version ' . $record->version_number;
- $newVersion->save();
-
- // Duplicate all FormElements and map new to old
- $oldToNewElementMap = [];
- foreach ($record->formElements()->orderBy('order')->get() as $element) {
- $newElement = $element->replicate(['id', 'form_version_id', 'parent_id', 'created_at', 'updated_at']);
- $newElement->form_version_id = $newVersion->id;
- $newElement->parent_id = null;
- $newElement->save();
-
- // Map old element ID to new element for parent relationship updates
- $oldToNewElementMap[$element->id] = [
- 'new_element' => $newElement,
- 'old_parent_id' => $element->parent_id
- ];
-
- // Attach tags
- $newElement->tags()->attach($element->tags->pluck('id'));
-
- // Duplicate data bindings
- foreach ($element->dataBindings as $dataBinding) {
- \App\Models\FormBuilding\FormElementDataBinding::create([
- 'form_element_id' => $newElement->id,
- 'form_data_source_id' => $dataBinding->form_data_source_id,
- 'path' => $dataBinding->path,
- 'condition' => $dataBinding->condition,
- 'order' => $dataBinding->order,
- ]);
- }
-
- // Duplicate polymorphic elementable and link to new element
- if ($element->elementable) {
- $elementableData = $element->elementable->getData();
-
- // Filter out null and empty string values to let model defaults apply
- $filteredData = array_filter($elementableData, function ($value) {
- return $value !== null && $value !== '';
- });
-
- $newElementable = $element->elementable_type::create($filteredData);
- $newElement->update(['elementable_id' => $newElementable->id]);
- }
- }
-
- // Update parent_id relationships for nested elements
- foreach ($oldToNewElementMap as $data) {
- if ($data['old_parent_id'] && isset($oldToNewElementMap[$data['old_parent_id']])) {
- $data['new_element']->update([
- 'parent_id' => $oldToNewElementMap[$data['old_parent_id']]['new_element']->id
- ]);
- }
- }
-
- // Duplicate related models using a helper method
- FormVersionHelper::duplicateRelatedModels($record->id, $newVersion->id, StyleSheet::class);
- FormVersionHelper::duplicateRelatedModels($record->id, $newVersion->id, FormScript::class);
-
- // Duplicate form data sources with their order
- foreach ($record->formVersionFormDataSources as $formDataSource) {
- \App\Models\FormBuilding\FormVersionFormDataSource::create([
- 'form_version_id' => $newVersion->id,
- 'form_data_source_id' => $formDataSource->form_data_source_id,
- 'order' => $formDataSource->order,
- ]);
- }
-
- // Duplicate form interfaces
- foreach ($record->formVersionFormInterfaces as $formInterface) {
- \App\Models\FormBuilding\FormVersionFormInterface::create([
- 'form_version_id' => $newVersion->id,
- 'form_interface_id' => $formInterface->form_interface_id,
- 'order' => $formInterface->order,
- ]);
- }
-
- // Redirect to build the new version
- if (Gate::allows('form-developer')) {
- return redirect()->to('/forms/form-versions/' . $newVersion->id . '/build');
- } else {
- return redirect()->to(FormVersionResource::getUrl('view', ['record' => $newVersion]));
- }
- })
- ->requiresConfirmation()
- ->modalDescription('This will create a new draft version based on this form version, including all form elements.'),
+ ->icon('heroicon-s-wrench-screwdriver'),
+ // Action::make('duplicate')
+ // ->label('Duplicate')
+ // ->icon('heroicon-o-document-duplicate')
+ // ->color('info')
+ // ->visible(fn($record) => (in_array($record->status, ['draft', 'testing'])) && Gate::allows('form-developer'))
+ // ->action(function ($record) {
+ // // Create a new version with incremented version number
+ // $newVersion = $record->replicate(['version_number', 'status', 'created_at', 'updated_at']);
+ // $newVersion->version_number = FormVersion::where('form_id', $record->form_id)->max('version_number') + 1;
+ // $newVersion->status = 'draft';
+ // $newVersion->form_developer_id = Auth::id();
+ // $newVersion->comments = 'Duplicated from version ' . $record->version_number;
+ // $newVersion->save();
+ //
+ // // Duplicate all FormElements and map new to old
+ // $oldToNewElementMap = [];
+ // foreach ($record->formElements()->orderBy('order')->get() as $element) {
+ // $newElement = $element->replicate(['id', 'form_version_id', 'parent_id', 'created_at', 'updated_at']);
+ // $newElement->form_version_id = $newVersion->id;
+ // $newElement->parent_id = null;
+ // $newElement->save();
+ //
+ // // Map old element ID to new element for parent relationship updates
+ // $oldToNewElementMap[$element->id] = [
+ // 'new_element' => $newElement,
+ // 'old_parent_id' => $element->parent_id
+ // ];
+ //
+ // // Attach tags
+ // $newElement->tags()->attach($element->tags->pluck('id'));
+ //
+ // // Duplicate data bindings
+ // foreach ($element->dataBindings as $dataBinding) {
+ // \App\Models\FormBuilding\FormElementDataBinding::create([
+ // 'form_element_id' => $newElement->id,
+ // 'form_data_source_id' => $dataBinding->form_data_source_id,
+ // 'path' => $dataBinding->path,
+ // 'condition' => $dataBinding->condition,
+ // 'order' => $dataBinding->order,
+ // ]);
+ // }
+ //
+ // // Duplicate polymorphic elementable and link to new element
+ // if ($element->elementable) {
+ // $elementableData = $element->elementable->getData();
+ //
+ // // Filter out null and empty string values to let model defaults apply
+ // $filteredData = array_filter($elementableData, function ($value) {
+ // return $value !== null && $value !== '';
+ // });
+ //
+ // $newElementable = $element->elementable_type::create($filteredData);
+ // $newElement->update(['elementable_id' => $newElementable->id]);
+ // }
+ // }
+ //
+ // // Update parent_id relationships for nested elements
+ // foreach ($oldToNewElementMap as $data) {
+ // if ($data['old_parent_id'] && isset($oldToNewElementMap[$data['old_parent_id']])) {
+ // $data['new_element']->update([
+ // 'parent_id' => $oldToNewElementMap[$data['old_parent_id']]['new_element']->id
+ // ]);
+ // }
+ // }
+ //
+ // // Duplicate related models using a helper method
+ // FormVersionHelper::duplicateRelatedModels($record->id, $newVersion->id, StyleSheet::class);
+ // FormVersionHelper::duplicateRelatedModels($record->id, $newVersion->id, FormScript::class);
+ //
+ // // Duplicate form data sources with their order
+ // foreach ($record->formVersionFormDataSources as $formDataSource) {
+ // \App\Models\FormBuilding\FormVersionFormDataSource::create([
+ // 'form_version_id' => $newVersion->id,
+ // 'form_data_source_id' => $formDataSource->form_data_source_id,
+ // 'order' => $formDataSource->order,
+ // ]);
+ // }
+ //
+ // // Duplicate form interfaces
+ // foreach ($record->formVersionFormInterfaces as $formInterface) {
+ // \App\Models\FormBuilding\FormVersionFormInterface::create([
+ // 'form_version_id' => $newVersion->id,
+ // 'form_interface_id' => $formInterface->form_interface_id,
+ // 'order' => $formInterface->order,
+ // ]);
+ // }
+ //
+ // // Redirect to build the new version
+ // if (Gate::allows('form-developer')) {
+ // return redirect()->to('/forms/form-versions/' . $newVersion->id . '/build');
+ // } else {
+ // return redirect()->to(FormVersionResource::getUrl('view', ['record' => $newVersion]));
+ // }
+ // })
+ // ->requiresConfirmation()
+ // ->modalDescription('This will create a new draft version based on this form version, including all form elements.'),
Action::make('archive')
->label('Archive')
->icon('heroicon-o-archive-box-arrow-down')
@@ -449,7 +453,8 @@ public static function table(Table $table): Table
25,
50,
100,
- ]);;
+ ]);
+ ;
}
public static function getRelations(): array
diff --git a/app/Filament/Forms/Resources/FormVersionResource/Pages/BuildFormVersion.php b/app/Filament/Forms/Resources/FormVersionResource/Pages/BuildFormVersion.php
index ed5a7635..a25b38de 100644
--- a/app/Filament/Forms/Resources/FormVersionResource/Pages/BuildFormVersion.php
+++ b/app/Filament/Forms/Resources/FormVersionResource/Pages/BuildFormVersion.php
@@ -310,14 +310,14 @@ protected function getHeaderActions(): array
// Refresh the page to update the tree
$this->redirect($this->getResource()::getUrl('build', ['record' => $this->record]));
} catch (\InvalidArgumentException $e) {
- \Filament\Notifications\Notification::make()
+ Notification::make()
->danger()
->title('Cannot Create Element')
->body($e->getMessage())
->persistent()
->send();
} catch (\Exception $e) {
- \Filament\Notifications\Notification::make()
+ Notification::make()
->danger()
->title('Error Creating Element')
->body('An unexpected error occurred: ' . $e->getMessage())
@@ -394,10 +394,8 @@ protected function getHeaderActions(): array
}),
ActionGroup::make([
- $this->makeDownloadJsonAction('download_old_json', 'Download v1', 1),
- $this->makeDownloadJsonAction('download_json', 'Download v2', 2),
- $this->makeCopyJsonAction('copy_json_v1', 'Copy v1 to Clipboard', 1),
- $this->makeCopyJsonAction('copy_json_v2', 'Copy v2 to Clipboard', 2),
+ $this->makeDownloadJsonAction('download_json', 'Download', 2),
+ $this->makeCopyJsonAction('copy_json_v2', 'Copy to Clipboard', 2),
])
->label('Download JSON')
->icon('heroicon-m-ellipsis-vertical')
@@ -532,8 +530,8 @@ protected function handleValidationFailures(array $issues): void
$bodyHtml = new HtmlString(
'
'
- . nl2br(e($payload)) .
- '
'
+ . nl2br(e($payload)) .
+ ''
);
Notification::make()
@@ -594,7 +592,7 @@ public function save(): void
{
// Prevent saving if not editable
if (!$this->isEditable()) {
- \Filament\Notifications\Notification::make()
+ Notification::make()
->warning()
->title('Cannot Save Changes')
->body('Form versions can only be saved when in draft status.')
@@ -629,9 +627,9 @@ public function save(): void
$this->getSavedNotification()?->send();
}
- protected function getSavedNotification(?string $message = null): ?\Filament\Notifications\Notification
+ protected function getSavedNotification(?string $message = null): ?Notification
{
- return \Filament\Notifications\Notification::make()
+ return Notification::make()
->success()
->title('Saved')
->body($message ?? 'The form builder changes have been saved successfully.');
@@ -770,7 +768,7 @@ public function triggerUpdateEvent(string $updateType = 'manual'): void
'js_content_pdf' => $data['js_content_pdf'] ?? '',
], $updateType, false);
- \Filament\Notifications\Notification::make()
+ Notification::make()
->success()
->title('Update Broadcasted')
->body('Form version update has been broadcasted to all connected clients.')
@@ -1064,8 +1062,8 @@ private function showFieldIssuesNotification(array $issues): void
$bodyHtml = new HtmlString(
''
- . nl2br(e($payload))
- . '
'
+ . nl2br(e($payload))
+ . ''
);
Notification::make()
@@ -1083,7 +1081,7 @@ public function importParsedSchemaElements()
{
$schemaContent = $this->importWizard['schema_content'] ?? null;
if (empty($schemaContent)) {
- \Filament\Notifications\Notification::make()
+ Notification::make()
->danger()
->title('No Parsed Schema')
->body('No schema content found to import.')
@@ -1110,7 +1108,7 @@ public function importParsedSchemaElements()
'cacheKey' => $cacheKey,
];
- \Filament\Notifications\Notification::make()
+ Notification::make()
->info()
->title('Import Started')
->body('The import is being processed in the background. This page will refresh when it is complete.')
@@ -1135,7 +1133,7 @@ public function pollImportStatus()
session()->forget('import_job_cache_key');
$this->importJobStatus['done'] = true;
$this->importJobStatus['status'] = 'complete';
- \Filament\Notifications\Notification::make()
+ Notification::make()
->success()
->title('Import Complete')
->body('Form elements have been successfully imported. The page will refresh to show the new elements.')
@@ -1151,7 +1149,7 @@ public function pollImportStatus()
$this->importJobStatus['done'] = true;
$this->importJobStatus['status'] = 'error';
- \Filament\Notifications\Notification::make()
+ Notification::make()
->danger()
->title('Import Failed')
->body($error ?: 'An error occurred during import.')
diff --git a/app/Filament/Forms/Resources/StyleSheetResource.php b/app/Filament/Forms/Resources/StyleSheetResource.php
index 1d56a118..44329984 100644
--- a/app/Filament/Forms/Resources/StyleSheetResource.php
+++ b/app/Filament/Forms/Resources/StyleSheetResource.php
@@ -90,7 +90,7 @@ public static function form(Form $form): Form
),
CustomMonacoEditor::make('content')
- ->label('Script Content')
+ ->label('Style Sheet Content')
->language('css')
->theme('vs-dark')
->live()
diff --git a/app/Filament/Forms/Resources/UserTypeResource.php b/app/Filament/Forms/Resources/UserTypeResource.php
index d4b8afc8..53f6683c 100644
--- a/app/Filament/Forms/Resources/UserTypeResource.php
+++ b/app/Filament/Forms/Resources/UserTypeResource.php
@@ -3,15 +3,12 @@
namespace App\Filament\Forms\Resources;
use App\Filament\Forms\Resources\UserTypeResource\Pages;
-use App\Filament\Forms\Resources\UserTypeResource\RelationManagers;
-use App\Models\UserType;
+use App\Models\FormMetadata\UserType;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
-use Illuminate\Database\Eloquent\Builder;
-use Illuminate\Database\Eloquent\SoftDeletingScope;
class UserTypeResource extends Resource
{
diff --git a/app/Helpers/GeneralTabHelper.php b/app/Helpers/GeneralTabHelper.php
index d328b911..15c31d24 100644
--- a/app/Helpers/GeneralTabHelper.php
+++ b/app/Helpers/GeneralTabHelper.php
@@ -63,14 +63,51 @@ public static function getGeneralTabSchema(
// Help text field
$schema[] = self::makeHelpTextField($disabled, $disabledCallback, $shouldShowTooltipsCallback);
- // Visibility grid
- $schema[] = self::makeVisibilityGrid($disabled, $disabledCallback);
-
- // Required grid
- $schema[] = self::makeRequiredGrid($disabled, $disabledCallback);
+ // Visibility button groups
+ $schema[] = Grid::make(2)
+ ->schema([
+ self::makeToggleButton(
+ field: 'visible_web',
+ label: 'Visible on Web',
+ tooltip: 'Control when this element is visible on web forms',
+ default: 'always',
+ disabled: $disabled,
+ disabledCallback: $disabledCallback,
+ shouldShowTooltipsCallback: $shouldShowTooltipsCallback,
+ ),
+ self::makeToggleButton(
+ field: 'visible_pdf',
+ label: 'Visible on PDF',
+ tooltip: 'Control when this element is visible on PDF forms',
+ default: 'always',
+ disabled: $disabled,
+ disabledCallback: $disabledCallback,
+ shouldShowTooltipsCallback: $shouldShowTooltipsCallback,
+ ),
+ ]);
- // Read Only grid
- $schema[] = self::makeReadOnlyGrid($disabled, $disabledCallback);
+ // Required and read only button groups
+ $schema[] = Grid::make(2)
+ ->schema([
+ self::makeToggleButton(
+ field: 'is_required',
+ label: 'Required',
+ tooltip: 'Control when this element is required',
+ default: 'never',
+ disabled: $disabled,
+ disabledCallback: $disabledCallback,
+ shouldShowTooltipsCallback: $shouldShowTooltipsCallback,
+ ),
+ self::makeToggleButton(
+ field: 'is_read_only',
+ label: 'Read-Only',
+ tooltip: 'Control when this element is read-only',
+ default: 'never',
+ disabled: $disabled,
+ disabledCallback: $disabledCallback,
+ shouldShowTooltipsCallback: $shouldShowTooltipsCallback,
+ )
+ ]);
// Template and Save On Submit toggles
$schema[] = Grid::make(2)
@@ -133,12 +170,10 @@ private static function makeTemplateField(?callable $shouldShowTooltipsCallback)
$set('description', $template->description);
$set('help_text', $template->help_text);
$set('elementable_type', $template->elementable_type);
- $set('is_required_toggle', $template->is_required !== null && $template->is_required !== '');
- $set('is_required', $template->is_required);
- $set('is_read_only_toggle', $template->is_read_only !== null && $template->is_read_only !== '');
- $set('is_read_only', $template->is_read_only);
$set('visible_web', $template->visible_web);
$set('visible_pdf', $template->visible_pdf);
+ $set('is_required', $template->is_required);
+ $set('is_read_only', $template->is_read_only);
$set('is_template', false); // New element should not be a template by default
// Prefill tags
@@ -420,104 +455,39 @@ private static function makeHelpTextField(
);
}
- private static function makeVisibilityGrid(bool $disabled, ?callable $disabledCallback): Component
- {
- return Grid::make(2)
- ->schema([
- Toggle::make('visible_web')
- ->label('Visible on Web')
- ->default(true)
- ->disabled($disabled || ($disabledCallback && $disabledCallback())),
- Toggle::make('visible_pdf')
- ->label('Visible on PDF')
- ->default(true)
- ->disabled($disabled || ($disabledCallback && $disabledCallback())),
- ]);
- }
-
- private static function makeRequiredGrid(bool $disabled, ?callable $disabledCallback): Component
- {
- $toggle = Toggle::make('is_required_toggle')
- ->label('Is Required')
- ->default(false)
- ->live()
- ->disabled($disabled || ($disabledCallback && $disabledCallback()))
- ->afterStateHydrated(function (Toggle $component, callable $set, callable $get) {
- $isRequired = $get('is_required');
- // Set toggle to true if is_required has any non-null value ('always' or 'portal')
- if ($isRequired !== null && $isRequired !== '') {
- $set('is_required_toggle', true);
- }
- });
-
- $buttons = ToggleButtons::make('is_required')
- ->label('Required When')
- ->options([
- 'always' => 'Always',
- 'portal' => 'On Portal Forms'
- ])
- ->default('always')
- ->inline()
- ->disabled(fn($get) => !$get('is_required_toggle'))
- ->afterStateHydrated(function (callable $set, callable $get) {
- $value = $get('is_required');
- if ($value === null || $value === '') {
- $set('is_required', 'always');
- }
- });
-
- return Grid::make(2)
- ->schema([
- $toggle,
- $buttons,
- ]);
- }
-
- private static function makeReadOnlyGrid(bool $disabled, ?callable $disabledCallback): Component
- {
- $toggle = Toggle::make('is_read_only_toggle')
- ->label('Is Read Only')
- ->default(false)
- ->live()
- ->disabled($disabled || ($disabledCallback && $disabledCallback()))
- ->afterStateHydrated(function (Toggle $component, callable $set, callable $get) {
- $isReadOnly = $get('is_read_only');
- // Set toggle to true if is_read_only has any non-null value ('always' or 'portal')
- if ($isReadOnly !== null && $isReadOnly !== '') {
- $set('is_read_only_toggle', true);
- }
- });
-
- $buttons = ToggleButtons::make('is_read_only')
- ->label('Read Only When')
- ->options([
- 'always' => 'Always',
- 'portal' => 'On Portal Forms'
- ])
- ->default('always')
+ /**
+ * Make ToggleButton group for selecting visibility, required, and read-only states
+ * @param string $field
+ * @param string $label
+ * @param string $tooltip
+ * @param string $default
+ * @param bool $disabled
+ * @param mixed $disabledCallback
+ * @param mixed $shouldShowTooltipsCallback
+ * @return Component
+ */
+ private static function makeToggleButton(
+ string $field,
+ string $label,
+ string $tooltip = '',
+ string $default = '',
+ bool $disabled = false,
+ ?callable $disabledCallback = null,
+ ?callable $shouldShowTooltipsCallback = null,
+ ): Component {
+ $buttonGroup = ToggleButtons::make($field)
+ ->label($label)
+ ->options(FormElement::getToggleButtonStates())
+ ->default($default)
->inline()
- ->disabled(fn($get) => !$get('is_read_only_toggle'))
- ->afterStateHydrated(function (callable $set, callable $get) {
- $value = $get('is_read_only');
- if ($value === null || $value === '') {
- $set('is_read_only', 'always');
- }
- });
-
- $customScript = TextArea::make('custom_read_only')
- ->label('Custom Read Only Script')
- ->visible(fn($get) => $get('is_read_only'))
- ->reactive()
- ->disabled(fn($get) => !$get('is_read_only_toggle'))
- ->hintIcon('heroicon-m-question-mark-circle', tooltip: 'Custom read only script to control when this element is read only. Use the format: "if (condition) { return true; } else { return false; }". This will be evaluated in the browser.')
- ->columnSpanFull();
+ ->grouped()
+ ->disabled($disabled || ($disabledCallback && $disabledCallback()));
- return Grid::make(2)
- ->schema([
- $toggle,
- $buttons,
- $customScript
- ]);
+ return self::withOptionalTooltip(
+ $buttonGroup,
+ $shouldShowTooltipsCallback,
+ $tooltip,
+ );
}
private static function makeTemplateToggle(
diff --git a/app/Helpers/SchemaHelper.php b/app/Helpers/SchemaHelper.php
index b71a76d8..0dbdc15d 100644
--- a/app/Helpers/SchemaHelper.php
+++ b/app/Helpers/SchemaHelper.php
@@ -5,6 +5,8 @@
use Filament\Forms\Components\Fieldset;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
+use Filament\Forms\Components\Repeater;
+use Filament\Forms\Components\Select;
use Filament\Forms\Components\Actions\Action;
class SchemaHelper
@@ -13,36 +15,18 @@ class SchemaHelper
* Get the common Carbon Design System fields for form elements
*
* @param bool $disabled Whether the fields should be disabled
+ * @param bool $labelRequired Whether the label text field should be required
* @return array Array of Filament form components
*/
- public static function getCommonCarbonFields(bool $disabled = false): array
+ public static function getCommonCarbonFields(bool $disabled = false, bool $labelRequired = false): Fieldset
{
- return [
- Fieldset::make('Field Label')
- ->schema([
- TextInput::make('elementable_data.labelText')
- ->label('Field Label')
- ->disabled($disabled)
- ->autocomplete(false)
- ->suffixAction(
- Action::make('generate_label_text')
- ->icon('heroicon-o-arrow-path')
- ->tooltip('Regenerate from Element Name')
- ->action(function (callable $set, callable $get) {
- $name = $get('name');
- if (!empty($name)) {
- $set('elementable_data.labelText', $name);
- }
- }),
- ),
- SchemaHelper::getEnableVariableSubstitutionToggle($disabled),
- Toggle::make('elementable_data.hideLabel')
- ->label('Hide Label')
- ->default(false)
- ->disabled($disabled),
- ])
- ->columns(1),
- ];
+ return Fieldset::make('Field Label')
+ ->schema([
+ self::getLabelTextField($disabled, $labelRequired),
+ self::getEnableVariableSubstitutionToggle($disabled),
+ self::getHideLabelToggle($disabled),
+ ])
+ ->columns(1);
}
/**
@@ -53,13 +37,17 @@ public static function getPlaceholderTextField(bool $disabled = false)
return TextInput::make('elementable_data.placeholder')
->label('Placeholder Text')
->autocomplete(false)
+ ->maxLength(255)
->disabled($disabled);
}
- public static function getLabelTextField(bool $disabled = false)
+ public static function getLabelTextField(bool $disabled = false, bool $required = false)
{
- return TextInput::make('elementable_data.labelText')
+ return TextInput::make('elementable_data.label_text')
->label('Field Label')
+ ->maxLength(255)
+ ->disabled($disabled)
+ ->required($required)
->suffixAction(
Action::make('generate_label_text')
->icon('heroicon-o-arrow-path')
@@ -67,36 +55,98 @@ public static function getLabelTextField(bool $disabled = false)
->action(function (callable $set, callable $get) {
$name = $get('name');
if (!empty($name)) {
- $set('elementable_data.labelText', $name);
+ $set('elementable_data.label_text', $name);
}
}),
- )
- ->disabled($disabled);
+ );
}
public static function getHideLabelToggle(bool $disabled = false)
{
- return Toggle::make('elementable_data.hideLabel')
+ return Toggle::make('elementable_data.hide_label')
->label('Hide Label')
->default(false)
->live()
->disabled($disabled);
}
- public static function getPlaceholderField(bool $disabled = false)
- {
- return TextInput::make('elementable_data.placeholder')
- ->label('Placeholder Text')
- ->disabled($disabled);
- }
-
public static function getEnableVariableSubstitutionToggle(bool $disabled = false)
{
- return Toggle::make('elementable_data.enableVarSub')
+ return Toggle::make('elementable_data.enable_var_sub')
->label('Enable Variable Substitution')
->helperText('Use {{variableName}} syntax in the label to dynamically insert values from other form fields.
Make sure you also insert the Moustache library in the Scripts tab and register the variable.')
->default(false)
->disabled($disabled);
}
+
+ public static function getOptionsRepeater(bool $disabled = false, string $label = 'Options'): Repeater
+ {
+ return Repeater::make('elementable_data.options')
+ ->label($label)
+ ->schema([
+ TextInput::make('label')
+ ->label('Option Label')
+ ->required()
+ ->maxLength(255)
+ ->columnSpan(2)
+ ->autocomplete(false)
+ ->live(onBlur: true)
+ ->afterStateUpdated(function (callable $set, callable $get, $state) {
+ $value = $get('value');
+ if (empty($value) && !empty($state)) {
+ $slug = \Illuminate\Support\Str::slug($state, '-');
+ $set('value', $slug);
+ }
+ }),
+ TextInput::make('value')
+ ->label('Option Value')
+ ->required()
+ ->maxLength(255)
+ ->columnSpan(2)
+ ->suffixAction(
+ Action::make('regenerate_value')
+ ->icon('heroicon-o-arrow-path')
+ ->tooltip('Regenerate from Option Label')
+ ->action(function (callable $set, callable $get) {
+ $label = $get('label');
+ if (!empty($label)) {
+ $slug = \Illuminate\Support\Str::slug($label, '-');
+ $set('value', $slug);
+ }
+ })
+ ),
+ ])
+ ->columns(2)
+ ->defaultItems(1)
+ ->addActionLabel('Add Option')
+ ->reorderableWithButtons()
+ ->collapsible()
+ ->itemLabel(fn(array $state): ?string => $state['label'] ?? 'Option')
+ ->disabled($disabled)
+ ->minItems(1);
+ }
+
+ public static function getOptionsDefaultSelectedSelect(bool $disabled = false, bool $multiple = false, string $label = 'Default Selected Value'): Select
+ {
+ $select = Select::make('elementable_data.default_selected')
+ ->label($label)
+ ->options(function (callable $get) {
+ $options = $get('elementable_data.options') ?? [];
+ $selectOptions = [];
+ foreach ($options as $option) {
+ if (!empty($option['value'])) {
+ $selectOptions[$option['value']] = $option['label'] ?? $option['value'];
+ }
+ }
+ return $selectOptions;
+ })
+ ->disabled($disabled);
+
+ if ($multiple) {
+ $select->multiple()->nullable()->live();
+ }
+
+ return $select;
+ }
}
diff --git a/app/Jobs/GenerateFormVersionJsonJob.php b/app/Jobs/GenerateFormVersionJsonJob.php
index 5db79213..93b73849 100644
--- a/app/Jobs/GenerateFormVersionJsonJob.php
+++ b/app/Jobs/GenerateFormVersionJsonJob.php
@@ -2,7 +2,10 @@
namespace App\Jobs;
+use App\Models\FormBuilding\CheckboxGroupFormElement;
use App\Models\FormBuilding\FormVersion;
+use App\Models\FormBuilding\RadioInputFormElement;
+use App\Models\FormBuilding\SelectInputFormElement;
use App\Services\FormVersionJsonService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -23,7 +26,8 @@ public function __construct(
public FormVersion $formVersion,
public int $userId,
public int $version = 2
- ) {}
+ ) {
+ }
public function handle(): void
{
@@ -34,9 +38,9 @@ public function handle(): void
'form',
'formElements.elementable' => function ($morphTo) {
$morphTo->morphWith([
- \App\Models\FormBuilding\SelectInputFormElement::class => ['options'],
- \App\Models\FormBuilding\RadioInputFormElement::class => ['options'],
- \App\Models\FormBuilding\CheckboxGroupFormElement::class => ['options'],
+ SelectInputFormElement::class => ['options'],
+ RadioInputFormElement::class => ['options'],
+ CheckboxGroupFormElement::class => ['options'],
]);
},
'formElements.dataBindings.formDataSource',
@@ -65,9 +69,11 @@ public function handle(): void
}
// Create filename with form title and version
+ $form_id = $this->formVersion->form->form_id;
+ $versionNumber = $this->formVersion->version_number;
$formTitle = $this->formVersion->form->form_title ?? 'Unknown Form';
$sanitizedTitle = preg_replace('/[^a-zA-Z0-9\-_]/', '_', $formTitle);
- $filename = "form_{$sanitizedTitle}_v{$this->formVersion->version_number}_{$this->formVersion->id}_formatversion_{$this->version}.json";
+ $filename = "form_{$form_id}_v{$versionNumber}_{$sanitizedTitle}.json";
// Store the JSON file
$filePath = "{$filename}";
@@ -80,7 +86,7 @@ public function handle(): void
Notification::make()
->success()
->title('JSON Export Complete')
- ->body("Your form JSON file has been generated successfully.")
+ ->body("Your export {$this->formVersion->form->form_id} \"{$formTitle}\" has been generated successfully.")
->actions([
\Filament\Notifications\Actions\Action::make('download')
->label('Download JSON')
diff --git a/app/Jobs/ImportFormVersionElementsJob.php b/app/Jobs/ImportFormVersionElementsJob.php
index 3af95ee6..6bd06c80 100644
--- a/app/Jobs/ImportFormVersionElementsJob.php
+++ b/app/Jobs/ImportFormVersionElementsJob.php
@@ -13,9 +13,23 @@
use Illuminate\Foundation\Bus\Dispatchable;
use App\Models\FormBuilding\FormElement;
use App\Events\FormVersionUpdateEvent;
+use App\Models\FormBuilding\ButtonInputFormElement;
+use App\Models\FormBuilding\CheckboxGroupFormElement;
+use App\Models\FormBuilding\CheckboxInputFormElement;
+use App\Models\FormBuilding\ContainerFormElement;
+use App\Models\FormBuilding\CurrencyInputFormElement;
+use App\Models\FormBuilding\DateSelectInputFormElement;
use App\Models\FormBuilding\FormElementDataBinding;
use App\Models\FormBuilding\FormScript;
+use App\Models\FormBuilding\HTMLFormElement;
+use App\Models\FormBuilding\NumberInputFormElement;
+use App\Models\FormBuilding\RadioInputFormElement;
+use App\Models\FormBuilding\SelectInputFormElement;
+use App\Models\FormBuilding\SelectOptionFormElement;
use App\Models\FormBuilding\StyleSheet;
+use App\Models\FormBuilding\TextareaInputFormElement;
+use App\Models\FormBuilding\TextInfoFormElement;
+use App\Models\FormBuilding\TextInputFormElement;
use App\Models\FormMetadata\FormDataSource;
use Filament\Notifications\Notification;
@@ -58,7 +72,7 @@ public function handle()
// Normalize format
$normalizedSchema = $this->normalizeSchema($parsed);
- // Process data sources, javascript, and stylesheets
+ // Process data sources, javascript, and stylesheets
$this->processDataSources($normalizedSchema, $formVersion);
$this->processJavaScript($normalizedSchema, $formVersion);
$this->processStyleSheets($normalizedSchema, $formVersion);
@@ -80,6 +94,40 @@ public function handle()
Cache::put($this->cacheKey . '_progress', "Completed: {$processedElements}/{$totalElements} elements", 3600);
Cache::put($this->cacheKey . '_status', 'complete', 3600);
+ // Check for template conflicts and send database notification
+ $templateConflicts = Cache::get($this->cacheKey . '_template_conflicts', []);
+ if (!empty($templateConflicts)) {
+ $user = \App\Models\User::find($this->userId);
+ if ($user) {
+ $filenames = array_column($templateConflicts, 'filename');
+ $conflictList = implode(', ', $filenames);
+ $count = count($templateConflicts);
+ // Construct action buttons for notification
+ $actions = [];
+ foreach ($templateConflicts as $conflict) {
+ // Choose the route based on the template type
+ $routeName = $conflict['type'] === 'stylesheet'
+ ? 'filament.forms.resources.style-sheets.view'
+ : 'filament.forms.resources.form-scripts.view';
+
+ $actions[] = \Filament\Notifications\Actions\Action::make('view_template_' . $conflict['id'])
+ ->button()
+ ->label("View '{$conflict['filename']}'")
+ ->url(route($routeName, ['record' => $conflict['id']]));
+ }
+
+ Notification::make()
+ ->title('Template Conflicts Detected')
+ ->warning()
+ ->body(
+ "During import of {$formVersion->form->form_id} version {$formVersion->version_number}, {$count} template(s) already existed with different content. "
+ . "The existing versions were kept: {$conflictList}"
+ )
+ ->actions($actions)
+ ->sendToDatabase($user);
+ }
+ }
+
FormVersionUpdateEvent::dispatch(
$formVersion->id,
$formVersion->form_id,
@@ -95,182 +143,80 @@ public function handle()
}
/**
- * Normalize different schema formats into a consistent structure
+ * Normalize the schema (Format 1 only - formversion structure)
+ * Format 2 and 3 are not supported
*/
private function normalizeSchema(array $parsed): array
{
- // Format 1: formversion structure
- if (isset($parsed['formversion'])) {
- return [
- 'elements' => $parsed['formversion']['elements'] ?? [],
- 'dataSources' => $parsed['formversion']['dataSources'] ?? [],
- 'javascript' => $this->extractJavaScriptFromFormversion($parsed['formversion']),
- 'stylesheets' => $this->extractStyleSheetsFromFormversion($parsed['formversion']),
- ];
+ if (!isset($parsed['formversion'])) {
+ throw new \Exception("Only 'formversion' format is supported");
}
- // Format 2: data structure
- if (isset($parsed['data'])) {
- $data = $parsed['data'];
-
- // Accept both "elements" and "items"
- $elements = $data['elements'] ?? $data['items'] ?? [];
-
- // Prefer "javascript" but gracefully convert "scripts" -> sections
- $javascript = $data['javascript'] ?? [];
- if ((!$javascript || !is_array($javascript)) && !empty($data['scripts']) && is_array($data['scripts'])) {
- $javascript = [];
- foreach ($data['scripts'] as $script) {
- $type = $script['type'] ?? 'web';
- $content = $script['content'] ?? '';
- if ($content !== '') {
- $javascript[$type] = ($javascript[$type] ?? '');
- $javascript[$type] .= ($javascript[$type] ? "\n" : "") . $content;
- }
- }
- }
- // Ensure styles array exists
- $data['styles'] ?? $data['styles'] = [];
-
- return [
- 'elements' => is_array($elements) ? $elements : [],
- 'dataSources' => $data['dataSources'] ?? ($parsed['dataSources'] ?? []),
- 'javascript' => is_array($javascript) ? $javascript : [],
- 'stylesheets' => is_array($data['styles']) ? $data['styles'] : [],
- ];
- }
-
- // Format 3: direct structure (legacy)
- if (isset($parsed['elements']) || isset($parsed['items'])) {
- $elements = $parsed['elements'] ?? $parsed['items'] ?? [];
- $javascript = $parsed['javascript'] ?? [];
- if ((!$javascript || !is_array($javascript)) && !empty($parsed['scripts']) && is_array($parsed['scripts'])) {
- $javascript = [];
- foreach ($parsed['scripts'] as $script) {
- $type = $script['type'] ?? 'web';
- $content = $script['content'] ?? '';
- if ($content !== '') {
- $javascript[$type] = ($javascript[$type] ?? '');
- $javascript[$type] .= ($javascript[$type] ? "\n" : "") . $content;
- }
- }
- }
-
- return [
- 'elements' => is_array($elements) ? $elements : [],
- 'dataSources' => $parsed['dataSources'] ?? [],
- 'javascript' => is_array($javascript) ? $javascript : [],
- 'stylesheets' => $parsed['styles'] ?? [],
- ];
- }
+ $formVersion = $parsed['formversion'];
- Log::warning('Unknown schema format, returning empty structure');
- return ['elements' => [], 'dataSources' => [], 'javascript' => [], 'stylesheets' => []];
+ return [
+ 'elements' => $formVersion['elements'] ?? [],
+ 'dataSources' => $formVersion['dataSources'] ?? [],
+ 'javascript' => $this->normalizeCodeAssets($formVersion['scripts'] ?? []),
+ 'stylesheets' => $this->normalizeCodeAssets($formVersion['styles'] ?? []),
+ ];
}
-
/**
- * Extract JavaScript from formversion format
+ * Normalize legacy boolean states to the new string enum states for visibility, required, and read-only fields.
+ * - true / 1 / '1' -> 'always'
+ * - false / 0 / '0' / null -> 'never'
+ * - existing strings ('always', 'icm', 'portal', 'never') are returned as-is
*/
- private function extractJavaScriptFromFormversion(array $formversion): array
+ private function normalizeLegacyState($value): string
{
- $javascript = [];
-
- // Check for scripts array in formversion format
- if (!empty($formversion['scripts']) && is_array($formversion['scripts'])) {
- foreach ($formversion['scripts'] as $script) {
- $type = $script['type'] ?? 'web';
- $content = $script['content'] ?? '';
- if ($type === 'template') {
- // Pass the template's filename as content so it can be attached
- $content = $script['filename'];
- }
- if ($content !== '') {
- if ($type === 'template') {
- if (!array_key_exists($type, $javascript)) {
- $javascript[$type] = [];
- }
- array_push($javascript[$type], $content);
- } else {
- // concatenate if multiple blocks of the same type exist
- $javascript[$type] = ($javascript[$type] ?? '');
- $javascript[$type] .= ($javascript[$type] ? "\n" : "") . $content;
- }
- }
- }
+ if ($value === true || $value === 1 || $value === '1') {
+ return 'always';
+ }
+
+ if ($value === false || $value === 0 || $value === '0' || $value === null) {
+ return 'never';
}
- return $javascript;
+ return (string) $value;
}
/**
- * Extract stylesheets from formversion format
+ * Normalize code assets (scripts/stylesheets) into a standard structure.
+ * Transforms the JSON array format into a structure that matches our storage model:
+ * - Web/PDF assets are concatenated into single strings
+ * - Script templates remain as individual objects with filename and content
*/
- private function extractStyleSheetsFromFormversion(array $formversion): array
+ private function normalizeCodeAssets(array $assets): array
{
- $stylesheets = [];
-
- // Check for styles array in formversion format
- if (!empty($formversion['styles']) && is_array($formversion['styles'])) {
- foreach ($formversion['styles'] as $stylesheet) {
- $type = $stylesheet['type'] ?? 'web';
- $content = $stylesheet['content'] ?? '';
- if ($type === 'template') {
- // Pass the template's filename as content so it can be attached
- $content = $stylesheet['filename'];
- }
- if ($content !== '') {
- if ($type === 'template') {
- if (!array_key_exists($type, $stylesheets)) {
- $stylesheets[$type] = [];
- }
- array_push($stylesheets[$type], $content);
- } else {
- // concatenate if multiple blocks of the same type exist
- $stylesheets[$type] = ($stylesheets[$type] ?? '');
- $stylesheets[$type] .= ($stylesheets[$type] ? "\n" : "") . $content;
+ $normalized = [];
+
+ foreach ($assets as $asset) {
+ $type = $asset['type'] ?? 'web';
+ $content = $asset['content'] ?? '';
+ $filename = $asset['filename'] ?? null;
+
+ if ($type === 'template') {
+ // Templates need to remain as individual objects for separate file creation
+ if ($filename && $filename !== '') {
+ if (!array_key_exists($type, $normalized)) {
+ $normalized[$type] = [];
}
+ $normalized[$type][] = [
+ 'filename' => $filename,
+ 'content' => $content,
+ ];
}
- }
- }
- return $stylesheets;
- }
-
- /**
- * Parse JavaScript content to extract individual sections
- */
- private function parseJavaScriptSections(string $content): array
- {
- $sections = [];
-
- // Split by section comments (// Section: sectionName)
- $lines = explode("\n", $content);
- $currentSection = null;
- $currentCode = [];
-
- foreach ($lines as $line) {
- // Check if this is a section header
- if (preg_match('/\/\/ Section: (.+)/', trim($line), $matches)) {
- // Save previous section if exists
- if ($currentSection && !empty($currentCode)) {
- $sections[$currentSection] = implode("\n", $currentCode);
+ } else {
+ // Web/PDF assets get concatenated into a single string
+ if ($content !== '') {
+ $normalized[$type] = ($normalized[$type] ?? '');
+ $normalized[$type] .= ($normalized[$type] ? "\n" : "") . $content;
}
-
- // Start new section
- $currentSection = trim($matches[1]);
- $currentCode = [];
- } elseif ($currentSection && trim($line) !== '') {
- // Add line to current section (skip empty lines at start)
- $currentCode[] = $line;
}
}
- // Save the last section
- if ($currentSection && !empty($currentCode)) {
- $sections[$currentSection] = implode("\n", $currentCode);
- }
-
- return $sections;
+ return $normalized;
}
/**
@@ -303,7 +249,7 @@ private function processDataSources(array $normalizedSchema, $formVersion): void
$host = $dataSourceData['host'] ?? null;
// Create or find the data source
- $dataSource = \App\Models\FormMetadata\FormDataSource::firstOrCreate([
+ $dataSource = FormDataSource::firstOrCreate([
'name' => $name,
'type' => $type,
], [
@@ -327,203 +273,201 @@ private function processDataSources(array $normalizedSchema, $formVersion): void
}
/**
- * Process JavaScript from the normalized schema
+ * Process JavaScript from the normalized schema. Handles form scripts and templates
*/
private function processJavaScript(array $normalizedSchema, $formVersion): void
{
$javascript = $normalizedSchema['javascript'] ?? [];
- if (!$javascript || !is_array($javascript)) {
+ if (empty($javascript))
return;
- }
-
- // If keys look like types, emit one FormScript per type.
- $knownTypes = ['web', 'pdf', 'portal', 'template'];
- $typeKeys = array_intersect(array_keys($javascript), $knownTypes);
- try {
- if (!class_exists(FormScript::class)) {
- throw new \Exception('FormScript class not found');
- }
+ foreach (['web', 'pdf', 'template'] as $type) {
+ if (!isset($javascript[$type]))
+ continue;
- if (!empty($typeKeys)) {
- foreach ($typeKeys as $t) {
- if ($t === 'template') {
- // Filenames are saved as content
- $filenames = $javascript[$t];
- // Find template by filename and attach to formVersion
- foreach ($filenames as $filename) {
- $id = FormScript::where('filename', $filename)->value('id');
- $formVersion->formScripts()->syncWithoutDetaching($id);
- }
- } else {
- $content = trim((string) ($javascript[$t] ?? ''));
- FormScript::createFormScript($formVersion, $content, $t);
- }
+ if ($type === 'template') {
+ foreach ($javascript[$type] as $templateData) {
+ $this->processTemplateScript($formVersion, $templateData);
}
} else {
- // Fallback: treat as “sections” and combine into a single web script
- $combined = "// Imported JavaScript from template\n\n";
- foreach ($javascript as $sectionName => $jsContent) {
- if (!empty($jsContent)) {
- $combined .= "// Section: {$sectionName}\n{$jsContent}\n\n";
- }
- }
- FormScript::createFormScript($formVersion, trim($combined), 'web');
+ FormScript::createFormScript($formVersion, trim($javascript[$type]), $type);
}
- } catch (\Exception $e) {
- Log::error('Failed to create JavaScript form script(s)', [
- 'form_version_id' => $formVersion->id,
- 'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString()
- ]);
}
}
/**
- * Process stylesheets from the normalized schema
+ * Process stylesheets from the normalized schema. Handles form styles and templates
*/
private function processStyleSheets(array $normalizedSchema, $formVersion): void
{
$stylesheets = $normalizedSchema['stylesheets'] ?? [];
- if (!$stylesheets || !is_array($stylesheets)) {
+ if (empty($stylesheets))
return;
- }
- // If keys look like types, emit one StyleSheet per type.
- $knownTypes = ['web', 'pdf', 'portal', 'template'];
- $typeKeys = array_intersect(array_keys($stylesheets), $knownTypes);
+ foreach (['web', 'pdf', 'template'] as $type) {
+ if (!isset($stylesheets[$type]))
+ continue;
- try {
- if (!class_exists(StyleSheet::class)) {
- throw new \Exception('StyleSheet class not found');
- }
-
- if (!empty($typeKeys)) {
- foreach ($typeKeys as $t) {
- if ($t === 'template') {
- // Filenames are saved as content
- $filenames = $stylesheets[$t];
- // Find template by filename and attach to formVersion
- foreach ($filenames as $filename) {
- $id = StyleSheet::where('filename', $filename)->value('id');
- $formVersion->styleSheets()->syncWithoutDetaching($id);
- }
- } else {
- $content = trim((string) ($stylesheets[$t] ?? ''));
- StyleSheet::createStyleSheet($formVersion, $content, $t);
- }
+ if ($type === 'template') {
+ foreach ($stylesheets[$type] as $templateData) {
+ $this->processTemplateStyleSheet($formVersion, $templateData);
}
} else {
- // Fallback: treat as “sections” and combine into a single web stylesheet
- $combined = "// Imported StyleSheet from template\n\n";
- foreach ($stylesheets as $sectionName => $jsContent) {
- if (!empty($jsContent)) {
- $combined .= "// Section: {$sectionName}\n{$jsContent}\n\n";
- }
- }
- StyleSheet::createStyleSheet($formVersion, trim($combined), 'web');
+ StyleSheet::createStyleSheet($formVersion, trim($stylesheets[$type]), $type);
}
- } catch (\Exception $e) {
- Log::error('Failed to create StyleSheet form stylesheet(s)', [
- 'form_version_id' => $formVersion->id,
- 'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString()
- ]);
}
}
-
/**
- * Return child elements for any container/group regardless of key naming.
+ * Process a template script as a transaction.
+ * Includes conflict resolution for when records exist with the same filename but different content.
*/
- private function getChildElements(array $element): array
+ private function processTemplateScript($formVersion, array $templateData): void
{
- $kids = $element['elements']
- ?? $element['children']
- ?? $element['containerItems']
- ?? $element['fields']
- ?? [];
+ $filename = $templateData['filename'];
+ $incomingContent = $templateData['content'] ?? '';
+
+ DB::transaction(function () use ($formVersion, $filename, $incomingContent) {
+ $existing = FormScript::where('filename', $filename)->where('type', 'template')->first();
+
+ if ($existing) {
+ // Read content from disk to compare
+ if ($existing->getJsContent() === $incomingContent) {
+ // Content matches - use existing template
+ $this->syncTemplate('script', $formVersion, $existing->id);
+ return;
+ }
- return is_array($kids) ? $kids : [];
- }
+ // Content differs - CONFLICT! Use existing but record the conflict
+ $this->syncTemplate('script', $formVersion, $existing->id);
+ $this->recordTemplateConflict($filename, $existing->id, 'script');
- // Add method to count total elements for progress tracking
- protected function countElementsRecursive(array $elements): int
- {
- $count = 0;
- foreach ($elements as $element) {
- $count++;
+ Log::warning('Template script conflict detected during import', [
+ 'filename' => $filename,
+ 'existing_template_id' => $existing->id,
+ 'form_version_id' => $formVersion->id,
+ 'message' => 'Template exists with different content. Using existing template.'
+ ]);
+ } else {
+ // Template doesn't exist - create it
+ $new = FormScript::create(['filename' => $filename, 'type' => 'template']);
- $kids = $this->getChildElements($element);
- if (!empty($kids)) {
- $count += $this->countElementsRecursive($kids);
+ // Save content to disk
+ if (!$new->saveJsContent($incomingContent)) {
+ throw new \Exception('Failed to save template JS content to file');
+ }
+
+ $this->syncTemplate('script', $formVersion, $new->id);
}
- }
- return $count;
+ });
}
/**
- * Check if element is a button type
+ * Process a template stylesheet as a transaction.
+ * Includes conflict resolution for when records exist with the same filename but different content.
*/
- private function isButtonElement($elementType): bool
+ private function processTemplateStyleSheet($formVersion, array $templateData): void
{
- return $elementType === \App\Models\FormBuilding\ButtonInputFormElement::class ||
- $elementType === 'ButtonInputFormElements' ||
- $elementType === 'button';
+ $filename = $templateData['filename'];
+ $incomingContent = $templateData['content'] ?? '';
+
+ DB::transaction(function () use ($formVersion, $filename, $incomingContent) {
+ $existing = StyleSheet::where('filename', $filename)->where('type', 'template')->first();
+
+ if ($existing) {
+ // Read content from disk to compare
+ if ($existing->getCssContent() === $incomingContent) {
+ // Content matches - use existing template
+ $this->syncTemplate('stylesheet', $formVersion, $existing->id);
+ return;
+ }
+
+ // Content differs - CONFLICT! Use existing but record the conflict
+ $this->syncTemplate('stylesheet', $formVersion, $existing->id);
+ $this->recordTemplateConflict($filename, $existing->id, 'stylesheet');
+
+ Log::warning('Template stylesheet conflict detected during import', [
+ 'filename' => $filename,
+ 'existing_template_id' => $existing->id,
+ 'form_version_id' => $formVersion->id,
+ 'message' => 'Template exists with different content. Using existing template.'
+ ]);
+ } else {
+ // Template doesn't exist - create it
+ $new = StyleSheet::create(['filename' => $filename, 'type' => 'template']);
+
+ // Save content to disk
+ if (!$new->saveCssContent($incomingContent)) {
+ throw new \Exception('Failed to save template CSS content to file');
+ }
+
+ $this->syncTemplate('stylesheet', $formVersion, $new->id);
+ }
+ });
}
/**
- * Check if button element has '+' or '-' label
+ * Sync a template to the form version relationship
+ * Encapsulates the mapping between template type and relationship method
*/
- private function isPlusMinusButton(array $element): bool
+ private function syncTemplate(string $type, $formVersion, int $templateId): void
{
- $label = trim($element['label'] ?? $element['name'] ?? '');
- return $label === '+' || $label === '-';
+ if ($type === 'script') {
+ $formVersion->formScripts()->syncWithoutDetaching($templateId);
+ } else {
+ $formVersion->styleSheets()->syncWithoutDetaching($templateId);
+ }
}
/**
- * Check if container element has '+' or '-' label
+ * Record script and stylesheet template conflicts for later notification
*/
- private function isPlusMinusContainer(array $element): bool
+ private function recordTemplateConflict(string $filename, int $existingTemplateId, string $type = 'script'): void
{
- $label = trim($element['label'] ?? $element['name'] ?? '');
- return $label === '+' || $label === '-';
+ $conflicts = Cache::get($this->cacheKey . '_template_conflicts', []);
+ $ids = array_column($conflicts, 'id');
+
+ if (!in_array($existingTemplateId, $ids)) {
+ $conflicts[] = [
+ 'filename' => $filename,
+ 'id' => $existingTemplateId,
+ 'type' => $type,
+ ];
+ Cache::put($this->cacheKey . '_template_conflicts', $conflicts, 3600);
+ }
}
/**
- * Check if element is a container (should not create form field)
+ * Return child elements for any container/group regardless of key naming.
*/
- private function isContainerElement(string $elementType, array $element): bool
+ private function getChildElements(array $element): array
{
- $containerTypes = [
- 'ContainerFormElements',
- \App\Models\FormBuilding\ContainerFormElement::class,
- 'container',
- 'section',
- 'group',
- 'fieldset'
- ];
+ $kids = $element['elements']
+ ?? $element['children']
+ ?? $element['containerItems']
+ ?? $element['fields']
+ ?? [];
- // Check element type directly
- if (in_array($elementType, $containerTypes)) {
- return true;
- }
+ return is_array($kids) ? $kids : [];
+ }
- // Check the actual resolved type
- $resolvedType = $this->resolveElementableType($elementType);
- if ($resolvedType === \App\Models\FormBuilding\ContainerFormElement::class) {
- return true;
- }
+ /**
+ * Count total elements recursively for progress tracking
+ */
+ protected function countElementsRecursive(array $elements): int
+ {
+ $count = 0;
+ foreach ($elements as $element) {
+ $count++;
- // Check container type property
- if (isset($element['containerType'])) {
- return true;
+ $kids = $this->getChildElements($element);
+ if (!empty($kids)) {
+ $count += $this->countElementsRecursive($kids);
+ }
}
-
- return false;
+ return $count;
}
-
/**
* Extract options from different element formats
*/
@@ -531,20 +475,21 @@ private function extractOptions(array $element): array
{
$options = [];
- // Format 1: formversion format with options array
+ // Handle formversion format with options array
if (!empty($element['options']) && is_array($element['options'])) {
-
foreach ($element['options'] as $index => $option) {
if (is_array($option)) {
$optionData = [
'label' => $option['label'] ?? '',
+ 'value' => $option['value'] ?? null,
'order' => $option['order'] ?? ($index + 1),
'description' => $option['description'] ?? null,
];
$options[] = $optionData;
} else {
$optionData = [
- 'label' => (string)$option,
+ 'label' => (string) $option,
+ 'value' => (string) $option,
'order' => $index + 1,
'description' => null,
];
@@ -552,36 +497,6 @@ private function extractOptions(array $element): array
}
}
}
- // Format 2: listItems array
- elseif (!empty($element['listItems']) && is_array($element['listItems'])) {
- foreach ($element['listItems'] as $idx => $item) {
- if (is_array($item)) {
- $options[] = [
- 'label' => $item['label'] ?? $item['text'] ?? $item['name'] ?? $item['value'] ?? '',
- 'order' => $item['order'] ?? ($idx + 1),
- 'description' => $item['description'] ?? null,
- ];
- } else {
- $options[] = [
- 'label' => isset($item['value']) ? $item['value'] : (string)$item,
- 'order' => $idx + 1,
- 'description' => null,
- ];
- }
- }
- }
- // Format 3: attributes.options
- elseif (!empty($element['attributes']['options']) && is_array($element['attributes']['options'])) {
- foreach ($element['attributes']['options'] as $idx => $option) {
- if (is_array($option)) {
- $options[] = [
- 'label' => $option['label'] ?? '',
- 'order' => $option['order'] ?? ($idx + 1),
- 'description' => $option['description'] ?? null,
- ];
- }
- }
- }
// Filter out options with empty labels
$options = array_filter($options, function ($option) {
@@ -600,50 +515,42 @@ private function extractOptions(array $element): array
}
/**
- * Create select options for SelectInputFormElement
+ * Create options for Select, Radio, and CheckboxGroup elements
*/
- private function createSelectOptions($selectModel, array $options): void
+ private function createOptionsForElement($model, string $type, array $options): void
{
- if (empty($options)) return;
-
- foreach ($options as $index => $optionData) {
- if (empty($optionData['label'])) continue; // Skip options without labels
+ if (empty($options))
+ return;
- try {
- \App\Models\FormBuilding\SelectOptionFormElement::createForSelect($selectModel, $optionData);
- } catch (\Exception $e) {
- Log::error('Failed to create select option', [
- 'option_data' => $optionData,
- 'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString()
- ]);
- }
- }
- }
+ $methodMap = [
+ SelectInputFormElement::class => 'createForSelect',
+ RadioInputFormElement::class => 'createForRadio',
+ CheckboxGroupFormElement::class => 'createForCheckboxGroup',
+ ];
- /**
- * Create radio options for RadioInputFormElement
- */
- private function createRadioOptions($radioModel, array $options): void
- {
+ $method = $methodMap[$type] ?? null;
+ if (!$method)
+ return;
- if (empty($options)) return;
+ foreach ($options as $optionData) {
+ if (empty($optionData['label']))
+ continue;
- foreach ($options as $index => $optionData) {
- if (empty($optionData['label'])) continue; // Skip options without labels
try {
- \App\Models\FormBuilding\SelectOptionFormElement::createForRadio($radioModel, $optionData);
+ SelectOptionFormElement::$method($model, $optionData);
} catch (\Exception $e) {
- Log::error('Failed to create radio option', [
+ Log::error('Failed to create option', [
+ 'type' => $type,
'option_data' => $optionData,
'error' => $e->getMessage(),
- 'trace' => $e->getTraceAsString()
]);
}
}
}
- // Updated to include progress tracking
+ /**
+ * Import elements recursively with progress tracking
+ */
protected function importElementsRecursive(array $elements, $parentId, $formVersion, $processedElements = 0, $totalElements = 0, $inRepeatableContainer = false, $inPlusContainer = false)
{
foreach ($elements as $element) {
@@ -678,7 +585,7 @@ protected function importElementsRecursive(array $elements, $parentId, $formVers
$formVersion,
$processedElements,
$totalElements,
- $inRepeatableContainer /* or $childInRepeatable when present */,
+ $inRepeatableContainer /* or $childInRepeatable when present */ ,
$inPlusContainer /* or $childInPlusContainer when present */
);
}
@@ -691,41 +598,42 @@ protected function importElementsRecursive(array $elements, $parentId, $formVers
// Fallback for lowercase/short types
if (!$type) {
$typeMap = [
- 'container' => \App\Models\FormBuilding\ContainerFormElement::class,
- 'group' => \App\Models\FormBuilding\ContainerFormElement::class,
- 'text-input' => \App\Models\FormBuilding\TextInputFormElement::class,
- 'textarea' => \App\Models\FormBuilding\TextareaInputFormElement::class,
- 'textarea-input' => \App\Models\FormBuilding\TextareaInputFormElement::class,
- 'radio' => \App\Models\FormBuilding\RadioInputFormElement::class,
- 'radio-input' => \App\Models\FormBuilding\RadioInputFormElement::class,
- 'dropdown' => \App\Models\FormBuilding\SelectInputFormElement::class,
- 'dropdown-input' => \App\Models\FormBuilding\SelectInputFormElement::class,
- 'select' => \App\Models\FormBuilding\SelectInputFormElement::class,
- 'select-input' => \App\Models\FormBuilding\SelectInputFormElement::class,
- 'checkbox' => \App\Models\FormBuilding\CheckboxInputFormElement::class,
- 'checkbox-input' => \App\Models\FormBuilding\CheckboxInputFormElement::class,
- 'checkbox-group' => \App\Models\FormBuilding\CheckboxGroupFormElement::class,
- 'checkbox-group-input' => \App\Models\FormBuilding\CheckboxGroupFormElement::class,
- 'date' => \App\Models\FormBuilding\DateSelectInputFormElement::class,
- 'date-select-input' => \App\Models\FormBuilding\DateSelectInputFormElement::class,
- 'number' => \App\Models\FormBuilding\NumberInputFormElement::class,
- 'number-input' => \App\Models\FormBuilding\NumberInputFormElement::class,
- 'currency' => \App\Models\FormBuilding\CurrencyInputFormElement::class,
- 'currency-input' => \App\Models\FormBuilding\CurrencyInputFormElement::class,
- 'html' => \App\Models\FormBuilding\HTMLFormElement::class,
- 'text-info' => \App\Models\FormBuilding\TextInfoFormElement::class,
- 'button' => \App\Models\FormBuilding\ButtonInputFormElement::class,
- 'button-input' => \App\Models\FormBuilding\ButtonInputFormElement::class,
+ 'container' => ContainerFormElement::class,
+ 'group' => ContainerFormElement::class,
+ 'text-input' => TextInputFormElement::class,
+ 'textarea' => TextareaInputFormElement::class,
+ 'textarea-input' => TextareaInputFormElement::class,
+ 'radio' => RadioInputFormElement::class,
+ 'radio-input' => RadioInputFormElement::class,
+ 'dropdown' => SelectInputFormElement::class,
+ 'dropdown-input' => SelectInputFormElement::class,
+ 'select' => SelectInputFormElement::class,
+ 'select-input' => SelectInputFormElement::class,
+ 'checkbox' => CheckboxInputFormElement::class,
+ 'checkbox-input' => CheckboxInputFormElement::class,
+ 'checkbox-group' => CheckboxGroupFormElement::class,
+ 'checkbox-group-input' => CheckboxGroupFormElement::class,
+ 'date' => DateSelectInputFormElement::class,
+ 'date-select-input' => DateSelectInputFormElement::class,
+ 'number' => NumberInputFormElement::class,
+ 'number-input' => NumberInputFormElement::class,
+ 'currency' => CurrencyInputFormElement::class,
+ 'currency-input' => CurrencyInputFormElement::class,
+ 'html' => HTMLFormElement::class,
+ 'text-info' => TextInfoFormElement::class,
+ 'button' => ButtonInputFormElement::class,
+ 'button-input' => ButtonInputFormElement::class,
];
if (isset($typeMap[$elementType])) {
$type = $typeMap[$elementType];
}
}
- if (!$type) continue;
+ if (!$type)
+ continue;
$isRepeatableContainer = false;
- if ($type === \App\Models\FormBuilding\ContainerFormElement::class) {
+ if ($type === ContainerFormElement::class) {
$isRepeatableContainer = $this->isRepeatableContainer($element);
}
@@ -738,7 +646,7 @@ protected function importElementsRecursive(array $elements, $parentId, $formVers
$formVersion,
$processedElements,
$totalElements,
- $inRepeatableContainer /* or $childInRepeatable when present */,
+ $inRepeatableContainer /* or $childInRepeatable when present */ ,
$inPlusContainer /* or $childInPlusContainer when present */
);
}
@@ -757,7 +665,7 @@ protected function importElementsRecursive(array $elements, $parentId, $formVers
$humanReadableLabel = null;
// Special handling for TextInfo elements - use content if it's short
- if ($type === \App\Models\FormBuilding\TextInfoFormElement::class && isset($element['content'])) {
+ if ($type === TextInfoFormElement::class && isset($element['content'])) {
$content = trim($element['content']);
if (!empty($content) && strlen($content) <= 30) {
$humanReadableLabel = $content;
@@ -806,11 +714,10 @@ protected function importElementsRecursive(array $elements, $parentId, $formVers
'reference_id' => $referenceId,
'description' => $attributes['description'] ?? '',
'help_text' => $attributes['help_text'] ?? '',
- 'is_read_only' => $attributes['is_read_only'] ? true : false,
- 'custom_read_only' => $attributes['is_read_only'] ? true : false,
- 'visible_web' => $attributes['visible_web'] ?? true,
- 'visible_pdf' => $attributes['visible_pdf'] ?? true,
- 'is_required' => $attributes['is_required'] ?? false,
+ 'visible_web' => $this->normalizeLegacyState($attributes['visible_web'] ?? null),
+ 'visible_pdf' => $this->normalizeLegacyState($attributes['visible_pdf'] ?? null),
+ 'is_required' => $this->normalizeLegacyState($attributes['is_required'] ?? null),
+ 'is_read_only' => $this->normalizeLegacyState($attributes['is_read_only'] ?? null),
'save_on_submit' => $attributes['save_on_submit'] ?? true,
];
@@ -822,69 +729,24 @@ protected function importElementsRecursive(array $elements, $parentId, $formVers
$formElement = null;
- if ($type === \App\Models\FormBuilding\ContainerFormElement::class) {
- $containerModel = \App\Models\FormBuilding\ContainerFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $containerModel->id;
- $formElement = FormElement::create($elementData);
- } else if ($type === \App\Models\FormBuilding\TextInputFormElement::class) {
- $textInputModel = \App\Models\FormBuilding\TextInputFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $textInputModel->id;
+ // Check if the class exists and is an Eloquent model
+ if (class_exists($type) && is_subclass_of($type, \Illuminate\Database\Eloquent\Model::class)) {
+ $elementableModel = $type::create($attributes['attributes']);
+ $elementData['elementable_id'] = $elementableModel->id;
$formElement = FormElement::create($elementData);
- } else if ($type === \App\Models\FormBuilding\TextareaInputFormElement::class) {
- $textareModel = \App\Models\FormBuilding\TextareaInputFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $textareModel->id;
- $formElement = FormElement::create($elementData);
- } elseif ($type === \App\Models\FormBuilding\TextInfoFormElement::class) {
- $textInfoModel = \App\Models\FormBuilding\TextInfoFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $textInfoModel->id;
- $formElement = FormElement::create($elementData);
- } else if ($type === \App\Models\FormBuilding\DateSelectInputFormElement::class) {
- $dateSelectModel = \App\Models\FormBuilding\DateSelectInputFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $dateSelectModel->id;
- $formElement = FormElement::create($elementData);
- } else if ($type === \App\Models\FormBuilding\CheckboxInputFormElement::class) {
- $checkboxInputModel = \App\Models\FormBuilding\CheckboxInputFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $checkboxInputModel->id;
- $formElement = FormElement::create($elementData);
- } else if ($type === \App\Models\FormBuilding\CheckboxGroupFormElement::class) {
- $checkboxGroupModel = \App\Models\FormBuilding\CheckboxGroupFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $checkboxGroupModel->id;
- $formElement = FormElement::create($elementData);
- } else if ($type === \App\Models\FormBuilding\SelectInputFormElement::class) {
- $selectModel = \App\Models\FormBuilding\SelectInputFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $selectModel->id;
- $formElement = FormElement::create($elementData);
- $this->createSelectOptions($selectModel, $options);
- } elseif ($type === \App\Models\FormBuilding\RadioInputFormElement::class) {
- $radioModel = \App\Models\FormBuilding\RadioInputFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $radioModel->id;
- $formElement = FormElement::create($elementData);
- $this->createRadioOptions($radioModel, $options);
- } else if ($type === \App\Models\FormBuilding\NumberInputFormElement::class) {
- $numberInputModel = \App\Models\FormBuilding\NumberInputFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $numberInputModel->id;
- $formElement = FormElement::create($elementData);
- } else if ($type === \App\Models\FormBuilding\CurrencyInputFormElement::class) {
- $currencyInputModel = \App\Models\FormBuilding\CurrencyInputFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $currencyInputModel->id;
- $formElement = FormElement::create($elementData);
- } else if ($type === \App\Models\FormBuilding\ButtonInputFormElement::class) {
- $buttonModel = \App\Models\FormBuilding\ButtonInputFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $buttonModel->id;
- $formElement = FormElement::create($elementData);
- } else if ($type === \App\Models\FormBuilding\HTMLFormElement::class) {
- $htmlModel = \App\Models\FormBuilding\HTMLFormElement::updateOrCreate($attributes['attributes']);
- $elementData['elementable_id'] = $htmlModel->id;
- $formElement = FormElement::create($elementData);
- } else {
- if (method_exists($type, 'create')) {
- $elementableModel = $type::create($attributes['attributes']);
- $elementData['elementable_id'] = $elementableModel->id;
+
+ // Handle options for Select, Radio, and CheckboxGroup elements
+ if (
+ in_array($type, [
+ SelectInputFormElement::class,
+ RadioInputFormElement::class,
+ CheckboxGroupFormElement::class
+ ])
+ ) {
+ $this->createOptionsForElement($elementableModel, $type, $options);
}
- $formElement = FormElement::create($elementData);
}
-
if ($formElement) {
// Create data binding
if ($dataBindingInfo) {
@@ -919,7 +781,7 @@ protected function importElementsRecursive(array $elements, $parentId, $formVers
}
} catch (\Exception $e) {
Log::error('Failed to import individual element', [
- 'element' => $element['name'],
+ 'element' => $element['name'] ?? 'unknown',
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
@@ -927,11 +789,12 @@ protected function importElementsRecursive(array $elements, $parentId, $formVers
}
if ($this->defaultDataSourceError) {
- Notification::make()
- ->title("Failed to create the default data source 'Imported Data Source'")
- ->body("Please reseed the form_data_sources table using the following command: sail artisan db:seed --class=FormDataSourceSeeder")
- ->warning()
- ->send();
+ Cache::put(
+ $this->cacheKey . '_warning',
+ "Failed to create the default data source.
+ Please reseed the form_data_sources table using the following command: sail artisan db:seed --class=FormDataSourceSeeder",
+ 3600
+ );
}
return $processedElements;
@@ -1060,22 +923,25 @@ private function createDataBinding($formElement, array $dataBindingInfo, $formVe
}
}
+ /**
+ * Resolve element type string to fully qualified class name
+ */
private function resolveElementableType(string $elementType): ?string
{
$map = [
- 'TextInputFormElements' => \App\Models\FormBuilding\TextInputFormElement::class,
- 'TextareaInputFormElements' => \App\Models\FormBuilding\TextareaInputFormElement::class,
- 'TextInfoFormElements' => \App\Models\FormBuilding\TextInfoFormElement::class,
- 'DateSelectInputFormElements' => \App\Models\FormBuilding\DateSelectInputFormElement::class,
- 'CheckboxInputFormElements' => \App\Models\FormBuilding\CheckboxInputFormElement::class,
- 'CheckboxGroupFormElements' => \App\Models\FormBuilding\CheckboxGroupFormElement::class,
- 'SelectInputFormElements' => \App\Models\FormBuilding\SelectInputFormElement::class,
- 'RadioInputFormElements' => \App\Models\FormBuilding\RadioInputFormElement::class,
- 'NumberInputFormElements' => \App\Models\FormBuilding\NumberInputFormElement::class,
- 'CurrencyInputFormElements' => \App\Models\FormBuilding\CurrencyInputFormElement::class,
- 'ButtonInputFormElements' => \App\Models\FormBuilding\ButtonInputFormElement::class,
- 'HTMLFormElements' => \App\Models\FormBuilding\HTMLFormElement::class,
- 'ContainerFormElements' => \App\Models\FormBuilding\ContainerFormElement::class,
+ 'TextInputFormElements' => TextInputFormElement::class,
+ 'TextareaInputFormElements' => TextareaInputFormElement::class,
+ 'TextInfoFormElements' => TextInfoFormElement::class,
+ 'DateSelectInputFormElements' => DateSelectInputFormElement::class,
+ 'CheckboxInputFormElements' => CheckboxInputFormElement::class,
+ 'CheckboxGroupFormElements' => CheckboxGroupFormElement::class,
+ 'SelectInputFormElements' => SelectInputFormElement::class,
+ 'RadioInputFormElements' => RadioInputFormElement::class,
+ 'NumberInputFormElements' => NumberInputFormElement::class,
+ 'CurrencyInputFormElements' => CurrencyInputFormElement::class,
+ 'ButtonInputFormElements' => ButtonInputFormElement::class,
+ 'HTMLFormElements' => HTMLFormElement::class,
+ 'ContainerFormElements' => ContainerFormElement::class,
];
if (isset($map[$elementType])) {
@@ -1091,6 +957,9 @@ private function resolveElementableType(string $elementType): ?string
return null;
}
+ /**
+ * Extract element attributes from the element array
+ */
private function extractElementAttributes(array $element): array
{
$exclude = [
@@ -1114,6 +983,9 @@ private function extractElementAttributes(array $element): array
];
$attributes = [];
+ // Initialize the nested array immediately so we don't get null errors
+ $attributes['attributes'] = [];
+
foreach ($element as $key => $value) {
if (!in_array($key, $exclude, true)) {
$attributes[$key] = $value;
@@ -1122,51 +994,50 @@ private function extractElementAttributes(array $element): array
// Handle both formats for repeatable containers
if (isset($element['repeats'])) {
- $attributes['is_repeatable'] = (bool)$element['repeats'];
- $attributes['attributes']['is_repeatable'] = (bool)$element['repeats'];
+ $attributes['attributes']['is_repeatable'] = (bool) $element['repeats'];
if (isset($element['attributes']['repeaterItemLabel'])) {
$attributes['attributes']['repeater_item_label'] = $element['attributes']['repeaterItemLabel'];
}
} elseif (isset($element['attributes']['isRepeatable'])) {
- $attributes['is_repeatable'] = (bool)$element['attributes']['isRepeatable'];
- $attributes['attributes']['is_repeatable'] = (bool)$element['attributes']['isRepeatable'];
+ $attributes['attributes']['is_repeatable'] = (bool) $element['attributes']['isRepeatable'];
if (isset($element['attributes']['repeaterItemLabel'])) {
$attributes['attributes']['repeater_item_label'] = $element['attributes']['repeaterItemLabel'];
}
}
+
// Handle min/max repeats
if (isset($element['minRepeats'])) {
- $attributes['min_repeats'] = (int)$element['minRepeats'];
+ $attributes['attributes']['min_repeats'] = (int) $element['minRepeats'];
} elseif (isset($element['min_repeats'])) {
- $attributes['min_repeats'] = (int)$element['min_repeats'];
+ $attributes['attributes']['min_repeats'] = (int) $element['min_repeats'];
}
if (isset($element['maxRepeats'])) {
- $attributes['max_repeats'] = (int)$element['maxRepeats'];
+ $attributes['attributes']['max_repeats'] = (int) $element['maxRepeats'];
} elseif (isset($element['max_repeats'])) {
- $attributes['max_repeats'] = (int)$element['max_repeats'];
+ $attributes['attributes']['max_repeats'] = (int) $element['max_repeats'];
}
// Handle container type mapping
if (isset($element['containerType'])) {
- $attributes['container_type'] = $element['containerType'];
+ $attributes['attributes']['container_type'] = $element['containerType'];
} elseif (isset($element['attributes']['containerType'])) {
$attributes['attributes']['container_type'] = $element['attributes']['containerType'];
}
// Handle collapsible properties
if (isset($element['collapsible'])) {
- $attributes['collapsible'] = (bool)$element['collapsible'];
+ $attributes['attributes']['collapsible'] = (bool) $element['collapsible'];
}
if (isset($element['collapsedByDefault'])) {
- $attributes['collapsed_by_default'] = (bool)$element['collapsedByDefault'];
+ $attributes['attributes']['collapsed_by_default'] = (bool) $element['collapsedByDefault'];
}
$elementType = $element['elementType'] ?? $element['type'] ?? '';
// For TextInfo elements, ensure content is properly mapped
if ($elementType === 'TextInfoFormElements' && isset($element['content'])) {
- $attributes['content'] = $element['content'];
+ $attributes['attributes']['content'] = $element['content'];
}
// For Button elements, ensure label is properly mapped
@@ -1174,43 +1045,41 @@ private function extractElementAttributes(array $element): array
$attributes['attributes']['text'] = $element['label'];
}
- // Handle options/list items (both formats)
- // if (isset($element['listItems'])) {
- // $attributes['listItems'] = $element['listItems'];
- // } elseif (isset($element['options'])) {
- // $attributes['options'] = $element['options'];
- // }
-
// Handle default values
if (isset($element['attributes']['value'])) {
- $attributes['attributes']['defaultValue'] = $element['attributes']['value'];
+ $attributes['attributes']['default_value'] = $element['attributes']['value'];
+ } elseif (isset($element['attributes']['defaultValue'])) {
+ $attributes['attributes']['default_value'] = $element['attributes']['defaultValue'];
}
// Handle date format
if (isset($element['dateFormat'])) {
- $attributes['dateFormat'] = \App\Models\FormBuilding\DateSelectInputFormElement::convertFromFlatpickrFormat($element['dateFormat']);
+ $attributes['attributes']['date_format'] = DateSelectInputFormElement::convertFromFlatpickrFormat($element['dateFormat']);
} else if (isset($element['attributes']['dateFormat'])) {
- $attributes['attributes']['dateFormat'] = \App\Models\FormBuilding\DateSelectInputFormElement::convertFromFlatpickrFormat($element['attributes']['dateFormat']);
+ $attributes['attributes']['date_format'] = DateSelectInputFormElement::convertFromFlatpickrFormat($element['attributes']['dateFormat']);
}
// Handle HTML content
if (isset($element['htmlContent'])) {
- $attributes['html_content'] = $element['htmlContent'];
+ $attributes['attributes']['html_content'] = $element['htmlContent'];
} else if (isset($element['attributes']['htmlContent'])) {
$attributes['attributes']['html_content'] = $element['attributes']['htmlContent'];
}
-
- // Ensure $attributes['attributes] exists
- if (!isset($attributes['attributes'])) {
- $attributes['attributes'] = [];
+ // Convert all keys in $attributes['attributes'] to snake_case
+ if (is_array($attributes['attributes'])) {
+ $snakeAttributes = [];
+ foreach ($attributes['attributes'] as $key => $value) {
+ $snakeAttributes[\Illuminate\Support\Str::snake($key)] = $value;
+ }
+ $attributes['attributes'] = $snakeAttributes;
}
return $attributes;
}
/**
- * Determine if the given element is a repeatable container.
+ * Determine if the given element is a repeatable container
*/
private function isRepeatableContainer(array $element): bool
{
@@ -1224,12 +1093,12 @@ private function isRepeatableContainer(array $element): bool
}
/**
- * Determine if the given type is a text field element.
+ * Determine if the given type is a text field element
*/
private function isTextField($type): bool
{
$textFieldTypes = [
- \App\Models\FormBuilding\TextInfoFormElement::class,
+ TextInfoFormElement::class,
];
return in_array($type, $textFieldTypes, true);
}
diff --git a/app/Livewire/FormElementTreeBuilder.php b/app/Livewire/FormElementTreeBuilder.php
index 6a077e76..67cf9b53 100644
--- a/app/Livewire/FormElementTreeBuilder.php
+++ b/app/Livewire/FormElementTreeBuilder.php
@@ -388,9 +388,9 @@ protected function mutateFormDataBeforeSave(array $data): array
// Filter out null values from elementable data to let model defaults apply
// But convert null values to empty strings for text fields that the user might want to clear
- $textFields = ['labelText', 'placeholder', 'helperText', 'mask', 'maskErrorMessage', 'content', 'legend', 'repeater_item_label'];
- $numericFields = ['min', 'max', 'step', 'defaultValue', 'maxCount', 'rows', 'cols', 'order', 'min_repeats', 'max_repeats'];
- $nullableFields = ['level', 'defaultSelected', 'minDate', 'maxDate'];
+ $textFields = ['label_text', 'placeholder', 'helperText', 'mask', 'mask_error_message', 'content', 'legend', 'repeater_item_label'];
+ $numericFields = ['min', 'max', 'step', 'default_value', 'max_count', 'rows', 'cols', 'order', 'min_repeats', 'max_repeats'];
+ $nullableFields = ['level', 'default_selected', 'min_date', 'max_date'];
$filteredElementableData = [];
foreach ($elementableData as $key => $value) {
@@ -557,9 +557,9 @@ protected function mutateFormDataBeforeCreate(array $data): array
// Filter out null values from elementable data to let model defaults apply
// But convert null values to empty strings for text fields that the user might want to clear
- $textFields = ['labelText', 'placeholder', 'helperText', 'mask', 'maskErrorMessage', 'content', 'legend', 'repeater_item_label'];
- $numericFields = ['min', 'max', 'step', 'defaultValue', 'maxCount', 'rows', 'cols', 'order', 'min_repeats', 'max_repeats'];
- $nullableFields = ['level', 'defaultSelected', 'minDate', 'maxDate'];
+ $textFields = ['label_text', 'placeholder', 'helperText', 'mask', 'mask_error_message', 'content', 'legend', 'repeater_item_label'];
+ $numericFields = ['min', 'max', 'step', 'default_value', 'max_count', 'rows', 'cols', 'order', 'min_repeats', 'max_repeats'];
+ $nullableFields = ['level', 'default_selected', 'min_date', 'max_date'];
$filteredElementableData = [];
foreach ($elementableData as $key => $value) {
diff --git a/app/Models/FormBuilding/ButtonInputFormElement.php b/app/Models/FormBuilding/ButtonInputFormElement.php
index 15d58d76..687bf020 100644
--- a/app/Models/FormBuilding/ButtonInputFormElement.php
+++ b/app/Models/FormBuilding/ButtonInputFormElement.php
@@ -17,7 +17,7 @@ class ButtonInputFormElement extends Model
protected $fillable = [
'text',
'kind',
- 'enableVarSub',
+ 'enable_var_sub',
];
protected $casts = [
@@ -30,12 +30,11 @@ class ButtonInputFormElement extends Model
public static function getFilamentSchema(bool $disabled = false): array
{
return [
- TextInput::make('elementable_data.text')
+ SchemaHelper::getLabelTextField($disabled)
->label('Button Text')
->default('Submit')
- ->required(true)
->autocomplete(false)
- ->disabled($disabled),
+ ->required(true),
SchemaHelper::getEnableVariableSubstitutionToggle($disabled),
Select::make('elementable_data.kind')
->label('Button Kind')
@@ -61,7 +60,7 @@ public function getData(): array
return [
'text' => $this->text,
'kind' => $this->kind,
- 'enableVarSub' => $this->enableVarSub,
+ 'enable_var_sub' => $this->enable_var_sub,
];
}
@@ -89,7 +88,7 @@ public static function getDefaultData(): array
return [
'text' => 'Submit',
'kind' => 'primary',
- 'enableVarSub' => false,
+ 'enable_var_sub' => false,
];
}
}
diff --git a/app/Models/FormBuilding/CheckboxGroupFormElement.php b/app/Models/FormBuilding/CheckboxGroupFormElement.php
index 937cbc82..c325b2cc 100644
--- a/app/Models/FormBuilding/CheckboxGroupFormElement.php
+++ b/app/Models/FormBuilding/CheckboxGroupFormElement.php
@@ -18,21 +18,21 @@ class CheckboxGroupFormElement extends Model
use HasFactory, SoftDeletes;
protected $fillable = [
- 'labelText',
- 'hideLabel',
- 'enableVarSub',
- 'defaultSelected',
+ 'label_text',
+ 'hide_label',
+ 'enable_var_sub',
+ 'default_selected',
];
protected $casts = [
- 'hideLabel' => 'boolean',
- 'defaultSelected' => 'array',
+ 'hide_label' => 'boolean',
+ 'default_selected' => 'array',
];
protected $attributes = [
- 'hideLabel' => false,
- 'labelText' => '',
- 'defaultSelected' => null,
+ 'hide_label' => false,
+ 'label_text' => '',
+ 'default_selected' => null,
];
/**
@@ -41,72 +41,11 @@ class CheckboxGroupFormElement extends Model
public static function getFilamentSchema(bool $disabled = false): array
{
return [
- Fieldset::make('Field Label')
- ->schema([
- SchemaHelper::getLabelTextField($disabled)->required(),
- SchemaHelper::getEnableVariableSubstitutionToggle($disabled),
- SchemaHelper::getHideLabelToggle($disabled),
- ])
- ->columns(1),
+ SchemaHelper::getCommonCarbonFields($disabled, true),
Fieldset::make('Values')
->schema([
- Select::make('elementable_data.defaultSelected')
- ->label('Default Selected Value')
- ->multiple()
- ->nullable()
- ->live()
- ->options(function (callable $get) {
- $options = $get('elementable_data.options') ?? [];
- $selectOptions = [];
- foreach ($options as $option) {
- if (!empty($option['value'])) {
- $selectOptions[$option['value']] = $option['label'] ?? $option['value'];
- }
- }
- return $selectOptions;
- })
- ->disabled($disabled),
- Repeater::make('elementable_data.options')
- ->label('Options')
- ->schema([
- TextInput::make('label')
- ->label('Option Label')
- ->required()
- ->columnSpan(2)
- ->autocomplete(false)
- ->live(onBlur: true)
- ->afterStateUpdated(function (callable $set, callable $get, $state) {
- $value = $get('value');
- if (empty($value) && !empty($state)) {
- $slug = \Illuminate\Support\Str::slug($state, '-');
- $set('value', $slug);
- }
- }),
- TextInput::make('value')
- ->label('Option Value')
- ->required()
- ->columnSpan(2)
- ->suffixAction(
- \Filament\Forms\Components\Actions\Action::make('regenerate_value')
- ->icon('heroicon-o-arrow-path')
- ->tooltip('Regenerate from Option Label')
- ->action(function (callable $set, callable $get) {
- $label = $get('label');
- if (!empty($label)) {
- $slug = \Illuminate\Support\Str::slug($label, '-');
- $set('value', $slug);
- }
- })
- ),
- ])
- ->columns(2)
- ->defaultItems(1)
- ->addActionLabel('Add Option')
- ->reorderableWithButtons()
- ->collapsible()
- ->itemLabel(fn(array $state): ?string => $state['label'] ?? 'Option')
- ->disabled($disabled)
- ->minItems(1),
+ SchemaHelper::getOptionsDefaultSelectedSelect($disabled, true),
+ SchemaHelper::getOptionsRepeater($disabled),
])
->columns(1),
];
@@ -126,10 +65,10 @@ public function formElement(): MorphOne
public function getData(): array
{
return [
- 'labelText' => $this->labelText,
- 'hideLabel' => $this->hideLabel,
- 'enableVarSub' => $this->enableVarSub,
- 'defaultSelected' => $this->defaultSelected ?? [],
+ 'label_text' => $this->label_text,
+ 'hide_label' => $this->hide_label,
+ 'enable_var_sub' => $this->enable_var_sub,
+ 'default_selected' => $this->default_selected ?? [],
];
}
@@ -147,10 +86,10 @@ public function options(): MorphMany
public static function getDefaultData(): array
{
return [
- 'hideLabel' => false,
- 'labelText' => '',
- 'enableVarSub' => false,
- 'defaultSelected' => [],
+ 'hide_label' => false,
+ 'label_text' => '',
+ 'enable_var_sub' => false,
+ 'default_selected' => [],
'options' => [
['label' => 'Option 1', 'value' => 'option_1'],
['label' => 'Option 2', 'value' => 'option_2'],
diff --git a/app/Models/FormBuilding/CheckboxInputFormElement.php b/app/Models/FormBuilding/CheckboxInputFormElement.php
index 562c09f2..cdfedd16 100644
--- a/app/Models/FormBuilding/CheckboxInputFormElement.php
+++ b/app/Models/FormBuilding/CheckboxInputFormElement.php
@@ -15,20 +15,20 @@ class CheckboxInputFormElement extends Model
use HasFactory, SoftDeletes;
protected $fillable = [
- 'labelText',
- 'hideLabel',
- 'defaultChecked',
- 'enableVarSub',
+ 'label_text',
+ 'hide_label',
+ 'default_checked',
+ 'enable_var_sub',
];
protected $casts = [
- 'hideLabel' => 'boolean',
- 'defaultChecked' => 'boolean',
+ 'hide_label' => 'boolean',
+ 'default_checked' => 'boolean',
];
protected $attributes = [
- 'hideLabel' => false,
- 'defaultChecked' => false,
+ 'hide_label' => false,
+ 'default_checked' => false,
];
/**
@@ -37,20 +37,11 @@ class CheckboxInputFormElement extends Model
public static function getFilamentSchema(bool $disabled = false): array
{
return [
- Fieldset::make('Field Label')
- ->schema([
- SchemaHelper::getLabelTextField($disabled)
- ->label('Checkbox Label')
- ->autocomplete(false)
- ->required(),
- SchemaHelper::getEnableVariableSubstitutionToggle($disabled),
- SchemaHelper::getHideLabelToggle($disabled),
- ])
- ->columns(1),
- Toggle::make('elementable_data.defaultChecked')
- ->label('Default Checked')
- ->default(false)
- ->disabled($disabled),
+ SchemaHelper::getCommonCarbonFields($disabled, true),
+ Toggle::make('elementable_data.default_checked')
+ ->label('Default Checked')
+ ->default(false)
+ ->disabled($disabled),
];
}
@@ -68,10 +59,10 @@ public function formElement(): MorphOne
public function getData(): array
{
return [
- 'labelText' => $this->labelText,
- 'hideLabel' => $this->hideLabel,
- 'defaultChecked' => $this->defaultChecked,
- 'enableVarSub' => $this->enableVarSub,
+ 'label_text' => $this->label_text,
+ 'hide_label' => $this->hide_label,
+ 'default_checked' => $this->default_checked,
+ 'enable_var_sub' => $this->enable_var_sub,
];
}
@@ -81,10 +72,10 @@ public function getData(): array
public static function getDefaultData(): array
{
return [
- 'hideLabel' => false,
- 'defaultChecked' => false,
- 'labelText' => '',
- 'enableVarSub' => false,
+ 'hide_label' => false,
+ 'default_checked' => false,
+ 'label_text' => '',
+ 'enable_var_sub' => false,
];
}
}
diff --git a/app/Models/FormBuilding/ContainerFormElement.php b/app/Models/FormBuilding/ContainerFormElement.php
index b7423c71..d9f24e02 100644
--- a/app/Models/FormBuilding/ContainerFormElement.php
+++ b/app/Models/FormBuilding/ContainerFormElement.php
@@ -28,7 +28,7 @@ class ContainerFormElement extends Model
'min_repeats',
'max_repeats',
'legend',
- 'enableVarSub',
+ 'enable_var_sub',
'level'
];
@@ -46,7 +46,8 @@ class ContainerFormElement extends Model
protected static function formatInteger(string $target): Closure
{
return function ($state, callable $set, Get $get) use ($target) {
- if ($state === null) return;
+ if ($state === null)
+ return;
$raw = trim((string) $state);
@@ -101,6 +102,7 @@ public static function getFilamentSchema(bool $disabled = false): array
->columnSpan(2)
->helperText('Label for individual repeater items (e.g., "Item", "Entry")')
->disabled($disabled)
+ ->maxLength(255)
->visible(fn(callable $get) => $get('elementable_data.is_repeatable')),
TextInput::make('elementable_data.min_repeats')
->label('Minimum Repeats')
@@ -155,19 +157,10 @@ public static function getFilamentSchema(bool $disabled = false): array
->nullable()
->helperText('Optional level override for the label (e.g., h2, h3, etc.)')
->disabled($disabled),
- TextInput::make('elementable_data.legend')
+ SchemaHelper::getLabelTextField($disabled)
->label('Legend/Title')
->helperText('Optional title for the container')
- ->suffixAction(Action::make('generate_label_text')
- ->icon('heroicon-o-arrow-path')
- ->tooltip('Regenerate from Element Name')
- ->action(function (callable $set, callable $get) {
- $name = $get('name');
- if (!empty($name)) {
- $set('elementable_data.legend', $name);
- }
- }))
- ->disabled($disabled),
+ ->maxLength(255),
SchemaHelper::getEnableVariableSubstitutionToggle($disabled),
])
->columns(1),
@@ -194,7 +187,7 @@ public function getData(): array
'min_repeats' => $this->min_repeats,
'max_repeats' => $this->max_repeats,
'legend' => $this->legend,
- 'enableVarSub' => $this->enableVarSub,
+ 'enable_var_sub' => $this->enable_var_sub,
'level' => $this->level,
];
}
@@ -222,7 +215,7 @@ public static function getDefaultData(): array
'container_type' => 'section',
'is_repeatable' => false,
'legend' => '',
- 'enableVarSub' => false,
+ 'enable_var_sub' => false,
'repeater_item_label' => '',
'min_repeats' => null,
'max_repeats' => null,
diff --git a/app/Models/FormBuilding/CurrencyInputFormElement.php b/app/Models/FormBuilding/CurrencyInputFormElement.php
index 82dacbe4..eeb42f59 100644
--- a/app/Models/FormBuilding/CurrencyInputFormElement.php
+++ b/app/Models/FormBuilding/CurrencyInputFormElement.php
@@ -19,32 +19,33 @@ class CurrencyInputFormElement extends Model
protected $fillable = [
'placeholder',
- 'labelText',
- 'hideLabel',
- 'enableVarSub',
+ 'label_text',
+ 'hide_label',
+ 'enable_var_sub',
'min',
'max',
- 'defaultValue',
+ 'default_value',
];
protected $casts = [
- 'hideLabel' => 'boolean',
+ 'hide_label' => 'boolean',
'min' => 'integer',
'max' => 'integer',
- 'defaultValue' => 'float',
+ 'default_value' => 'float',
];
protected $attributes = [
- 'hideLabel' => false,
+ 'hide_label' => false,
];
/*
* Format the value as currency
- */
+ */
protected static function formatCurrency(string $target): Closure
{
return function ($state, callable $set) use ($target): void {
- if ($state === null) return;
+ if ($state === null)
+ return;
$raw = trim((string) $state);
@@ -56,17 +57,20 @@ protected static function formatCurrency(string $target): Closure
// Collapse lone sign/dot or all-zero forms to "0.00"
$isZeroish = static function (string $s): bool {
$s = trim($s);
- if ($s === '') return false;
+ if ($s === '')
+ return false;
// remove leading sign
$s = ltrim($s, "+-");
// empty after sign or just a dot -> zero-ish
- if ($s === '' || $s === '.') return true;
+ if ($s === '' || $s === '.')
+ return true;
// keep only digits and a single dot
$clean = preg_replace('/[^\d.]/', '', $s) ?? '';
- if ($clean === '' || substr_count($clean, '.') > 1) return false;
+ if ($clean === '' || substr_count($clean, '.') > 1)
+ return false;
// if all remaining digits are zeros, it's zero-ish
$digitsOnly = str_replace('.', '', $clean);
@@ -86,11 +90,11 @@ protected static function formatCurrency(string $target): Closure
}
// Extract sign once; operate on a signless body
- $neg = ($raw !== '' && $raw[0] === '-');
+ $neg = ($raw !== '' && $raw[0] === '-');
$body = ltrim($raw, '+-');
// Split into integer + fractional segments
- $int = $body;
+ $int = $body;
$frac = '';
if (strpos($body, '.') !== false) {
[$int, $frac] = explode('.', $body, 2);
@@ -98,7 +102,8 @@ protected static function formatCurrency(string $target): Closure
// Normalize integer: strip extra leading zeros, keep at least one '0'
$int = ltrim($int, '0');
- if ($int === '') $int = '0';
+ if ($int === '')
+ $int = '0';
// Fraction handling:
// - If <= 2 digits: pad to exactly 2
@@ -142,68 +147,66 @@ public static function getFilamentSchema(bool $disabled = false): array
$noSci = 'not_regex:/[eE]/'; // forbid scientific notation
$currencyRegex = 'regex:/^-?\d+(\.\d{1,2})?$/';
- return array_merge(
+ return [
SchemaHelper::getCommonCarbonFields($disabled),
- [
- Fieldset::make('Value')
- ->schema([
- SchemaHelper::getPlaceholderTextField($disabled)
- ->columnSpan(3),
- TextInput::make('elementable_data.defaultValue')
- ->label('Default Value')
- ->numeric()
- ->nullable()
- ->step(.01)
- ->live(onBlur: true)
- ->afterStateUpdated(self::formatCurrency('elementable_data.defaultValue'))
- ->rules([$currencyRegex, $noSci])
- ->rule(NumericRules::compareWith(
- minPath: 'elementable_data.min',
- maxPath: 'elementable_data.max',
- options: [
- 'format' => fn(float $n) => number_format($n, 2, '.', ''),
- ]
- ))
- ->columnSpan(1)
- ->disabled($disabled),
- TextInput::make('elementable_data.min')
- ->label('Minimum Value')
- ->numeric()
- ->nullable()
- ->step(.01)
- ->live(onBlur: true)
- ->afterStateUpdated(self::formatCurrency('elementable_data.min'))
- ->rules([$currencyRegex, $noSci])
- ->rule(NumericRules::compareWith(
- minPath: null,
- maxPath: 'elementable_data.max',
- options: [
- 'format' => fn(float $n) => number_format($n, 2, '.', ''),
- ]
- ))
- ->columnSpan(1)
- ->disabled($disabled),
- TextInput::make('elementable_data.max')
- ->label('Maximum Value')
- ->numeric()
- ->nullable()
- ->step(.01)
- ->live(onBlur: true)
- ->afterStateUpdated(self::formatCurrency('elementable_data.max'))
- ->rules([$currencyRegex, $noSci])
- ->rule(NumericRules::compareWith(
- minPath: 'elementable_data.min',
- maxPath: null,
- options: [
- 'format' => fn(float $n) => number_format($n, 2, '.', ''),
- ]
- ))
- ->columnSpan(1)
- ->disabled($disabled),
- ])
- ->columns(3),
- ]
- );
+ Fieldset::make('Value')
+ ->schema([
+ SchemaHelper::getPlaceholderTextField($disabled)
+ ->columnSpan(3),
+ TextInput::make('elementable_data.default_value')
+ ->label('Default Value')
+ ->numeric()
+ ->nullable()
+ ->step(.01)
+ ->live(onBlur: true)
+ ->afterStateUpdated(self::formatCurrency('elementable_data.default_value'))
+ ->rules([$currencyRegex, $noSci])
+ ->rule(NumericRules::compareWith(
+ minPath: 'elementable_data.min',
+ maxPath: 'elementable_data.max',
+ options: [
+ 'format' => fn(float $n) => number_format($n, 2, '.', ''),
+ ]
+ ))
+ ->columnSpan(1)
+ ->disabled($disabled),
+ TextInput::make('elementable_data.min')
+ ->label('Minimum Value')
+ ->numeric()
+ ->nullable()
+ ->step(.01)
+ ->live(onBlur: true)
+ ->afterStateUpdated(self::formatCurrency('elementable_data.min'))
+ ->rules([$currencyRegex, $noSci])
+ ->rule(NumericRules::compareWith(
+ minPath: null,
+ maxPath: 'elementable_data.max',
+ options: [
+ 'format' => fn(float $n) => number_format($n, 2, '.', ''),
+ ]
+ ))
+ ->columnSpan(1)
+ ->disabled($disabled),
+ TextInput::make('elementable_data.max')
+ ->label('Maximum Value')
+ ->numeric()
+ ->nullable()
+ ->step(.01)
+ ->live(onBlur: true)
+ ->afterStateUpdated(self::formatCurrency('elementable_data.max'))
+ ->rules([$currencyRegex, $noSci])
+ ->rule(NumericRules::compareWith(
+ minPath: 'elementable_data.min',
+ maxPath: null,
+ options: [
+ 'format' => fn(float $n) => number_format($n, 2, '.', ''),
+ ]
+ ))
+ ->columnSpan(1)
+ ->disabled($disabled),
+ ])
+ ->columns(3),
+ ];
}
/**
@@ -221,12 +224,12 @@ public function getData(): array
{
return [
'placeholder' => $this->placeholder,
- 'labelText' => $this->labelText,
- 'hideLabel' => $this->hideLabel,
- 'enableVarSub' => $this->enableVarSub,
+ 'label_text' => $this->label_text,
+ 'hide_label' => $this->hide_label,
+ 'enable_var_sub' => $this->enable_var_sub,
'min' => $this->min,
'max' => $this->max,
- 'defaultValue' => $this->defaultValue,
+ 'default_value' => $this->default_value,
];
}
@@ -237,12 +240,12 @@ public static function getDefaultData(): array
{
return [
'placeholder' => '',
- 'labelText' => '',
- 'hideLabel' => false,
- 'enableVarSub' => false,
+ 'label_text' => '',
+ 'hide_label' => false,
+ 'enable_var_sub' => false,
'min' => null,
'max' => null,
- 'defaultValue' => null,
+ 'default_value' => null,
];
}
}
diff --git a/app/Models/FormBuilding/DateSelectInputFormElement.php b/app/Models/FormBuilding/DateSelectInputFormElement.php
index f7073646..22d8bbe0 100644
--- a/app/Models/FormBuilding/DateSelectInputFormElement.php
+++ b/app/Models/FormBuilding/DateSelectInputFormElement.php
@@ -17,23 +17,23 @@ class DateSelectInputFormElement extends Model
protected $fillable = [
'placeholder',
- 'labelText',
- 'hideLabel',
- 'enableVarSub',
- 'minDate',
- 'maxDate',
- 'dateFormat',
+ 'label_text',
+ 'hide_label',
+ 'enable_var_sub',
+ 'min_date',
+ 'max_date',
+ 'date_format',
];
protected $casts = [
- 'hideLabel' => 'boolean',
- 'minDate' => 'date',
- 'maxDate' => 'date',
+ 'hide_label' => 'boolean',
+ 'min_date' => 'date',
+ 'max_date' => 'date',
];
protected $attributes = [
- 'hideLabel' => false,
- 'dateFormat' => 'YYYY-MMM-DD',
+ 'hide_label' => false,
+ 'date_format' => 'YYYY-MMM-DD',
];
/**
@@ -41,30 +41,28 @@ class DateSelectInputFormElement extends Model
*/
public static function getFilamentSchema(bool $disabled = false): array
{
- return array_merge(
+ return [
SchemaHelper::getCommonCarbonFields($disabled),
- [
- Fieldset::make('Value')
- ->schema([
- SchemaHelper::getPlaceholderTextField($disabled),
- Select::make('elementable_data.dateFormat')
- ->label('Date Format')
- ->options(static::getDateFormats())
- ->default('YYYY-MMM-DD')
- ->required()
- ->disabled($disabled),
- DatePicker::make('elementable_data.minDate')
- ->label('Minimum Date')
- ->helperText('Earliest date users can select')
- ->disabled($disabled),
- DatePicker::make('elementable_data.maxDate')
- ->label('Maximum Date')
- ->helperText('Latest date users can select')
- ->disabled($disabled),
- ])
- ->columns(1),
- ]
- );
+ Fieldset::make('Value')
+ ->schema([
+ SchemaHelper::getPlaceholderTextField($disabled),
+ Select::make('elementable_data.date_format')
+ ->label('Date Format')
+ ->options(static::getDateFormats())
+ ->default('YYYY-MMM-DD')
+ ->required()
+ ->disabled($disabled),
+ DatePicker::make('elementable_data.min_date')
+ ->label('Minimum Date')
+ ->helperText('Earliest date users can select')
+ ->disabled($disabled),
+ DatePicker::make('elementable_data.max_date')
+ ->label('Maximum Date')
+ ->helperText('Latest date users can select')
+ ->disabled($disabled),
+ ])
+ ->columns(1),
+ ];
}
/**
@@ -82,12 +80,12 @@ public function getData(): array
{
return [
'placeholder' => $this->placeholder,
- 'labelText' => $this->labelText,
- 'hideLabel' => $this->hideLabel,
- 'enableVarSub' => $this->enableVarSub,
- 'minDate' => $this->minDate,
- 'maxDate' => $this->maxDate,
- 'dateFormat' => $this->dateFormat,
+ 'label_text' => $this->label_text,
+ 'hide_label' => $this->hide_label,
+ 'enable_var_sub' => $this->enable_var_sub,
+ 'min_date' => $this->min_date,
+ 'max_date' => $this->max_date,
+ 'date_format' => $this->date_format,
];
}
@@ -197,12 +195,12 @@ public static function getDefaultData(): array
{
return [
'placeholder' => '',
- 'labelText' => 'Date Select Input',
- 'hideLabel' => false,
- 'enableVarSub' => false,
- 'minDate' => null,
- 'maxDate' => null,
- 'dateFormat' => 'YYYY-MMM-DD',
+ 'label_text' => 'Date Select Input',
+ 'hide_label' => false,
+ 'enable_var_sub' => false,
+ 'min_date' => null,
+ 'max_date' => null,
+ 'date_format' => 'YYYY-MMM-DD',
];
}
}
diff --git a/app/Models/FormBuilding/FormElement.php b/app/Models/FormBuilding/FormElement.php
index e1218510..ab84fcd2 100644
--- a/app/Models/FormBuilding/FormElement.php
+++ b/app/Models/FormBuilding/FormElement.php
@@ -30,12 +30,11 @@ class FormElement extends Model
'elementable_id',
'help_text',
'calculated_value',
+ 'visible_web',
+ 'visible_pdf',
'is_read_only',
- 'custom_read_only',
'is_required',
'save_on_submit',
- 'visible_web',
- 'visible_pdf',
'is_template',
'source_element_id',
];
@@ -44,9 +43,11 @@ class FormElement extends Model
'order' => 'integer',
'parent_id' => 'integer',
'save_on_submit' => 'boolean',
- 'visible_web' => 'boolean',
- 'visible_pdf' => 'boolean',
'is_template' => 'boolean',
+ 'visible_web' => 'string',
+ 'visible_pdf' => 'string',
+ 'is_required' => 'string',
+ 'is_read_only' => 'string',
];
protected static $logAttributes = [
@@ -57,6 +58,10 @@ class FormElement extends Model
'form_version_id',
'elementable_type',
'help_text',
+ 'visible_web',
+ 'visible_pdf',
+ 'is_required',
+ 'is_read_only',
];
public static function boot()
@@ -240,7 +245,9 @@ public function scopeOrdered($query)
*/
public function scopeVisible($query)
{
- return $query->where('visible_web', true);
+ return $query
+ ->whereIn('visible_web', self::getActiveToggleButtonStates())
+ ->orWhereIn('visible_pdf', self::getActiveToggleButtonStates());
}
/**
@@ -248,7 +255,7 @@ public function scopeVisible($query)
*/
public function scopeVisibleWeb($query)
{
- return $query->where('visible_web', true);
+ return $query->whereIn('visible_web', self::getActiveToggleButtonStates());
}
/**
@@ -256,7 +263,7 @@ public function scopeVisibleWeb($query)
*/
public function scopeVisiblePdf($query)
{
- return $query->where('visible_pdf', true);
+ return $query->whereIn('visible_pdf', self::getActiveToggleButtonStates());
}
/**
@@ -264,12 +271,12 @@ public function scopeVisiblePdf($query)
*/
public function scopeReadOnly($query)
{
- return $query->where('is_read_only', true);
+ return $query->whereIn('is_read_only', self::getActiveToggleButtonStates());
}
public function scopeIsRequired($query)
{
- return $query->where('is_required', true);
+ return $query->where('is_required', self::getActiveToggleButtonStates());
}
/**
@@ -277,7 +284,7 @@ public function scopeIsRequired($query)
*/
public function scopeEditable($query)
{
- return $query->where('is_read_only', false);
+ return $query->whereIn('is_read_only', self::getInactiveToggleButtonStates());
}
/**
@@ -659,16 +666,47 @@ public function getOptions()
return collect();
}
+ /**
+ * Get all state options for ToggleButtons (required, read-only, visibility)
+ */
+ public static function getToggleButtonStates(): array
+ {
+ return [
+ 'always' => 'Always',
+ 'icm' => 'On ICM Forms',
+ 'portal' => 'On Portal Forms',
+ 'never' => 'Never',
+ ];
+ }
+
+ /**
+ * Get the ToggleButton keys that represent an "active" or "enabled" state
+ */
+ public static function getActiveToggleButtonStates(): array
+ {
+ return array_diff(array_keys(self::getToggleButtonStates()), ['never']);
+ }
+
+ /**
+ * Get the ToggleButton values that represent an "inactive" or "disabled" state
+ */
+ public static function getInactiveToggleButtonStates(): array
+ {
+ return ['never', ''];
+ }
+
/**
* Check if element is visible for a specific platform
*/
public function isVisibleFor(string $platform): bool
{
- return match ($platform) {
+ $value = match ($platform) {
'web' => $this->visible_web,
'pdf' => $this->visible_pdf,
- default => false,
+ default => null,
};
+
+ return in_array($value, self::getActiveToggleButtonStates(), true);
}
/**
@@ -676,17 +714,43 @@ public function isVisibleFor(string $platform): bool
*/
public function shouldSaveOnSubmit(): bool
{
- return $this->save_on_submit && ($this->isVisibleFor('web') || $this->isVisibleFor('pdf')) && !$this->is_read_only;
+ $isReadOnly = in_array($this->is_read_only, self::getActiveToggleButtonStates(), true);
+ return $this->save_on_submit && ($this->isVisibleFor('web') || $this->isVisibleFor('pdf')) && !$isReadOnly;
}
/**
- * Set visibility for specific platforms
+ * Set visibility for web
*/
- public function setVisibilityFor(array $platforms): self
+ public function setVisibilityWeb(string $value): self
{
- $this->visible_web = in_array('web', $platforms);
- $this->visible_pdf = in_array('pdf', $platforms);
+ $this->visible_web = $value;
+ return $this;
+ }
+ /**
+ * Set visibility for pdf
+ */
+ public function setVisibilityPdf(string $value): self
+ {
+ $this->visible_pdf = $value;
+ return $this;
+ }
+
+ /**
+ * Set required
+ */
+ public function setRequired(string $value): self
+ {
+ $this->is_required = $value;
+ return $this;
+ }
+
+ /**
+ * Set read-only for pdf
+ */
+ public function setReadOnly(string $value): self
+ {
+ $this->is_read_only = $value;
return $this;
}
diff --git a/app/Models/FormBuilding/NumberInputFormElement.php b/app/Models/FormBuilding/NumberInputFormElement.php
index 918ed675..e8b61ee7 100644
--- a/app/Models/FormBuilding/NumberInputFormElement.php
+++ b/app/Models/FormBuilding/NumberInputFormElement.php
@@ -20,28 +20,28 @@ class NumberInputFormElement extends Model
protected $fillable = [
'placeholder',
- 'labelText',
- 'hideLabel',
- 'enableVarSub',
+ 'label_text',
+ 'hide_label',
+ 'enable_var_sub',
'min',
'max',
'step',
- 'defaultValue',
- 'maskType',
+ 'default_value',
+ 'mask_type',
];
protected $casts = [
- 'hideLabel' => 'boolean',
+ 'hide_label' => 'boolean',
'min' => 'integer',
'max' => 'integer',
'step' => 'float',
- 'defaultValue' => 'float',
+ 'default_value' => 'float',
];
protected $attributes = [
- 'hideLabel' => false,
+ 'hide_label' => false,
'step' => 1,
- 'maskType' => 'integer',
+ 'mask_type' => 'integer',
];
/**
@@ -50,10 +50,11 @@ class NumberInputFormElement extends Model
protected static function formatNumberByMask(string $target): Closure
{
return function ($state, callable $set, Get $get) use ($target) {
- if ($state === null) return;
+ if ($state === null)
+ return;
- $raw = trim((string) $state);
- $mask = strtolower((string) ($get('elementable_data.maskType') ?? 'integer')); // 'integer' | 'decimal'
+ $raw = trim((string) $state);
+ $mask = strtolower((string) ($get('elementable_data.mask_type') ?? 'integer')); // 'integer' | 'decimal'
// Do not "fix" scientific notation or thousands separators; let validation reject them.
if (preg_match('/[eE, ]/', $state)) {
@@ -64,13 +65,16 @@ protected static function formatNumberByMask(string $target): Closure
$isZeroish = static function (string $s): bool {
$s = trim($s);
- if ($s === '') return false;
+ if ($s === '')
+ return false;
$s = ltrim($s, "+-");
- if ($s === '' || $s === '.') return true;
+ if ($s === '' || $s === '.')
+ return true;
$clean = preg_replace('/[^\d.]/', '', $s) ?? '';
- if ($clean === '' || substr_count($clean, '.') > 1) return false;
+ if ($clean === '' || substr_count($clean, '.') > 1)
+ return false;
$digitsOnly = str_replace('.', '', $clean);
return $digitsOnly !== '' && preg_match('/^0+$/', $digitsOnly) === 1;
@@ -117,7 +121,8 @@ protected static function formatNumberByMask(string $target): Closure
// normalize leading zeros in integer part
$int = ltrim($int, '0');
- if ($int === '') $int = '0';
+ if ($int === '')
+ $int = '0';
// trim trailing zeros in fractional part; drop the dot if empty
$frac = rtrim($frac, '0');
@@ -127,7 +132,8 @@ protected static function formatNumberByMask(string $target): Closure
// integer-like in decimal context
$int = preg_replace('/\D/', '', $body) ?? '';
$int = ltrim($int, '0');
- if ($int === '') $int = '0';
+ if ($int === '')
+ $int = '0';
$out = $int;
}
@@ -147,128 +153,126 @@ protected static function formatNumberByMask(string $target): Closure
public static function getFilamentSchema(bool $disabled = false): array
{
- $isDecimal = fn(Get $get) => strtolower($get('elementable_data.maskType') ?? 'integer') === 'decimal';
+ $isDecimal = fn(Get $get) => strtolower($get('elementable_data.mask_type') ?? 'integer') === 'decimal';
$noSci = 'not_regex:/[eE]/'; // forbid scientific notation
$plainDecimal = 'regex:/^-?\d+(\.\d+)?$/'; // allow optional leading '-', digits, and one dot
- return array_merge(
+ return [
SchemaHelper::getCommonCarbonFields($disabled),
- [
- Fieldset::make('Value')
- ->schema([
- SchemaHelper::getPlaceholderTextField($disabled)
- ->columnSpan(6),
- TextInput::make('elementable_data.defaultValue')
- ->label('Default Value')
- ->numeric()
- ->nullable()
- ->step(fn(Get $get) => $get('elementable_data.step') ?? 1)
- ->live(onBlur: true)
- ->afterStateUpdated(self::formatNumberByMask('elementable_data.defaultValue'))
- ->rules(function (Get $get) use ($isDecimal, $noSci, $plainDecimal) {
- $rules = $isDecimal($get)
- ? ['numeric', $noSci, $plainDecimal]
- : ['integer'];
- return $rules;
- })
- ->rule(NumericRules::compareWith(
- minPath: 'elementable_data.min',
- maxPath: 'elementable_data.max',
- ))
- ->columnSpan(2)
- ->disabled($disabled),
- TextInput::make('elementable_data.min')
- ->label('Minimum Value')
- ->numeric()
- ->nullable()
- ->step(fn(Get $get) => $get('elementable_data.step') ?? 1)
- ->live(onBlur: true)
- ->afterStateUpdated(self::formatNumberByMask('elementable_data.min'))
- ->rules(function (Get $get) use ($isDecimal, $noSci, $plainDecimal) {
- $rules = $isDecimal($get)
- ? ['numeric', $noSci, $plainDecimal]
- : ['integer'];
- return $rules;
- })
- ->rule(NumericRules::compareWith(
- minPath: null,
- maxPath: 'elementable_data.max',
- ))
- ->columnSpan(2)
- ->disabled($disabled),
- TextInput::make('elementable_data.max')
- ->label('Maximum Value')
- ->numeric()
- ->nullable()
- ->step(fn(Get $get) => $get('elementable_data.step') ?? 1)
- ->live(onBlur: true)
- ->afterStateUpdated(self::formatNumberByMask('elementable_data.max'))
- ->rules(function (Get $get) use ($isDecimal, $noSci, $plainDecimal) {
- $rules = $isDecimal($get)
- ? ['numeric', $noSci, $plainDecimal]
- : ['integer'];
- return $rules;
- })
- ->rule(NumericRules::compareWith(
- minPath: 'elementable_data.min',
- maxPath: null,
- ))
- ->columnSpan(2)
- ->disabled($disabled),
- ToggleButtons::make('elementable_data.maskType')
- ->label('Input Mask Type')
- ->options([
- 'integer' => 'Integer',
- 'decimal' => 'Decimal',
- ])
- ->inline()
- ->default('integer')
- ->live()
- ->columnSpan(3)
- ->afterStateUpdated(function (string $state, callable $set, Get $get) {
- if ($state === 'integer') {
- // Force step = 1 for integer mode
- $set('elementable_data.step', 1);
-
- // Coerce existing values to integers (if set)
- foreach (['defaultValue', 'min', 'max'] as $key) {
- $path = "elementable_data.$key";
- $val = $get($path);
- if (filled($val)) {
- $set($path, (int) round((float) $val));
- }
+ Fieldset::make('Value')
+ ->schema([
+ SchemaHelper::getPlaceholderTextField($disabled)
+ ->columnSpan(6),
+ TextInput::make('elementable_data.default_value')
+ ->label('Default Value')
+ ->numeric()
+ ->nullable()
+ ->step(fn(Get $get) => $get('elementable_data.step') ?? 1)
+ ->live(onBlur: true)
+ ->afterStateUpdated(self::formatNumberByMask('elementable_data.default_value'))
+ ->rules(function (Get $get) use ($isDecimal, $noSci, $plainDecimal) {
+ $rules = $isDecimal($get)
+ ? ['numeric', $noSci, $plainDecimal]
+ : ['integer'];
+ return $rules;
+ })
+ ->rule(NumericRules::compareWith(
+ minPath: 'elementable_data.min',
+ maxPath: 'elementable_data.max',
+ ))
+ ->columnSpan(2)
+ ->disabled($disabled),
+ TextInput::make('elementable_data.min')
+ ->label('Minimum Value')
+ ->numeric()
+ ->nullable()
+ ->step(fn(Get $get) => $get('elementable_data.step') ?? 1)
+ ->live(onBlur: true)
+ ->afterStateUpdated(self::formatNumberByMask('elementable_data.min'))
+ ->rules(function (Get $get) use ($isDecimal, $noSci, $plainDecimal) {
+ $rules = $isDecimal($get)
+ ? ['numeric', $noSci, $plainDecimal]
+ : ['integer'];
+ return $rules;
+ })
+ ->rule(NumericRules::compareWith(
+ minPath: null,
+ maxPath: 'elementable_data.max',
+ ))
+ ->columnSpan(2)
+ ->disabled($disabled),
+ TextInput::make('elementable_data.max')
+ ->label('Maximum Value')
+ ->numeric()
+ ->nullable()
+ ->step(fn(Get $get) => $get('elementable_data.step') ?? 1)
+ ->live(onBlur: true)
+ ->afterStateUpdated(self::formatNumberByMask('elementable_data.max'))
+ ->rules(function (Get $get) use ($isDecimal, $noSci, $plainDecimal) {
+ $rules = $isDecimal($get)
+ ? ['numeric', $noSci, $plainDecimal]
+ : ['integer'];
+ return $rules;
+ })
+ ->rule(NumericRules::compareWith(
+ minPath: 'elementable_data.min',
+ maxPath: null,
+ ))
+ ->columnSpan(2)
+ ->disabled($disabled),
+ ToggleButtons::make('elementable_data.mask_type')
+ ->label('Input Mask Type')
+ ->options([
+ 'integer' => 'Integer',
+ 'decimal' => 'Decimal',
+ ])
+ ->inline()
+ ->default('integer')
+ ->live()
+ ->columnSpan(3)
+ ->afterStateUpdated(function (string $state, callable $set, Get $get) {
+ if ($state === 'integer') {
+ // Force step = 1 for integer mode
+ $set('elementable_data.step', 1);
+
+ // Coerce existing values to integers (if set)
+ foreach (['default_value', 'min', 'max'] as $key) {
+ $path = "elementable_data.$key";
+ $val = $get($path);
+ if (filled($val)) {
+ $set($path, (int) round((float) $val));
}
- } else {
- // Decimal mode: keep or set a reasonable decimal step
- $set('elementable_data.step', 0.01);
}
- }),
- TextInput::make('elementable_data.step')
- ->required()
- ->label('Step Size')
- ->numeric()
- ->default(1)
- ->live(onBlur: true)
- // Ensure UI "step" attribute makes sense (1 for integer; any step otherwise)
- ->step(fn(Get $get) => $isDecimal($get) ? "any" : 1)
- ->afterStateUpdated(self::formatNumberByMask('elementable_data.step'))
- // Validation: integer & >=1 in integer mode; numeric & >0 in decimal mode
- ->rule(fn(Get $get) => $isDecimal($get)
- ? [
- 'numeric',
- 'gt:0',
- $noSci,
- 'regex:/^\d+(\.\d+)?$/', // only digits and one dot
- ]
- : ['integer', 'min:1'])
- ->columnSpan(3)
- ->disabled($disabled),
- ])
- ->columns(6),
- ]
- );
+ } else {
+ // Decimal mode: keep or set a reasonable decimal step
+ $set('elementable_data.step', 0.01);
+ }
+ }),
+ TextInput::make('elementable_data.step')
+ ->required()
+ ->label('Step Size')
+ ->numeric()
+ ->default(1)
+ ->live(onBlur: true)
+ // Ensure UI "step" attribute makes sense (1 for integer; any step otherwise)
+ ->step(fn(Get $get) => $isDecimal($get) ? "any" : 1)
+ ->afterStateUpdated(self::formatNumberByMask('elementable_data.step'))
+ // Validation: integer & >=1 in integer mode; numeric & >0 in decimal mode
+ ->rule(fn(Get $get) => $isDecimal($get)
+ ? [
+ 'numeric',
+ 'gt:0',
+ $noSci,
+ 'regex:/^\d+(\.\d+)?$/', // only digits and one dot
+ ]
+ : ['integer', 'min:1'])
+ ->columnSpan(3)
+ ->disabled($disabled),
+ ])
+ ->columns(6),
+ ];
}
/**
@@ -286,14 +290,14 @@ public function getData(): array
{
return [
'placeholder' => $this->placeholder,
- 'labelText' => $this->labelText,
- 'hideLabel' => $this->hideLabel,
- 'enableVarSub' => $this->enableVarSub,
+ 'label_text' => $this->label_text,
+ 'hide_label' => $this->hide_label,
+ 'enable_var_sub' => $this->enable_var_sub,
'min' => $this->min,
'max' => $this->max,
'step' => $this->step,
- 'defaultValue' => $this->defaultValue,
- 'maskType' => $this->maskType,
+ 'default_value' => $this->default_value,
+ 'mask_type' => $this->mask_type,
];
}
@@ -304,14 +308,14 @@ public static function getDefaultData(): array
{
return [
'placeholder' => '',
- 'labelText' => '',
- 'hideLabel' => false,
- 'enableVarSub' => false,
+ 'label_text' => '',
+ 'hide_label' => false,
+ 'enable_var_sub' => false,
'min' => null,
'max' => null,
'step' => 1,
- 'defaultValue' => null,
- 'maskType' => 'integer',
+ 'default_value' => null,
+ 'mask_type' => 'integer',
];
}
}
diff --git a/app/Models/FormBuilding/RadioInputFormElement.php b/app/Models/FormBuilding/RadioInputFormElement.php
index ccc8ac06..79579e47 100644
--- a/app/Models/FormBuilding/RadioInputFormElement.php
+++ b/app/Models/FormBuilding/RadioInputFormElement.php
@@ -18,22 +18,22 @@ class RadioInputFormElement extends Model
use HasFactory, SoftDeletes;
protected $fillable = [
- 'labelText',
- 'hideLabel',
- 'enableVarSub',
- 'defaultSelected',
- 'labelPosition',
+ 'label_text',
+ 'hide_label',
+ 'enable_var_sub',
+ 'default_selected',
+ 'label_position',
'orientation',
];
protected $casts = [
- 'hideLabel' => 'boolean',
+ 'hide_label' => 'boolean',
];
protected $attributes = [
- 'hideLabel' => false,
- 'labelText' => '',
- 'labelPosition' => 'right',
+ 'hide_label' => false,
+ 'label_text' => '',
+ 'label_position' => 'right',
'orientation' => 'vertical',
];
@@ -48,16 +48,15 @@ public static function getFilamentSchema(bool $disabled = false): array
SchemaHelper::getLabelTextField($disabled)->required(),
SchemaHelper::getEnableVariableSubstitutionToggle($disabled),
SchemaHelper::getHideLabelToggle($disabled),
- Select::make('elementable_data.labelPosition')
+ Select::make('elementable_data.label_position')
->label('Label Position')
->options([
'left' => 'Left',
'right' => 'Right',
])
->default('right')
- ->visible(fn(callable $get): bool => !$get('elementable_data.hideLabel'))
+ ->visible(fn(callable $get): bool => !$get('elementable_data.hide_label'))
->disabled($disabled),
-
])
->columns(1),
Fieldset::make('Values')
@@ -70,60 +69,8 @@ public static function getFilamentSchema(bool $disabled = false): array
])
->default('vertical')
->disabled($disabled),
- Select::make('elementable_data.defaultSelected')
- ->label('Default Selected Value')
- ->options(function (callable $get) {
- $options = $get('elementable_data.options') ?? [];
- $selectOptions = [];
- foreach ($options as $option) {
- if (!empty($option['value'])) {
- $selectOptions[$option['value']] = $option['label'] ?? $option['value'];
- }
- }
- return $selectOptions;
- })
- ->disabled($disabled),
- Repeater::make('elementable_data.options')
- ->label('Options')
- ->schema([
- TextInput::make('label')
- ->label('Option Label')
- ->required()
- ->columnSpan(2)
- ->autocomplete(false)
- ->live(onBlur: true)
- ->afterStateUpdated(function (callable $set, callable $get, $state) {
- $value = $get('value');
- if (empty($value) && !empty($state)) {
- $slug = \Illuminate\Support\Str::slug($state, '-');
- $set('value', $slug);
- }
- }),
- TextInput::make('value')
- ->label('Option Value')
- ->required()
- ->columnSpan(2)
- ->suffixAction(
- \Filament\Forms\Components\Actions\Action::make('regenerate_value')
- ->icon('heroicon-o-arrow-path')
- ->tooltip('Regenerate from Option Label')
- ->action(function (callable $set, callable $get) {
- $label = $get('label');
- if (!empty($label)) {
- $slug = \Illuminate\Support\Str::slug($label, '-');
- $set('value', $slug);
- }
- })
- ),
- ])
- ->columns(2)
- ->defaultItems(1)
- ->addActionLabel('Add Option')
- ->reorderableWithButtons()
- ->collapsible()
- ->itemLabel(fn(array $state): ?string => $state['label'] ?? 'Option')
- ->disabled($disabled)
- ->minItems(1),
+ SchemaHelper::getOptionsDefaultSelectedSelect($disabled),
+ SchemaHelper::getOptionsRepeater($disabled),
])
->columns(1),
];
@@ -143,11 +90,11 @@ public function formElement(): MorphOne
public function getData(): array
{
return [
- 'labelText' => $this->labelText,
- 'hideLabel' => $this->hideLabel,
- 'enableVarSub' => $this->enableVarSub,
- 'defaultSelected' => $this->defaultSelected,
- 'labelPosition' => $this->labelPosition,
+ 'label_text' => $this->label_text,
+ 'hide_label' => $this->hide_label,
+ 'enable_var_sub' => $this->enable_var_sub,
+ 'default_selected' => $this->default_selected,
+ 'label_position' => $this->label_position,
'orientation' => $this->orientation,
];
}
@@ -166,10 +113,10 @@ public function options(): MorphMany
public static function getDefaultData(): array
{
return [
- 'hideLabel' => false,
- 'labelText' => '',
- 'enableVarSub' => false,
- 'labelPosition' => 'right',
+ 'hide_label' => false,
+ 'label_text' => '',
+ 'enable_var_sub' => false,
+ 'label_position' => 'right',
'orientation' => 'vertical',
'options' => [
['label' => 'True', 'value' => 'true'],
diff --git a/app/Models/FormBuilding/SelectInputFormElement.php b/app/Models/FormBuilding/SelectInputFormElement.php
index 3d703242..3ba063a2 100644
--- a/app/Models/FormBuilding/SelectInputFormElement.php
+++ b/app/Models/FormBuilding/SelectInputFormElement.php
@@ -18,20 +18,20 @@ class SelectInputFormElement extends Model
use HasFactory, SoftDeletes;
protected $fillable = [
- 'labelText',
- 'hideLabel',
- 'enableVarSub',
- 'defaultSelected',
+ 'label_text',
+ 'hide_label',
+ 'enable_var_sub',
+ 'default_selected',
];
protected $casts = [
- 'hideLabel' => 'boolean',
+ 'hide_label' => 'boolean',
];
protected $attributes = [
- 'hideLabel' => false,
- 'labelText' => '',
- 'defaultSelected' => null,
+ 'hide_label' => false,
+ 'label_text' => '',
+ 'default_selected' => null,
];
/**
@@ -40,69 +40,11 @@ class SelectInputFormElement extends Model
public static function getFilamentSchema(bool $disabled = false): array
{
return [
- Fieldset::make('Field Label')
- ->schema([
- SchemaHelper::getLabelTextField($disabled)->required(),
- SchemaHelper::getEnableVariableSubstitutionToggle($disabled),
- SchemaHelper::getHideLabelToggle($disabled),
- ])
- ->columns(1),
+ SchemaHelper::getCommonCarbonFields($disabled, true),
Fieldset::make('Values')
->schema([
- Select::make('elementable_data.defaultSelected')
- ->label('Default Selected Value')
- ->options(function (callable $get) {
- $options = $get('elementable_data.options') ?? [];
- $selectOptions = [];
- foreach ($options as $option) {
- if (!empty($option['value'])) {
- $selectOptions[$option['value']] = $option['label'] ?? $option['value'];
- }
- }
- return $selectOptions;
- })
- ->disabled($disabled),
- Repeater::make('elementable_data.options')
- ->label('Options')
- ->schema([
- TextInput::make('label')
- ->label('Option Label')
- ->required()
- ->columnSpan(2)
- ->autocomplete(false)
- ->live(onBlur: true)
- ->afterStateUpdated(function (callable $set, callable $get, $state) {
- $value = $get('value');
- if (empty($value) && !empty($state)) {
- $slug = \Illuminate\Support\Str::slug($state, '-');
- $set('value', $slug);
- }
- }),
- TextInput::make('value')
- ->label('Option Value')
- ->required()
- ->columnSpan(2)
- ->suffixAction(
- \Filament\Forms\Components\Actions\Action::make('regenerate_value')
- ->icon('heroicon-o-arrow-path')
- ->tooltip('Regenerate from Option Label')
- ->action(function (callable $set, callable $get) {
- $label = $get('label');
- if (!empty($label)) {
- $slug = \Illuminate\Support\Str::slug($label, '-');
- $set('value', $slug);
- }
- })
- ),
- ])
- ->columns(2)
- ->defaultItems(1)
- ->addActionLabel('Add Option')
- ->reorderableWithButtons()
- ->collapsible()
- ->itemLabel(fn(array $state): ?string => $state['label'] ?? 'Option')
- ->disabled($disabled)
- ->minItems(1),
+ SchemaHelper::getOptionsDefaultSelectedSelect($disabled),
+ SchemaHelper::getOptionsRepeater($disabled),
])
->columns(1),
];
@@ -122,10 +64,10 @@ public function formElement(): MorphOne
public function getData(): array
{
return [
- 'labelText' => $this->labelText,
- 'hideLabel' => $this->hideLabel,
- 'enableVarSub' => $this->enableVarSub,
- 'defaultSelected' => $this->defaultSelected,
+ 'label_text' => $this->label_text,
+ 'hide_label' => $this->hide_label,
+ 'enable_var_sub' => $this->enable_var_sub,
+ 'default_selected' => $this->default_selected,
];
}
@@ -143,10 +85,10 @@ public function options(): MorphMany
public static function getDefaultData(): array
{
return [
- 'hideLabel' => false,
- 'labelText' => '',
- 'enableVarSub' => false,
- 'defaultSelected' => null,
+ 'hide_label' => false,
+ 'label_text' => '',
+ 'enable_var_sub' => false,
+ 'default_selected' => null,
'options' => [
['label' => 'True', 'value' => 'true'],
['label' => 'False', 'value' => 'false'],
diff --git a/app/Models/FormBuilding/TextInputFormElement.php b/app/Models/FormBuilding/TextInputFormElement.php
index 8c0d17a0..511a98eb 100644
--- a/app/Models/FormBuilding/TextInputFormElement.php
+++ b/app/Models/FormBuilding/TextInputFormElement.php
@@ -17,24 +17,24 @@ class TextInputFormElement extends Model
protected $fillable = [
'placeholder',
- 'labelText',
- 'hideLabel',
- 'enableVarSub',
- 'maskType',
+ 'label_text',
+ 'hide_label',
+ 'enable_var_sub',
+ 'mask_type',
'mask',
- 'maskErrorMessage',
- 'maxCount',
- 'defaultValue',
+ 'mask_error_message',
+ 'max_count',
+ 'default_value',
];
protected $casts = [
- 'hideLabel' => 'boolean',
- 'maxCount' => 'integer',
+ 'hide_label' => 'boolean',
+ 'max_count' => 'integer',
];
protected $attributes = [
- 'hideLabel' => false,
- 'maskType' => 'custom',
+ 'hide_label' => false,
+ 'mask_type' => 'custom',
];
/**
@@ -42,53 +42,53 @@ class TextInputFormElement extends Model
*/
public static function getFilamentSchema(bool $disabled = false): array
{
- return array_merge(
+ return [
SchemaHelper::getCommonCarbonFields($disabled),
- [
- Fieldset::make('Value')
- ->schema([
- SchemaHelper::getPlaceholderTextField($disabled),
- TextInput::make('elementable_data.defaultValue')
- ->label('Default Value')
- ->disabled($disabled),
- TextInput::make('elementable_data.maxCount')
- ->label('Maximum Character Count')
- ->numeric()
- ->disabled($disabled),
- ToggleButtons::make('elementable_data.maskType')
- ->label('Input Mask Type')
- ->options([
- 'email' => 'Email',
- 'phone' => 'Phone',
- 'postal' => 'Postal Code',
- 'custom' => 'Custom',
- ])
- ->inline()
- ->live()
- ->afterStateUpdated(function ($state, callable $set) {
- $maskPatterns = [
- 'email' => '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
- 'phone' => '### ###-####',
- 'postal' => '@#@ #@#',
- 'custom' => '',
- ];
- $set('elementable_data.mask', $maskPatterns[$state] ?? '');
- }),
- TextInput::make('elementable_data.mask')
- ->label('Input Mask')
- ->autocomplete(false)
- ->hint('Supports Maska syntax, regular expressions, or character classes like "a-zA-Z0-9"')
- ->disabled($disabled),
- TextInput::make('elementable_data.maskErrorMessage')
- ->label('Validation Message')
- ->placeholder('e.g. Only letters and spaces are allowed')
- ->hint('Displayed when input doesn\'t match the mask')
- ->visible(fn($get) => $get('elementable_data.maskType') === 'custom')
- ->disabled($disabled),
- ])
- ->columns(1),
- ]
- );
+ Fieldset::make('Value')
+ ->schema([
+ SchemaHelper::getPlaceholderTextField($disabled),
+ TextInput::make('elementable_data.default_value')
+ ->label('Default Value')
+ ->maxLength(255)
+ ->disabled($disabled),
+ TextInput::make('elementable_data.max_count')
+ ->label('Maximum Character Count')
+ ->numeric()
+ ->disabled($disabled),
+ ToggleButtons::make('elementable_data.mask_type')
+ ->label('Input Mask Type')
+ ->options([
+ 'email' => 'Email',
+ 'phone' => 'Phone',
+ 'postal' => 'Postal Code',
+ 'custom' => 'Custom',
+ ])
+ ->inline()
+ ->live()
+ ->afterStateUpdated(function ($state, callable $set) {
+ $maskPatterns = [
+ 'email' => '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
+ 'phone' => '### ###-####',
+ 'postal' => '@#@ #@#',
+ 'custom' => '',
+ ];
+ $set('elementable_data.mask', $maskPatterns[$state] ?? '');
+ }),
+ TextInput::make('elementable_data.mask')
+ ->label('Input Mask')
+ ->autocomplete(false)
+ ->maxLength(255)
+ ->hint('Supports Maska syntax, regular expressions, or character classes like "a-zA-Z0-9"')
+ ->disabled($disabled),
+ TextInput::make('elementable_data.mask_error_message')
+ ->label('Validation Message')
+ ->placeholder('e.g. Only letters and spaces are allowed')
+ ->hint('Displayed when input doesn\'t match the mask')
+ ->visible(fn($get) => $get('elementable_data.mask_type') === 'custom')
+ ->disabled($disabled),
+ ])
+ ->columns(1),
+ ];
}
/**
@@ -106,13 +106,13 @@ public function getData(): array
{
return [
'placeholder' => $this->placeholder,
- 'labelText' => $this->labelText,
- 'hideLabel' => $this->hideLabel,
- 'enableVarSub' => $this->enableVarSub,
+ 'label_text' => $this->label_text,
+ 'hide_label' => $this->hide_label,
+ 'enable_var_sub' => $this->enable_var_sub,
'mask' => $this->mask,
- 'maskErrorMessage' => $this->maskErrorMessage,
- 'maxCount' => $this->maxCount,
- 'defaultValue' => $this->defaultValue,
+ 'mask_error_message' => $this->mask_error_message,
+ 'max_count' => $this->max_count,
+ 'default_value' => $this->default_value,
];
}
@@ -123,13 +123,13 @@ public static function getDefaultData(): array
{
return [
'placeholder' => '',
- 'labelText' => '',
- 'hideLabel' => false,
- 'enableVarSub' => false,
+ 'label_text' => '',
+ 'hide_label' => false,
+ 'enable_var_sub' => false,
'mask' => '',
- 'maskErrorMessage' => '',
- 'maxCount' => null,
- 'defaultValue' => '',
+ 'mask_error_message' => '',
+ 'max_count' => null,
+ 'default_value' => '',
];
}
}
diff --git a/app/Models/FormBuilding/TextareaInputFormElement.php b/app/Models/FormBuilding/TextareaInputFormElement.php
index 708dc9d7..940fb90e 100644
--- a/app/Models/FormBuilding/TextareaInputFormElement.php
+++ b/app/Models/FormBuilding/TextareaInputFormElement.php
@@ -16,24 +16,24 @@ class TextareaInputFormElement extends Model
protected $fillable = [
'placeholder',
- 'labelText',
- 'hideLabel',
- 'enableVarSub',
+ 'label_text',
+ 'hide_label',
+ 'enable_var_sub',
'rows',
'cols',
- 'maxCount',
- 'defaultValue',
+ 'max_count',
+ 'default_value',
];
protected $casts = [
- 'hideLabel' => 'boolean',
+ 'hide_label' => 'boolean',
'rows' => 'integer',
'cols' => 'integer',
- 'maxCount' => 'integer',
+ 'max_count' => 'integer',
];
protected $attributes = [
- 'hideLabel' => false,
+ 'hide_label' => false,
'rows' => 3,
];
@@ -42,12 +42,11 @@ class TextareaInputFormElement extends Model
*/
public static function getFilamentSchema(bool $disabled = false): array
{
- return array_merge(
+ return [
SchemaHelper::getCommonCarbonFields($disabled),
- [
- Fieldset::make('Value')
- ->schema([
- SchemaHelper::getPlaceholderTextField($disabled),
+ Fieldset::make('Value')
+ ->schema([
+ SchemaHelper::getPlaceholderTextField($disabled),
TextInput::make('elementable_data.rows')
->label('Number of Rows')
->numeric()
@@ -57,17 +56,17 @@ public static function getFilamentSchema(bool $disabled = false): array
->label('Number of Columns')
->numeric()
->disabled($disabled),
- TextInput::make('elementable_data.maxCount')
+ TextInput::make('elementable_data.max_count')
->label('Maximum Character Count')
->numeric()
->disabled($disabled),
- TextInput::make('elementable_data.defaultValue')
+ TextInput::make('elementable_data.default_value')
->label('Default Value')
+ ->maxLength(255)
->disabled($disabled),
- ])
- ->columns(1),
- ]
- );
+ ])
+ ->columns(1),
+ ];
}
/**
@@ -85,13 +84,13 @@ public function getData(): array
{
return [
'placeholder' => $this->placeholder,
- 'labelText' => $this->labelText,
- 'hideLabel' => $this->hideLabel,
- 'enableVarSub' => $this->enableVarSub,
+ 'label_text' => $this->label_text,
+ 'hide_label' => $this->hide_label,
+ 'enable_var_sub' => $this->enable_var_sub,
'rows' => $this->rows,
'cols' => $this->cols,
- 'maxCount' => $this->maxCount,
- 'defaultValue' => $this->defaultValue,
+ 'max_count' => $this->max_count,
+ 'default_value' => $this->default_value,
];
}
@@ -102,13 +101,13 @@ public static function getDefaultData(): array
{
return [
'placeholder' => '',
- 'labelText' => '',
- 'hideLabel' => false,
- 'enableVarSub' => false,
+ 'label_text' => '',
+ 'hide_label' => false,
+ 'enable_var_sub' => false,
'rows' => 3,
'cols' => null,
- 'maxCount' => null,
- 'defaultValue' => '',
+ 'max_count' => null,
+ 'default_value' => '',
];
}
}
diff --git a/app/Policies/UserTypePolicy.php b/app/Policies/UserTypePolicy.php
index 84bed4c7..06e7ed1d 100644
--- a/app/Policies/UserTypePolicy.php
+++ b/app/Policies/UserTypePolicy.php
@@ -3,8 +3,7 @@
namespace App\Policies;
use App\Models\User;
-use App\Models\UserType;
-use Illuminate\Auth\Access\Response;
+use App\Models\FormMetadata\UserType;
class UserTypePolicy
{
diff --git a/app/Services/FormVersionJsonService.php b/app/Services/FormVersionJsonService.php
index 8cafddc6..fb9c831b 100644
--- a/app/Services/FormVersionJsonService.php
+++ b/app/Services/FormVersionJsonService.php
@@ -148,31 +148,19 @@ protected function getStylesPreMigration(FormVersion $formVersion): array
$key = $sheet->id ? ('style:' . $sheet->id) : null;
if ($key && isset($added[$key]))
continue;
-
$css = $sheet->getCssContent() ?? '';
if ($css !== '') {
- $webCss .= ($webCss !== '' ? "\n\n" : '')
- . "/* Attached stylesheet */\n"
- . $css;
+ $webCss .= ($webCss !== '' ? "\n\n" : '') . "/* Attached stylesheet */\n" . $css;
}
if ($key)
$added[$key] = true;
}
-
if ($webCss !== '') {
- $styles[] = [
- 'type' => 'web',
- 'content' => $webCss
- ];
+ $styles[] = ['type' => 'web', 'content' => $webCss];
}
-
if ($formVersion->pdfStyleSheet) {
- $styles[] = [
- 'type' => 'pdf',
- 'content' => $formVersion->pdfStyleSheet->getCssContent()
- ];
+ $styles[] = ['type' => 'pdf', 'content' => $formVersion->pdfStyleSheet->getCssContent()];
}
-
return $styles;
}
@@ -200,26 +188,18 @@ protected function getScriptsPreMigration(FormVersion $formVersion): array
$js = $script->getJsContent() ?? '';
if ($js !== '') {
- $webJs .= ($webJs !== '' ? "\n\n" : '')
- . "/* Attached form script */\n"
- . $js;
+ $webJs .= ($webJs !== '' ? "\n\n" : '') . "/* Attached form script */\n" . $js;
}
if ($key)
$added[$key] = true;
}
if ($webJs !== '') {
- $scripts[] = [
- 'type' => 'web',
- 'content' => $webJs
- ];
+ $scripts[] = ['type' => 'web', 'content' => $webJs];
}
if ($formVersion->pdfFormScript) {
- $scripts[] = [
- 'type' => 'pdf',
- 'content' => $formVersion->pdfFormScript->getJsContent()
- ];
+ $scripts[] = ['type' => 'pdf', 'content' => $formVersion->pdfFormScript->getJsContent()];
}
return $scripts;
@@ -298,6 +278,7 @@ protected function getScripts(FormVersion $formVersion): array
'filename' => $formVersion->pdfFormScript->filename,
'content' => $formVersion->pdfFormScript->getJsContent() ?? ''
];
+
if ($formVersion->pdfFormScript->id) {
$added['script:' . $formVersion->pdfFormScript->id] = true;
}
@@ -316,8 +297,9 @@ protected function getScripts(FormVersion $formVersion): array
'filename' => $script->filename,
'content' => $script->getJsContent() ?? ''
];
- if ($key)
+ if ($key) {
$added[$key] = true;
+ }
}
return $scripts;
@@ -337,8 +319,7 @@ protected function getElements(FormVersion $formVersion): array
// Get root elements (elements without a parent - parent_id is -1 for root elements)
$rootElements = $formVersion->formElements()
->where(function ($query) {
- $query->whereNull('parent_id')
- ->orWhere('parent_id', -1);
+ $query->whereNull('parent_id')->orWhere('parent_id', -1);
})
->orderBy('order')
->get();
@@ -350,8 +331,7 @@ protected function getElements(FormVersion $formVersion): array
protected function getTags(FormElement $element): array
{
- $tags = $element->tags->pluck('filename', 'id')->toArray();
- return $tags;
+ return $element->tags->pluck('filename', 'id')->toArray();
}
protected function transformElement(FormElement $element): array
@@ -365,10 +345,10 @@ protected function transformElement(FormElement $element): array
'name' => $element->name,
'description' => $element->description,
'help_text' => $element->help_text,
- 'is_required' => $element->is_required,
'visible_web' => $element->visible_web,
'visible_pdf' => $element->visible_pdf,
- 'is_read_only' => $element->is_read_only && $element->custom_read_only ? $element->custom_read_only : $element->is_read_only,
+ 'is_required' => $element->is_required,
+ 'is_read_only' => $element->is_read_only,
'save_on_submit' => $element->save_on_submit,
'order' => $element->order,
'tags' => $this->getTags($element),
@@ -433,8 +413,7 @@ protected function transformElementsToPreMigrationFormat(FormVersion $formVersio
// Get root elements (elements without a parent - parent_id is -1 for root elements)
$rootElements = $formVersion->formElements()
->where(function ($query) {
- $query->whereNull('parent_id')
- ->orWhere('parent_id', -1);
+ $query->whereNull('parent_id')->orWhere('parent_id', -1);
})
->with(['elementable', 'dataBindings.formDataSource'])
->orderBy('order')
@@ -484,21 +463,15 @@ protected function transformContainerElement(FormElement $element, array $elemen
$elementData['containerId'] = (string) ($element->id ?? '');
$elementData['clear_button'] = false;
- $elementData['codeContext'] = [
- 'name' => $this->generateCodeContextName($element->name ?? 'container')
- ];
+ $elementData['codeContext'] = ['name' => $this->generateCodeContextName($element->name ?? 'container')];
$elementData['attributes'] = $this->remapAttributes($this->getElementAttributes($element));
$elementData['label'] = $elementData['attributes']['legend'] ?? null;
$elementData['repeater'] = false;
$elementData['repeaterLabel'] = null;
- $elementData['pdfStyles'] = [
- 'display' => $element->visible_pdf ? null : 'none',
- ];
- $elementData['webStyles'] = [
- 'display' => $element->visible_web ? null : 'none',
- ];
+ $elementData['pdfStyles'] = ['display' => $this->isActive($element->visible_pdf) ? null : 'none'];
+ $elementData['webStyles'] = ['display' => $this->isActive($element->visible_web) ? null : 'none'];
// Add validation rules
$validation = $this->transformValidationRules($element);
@@ -540,17 +513,11 @@ protected function transformRepeatableContainerAsGroup(FormElement $element, arr
$elementData['minRepeats'] = $element->elementable?->min_repeats ?? null;
$elementData['maxRepeats'] = $element->elementable?->max_repeats ?? null;
$elementData['clear_button'] = $element->elementable?->clear_button ?? false;
- $elementData['codeContext'] = [
- 'name' => $this->generateCodeContextName($element->name ?? 'group')
- ];
+ $elementData['codeContext'] = ['name' => $this->generateCodeContextName($element->name ?? 'group')];
// Add styles
- $elementData['pdfStyles'] = [
- 'display' => $element->visible_pdf ? null : 'none',
- ];
- $elementData['webStyles'] = [
- 'display' => $element->visible_web ? null : 'none',
- ];
+ $elementData['pdfStyles'] = ['display' => $this->isActive($element->visible_pdf) ? null : 'none'];
+ $elementData['webStyles'] = ['display' => $this->isActive($element->visible_web) ? null : 'none'];
// Add validation rules
$validation = $this->transformValidationRules($element);
@@ -578,15 +545,11 @@ protected function transformRepeatableContainerAsGroup(FormElement $element, arr
$fields = [];
if ($children->count() > 0) {
- $fields = $children->map(function (FormElement $child) {
- return $this->transformElementToPreMigrationFormat($child);
- })->toArray();
+ $fields = $children->map(fn(FormElement $child) => $this->transformElementToPreMigrationFormat($child))->toArray();
}
// Create groupItems structure expected by the renderer
- $elementData['groupItems'] = [
- ['fields' => $fields]
- ];
+ $elementData['groupItems'] = [['fields' => $fields]];
// Add container-specific attributes
$attributes = $this->getElementAttributes($element);
@@ -609,17 +572,11 @@ protected function transformGroupElement(FormElement $element, array $elementDat
$elementData['minRepeats'] = $element->elementable?->min_repeats ?? null;
$elementData['maxRepeats'] = $element->elementable?->max_repeats ?? null;
$elementData['clear_button'] = $element->elementable?->clear_button ?? false;
- $elementData['codeContext'] = [
- 'name' => $this->generateCodeContextName($element->name ?? 'group')
- ];
+ $elementData['codeContext'] = ['name' => $this->generateCodeContextName($element->name ?? 'group')];
// Add styles
- $elementData['pdfStyles'] = [
- 'display' => $element->visible_pdf ? null : 'none',
- ];
- $elementData['webStyles'] = [
- 'display' => $element->visible_web ? null : 'none',
- ];
+ $elementData['pdfStyles'] = ['display' => $this->isActive($element->visible_pdf) ? null : 'none'];
+ $elementData['webStyles'] = ['display' => $this->isActive($element->visible_web) ? null : 'none'];
// Add validation rules
$validation = $this->transformValidationRules($element);
@@ -641,14 +598,9 @@ protected function transformGroupElement(FormElement $element, array $elementDat
$fields = [];
if ($children->count() > 0) {
- $fields = $children->map(function (FormElement $child) {
- return $this->transformElementToPreMigrationFormat($child);
- })->toArray();
+ $fields = $children->map(fn(FormElement $child) => $this->transformElementToPreMigrationFormat($child))->toArray();
}
-
- $elementData['groupItems'] = [
- ['fields' => $fields]
- ];
+ $elementData['groupItems'] = [['fields' => $fields]];
// Add group-specific attributes
$attributes = $this->getElementAttributes($element);
@@ -665,23 +617,14 @@ protected function transformStandardElement(FormElement $element, array $element
$elementData['attributes'] = $this->remapAttributes($this->getElementAttributes($element));
// Basic properties for all standard elements
$attributes = $this->getElementAttributes($element);
- if (isset($attributes['hideLabel']) && $attributes['hideLabel']) {
- $elementData['label'] = '';
- } else {
- $elementData['label'] = $attributes['labelText'] ?? '';
- }
+
+ $elementData['label'] = (isset($attributes['hide_label']) && $attributes['hide_label']) ? '' : ($attributes['label_text'] ?? '');
$elementData['helperText'] = $element->help_text;
$elementData['mask'] = null;
- $elementData['codeContext'] = [
- 'name' => $this->generateCodeContextName($element->name ?? 'field')
- ];
+ $elementData['codeContext'] = ['name' => $this->generateCodeContextName($element->name ?? 'field')];
- $elementData['pdfStyles'] = [
- 'display' => $element->visible_pdf ? null : 'none',
- ];
- $elementData['webStyles'] = [
- 'display' => $element->visible_web ? null : 'none',
- ];
+ $elementData['pdfStyles'] = ['display' => $this->isActive($element->visible_pdf) ? null : 'none'];
+ $elementData['webStyles'] = ['display' => $this->isActive($element->visible_web) ? null : 'none'];
// Add input type for specific elements
if (in_array($originalType, ['text-input', 'text-area', 'textarea-input', 'number-input', 'date-input', 'date-select-input', 'file-input'])) {
@@ -710,6 +653,7 @@ protected function transformStandardElement(FormElement $element, array $element
$this->addElementSpecificProperties($elementData, $element, $originalType);
$this->addElementStyles($elementData, $element);
+
return $elementData;
}
@@ -791,12 +735,10 @@ protected function addElementSpecificProperties(array &$elementData, FormElement
$radioOptions = [];
if ($element->elementable && method_exists($element->elementable, 'options')) {
$optionsCollection = $element->elementable->options()->ordered()->get();
- $radioOptions = $optionsCollection->map(function ($option) {
- return [
- 'value' => $option->value ?? '',
- 'text' => $option->label ?? '',
- ];
- })->toArray();
+ $radioOptions = $optionsCollection->map(fn($option) => [
+ 'value' => $option->value ?? '',
+ 'text' => $option->label ?? '',
+ ])->toArray();
}
if (!empty($radioOptions)) {
$elementData['listItems'] = $radioOptions;
@@ -808,17 +750,14 @@ protected function addElementSpecificProperties(array &$elementData, FormElement
$options = [];
if ($element->elementable && method_exists($element->elementable, 'options')) {
$optionsCollection = $element->elementable->options()->ordered()->get();
- $options = $optionsCollection->map(function ($option) {
- return [
- 'name' => $option->label ?? '',
- 'text' => $option->label ?? '',
- 'value' => $option->value ?? '',
- ];
- })->toArray();
+ $options = $optionsCollection->map(fn($option) => [
+ 'name' => $option->label ?? '',
+ 'text' => $option->label ?? '',
+ 'value' => $option->value ?? '',
+ ])->toArray();
}
- if (!empty($options)) {
+ if (!empty($options))
$elementData['listItems'] = $options;
- }
break;
case 'file':
// Add file-specific properties
@@ -946,18 +885,11 @@ protected function transformConditions(FormElement $element): array
'value' => $element->save_on_submit ? '{return true}' : '{return false}'
];
- if (!empty($element->custom_read_only) && $element->is_read_only) {
- $conditions[] = [
- 'type' => 'readOnly',
- 'value' => $element->custom_read_only
- ];
- } else {
- // If no custom read-only condition, use the default read-only state
- $conditions[] = [
- 'type' => 'readOnly',
- 'value' => $element->is_read_only ? '{return true}' : '{return false}'
- ];
- }
+ $isReadOnly = $this->isActive($element->is_read_only);
+ $conditions[] = [
+ 'type' => 'readOnly',
+ 'value' => $isReadOnly ? '{return true}' : '{return false}'
+ ];
return $conditions;
}
@@ -1120,9 +1052,7 @@ protected function convertKeyValueArrayToObject($keyValueArray): object
*/
protected function toCamelCase(string $str): string
{
- return preg_replace_callback('/_([a-z])/', function ($matches) {
- return strtoupper($matches[1]);
- }, $str);
+ return preg_replace_callback('/_([a-z])/', fn($matches) => strtoupper($matches[1]), $str);
}
/**
@@ -1166,10 +1096,7 @@ protected function customAttributeMapping(string $key, $value): ?array
case 'max':
case 'min':
case 'step':
- if (is_numeric($value)) {
- return [$key, (float) $value];
- }
- return [$key, $value];
+ return is_numeric($value) ? [$key, (float) $value] : [$key, $value];
case 'dateFormat':
if ($value) {
return ['dateFormat', DateSelectInputFormElement::convertToFlatpickrFormat($value)];
@@ -1241,4 +1168,12 @@ protected function remapAttributes(array $attributes): array
{
return $this->normalizeAttributes($attributes);
}
-}
+
+ /**
+ * Check if a state string represents an "active" state (i.e., not 'never', '', null, or false).
+ */
+ protected function isActive($state): bool
+ {
+ return !in_array($state, FormElement::getActiveToggleButtonStates(), true);
+ }
+}
\ No newline at end of file
diff --git a/database/migrations/2026_06_18_214659_add_visible_when_toggles.php b/database/migrations/2026_06_18_214659_add_visible_when_toggles.php
new file mode 100644
index 00000000..f93d3b11
--- /dev/null
+++ b/database/migrations/2026_06_18_214659_add_visible_when_toggles.php
@@ -0,0 +1,96 @@
+columns as $column) {
+ $table->string("{$column}_new")->nullable()->after($column);
+ }
+ });
+
+ // Step 2: Migrate data: true → 'always', false/null → null
+ foreach ($this->columns as $column) {
+ DB::table('form_elements')->chunkById(1000, function ($rows) use ($column) {
+ foreach ($rows as $row) {
+ $newValue = match ($row->{$column}) {
+ true => 'always',
+ default => null,
+ };
+
+ DB::table('form_elements')
+ ->where('id', $row->id)
+ ->update(["{$column}_new" => $newValue]);
+ }
+ });
+ }
+
+ // Step 3: Drop old boolean columns
+ Schema::table('form_elements', function (Blueprint $table) {
+ foreach ($this->columns as $column) {
+ $table->dropColumn($column);
+ }
+ });
+
+ // Step 4: Rename new columns to original names
+ Schema::table('form_elements', function (Blueprint $table) {
+ foreach ($this->columns as $column) {
+ $table->renameColumn("{$column}_new", $column);
+ }
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ // Step 1: Add temporary boolean columns
+ Schema::table('form_elements', function (Blueprint $table) {
+ foreach ($this->columns as $column) {
+ $table->boolean("{$column}_new")->default(false)->after($column);
+ }
+ });
+
+ // Step 2: Reverse migrate: 'always' → true, null → false
+ foreach ($this->columns as $column) {
+ DB::table('form_elements')->chunkById(1000, function ($rows) use ($column) {
+ foreach ($rows as $row) {
+ $newValue = $row->{$column} === 'always';
+
+ DB::table('form_elements')
+ ->where('id', $row->id)
+ ->update(["{$column}_new" => $newValue]);
+ }
+ });
+ }
+
+ // Step 3: Drop enum columns
+ Schema::table('form_elements', function (Blueprint $table) {
+ foreach ($this->columns as $column) {
+ $table->dropColumn($column);
+ }
+ });
+
+ // Step 4: Rename boolean columns back
+ Schema::table('form_elements', function (Blueprint $table) {
+ foreach ($this->columns as $column) {
+ $table->renameColumn("{$column}_new", $column);
+ }
+ });
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_06_23_191830_remove_custom_read_only_from_form_elements.php b/database/migrations/2026_06_23_191830_remove_custom_read_only_from_form_elements.php
new file mode 100644
index 00000000..c2b2199b
--- /dev/null
+++ b/database/migrations/2026_06_23_191830_remove_custom_read_only_from_form_elements.php
@@ -0,0 +1,28 @@
+dropColumn('custom_read_only');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('form_elements', function (Blueprint $table) {
+ // Restoring as a nullable text column since it previously held JS scripts
+ $table->text('custom_read_only')->nullable()->after('is_read_only');
+ });
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_07_02_150324_fix_toggle_defaults.php b/database/migrations/2026_07_02_150324_fix_toggle_defaults.php
new file mode 100644
index 00000000..9bf45db7
--- /dev/null
+++ b/database/migrations/2026_07_02_150324_fix_toggle_defaults.php
@@ -0,0 +1,51 @@
+where(function ($query) use ($col) {
+ $query->whereNull($col)
+ ->orWhere($col, false)
+ ->orWhere($col, 'false');
+ })
+ ->update([$col => 'never']);
+ }
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ $columns = [
+ 'visible_web',
+ 'visible_pdf',
+ 'is_required',
+ 'is_read_only',
+ ];
+
+ // Revert 'never' values back to null for the specified columns
+ foreach ($columns as $col) {
+ DB::table('form_elements')
+ ->where($col, 'never')
+ ->update([$col => null]);
+ }
+ }
+};
\ No newline at end of file
diff --git a/database/migrations/2026_07_07_211531_refactor_camel_case_columns.php b/database/migrations/2026_07_07_211531_refactor_camel_case_columns.php
new file mode 100644
index 00000000..59bae275
--- /dev/null
+++ b/database/migrations/2026_07_07_211531_refactor_camel_case_columns.php
@@ -0,0 +1,153 @@
+renameColumn('enableVarSub', 'enable_var_sub');
+ });
+ Schema::table('checkbox_group_form_elements', function (Blueprint $table) {
+ $table->renameColumn('labelText', 'label_text');
+ $table->renameColumn('hideLabel', 'hide_label');
+ $table->renameColumn('defaultSelected', 'default_selected');
+ $table->renameColumn('enableVarSub', 'enable_var_sub');
+ });
+ Schema::table('checkbox_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('labelText', 'label_text');
+ $table->renameColumn('hideLabel', 'hide_label');
+ $table->renameColumn('defaultChecked', 'default_checked');
+ $table->renameColumn('enableVarSub', 'enable_var_sub');
+ });
+ Schema::table('container_form_elements', function (Blueprint $table) {
+ $table->renameColumn('enableVarSub', 'enable_var_sub');
+ });
+ Schema::table('currency_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('enableVarSub', 'enable_var_sub');
+ $table->renameColumn('hideLabel', 'hide_label');
+ $table->renameColumn('defaultValue', 'default_value');
+ $table->renameColumn('labelText', 'label_text');
+ });
+ Schema::table('date_select_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('labelText', 'label_text');
+ $table->renameColumn('hideLabel', 'hide_label');
+ $table->renameColumn('minDate', 'min_date');
+ $table->renameColumn('maxDate', 'max_date');
+ $table->renameColumn('dateFormat', 'date_format');
+ $table->renameColumn('enableVarSub', 'enable_var_sub');
+ });
+ Schema::table('number_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('labelText', 'label_text');
+ $table->renameColumn('hideLabel', 'hide_label');
+ $table->renameColumn('defaultValue', 'default_value');
+ $table->renameColumn('enableVarSub', 'enable_var_sub');
+ $table->renameColumn('maskType', 'mask_type');
+ });
+ Schema::table('radio_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('labelText', 'label_text');
+ $table->renameColumn('hideLabel', 'hide_label');
+ $table->renameColumn('defaultSelected', 'default_selected');
+ $table->renameColumn('labelPosition', 'label_position');
+ $table->renameColumn('enableVarSub', 'enable_var_sub');
+ });
+ Schema::table('select_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('labelText', 'label_text');
+ $table->renameColumn('hideLabel', 'hide_label');
+ $table->renameColumn('defaultSelected', 'default_selected');
+ $table->renameColumn('enableVarSub', 'enable_var_sub');
+ });
+ Schema::table('textarea_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('labelText', 'label_text');
+ $table->renameColumn('hideLabel', 'hide_label');
+ $table->renameColumn('maxCount', 'max_count');
+ $table->renameColumn('defaultValue', 'default_value');
+ $table->renameColumn('enableVarSub', 'enable_var_sub');
+ });
+ Schema::table('text_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('labelText', 'label_text');
+ $table->renameColumn('hideLabel', 'hide_label');
+ $table->renameColumn('maxCount', 'max_count');
+ $table->renameColumn('defaultValue', 'default_value');
+ $table->renameColumn('enableVarSub', 'enable_var_sub');
+ $table->renameColumn('maskType', 'mask_type');
+ $table->renameColumn('maskErrorMessage', 'mask_error_message');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('button_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('enable_var_sub', 'enableVarSub');
+ });
+ Schema::table('checkbox_group_form_elements', function (Blueprint $table) {
+ $table->renameColumn('label_text', 'labelText');
+ $table->renameColumn('hide_label', 'hideLabel');
+ $table->renameColumn('default_selected', 'defaultSelected');
+ $table->renameColumn('enable_var_sub', 'enableVarSub');
+ });
+ Schema::table('checkbox_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('label_text', 'labelText');
+ $table->renameColumn('hide_label', 'hideLabel');
+ $table->renameColumn('default_checked', 'defaultChecked');
+ $table->renameColumn('enable_var_sub', 'enableVarSub');
+ });
+ Schema::table('container_form_elements', function (Blueprint $table) {
+ $table->renameColumn('enable_var_sub', 'enableVarSub');
+ });
+ Schema::table('currency_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('enable_var_sub', 'enableVarSub');
+ $table->renameColumn('hide_label', 'hideLabel');
+ $table->renameColumn('default_value', 'defaultValue');
+ $table->renameColumn('label_text', 'labelText');
+ });
+ Schema::table('date_select_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('label_text', 'labelText');
+ $table->renameColumn('hide_label', 'hideLabel');
+ $table->renameColumn('min_date', 'minDate');
+ $table->renameColumn('max_date', 'maxDate');
+ $table->renameColumn('date_format', 'dateFormat');
+ $table->renameColumn('enable_var_sub', 'enableVarSub');
+ });
+ Schema::table('number_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('label_text', 'labelText');
+ $table->renameColumn('hide_label', 'hideLabel');
+ $table->renameColumn('default_value', 'defaultValue');
+ $table->renameColumn('enable_var_sub', 'enableVarSub');
+ $table->renameColumn('mask_type', 'maskType');
+ });
+ Schema::table('radio_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('label_text', 'labelText');
+ $table->renameColumn('hide_label', 'hideLabel');
+ $table->renameColumn('default_selected', 'defaultSelected');
+ $table->renameColumn('label_position', 'labelPosition');
+ $table->renameColumn('enable_var_sub', 'enableVarSub');
+ });
+ Schema::table('select_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('label_text', 'labelText');
+ $table->renameColumn('hide_label', 'hideLabel');
+ $table->renameColumn('default_selected', 'defaultSelected');
+ $table->renameColumn('enable_var_sub', 'enableVarSub');
+ });
+ Schema::table('textarea_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('enable_var_sub', 'enableVarSub');
+ });
+ Schema::table('text_input_form_elements', function (Blueprint $table) {
+ $table->renameColumn('label_text', 'labelText');
+ $table->renameColumn('hide_label', 'hideLabel');
+ $table->renameColumn('max_count', 'maxCount');
+ $table->renameColumn('default_value', 'defaultValue');
+ $table->renameColumn('enable_var_sub', 'enableVarSub');
+ $table->renameColumn('mask_type', 'maskType');
+ $table->renameColumn('mask_error_message', 'maskErrorMessage');
+ });
+ }
+};
diff --git a/database/seeders/BulkFormMetadataUpdateSeeder.php b/database/seeders/BulkFormMetadataUpdateSeeder.php
new file mode 100644
index 00000000..8941293c
--- /dev/null
+++ b/database/seeders/BulkFormMetadataUpdateSeeder.php
@@ -0,0 +1,910 @@
+ 'HR3740E', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3740F', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3741', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3681', 'name' => 'Unknown'],
+ ['form_id' => 'HR3691F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3704F', 'name' => 'Unknown'],
+ ['form_id' => 'HR4008', 'name' => 'Unknown'],
+ ['form_id' => 'HR3689E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3687E-AT', 'name' => 'Unknown'],
+ ['form_id' => 'HR3691E - ATa', 'name' => 'Unknown'],
+ ['form_id' => 'HR3691E - ATb', 'name' => 'Unknown'],
+ ['form_id' => 'HR3691E - ATd', 'name' => 'Unknown'],
+ ['form_id' => 'HR3691E - ATf', 'name' => 'Unknown'],
+ ['form_id' => 'HR3260', 'name' => 'Adobe'],
+ ['form_id' => 'HR3599', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3219', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3262', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3534', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3233', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3236', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3249', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3334', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3335', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3555', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3633', 'name' => 'Unknown'],
+ ['form_id' => 'HR2247', 'name' => 'Unknown'],
+ ['form_id' => 'HR2766', 'name' => 'Unknown'],
+ ['form_id' => 'HR3352F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3353F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3354F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3355F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3356F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3357F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3358F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3360F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3361F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3362F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3363F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3364F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3365F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3366F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3367F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3368F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3369F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3370F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3372F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3376F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3379F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3380F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3381F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3382F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3383F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3384F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3385F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3386F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3387F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3388F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3389F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3390F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3391F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3392F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3393F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3394F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3396F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3397F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3398F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3399F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3400F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3450F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3451F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3452F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3453F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3454F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3455F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3456F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3458F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3459F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3484F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3688F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3692F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3693F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3694F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3695F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3696F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3698E-AT', 'name' => 'Unknown'],
+ ['form_id' => 'HR3698F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3699F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3700F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3701F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3702F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3703F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3710F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3655E', 'name' => 'Unknown'],
+ ['form_id' => 'CF0605_portal', 'name' => 'Unknown'],
+ ['form_id' => 'HR3492E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3600', 'name' => 'Unknown'],
+ ['form_id' => 'HR3601E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3602', 'name' => 'Unknown'],
+ ['form_id' => 'HR3603', 'name' => 'Unknown'],
+ ['form_id' => 'HR3604', 'name' => 'Unknown'],
+ ['form_id' => 'HR3605', 'name' => 'Unknown'],
+ ['form_id' => 'HR3606', 'name' => 'Unknown'],
+ ['form_id' => 'HR3607', 'name' => 'Unknown'],
+ ['form_id' => 'HR3608', 'name' => 'Unknown'],
+ ['form_id' => 'HR3609', 'name' => 'Unknown'],
+ ['form_id' => 'HR3610', 'name' => 'Unknown'],
+ ['form_id' => 'HR3611', 'name' => 'Unknown'],
+ ['form_id' => 'HR3612', 'name' => 'Unknown'],
+ ['form_id' => 'HR3613', 'name' => 'Unknown'],
+ ['form_id' => 'HR3654E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3671', 'name' => 'Unknown'],
+ ['form_id' => 'HR3711', 'name' => 'Unknown'],
+ ['form_id' => 'HR3714E', 'name' => 'Unknown'],
+ ['form_id' => 'MW-001', 'name' => 'Unknown'],
+ ['form_id' => 'HR3488E', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3499E', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3527E', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3589A', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3589B', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3589C', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3653E', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3678B', 'name' => 'Adobe Livecycle'],
+ ['form_id' => 'HR3259', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3322', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3347', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3348', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3349', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3476', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3478', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3479', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3480', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3481', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3482', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3491', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3537', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3538', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR3539', 'name' => 'Microsoft Word'],
+ ['form_id' => 'HR2152', 'name' => 'Unknown'],
+ ['form_id' => 'HR2181A', 'name' => 'Unknown'],
+ ['form_id' => 'HR2181B', 'name' => 'Unknown'],
+ ['form_id' => 'HR2181C', 'name' => 'Unknown'],
+ ['form_id' => 'HR2192', 'name' => 'Unknown'],
+ ['form_id' => 'HR2193', 'name' => 'Unknown'],
+ ['form_id' => 'HR2274', 'name' => 'Unknown'],
+ ['form_id' => 'HR2340', 'name' => 'Unknown'],
+ ['form_id' => 'HR2365', 'name' => 'Unknown'],
+ ['form_id' => 'HR2366', 'name' => 'Unknown'],
+ ['form_id' => 'HR2368', 'name' => 'Unknown'],
+ ['form_id' => 'HR2527', 'name' => 'Unknown'],
+ ['form_id' => 'HR2545', 'name' => 'Unknown'],
+ ['form_id' => 'HR2609', 'name' => 'Unknown'],
+ ['form_id' => 'HR2663B', 'name' => 'Unknown'],
+ ['form_id' => 'HR2674', 'name' => 'Unknown'],
+ ['form_id' => 'HR2754', 'name' => 'Unknown'],
+ ['form_id' => 'HR2767', 'name' => 'Unknown'],
+ ['form_id' => 'HR2778', 'name' => 'Unknown'],
+ ['form_id' => 'HR2781', 'name' => 'Unknown'],
+ ['form_id' => 'HR2791', 'name' => 'Unknown'],
+ ['form_id' => 'HR2808', 'name' => 'Unknown'],
+ ['form_id' => 'HR2846', 'name' => 'Unknown'],
+ ['form_id' => 'HR2857', 'name' => 'Unknown'],
+ ['form_id' => 'HR2859', 'name' => 'Unknown'],
+ ['form_id' => 'HR2860', 'name' => 'Unknown'],
+ ['form_id' => 'HR2863B', 'name' => 'Unknown'],
+ ['form_id' => 'HR2948', 'name' => 'Unknown'],
+ ['form_id' => 'HR2992', 'name' => 'Unknown'],
+ ['form_id' => 'HR3014', 'name' => 'Unknown'],
+ ['form_id' => 'HR3030', 'name' => 'Unknown'],
+ ['form_id' => 'HR3031', 'name' => 'Unknown'],
+ ['form_id' => 'HR3045', 'name' => 'Unknown'],
+ ['form_id' => 'HR3051', 'name' => 'Unknown'],
+ ['form_id' => 'HR3073', 'name' => 'Unknown'],
+ ['form_id' => 'HR3132', 'name' => 'Unknown'],
+ ['form_id' => 'HR3136', 'name' => 'Unknown'],
+ ['form_id' => 'HR3137', 'name' => 'Unknown'],
+ ['form_id' => 'HR3161', 'name' => 'Unknown'],
+ ['form_id' => 'HR3186', 'name' => 'Unknown'],
+ ['form_id' => 'HR3201', 'name' => 'Unknown'],
+ ['form_id' => 'HR3207A', 'name' => 'Unknown'],
+ ['form_id' => 'HR3215', 'name' => 'Unknown'],
+ ['form_id' => 'HR3221', 'name' => 'Unknown'],
+ ['form_id' => 'HR3222', 'name' => 'Unknown'],
+ ['form_id' => 'HR3223', 'name' => 'Unknown'],
+ ['form_id' => 'HR3242', 'name' => 'Unknown'],
+ ['form_id' => 'HR3243', 'name' => 'Unknown'],
+ ['form_id' => 'HR3246', 'name' => 'Unknown'],
+ ['form_id' => 'HR3247', 'name' => 'Unknown'],
+ ['form_id' => 'HR3270', 'name' => 'Unknown'],
+ ['form_id' => 'HR3272', 'name' => 'Unknown'],
+ ['form_id' => 'HR3313', 'name' => 'Unknown'],
+ ['form_id' => 'HR3333', 'name' => 'Unknown'],
+ ['form_id' => 'HR3338', 'name' => 'Unknown'],
+ ['form_id' => 'HR3339', 'name' => 'Unknown'],
+ ['form_id' => 'HR3346', 'name' => 'Unknown'],
+ ['form_id' => 'HR3467', 'name' => 'Unknown'],
+ ['form_id' => 'HR3483', 'name' => 'Unknown'],
+ ['form_id' => 'HR3486', 'name' => 'Unknown'],
+ ['form_id' => 'HR3487', 'name' => 'Unknown'],
+ ['form_id' => 'HR3493E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3494E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3495E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3496E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3497E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3498E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3507', 'name' => 'Unknown'],
+ ['form_id' => 'HR3540', 'name' => 'Unknown'],
+ ['form_id' => 'HR3550', 'name' => 'Unknown'],
+ ['form_id' => 'HR3587', 'name' => 'Unknown'],
+ ['form_id' => 'HR3588', 'name' => 'Unknown'],
+ ['form_id' => 'HR3590', 'name' => 'Unknown'],
+ ['form_id' => 'HR3593', 'name' => 'Unknown'],
+ ['form_id' => 'HR3594', 'name' => 'Unknown'],
+ ['form_id' => 'HR3596', 'name' => 'Unknown'],
+ ['form_id' => 'HR3597', 'name' => 'Unknown'],
+ ['form_id' => 'HR3623', 'name' => 'Unknown'],
+ ['form_id' => 'HR3624E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3625E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3631', 'name' => 'Unknown'],
+ ['form_id' => 'HR3632', 'name' => 'Unknown'],
+ ['form_id' => 'HR3638', 'name' => 'Unknown'],
+ ['form_id' => 'HR3645', 'name' => 'Unknown'],
+ ['form_id' => 'HR3646', 'name' => 'Unknown'],
+ ['form_id' => 'HR3648', 'name' => 'Unknown'],
+ ['form_id' => 'HR3652', 'name' => 'Unknown'],
+ ['form_id' => 'HR3656', 'name' => 'Unknown'],
+ ['form_id' => 'HR3657E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3660', 'name' => 'Unknown'],
+ ['form_id' => 'HR3661', 'name' => 'Unknown'],
+ ['form_id' => 'HR3663', 'name' => 'Unknown'],
+ ['form_id' => 'HR3664', 'name' => 'Unknown'],
+ ['form_id' => 'HR3665', 'name' => 'Unknown'],
+ ['form_id' => 'HR3666', 'name' => 'Unknown'],
+ ['form_id' => 'HR3668', 'name' => 'Unknown'],
+ ['form_id' => 'HR3668B', 'name' => 'Unknown'],
+ ['form_id' => 'HR3673', 'name' => 'Unknown'],
+ ['form_id' => 'HR3675', 'name' => 'Unknown'],
+ ['form_id' => 'HR3676', 'name' => 'Unknown'],
+ ['form_id' => 'HR3682', 'name' => 'Unknown'],
+ ['form_id' => 'HR3683', 'name' => 'Unknown'],
+ ['form_id' => 'HR3684', 'name' => 'Unknown'],
+ ['form_id' => 'HR3719', 'name' => 'Unknown'],
+ ['form_id' => 'HR3731', 'name' => 'Unknown'],
+ ['form_id' => 'HR3732', 'name' => 'Unknown'],
+ ['form_id' => 'HR3733', 'name' => 'Unknown'],
+ ['form_id' => 'HR3734', 'name' => 'Unknown'],
+ ['form_id' => 'HR4005', 'name' => 'Unknown'],
+ ['form_id' => 'HR4006', 'name' => 'Unknown'],
+ ['form_id' => 'HR4007', 'name' => 'Unknown'],
+ ['form_id' => 'HR4009', 'name' => 'Unknown'],
+ ['form_id' => 'HR4016', 'name' => 'Unknown'],
+ ['form_id' => 'HR4027', 'name' => 'Unknown'],
+ ['form_id' => 'HR4032', 'name' => 'Unknown'],
+ ['form_id' => 'HR3655F', 'name' => 'Unknown'],
+ ['form_id' => 'HR3697F', 'name' => 'Unknown'],
+ ];
+
+ foreach ($softwareSourceUpdates as $update) {
+ $form = Form::where('form_id', $update['form_id'])->first();
+ $source = FormSoftwareSource::where('name', $update['name'])->first();
+
+ if ($form && $source) {
+ DB::table('form_software_source_form')->insertOrIgnore([
+ 'form_id' => $form->id,
+ 'form_software_source_id' => $source->id,
+ ]);
+ } else {
+ // Log a warning if a title in your spreadsheet doesn't match the DB
+ if (!$form) {
+ Log::warning("BulkMetadataUpdateSeeder mismatch: Could not find Form '{$update['form_id']}'");
+ }
+ if (!$source) {
+ Log::warning("BulkMetadataUpdateSeeder mismatch: Could not find Software Source '{$update['name']}'");
+ }
+ }
+ }
+
+ // ========================================================================
+ // 2. FORM LOCATION UPDATES
+ // ========================================================================
+ Log::warning("BulkMetadataUpdateSeeder: Beginning bulk update of form locations...");
+
+ $formLocationUpdates = [
+ ['form_id' => 'CF0149', 'name' => 'Unknown'],
+ ['form_id' => 'CF2094', 'name' => 'Unknown'],
+ ['form_id' => 'HR3689E', 'name' => 'ICM'],
+ ['form_id' => 'CFL00540', 'name' => 'ICM'],
+ ['form_id' => 'CFL00544', 'name' => 'ICM'],
+ ['form_id' => 'HR3687E-AT', 'name' => 'Unknown'],
+ ['form_id' => 'HR3691E - ATa', 'name' => 'Unknown'],
+ ['form_id' => 'HR3691E - ATb', 'name' => 'Unknown'],
+ ['form_id' => 'HR3691E - ATd', 'name' => 'Unknown'],
+ ['form_id' => 'HR3691E - ATf', 'name' => 'Unknown'],
+ ['form_id' => 'CF2447', 'name' => 'Unknown'],
+ ['form_id' => 'CF0725', 'name' => 'Unknown'],
+ ['form_id' => 'CF2601', 'name' => 'Unknown'],
+ ['form_id' => 'CF2156', 'name' => 'ICM'],
+ ['form_id' => 'CF2157', 'name' => 'ICM'],
+ ['form_id' => 'CF4087', 'name' => 'ICM'],
+ ['form_id' => 'CFL01015', 'name' => 'ICM'],
+ ['form_id' => 'CFL01048', 'name' => 'ICM'],
+ ['form_id' => 'CFL01071', 'name' => 'ICM'],
+ ['form_id' => 'CFL01072', 'name' => 'ICM'],
+ ['form_id' => 'CFL04556', 'name' => 'ICM'],
+ ['form_id' => 'HR3655E', 'name' => 'Unknown'],
+ ['form_id' => 'CF0605_portal', 'name' => 'Unknown'],
+ ['form_id' => 'HR3492E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3600', 'name' => 'Unknown'],
+ ['form_id' => 'HR3601E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3602', 'name' => 'Unknown'],
+ ['form_id' => 'HR3603', 'name' => 'Unknown'],
+ ['form_id' => 'HR3604', 'name' => 'Unknown'],
+ ['form_id' => 'HR3605', 'name' => 'Unknown'],
+ ['form_id' => 'HR3606', 'name' => 'Unknown'],
+ ['form_id' => 'HR3607', 'name' => 'Unknown'],
+ ['form_id' => 'HR3608', 'name' => 'Unknown'],
+ ['form_id' => 'HR3609', 'name' => 'Unknown'],
+ ['form_id' => 'HR3610', 'name' => 'Unknown'],
+ ['form_id' => 'HR3611', 'name' => 'Unknown'],
+ ['form_id' => 'HR3612', 'name' => 'Unknown'],
+ ['form_id' => 'HR3613', 'name' => 'Unknown'],
+ ['form_id' => 'HR3654E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3671', 'name' => 'Unknown'],
+ ['form_id' => 'HR3711', 'name' => 'Unknown'],
+ ['form_id' => 'HR3714E', 'name' => 'Unknown'],
+ ['form_id' => 'MW-001', 'name' => 'Unknown'],
+ ['form_id' => 'CF0620', 'name' => 'Unknown'],
+ ['form_id' => 'CF3002', 'name' => 'Unknown'],
+ ['form_id' => 'CF4182', 'name' => 'Unknown'],
+ ['form_id' => 'CF4183', 'name' => 'Unknown'],
+ ['form_id' => 'CF4184', 'name' => 'Unknown'],
+ ['form_id' => 'CF4185', 'name' => 'Unknown'],
+ ['form_id' => 'Sample Non-Interactive', 'name' => 'Unknown'],
+ ['form_id' => 'CF0006', 'name' => 'Unknown'],
+ ['form_id' => 'Bus Pass Renewal Application', 'name' => 'Unknown'],
+ ['form_id' => 'CF0075', 'name' => 'Unknown'],
+ ['form_id' => 'CF0247', 'name' => 'Unknown'],
+ ['form_id' => 'CF0263', 'name' => 'Unknown'],
+ ['form_id' => 'CF0270', 'name' => 'Unknown'],
+ ['form_id' => 'CF0271', 'name' => 'Unknown'],
+ ['form_id' => 'CF0328', 'name' => 'Unknown'],
+ ['form_id' => 'CF0380', 'name' => 'Unknown'],
+ ['form_id' => 'CF0381', 'name' => 'Unknown'],
+ ['form_id' => 'CF0411A', 'name' => 'Unknown'],
+ ['form_id' => 'CF0453', 'name' => 'Unknown'],
+ ['form_id' => 'CF0603', 'name' => 'Unknown'],
+ ['form_id' => 'CF0610', 'name' => 'Unknown'],
+ ['form_id' => 'CF0635', 'name' => 'Unknown'],
+ ['form_id' => 'CF0636', 'name' => 'Unknown'],
+ ['form_id' => 'CF0637', 'name' => 'Unknown'],
+ ['form_id' => 'CF0638', 'name' => 'Unknown'],
+ ['form_id' => 'CF0639', 'name' => 'Unknown'],
+ ['form_id' => 'CF0652', 'name' => 'Unknown'],
+ ['form_id' => 'CF0654', 'name' => 'Unknown'],
+ ['form_id' => 'CF0654A', 'name' => 'Unknown'],
+ ['form_id' => 'CF0656', 'name' => 'Unknown'],
+ ['form_id' => 'CF0660', 'name' => 'Unknown'],
+ ['form_id' => 'CF0664', 'name' => 'Unknown'],
+ ['form_id' => 'CF0666', 'name' => 'Unknown'],
+ ['form_id' => 'CF0670', 'name' => 'Unknown'],
+ ['form_id' => 'CF0673', 'name' => 'Unknown'],
+ ['form_id' => 'CF0707', 'name' => 'Unknown'],
+ ['form_id' => 'CF0708', 'name' => 'Unknown'],
+ ['form_id' => 'CF0753', 'name' => 'Unknown'],
+ ['form_id' => 'CF0754', 'name' => 'Unknown'],
+ ['form_id' => 'CF0755', 'name' => 'Unknown'],
+ ['form_id' => 'CF0756', 'name' => 'Unknown'],
+ ['form_id' => 'CF0900', 'name' => 'Unknown'],
+ ['form_id' => 'CF0902B', 'name' => 'Unknown'],
+ ['form_id' => 'CF0903', 'name' => 'Unknown'],
+ ['form_id' => 'CF0912', 'name' => 'Unknown'],
+ ['form_id' => 'CF0927', 'name' => 'Unknown'],
+ ['form_id' => 'CF0950', 'name' => 'Unknown'],
+ ['form_id' => 'CF1001', 'name' => 'Unknown'],
+ ['form_id' => 'CF1005', 'name' => 'Unknown'],
+ ['form_id' => 'CF1005C', 'name' => 'Unknown'],
+ ['form_id' => 'CF1036', 'name' => 'Unknown'],
+ ['form_id' => 'CF1048', 'name' => 'Unknown'],
+ ['form_id' => 'CF1054', 'name' => 'Unknown'],
+ ['form_id' => 'CF1297', 'name' => 'Unknown'],
+ ['form_id' => 'CF1298', 'name' => 'Unknown'],
+ ['form_id' => 'CF1301', 'name' => 'Unknown'],
+ ['form_id' => 'CF1304', 'name' => 'Unknown'],
+ ['form_id' => 'CF1305', 'name' => 'Unknown'],
+ ['form_id' => 'CF1305A', 'name' => 'Unknown'],
+ ['form_id' => 'CF1305B', 'name' => 'Unknown'],
+ ['form_id' => 'CF1305C', 'name' => 'Unknown'],
+ ['form_id' => 'CF1305D', 'name' => 'Unknown'],
+ ['form_id' => 'CF1306', 'name' => 'Unknown'],
+ ['form_id' => 'CF1320', 'name' => 'Unknown'],
+ ['form_id' => 'CF1320A', 'name' => 'Unknown'],
+ ['form_id' => 'CF1321', 'name' => 'Unknown'],
+ ['form_id' => 'CF1324', 'name' => 'Unknown'],
+ ['form_id' => 'CF1326', 'name' => 'Unknown'],
+ ['form_id' => 'CF1325a', 'name' => 'Unknown'],
+ ['form_id' => 'CF1325b', 'name' => 'Unknown'],
+ ['form_id' => 'CF1330', 'name' => 'Unknown'],
+ ['form_id' => 'CF1331', 'name' => 'Unknown'],
+ ['form_id' => 'CF1332', 'name' => 'Unknown'],
+ ['form_id' => 'CF1335', 'name' => 'Unknown'],
+ ['form_id' => 'CF1336', 'name' => 'Unknown'],
+ ['form_id' => 'CF1345', 'name' => 'Unknown'],
+ ['form_id' => 'CF1346', 'name' => 'Unknown'],
+ ['form_id' => 'CF1369', 'name' => 'Unknown'],
+ ['form_id' => 'CF1370', 'name' => 'Unknown'],
+ ['form_id' => 'CF1371', 'name' => 'Unknown'],
+ ['form_id' => 'CF1372', 'name' => 'Unknown'],
+ ['form_id' => 'CF1470a', 'name' => 'Unknown'],
+ ['form_id' => 'CF1470b', 'name' => 'Unknown'],
+ ['form_id' => 'CF1703A', 'name' => 'Unknown'],
+ ['form_id' => 'CF1704A', 'name' => 'Unknown'],
+ ['form_id' => 'CF1706a', 'name' => 'Unknown'],
+ ['form_id' => 'CF1706b', 'name' => 'Unknown'],
+ ['form_id' => 'CF1900', 'name' => 'Unknown'],
+ ['form_id' => 'CF1901', 'name' => 'Unknown'],
+ ['form_id' => 'CF1902', 'name' => 'Unknown'],
+ ['form_id' => 'CF1903', 'name' => 'Unknown'],
+ ['form_id' => 'CF1904', 'name' => 'Unknown'],
+ ['form_id' => 'CF1905', 'name' => 'Unknown'],
+ ['form_id' => 'CF2002', 'name' => 'Unknown'],
+ ['form_id' => 'CF2003', 'name' => 'Unknown'],
+ ['form_id' => 'CF2004', 'name' => 'Unknown'],
+ ['form_id' => 'CF2007', 'name' => 'Unknown'],
+ ['form_id' => 'CF2040', 'name' => 'Unknown'],
+ ['form_id' => 'CF2040A', 'name' => 'Unknown'],
+ ['form_id' => 'CF2044A', 'name' => 'Unknown'],
+ ['form_id' => 'CF2045', 'name' => 'Unknown'],
+ ['form_id' => 'CF2185', 'name' => 'Unknown'],
+ ['form_id' => 'CF2343', 'name' => 'Unknown'],
+ ['form_id' => 'CF2512', 'name' => 'Unknown'],
+ ['form_id' => 'CF2514', 'name' => 'Unknown'],
+ ['form_id' => 'CF2515', 'name' => 'Unknown'],
+ ['form_id' => 'CF2568', 'name' => 'Unknown'],
+ ['form_id' => 'CF2600', 'name' => 'Unknown'],
+ ['form_id' => 'CF2644', 'name' => 'Unknown'],
+ ['form_id' => 'CF2655', 'name' => 'Unknown'],
+ ['form_id' => 'CF2657', 'name' => 'Unknown'],
+ ['form_id' => 'CF2657A', 'name' => 'Unknown'],
+ ['form_id' => 'CF2657B', 'name' => 'Unknown'],
+ ['form_id' => 'CF2657C', 'name' => 'Unknown'],
+ ['form_id' => 'CF2671', 'name' => 'Unknown'],
+ ['form_id' => 'CF2679', 'name' => 'Unknown'],
+ ['form_id' => 'CF2682', 'name' => 'Unknown'],
+ ['form_id' => 'CF2684', 'name' => 'Unknown'],
+ ['form_id' => 'CF2685', 'name' => 'Unknown'],
+ ['form_id' => 'CF2710', 'name' => 'Unknown'],
+ ['form_id' => 'CF2723', 'name' => 'Unknown'],
+ ['form_id' => 'CF2732', 'name' => 'Unknown'],
+ ['form_id' => 'CF2734', 'name' => 'Unknown'],
+ ['form_id' => 'CF2910A', 'name' => 'Unknown'],
+ ['form_id' => 'CF2932', 'name' => 'Unknown'],
+ ['form_id' => 'CF2960A', 'name' => 'Unknown'],
+ ['form_id' => 'CF2961', 'name' => 'Unknown'],
+ ['form_id' => 'CF2962', 'name' => 'Unknown'],
+ ['form_id' => 'CF2963', 'name' => 'Unknown'],
+ ['form_id' => 'CF3030', 'name' => 'Unknown'],
+ ['form_id' => 'CF3501A', 'name' => 'Unknown'],
+ ['form_id' => 'CF3700', 'name' => 'Unknown'],
+ ['form_id' => 'CF4070', 'name' => 'Unknown'],
+ ['form_id' => 'CF4071', 'name' => 'Unknown'],
+ ['form_id' => 'CF4072', 'name' => 'Unknown'],
+ ['form_id' => 'CF4076', 'name' => 'Unknown'],
+ ['form_id' => 'CF4084', 'name' => 'Unknown'],
+ ['form_id' => 'CF4085', 'name' => 'Unknown'],
+ ['form_id' => 'CF4086', 'name' => 'Unknown'],
+ ['form_id' => 'CF4092', 'name' => 'Unknown'],
+ ['form_id' => 'CF4101', 'name' => 'Unknown'],
+ ['form_id' => 'CF4102', 'name' => 'Unknown'],
+ ['form_id' => 'CF4103', 'name' => 'Unknown'],
+ ['form_id' => 'CF4104', 'name' => 'Unknown'],
+ ['form_id' => 'CF4105', 'name' => 'Unknown'],
+ ['form_id' => 'CF4106', 'name' => 'Unknown'],
+ ['form_id' => 'CF4107', 'name' => 'Unknown'],
+ ['form_id' => 'CF4108', 'name' => 'Unknown'],
+ ['form_id' => 'CF4109', 'name' => 'Unknown'],
+ ['form_id' => 'CF4110', 'name' => 'Unknown'],
+ ['form_id' => 'CF4111', 'name' => 'Unknown'],
+ ['form_id' => 'CF4114', 'name' => 'Unknown'],
+ ['form_id' => 'CF4156', 'name' => 'Unknown'],
+ ['form_id' => 'CF4160', 'name' => 'Unknown'],
+ ['form_id' => 'CF4181', 'name' => 'Unknown'],
+ ['form_id' => 'CF4112', 'name' => 'Unknown'],
+ ['form_id' => 'CF4113', 'name' => 'Unknown'],
+ ['form_id' => 'CF4144', 'name' => 'Unknown'],
+ ['form_id' => 'CF4145', 'name' => 'Unknown'],
+ ['form_id' => 'CF4146', 'name' => 'Unknown'],
+ ['form_id' => 'CFL000001AA', 'name' => 'Unknown'],
+ ['form_id' => 'CFL000010', 'name' => 'Unknown'],
+ ['form_id' => 'CFL00010', 'name' => 'Unknown'],
+ ['form_id' => 'CFL00543', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01023', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01024', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01025', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01026', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01027', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01028', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01029', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01030', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01031', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01032', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01033', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01034', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01035', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01036', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01049', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01050', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01051', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01052', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01053', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01054', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01055', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01056', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01057', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01058', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01059', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01060', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01061', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01062', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01063', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01064', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01065', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01066', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01067', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01068', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01069', 'name' => 'Unknown'],
+ ['form_id' => 'CFL01070', 'name' => 'Unknown'],
+ ['form_id' => 'CFL02015', 'name' => 'Unknown'],
+ ['form_id' => 'CFL04555', 'name' => 'Unknown'],
+ ['form_id' => 'CFL04557', 'name' => 'Unknown'],
+ ['form_id' => 'CFL04558', 'name' => 'Unknown'],
+ ['form_id' => 'CFL04559', 'name' => 'Unknown'],
+ ['form_id' => 'HR0080P', 'name' => 'Unknown'],
+ ['form_id' => 'HR3374E', 'name' => 'Unknown'],
+ ['form_id' => 'HR3375E', 'name' => 'Unknown'],
+ ['form_id' => 'Trauma Brief Screen', 'name' => 'Unknown'],
+ ['form_id' => 'HR4029', 'name' => 'Unknown'],
+ ['form_id' => 'CF0643', 'name' => 'Unknown'],
+ ['form_id' => 'CF0911', 'name' => 'Unknown'],
+ ['form_id' => 'CF3505', 'name' => 'Unknown'],
+ ['form_id' => 'Coding Block', 'name' => 'Unknown'],
+ ['form_id' => 'Petty Cash Reconciliation Replenishment Report', 'name' => 'Unknown'],
+ ];
+
+ foreach ($formLocationUpdates as $update) {
+ $form = Form::where('form_id', $update['form_id'])->first();
+ $location = FormLocation::where('name', $update['name'])->first();
+
+ if ($form && $location) {
+ DB::table('form_form_location')->insertOrIgnore([
+ 'form_id' => $form->id,
+ 'form_location_id' => $location->id,
+ ]);
+ } else {
+ if (!$form) {
+ Log::warning("BulkMetadataUpdateSeeder mismatch: Could not find Form '{$update['form_id']}'");
+ }
+ if (!$location) {
+ Log::warning("BulkMetadataUpdateSeeder mismatch: Could not find Location '{$update['name']}'");
+ }
+ }
+ }
+
+ // ========================================================================
+ // 3. USER TYPE UPDATES
+ // ========================================================================
+
+ Log::warning("BulkMetadataUpdateSeeder: Beginning bulk update of form user types...");
+
+ $userTypeUpdates = [
+ ['form_id' => 'CF0149', 'name' => 'Internal'],
+ ['form_id' => 'CF2094', 'name' => 'Internal'],
+ ['form_id' => 'HR3740E', 'name' => 'Internal'],
+ ['form_id' => 'HR3740F', 'name' => 'Internal'],
+ ['form_id' => 'HR3741', 'name' => 'Internal'],
+ ['form_id' => 'HR3681', 'name' => 'Internal'],
+ ['form_id' => 'HR3691F', 'name' => 'Internal'],
+ ['form_id' => 'HR3704F', 'name' => 'Internal'],
+ ['form_id' => 'HR4008', 'name' => 'Internal'],
+ ['form_id' => 'HR4041', 'name' => 'Internal'],
+ ['form_id' => 'HR4042', 'name' => 'Internal'],
+ ['form_id' => 'T5007', 'name' => 'Internal'],
+ ['form_id' => 'Decision Report Template', 'name' => 'Internal'],
+ ['form_id' => 'CF2186', 'name' => 'Internal'],
+ ['form_id' => 'CF0671', 'name' => 'Internal'],
+ ['form_id' => 'CF0711', 'name' => 'Internal'],
+ ['form_id' => 'CF1071', 'name' => 'Internal'],
+ ['form_id' => 'CF1072', 'name' => 'Internal'],
+ ['form_id' => 'CF2190', 'name' => 'Internal'],
+ ['form_id' => 'CF4177', 'name' => 'Internal'],
+ ['form_id' => 'CFDRT001', 'name' => 'Internal'],
+ ['form_id' => 'CFL00514', 'name' => 'Internal'],
+ ['form_id' => 'CFL01010', 'name' => 'Internal'],
+ ['form_id' => 'CFL01013', 'name' => 'Internal'],
+ ['form_id' => 'CFL01014', 'name' => 'Internal'],
+ ['form_id' => 'CFL01016', 'name' => 'Internal'],
+ ['form_id' => 'CFL01017', 'name' => 'Internal'],
+ ['form_id' => 'CFL01018', 'name' => 'Internal'],
+ ['form_id' => 'CFL01019', 'name' => 'Internal'],
+ ['form_id' => 'CFL01021', 'name' => 'Internal'],
+ ['form_id' => 'CFL01041', 'name' => 'Internal'],
+ ['form_id' => 'CFL01042', 'name' => 'Internal'],
+ ['form_id' => 'CFL02010', 'name' => 'Internal'],
+ ['form_id' => 'CFL02012', 'name' => 'Internal'],
+ ['form_id' => 'CFL02016', 'name' => 'Internal'],
+ ['form_id' => 'CFL02019', 'name' => 'Internal'],
+ ['form_id' => 'CFL02021', 'name' => 'Internal'],
+ ['form_id' => 'CFL04510', 'name' => 'Internal'],
+ ['form_id' => 'CFL04512', 'name' => 'Internal'],
+ ['form_id' => 'CFL04515', 'name' => 'Internal'],
+ ['form_id' => 'CFL04516', 'name' => 'Internal'],
+ ['form_id' => 'CFL04520', 'name' => 'Internal'],
+ ['form_id' => 'CFL04523', 'name' => 'Internal'],
+ ['form_id' => 'CFL04524', 'name' => 'Internal'],
+ ['form_id' => 'CFL04526', 'name' => 'Internal'],
+ ['form_id' => 'CFL04527', 'name' => 'Internal'],
+ ['form_id' => 'CFL04528', 'name' => 'Internal'],
+ ['form_id' => 'CFL04529', 'name' => 'Internal'],
+ ['form_id' => 'CFL04530', 'name' => 'Internal'],
+ ['form_id' => 'CFL04533', 'name' => 'Internal'],
+ ['form_id' => 'CFL04536', 'name' => 'Internal'],
+ ['form_id' => 'CFL04541', 'name' => 'Internal'],
+ ['form_id' => 'CFL04543', 'name' => 'Internal'],
+ ['form_id' => 'CFL04554', 'name' => 'Internal'],
+ ['form_id' => 'HR2848', 'name' => 'Internal'],
+ ['form_id' => 'HR2865', 'name' => 'Internal'],
+ ['form_id' => 'HR3460', 'name' => 'Internal'],
+ ['form_id' => 'HR3461', 'name' => 'Internal'],
+ ['form_id' => 'HR3462', 'name' => 'Internal'],
+ ['form_id' => 'HR3464', 'name' => 'Internal'],
+ ['form_id' => 'HR3466', 'name' => 'Internal'],
+ ['form_id' => 'HR3504', 'name' => 'Internal'],
+ ['form_id' => 'HR3627', 'name' => 'Internal'],
+ ['form_id' => 'HR3628', 'name' => 'Internal'],
+ ['form_id' => 'HR3629', 'name' => 'Internal'],
+ ['form_id' => 'HR3630', 'name' => 'Internal'],
+ ['form_id' => 'HR3688R', 'name' => 'Internal'],
+ ['form_id' => 'HR3701E', 'name' => 'Internal'],
+ ['form_id' => 'HR3702E', 'name' => 'Internal'],
+ ['form_id' => 'NOA GST', 'name' => 'Internal'],
+ ['form_id' => 'CF0025', 'name' => 'Internal'],
+ ['form_id' => 'CF2511', 'name' => 'Internal'],
+ ['form_id' => 'HR2916', 'name' => 'Internal'],
+ ['form_id' => 'HR3503', 'name' => 'Internal'],
+ ['form_id' => 'HR2892A', 'name' => 'Internal'],
+ ['form_id' => 'HR3687E', 'name' => 'Internal'],
+ ['form_id' => 'HR2384', 'name' => 'Internal'],
+ ['form_id' => 'CF8787', 'name' => 'Internal'],
+ ['form_id' => 'CF0640', 'name' => 'Internal'],
+ ['form_id' => 'HR3234', 'name' => 'Internal'],
+ ['form_id' => 'HR3689E', 'name' => 'Public'],
+ ['form_id' => 'CFL00540', 'name' => 'Public'],
+ ['form_id' => 'CFL00544', 'name' => 'Public'],
+ ['form_id' => 'HR3687E-AT', 'name' => 'Public'],
+ ['form_id' => 'HR3691E - ATa', 'name' => 'Public'],
+ ['form_id' => 'HR3691E - ATb', 'name' => 'Public'],
+ ['form_id' => 'HR3691E - ATd', 'name' => 'Public'],
+ ['form_id' => 'HR3691E - ATf', 'name' => 'Public'],
+ ['form_id' => 'CF2447', 'name' => 'Public'],
+ ['form_id' => 'CF0725', 'name' => 'Public'],
+ ['form_id' => 'CF2601', 'name' => 'Public'],
+ ['form_id' => 'HR3260', 'name' => 'Public'],
+ ['form_id' => 'HR3599', 'name' => 'Public'],
+ ['form_id' => 'HR3219', 'name' => 'Public'],
+ ['form_id' => 'HR3262', 'name' => 'Public'],
+ ['form_id' => 'HR3534', 'name' => 'Public'],
+ ['form_id' => 'HR3233', 'name' => 'Public'],
+ ['form_id' => 'HR3236', 'name' => 'Public'],
+ ['form_id' => 'HR3249', 'name' => 'Public'],
+ ['form_id' => 'HR3334', 'name' => 'Public'],
+ ['form_id' => 'HR3335', 'name' => 'Public'],
+ ['form_id' => 'HR3555', 'name' => 'Public'],
+ ['form_id' => 'HR3633', 'name' => 'Public'],
+ ['form_id' => 'HR2247', 'name' => 'Public'],
+ ['form_id' => 'HR2766', 'name' => 'Public'],
+ ['form_id' => 'HR3352F', 'name' => 'Public'],
+ ['form_id' => 'HR3353F', 'name' => 'Public'],
+ ['form_id' => 'HR3354F', 'name' => 'Public'],
+ ['form_id' => 'HR3355F', 'name' => 'Public'],
+ ['form_id' => 'HR3356F', 'name' => 'Public'],
+ ['form_id' => 'HR3357F', 'name' => 'Public'],
+ ['form_id' => 'HR3358F', 'name' => 'Public'],
+ ['form_id' => 'HR3360F', 'name' => 'Public'],
+ ['form_id' => 'HR3361F', 'name' => 'Public'],
+ ['form_id' => 'HR3362F', 'name' => 'Public'],
+ ['form_id' => 'HR3363F', 'name' => 'Public'],
+ ['form_id' => 'HR3364F', 'name' => 'Public'],
+ ['form_id' => 'HR3365F', 'name' => 'Public'],
+ ['form_id' => 'HR3366F', 'name' => 'Public'],
+ ['form_id' => 'HR3367F', 'name' => 'Public'],
+ ['form_id' => 'HR3368F', 'name' => 'Public'],
+ ['form_id' => 'HR3369F', 'name' => 'Public'],
+ ['form_id' => 'HR3370F', 'name' => 'Public'],
+ ['form_id' => 'HR3372F', 'name' => 'Public'],
+ ['form_id' => 'HR3376F', 'name' => 'Public'],
+ ['form_id' => 'HR3379F', 'name' => 'Public'],
+ ['form_id' => 'HR3380F', 'name' => 'Public'],
+ ['form_id' => 'HR3381F', 'name' => 'Public'],
+ ['form_id' => 'HR3382F', 'name' => 'Public'],
+ ['form_id' => 'HR3383F', 'name' => 'Public'],
+ ['form_id' => 'HR3384F', 'name' => 'Public'],
+ ['form_id' => 'HR3385F', 'name' => 'Public'],
+ ['form_id' => 'HR3386F', 'name' => 'Public'],
+ ['form_id' => 'HR3387F', 'name' => 'Public'],
+ ['form_id' => 'HR3388F', 'name' => 'Public'],
+ ['form_id' => 'HR3389F', 'name' => 'Public'],
+ ['form_id' => 'HR3390F', 'name' => 'Public'],
+ ['form_id' => 'HR3391F', 'name' => 'Public'],
+ ['form_id' => 'HR3392F', 'name' => 'Public'],
+ ['form_id' => 'HR3393F', 'name' => 'Public'],
+ ['form_id' => 'HR3394F', 'name' => 'Public'],
+ ['form_id' => 'HR3396F', 'name' => 'Public'],
+ ['form_id' => 'HR3397F', 'name' => 'Public'],
+ ['form_id' => 'HR3398F', 'name' => 'Public'],
+ ['form_id' => 'HR3399F', 'name' => 'Public'],
+ ['form_id' => 'HR3400F', 'name' => 'Public'],
+ ['form_id' => 'HR3450F', 'name' => 'Public'],
+ ['form_id' => 'HR3451F', 'name' => 'Public'],
+ ['form_id' => 'HR3452F', 'name' => 'Public'],
+ ['form_id' => 'HR3453F', 'name' => 'Public'],
+ ['form_id' => 'HR3454F', 'name' => 'Public'],
+ ['form_id' => 'HR3455F', 'name' => 'Public'],
+ ['form_id' => 'HR3456F', 'name' => 'Public'],
+ ['form_id' => 'HR3458F', 'name' => 'Public'],
+ ['form_id' => 'HR3459F', 'name' => 'Public'],
+ ['form_id' => 'HR3484F', 'name' => 'Public'],
+ ['form_id' => 'HR3688F', 'name' => 'Public'],
+ ['form_id' => 'HR3692F', 'name' => 'Public'],
+ ['form_id' => 'HR3693F', 'name' => 'Public'],
+ ['form_id' => 'HR3694F', 'name' => 'Public'],
+ ['form_id' => 'HR3695F', 'name' => 'Public'],
+ ['form_id' => 'HR3696F', 'name' => 'Public'],
+ ['form_id' => 'HR3698E-AT', 'name' => 'Public'],
+ ['form_id' => 'HR3698F', 'name' => 'Public'],
+ ['form_id' => 'HR3699F', 'name' => 'Public'],
+ ['form_id' => 'HR3700F', 'name' => 'Public'],
+ ['form_id' => 'HR3701F', 'name' => 'Public'],
+ ['form_id' => 'HR3702F', 'name' => 'Public'],
+ ['form_id' => 'HR3703F', 'name' => 'Public'],
+ ['form_id' => 'HR3710F', 'name' => 'Public'],
+ ['form_id' => 'CF1707', 'name' => 'Public'],
+ ['form_id' => 'CF2602', 'name' => 'Public'],
+ ['form_id' => 'CF4139', 'name' => 'Public'],
+ ['form_id' => 'CF4141', 'name' => 'Public'],
+ ['form_id' => 'vsa433', 'name' => 'Public'],
+ ['form_id' => 'CF0700', 'name' => 'Public'],
+ ['form_id' => 'CF0702', 'name' => 'Public'],
+ ['form_id' => 'CF1234', 'name' => 'Public'],
+ ['form_id' => 'CF1706', 'name' => 'Public'],
+ ['form_id' => 'Bus Pass Replacement Application', 'name' => 'Public'],
+ ['form_id' => 'HR0101', 'name' => 'Public'],
+ ['form_id' => 'HR3235', 'name' => 'Public'],
+ ['form_id' => 'HR2724', 'name' => 'Public'],
+ ['form_id' => 'CF2595', 'name' => 'Public'],
+ ['form_id' => 'CF2900', 'name' => 'Public'],
+ ['form_id' => 'CFL01020', 'name' => 'Public'],
+ ['form_id' => 'CFL00517', 'name' => 'Public'],
+ ['form_id' => 'CFL00519', 'name' => 'Public'],
+ ['form_id' => 'CFL00520', 'name' => 'Public'],
+ ['form_id' => 'CFL00522', 'name' => 'Public'],
+ ['form_id' => 'CFL00523', 'name' => 'Public'],
+ ['form_id' => 'CFL00524', 'name' => 'Public'],
+ ['form_id' => 'CFL00541', 'name' => 'Public'],
+ ['form_id' => 'CFL00542', 'name' => 'Public'],
+ ['form_id' => 'CFL00550', 'name' => 'Public'],
+ ['form_id' => 'CFL01022', 'name' => 'Public'],
+ ['form_id' => 'HR3700E', 'name' => 'Public'],
+ ['form_id' => 'HR3704E', 'name' => 'Public'],
+ ['form_id' => 'HR3710E', 'name' => 'Public'],
+ ['form_id' => 'CF0035', 'name' => 'Public'],
+ ['form_id' => 'CF0700A', 'name' => 'Public'],
+ ['form_id' => 'CF0701', 'name' => 'Public'],
+ ['form_id' => 'CF0711A', 'name' => 'Public'],
+ ['form_id' => 'CF0711B', 'name' => 'Public'],
+ ['form_id' => 'CF1630', 'name' => 'Public'],
+ ['form_id' => 'CF2151', 'name' => 'Public'],
+ ['form_id' => 'CF2159', 'name' => 'Public'],
+ ['form_id' => 'CF2164', 'name' => 'Public'],
+ ['form_id' => 'CF2201', 'name' => 'Public'],
+ ['form_id' => 'CF2593', 'name' => 'Public'],
+ ['form_id' => 'CF2615', 'name' => 'Public'],
+ ['form_id' => 'CF2631', 'name' => 'Public'],
+ ['form_id' => 'CF4051', 'name' => 'Public'],
+ ['form_id' => 'CF0906', 'name' => 'Public'],
+ ['form_id' => 'CF2933', 'name' => 'Public'],
+ ['form_id' => 'CFL00516', 'name' => 'Public'],
+ ['form_id' => 'CFL00518', 'name' => 'Public'],
+ ['form_id' => 'CFL00521', 'name' => 'Public'],
+ ['form_id' => 'CFL01040', 'name' => 'Public'],
+ ['form_id' => 'CFL01043', 'name' => 'Public'],
+ ['form_id' => 'CFL01044', 'name' => 'Public'],
+ ['form_id' => 'CFL01045', 'name' => 'Public'],
+ ['form_id' => 'CF0928', 'name' => 'Public'],
+ ['form_id' => 'CF0929', 'name' => 'Public'],
+ ['form_id' => 'CFL00510', 'name' => 'Public'],
+ ['form_id' => 'CFL00511', 'name' => 'Public'],
+ ['form_id' => 'CFL00513', 'name' => 'Public'],
+ ['form_id' => 'CFL00515', 'name' => 'Public'],
+ ['form_id' => 'HR3634', 'name' => 'Public'],
+ ['form_id' => 'HR3637', 'name' => 'Public'],
+ ['form_id' => 'HR3691E', 'name' => 'Public'],
+ ['form_id' => 'HR3692E', 'name' => 'Public'],
+ ['form_id' => 'HR3695AE', 'name' => 'Public'],
+ ['form_id' => 'HR3696E', 'name' => 'Public'],
+ ['form_id' => 'HR3690E', 'name' => 'Public'],
+ ['form_id' => 'HR3690E-AT', 'name' => 'Public'],
+ ['form_id' => 'HR3693E', 'name' => 'Public'],
+ ['form_id' => 'HR3694E', 'name' => 'Public'],
+ ['form_id' => 'HR3695E', 'name' => 'Public'],
+ ['form_id' => 'HR3698E', 'name' => 'Public'],
+ ['form_id' => 'HR3699E', 'name' => 'Public'],
+ ['form_id' => 'HR3703E', 'name' => 'Public'],
+ ['form_id' => 'HR3709E', 'name' => 'Public'],
+ ['form_id' => 'HR0150A', 'name' => 'Public'],
+ ['form_id' => 'HR0150B', 'name' => 'Public'],
+ ['form_id' => 'HR2528', 'name' => 'Public'],
+ ['form_id' => 'HR2949', 'name' => 'Public'],
+ ['form_id' => 'HR3032', 'name' => 'Public'],
+ ['form_id' => 'HR3138', 'name' => 'Public'],
+ ['form_id' => 'HR3241', 'name' => 'Public'],
+ ['form_id' => 'HR3245', 'name' => 'Public'],
+ ['form_id' => 'HR3254', 'name' => 'Public'],
+ ['form_id' => 'HR3344', 'name' => 'Public'],
+ ['form_id' => 'HR3582', 'name' => 'Public'],
+ ['form_id' => 'HR3583', 'name' => 'Public'],
+ ['form_id' => 'HR3584', 'name' => 'Public'],
+ ['form_id' => 'HR3585', 'name' => 'Public'],
+ ['form_id' => 'HR2863', 'name' => 'Public'],
+ ['form_id' => 'HR3162', 'name' => 'Public'],
+ ['form_id' => 'HR3463', 'name' => 'Public'],
+ ['form_id' => 'HR3189', 'name' => 'Public'],
+ ['form_id' => 'HR3238', 'name' => 'Public'],
+ ['form_id' => 'HR2894', 'name' => 'Public'],
+ ['form_id' => 'HR3115', 'name' => 'Public'],
+ ['form_id' => 'HR3320', 'name' => 'Public'],
+ ['form_id' => 'HR2883', 'name' => 'Public'],
+ ['form_id' => 'HR3043', 'name' => 'Public'],
+ ['form_id' => 'HR0081', 'name' => 'Public'],
+ ['form_id' => 'HR3371E', 'name' => 'Public'],
+ ['form_id' => 'HR3375F', 'name' => 'Public'],
+ ['form_id' => 'HR3687F', 'name' => 'Public'],
+ ['form_id' => 'HR3689F', 'name' => 'Public'],
+ ['form_id' => 'HR3690F', 'name' => 'Public'],
+ ['form_id' => 'HR3709F', 'name' => 'Public'],
+ ['form_id' => 'HR3689E-AT', 'name' => 'Public'],
+ ['form_id' => 'CF1702A', 'name' => 'Public'],
+ ['form_id' => 'CF1707A', 'name' => 'Public'],
+ ['form_id' => 'CF1707B', 'name' => 'Public'],
+ ['form_id' => 'CF0040', 'name' => 'Public'],
+ ['form_id' => 'CF0042', 'name' => 'Public'],
+ ['form_id' => 'CF0043', 'name' => 'Public'],
+ ['form_id' => 'CF0044', 'name' => 'Public'],
+ ['form_id' => 'CF0045', 'name' => 'Public'],
+ ['form_id' => 'CF0046', 'name' => 'Public'],
+ ['form_id' => 'CF0047', 'name' => 'Public'],
+ ['form_id' => 'CF0048', 'name' => 'Public'],
+ ['form_id' => 'CF2798', 'name' => 'Public'],
+ ['form_id' => 'HR4025', 'name' => 'Public'],
+ ];
+
+ foreach ($userTypeUpdates as $update) {
+ $form = Form::where('form_id', $update['form_id'])->first();
+ $userType = UserType::where('name', $update['name'])->first();
+
+ if ($form && $userType) {
+ DB::table('form_user_type')->insertOrIgnore([
+ 'form_id' => $form->id,
+ 'user_type_id' => $userType->id,
+ ]);
+ } else {
+ if (!$form) {
+ Log::warning("BulkMetadataUpdateSeeder mismatch: Could not find Form '{$update['form_id']}'");
+ }
+ if (!$userType) {
+ Log::warning("BulkMetadataUpdateSeeder mismatch: Could not find User Type '{$update['name']}'");
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/database/seeders/FormLocationSeeder.php b/database/seeders/FormLocationSeeder.php
index cc027444..7948a8ee 100644
--- a/database/seeders/FormLocationSeeder.php
+++ b/database/seeders/FormLocationSeeder.php
@@ -15,17 +15,25 @@ class FormLocationSeeder extends Seeder
public function run(): void
{
$locations = [
- 'ICM Target',
- 'ICM Dev',
- 'ICM Test',
- 'iConnect',
- 'Resource Finder',
- 'MySelfServe',
- 'Loop',
+ ['name' => 'ICM', 'description' => ''],
+ ['name' => 'iConnect', 'description' => 'MCFD Intranet'],
+ ['name' => 'Resource Finder', 'description' => 'SDPR document store and standard operating procedures'],
+ ['name' => 'MySS', 'description' => 'Form exists in MySelfServe portal as a template'],
+ ['name' => 'Loop', 'description' => 'SDPR Intranet'],
+ ['name' => 'BC Gov Web', 'description' => 'Hosted on the BC Government Website'],
+ ['name' => 'PPM', 'description' => 'Policy and Procedures Manual'],
+ ['name' => 'ConECCt', 'description' => 'ECC (Education and Childcare) Intranet'],
+ ['name' => 'FormFoundry', 'description' => ''],
+ ['name' => 'MyFS', 'description' => 'My Family Services Portal, used by MCFD Autism Services and ECC Child Care Services'],
+ ['name' => 'OES', 'description' => 'Online Employment Services portal'],
+ ['name' => 'WorkBC Extranet', 'description' => ''],
+ ['name' => 'Caregiver Portal', 'description' => ''],
+ ['name' => 'Unknown', 'description' => 'This form cannot be located'],
+ ['name' => 'CHEFS', 'description' => ''],
];
- foreach ($locations as $name) {
- FormLocation::firstOrCreate(['name' => $name]);
+ foreach ($locations as $location) {
+ FormLocation::firstOrCreate($location);
}
}
}
diff --git a/database/seeders/FormSeeder.php b/database/seeders/FormSeeder.php
index 99b62e61..1a94536c 100644
--- a/database/seeders/FormSeeder.php
+++ b/database/seeders/FormSeeder.php
@@ -20,6 +20,7 @@ public function run(): void
FormUserTypeTableSeeder::class,
FormWorkbenchPathsTableSeeder::class,
FormBusinessAreaTableSeeder::class,
+ BulkFormMetadataUpdateSeeder::class,
]);
}
}
diff --git a/database/seeders/FormSoftwareSourceSeeder.php b/database/seeders/FormSoftwareSourceSeeder.php
index 257b2396..603e2f4b 100644
--- a/database/seeders/FormSoftwareSourceSeeder.php
+++ b/database/seeders/FormSoftwareSourceSeeder.php
@@ -14,16 +14,21 @@ class FormSoftwareSourceSeeder extends Seeder
public function run(): void
{
$softwareSources = [
- 'Livecycle',
- 'Adobe Acrobat',
- 'Orbeon',
- 'Microsoft Word',
- 'Microsoft PowerPoint',
- 'Klamm',
+ ['name' => 'Adobe Livecycle', 'description' => ''],
+ ['name' => 'Adobe Acrobat', 'description' => ''],
+ ['name' => 'Orbeon', 'description' => ''],
+ ['name' => 'Microsoft Word', 'description' => ''],
+ ['name' => 'Microsoft PowerPoint', 'description' => ''],
+ ['name' => 'Klamm', 'description' => 'Form is built in FormFoundry'],
+ ['name' => 'CHEFS', 'description' => ''],
+ ['name' => 'Microsoft Excel', 'description' => ''],
+ ['name' => 'Infopath', 'description' => ''],
+ ['name' => 'Adobe', 'description' => 'Adobe PDF form'],
+ ['name' => 'Unknown', 'description' => 'We have no way to verify the software source for this document'],
];
- foreach ($softwareSources as $name) {
- FormSoftwareSource::firstOrCreate(['name' => $name]);
+ foreach ($softwareSources as $source) {
+ FormSoftwareSource::firstOrCreate($source);
}
}
}
diff --git a/database/seeders/FormTagSeeder.php b/database/seeders/FormTagSeeder.php
index 03927a1c..213df794 100644
--- a/database/seeders/FormTagSeeder.php
+++ b/database/seeders/FormTagSeeder.php
@@ -13,8 +13,6 @@ class FormTagSeeder extends Seeder
*/
public function run(): void
{
- FormTag::truncate();
-
$tags = [
'BCMailPlus',
'MySS',
@@ -24,10 +22,14 @@ public function run(): void
'JAWS',
'MIS',
'ServiceCanada',
+ 'storeXML',
+ 'Digital Signature',
+ 'migration2025',
+ 'CHEFSCandidate',
];
foreach ($tags as $tag) {
- FormTag::create(['name' => $tag]);
+ FormTag::firstOrCreate(['name' => $tag]);
}
}
}
diff --git a/database/seeders/FormsTableSeeder.php b/database/seeders/FormsTableSeeder.php
index f5cf3b94..5388e9e2 100644
--- a/database/seeders/FormsTableSeeder.php
+++ b/database/seeders/FormsTableSeeder.php
@@ -48955,6 +48955,75 @@ public function run()
'program' => NULL,
'deleted_at' => NULL,
],
+ 124 => [
+ 'id' => 2175,
+ 'form_id' => 'HR3740E',
+ 'form_title' => 'ELMSD Employability Plan (English)',
+ 'ministry_id' => 2,
+ 'form_purpose' => NULL,
+ 'notes' => 'ADO 3860',
+ 'fill_type_id' => NULL,
+ 'decommissioned' => false,
+ 'form_frequency_id' => NULL,
+ 'form_reach_id' => NULL,
+ 'created_at' => NULL,
+ 'updated_at' => NULL,
+ 'print_reason' => NULL,
+ 'retention_needs' => NULL,
+ 'icm_non_interactive' => NULL,
+ 'footer_fragment_path' => NULL,
+ 'dcv_material_number' => NULL,
+ 'orbeon_functions' => NULL,
+ 'icm_generated' => true,
+ 'program' => 'Employment Labour Market Services Division (ELMSD)',
+ 'deleted_at' => NULL,
+ ],
+ 125 => [
+ 'id' => 2176,
+ 'form_id' => 'HR3740F',
+ 'form_title' => 'ELMSD Employability Plan (French)',
+ 'ministry_id' => 2,
+ 'form_purpose' => NULL,
+ 'notes' => 'ADO 3860',
+ 'fill_type_id' => NULL,
+ 'decommissioned' => false,
+ 'form_frequency_id' => NULL,
+ 'form_reach_id' => NULL,
+ 'created_at' => NULL,
+ 'updated_at' => NULL,
+ 'print_reason' => NULL,
+ 'retention_needs' => NULL,
+ 'icm_non_interactive' => NULL,
+ 'footer_fragment_path' => NULL,
+ 'dcv_material_number' => NULL,
+ 'orbeon_functions' => NULL,
+ 'icm_generated' => true,
+ 'program' => 'Employment Labour Market Services Division (ELMSD)',
+ 'deleted_at' => NULL,
+ ],
+ 126 => [
+ 'id' => 2177,
+ 'form_id' => 'HR3741',
+ 'form_title' => 'ELMSD Employability Planning Authorization and Consent Form',
+ 'ministry_id' => 2,
+ 'form_purpose' => NULL,
+ 'notes' => 'Not yet developed. ADO https://dev.azure.com/BC-SDPR/Forms%20Modernization/_workitems/edit/4010',
+ 'fill_type_id' => NULL,
+ 'decommissioned' => false,
+ 'form_frequency_id' => NULL,
+ 'form_reach_id' => NULL,
+ 'created_at' => NULL,
+ 'updated_at' => NULL,
+ 'print_reason' => NULL,
+ 'retention_needs' => NULL,
+ 'icm_non_interactive' => NULL,
+ 'footer_fragment_path' => NULL,
+ 'dcv_material_number' => NULL,
+ 'orbeon_functions' => NULL,
+ 'icm_generated' => true,
+ 'program' => 'Employment Labour Market Services Division (ELMSD)',
+ 'deleted_at' => NULL,
+ ],
]);
// Reset the ID sequence to avoid conflicts with future inserts