netgescon-day0/resources/views/admin/tabelle-millesimali/index.blade.php

602 lines
28 KiB
PHP
Executable File

@extends('admin.layouts.netgescon')
@section('page-title', 'Tabelle Millesimali - NetGescon')
@push('head')
<meta name="csrf-token" content="{{ csrf_token() }}">
<style>
.draggable-row {
cursor: move;
}
.draggable-row:hover {
background-color: #f8f9fa;
}
.drag-handle {
cursor: grab;
color: #6c757d;
}
.drag-handle:active {
cursor: grabbing;
}
</style>
@endpush
@section('content')
@php
$selectedStabileId = request('stabile_id', $stabileId ?? null);
$stabileSelezionato = $stabili->firstWhere('id', $selectedStabileId);
@endphp
<div class="space-y-8 netgescon-fade-in">
<!-- Header -->
<div class="netgescon-card">
<div class="flex items-center justify-between">
<div>
<h2 class="netgescon-title">
<i class="fas fa-table text-green-500 mr-3"></i>
Tabelle Millesimali
</h2>
<p class="netgescon-text mt-2">
Gestione completa delle tabelle millesimali con ordinamento e voci di spesa
</p>
@if($stabileSelezionato)
<div class="mt-2 inline-flex items-center px-3 py-1 rounded-full bg-blue-50 border border-blue-200 text-sm text-blue-800">
<i class="fas fa-building mr-2"></i>
{{ $stabileSelezionato->denominazione ?? 'Stabile selezionato' }}
@if(!empty($stabileSelezionato->codice_interno))
<span class="ml-2 text-xs text-blue-600">(Cod {{ $stabileSelezionato->codice_interno }})</span>
@endif
</div>
@endif
</div>
<div class="flex space-x-3">
<button onclick="openCreateModal()" class="netgescon-btn netgescon-btn-primary">
<i class="fas fa-plus mr-2"></i>
Nuova Tabella
</button>
<button onclick="openOrderModal()" class="netgescon-btn netgescon-btn-secondary">
<i class="fas fa-sort mr-2"></i>
Riordina Tabelle
</button>
</div>
</div>
</div>
<!-- Filtri -->
<div class="netgescon-card">
<form method="GET" class="flex flex-col lg:flex-row gap-4">
<div class="flex-1">
<select name="stabile_id" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
<option value="">Tutti gli stabili</option>
@foreach($stabili as $stabile)
<option value="{{ $stabile->id }}" {{ $selectedStabileId == $stabile->id ? 'selected' : '' }}>
{{ $stabile->denominazione }}
</option>
@endforeach
</select>
</div>
<div class="flex-1">
<select name="tipo_tabella" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500">
<option value="">Tutti i tipi</option>
<option value="generali" {{ request('tipo_tabella') == 'generali' ? 'selected' : '' }}>Tabelle Generali (TAB.A)</option>
<option value="riscaldamento" {{ request('tipo_tabella') == 'riscaldamento' ? 'selected' : '' }}>Riscaldamento</option>
<option value="scale" {{ request('tipo_tabella') == 'scale' ? 'selected' : '' }}>Scale</option>
<option value="ascensore" {{ request('tipo_tabella') == 'ascensore' ? 'selected' : '' }}>Ascensore</option>
<option value="altro" {{ request('tipo_tabella') == 'altro' ? 'selected' : '' }}>Altro</option>
</select>
</div>
<div class="flex gap-2">
<button type="submit" class="netgescon-btn netgescon-btn-primary">
<i class="fas fa-search mr-2"></i>
Filtra
</button>
@if(request()->hasAny(['stabile_id', 'tipo_tabella']))
<a href="{{ route('admin.tabelle-millesimali.index') }}" class="netgescon-btn netgescon-btn-secondary">
<i class="fas fa-times mr-2"></i>
Reset
</a>
@endif
</div>
</form>
</div>
<!-- Layout a due colonne -->
<div class="grid grid-cols-1 xl:grid-cols-2 gap-8">
<!-- Colonna SX: Tabelle Millesimali -->
<div class="space-y-6">
<div class="netgescon-card">
<div class="netgescon-card-header">
<h3 class="netgescon-card-title">
<i class="fas fa-list mr-2"></i>
Tabelle Millesimali
<span class="text-sm font-normal text-gray-500 ml-2">
({{ $tabelle->count() }} tabelle)
</span>
</h3>
</div>
@if($tabelle->count() > 0)
<div class="space-y-2">
@php
$tabellePerTipo = $tabelle->groupBy('tipo_tabella');
$ordineVisualizazione = ['generali', 'riscaldamento', 'scale', 'ascensore', 'altro'];
@endphp
@foreach($ordineVisualizazione as $tipoTabella)
@if(isset($tabellePerTipo[$tipoTabella]))
<!-- Intestazione Tipo -->
<div class="bg-gray-50 px-4 py-2 border-l-4 border-l-blue-500">
<h4 class="font-semibold text-gray-800">
@switch($tipoTabella)
@case('generali')
<i class="fas fa-building mr-2 text-blue-600"></i>
Tabelle Generali (TAB.A - Proprietà)
@break
@case('riscaldamento')
<i class="fas fa-fire mr-2 text-red-600"></i>
Riscaldamento
@break
@case('scale')
<i class="fas fa-stairs mr-2 text-green-600"></i>
Scale
@break
@case('ascensore')
<i class="fas fa-elevator mr-2 text-purple-600"></i>
Ascensore
@break
@default
<i class="fas fa-cog mr-2 text-gray-600"></i>
Altro
@endswitch
</h4>
</div>
@foreach($tabellePerTipo[$tipoTabella]->sortBy(function($tabella){ return $tabella->nord ?? $tabella->ordinamento ?? 999999; }) as $tabella)
<div class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow cursor-pointer tabella-item {{ $loop->first && $loop->parent->first ? 'bg-blue-50 border-blue-300' : '' }}"
onclick="selectTabella({{ $tabella->id }})"
data-tabella-id="{{ $tabella->id }}">
<div class="flex items-center justify-between">
<div class="flex-1">
<div class="flex items-center">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
@if($tabella->stato_tabella == 'attiva') bg-green-100 text-green-800
@elseif($tabella->stato_tabella == 'sospesa') bg-yellow-100 text-yellow-800
@else bg-gray-100 text-gray-800 @endif mr-3">
{{ $tabella->codice_tabella ?? 'N/D' }}
</span>
<div>
<h5 class="font-medium text-gray-900">
{{ $tabella->nome_tabella ?? $tabella->denominazione }}
</h5>
<p class="text-sm text-gray-500">
{{ $tabella->stabile->denominazione ?? 'N/D' }}
@if($tabella->totale_millesimi)
| Tot. {{ number_format($tabella->totale_millesimi, 2) }}
@endif
</p>
</div>
</div>
</div>
<div class="flex items-center space-x-2">
<span class="text-xs text-gray-400">#{{ $tabella->ordinamento ?? 0 }}</span>
<div class="flex space-x-1">
<button onclick="event.stopPropagation(); editTabella({{ $tabella->id }})"
class="text-blue-600 hover:text-blue-800" title="Modifica">
<i class="fas fa-edit text-sm"></i>
</button>
<button onclick="event.stopPropagation(); configuraMillesimi({{ $tabella->id }})"
class="text-green-600 hover:text-green-800" title="Configura Millesimi">
<i class="fas fa-calculator text-sm"></i>
</button>
</div>
</div>
</div>
</div>
@endforeach
@endif
@endforeach
</div>
@else
<div class="text-center py-12">
<div class="text-gray-500">
<i class="fas fa-table text-4xl mb-4 opacity-50"></i>
<p class="text-lg font-medium">Nessuna tabella millesimale trovata</p>
<p class="text-sm mt-2">Crea la prima tabella per iniziare</p>
</div>
</div>
@endif
</div>
</div>
<!-- Colonna DX: Voci di Spesa -->
<div class="space-y-6">
<div class="netgescon-card">
<div class="netgescon-card-header">
<h3 class="netgescon-card-title">
<i class="fas fa-receipt mr-2"></i>
Voci di Spesa Collegate
<span id="tabella-selezionata-nome" class="text-sm font-normal text-gray-500 ml-2">
(Seleziona una tabella)
</span>
</h3>
</div>
<div id="voci-spesa-container">
<div class="text-center py-12">
<div class="text-gray-500">
<i class="fas fa-mouse-pointer text-4xl mb-4 opacity-50"></i>
<p class="text-lg font-medium">Seleziona una tabella</p>
<p class="text-sm mt-2">Le voci di spesa collegate verranno visualizzate qui</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal Crea Tabella -->
<div id="createTabellaModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full hidden">
<div class="relative top-20 mx-auto p-5 border w-11/12 md:w-2/3 lg:w-1/2 shadow-lg rounded-md bg-white">
<div class="mt-3">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-medium text-gray-900">Crea Nuova Tabella Millesimale</h3>
<button onclick="closeCreateModal()" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times"></i>
</button>
</div>
<form id="createTabellaForm" method="POST" action="{{ route('admin.tabelle-millesimali.store') }}">
@csrf
<div class="space-y-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700">Stabile</label>
<select name="stabile_id" required class="mt-1 block w-full border-gray-300 rounded-md shadow-sm">
<option value="">Seleziona stabile...</option>
@foreach($stabili as $stabile)
<option value="{{ $stabile->id }}">{{ $stabile->denominazione }}</option>
@endforeach
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Tipo Tabella</label>
<select name="tipo_tabella" required class="mt-1 block w-full border-gray-300 rounded-md shadow-sm">
<option value="generali">Tabelle Generali (TAB.A)</option>
<option value="riscaldamento">Riscaldamento</option>
<option value="scale">Scale</option>
<option value="ascensore">Ascensore</option>
<option value="altro">Altro</option>
</select>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700">Codice Tabella</label>
<input type="text" name="codice_tabella" placeholder="es. TAB.A, TAB.B..."
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Ordinamento</label>
<input type="number" name="ordinamento" min="1" value="1"
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm">
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Nome Tabella</label>
<input type="text" name="nome_tabella" required
placeholder="es. Spese Generali, Riscaldamento Scala A..."
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm">
</div>
<div>
<label class="block text-sm font-medium text-gray-700">Descrizione</label>
<textarea name="descrizione" rows="3"
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm"></textarea>
</div>
</div>
<div class="flex justify-end space-x-2 mt-6">
<button type="button" onclick="closeCreateModal()"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-200 rounded-md hover:bg-gray-300">
Annulla
</button>
<button type="submit"
class="px-4 py-2 text-sm font-medium text-white bg-green-600 rounded-md hover:bg-green-700">
Crea Tabella
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modal Riordina Tabelle -->
<div id="orderModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full hidden">
<div class="relative top-20 mx-auto p-5 border w-11/12 md:w-2/3 shadow-lg rounded-md bg-white">
<div class="mt-3">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-medium text-gray-900">Riordina Tabelle Millesimali</h3>
<button onclick="closeOrderModal()" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times"></i>
</button>
</div>
<div class="mb-4">
<p class="text-sm text-gray-600">
Trascina le tabelle per riordinarle. L'ordine influisce sulla visualizzazione e sui report.
</p>
</div>
<div id="sortable-tabelle" class="space-y-2 max-h-96 overflow-y-auto">
<!-- Le tabelle verranno caricate qui via JavaScript -->
</div>
<div class="flex justify-end space-x-2 mt-6">
<button type="button" onclick="closeOrderModal()"
class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-200 rounded-md hover:bg-gray-300">
Annulla
</button>
<button onclick="saveOrder()"
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700">
Salva Ordinamento
</button>
</div>
</div>
</div>
</div>
<script>
let tabellaSelezionata = null;
let vociCache = [];
let vociSearch = '';
let vociSortField = 'codice';
let vociSortDir = 'asc';
function renderVociSpesa() {
const container = document.getElementById('voci-spesa-container');
const search = (vociSearch || '').toLowerCase();
const filtered = vociCache
.filter(voce => {
if (!search) return true;
return (voce.codice || '').toLowerCase().includes(search) || (voce.descrizione || '').toLowerCase().includes(search);
})
.sort((a, b) => {
const dir = vociSortDir === 'asc' ? 1 : -1;
if (vociSortField === 'importo') {
const va = parseFloat(a.importo) || 0;
const vb = parseFloat(b.importo) || 0;
return va === vb ? 0 : (va > vb ? dir : -dir);
}
const va = (a[vociSortField] || '').toString().toLowerCase();
const vb = (b[vociSortField] || '').toString().toLowerCase();
if (va === vb) return 0;
return va > vb ? dir : -dir;
});
let html = `
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-2 mb-3">
<input id="voci-search" type="text" placeholder="Cerca per codice o descrizione" value="${vociSearch}" class="w-full md:w-1/2 px-3 py-2 border border-gray-300 rounded" />
<div class="flex items-center gap-2 text-sm text-gray-700">
<span>Ordina per</span>
<select id="voci-sort-field" class="px-2 py-1 border border-gray-300 rounded">
<option value="codice" ${vociSortField === 'codice' ? 'selected' : ''}>Codice</option>
<option value="descrizione" ${vociSortField === 'descrizione' ? 'selected' : ''}>Descrizione</option>
<option value="importo" ${vociSortField === 'importo' ? 'selected' : ''}>Importo</option>
</select>
<button id="voci-sort-dir" class="px-2 py-1 border border-gray-300 rounded bg-white" title="Inverti ordine">${vociSortDir === 'asc' ? 'ASC' : 'DESC'}</button>
</div>
</div>
`;
if (filtered.length > 0) {
html += '<div class="space-y-3">';
filtered.forEach(voce => {
html += `
<div class="border border-gray-200 rounded-lg p-4">
<div class="flex items-center justify-between">
<div>
<h5 class="font-medium text-gray-900">${voce.descrizione}</h5>
<p class="text-sm text-gray-500">
Codice: ${voce.codice || 'N/D'} |
Importo: €${voce.importo ? parseFloat(voce.importo).toFixed(2) : '0.00'}
</p>
</div>
<div class="flex space-x-2">
<button onclick="editVoce(${voce.id})" class="text-blue-600 hover:text-blue-800">
<i class="fas fa-edit"></i>
</button>
<button onclick="deleteVoce(${voce.id})" class="text-red-600 hover:text-red-800">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
</div>
`;
});
html += '</div>';
html += `
<div class="mt-4 pt-4 border-t border-gray-200">
<button onclick="addVoce(${tabellaSelezionata})" class="netgescon-btn netgescon-btn-primary">
<i class="fas fa-plus mr-2"></i>
Aggiungi Voce
</button>
</div>
`;
} else {
html += `
<div class="text-center py-12">
<div class="text-gray-500">
<i class="fas fa-receipt text-4xl mb-4 opacity-50"></i>
<p class="text-lg font-medium">Nessuna voce di spesa</p>
<p class="text-sm mt-2">Aggiungi la prima voce per questa tabella</p>
<button onclick="addVoce(${tabellaSelezionata})" class="netgescon-btn netgescon-btn-primary mt-4">
<i class="fas fa-plus mr-2"></i>
Aggiungi Voce
</button>
</div>
</div>
`;
}
container.innerHTML = html;
const searchInput = document.getElementById('voci-search');
if (searchInput) {
searchInput.addEventListener('input', (e) => {
vociSearch = e.target.value;
renderVociSpesa();
});
}
const sortFieldSelect = document.getElementById('voci-sort-field');
if (sortFieldSelect) {
sortFieldSelect.addEventListener('change', (e) => {
vociSortField = e.target.value;
renderVociSpesa();
});
}
const sortDirBtn = document.getElementById('voci-sort-dir');
if (sortDirBtn) {
sortDirBtn.addEventListener('click', () => {
vociSortDir = vociSortDir === 'asc' ? 'desc' : 'asc';
renderVociSpesa();
});
}
}
// Seleziona una tabella e carica le voci
function selectTabella(tabellaId) {
// Rimuovi selezione precedente
document.querySelectorAll('.tabella-item').forEach(item => {
item.classList.remove('bg-blue-50', 'border-blue-300');
item.classList.add('border-gray-200');
});
// Evidenzia tabella selezionata
const selectedItem = document.querySelector(`[data-tabella-id="${tabellaId}"]`);
if (selectedItem) {
selectedItem.classList.add('bg-blue-50', 'border-blue-300');
selectedItem.classList.remove('border-gray-200');
}
tabellaSelezionata = tabellaId;
caricaVociSpesa(tabellaId);
}
// Carica le voci di spesa per una tabella
function caricaVociSpesa(tabellaId) {
const container = document.getElementById('voci-spesa-container');
const nomeTabella = document.getElementById('tabella-selezionata-nome');
// Mostra loading
container.innerHTML = `
<div class="text-center py-12">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-green-500 mx-auto mb-4"></div>
<p class="text-gray-500">Caricamento voci di spesa...</p>
</div>
`;
fetch(`/admin/tabelle-millesimali/${tabellaId}/voci-spesa`)
.then(response => response.json())
.then(data => {
if (data.tabella) {
nomeTabella.textContent = `(${data.tabella.nome_tabella})`;
}
vociCache = data.voci || [];
vociSearch = '';
vociSortField = 'codice';
vociSortDir = 'asc';
renderVociSpesa();
})
.catch(error => {
console.error('Errore nel caricamento delle voci:', error);
container.innerHTML = `
<div class="text-center py-12">
<div class="text-red-500">
<i class="fas fa-exclamation-triangle text-4xl mb-4 opacity-50"></i>
<p class="text-lg font-medium">Errore nel caricamento</p>
<p class="text-sm mt-2">Riprova più tardi</p>
</div>
</div>
`;
});
}
// Modal functions
function openCreateModal() {
document.getElementById('createTabellaModal').classList.remove('hidden');
}
function closeCreateModal() {
document.getElementById('createTabellaModal').classList.add('hidden');
}
function openOrderModal() {
document.getElementById('orderModal').classList.remove('hidden');
loadTabelleForOrdering();
}
function closeOrderModal() {
document.getElementById('orderModal').classList.add('hidden');
}
// Carica tabelle per il riordinamento
function loadTabelleForOrdering() {
const container = document.getElementById('sortable-tabelle');
// Implementazione del caricamento e drag & drop
container.innerHTML = '<p class="text-center text-gray-500">Funzionalità in sviluppo...</p>';
}
// Altre funzioni
function editTabella(id) {
// Implementa modifica tabella
alert('Modifica tabella ' + id + ' - Funzionalità in sviluppo');
}
function configuraMillesimi(id) {
window.location.href = `/admin/tabelle-millesimali/${id}/configura-millesimi`;
}
function addVoce(tabellaId) {
// Implementa aggiunta voce
alert('Aggiungi voce per tabella ' + tabellaId + ' - Funzionalità in sviluppo');
}
function editVoce(id) {
alert('Modifica voce ' + id + ' - Funzionalità in sviluppo');
}
function deleteVoce(id) {
if (confirm('Sei sicuro di voler eliminare questa voce?')) {
// Implementa eliminazione
alert('Elimina voce ' + id + ' - Funzionalità in sviluppo');
}
}
// Inizializza la prima tabella se disponibile
document.addEventListener('DOMContentLoaded', function() {
const primaTabella = document.querySelector('.tabella-item');
if (primaTabella) {
const tabellaId = primaTabella.getAttribute('data-tabella-id');
selectTabella(tabellaId);
}
});
// Chiudi modal cliccando fuori
document.addEventListener('click', function(event) {
const modals = ['createTabellaModal', 'orderModal'];
modals.forEach(modalId => {
const modal = document.getElementById(modalId);
if (event.target === modal) {
modal.classList.add('hidden');
}
});
});
</script>
@endsection