netgescon-day0/resources/views/admin/stabili/tabs/palazzine.blade.php

910 lines
90 KiB
PHP
Executable File

@php
$palazzine = $stabile->palazzine()
->with(['unitaImmobiliari' => function ($q) {
$q->with('soggetti')->orderBy('scala')->orderBy('piano')->orderBy('interno');
}])
->orderBy('codice_palazzina')
->get();
$unitaAll = $stabile->unitaImmobiliari()->with('soggetti')->get();
$fallbackGroups = collect();
if ($palazzine->isEmpty() && $unitaAll->isNotEmpty()) {
$fallbackGroups = $unitaAll->groupBy(fn($u) => strtoupper(trim($u->palazzina ?? '')))->sortKeys();
}
$unitaByPalCode = $unitaAll->groupBy(fn($u) => strtoupper(trim($u->palazzina ?? '')));
$formatSoggettoNome = static function ($soggetto) {
$nome = trim(($soggetto->nome ?? '') . ' ' . ($soggetto->cognome ?? ''));
return $nome !== '' ? $nome : ($soggetto->ragione_sociale ?? '—');
};
$ruoliProprietario = ['proprieta','proprietario','piena_proprieta','comproprietario','comproprieta','nudo_proprietario','nuda_proprieta','usufruttuario','usufrutto','diritto_abitazione','diritto_di_abitazione'];
$ruoliInquilino = ['locazione','inquilino','conduttore','locatario'];
$rolePriority = ['proprietario' => 0, 'inquilino' => 1, 'altro' => 2];
$badgePalette = [
'proprietario' => ['bg' => 'bg-emerald-50', 'text' => 'text-emerald-700', 'border' => 'border-emerald-200', 'icon' => 'fa-user-tie'],
'inquilino' => ['bg' => 'bg-sky-50', 'text' => 'text-sky-700', 'border' => 'border-sky-200', 'icon' => 'fa-user'],
'altro' => ['bg' => 'bg-slate-50', 'text' => 'text-slate-600', 'border' => 'border-slate-200', 'icon' => 'fa-user-circle'],
];
$categoriaColor = [
'A' => ['bg' => 'bg-emerald-50', 'text' => 'text-emerald-700', 'border' => 'border-emerald-200', 'icon' => 'fa-building'],
'B' => ['bg' => 'bg-sky-50', 'text' => 'text-sky-700', 'border' => 'border-sky-200', 'icon' => 'fa-warehouse'],
'C' => ['bg' => 'bg-amber-50', 'text' => 'text-amber-700', 'border' => 'border-amber-200', 'icon' => 'fa-store'],
'D' => ['bg' => 'bg-fuchsia-50', 'text' => 'text-fuchsia-700', 'border' => 'border-fuchsia-200', 'icon' => 'fa-industry'],
'E' => ['bg' => 'bg-rose-50', 'text' => 'text-rose-700', 'border' => 'border-rose-200', 'icon' => 'fa-landmark'],
'F' => ['bg' => 'bg-purple-50', 'text' => 'text-purple-700', 'border' => 'border-purple-200', 'icon' => 'fa-drafting-compass'],
'G' => ['bg' => 'bg-lime-50', 'text' => 'text-lime-700', 'border' => 'border-lime-200', 'icon' => 'fa-sitemap'],
'S' => ['bg' => 'bg-stone-50', 'text' => 'text-stone-600', 'border' => 'border-stone-200', 'icon' => 'fa-tree'],
];
$categoriaLegenda = [
'A' => ['label' => 'Gruppo A', 'descrizione' => 'Uso abitativo'],
'B' => ['label' => 'Gruppo B', 'descrizione' => 'Servizi collettivi'],
'C' => ['label' => 'Gruppo C', 'descrizione' => 'Depositi, box, negozi'],
'D' => ['label' => 'Gruppo D', 'descrizione' => 'Immobili a uso speciale'],
'E' => ['label' => 'Gruppo E', 'descrizione' => 'Immobili a destinazione particolare'],
'F' => ['label' => 'Gruppo F', 'descrizione' => 'Entità urbane particolari'],
'G' => ['label' => 'Gruppo G', 'descrizione' => 'Opere speciali e servizi'],
'S' => ['label' => 'Terreni', 'descrizione' => 'Terreni e aree scoperte'],
];
$legacyTipologie = [
'A' => 'Appartamento',
'B' => 'Autorimessa / Box',
'C' => 'Cantina / Deposito',
'D' => 'Locale commerciale',
'M' => 'Magazzino',
'S' => 'Soffitta / Sottotetto',
'L' => 'Locale tecnico',
];
$labelPiano = static function (int $piano): string {
return match ($piano) {
9 => 'Soffitte (+1)',
8 => 'Piano 8',
7 => 'Piano 7',
6 => 'Piano 6',
5 => 'Piano 5',
4 => 'Piano 4',
3 => 'Piano 3',
2 => 'Piano 2',
1 => 'Piano 1',
0 => 'Piano Terra',
-1 => 'Cantina (-1)',
default => 'Piano '.$piano,
};
};
$highlightPalazzinaId = isset($highlightPalazzina) ? (string) $highlightPalazzina : '';
$highlightPalazzinaCode = isset($highlightPalazzinaCode) ? strtoupper((string) $highlightPalazzinaCode) : '';
$highlightScala = isset($highlightScala) ? strtoupper((string) $highlightScala) : '';
$highlightUnita = isset($highlightUnita) ? (string) $highlightUnita : '';
$highlightInterno = isset($highlightInterno) ? strtoupper((string) $highlightInterno) : '';
@endphp
<div class="rounded-2xl border bg-white p-4">
<div class="space-y-6" id="palazzine-wrapper"
data-highlight-palazzina-id="{{ $highlightPalazzinaId }}"
data-highlight-palazzina-code="{{ $highlightPalazzinaCode }}"
data-highlight-scala="{{ $highlightScala }}"
data-highlight-unita="{{ $highlightUnita }}"
data-highlight-interno="{{ $highlightInterno }}">
<style>
.palazzine-layout { align-items: start; }
.palazzine-layout .left-col { position: static; top: auto; align-self: start; }
.unit-card:hover { box-shadow: 0 10px 15px -3px rgb(15 23 42 / 0.12); }
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
</style>
<!-- KPI palazzine -->
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4 gap-4">
<div class="bg-blue-50 border border-blue-200 shadow-sm p-4 rounded-lg">
<div class="flex items-center">
<div class="p-2 bg-blue-100 rounded"><i class="fas fa-building text-blue-600"></i></div>
<div class="ml-3">
<p class="text-sm text-blue-600">Palazzine</p>
<p class="text-2xl font-bold text-blue-900">{{ $palazzine->isNotEmpty() ? $palazzine->count() : $fallbackGroups->count() }}</p>
</div>
</div>
</div>
<div class="bg-indigo-50 border border-indigo-200 shadow-sm p-4 rounded-lg">
<div class="flex items-center">
<div class="p-2 bg-indigo-100 rounded"><i class="fas fa-layer-group text-indigo-600"></i></div>
<div class="ml-3">
<p class="text-sm text-indigo-600">Unità immobiliari</p>
<p class="text-2xl font-bold text-indigo-900">{{ $palazzine->isNotEmpty() ? $palazzine->flatMap->unitaImmobiliari->count() : $unitaAll->count() }}</p>
</div>
</div>
</div>
<div class="bg-green-50 border border-green-200 shadow-sm p-4 rounded-lg">
<div class="flex items-center">
<div class="p-2 bg-green-100 rounded"><i class="fas fa-users text-green-600"></i></div>
<div class="ml-3">
<p class="text-sm text-green-600">Unità con soggetti</p>
@php $withSubjectsKpi = $palazzine->isNotEmpty()
? $palazzine->flatMap->unitaImmobiliari->filter(fn($u) => $u->soggetti->isNotEmpty())->count()
: $unitaAll->filter(fn($u) => $u->soggetti->isNotEmpty())->count(); @endphp
<p class="text-2xl font-bold text-green-900">{{ $withSubjectsKpi }}</p>
</div>
</div>
</div>
<div class="bg-amber-50 border border-amber-200 shadow-sm p-4 rounded-lg">
<div class="flex items-center">
<div class="p-2 bg-amber-100 rounded"><i class="fas fa-wrench text-amber-600"></i></div>
<div class="ml-3">
<p class="text-sm text-amber-600">Locali tecnici</p>
@php $localiTecniciKpi = ($palazzine->isNotEmpty() ? $palazzine->flatMap->unitaImmobiliari : $unitaAll)
->filter(fn($u) => in_array($u->tipo_unita, ['locale_tecnico','centrale_termica','autorimessa','cantina','box','solaio']))
->count(); @endphp
<p class="text-2xl font-bold text-amber-900">{{ $localiTecniciKpi }}</p>
</div>
</div>
</div>
</div>
<div class="grid grid-cols-1 gap-6 lg:grid-cols-[18rem_minmax(0,1fr)] xl:grid-cols-[19rem_minmax(0,1fr)] 2xl:grid-cols-[20rem_minmax(0,1fr)] palazzine-layout">
<!-- SX elenco palazzine -->
<div class="left-col w-full">
<div class="bg-white rounded-lg border border-gray-200">
<div class="px-4 py-3 border-b">
<h4 class="text-md font-semibold text-gray-800"><i class="fas fa-city mr-2 text-blue-600"></i>Palazzine/Scale</h4>
</div>
<div class="p-2 space-y-1" id="palazzine-list">
@if($palazzine->isNotEmpty())
@foreach($palazzine as $pal)
@php
$pid = 'pal_'.$pal->id;
$label = $pal->denominazione ?: ($pal->codice_palazzina ?: '—');
$scalaCode = strtoupper(trim($pal->codice_palazzina ?? ''));
$unitaPal = $pal->unitaImmobiliari;
if ($unitaPal->isEmpty() && $scalaCode !== '') {
$unitaPal = $unitaByPalCode[$scalaCode] ?? collect();
}
$cnt = $unitaPal->count();
$isFirst = $loop->first;
@endphp
<button class="w-full text-left px-3 py-2 rounded hover:bg-blue-50 border flex items-center justify-between {{ $isFirst ? 'bg-blue-100 border-blue-300' : 'border-transparent' }}"
data-target="#{{ $pid }}" data-palazzina-id="{{ $pal->id }}" data-palazzina-code="{{ $scalaCode }}"
onclick="selectPalazzina(this)">
<span class="text-sm text-gray-800">{{ $label }} @if($pal->codice_palazzina && $pal->denominazione)<span class="text-xs text-gray-400 ml-1">(Cod {{ $pal->codice_palazzina }})</span>@endif</span>
<span class="text-xs bg-blue-100 text-blue-800 px-2 py-0.5 rounded">{{ $cnt }}</span>
</button>
@endforeach
@elseif($fallbackGroups->isNotEmpty())
@foreach($fallbackGroups as $codicePalazzina => $group)
@php $pid = 'fb_'.str_replace(' ','_', $codicePalazzina ?: 'ND'); @endphp
<button class="w-full text-left px-3 py-2 rounded hover:bg-blue-50 border flex items-center justify-between {{ $loop->first ? 'bg-blue-100 border-blue-300' : 'border-transparent' }}"
data-target="#{{ $pid }}" data-palazzina-code="{{ strtoupper($codicePalazzina ?: '') }}"
onclick="selectPalazzina(this)">
<span class="text-sm text-gray-800">{{ $codicePalazzina ?: '—' }} <span class="text-xs text-gray-400">(fallback)</span></span>
<span class="text-xs bg-blue-100 text-blue-800 px-2 py-0.5 rounded">{{ $group->count() }}</span>
</button>
@endforeach
@else
<div class="p-4 text-center text-gray-500">Nessuna palazzina. <a class="text-blue-600" href="{{ route('admin.palazzine.create', ['stabile_id' => $stabile->id]) }}">Crea</a></div>
@endif
</div>
</div>
</div>
<!-- DX pannelli palazzine -->
<div class="flex-1 min-w-0 space-y-6">
@if($palazzine->isNotEmpty())
@foreach($palazzine as $pal)
@php
$pid = 'pal_'.$pal->id;
$panelClasses = 'palazzina-panel ' . ($loop->first ? '' : 'hidden');
$unitaPal = $pal->unitaImmobiliari;
if ($unitaPal->isEmpty()) {
$code = strtoupper(trim($pal->codice_palazzina ?? ''));
$unitaPal = $code !== '' ? ($unitaByPalCode[$code] ?? collect()) : collect();
}
$unitCount = $unitaPal->count();
$withSubjectsCount = $unitaPal->filter(fn($u) => $u->soggetti->isNotEmpty())->count();
$localiTecniciPalCount = $unitaPal->filter(fn($u) => in_array($u->tipo_unita, ['locale_tecnico','centrale_termica','autorimessa','cantina','box','solaio']))->count();
$libereCount = $unitaPal->where('stato_occupazione', 'libera')->count();
$occupateProp = $unitaPal->where('stato_occupazione', 'occupata_proprietario')->count();
$occupateInq = $unitaPal->where('stato_occupazione', 'occupata_inquilino')->count();
@endphp
<div id="{{ $pid }}" class="{{ $panelClasses }}">
<div class="bg-white rounded-lg border border-gray-200">
<div class="px-6 py-4 border-b flex flex-col md:flex-row md:items-center md:justify-between gap-3 palazzina-header">
<div>
<div class="flex items-center space-x-3">
<i class="fas fa-city text-blue-600"></i>
<h3 class="text-lg font-semibold text-gray-900">Palazzina {{ $pal->denominazione ?: ($pal->codice_palazzina ?: '—') }}</h3>
@if($pal->codice_palazzina && $pal->denominazione)
<span class="text-xs text-gray-500">(Cod {{ $pal->codice_palazzina }})</span>
@endif
</div>
</div>
<div class="flex flex-wrap items-center gap-2 text-xs md:text-sm">
<span class="px-2 py-0.5 rounded border border-blue-200 bg-blue-50 text-blue-800"><i class="fas fa-layer-group mr-1"></i>{{ $unitCount }} unità</span>
<span class="px-2 py-0.5 rounded border border-purple-200 bg-purple-50 text-purple-800"><i class="fas fa-users mr-1"></i>{{ $withSubjectsCount }} con soggetti</span>
<span class="px-2 py-0.5 rounded border border-amber-200 bg-amber-50 text-amber-700"><i class="fas fa-wrench mr-1"></i>{{ $localiTecniciPalCount }} locali tecnici</span>
<span class="px-2 py-0.5 rounded border border-emerald-200 bg-emerald-50 text-emerald-700"><i class="fas fa-user-check mr-1"></i>{{ $occupateProp }} proprietario</span>
<span class="px-2 py-0.5 rounded border border-sky-200 bg-sky-50 text-sky-700"><i class="fas fa-user mr-1"></i>{{ $occupateInq }} inquilino</span>
<span class="px-2 py-0.5 rounded border border-stone-200 bg-stone-50 text-stone-600"><i class="fas fa-door-open mr-1"></i>{{ $libereCount }} libere</span>
</div>
</div>
@php $perScala = $unitaPal->groupBy('scala'); @endphp
<div class="p-6 space-y-8">
@foreach($perScala as $scala => $unitaScala)
@php $pianiGrouped = $unitaScala->groupBy(fn($u) => (int) ($u->piano ?? 0))->sortKeysDesc(); @endphp
<div>
<h4 class="text-md font-medium text-gray-700 mb-3"><i class="fas fa-stairs mr-2"></i>Scala {{ $scala ?? '-' }}</h4>
<div class="flex flex-col gap-6">
@foreach($pianiGrouped as $piano => $unitaPiano)
<div class="border border-gray-200 rounded-lg">
<div class="bg-gray-50 px-3 py-2 border-b text-sm text-gray-700 font-semibold">{{ $labelPiano((int) $piano) }}</div>
<div class="p-4">
@if($unitaPiano->isNotEmpty())
<div class="relative group/floor">
<button type="button"
onclick="scrollFloor(this, 'left')"
class="absolute left-0 top-1/2 -translate-y-1/2 -ml-2 z-20 w-8 h-8 rounded-full bg-white/95 shadow-md border border-gray-200 flex items-center justify-center text-gray-700 hover:bg-gray-50 focus:outline-none opacity-0 group-hover/floor:opacity-100 transition-opacity duration-200">
<i class="fas fa-chevron-left text-xs"></i>
</button>
<div class="overflow-x-auto pb-3 -mx-4 px-4 no-scrollbar scroll-smooth snap-x snap-mandatory">
<div class="flex flex-row flex-nowrap gap-5" style="width: max-content; min-width: 100%;">
@foreach($unitaPiano->sortBy(fn ($u) => \App\Support\InternoSort::key($u->interno !== null ? (string) $u->interno : null)) as $u)
@php
$categoriaCompleta = strtoupper(trim($u->categoria_catastale ?? ''));
$categoriaGruppo = $categoriaCompleta !== '' ? substr($categoriaCompleta, 0, 1) : null;
$categoriaStyle = $categoriaColor[$categoriaGruppo] ?? ['bg' => 'bg-slate-50', 'text' => 'text-slate-600', 'border' => 'border-slate-200', 'icon' => 'fa-tags'];
$categoriaDescrizione = $categoriaLegenda[$categoriaGruppo]['descrizione'] ?? null;
$tipoUnitaLabel = $legacyTipologie[strtoupper(substr($u->tipo_unita ?? '', 0, 1))] ?? ucfirst(str_replace('_', ' ', $u->tipo_unita ?? 'ND'));
$utilizzo = $u->utilizzo_attuale ? ucfirst($u->utilizzo_attuale) : null;
$consistenza = $u->consistenza ? number_format((float) $u->consistenza, 2, ',', '.') : null;
$rendita = $u->rendita_catastale ? number_format((float) $u->rendita_catastale, 2, ',', '.') : null;
$occupanti = $u->soggetti->map(function ($soggetto) use ($formatSoggettoNome, $ruoliProprietario, $ruoliInquilino, $badgePalette, $rolePriority) {
$tipoPivot = strtolower(trim($soggetto->pivot?->tipo_diritto ?? ''));
$ruoloKey = in_array($tipoPivot, $ruoliProprietario, true)
? 'proprietario'
: (in_array($tipoPivot, $ruoliInquilino, true) ? 'inquilino' : 'altro');
$palette = $badgePalette[$ruoloKey] ?? $badgePalette['altro'];
return [
'nome' => $formatSoggettoNome($soggetto),
'ruolo_label' => $tipoPivot !== '' ? ucfirst(str_replace('_', ' ', $tipoPivot)) : ($ruoloKey === 'altro' ? 'Altro' : ucfirst($ruoloKey)),
'ruolo_key' => $ruoloKey,
'palette' => $palette,
'percentuale' => $soggetto->pivot?->percentuale_possesso,
'inizio' => optional(\Illuminate\Support\Carbon::make($soggetto->pivot?->data_inizio))?->format('d/m/Y'),
'fine' => optional(\Illuminate\Support\Carbon::make($soggetto->pivot?->data_fine))?->format('d/m/Y'),
];
})->sortBy(fn($item) => $rolePriority[$item['ruolo_key']] ?? 99)->values();
$statoLabel = match($u->stato_occupazione) {
'occupata_proprietario' => 'Proprietà',
'occupata_inquilino' => 'Locata',
'libera' => 'Libera',
default => ucfirst(str_replace('_', ' ', $u->stato_occupazione ?? 'Sconosciuta')),
};
$statoColor = match($u->stato_occupazione) {
'occupata_proprietario' => 'bg-emerald-100 text-emerald-700 border-emerald-200',
'occupata_inquilino' => 'bg-sky-100 text-sky-700 border-sky-200',
'libera' => 'bg-stone-100 text-stone-600 border-stone-200',
default => 'bg-slate-100 text-slate-600 border-slate-200',
};
@endphp
<div class="w-[320px] flex-shrink-0 border border-gray-200 rounded-lg bg-white shadow-sm p-4 space-y-4 unit-card snap-start"
data-unita-id="{{ $u->id }}"
data-scala="{{ strtoupper($scala ?? '') }}"
data-interno="{{ strtoupper($u->interno ?? '') }}">
<div class="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3">
<div class="flex items-start space-x-3">
<div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center border border-blue-200">
<span class="text-sm font-bold text-blue-700">{{ $u->interno ?: '—' }}</span>
</div>
<div>
<p class="text-sm font-semibold text-gray-900">{{ $u->codice_unita ?: 'Unità '.$u->id }}</p>
<div class="mt-1 flex flex-wrap gap-2 text-xxs text-gray-600">
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-stairs mr-1"></i>Scala {{ $scala ?? '—' }}</span>
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-layer-group mr-1"></i>{{ $labelPiano((int) ($u->piano ?? 0)) }}</span>
<span class="inline-flex items-center px-2 py-0.5 {{ $categoriaStyle['bg'] }} {{ $categoriaStyle['text'] }} border {{ $categoriaStyle['border'] }} rounded-full" title="{{ $categoriaDescrizione }}"><i class="fas {{ $categoriaStyle['icon'] ?? 'fa-tags' }} mr-1"></i>{{ $categoriaCompleta ?: 'Cat. ND' }}</span>
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-home mr-1"></i>{{ $tipoUnitaLabel }}</span>
@if($utilizzo)
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-briefcase mr-1"></i>{{ $utilizzo }}</span>
@endif
</div>
<div class="mt-2 flex flex-wrap items-center gap-2 text-xs">
<a href="{{ route('admin.unita-immobiliari.show', $u) }}" class="inline-flex items-center px-2 py-1 bg-blue-50 text-blue-700 border border-blue-200 rounded hover:bg-blue-100 transition"><i class="fas fa-eye mr-1"></i>Apri</a>
<a href="{{ route('admin.unita-immobiliari.edit', $u) }}" class="inline-flex items-center px-2 py-1 bg-emerald-50 text-emerald-700 border border-emerald-200 rounded hover:bg-emerald-100 transition"><i class="fas fa-pen mr-1"></i>Modifica</a>
</div>
</div>
</div>
<div class="flex flex-col items-end space-y-2">
<span class="inline-flex items-center px-2 py-0.5 text-xxs font-semibold rounded border {{ $statoColor }}"><i class="fas fa-circle mr-1"></i>{{ $statoLabel }}</span>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-2 text-xxs text-gray-600">
@if($u->subalterno)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Subalterno:</span> {{ $u->subalterno }}</div>
@endif
@if($consistenza)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Consistenza:</span> {{ $consistenza }}</div>
@endif
@if($rendita)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Rendita:</span> {{ $rendita }}</div>
@endif
@if($u->millesimi_generali)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Millesimi Proprietà:</span> {{ number_format((float) $u->millesimi_generali, 3, ',', '.') }}</div>
@endif
@if($u->millesimi_riscaldamento)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Millesimi Riscaldamento:</span> {{ number_format((float) $u->millesimi_riscaldamento, 3, ',', '.') }}</div>
@endif
@if($u->millesimi_acqua)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Millesimi Acqua:</span> {{ number_format((float) $u->millesimi_acqua, 3, ',', '.') }}</div>
@endif
</div>
<div class="space-y-2">
@if($occupanti->isNotEmpty())
@foreach($occupanti as $occupante)
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 text-xs {{ $occupante['ruolo_key'] === 'proprietario' ? 'bg-emerald-50 border border-emerald-200 rounded p-2' : ($occupante['ruolo_key'] === 'inquilino' ? 'bg-sky-50 border border-sky-200 rounded p-2' : 'bg-slate-50 border border-slate-200 rounded p-2') }}">
<div class="flex items-center gap-2">
<span class="inline-flex items-center px-2 py-0.5 rounded border {{ $occupante['palette']['border'] }} {{ $occupante['palette']['bg'] }} {{ $occupante['palette']['text'] }}">
<i class="fas {{ $occupante['palette']['icon'] }} mr-1"></i>{{ \Illuminate\Support\Str::limit($occupante['nome'], 60) }}
</span>
<span class="text-xxs text-slate-500">{{ $occupante['ruolo_label'] }}</span>
</div>
<div class="text-xxs text-right text-slate-500 space-y-1">
@if(!is_null($occupante['percentuale']))
<div>Quota {{ rtrim(rtrim(number_format((float) $occupante['percentuale'], 4, ',', '.'), '0'), ',') }}%</div>
@endif
@if($occupante['inizio'] || $occupante['fine'])
<div>
@if($occupante['inizio'] && $occupante['fine'])
{{ $occupante['inizio'] }} {{ $occupante['fine'] }}
@elseif($occupante['inizio'])
dal {{ $occupante['inizio'] }}
@else
fino al {{ $occupante['fine'] }}
@endif
</div>
@endif
</div>
</div>
@endforeach
@else
<p class="text-xs text-slate-500 italic">Nessun soggetto associato a questa unità.</p>
@endif
</div>
</div>
@endforeach
</div>
</div>
<button type="button"
onclick="scrollFloor(this, 'right')"
class="absolute right-0 top-1/2 -translate-y-1/2 -mr-2 z-20 w-8 h-8 rounded-full bg-white/95 shadow-md border border-gray-200 flex items-center justify-center text-gray-700 hover:bg-gray-50 focus:outline-none opacity-0 group-hover/floor:opacity-100 transition-opacity duration-200">
<i class="fas fa-chevron-right text-xs"></i>
</button>
</div>
@else
<p class="text-sm text-slate-500">Nessuna unità su questo piano.</p>
@endif
</div>
</div>
@endforeach
</div>
</div>
@endforeach
</div>
</div>
</div>
@endforeach
@elseif($fallbackGroups->isNotEmpty())
@foreach($fallbackGroups as $codicePalazzina => $group)
@php
$pid = 'fb_'.str_replace(' ','_', $codicePalazzina ?: 'ND');
$unitCount = $group->count();
$withSubjectsCount = $group->filter(fn($u) => $u->soggetti->isNotEmpty())->count();
$localiTecniciPalCount = $group->filter(fn($u) => in_array($u->tipo_unita, ['locale_tecnico','centrale_termica','autorimessa','cantina','box','solaio']))->count();
$libereCount = $group->where('stato_occupazione', 'libera')->count();
$occupateProp = $group->where('stato_occupazione', 'occupata_proprietario')->count();
$occupateInq = $group->where('stato_occupazione', 'occupata_inquilino')->count();
@endphp
<div id="{{ $pid }}" class="palazzina-panel {{ $loop->first ? '' : 'hidden' }}">
<div class="bg-white rounded-lg border border-gray-200">
<div class="px-6 py-4 border-b flex flex-col md:flex-row md:items-center md:justify-between gap-3">
<div>
<div class="flex items-center space-x-3">
<i class="fas fa-city text-blue-600"></i>
<h3 class="text-lg font-semibold text-gray-900">Palazzina {{ $codicePalazzina ?: '—' }} <span class="text-gray-500 text-sm">(fallback)</span></h3>
</div>
</div>
<div class="flex flex-wrap items-center gap-2 text-xs md:text-sm">
<span class="px-2 py-0.5 rounded border border-blue-200 bg-blue-50 text-blue-800"><i class="fas fa-layer-group mr-1"></i>{{ $unitCount }} unità</span>
<span class="px-2 py-0.5 rounded border border-purple-200 bg-purple-50 text-purple-800"><i class="fas fa-users mr-1"></i>{{ $withSubjectsCount }} con soggetti</span>
<span class="px-2 py-0.5 rounded border border-amber-200 bg-amber-50 text-amber-700"><i class="fas fa-wrench mr-1"></i>{{ $localiTecniciPalCount }} locali tecnici</span>
<span class="px-2 py-0.5 rounded border border-emerald-200 bg-emerald-50 text-emerald-700"><i class="fas fa-user-check mr-1"></i>{{ $occupateProp }} proprietario</span>
<span class="px-2 py-0.5 rounded border border-sky-200 bg-sky-50 text-sky-700"><i class="fas fa-user mr-1"></i>{{ $occupateInq }} inquilino</span>
<span class="px-2 py-0.5 rounded border border-stone-200 bg-stone-50 text-stone-600"><i class="fas fa-door-open mr-1"></i>{{ $libereCount }} libere</span>
</div>
</div>
@php $perScala = $group->groupBy('scala'); @endphp
<div class="p-6 space-y-8">
@foreach($perScala as $scala => $unitaScala)
@php $pianiGrouped = $unitaScala->groupBy(fn($u) => (int) ($u->piano ?? 0))->sortKeysDesc(); @endphp
<div>
<h4 class="text-md font-medium text-gray-700 mb-3"><i class="fas fa-stairs mr-2"></i>Scala {{ $scala ?? '-' }}</h4>
<div class="flex flex-col gap-6">
@foreach($pianiGrouped as $piano => $unitaPiano)
<div class="border border-gray-200 rounded-lg">
<div class="bg-gray-50 px-3 py-2 border-b text-sm text-gray-700 font-semibold">{{ $labelPiano((int) $piano) }}</div>
<div class="p-4">
@if($unitaPiano->isNotEmpty())
<div class="relative group/floor">
<button type="button"
onclick="scrollFloor(this, 'left')"
class="absolute left-0 top-1/2 -translate-y-1/2 -ml-2 z-20 w-8 h-8 rounded-full bg-white/95 shadow-md border border-gray-200 flex items-center justify-center text-gray-700 hover:bg-gray-50 focus:outline-none opacity-0 group-hover/floor:opacity-100 transition-opacity duration-200">
<i class="fas fa-chevron-left text-xs"></i>
</button>
<div class="overflow-x-auto pb-3 -mx-4 px-4 no-scrollbar scroll-smooth snap-x snap-mandatory">
<div class="flex flex-row flex-nowrap gap-5" style="width: max-content; min-width: 100%;">
@foreach($unitaPiano->sortBy(fn ($u) => \App\Support\InternoSort::key($u->interno !== null ? (string) $u->interno : null)) as $u)
@php
$categoriaCompleta = strtoupper(trim($u->categoria_catastale ?? ''));
$categoriaGruppo = $categoriaCompleta !== '' ? substr($categoriaCompleta, 0, 1) : null;
$categoriaStyle = $categoriaColor[$categoriaGruppo] ?? ['bg' => 'bg-slate-50', 'text' => 'text-slate-600', 'border' => 'border-slate-200', 'icon' => 'fa-tags'];
$categoriaDescrizione = $categoriaLegenda[$categoriaGruppo]['descrizione'] ?? null;
$tipoUnitaLabel = $legacyTipologie[strtoupper(substr($u->tipo_unita ?? '', 0, 1))] ?? ucfirst(str_replace('_', ' ', $u->tipo_unita ?? 'ND'));
$utilizzo = $u->utilizzo_attuale ? ucfirst($u->utilizzo_attuale) : null;
$consistenza = $u->consistenza ? number_format((float) $u->consistenza, 2, ',', '.') : null;
$rendita = $u->rendita_catastale ? number_format((float) $u->rendita_catastale, 2, ',', '.') : null;
$occupanti = $u->soggetti->map(function ($soggetto) use ($formatSoggettoNome, $ruoliProprietario, $ruoliInquilino, $badgePalette, $rolePriority) {
$tipoPivot = strtolower(trim($soggetto->pivot?->tipo_diritto ?? ''));
$ruoloKey = in_array($tipoPivot, $ruoliProprietario, true)
? 'proprietario'
: (in_array($tipoPivot, $ruoliInquilino, true) ? 'inquilino' : 'altro');
$palette = $badgePalette[$ruoloKey] ?? $badgePalette['altro'];
return [
'nome' => $formatSoggettoNome($soggetto),
'ruolo_label' => $tipoPivot !== '' ? ucfirst(str_replace('_', ' ', $tipoPivot)) : ($ruoloKey === 'altro' ? 'Altro' : ucfirst($ruoloKey)),
'ruolo_key' => $ruoloKey,
'palette' => $palette,
'percentuale' => $soggetto->pivot?->percentuale_possesso,
'inizio' => optional(\Illuminate\Support\Carbon::make($soggetto->pivot?->data_inizio))?->format('d/m/Y'),
'fine' => optional(\Illuminate\Support\Carbon::make($soggetto->pivot?->data_fine))?->format('d/m/Y'),
];
})->sortBy(fn($item) => $rolePriority[$item['ruolo_key']] ?? 99)->values();
$statoLabel = match($u->stato_occupazione) {
'occupata_proprietario' => 'Proprietà',
'occupata_inquilino' => 'Locata',
'libera' => 'Libera',
default => ucfirst(str_replace('_', ' ', $u->stato_occupazione ?? 'Sconosciuta')),
};
$statoColor = match($u->stato_occupazione) {
'occupata_proprietario' => 'bg-emerald-100 text-emerald-700 border-emerald-200',
'occupata_inquilino' => 'bg-sky-100 text-sky-700 border-sky-200',
'libera' => 'bg-stone-100 text-stone-600 border-stone-200',
default => 'bg-slate-100 text-slate-600 border-slate-200',
};
@endphp
<div class="w-[320px] flex-shrink-0 border border-gray-200 rounded-lg bg-white shadow-sm p-4 space-y-4 unit-card snap-start"
data-unita-id="{{ $u->id }}"
data-scala="{{ strtoupper($scala ?? '') }}"
data-interno="{{ strtoupper($u->interno ?? '') }}">
<div class="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3">
<div class="flex items-start space-x-3">
<div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center border border-blue-200">
<span class="text-sm font-bold text-blue-700">{{ $u->interno ?: '—' }}</span>
</div>
<div>
<p class="text-sm font-semibold text-gray-900">{{ $u->codice_unita ?: 'Unità '.$u->id }}</p>
<div class="mt-1 flex flex-wrap gap-2 text-xxs text-gray-600">
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-stairs mr-1"></i>Scala {{ $scala ?? '—' }}</span>
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-layer-group mr-1"></i>{{ $labelPiano((int) ($u->piano ?? 0)) }}</span>
<span class="inline-flex items-center px-2 py-0.5 {{ $categoriaStyle['bg'] }} {{ $categoriaStyle['text'] }} border {{ $categoriaStyle['border'] }} rounded-full" title="{{ $categoriaDescrizione }}"><i class="fas {{ $categoriaStyle['icon'] ?? 'fa-tags' }} mr-1"></i>{{ $categoriaCompleta ?: 'Cat. ND' }}</span>
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-home mr-1"></i>{{ $tipoUnitaLabel }}</span>
@if($utilizzo)
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-briefcase mr-1"></i>{{ $utilizzo }}</span>
@endif
</div>
<div class="mt-2 flex flex-wrap items-center gap-2 text-xs">
<a href="{{ route('admin.unita-immobiliari.show', $u) }}" class="inline-flex items-center px-2 py-1 bg-blue-50 text-blue-700 border border-blue-200 rounded hover:bg-blue-100 transition"><i class="fas fa-eye mr-1"></i>Apri</a>
<a href="{{ route('admin.unita-immobiliari.edit', $u) }}" class="inline-flex items-center px-2 py-1 bg-emerald-50 text-emerald-700 border border-emerald-200 rounded hover:bg-emerald-100 transition"><i class="fas fa-pen mr-1"></i>Modifica</a>
</div>
</div>
</div>
<div class="flex flex-col items-end space-y-2">
<span class="inline-flex items-center px-2 py-0.5 text-xxs font-semibold rounded border {{ $statoColor }}"><i class="fas fa-circle mr-1"></i>{{ $statoLabel }}</span>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-2 text-xxs text-gray-600">
@if($u->subalterno)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Subalterno:</span> {{ $u->subalterno }}</div>
@endif
@if($consistenza)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Consistenza:</span> {{ $consistenza }}</div>
@endif
@if($rendita)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Rendita:</span> {{ $rendita }}</div>
@endif
@if($u->millesimi_generali)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Millesimi Proprietà:</span> {{ number_format((float) $u->millesimi_generali, 3, ',', '.') }}</div>
@endif
@if($u->millesimi_riscaldamento)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Millesimi Riscaldamento:</span> {{ number_format((float) $u->millesimi_riscaldamento, 3, ',', '.') }}</div>
@endif
@if($u->millesimi_acqua)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Millesimi Acqua:</span> {{ number_format((float) $u->millesimi_acqua, 3, ',', '.') }}</div>
@endif
</div>
<div class="space-y-2">
@if($occupanti->isNotEmpty())
@foreach($occupanti as $occupante)
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 text-xs {{ $occupante['ruolo_key'] === 'proprietario' ? 'bg-emerald-50 border border-emerald-200 rounded p-2' : ($occupante['ruolo_key'] === 'inquilino' ? 'bg-sky-50 border border-sky-200 rounded p-2' : 'bg-slate-50 border border-slate-200 rounded p-2') }}">
<div class="flex items-center gap-2">
<span class="inline-flex items-center px-2 py-0.5 rounded border {{ $occupante['palette']['border'] }} {{ $occupante['palette']['bg'] }} {{ $occupante['palette']['text'] }}">
<i class="fas {{ $occupante['palette']['icon'] }} mr-1"></i>{{ \Illuminate\Support\Str::limit($occupante['nome'], 60) }}
</span>
<span class="text-xxs text-slate-500">{{ $occupante['ruolo_label'] }}</span>
</div>
<div class="text-xxs text-right text-slate-500 space-y-1">
@if(!is_null($occupante['percentuale']))
<div>Quota {{ rtrim(rtrim(number_format((float) $occupante['percentuale'], 4, ',', '.'), '0'), ',') }}%</div>
@endif
@if($occupante['inizio'] || $occupante['fine'])
<div>
@if($occupante['inizio'] && $occupante['fine'])
{{ $occupante['inizio'] }} {{ $occupante['fine'] }}
@elseif($occupante['inizio'])
dal {{ $occupante['inizio'] }}
@else
fino al {{ $occupante['fine'] }}
@endif
</div>
@endif
</div>
</div>
@endforeach
@else
<p class="text-xs text-slate-500 italic">Nessun soggetto associato a questa unità.</p>
@endif
</div>
</div>
@endforeach
</div>
</div>
<button type="button"
onclick="scrollFloor(this, 'right')"
class="absolute right-0 top-1/2 -translate-y-1/2 -mr-2 z-20 w-8 h-8 rounded-full bg-white/95 shadow-md border border-gray-200 flex items-center justify-center text-gray-700 hover:bg-gray-50 focus:outline-none opacity-0 group-hover/floor:opacity-100 transition-opacity duration-200">
<i class="fas fa-chevron-right text-xs"></i>
</button>
</div>
@else
<p class="text-sm text-slate-500">Nessuna unità su questo piano.</p>
@endif
</div>
</div>
@endforeach
</div>
</div>
@endforeach
</div>
</div>
</div>
@endforeach
@elseif($fallbackGroups->isNotEmpty())
@foreach($fallbackGroups as $codicePalazzina => $group)
@php
$pid = 'fb_'.str_replace(' ','_', $codicePalazzina ?: 'ND');
$unitCount = $group->count();
$withSubjectsCount = $group->filter(fn($u) => $u->soggetti->isNotEmpty())->count();
$localiTecniciPalCount = $group->filter(fn($u) => in_array($u->tipo_unita, ['locale_tecnico','centrale_termica','autorimessa','cantina','box','solaio']))->count();
$libereCount = $group->where('stato_occupazione', 'libera')->count();
$occupateProp = $group->where('stato_occupazione', 'occupata_proprietario')->count();
$occupateInq = $group->where('stato_occupazione', 'occupata_inquilino')->count();
@endphp
<div id="{{ $pid }}" class="palazzina-panel {{ $loop->first ? '' : 'hidden' }}">
<div class="bg-white rounded-lg border border-gray-200">
<div class="px-6 py-4 border-b flex flex-col md:flex-row md:items-center md:justify-between gap-3">
<div>
<div class="flex items-center space-x-3">
<i class="fas fa-city text-blue-600"></i>
<h3 class="text-lg font-semibold text-gray-900">Palazzina {{ $codicePalazzina ?: '—' }} <span class="text-gray-500 text-sm">(fallback)</span></h3>
</div>
</div>
<div class="flex flex-wrap items-center gap-2 text-xs md:text-sm">
<span class="px-2 py-0.5 rounded border border-blue-200 bg-blue-50 text-blue-800"><i class="fas fa-layer-group mr-1"></i>{{ $unitCount }} unità</span>
<span class="px-2 py-0.5 rounded border border-purple-200 bg-purple-50 text-purple-800"><i class="fas fa-users mr-1"></i>{{ $withSubjectsCount }} con soggetti</span>
<span class="px-2 py-0.5 rounded border border-amber-200 bg-amber-50 text-amber-700"><i class="fas fa-wrench mr-1"></i>{{ $localiTecniciPalCount }} locali tecnici</span>
<span class="px-2 py-0.5 rounded border border-emerald-200 bg-emerald-50 text-emerald-700"><i class="fas fa-user-check mr-1"></i>{{ $occupateProp }} proprietario</span>
<span class="px-2 py-0.5 rounded border border-sky-200 bg-sky-50 text-sky-700"><i class="fas fa-user mr-1"></i>{{ $occupateInq }} inquilino</span>
<span class="px-2 py-0.5 rounded border border-stone-200 bg-stone-50 text-stone-600"><i class="fas fa-door-open mr-1"></i>{{ $libereCount }} libere</span>
</div>
</div>
@php $perScala = $group->groupBy('scala'); @endphp
<div class="p-6 space-y-8">
@foreach($perScala as $scala => $unitaScala)
@php $pianiGrouped = $unitaScala->groupBy(fn($u) => (int) ($u->piano ?? 0))->sortKeysDesc(); @endphp
<div>
<h4 class="text-md font-medium text-gray-700 mb-3"><i class="fas fa-stairs mr-2"></i>Scala {{ $scala ?? '-' }}</h4>
<div class="grid grid-cols-1 2xl:grid-cols-2 gap-6">
@foreach($pianiGrouped as $piano => $unitaPiano)
<div class="border border-gray-200 rounded-lg">
<div class="bg-gray-50 px-3 py-2 border-b text-sm text-gray-700">{{ $labelPiano((int) $piano) }}</div>
<div class="p-3">
@if($unitaPiano->isNotEmpty())
<div class="grid grid-cols-1 lg:grid-cols-2 gap-5">
@foreach($unitaPiano->sortBy(fn ($u) => \App\Support\InternoSort::key($u->interno !== null ? (string) $u->interno : null)) as $u)
@php
$categoriaCompleta = strtoupper(trim($u->categoria_catastale ?? ''));
$categoriaGruppo = $categoriaCompleta !== '' ? substr($categoriaCompleta, 0, 1) : null;
$categoriaStyle = $categoriaColor[$categoriaGruppo] ?? ['bg' => 'bg-slate-50', 'text' => 'text-slate-600', 'border' => 'border-slate-200', 'icon' => 'fa-tags'];
$categoriaDescrizione = $categoriaLegenda[$categoriaGruppo]['descrizione'] ?? null;
$tipoUnitaLabel = $legacyTipologie[strtoupper(substr($u->tipo_unita ?? '', 0, 1))] ?? ucfirst(str_replace('_', ' ', $u->tipo_unita ?? 'ND'));
$utilizzo = $u->utilizzo_attuale ? ucfirst($u->utilizzo_attuale) : null;
$consistenza = $u->consistenza ? number_format((float) $u->consistenza, 2, ',', '.') : null;
$rendita = $u->rendita_catastale ? number_format((float) $u->rendita_catastale, 2, ',', '.') : null;
$occupanti = $u->soggetti->map(function ($soggetto) use ($formatSoggettoNome, $ruoliProprietario, $ruoliInquilino, $badgePalette, $rolePriority) {
$tipoPivot = strtolower(trim($soggetto->pivot?->tipo_diritto ?? ''));
$ruoloKey = in_array($tipoPivot, $ruoliProprietario, true)
? 'proprietario'
: (in_array($tipoPivot, $ruoliInquilino, true) ? 'inquilino' : 'altro');
$palette = $badgePalette[$ruoloKey] ?? $badgePalette['altro'];
return [
'nome' => $formatSoggettoNome($soggetto),
'ruolo_label' => $tipoPivot !== '' ? ucfirst(str_replace('_', ' ', $tipoPivot)) : ($ruoloKey === 'altro' ? 'Altro' : ucfirst($ruoloKey)),
'ruolo_key' => $ruoloKey,
'palette' => $palette,
'percentuale' => $soggetto->pivot?->percentuale_possesso,
'inizio' => optional(\Illuminate\Support\Carbon::make($soggetto->pivot?->data_inizio))?->format('d/m/Y'),
'fine' => optional(\Illuminate\Support\Carbon::make($soggetto->pivot?->data_fine))?->format('d/m/Y'),
];
})->sortBy(fn($item) => $rolePriority[$item['ruolo_key']] ?? 99)->values();
$statoLabel = match($u->stato_occupazione) {
'occupata_proprietario' => 'Proprietà',
'occupata_inquilino' => 'Locata',
'libera' => 'Libera',
default => ucfirst(str_replace('_', ' ', $u->stato_occupazione ?? 'Sconosciuta')),
};
$statoColor = match($u->stato_occupazione) {
'occupata_proprietario' => 'bg-emerald-100 text-emerald-700 border-emerald-200',
'occupata_inquilino' => 'bg-sky-100 text-sky-700 border-sky-200',
'libera' => 'bg-stone-100 text-stone-600 border-stone-200',
default => 'bg-slate-100 text-slate-600 border-slate-200',
};
@endphp
<div class="border border-gray-200 rounded-lg bg-white shadow-sm p-4 space-y-4 unit-card"
data-unita-id="{{ $u->id }}"
data-scala="{{ strtoupper($scala ?? '') }}"
data-interno="{{ strtoupper($u->interno ?? '') }}">
<div class="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-3">
<div class="flex items-start space-x-3">
<div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center border border-blue-200">
<span class="text-sm font-bold text-blue-700">{{ $u->interno ?: '—' }}</span>
</div>
<div>
<p class="text-sm font-semibold text-gray-900">{{ $u->codice_unita ?: 'Unità '.$u->id }}</p>
<div class="mt-1 flex flex-wrap gap-2 text-xxs text-gray-600">
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-stairs mr-1"></i>Scala {{ $scala ?? '—' }}</span>
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-layer-group mr-1"></i>{{ $labelPiano((int) ($u->piano ?? 0)) }}</span>
<span class="inline-flex items-center px-2 py-0.5 {{ $categoriaStyle['bg'] }} {{ $categoriaStyle['text'] }} border {{ $categoriaStyle['border'] }} rounded-full" title="{{ $categoriaDescrizione }}"><i class="fas {{ $categoriaStyle['icon'] ?? 'fa-tags' }} mr-1"></i>{{ $categoriaCompleta ?: 'Cat. ND' }}</span>
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-home mr-1"></i>{{ $tipoUnitaLabel }}</span>
@if($utilizzo)
<span class="inline-flex items-center px-2 py-0.5 bg-slate-100 rounded-full border border-slate-200"><i class="fas fa-briefcase mr-1"></i>{{ $utilizzo }}</span>
@endif
</div>
<div class="mt-2 flex flex-wrap items-center gap-2 text-xs">
<a href="{{ route('admin.unita-immobiliari.show', $u) }}" class="inline-flex items-center px-2 py-1 bg-blue-50 text-blue-700 border border-blue-200 rounded hover:bg-blue-100 transition"><i class="fas fa-eye mr-1"></i>Apri</a>
<a href="{{ route('admin.unita-immobiliari.edit', $u) }}" class="inline-flex items-center px-2 py-1 bg-emerald-50 text-emerald-700 border border-emerald-200 rounded hover:bg-emerald-100 transition"><i class="fas fa-pen mr-1"></i>Modifica</a>
</div>
</div>
</div>
<div class="flex flex-col items-end space-y-2">
<span class="inline-flex items-center px-2 py-0.5 text-xxs font-semibold rounded border {{ $statoColor }}"><i class="fas fa-circle mr-1"></i>{{ $statoLabel }}</span>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-2 text-xxs text-gray-600">
@if($u->subalterno)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Subalterno:</span> {{ $u->subalterno }}</div>
@endif
@if($consistenza)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Consistenza:</span> {{ $consistenza }}</div>
@endif
@if($rendita)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Rendita:</span> {{ $rendita }}</div>
@endif
@if($u->millesimi_generali)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Millesimi Proprietà:</span> {{ number_format((float) $u->millesimi_generali, 3, ',', '.') }}</div>
@endif
@if($u->millesimi_riscaldamento)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Millesimi Riscaldamento:</span> {{ number_format((float) $u->millesimi_riscaldamento, 3, ',', '.') }}</div>
@endif
@if($u->millesimi_acqua)
<div class="px-2 py-1 bg-slate-50 border border-slate-200 rounded"><span class="font-semibold text-slate-700">Millesimi Acqua:</span> {{ number_format((float) $u->millesimi_acqua, 3, ',', '.') }}</div>
@endif
</div>
<div class="space-y-2">
@if($occupanti->isNotEmpty())
@foreach($occupanti as $occupante)
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 text-xs {{ $occupante['ruolo_key'] === 'proprietario' ? 'bg-emerald-50 border border-emerald-200 rounded p-2' : ($occupante['ruolo_key'] === 'inquilino' ? 'bg-sky-50 border border-sky-200 rounded p-2' : 'bg-slate-50 border border-slate-200 rounded p-2') }}">
<div class="flex items-center gap-2">
<span class="inline-flex items-center px-2 py-0.5 rounded border {{ $occupante['palette']['border'] }} {{ $occupante['palette']['bg'] }} {{ $occupante['palette']['text'] }}">
<i class="fas {{ $occupante['palette']['icon'] }} mr-1"></i>{{ \Illuminate\Support\Str::limit($occupante['nome'], 60) }}
</span>
<span class="text-xxs text-slate-500">{{ $occupante['ruolo_label'] }}</span>
</div>
<div class="text-xxs text-right text-slate-500 space-y-1">
@if(!is_null($occupante['percentuale']))
<div>Quota {{ rtrim(rtrim(number_format((float) $occupante['percentuale'], 4, ',', '.'), '0'), ',') }}%</div>
@endif
@if($occupante['inizio'] || $occupante['fine'])
<div>
@if($occupante['inizio'] && $occupante['fine'])
{{ $occupante['inizio'] }} {{ $occupante['fine'] }}
@elseif($occupante['inizio'])
dal {{ $occupante['inizio'] }}
@else
fino al {{ $occupante['fine'] }}
@endif
</div>
@endif
</div>
</div>
@endforeach
@else
<p class="text-xs text-slate-500 italic">Nessun soggetto associato a questa unità.</p>
@endif
</div>
</div>
@endforeach
</div>
@else
<p class="text-sm text-slate-500">Nessuna unità su questo piano.</p>
@endif
</div>
</div>
@endforeach
</div>
</div>
@endforeach
</div>
</div>
</div>
@endforeach
@else
<div class="bg-white rounded-lg border border-gray-200 p-6 text-center text-slate-500">Nessuna palazzina o unità disponibili.</div>
@endif
</div>
</div>
<script>
function selectPalazzina(btn) {
const target = btn.getAttribute('data-target');
document.querySelectorAll('#palazzine-list button').forEach(b => {
b.classList.remove('bg-blue-100', 'border-blue-300');
b.classList.add('border-transparent');
});
btn.classList.add('bg-blue-100', 'border-blue-300');
document.querySelectorAll('.palazzina-panel').forEach(panel => panel.classList.add('hidden'));
const panelEl = document.querySelector(target);
if (panelEl) {
panelEl.classList.remove('hidden');
}
}
document.addEventListener('DOMContentLoaded', () => {
const wrapper = document.getElementById('palazzine-wrapper');
if (!wrapper) return;
const palId = wrapper.dataset.highlightPalazzinaId;
const palCode = wrapper.dataset.highlightPalazzinaCode;
const scala = wrapper.dataset.highlightScala;
const unitaId = wrapper.dataset.highlightUnita;
const interno = wrapper.dataset.highlightInterno;
if (palId || palCode) {
const btn = Array.from(document.querySelectorAll('#palazzine-list button')).find(b => {
return (palId && b.dataset.palazzinaId === palId) || (palCode && b.dataset.palazzinaCode === palCode);
});
if (btn) {
btn.click();
}
}
const cards = Array.from(document.querySelectorAll('.unit-card'));
const targetCard = cards.find(card => {
const matchScala = scala ? card.dataset.scala === scala : true;
const matchInterno = interno ? card.dataset.interno === interno : true;
const matchUnita = unitaId ? card.dataset.unitaId === unitaId : true;
return matchScala && matchInterno && matchUnita;
});
if (targetCard) {
targetCard.classList.add('ring', 'ring-2', 'ring-blue-500');
targetCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
// Implementazione Hover Spaziale Relazionale (Accanto / Sopra / Sotto)
cards.forEach(card => {
card.style.position = 'relative';
card.addEventListener('mouseenter', () => {
// Evidenzia unità attiva
card.classList.add('ring', 'ring-2', 'ring-blue-500', 'scale-[1.01]', 'z-10');
const currentFloorGrid = card.parentElement;
const currentFloor = currentFloorGrid.closest('.border.border-gray-200.rounded-lg');
if (!currentFloor) return;
// Individua i vicini sullo stesso piano (index in DOM)
const cardsOnFloor = Array.from(currentFloorGrid.querySelectorAll('.unit-card'));
const index = cardsOnFloor.indexOf(card);
// Vicino Sinistra
if (index > 0) {
highlightNeighbor(cardsOnFloor[index - 1], 'Fianco Sx', 'Accanto');
}
// Vicino Destra
if (index < cardsOnFloor.length - 1) {
highlightNeighbor(cardsOnFloor[index + 1], 'Fianco Dx', 'Accanto');
}
// Vicino Sopra (piano precedente nel DOM per ordinamento decrescente dei piani)
const floorAbove = currentFloor.previousElementSibling;
if (floorAbove && floorAbove.classList.contains('border')) {
const cardsAbove = Array.from(floorAbove.querySelectorAll('.unit-card'));
if (cardsAbove.length > 0) {
const targetAbove = cardsAbove[Math.min(index, cardsAbove.length - 1)];
highlightNeighbor(targetAbove, 'Sopra', 'Verticale');
}
}
// Vicino Sotto (piano successivo nel DOM)
const floorBelow = currentFloor.nextElementSibling;
if (floorBelow && floorBelow.classList.contains('border')) {
const cardsBelow = Array.from(floorBelow.querySelectorAll('.unit-card'));
if (cardsBelow.length > 0) {
const targetBelow = cardsBelow[Math.min(index, cardsBelow.length - 1)];
highlightNeighbor(targetBelow, 'Sotto', 'Verticale');
}
}
// Sfoca leggermente gli altri appartamenti non correlati per focalizzare lo sguardo
cards.forEach(c => {
if (c !== card && !c.classList.contains('ring-amber-500')) {
c.classList.add('opacity-40');
}
});
});
card.addEventListener('mouseleave', () => {
// Ripristina lo stato grafico originale
cards.forEach(c => {
c.classList.remove('ring', 'ring-2', 'ring-blue-500', 'ring-amber-500', 'scale-[1.01]', 'z-10', 'opacity-40');
const indicator = c.querySelector('.neighbor-indicator');
if (indicator) {
indicator.remove();
}
});
});
});
function highlightNeighbor(element, label, type) {
if (!element) return;
element.classList.add('ring', 'ring-2', 'ring-amber-500', 'z-10');
// Crea etichetta badge fluttuante
if (!element.querySelector('.neighbor-indicator')) {
const indicator = document.createElement('span');
indicator.className = 'neighbor-indicator absolute top-2 right-2 inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-semibold tracking-wider uppercase shadow-sm border ' +
(type === 'Verticale'
? 'bg-amber-100 text-amber-800 border-amber-300'
: 'bg-orange-100 text-orange-800 border-orange-300');
indicator.textContent = label;
element.appendChild(indicator);
}
}
});
function scrollFloor(btn, direction) {
const container = btn.parentElement.querySelector('.overflow-x-auto');
if (!container) return;
const cardWidth = 340; // 320px card + 20px gap
const scrollAmount = direction === 'left' ? -cardWidth : cardWidth;
container.scrollBy({ left: scrollAmount, behavior: 'smooth' });
}
</script>
</div>
</div>