Skip to content

Commit 3eafe29

Browse files
committed
feat: enhance import functionality with improved column matching and type inference
1 parent 8bb8059 commit 3eafe29

File tree

25 files changed

+690
-235
lines changed

25 files changed

+690
-235
lines changed

app-modules/ImportWizard/resources/views/livewire/partials/step-map.blade.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class="space-y-6"
1919
@php
2020
$mappedField = array_search($header, $columnMap);
2121
$selectedValue = $mappedField !== false ? $mappedField : '';
22+
$isInferred = isset($this->inferredMappings[$header]);
23+
$inferenceInfo = $isInferred ? $this->inferredMappings[$header] : null;
2224
@endphp
2325
<div
2426
wire:key="map-{{ md5($header) }}"
@@ -27,7 +29,17 @@ class="flex items-center py-2 px-2 -mx-2 rounded-lg transition-colors"
2729
@mouseenter="hoveredColumn = '{{ addslashes($header) }}'"
2830
>
2931
{{-- CSV Column Name --}}
30-
<div class="flex-1 text-sm text-gray-950 dark:text-white">{{ $header }}</div>
32+
<div class="flex-1 min-w-0">
33+
<div class="text-sm text-gray-950 dark:text-white truncate">{{ $header }}</div>
34+
@if ($isInferred)
35+
<div class="flex items-center gap-1 mt-0.5">
36+
<span class="inline-flex items-center gap-1 px-1.5 py-0.5 text-xs rounded-md bg-info-100 text-info-700 dark:bg-info-900 dark:text-info-300">
37+
<x-filament::icon icon="heroicon-m-sparkles" class="h-3 w-3" />
38+
Suggested ({{ number_format($inferenceInfo['confidence'] * 100) }}%)
39+
</span>
40+
</div>
41+
@endif
42+
</div>
3143

3244
{{-- Arrow --}}
3345
<div class="w-6 flex justify-center">

app-modules/ImportWizard/resources/views/livewire/partials/step-preview.blade.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
'sessionId' => $sessionId,
99
'entityType' => $entityType,
1010
'columnMap' => $columnMap,
11+
'fieldLabels' => $this->fieldLabels,
1112
'previewRows' => $previewRows,
1213
'totalRows' => $totalRows,
1314
], key('preview-table-' . $sessionId))

app-modules/ImportWizard/resources/views/livewire/partials/step-review.blade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
</span>
2828
@endif
2929
</div>
30-
<div class="text-xs text-gray-500 dark:text-gray-400">{{ $analysis->mappedToField }}</div>
30+
<div class="text-xs text-gray-500 dark:text-gray-400">{{ $this->getFieldLabel($analysis->mappedToField) }}</div>
3131
</button>
3232
@endforeach
3333
</div>

app-modules/ImportWizard/src/Filament/Imports/CompanyImporter.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,13 @@ public static function getColumns(): array
2828
ImportColumn::make('name')
2929
->label('Name')
3030
->requiredMapping()
31-
->guess(['name', 'company_name', 'company', 'organization', 'account', 'account_name'])
31+
->guess([
32+
'name', 'company_name', 'company', 'organization', 'account', 'account_name',
33+
'company name', 'associated company', 'company domain name',
34+
'account name', 'parent account', 'billing name',
35+
'business', 'business_name', 'org', 'org_name', 'organisation',
36+
'firm', 'client', 'customer', 'customer_name', 'vendor', 'vendor_name',
37+
])
3238
->rules(['required', 'string', 'max:255'])
3339
->example('Acme Corporation')
3440
->fillRecordUsing(function (Company $record, string $state, CompanyImporter $importer): void {
@@ -38,7 +44,11 @@ public static function getColumns(): array
3844

3945
ImportColumn::make('account_owner_email')
4046
->label('Account Owner Email')
41-
->guess(['account_owner', 'owner_email', 'owner', 'assigned_to', 'account_manager'])
47+
->guess([
48+
'account_owner', 'owner_email', 'owner', 'assigned_to', 'account_manager',
49+
'owner email', 'sales rep', 'sales_rep', 'rep', 'salesperson', 'sales_owner',
50+
'account_rep', 'assigned_user', 'manager_email', 'contact_owner',
51+
])
4252
->rules(['nullable', 'email'])
4353
->example('[email protected]')
4454
->fillRecordUsing(function (Company $record, ?string $state, Importer $importer): void {

app-modules/ImportWizard/src/Filament/Imports/Concerns/HasPolymorphicEntityAttachment.php

Lines changed: 0 additions & 52 deletions
This file was deleted.

app-modules/ImportWizard/src/Filament/Imports/NoteImporter.php

Lines changed: 5 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@
77
use App\Models\Note;
88
use Filament\Actions\Imports\ImportColumn;
99
use Relaticle\CustomFields\Facades\CustomFields;
10-
use Relaticle\ImportWizard\Filament\Imports\Concerns\HasPolymorphicEntityAttachment;
1110

1211
final class NoteImporter extends BaseImporter
1312
{
14-
use HasPolymorphicEntityAttachment;
15-
1613
protected static ?string $model = Note::class;
1714

1815
protected static bool $skipUniqueIdentifierWarning = true;
@@ -25,41 +22,18 @@ public static function getColumns(): array
2522
ImportColumn::make('title')
2623
->label('Title')
2724
->requiredMapping()
28-
->guess(['title', 'note_title', 'subject', 'name', 'heading'])
25+
->guess([
26+
'title', 'note_title', 'subject', 'name', 'heading',
27+
'note', 'summary', 'description', 'content_title',
28+
'note name', 'note subject', 'topic',
29+
])
2930
->rules(['required', 'string', 'max:255'])
3031
->example('Meeting Notes - Q1 Review')
3132
->fillRecordUsing(function (Note $record, string $state, NoteImporter $importer): void {
3233
$record->title = trim($state);
3334
$importer->initializeNewRecord($record);
3435
}),
3536

36-
ImportColumn::make('company_name')
37-
->label('Company Name')
38-
->guess(['company_name', 'company', 'organization', 'account', 'related_company'])
39-
->rules(['nullable', 'string', 'max:255'])
40-
->example('Acme Corporation')
41-
->fillRecordUsing(function (): void {
42-
// Relationship attached in afterSave()
43-
}),
44-
45-
ImportColumn::make('person_name')
46-
->label('Person Name')
47-
->guess(['person_name', 'contact_name', 'contact', 'person', 'related_contact'])
48-
->rules(['nullable', 'string', 'max:255'])
49-
->example('John Doe')
50-
->fillRecordUsing(function (): void {
51-
// Relationship attached in afterSave()
52-
}),
53-
54-
ImportColumn::make('opportunity_name')
55-
->label('Opportunity Name')
56-
->guess(['opportunity_name', 'opportunity', 'deal', 'deal_name', 'related_deal'])
57-
->rules(['nullable', 'string', 'max:255'])
58-
->example('Enterprise License Deal')
59-
->fillRecordUsing(function (): void {
60-
// Relationship attached in afterSave()
61-
}),
62-
6337
...CustomFields::importer()->forModel(self::getModel())->columns(),
6438
];
6539
}
@@ -78,16 +52,6 @@ public function resolveRecord(): Note
7852
return new Note;
7953
}
8054

81-
/**
82-
* Attach polymorphic relationships after the note is saved.
83-
*/
84-
protected function afterSave(): void
85-
{
86-
parent::afterSave();
87-
88-
$this->attachRelatedEntities();
89-
}
90-
9155
public static function getEntityName(): string
9256
{
9357
return 'note';

app-modules/ImportWizard/src/Filament/Imports/OpportunityImporter.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ public static function getColumns(): array
2828
ImportColumn::make('name')
2929
->label('Name')
3030
->requiredMapping()
31-
->guess(['name', 'opportunity_name', 'title'])
31+
->guess([
32+
'name', 'opportunity_name', 'title',
33+
'deal name', 'deal_name', 'deal',
34+
'opportunity name', 'opp_name', 'opp name',
35+
'project', 'project_name', 'sale', 'sale_name', 'prospect',
36+
])
3237
->rules(['required', 'string', 'max:255'])
3338
->example('Q1 Sales Opportunity')
3439
->fillRecordUsing(function (Opportunity $record, string $state, OpportunityImporter $importer): void {
@@ -61,7 +66,11 @@ public static function getColumns(): array
6166

6267
ImportColumn::make('company_name')
6368
->label('Company Name')
64-
->guess(['company_name', 'company', 'account'])
69+
->guess([
70+
'company_name', 'company', 'account',
71+
'company name', 'account_name', 'account name', 'organization',
72+
'associated company', 'business', 'client', 'customer',
73+
])
6574
->rules(['nullable', 'string', 'max:255'])
6675
->example('Acme Corporation')
6776
->fillRecordUsing(function (Opportunity $record, ?string $state, Importer $importer): void {
@@ -96,7 +105,11 @@ public static function getColumns(): array
96105

97106
ImportColumn::make('contact_name')
98107
->label('Contact Name')
99-
->guess(['contact_name', 'contact', 'person'])
108+
->guess([
109+
'contact_name', 'contact', 'person',
110+
'contact name', 'primary contact', 'main contact', 'lead',
111+
'prospect', 'decision maker', 'buyer',
112+
])
100113
->rules(['nullable', 'string', 'max:255'])
101114
->example('John Doe')
102115
->fillRecordUsing(function (Opportunity $record, ?string $state, Importer $importer): void {

app-modules/ImportWizard/src/Filament/Imports/PeopleImporter.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ public static function getColumns(): array
3030
ImportColumn::make('name')
3131
->label('Name')
3232
->requiredMapping()
33-
->guess(['name', 'full_name', 'person_name'])
33+
->guess([
34+
'name', 'full_name', 'person_name',
35+
'contact', 'contact_name', 'person', 'individual', 'member', 'employee',
36+
'full name', 'display_name', 'displayname',
37+
'contact name', 'lead name', 'prospect name',
38+
])
3439
->rules(['required', 'string', 'max:255'])
3540
->example('John Doe')
3641
->fillRecordUsing(function (People $record, string $state, PeopleImporter $importer): void {
@@ -63,7 +68,11 @@ public static function getColumns(): array
6368

6469
ImportColumn::make('company_name')
6570
->label('Company Name')
66-
->guess(['company_name', 'Company'])
71+
->guess([
72+
'company_name', 'Company',
73+
'company', 'employer', 'organization', 'organisation', 'works_at',
74+
'associated company', 'account', 'account_name', 'business',
75+
])
6776
->rules(['nullable', 'string', 'max:255'])
6877
->example('Acme Corporation')
6978
->fillRecordUsing(function (People $record, ?string $state, PeopleImporter $importer): void {

app-modules/ImportWizard/src/Filament/Imports/TaskImporter.php

Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@
77
use App\Models\Task;
88
use Filament\Actions\Imports\ImportColumn;
99
use Relaticle\CustomFields\Facades\CustomFields;
10-
use Relaticle\ImportWizard\Filament\Imports\Concerns\HasPolymorphicEntityAttachment;
1110

1211
final class TaskImporter extends BaseImporter
1312
{
14-
use HasPolymorphicEntityAttachment;
15-
1613
protected static ?string $model = Task::class;
1714

1815
protected static array $uniqueIdentifierColumns = ['id'];
@@ -32,44 +29,25 @@ public static function getColumns(): array
3229
ImportColumn::make('title')
3330
->label('Title')
3431
->requiredMapping()
35-
->guess(['title', 'task_title', 'task_name', 'name', 'subject'])
32+
->guess([
33+
'title', 'task_title', 'task_name', 'name', 'subject',
34+
'task', 'action', 'action_item', 'to_do', 'todo', 'activity',
35+
'task subject', 'task description', 'summary', 'description',
36+
])
3637
->rules(['required', 'string', 'max:255'])
3738
->example('Follow up with client')
3839
->fillRecordUsing(function (Task $record, string $state, TaskImporter $importer): void {
3940
$record->title = trim($state);
4041
$importer->initializeNewRecord($record);
4142
}),
4243

43-
ImportColumn::make('company_name')
44-
->label('Company Name')
45-
->guess(['company_name', 'company', 'organization', 'account'])
46-
->rules(['nullable', 'string', 'max:255'])
47-
->example('Acme Corporation')
48-
->fillRecordUsing(function (): void {
49-
// Relationship attached in afterSave()
50-
}),
51-
52-
ImportColumn::make('person_name')
53-
->label('Person Name')
54-
->guess(['person_name', 'contact_name', 'contact', 'person', 'related_to'])
55-
->rules(['nullable', 'string', 'max:255'])
56-
->example('John Doe')
57-
->fillRecordUsing(function (): void {
58-
// Relationship attached in afterSave()
59-
}),
60-
61-
ImportColumn::make('opportunity_name')
62-
->label('Opportunity Name')
63-
->guess(['opportunity_name', 'opportunity', 'deal', 'deal_name'])
64-
->rules(['nullable', 'string', 'max:255'])
65-
->example('Enterprise License Deal')
66-
->fillRecordUsing(function (): void {
67-
// Relationship attached in afterSave()
68-
}),
69-
7044
ImportColumn::make('assignee_email')
7145
->label('Assignee Email')
72-
->guess(['assignee_email', 'assignee', 'assigned_to', 'owner', 'responsible'])
46+
->guess([
47+
'assignee_email', 'assignee', 'assigned_to', 'owner', 'responsible',
48+
'assignee email', 'assigned to', 'task owner', 'task assignee',
49+
'owner_email', 'owner email', 'rep', 'representative',
50+
])
7351
->rules(['nullable', 'email'])
7452
->example('[email protected]')
7553
->fillRecordUsing(function (Task $record, ?string $state, TaskImporter $importer): void {
@@ -104,15 +82,12 @@ public function resolveRecord(): Task
10482
}
10583

10684
/**
107-
* Attach polymorphic relationships and assignees after the task is saved.
85+
* Attach assignees after the task is saved.
10886
*/
10987
protected function afterSave(): void
11088
{
11189
parent::afterSave();
11290

113-
$this->attachRelatedEntities();
114-
115-
// Attach assignee (Task-specific)
11691
if ($this->pendingAssigneeId !== null) {
11792
/** @var Task $task */
11893
$task = $this->record;

0 commit comments

Comments
 (0)