Add guided imported history wizard

This commit is contained in:
Codex
2026-07-10 17:33:27 -05:00
parent 24f73084fa
commit 3b856b5e46
12 changed files with 371 additions and 29 deletions
+6
View File
@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Models\Attachment;
use App\Models\Dispute;
use App\Models\ExternalProfileImport;
use App\Models\JobPost;
use App\Models\Review;
use App\Models\User;
@@ -34,6 +35,11 @@ class AdminController extends Controller
->latest('reported_at')
->limit(10)
->get(),
'importedHistories' => ExternalProfileImport::with('providerProfile.user', 'attachments')
->whereIn('verification_status', ['provider_attested', 'needs_more_proof'])
->latest()
->limit(10)
->get(),
]);
}
}
@@ -2,6 +2,7 @@
namespace App\Http\Controllers;
use App\Models\ExternalProfileImport;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@@ -17,17 +18,72 @@ class ExternalProfileImportController extends Controller
'platform' => ['required', 'string', 'max:255'],
'external_id' => ['nullable', 'string', 'max:255'],
'profile_url' => ['nullable', 'url', 'max:255'],
'visibility' => ['nullable', 'string', 'in:'.implode(',', array_keys(ExternalProfileImport::VISIBILITIES))],
'rating' => ['nullable', 'numeric', 'min:0', 'max:5'],
'review_count' => ['nullable', 'integer', 'min:0'],
'completed_jobs' => ['nullable', 'integer', 'min:0'],
'client_count' => ['nullable', 'integer', 'min:0'],
'on_time_rate' => ['nullable', 'numeric', 'min:0', 'max:100'],
'backout_rate' => ['nullable', 'numeric', 'min:0', 'max:100'],
'work_categories_text' => ['nullable', 'string'],
'endorsements' => ['nullable', 'array'],
'endorsements.*' => ['nullable', 'string', 'max:255'],
'selected_reviews_text' => ['nullable', 'string'],
'notes' => ['nullable', 'string'],
]);
$profile->externalImports()->create($data + [
$operationalMetrics = array_filter([
'client_count' => $data['client_count'] ?? null,
'on_time_rate' => $data['on_time_rate'] ?? null,
'backout_rate' => $data['backout_rate'] ?? null,
], fn ($value): bool => $value !== null && $value !== '');
$profile->externalImports()->create([
'platform' => $data['platform'],
'external_id' => $data['external_id'] ?? null,
'profile_url' => $data['profile_url'] ?? null,
'status' => 'manual',
'visibility' => $data['visibility'] ?? 'private',
'verification_status' => 'provider_attested',
'rating' => $data['rating'] ?? null,
'review_count' => $data['review_count'] ?? null,
'completed_jobs' => $data['completed_jobs'] ?? null,
'work_categories' => $this->linesToList($data['work_categories_text'] ?? ''),
'endorsements' => array_values(array_filter($data['endorsements'] ?? [])),
'operational_metrics' => $operationalMetrics,
'metrics' => $operationalMetrics,
'selected_reviews' => $this->linesToList($data['selected_reviews_text'] ?? ''),
'review_snapshots' => $this->linesToList($data['selected_reviews_text'] ?? ''),
'notes' => $data['notes'] ?? null,
'imported_at' => now(),
]);
return redirect()->route('providers.edit')->with('status', 'External profile snapshot saved.');
}
public function verify(Request $request, ExternalProfileImport $import): RedirectResponse
{
abort_unless($request->user()->hasRole('admin'), 403);
$data = $request->validate([
'verification_status' => ['required', 'string', 'in:admin_verified,needs_more_proof,unverified'],
]);
$import->update([
'verification_status' => $data['verification_status'],
'verified_by_id' => $request->user()->id,
'verified_at' => now(),
]);
return back()->with('status', 'Imported history verification updated.');
}
private function linesToList(string $text): array
{
return collect(preg_split('/\r\n|\r|\n/', $text))
->map(fn (string $line): string => trim($line, " \t\n\r\0\x0B-"))
->filter()
->values()
->all();
}
}
+42 -1
View File
@@ -9,16 +9,37 @@ use Illuminate\Database\Eloquent\Model;
#[Fillable([
'provider_profile_id', 'platform', 'external_id', 'profile_url', 'status',
'rating', 'review_count', 'completed_jobs', 'metrics', 'review_snapshots',
'visibility', 'verification_status', 'verified_by_id', 'verified_at',
'rating', 'review_count', 'completed_jobs', 'work_categories', 'endorsements',
'operational_metrics', 'metrics', 'review_snapshots', 'selected_reviews',
'notes', 'imported_at',
])]
class ExternalProfileImport extends Model
{
public const VISIBILITIES = [
'private' => 'Private only',
'summary' => 'Public summary only',
'selected_reviews' => 'Public selected reviews',
'proof_public' => 'Public proof attachments',
];
public const VERIFICATION_STATUSES = [
'unverified' => 'Unverified imported history',
'provider_attested' => 'Provider attested',
'admin_verified' => 'Admin verified',
'needs_more_proof' => 'Needs more proof',
];
protected function casts(): array
{
return [
'work_categories' => 'array',
'endorsements' => 'array',
'operational_metrics' => 'array',
'metrics' => 'array',
'review_snapshots' => 'array',
'selected_reviews' => 'array',
'verified_at' => 'datetime',
'imported_at' => 'datetime',
];
}
@@ -28,8 +49,28 @@ class ExternalProfileImport extends Model
return $this->belongsTo(ProviderProfile::class);
}
public function verifiedBy(): BelongsTo
{
return $this->belongsTo(User::class, 'verified_by_id');
}
public function attachments(): MorphMany
{
return $this->morphMany(Attachment::class, 'attachable');
}
public function publiclyVisible(): bool
{
return $this->visibility !== 'private';
}
public function canShowSelectedReviews(): bool
{
return in_array($this->visibility, ['selected_reviews', 'proof_public'], true);
}
public function canShowProofAttachments(): bool
{
return $this->visibility === 'proof_public';
}
}