Google rubrica import preview and command unification
This commit is contained in:
parent
478c0a120b
commit
dba907a85f
|
|
@ -15,6 +15,7 @@
|
||||||
use Filament\Widgets\Widget;
|
use Filament\Widgets\Widget;
|
||||||
use Illuminate\Support\Arr;
|
use Illuminate\Support\Arr;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
|
||||||
|
|
@ -46,6 +47,12 @@ class GoogleWorkspaceOverview extends Widget
|
||||||
|
|
||||||
public bool $showTechnicalBoxes = true;
|
public bool $showTechnicalBoxes = true;
|
||||||
|
|
||||||
|
public ?string $rubricaSyncCommandPreview = null;
|
||||||
|
|
||||||
|
public ?string $rubricaSyncLastOutput = null;
|
||||||
|
|
||||||
|
public ?string $rubricaSyncLastRunLabel = null;
|
||||||
|
|
||||||
public function mount(): void
|
public function mount(): void
|
||||||
{
|
{
|
||||||
$amministratore = $this->resolveAmministratore();
|
$amministratore = $this->resolveAmministratore();
|
||||||
|
|
@ -62,6 +69,7 @@ public function mount(): void
|
||||||
$email = (string) Arr::get($oauth, 'email', '');
|
$email = (string) Arr::get($oauth, 'email', '');
|
||||||
$this->connectionLabel = $email !== '' ? $email : ($this->googleConnected ? 'Collegato' : 'Non collegato');
|
$this->connectionLabel = $email !== '' ? $email : ($this->googleConnected ? 'Collegato' : 'Non collegato');
|
||||||
$this->calendarId = (string) Arr::get($google, 'calendar_id', 'primary') ?: 'primary';
|
$this->calendarId = (string) Arr::get($google, 'calendar_id', 'primary') ?: 'primary';
|
||||||
|
$this->rubricaSyncCommandPreview = 'php artisan google:sync-rubrica --admin-id=' . (int) $amministratore->id . ' --max-pages=20';
|
||||||
|
|
||||||
if (! $this->googleConnected) {
|
if (! $this->googleConnected) {
|
||||||
$this->errorMessage = 'Google non e collegato. Vai su Scheda amministratore per completare il collegamento.';
|
$this->errorMessage = 'Google non e collegato. Vai su Scheda amministratore per completare il collegamento.';
|
||||||
|
|
@ -99,7 +107,17 @@ public function getTicketGestioneUrl(int $ticketId): string
|
||||||
return TicketGestione::getUrl(['ticket' => $ticketId, 'tab' => 'scheda'], panel: 'admin-filament');
|
return TicketGestione::getUrl(['ticket' => $ticketId, 'tab' => 'scheda'], panel: 'admin-filament');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function previewImportContactsToRubrica(): void
|
||||||
|
{
|
||||||
|
$this->runRubricaSync(true);
|
||||||
|
}
|
||||||
|
|
||||||
public function importContactsToRubrica(): void
|
public function importContactsToRubrica(): void
|
||||||
|
{
|
||||||
|
$this->runRubricaSync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function runRubricaSync(bool $dryRun): void
|
||||||
{
|
{
|
||||||
$amministratore = $this->resolveAmministratore();
|
$amministratore = $this->resolveAmministratore();
|
||||||
|
|
||||||
|
|
@ -118,154 +136,39 @@ public function importContactsToRubrica(): void
|
||||||
|
|
||||||
if ($token === null) {
|
if ($token === null) {
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title('Import rubrica non riuscito')
|
->title($dryRun ? 'Preview rubrica non disponibile' : 'Import rubrica non riuscito')
|
||||||
->warning()
|
->warning()
|
||||||
->body($this->errorMessage ?? 'Token Google non disponibile.')
|
->body($this->errorMessage ?? 'Token Google non disponibile.')
|
||||||
->send();
|
->send();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$nextPageToken = null;
|
$params = [
|
||||||
$created = 0;
|
'--admin-id' => [(string) $amministratore->id],
|
||||||
$updated = 0;
|
'--max-pages' => 20,
|
||||||
|
];
|
||||||
|
|
||||||
// Import limitato a 5 pagine per evitare timeout dashboard.
|
if ($dryRun) {
|
||||||
for ($page = 0; $page < 5; $page++) {
|
$params['--dry-run'] = true;
|
||||||
$response = Http::withToken($token)
|
|
||||||
->acceptJson()
|
|
||||||
->get('https://people.googleapis.com/v1/people/me/connections', [
|
|
||||||
'personFields' => 'names,emailAddresses,phoneNumbers',
|
|
||||||
'pageSize' => 200,
|
|
||||||
'pageToken' => $nextPageToken,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($response->status() === 401) {
|
|
||||||
$token = $this->resolveAccessToken($amministratore, $google, $oauth, true) ?? $token;
|
|
||||||
$response = Http::withToken($token)
|
|
||||||
->acceptJson()
|
|
||||||
->get('https://people.googleapis.com/v1/people/me/connections', [
|
|
||||||
'personFields' => 'names,emailAddresses,phoneNumbers',
|
|
||||||
'pageSize' => 200,
|
|
||||||
'pageToken' => $nextPageToken,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! $response->successful()) {
|
|
||||||
Notification::make()
|
|
||||||
->title('Import rubrica interrotto')
|
|
||||||
->warning()
|
|
||||||
->body($this->googleErrorBody($response, 'Google People API ha restituito un errore durante l import.'))
|
|
||||||
->send();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$connections = $response->json('connections');
|
|
||||||
if (! is_array($connections) || count($connections) === 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($connections as $person) {
|
|
||||||
$name = trim((string) Arr::get($person, 'names.0.displayName', ''));
|
|
||||||
$email = trim((string) Arr::get($person, 'emailAddresses.0.value', ''));
|
|
||||||
$phone = trim((string) Arr::get($person, 'phoneNumbers.0.value', ''));
|
|
||||||
|
|
||||||
if ($name === '' && $email === '' && $phone === '') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[$nome, $cognome] = $this->splitDisplayName($name, $email);
|
|
||||||
$categoria = $this->inferCategoria($name, $email);
|
|
||||||
$tipoContatto = $this->inferTipoContatto($name, $email);
|
|
||||||
$today = now()->toDateString();
|
|
||||||
|
|
||||||
$existing = RubricaUniversale::withTrashed()
|
|
||||||
->where(function ($q) use ($email, $phone): void {
|
|
||||||
if ($email !== '') {
|
|
||||||
$q->orWhere('email', $email);
|
|
||||||
}
|
|
||||||
if ($phone !== '') {
|
|
||||||
$q->orWhere('telefono_cellulare', $phone)
|
|
||||||
->orWhere('telefono_ufficio', $phone)
|
|
||||||
->orWhere('telefono_casa', $phone);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if ($existing instanceof RubricaUniversale) {
|
|
||||||
if (method_exists($existing, 'trashed') && $existing->trashed()) {
|
|
||||||
$existing->restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
$changed = false;
|
|
||||||
if (($existing->nome ?? '') === '' && $nome !== '') {
|
|
||||||
$existing->nome = $nome;
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
if (($existing->cognome ?? '') === '' && $cognome !== '') {
|
|
||||||
$existing->cognome = $cognome;
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
if (($existing->email ?? '') === '' && $email !== '') {
|
|
||||||
$existing->email = $email;
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
if (($existing->telefono_cellulare ?? '') === '' && $phone !== '') {
|
|
||||||
$existing->telefono_cellulare = $phone;
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
if (($existing->categoria ?? '') === '' || $existing->categoria === 'altro') {
|
|
||||||
$existing->categoria = $categoria;
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
if (($existing->tipo_contatto ?? '') === '') {
|
|
||||||
$existing->tipo_contatto = $tipoContatto;
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
if (($existing->note ?? '') === '') {
|
|
||||||
$existing->note = 'Origine contatto: Google Workspace';
|
|
||||||
$changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($changed) {
|
|
||||||
$existing->data_ultima_modifica = $today;
|
|
||||||
$existing->modificato_da = Auth::id();
|
|
||||||
$existing->save();
|
|
||||||
$updated++;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
RubricaUniversale::create([
|
|
||||||
'nome' => $nome,
|
|
||||||
'cognome' => $cognome,
|
|
||||||
'tipo_contatto' => $tipoContatto,
|
|
||||||
'telefono_cellulare' => $phone !== '' ? $phone : null,
|
|
||||||
'email' => $email !== '' ? $email : null,
|
|
||||||
'categoria' => $categoria,
|
|
||||||
'stato' => 'attivo',
|
|
||||||
'note' => 'Origine contatto: Google Workspace',
|
|
||||||
'data_inserimento' => $today,
|
|
||||||
'data_ultima_modifica' => $today,
|
|
||||||
'creato_da' => Auth::id(),
|
|
||||||
'modificato_da' => Auth::id(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$created++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$nextPageToken = $response->json('nextPageToken');
|
|
||||||
if (! is_string($nextPageToken) || $nextPageToken === '') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->loadContactsPreview($token);
|
$exit = Artisan::call('google:sync-rubrica', $params);
|
||||||
|
$output = trim((string) Artisan::output());
|
||||||
|
|
||||||
|
$this->rubricaSyncLastRunLabel = now()->format('d/m/Y H:i:s') . ($dryRun ? ' · dry-run' : ' · apply');
|
||||||
|
$this->rubricaSyncLastOutput = $output !== '' ? $output : 'Nessun output disponibile.';
|
||||||
|
$this->rubricaSyncCommandPreview = 'php artisan google:sync-rubrica --admin-id=' . (int) $amministratore->id . ' --max-pages=20' . ($dryRun ? ' --dry-run' : '');
|
||||||
|
|
||||||
|
if (! $dryRun) {
|
||||||
|
$this->loadContactsPreview($token);
|
||||||
|
}
|
||||||
|
|
||||||
Notification::make()
|
Notification::make()
|
||||||
->title('Import rubrica completato')
|
->title($exit === 0
|
||||||
->success()
|
? ($dryRun ? 'Preview import rubrica completata' : 'Import rubrica completato')
|
||||||
->body("Creati {$created} contatti, aggiornati {$updated} contatti.")
|
: ($dryRun ? 'Preview import rubrica con errori' : 'Import rubrica con errori'))
|
||||||
|
->body((string) collect(preg_split('/\R/', $this->rubricaSyncLastOutput) ?: [])->filter()->last())
|
||||||
|
->{$exit === 0 ? 'success' : 'warning'}()
|
||||||
->send();
|
->send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,9 @@
|
||||||
<a href="{{ $this->getRubricaUrl() }}" class="inline-flex items-center rounded-md bg-gray-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-gray-600">
|
<a href="{{ $this->getRubricaUrl() }}" class="inline-flex items-center rounded-md bg-gray-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-gray-600">
|
||||||
Apri rubrica interna
|
Apri rubrica interna
|
||||||
</a>
|
</a>
|
||||||
|
<button type="button" wire:click="previewImportContactsToRubrica" class="inline-flex items-center rounded-md bg-slate-700 px-3 py-1.5 text-xs font-medium text-white hover:bg-slate-600">
|
||||||
|
Preview import rubrica
|
||||||
|
</button>
|
||||||
<button type="button" wire:click="importContactsToRubrica" class="inline-flex items-center rounded-md bg-emerald-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-emerald-500">
|
<button type="button" wire:click="importContactsToRubrica" class="inline-flex items-center rounded-md bg-emerald-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-emerald-500">
|
||||||
Importa in Rubrica NetGescon
|
Importa in Rubrica NetGescon
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -95,6 +98,23 @@
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if(($showTechnicalBoxes ?? true) === true)
|
||||||
|
<div class="rounded-lg border border-gray-200 p-3">
|
||||||
|
<div class="mb-2 text-sm font-semibold text-gray-800">Import rubrica operativo</div>
|
||||||
|
<div class="text-xs text-gray-500">Usa `Preview` per controllare cosa entrerebbe in rubrica, poi `Importa` per applicare davvero usando il comando unificato lato Laravel.</div>
|
||||||
|
<pre class="mt-3 overflow-x-auto rounded-lg bg-slate-900 p-3 text-xs text-slate-100">{{ $rubricaSyncCommandPreview ?: 'php artisan google:sync-rubrica --admin-id=... --max-pages=20 --dry-run' }}</pre>
|
||||||
|
|
||||||
|
@if($rubricaSyncLastRunLabel)
|
||||||
|
<div class="mt-3 text-[11px] font-semibold uppercase tracking-wide text-slate-500">Ultima esecuzione</div>
|
||||||
|
<div class="mt-1 text-xs text-slate-700">{{ $rubricaSyncLastRunLabel }}</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($rubricaSyncLastOutput)
|
||||||
|
<pre class="mt-3 max-h-72 overflow-auto rounded-lg bg-slate-50 p-3 text-xs text-slate-700">{{ $rubricaSyncLastOutput }}</pre>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
<div class="rounded-lg border border-gray-200 p-3">
|
<div class="rounded-lg border border-gray-200 p-3">
|
||||||
<div class="mb-2 text-sm font-semibold text-gray-800">Ticket aperti (operativi)</div>
|
<div class="mb-2 text-sm font-semibold text-gray-800">Ticket aperti (operativi)</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user