Fix ticket attachment visibility and unify anagrafica lookup UX
This commit is contained in:
parent
972d2c7e83
commit
ab074eb321
|
|
@ -25,6 +25,7 @@
|
|||
use App\Services\Consumi\ConsumiAcquaTariffeIngestionService;
|
||||
use App\Services\Documenti\PdfTextExtractionService;
|
||||
use App\Services\FattureElettroniche\FatturaElettronicaProtocolloService;
|
||||
use App\Support\Livewire\SupportsRubricaLookup;
|
||||
use App\Support\StabileContext;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Forms\Components\Hidden;
|
||||
|
|
@ -43,6 +44,7 @@
|
|||
class FornitoreScheda extends Page
|
||||
{
|
||||
use ResolvesOperatoreContext;
|
||||
use SupportsRubricaLookup;
|
||||
|
||||
protected static ?string $title = 'Scheda fornitore';
|
||||
|
||||
|
|
@ -94,6 +96,13 @@ class FornitoreScheda extends Page
|
|||
|
||||
public string $nuovoDipendenteTelefono = '';
|
||||
|
||||
public string $dipendenteRubricaSearch = '';
|
||||
|
||||
public ?int $dipendenteRubricaId = null;
|
||||
|
||||
/** @var array<int, array<string, mixed>> */
|
||||
public array $dipendenteRubricaMatches = [];
|
||||
|
||||
public ?string $lastGeneratedPassword = null;
|
||||
|
||||
public function cleanDisplayValue(?string $value, string $fallback = '-'): string
|
||||
|
|
@ -227,6 +236,83 @@ public function creaDipendenteFornitore(): void
|
|||
Notification::make()->title('Dipendente aggiunto')->success()->send();
|
||||
}
|
||||
|
||||
public function updatedDipendenteRubricaSearch(): void
|
||||
{
|
||||
$this->searchDipendenteRubricaMatches();
|
||||
}
|
||||
|
||||
public function selezionaDipendenteRubrica(int $rubricaId): void
|
||||
{
|
||||
$rubrica = RubricaUniversale::query()->find($rubricaId);
|
||||
if (! $rubrica instanceof RubricaUniversale) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->dipendenteRubricaId = (int) $rubrica->id;
|
||||
$this->dipendenteRubricaSearch = $this->formatRubricaLookupLabel($rubrica);
|
||||
$this->dipendenteRubricaMatches = [$this->mapRubricaLookupRow($rubrica)];
|
||||
}
|
||||
|
||||
public function resetDipendenteRubricaSelection(): void
|
||||
{
|
||||
$this->dipendenteRubricaId = null;
|
||||
$this->dipendenteRubricaSearch = '';
|
||||
$this->dipendenteRubricaMatches = [];
|
||||
}
|
||||
|
||||
public function collegaDipendenteDaRubrica(): void
|
||||
{
|
||||
$rubricaId = (int) ($this->dipendenteRubricaId ?? 0);
|
||||
if ($rubricaId <= 0) {
|
||||
Notification::make()->title('Seleziona un nominativo rubrica')->warning()->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$rubrica = RubricaUniversale::query()->find($rubricaId);
|
||||
if (! $rubrica instanceof RubricaUniversale) {
|
||||
Notification::make()->title('Nominativo rubrica non trovato')->danger()->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$email = mb_strtolower(trim((string) ($rubrica->email ?? '')));
|
||||
|
||||
$query = FornitoreDipendente::query()
|
||||
->where('fornitore_id', (int) $this->fornitore->id)
|
||||
->where(function ($builder) use ($rubricaId, $email, $rubrica): void {
|
||||
$builder->where('rubrica_id', $rubricaId);
|
||||
|
||||
if ($email !== '') {
|
||||
$builder->orWhereRaw('LOWER(email) = ?', [$email]);
|
||||
}
|
||||
|
||||
$builder->orWhere(function ($sub) use ($rubrica): void {
|
||||
$sub->where('nome', (string) ($rubrica->nome ?? ''))
|
||||
->where('cognome', (string) ($rubrica->cognome ?? ''));
|
||||
});
|
||||
});
|
||||
|
||||
$dipendente = $query->first();
|
||||
if (! $dipendente instanceof FornitoreDipendente) {
|
||||
$dipendente = new FornitoreDipendente();
|
||||
$dipendente->fornitore_id = (int) $this->fornitore->id;
|
||||
$dipendente->created_by_user_id = Auth::id();
|
||||
}
|
||||
|
||||
$dipendente->rubrica_id = $rubricaId;
|
||||
$dipendente->nome = (string) ($rubrica->nome ?: $rubrica->ragione_sociale ?: 'Dipendente');
|
||||
$dipendente->cognome = trim((string) ($rubrica->cognome ?? '')) !== '' ? (string) $rubrica->cognome : null;
|
||||
$dipendente->email = $email !== '' ? $email : null;
|
||||
$dipendente->telefono = trim((string) ($rubrica->telefono_cellulare ?: $rubrica->telefono_ufficio ?: $rubrica->telefono_casa)) ?: null;
|
||||
$dipendente->attivo = true;
|
||||
$dipendente->updated_by_user_id = Auth::id();
|
||||
$dipendente->save();
|
||||
|
||||
$this->resetDipendenteRubricaSelection();
|
||||
$this->refreshDipendentiRows();
|
||||
|
||||
Notification::make()->title('Dipendente collegato dalla rubrica')->success()->send();
|
||||
}
|
||||
|
||||
public function abilitaAccessoDipendente(int $dipendenteId): void
|
||||
{
|
||||
$dipendente = FornitoreDipendente::query()
|
||||
|
|
@ -446,7 +532,7 @@ private function loadTagSuggestions(): void
|
|||
private function refreshDipendentiRows(): void
|
||||
{
|
||||
$this->dipendentiRows = FornitoreDipendente::query()
|
||||
->with('user:id,name,email')
|
||||
->with(['user:id,name,email', 'rubrica:id,nome,cognome,ragione_sociale'])
|
||||
->where('fornitore_id', (int) $this->fornitore->id)
|
||||
->orderByDesc('attivo')
|
||||
->orderBy('nome')
|
||||
|
|
@ -460,6 +546,8 @@ private function refreshDipendentiRows(): void
|
|||
'email' => (string) ($d->email ?? ''),
|
||||
'telefono' => (string) ($d->telefono ?? ''),
|
||||
'attivo' => (bool) $d->attivo,
|
||||
'rubrica_id' => (int) ($d->rubrica_id ?? 0),
|
||||
'rubrica' => $d->rubrica ? $this->formatRubricaLookupLabel($d->rubrica) : '',
|
||||
'user_id' => $userId,
|
||||
'user_label' => $userId > 0 ? ((string) ($d->user?->name ?: ('Utente #' . $userId))): '-',
|
||||
];
|
||||
|
|
@ -467,6 +555,17 @@ private function refreshDipendentiRows(): void
|
|||
->all();
|
||||
}
|
||||
|
||||
private function searchDipendenteRubricaMatches(): void
|
||||
{
|
||||
$this->dipendenteRubricaMatches = $this->searchRubricaLookupMatches(
|
||||
$this->dipendenteRubricaSearch,
|
||||
$this->dipendenteRubricaId,
|
||||
null,
|
||||
12,
|
||||
(int) ($this->fornitore->amministratore_id ?? 0)
|
||||
);
|
||||
}
|
||||
|
||||
private function refreshCatalogRows(): void
|
||||
{
|
||||
$this->catalogoProdottiRows = [];
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ class RubricaUniversaleScheda extends Page
|
|||
|
||||
public bool $isInlineEditing = false;
|
||||
|
||||
public string $sideTab = 'collegamenti';
|
||||
public string $sideTab = 'dati';
|
||||
|
||||
/** @var array<int,array<string,mixed>> */
|
||||
public array $fornitoriCollegati = [];
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@
|
|||
use App\Models\CommunicationMessage;
|
||||
use App\Models\Fornitore;
|
||||
use App\Models\FornitoreDipendente;
|
||||
use App\Models\RubricaUniversale;
|
||||
use App\Models\Stabile;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use App\Support\Livewire\SupportsRubricaLookup;
|
||||
use App\Support\StabileContext;
|
||||
use BackedEnum;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
|
|
@ -36,6 +38,7 @@
|
|||
class SchedaAmministratore extends Page implements HasForms
|
||||
{
|
||||
use InteractsWithForms;
|
||||
use SupportsRubricaLookup;
|
||||
|
||||
protected static ?string $navigationLabel = 'Scheda amministratore';
|
||||
|
||||
|
|
@ -79,6 +82,13 @@ class SchedaAmministratore extends Page implements HasForms
|
|||
/** @var array<int,bool> */
|
||||
public array $collaboratorePbxClickToCallEnabled = [];
|
||||
|
||||
public string $collaboratoreRubricaSearch = '';
|
||||
|
||||
public ?int $collaboratoreRubricaId = null;
|
||||
|
||||
/** @var array<int,array<string,mixed>> */
|
||||
public array $collaboratoreRubricaMatches = [];
|
||||
|
||||
public Amministratore $amministratore;
|
||||
|
||||
public static function canAccess(): bool
|
||||
|
|
@ -1347,6 +1357,81 @@ public function getCollaboratoriCentralinoRowsProperty(): array
|
|||
->all();
|
||||
}
|
||||
|
||||
public function updatedCollaboratoreRubricaSearch(): void
|
||||
{
|
||||
$this->searchCollaboratoreRubricaMatches();
|
||||
}
|
||||
|
||||
public function selezionaCollaboratoreRubrica(int $rubricaId): void
|
||||
{
|
||||
$rubrica = RubricaUniversale::query()->find($rubricaId);
|
||||
if (! $rubrica instanceof RubricaUniversale) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->collaboratoreRubricaId = (int) $rubrica->id;
|
||||
$this->collaboratoreRubricaSearch = $this->formatRubricaLookupLabel($rubrica);
|
||||
$this->collaboratoreRubricaMatches = [$this->mapRubricaLookupRow($rubrica)];
|
||||
}
|
||||
|
||||
public function resetCollaboratoreRubricaSelection(): void
|
||||
{
|
||||
$this->collaboratoreRubricaId = null;
|
||||
$this->collaboratoreRubricaSearch = '';
|
||||
$this->collaboratoreRubricaMatches = [];
|
||||
}
|
||||
|
||||
public function abilitaCollaboratoreDaRubrica(): void
|
||||
{
|
||||
$rubricaId = (int) ($this->collaboratoreRubricaId ?? 0);
|
||||
if ($rubricaId <= 0) {
|
||||
Notification::make()->title('Seleziona un nominativo rubrica')->warning()->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$rubrica = RubricaUniversale::query()->find($rubricaId);
|
||||
if (! $rubrica instanceof RubricaUniversale) {
|
||||
Notification::make()->title('Nominativo rubrica non trovato')->danger()->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$email = mb_strtolower(trim((string) ($rubrica->email ?? '')));
|
||||
if ($email === '') {
|
||||
Notification::make()->title('Email rubrica mancante')->warning()->body('Per creare o collegare il collaboratore serve un indirizzo email principale nella rubrica.')->send();
|
||||
return;
|
||||
}
|
||||
|
||||
$user = User::query()->whereRaw('LOWER(email) = ?', [$email])->first();
|
||||
$created = false;
|
||||
|
||||
if (! $user instanceof User) {
|
||||
$password = $this->generateTemporaryPassword();
|
||||
$user = User::query()->create([
|
||||
'name' => trim((string) ($rubrica->nome_completo ?: $rubrica->ragione_sociale ?: 'Collaboratore Studio')),
|
||||
'email' => $email,
|
||||
'password' => Hash::make($password),
|
||||
'email_verified_at' => now(),
|
||||
'is_active' => true,
|
||||
'registration_status' => 'approved',
|
||||
]);
|
||||
|
||||
$this->lastGeneratedPassword = $password;
|
||||
$created = true;
|
||||
}
|
||||
|
||||
$user->assignRole('collaboratore');
|
||||
$this->syncCollaboratoreRubricaStabili($user);
|
||||
|
||||
$this->collaboratorePbxExtension[(int) $user->id] = (string) ($user->pbx_extension ?? '');
|
||||
$this->searchCollaboratoreRubricaMatches();
|
||||
|
||||
Notification::make()
|
||||
->title('Collaboratore collegato dalla rubrica')
|
||||
->body($created ? 'Utente creato e assegnato agli stabili dell\'amministratore.' : 'Utente esistente aggiornato e assegnato agli stabili dell\'amministratore.')
|
||||
->success()
|
||||
->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{extensions:array<int,string>,groups:array<int,string>,lines:array<int,string>}
|
||||
*/
|
||||
|
|
@ -1733,6 +1818,58 @@ private function normalizePbxMappingInput(string $value): array
|
|||
}, $tokens), fn(string $token): bool => $token !== '')));
|
||||
}
|
||||
|
||||
private function searchCollaboratoreRubricaMatches(): void
|
||||
{
|
||||
$this->collaboratoreRubricaMatches = $this->searchRubricaLookupMatches(
|
||||
$this->collaboratoreRubricaSearch,
|
||||
$this->collaboratoreRubricaId,
|
||||
null,
|
||||
12,
|
||||
(int) $this->amministratore->id
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int,int>
|
||||
*/
|
||||
private function getAmministratoreStabileIds(): array
|
||||
{
|
||||
return $this->amministratore->stabili()
|
||||
->pluck('id')
|
||||
->map(fn($value) => (int) $value)
|
||||
->filter(fn(int $value) => $value > 0)
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
private function syncCollaboratoreRubricaStabili(User $user): void
|
||||
{
|
||||
if (! Schema::hasTable('collaboratore_stabile') || ! method_exists($user, 'stabiliAssegnati')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$adminStabileIds = $this->getAmministratoreStabileIds();
|
||||
if ($adminStabileIds === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$currentIds = $user->stabiliAssegnati()
|
||||
->pluck('stabili.id')
|
||||
->map(fn($value) => (int) $value)
|
||||
->filter(fn(int $value) => $value > 0)
|
||||
->values()
|
||||
->all();
|
||||
|
||||
$outsideAdminIds = Stabile::query()
|
||||
->whereIn('id', $currentIds)
|
||||
->where('amministratore_id', '!=', (int) $this->amministratore->id)
|
||||
->pluck('id')
|
||||
->map(fn($value) => (int) $value)
|
||||
->all();
|
||||
|
||||
$user->stabiliAssegnati()->sync(array_values(array_unique(array_merge($outsideAdminIds, $adminStabileIds))));
|
||||
}
|
||||
|
||||
public function abilitaAccessoFornitore(int $dipendenteId): void
|
||||
{
|
||||
$dipendente = FornitoreDipendente::query()
|
||||
|
|
|
|||
|
|
@ -32,8 +32,14 @@ public function show(TicketAttachment $attachment): BinaryFileResponse
|
|||
if ($user->hasRole('fornitore')) {
|
||||
$this->authorizeSupplierAttachment($user, $attachment);
|
||||
} else {
|
||||
$stabileId = StabileContext::resolveActiveStabileId($user);
|
||||
if (! $stabileId || (int) $ticket->stabile_id !== (int) $stabileId) {
|
||||
$allowedStabili = StabileContext::accessibleStabili($user)
|
||||
->pluck('id')
|
||||
->map(fn($value) => (int) $value)
|
||||
->filter(fn(int $value) => $value > 0)
|
||||
->values()
|
||||
->all();
|
||||
|
||||
if ($allowedStabili === [] || ! in_array((int) $ticket->stabile_id, $allowedStabili, true)) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ protected function mapRubricaLookupRow(RubricaUniversale $rubrica): array
|
|||
'email' => trim((string) ($rubrica->email ?? '')),
|
||||
'phone' => $phone,
|
||||
'category' => (string) ($rubrica->categoria ?? ''),
|
||||
'cf' => trim((string) ($rubrica->codice_fiscale ?? '')),
|
||||
'piva' => trim((string) ($rubrica->partita_iva ?? '')),
|
||||
'note' => Str::limit(trim((string) ($rubrica->note ?? '')), 100),
|
||||
];
|
||||
}
|
||||
|
|
@ -71,10 +73,10 @@ protected function mapRubricaLookupRow(RubricaUniversale $rubrica): array
|
|||
/**
|
||||
* Ricerca libera su rubrica con deduplica label e fallback sulla selezione corrente.
|
||||
*
|
||||
* @param array<int,string> $categories
|
||||
* @param array<int,string>|null $categories
|
||||
* @return array<int,array<string,mixed>>
|
||||
*/
|
||||
protected function searchRubricaLookupMatches(string $raw, ?int $selectedId = null, array $categories = ['assicurazione', 'fornitore', 'altro'], int $limit = 12): array
|
||||
protected function searchRubricaLookupMatches(string $raw, ?int $selectedId = null, ?array $categories = null, int $limit = 12, ?int $amministratoreId = null): array
|
||||
{
|
||||
$raw = $this->normalizeRubricaLookupText($raw);
|
||||
|
||||
|
|
@ -89,46 +91,89 @@ protected function searchRubricaLookupMatches(string $raw, ?int $selectedId = nu
|
|||
|
||||
$needle = '%' . mb_strtolower($raw) . '%';
|
||||
$digits = preg_replace('/\D+/', '', $raw) ?: '';
|
||||
$seen = [];
|
||||
$rows = [];
|
||||
|
||||
$matches = RubricaUniversale::query()
|
||||
$query = RubricaUniversale::query()
|
||||
->whereNull('deleted_at')
|
||||
->whereIn('categoria', $categories)
|
||||
->where(function ($query) use ($needle, $digits): void {
|
||||
$query->whereRaw("LOWER(COALESCE(ragione_sociale, '')) LIKE ?", [$needle])
|
||||
->orWhereRaw("LOWER(COALESCE(nome, '')) LIKE ?", [$needle])
|
||||
->orWhereRaw("LOWER(COALESCE(cognome, '')) LIKE ?", [$needle])
|
||||
->orWhereRaw("LOWER(COALESCE(email, '')) LIKE ?", [$needle])
|
||||
->orWhereRaw("LOWER(COALESCE(note, '')) LIKE ?", [$needle]);
|
||||
->orWhereRaw("LOWER(COALESCE(note, '')) LIKE ?", [$needle])
|
||||
->orWhereRaw("LOWER(COALESCE(codice_fiscale, '')) LIKE ?", [$needle])
|
||||
->orWhereRaw("LOWER(COALESCE(partita_iva, '')) LIKE ?", [$needle]);
|
||||
|
||||
if ($digits !== '') {
|
||||
$query->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_ufficio, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') LIKE ?", ['%' . $digits . '%'])
|
||||
->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_cellulare, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') LIKE ?", ['%' . $digits . '%']);
|
||||
->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_cellulare, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') LIKE ?", ['%' . $digits . '%'])
|
||||
->orWhereRaw("REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(COALESCE(telefono_casa, ''), ' ', ''), '+', ''), '-', ''), '(', ''), ')', '') LIKE ?", ['%' . $digits . '%']);
|
||||
}
|
||||
})
|
||||
->orderByRaw('CASE WHEN id = ? THEN 0 ELSE 1 END', [(int) ($selectedId ?? 0)])
|
||||
->orderBy('ragione_sociale')
|
||||
->orderBy('cognome')
|
||||
->orderBy('nome')
|
||||
->limit(80)
|
||||
->get();
|
||||
->limit(80);
|
||||
|
||||
if ($categories !== null && $categories !== []) {
|
||||
$query->whereIn('categoria', $categories);
|
||||
}
|
||||
|
||||
if ($amministratoreId !== null && $amministratoreId > 0 && method_exists(new RubricaUniversale(), 'getTable')) {
|
||||
$query->where(function ($builder) use ($amministratoreId): void {
|
||||
$builder->where('amministratore_id', $amministratoreId)
|
||||
->orWhereNull('amministratore_id');
|
||||
});
|
||||
}
|
||||
|
||||
$matches = $query->get();
|
||||
|
||||
foreach ($matches as $rubrica) {
|
||||
$row = $this->mapRubricaLookupRow($rubrica);
|
||||
$key = mb_strtolower(trim((string) $row['label']));
|
||||
|
||||
if ($key === '' || isset($seen[$key])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$seen[$key] = true;
|
||||
$rows[] = $row;
|
||||
|
||||
if (count($rows) >= $limit) {
|
||||
break;
|
||||
}
|
||||
$row['score'] = $this->scoreRubricaLookupMatch($row, $raw, $digits, $selectedId);
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
usort($rows, fn(array $left, array $right): int => ((int) $right['score']) <=> ((int) $left['score']));
|
||||
|
||||
$rows = array_values(array_slice(array_map(function (array $row): array {
|
||||
unset($row['score']);
|
||||
|
||||
return $row;
|
||||
}, $rows), 0, $limit));
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $row
|
||||
*/
|
||||
protected function scoreRubricaLookupMatch(array $row, string $raw, string $digits, ?int $selectedId = null): int
|
||||
{
|
||||
$score = (int) (($selectedId ?? 0) > 0 && (int) ($row['id'] ?? 0) === (int) $selectedId ? 1000 : 0);
|
||||
|
||||
$haystack = mb_strtolower(implode(' ', array_filter([
|
||||
(string) ($row['label'] ?? ''),
|
||||
(string) ($row['email'] ?? ''),
|
||||
(string) ($row['phone'] ?? ''),
|
||||
(string) ($row['cf'] ?? ''),
|
||||
(string) ($row['piva'] ?? ''),
|
||||
(string) ($row['category'] ?? ''),
|
||||
(string) ($row['note'] ?? ''),
|
||||
])));
|
||||
|
||||
if ($raw !== '' && str_contains($haystack, mb_strtolower($raw))) {
|
||||
$score += 90;
|
||||
}
|
||||
|
||||
if ($digits !== '') {
|
||||
$phoneDigits = preg_replace('/\D+/', '', (string) ($row['phone'] ?? '')) ?: '';
|
||||
if ($phoneDigits !== '' && str_contains($phoneDigits, $digits)) {
|
||||
$score += 70;
|
||||
}
|
||||
}
|
||||
|
||||
return $score;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,52 @@
|
|||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-4 rounded-xl border border-slate-200 bg-slate-50 p-3">
|
||||
<label class="block text-sm">
|
||||
<span class="mb-1 block font-medium">Ricerca nominativo in anagrafica unica</span>
|
||||
<input type="text" wire:model.live.debounce.250ms="dipendenteRubricaSearch" class="w-full rounded-lg border-gray-300" placeholder="Ricerca per telefono, nome, cognome, ragione sociale o email e seleziona il contatto con un click. Es. +39 333 1234567 oppure Mario Rossi" />
|
||||
</label>
|
||||
<div class="mt-2 text-xs text-gray-500">Usa lo stesso lookup operativo del ticket per agganciare un nominativo già presente nella rubrica unica.</div>
|
||||
|
||||
@if($dipendenteRubricaId)
|
||||
<div class="mt-3 rounded-lg border border-emerald-200 bg-emerald-50 p-3 text-xs text-emerald-800">
|
||||
Nominativo selezionato: <span class="font-semibold">{{ $dipendenteRubricaSearch }}</span>
|
||||
<button type="button" wire:click="resetDipendenteRubricaSelection" class="ml-2 inline-flex items-center rounded-md bg-white px-2 py-1 text-[11px] font-medium text-emerald-700 ring-1 ring-inset ring-emerald-300 hover:bg-emerald-100">Cambia</button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(count($dipendenteRubricaMatches) > 0)
|
||||
<div class="mt-3 space-y-2">
|
||||
@foreach($dipendenteRubricaMatches as $match)
|
||||
<button type="button" wire:click="selezionaDipendenteRubrica({{ (int) $match['id'] }})" class="w-full rounded-lg border p-3 text-left {{ (int) $dipendenteRubricaId === (int) $match['id'] ? 'border-emerald-400 bg-emerald-50 ring-1 ring-emerald-200' : 'bg-white hover:bg-gray-50' }}">
|
||||
<div class="text-sm font-medium">{{ $match['label'] }}</div>
|
||||
<div class="mt-1 text-xs text-gray-600">
|
||||
{{ $match['phone'] ?: 'Telefono non valorizzato' }}
|
||||
@if($match['email'])
|
||||
· {{ $match['email'] }}
|
||||
@endif
|
||||
@if($match['cf'])
|
||||
· CF {{ $match['cf'] }}
|
||||
@endif
|
||||
</div>
|
||||
@if($match['category'])
|
||||
<div class="mt-1 text-xs text-indigo-700">Categoria: {{ $match['category'] }}</div>
|
||||
@endif
|
||||
@if($match['note'])
|
||||
<div class="mt-1 text-xs text-gray-500">{{ $match['note'] }}</div>
|
||||
@endif
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
@elseif(filled($dipendenteRubricaSearch) && ! $dipendenteRubricaId)
|
||||
<div class="mt-3 text-xs text-gray-500">Nessun nominativo trovato con questa ricerca.</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-3">
|
||||
<x-filament::button size="sm" color="primary" type="button" wire:click="collegaDipendenteDaRubrica">Collega nominativo selezionato</x-filament::button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 grid grid-cols-1 gap-2 md:grid-cols-4">
|
||||
<input type="text" wire:model.defer="nuovoDipendenteNome" class="rounded-md border-gray-300 text-xs" placeholder="Nome" />
|
||||
<input type="text" wire:model.defer="nuovoDipendenteCognome" class="rounded-md border-gray-300 text-xs" placeholder="Cognome" />
|
||||
|
|
@ -84,8 +130,15 @@
|
|||
<td class="border px-2 py-1.5">
|
||||
@if((int) ($row['user_id'] ?? 0) > 0)
|
||||
{{ $row['user_label'] }} (ID {{ (int) $row['user_id'] }})
|
||||
@if((int) ($row['rubrica_id'] ?? 0) > 0)
|
||||
<div class="mt-1 text-[11px] text-slate-500">Rubrica: {{ $row['rubrica'] ?: ('#' . (int) $row['rubrica_id']) }}</div>
|
||||
@endif
|
||||
@else
|
||||
-
|
||||
@if((int) ($row['rubrica_id'] ?? 0) > 0)
|
||||
<div>{{ $row['rubrica'] ?: ('Rubrica #' . (int) $row['rubrica_id']) }}</div>
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
@endif
|
||||
</td>
|
||||
<td class="border px-2 py-1.5">
|
||||
|
|
|
|||
|
|
@ -221,8 +221,19 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-3">
|
||||
<x-filament::section class="lg:col-span-2">
|
||||
<div class="rounded-lg border border-gray-200 bg-white p-2">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button type="button" wire:click="$set('sideTab', 'dati')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $sideTab === 'dati' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">Dati condivisi</button>
|
||||
<button type="button" wire:click="$set('sideTab', 'collegamenti')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $sideTab === 'collegamenti' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">Collegamenti</button>
|
||||
<button type="button" wire:click="$set('sideTab', 'studio')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $sideTab === 'studio' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">Collaboratore studio</button>
|
||||
<button type="button" wire:click="$set('sideTab', 'dipendenti')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $sideTab === 'dipendenti' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">Dipendenti</button>
|
||||
<button type="button" wire:click="$set('sideTab', 'autorizzazioni')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $sideTab === 'autorizzazioni' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">Ruoli e autorizzazioni</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
@if($sideTab === 'dati')
|
||||
<x-filament::section>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div>
|
||||
<div class="text-sm text-gray-500">Nominativo</div>
|
||||
|
|
@ -412,16 +423,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</x-filament::section>
|
||||
@endif
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="rounded-lg border border-gray-200 bg-white p-2">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<button type="button" wire:click="$set('sideTab', 'collegamenti')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $sideTab === 'collegamenti' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">Collegamenti</button>
|
||||
<button type="button" wire:click="$set('sideTab', 'studio')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $sideTab === 'studio' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">Collaboratore studio</button>
|
||||
<button type="button" wire:click="$set('sideTab', 'dipendenti')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $sideTab === 'dipendenti' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">Dipendenti</button>
|
||||
<button type="button" wire:click="$set('sideTab', 'autorizzazioni')" class="rounded-md px-3 py-1.5 text-xs font-medium {{ $sideTab === 'autorizzazioni' ? 'bg-indigo-700 text-white' : 'bg-gray-100 text-gray-700 hover:bg-gray-200' }}">Ruoli e autorizzazioni</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($sideTab === 'studio')
|
||||
<x-filament::section>
|
||||
|
|
|
|||
|
|
@ -58,9 +58,8 @@ class="inline-flex items-center rounded-md px-3 py-1.5 text-xs font-medium {{ $t
|
|||
<div class="flex w-full flex-col gap-2 md:w-[760px] md:flex-row md:items-center">
|
||||
<input
|
||||
type="text"
|
||||
wire:model.defer="searchInput"
|
||||
wire:keydown.enter="applicaRicerca"
|
||||
placeholder="Ragione sociale, nome, P.IVA, CF, email, telefono, TAG"
|
||||
wire:model.live.debounce.250ms="searchInput"
|
||||
placeholder="Ricerca per telefono, nome, cognome, ragione sociale, email, P.IVA, CF o TAG e seleziona il fornitore con un click"
|
||||
class="w-full rounded-lg border-gray-300 text-sm"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
|
|
@ -103,6 +102,35 @@ class="inline-flex h-4 w-4 items-center justify-center rounded-full text-slate-5
|
|||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-2 text-xs text-gray-500">La ricerca usa lo stesso stile operativo del ticket: risultati immediati, click diretto sulla scheda e lettura per telefono, email, ragione sociale o anagrafica unica collegata.</div>
|
||||
|
||||
@if($this->fornitoreMatches->count() > 0)
|
||||
<div class="mt-3 space-y-2 md:max-w-[760px]">
|
||||
@foreach($this->fornitoreMatches as $match)
|
||||
<button type="button" wire:click="apriScheda({{ (int) $match->id }})" class="w-full rounded-lg border p-3 text-left {{ (int) ($this->selectedFornitoreId ?? 0) === (int) $match->id ? 'border-emerald-400 bg-emerald-50 ring-1 ring-emerald-200' : 'bg-white hover:bg-gray-50' }}">
|
||||
<div class="text-sm font-medium">{{ $this->getFornitoreLabel($match) }}</div>
|
||||
<div class="mt-1 text-xs text-gray-600">
|
||||
{{ $match->telefono ?: ($match->cellulare ?: 'Telefono non valorizzato') }}
|
||||
@if($match->email)
|
||||
· {{ $match->email }}
|
||||
@endif
|
||||
@if($match->partita_iva)
|
||||
· P.IVA {{ $match->partita_iva }}
|
||||
@endif
|
||||
</div>
|
||||
@if($match->tags)
|
||||
<div class="mt-1 text-xs text-indigo-700">Tag: {{ $match->tags }}</div>
|
||||
@endif
|
||||
@if($match->note)
|
||||
<div class="mt-1 text-xs text-gray-500">{{ \\Illuminate\\Support\\Str::limit((string) $match->note, 120) }}</div>
|
||||
@endif
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
@elseif(trim((string) ($this->searchInput ?? '')) !== '')
|
||||
<div class="mt-3 text-xs text-gray-500">Nessun fornitore trovato con questa ricerca.</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,52 @@
|
|||
<div class="space-y-4">
|
||||
<div class="rounded-lg border border-gray-200 bg-white p-3">
|
||||
<div class="mb-2 text-sm font-semibold text-gray-700">Aggancia collaboratore da anagrafica unica</div>
|
||||
<div class="mb-3 text-xs text-gray-500">Usa la stessa ricerca guidata del ticket per trasformare un nominativo rubrica in collaboratore studio e assegnarlo agli stabili dell'amministratore corrente.</div>
|
||||
|
||||
<label class="block text-sm">
|
||||
<span class="mb-1 block font-medium">Ricerca nominativo</span>
|
||||
<input type="text" wire:model.live.debounce.250ms="collaboratoreRubricaSearch" class="w-full rounded-lg border-gray-300" placeholder="Ricerca per telefono, nome, cognome, ragione sociale o email e seleziona il contatto con un click. Es. +39 333 1234567 oppure Mario Rossi" />
|
||||
</label>
|
||||
|
||||
@if($collaboratoreRubricaId)
|
||||
<div class="mt-3 rounded-lg border border-emerald-200 bg-emerald-50 p-3 text-xs text-emerald-800">
|
||||
Nominativo selezionato: <span class="font-semibold">{{ $collaboratoreRubricaSearch }}</span>
|
||||
<button type="button" wire:click="resetCollaboratoreRubricaSelection" class="ml-2 inline-flex items-center rounded-md bg-white px-2 py-1 text-[11px] font-medium text-emerald-700 ring-1 ring-inset ring-emerald-300 hover:bg-emerald-100">Cambia</button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(count($collaboratoreRubricaMatches) > 0)
|
||||
<div class="mt-3 space-y-2">
|
||||
@foreach($collaboratoreRubricaMatches as $match)
|
||||
<button type="button" wire:click="selezionaCollaboratoreRubrica({{ (int) $match['id'] }})" class="w-full rounded-lg border p-3 text-left {{ (int) $collaboratoreRubricaId === (int) $match['id'] ? 'border-emerald-400 bg-emerald-50 ring-1 ring-emerald-200' : 'bg-white hover:bg-gray-50' }}">
|
||||
<div class="text-sm font-medium">{{ $match['label'] }}</div>
|
||||
<div class="mt-1 text-xs text-gray-600">
|
||||
{{ $match['phone'] ?: 'Telefono non valorizzato' }}
|
||||
@if($match['email'])
|
||||
· {{ $match['email'] }}
|
||||
@endif
|
||||
@if($match['cf'])
|
||||
· CF {{ $match['cf'] }}
|
||||
@endif
|
||||
</div>
|
||||
@if($match['category'])
|
||||
<div class="mt-1 text-xs text-indigo-700">Categoria: {{ $match['category'] }}</div>
|
||||
@endif
|
||||
@if($match['note'])
|
||||
<div class="mt-1 text-xs text-gray-500">{{ $match['note'] }}</div>
|
||||
@endif
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
@elseif(filled($collaboratoreRubricaSearch) && ! $collaboratoreRubricaId)
|
||||
<div class="mt-3 text-xs text-gray-500">Nessun nominativo trovato con questa ricerca.</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-3">
|
||||
<x-filament::button size="sm" color="primary" type="button" wire:click="abilitaCollaboratoreDaRubrica">Abilita collaboratore dallo studio</x-filament::button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg border border-gray-200 bg-white p-3">
|
||||
<div class="mb-2 text-sm font-semibold text-gray-700">Centralino studio e interni collaboratori</div>
|
||||
<div class="mb-3 text-xs text-gray-500">I numeri studio e gli interni PBX/CSTA sono salvati qui. Gli interni condivisi del centralino permettono di instradare popup e chiamate anche quando il PBX espone gruppi giorno/notte invece dell'interno personale.</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user