setup viste

This commit is contained in:
2026-06-10 14:03:43 +02:00
parent 0dd567cf7a
commit 43a2c81312
17 changed files with 1306 additions and 582 deletions
+36 -9
View File
@@ -5,6 +5,9 @@
@php
$canWriteDocumenti = Auth::user()->canManage('documenti');
$canDeleteDocumenti = Auth::user()->canDelete('documenti');
$vistaDefaultJson = isset($vista) ? $vista->toJson() : 'null';
$visibleColumnsJson = json_encode($visibleColumns ?? []);
$tableColumnsJson = json_encode($tableColumns ?? []);
@endphp
@section('css')
@@ -293,21 +296,21 @@ $canDeleteDocumenti = Auth::user()->canDelete('documenti');
{{-- View: Lista --}}
<div id="viewList" style="display:none;">
@if($sottoCartelle->count() > 0 || $documenti->count() > 0)
<table class="table table-bordered table-hover table-striped mb-0">
<table class="table table-bordered table-hover table-striped mb-0" id="documenti-table">
<thead class="thead-light">
<tr>
@if($canDeleteDocumenti)
<th style="width: 40px;">
<th style="width: 40px;" data-column="select">
<input type="checkbox" id="selectAll" onchange="toggleAll(this)">
</th>
@endif
<th>Nome</th>
<th style="width: 100px;">Tipologia</th>
<th style="width: 90px;">Dimensione</th>
<th style="width: 120px;">Contesto</th>
<th style="width: 100px;">Tag</th>
<th style="width: 110px;">Data</th>
<th style="width: 130px;">Azioni</th>
<th data-column="nome">Nome</th>
<th style="width: 100px;" data-column="tipologia">Tipologia</th>
<th style="width: 90px;" data-column="dimensione">Dimensione</th>
<th style="width: 120px;" data-column="visibilita">Contesto</th>
<th style="width: 100px;" data-column="tag">Tag</th>
<th style="width: 110px;" data-column="created_at">Data</th>
<th style="width: 130px;" data-column="azioni">Azioni</th>
</tr>
</thead>
<tbody>
@@ -515,6 +518,10 @@ $canDeleteDocumenti = Auth::user()->canDelete('documenti');
</div>
</form>{{-- /massForm --}}
@include('partials.table-settings')
<div id="vista-data" style="display:none;">{{ $vistaDefaultJson }}</div>
{{-- MODAL: Nuova Cartella --}}
<div class="modal fade" id="newFolderModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-dialog-scrollable" role="document">
@@ -938,6 +945,7 @@ $canDeleteDocumenti = Auth::user()->canDelete('documenti');
@endsection
@section('scripts')
<script src="{{ asset('js/column-manager.js') }}"></script>
<script>
const individui = @json($individui->map(fn($i) => ['id' => $i->id, 'label' => $i->cognome . ' ' . $i->nome]));
const gruppi = @json($gruppi->map(fn($g) => ['id' => $g->id, 'label' => $g->nome]));
@@ -1941,5 +1949,24 @@ function deleteFolder(id, nome) {
}
});
}
document.addEventListener('DOMContentLoaded', function () {
const table = document.getElementById('documenti-table');
if (table) {
window.columnManager = new ColumnManager('documenti-table', {
entityType: 'documenti',
storageKey: 'cm_documenti',
resizable: true,
reorderable: false,
});
initTableSettings();
}
});
window.printWithCurrentSettings = function () {
if (window.columnManager) {
window.columnManager.printWithSettings();
}
};
</script>
@endsection
+74 -11
View File
@@ -5,6 +5,10 @@
@php
$canWriteEventi = Auth::user()->canManage('eventi');
$canDeleteEventi = Auth::user()->canDelete('eventi');
$vistaDefaultJson = isset($vista) ? $vista->toJson() : 'null';
$visibleColumnsJson = json_encode($visibleColumns ?? []);
$tableColumnsJson = json_encode($tableColumns ?? []);
$entityType = $entityType ?? 'eventi';
@endphp
@section('content')
@@ -20,6 +24,9 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
<div class="card-header">
<h3 class="card-title">Tutti gli Eventi</h3>
<div class="card-tools">
<button type="button" class="btn btn-sm btn-secondary mr-1" onclick="$('#table-settings-modal').modal('show')" title="Impostazioni tabella">
<i class="fas fa-cog"></i>
</button>
<a href="/eventi/calendar" class="btn btn-sm btn-outline-primary mr-2">
<i class="fas fa-calendar-alt mr-1"></i> Calendario
</a>
@@ -75,10 +82,10 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
</div>
</form>
<table class="table table-bordered table-hover">
<table class="table table-bordered table-hover" id="eventi-table">
<thead class="thead-light">
<tr>
<th style="width: 40px;">
<th style="width: 40px;" data-column="select">
@if($canDeleteEventi)
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="select-all" onchange="toggleSelectAll(this)">
@@ -86,7 +93,8 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
</div>
@endif
</th>
<th>
@if(in_array('nome_evento', $visibleColumns))
<th data-column="nome_evento" data-sortable="true">
<a href="{{ request()->fullUrlWithQuery(['sort' => 'nome_evento', 'direction' => request('sort') === 'nome_evento' && request('direction') === 'asc' ? 'desc' : 'asc']) }}" class="text-dark">
Nome Evento
@if(request('sort') === 'nome_evento')
@@ -96,7 +104,9 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
@endif
</a>
</th>
<th>
@endif
@if(in_array('descrizione_evento', $visibleColumns))
<th data-column="descrizione_evento" data-sortable="true">
<a href="{{ request()->fullUrlWithQuery(['sort' => 'descrizione_evento', 'direction' => request('sort') === 'descrizione_evento' && request('direction') === 'asc' ? 'desc' : 'asc']) }}" class="text-dark">
Descrizione
@if(request('sort') === 'descrizione_evento')
@@ -106,7 +116,9 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
@endif
</a>
</th>
<th>
@endif
@if(in_array('tipo_evento', $visibleColumns))
<th data-column="tipo_evento" data-sortable="true">
<a href="{{ request()->fullUrlWithQuery(['sort' => 'tipo_evento', 'direction' => request('sort') === 'tipo_evento' && request('direction') === 'asc' ? 'desc' : 'asc']) }}" class="text-dark">
Tipologia
@if(request('sort') === 'tipo_evento')
@@ -116,7 +128,9 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
@endif
</a>
</th>
<th>
@endif
@if(in_array('tipo_recorrenza', $visibleColumns))
<th data-column="tipo_recorrenza" data-sortable="true">
<a href="{{ request()->fullUrlWithQuery(['sort' => 'tipo_recorrenza', 'direction' => request('sort') === 'tipo_recorrenza' && request('direction') === 'asc' ? 'desc' : 'asc']) }}" class="text-dark">
Ricorrenza
@if(request('sort') === 'tipo_recorrenza')
@@ -126,7 +140,9 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
@endif
</a>
</th>
<th>
@endif
@if(in_array('gruppi', $visibleColumns))
<th data-column="gruppi">
<a href="{{ request()->fullUrlWithQuery(['sort' => 'gruppi', 'direction' => request('sort') === 'gruppi' && request('direction') === 'asc' ? 'desc' : 'asc']) }}" class="text-dark">
Gruppi
@if(request('sort') === 'gruppi')
@@ -136,7 +152,9 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
@endif
</a>
</th>
<th>
@endif
@if(in_array('responsabili', $visibleColumns))
<th data-column="responsabili">
<a href="{{ request()->fullUrlWithQuery(['sort' => 'responsabili', 'direction' => request('sort') === 'responsabili' && request('direction') === 'asc' ? 'desc' : 'asc']) }}" class="text-dark">
Responsabili
@if(request('sort') === 'responsabili')
@@ -146,8 +164,12 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
@endif
</a>
</th>
<th>Tag</th>
<th style="width: 120px;">
@endif
@if(in_array('tag', $visibleColumns))
<th data-column="tag">Tag</th>
@endif
@if(in_array('created_at', $visibleColumns))
<th style="width: 120px;" data-column="created_at">
<a href="{{ request()->fullUrlWithQuery(['sort' => 'created_at', 'direction' => request('sort') === 'created_at' && request('direction') === 'asc' ? 'desc' : 'asc']) }}" class="text-dark">
Creato
@if(request('sort') === 'created_at')
@@ -157,6 +179,7 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
@endif
</a>
</th>
@endif
<th style="width: 120px;">Azioni</th>
</tr>
</thead>
@@ -171,6 +194,7 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
</div>
@endif
</td>
@if(in_array('nome_evento', $visibleColumns))
<td>
<a href="/eventi/{{ $evento->id }}">
<i class="fas fa-calendar mr-1 text-primary"></i>
@@ -180,7 +204,11 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
<span class="badge badge-success ml-1">Incluso</span>
@endif
</td>
@endif
@if(in_array('descrizione_evento', $visibleColumns))
<td>{{ Str::limit($evento->descrizione_evento, 50) ?: '-' }}</td>
@endif
@if(in_array('tipo_evento', $visibleColumns))
<td>
@if($evento->tipo_evento)
@php
@@ -195,6 +223,8 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
<span class="text-muted">-</span>
@endif
</td>
@endif
@if(in_array('tipo_recorrenza', $visibleColumns))
<td>
@if($evento->tipo_recorrenza && $evento->tipo_recorrenza !== 'singolo')
<span class="badge badge-info">{{ $evento->periodicita_label }}</span>
@@ -202,6 +232,8 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
<span class="badge badge-secondary">Singolo</span>
@endif
</td>
@endif
@if(in_array('gruppi', $visibleColumns))
<td>
@forelse($evento->gruppi->take(2) as $gruppo)
<a href="/gruppi/{{ $gruppo->id }}">
@@ -214,6 +246,8 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
<small class="text-muted">+{{ $evento->gruppi->count() - 2 }}</small>
@endif
</td>
@endif
@if(in_array('responsabili', $visibleColumns))
<td>
@forelse($evento->responsabili->take(2) as $resp)
<small>{{ $resp->nome_completo }}</small><br>
@@ -224,12 +258,17 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
<small class="text-muted">+{{ $evento->responsabili->count() - 2 }} altri</small>
@endif
</td>
@endif
@if(in_array('tag', $visibleColumns))
<td>
@foreach($evento->tags as $tag)
<a href="{{ route('eventi.index', ['tag[]' => $tag->slug]) }}" class="badge" style="background-color: {{ $tag->color ?? '#6c757d' }}; color: #fff;">{{ $tag->name }}</a>
@endforeach
</td>
@endif
@if(in_array('created_at', $visibleColumns))
<td><small class="text-muted">{{ $evento->created_at->format('d/m/Y') }}</small></td>
@endif
<td>
<a href="{{ url('/eventi/' . $evento->id) }}" class="btn btn-xs btn-info" title="Visualizza">
<i class="fas fa-eye"></i>
@@ -249,7 +288,7 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
</tr>
@empty
<tr>
<td colspan="10" class="text-center text-muted py-4">
<td colspan="20" class="text-center text-muted py-4">
<i class="fas fa-calendar fa-2x mb-2"></i>
<p class="mb-0">Nessun evento trovato</p>
</td>
@@ -284,6 +323,10 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
</div>
</div>
@include('partials.table-settings')
<div id="vista-data" style="display:none;">{{ $vistaDefaultJson }}</div>
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog">
@if($canDeleteEventi)
<div class="modal-dialog" role="document">
@@ -309,6 +352,7 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
@endsection
@section('scripts')
<script src="{{ asset('js/column-manager.js') }}"></script>
<script>
function toggleSelectAll(source) {
document.querySelectorAll('.row-checkbox').forEach(cb => cb.checked = source.checked);
@@ -374,5 +418,24 @@ function exportSelectedIcs() {
document.body.appendChild(form);
form.submit();
}
document.addEventListener('DOMContentLoaded', function () {
const table = document.getElementById('eventi-table');
if (table) {
window.columnManager = new ColumnManager('eventi-table', {
entityType: 'eventi',
storageKey: 'cm_eventi',
resizable: true,
reorderable: true,
});
initTableSettings();
}
});
window.printWithCurrentSettings = function () {
if (window.columnManager) {
window.columnManager.printWithSettings();
}
};
</script>
@endsection
+50 -149
View File
@@ -22,6 +22,7 @@ $columnLabels = [
$vistaDefaultJson = $vista ? $vista->toJson() : 'null';
$allColumnsJson = json_encode($allColumns);
$visibleColumnsJson = json_encode($visibleColumns);
$entityType = $entityType ?? 'gruppi';
@endphp
@section('content')
@@ -44,6 +45,9 @@ $visibleColumnsJson = json_encode($visibleColumns);
<div class="card-header">
<h3 class="card-title"><i class="fas fa-sitemap mr-2"></i>Elenco Gruppi</h3>
<div class="card-tools">
<button type="button" class="btn btn-sm btn-secondary mr-1" onclick="$('#table-settings-modal').modal('show')" title="Impostazioni tabella">
<i class="fas fa-cog"></i>
</button>
<div class="btn-group mr-2">
<button type="button" class="btn btn-sm btn-outline-primary {{ $viewMode === 'table' ? 'active' : '' }}" onclick="switchView('table')" id="btn-table-view">
<i class="fas fa-table mr-1"></i> Tabella
@@ -68,9 +72,7 @@ $visibleColumnsJson = json_encode($visibleColumns);
@endif
</div>
</div>
<button type="button" class="btn btn-primary btn-sm mr-1" onclick="showSaveVistaModal()">
<i class="fas fa-save mr-1"></i> Salva Vista
</button>
@if($canWriteGruppi)
<a href="{{ route('gruppi.import') }}" class="btn btn-warning btn-sm mr-1">
<i class="fas fa-file-import mr-1"></i> Importa
@@ -84,54 +86,58 @@ $visibleColumnsJson = json_encode($visibleColumns);
@include('partials._tag-filter-bar', ['filterTags' => $allTags])
@include('partials.table-settings')
<div id="vista-data" style="display:none;">{{ $vistaDefaultJson }}</div>
@if($viewMode === 'table')
<div class="card-body p-0">
<table class="table table-bordered table-hover table-striped mb-0" id="gruppi-table">
<thead class="thead-light">
<tr>
<th style="width: 40px;">
<th style="width: 40px;" data-column="select">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="select-all" onchange="toggleSelectAll(this)">
<label class="custom-control-label" for="select-all"></label>
</div>
</th>
@if(in_array('nome', $visibleColumns))
<th data-sortable="true" onclick="sortTable('nome')" style="cursor:pointer;">Nome <i class="fas fa-sort float-right text-muted"></i></th>
<th data-sortable="true" data-column="nome" onclick="sortTable('nome')" style="cursor:pointer;">Nome <i class="fas fa-sort float-right text-muted"></i></th>
@endif
@if(in_array('descrizione', $visibleColumns))
<th data-sortable="true" onclick="sortTable('descrizione')" style="cursor:pointer;">Descrizione <i class="fas fa-sort float-right text-muted"></i></th>
<th data-sortable="true" data-column="descrizione" onclick="sortTable('descrizione')" style="cursor:pointer;">Descrizione <i class="fas fa-sort float-right text-muted"></i></th>
@endif
@if(in_array('diocesi', $visibleColumns))
<th>Diocesi</th>
<th data-column="diocesi">Diocesi</th>
@endif
@if(in_array('livello', $visibleColumns))
<th style="width: 80px;">Livello</th>
<th style="width: 80px;" data-column="livello">Livello</th>
@endif
@if(in_array('padre', $visibleColumns))
<th>Gruppo Padre</th>
<th data-column="padre">Gruppo Padre</th>
@endif
@if(in_array('membri', $visibleColumns))
<th style="width: 100px;">Membri</th>
<th style="width: 100px;" data-column="membri">Membri</th>
@endif
@if(in_array('responsabili', $visibleColumns))
<th>Responsabili</th>
<th data-column="responsabili">Responsabili</th>
@endif
@if(in_array('indirizzo', $visibleColumns))
<th>Indirizzo</th>
<th data-column="indirizzo">Indirizzo</th>
@endif
@if(in_array('citta', $visibleColumns))
<th>Città</th>
<th data-column="citta">Città</th>
@endif
@if(in_array('telefono', $visibleColumns))
<th>Telefono</th>
<th data-column="telefono">Telefono</th>
@endif
@if(in_array('email', $visibleColumns))
<th>Email</th>
<th data-column="email">Email</th>
@endif
@if(in_array('tag', $visibleColumns))
<th>Tag</th>
<th data-column="tag">Tag</th>
@endif
<th style="width: 120px;">Azioni</th>
<th style="width: 120px;" data-column="azioni">Azioni</th>
</tr>
</thead>
<tbody id="table-body">
@@ -263,93 +269,7 @@ $visibleColumnsJson = json_encode($visibleColumns);
@endif
</div>
<div class="modal fade" id="saveVistaModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-save mr-2"></i>Salva Vista</h5>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Nome Vista *</label>
<input type="text" id="vista-nome" class="form-control" placeholder="Es. Elenco gruppi attivi">
</div>
<div class="form-group">
<label>Colonne visibili</label>
<div class="row" id="colonne-checkboxes">
@foreach($allColumns as $col)
<div class="col-md-4 mb-2">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input col-checkbox" id="col-{{ $col }}" value="{{ $col }}" {{ in_array($col, $visibleColumns) ? 'checked' : '' }}>
<label class="custom-control-label" for="col-{{ $col }}">{{ $columnLabels[$col] ?? $col }}</label>
</div>
</div>
@endforeach
</div>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="vista-default">
<label class="custom-control-label" for="vista-default">Imposta come vista predefinita</label>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annulla</button>
<button type="button" class="btn btn-primary" onclick="saveVista()">
<i class="fas fa-save mr-1"></i> Salva
</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="vistaListModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-bookmark mr-2"></i>Viste Salvate</h5>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
@php
$visteSalvate = \App\Models\VistaReport::where('user_id', Auth::id())
->where('tipo', 'gruppi')
->orderBy('is_default', 'desc')
->orderBy('nome')
->get();
@endphp
@if($visteSalvate->count() > 0)
<div class="list-group">
@foreach($visteSalvate as $v)
<div class="list-group-item d-flex justify-content-between align-items-center">
<div>
<a href="?vista_id={{ $v->id }}">{{ $v->nome }}</a>
@if($v->is_default)
<span class="badge badge-success ml-1">Predefinita</span>
@endif
</div>
<form action="{{ route('gruppi.delete-vista', $v->id) }}" method="POST" class="d-inline">
@csrf @method('DELETE')
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questa vista?')">
<i class="fas fa-trash"></i>
</button>
</form>
</div>
@endforeach
</div>
@else
<p class="text-muted text-center">Nessuna vista salvata</p>
@endif
</div>
</div>
</div>
</div>
{{-- MODAL: Gestione Tag massiva --}}
<div class="modal fade" id="massTagModal" tabindex="-1" role="dialog">
@@ -415,6 +335,7 @@ $visibleColumnsJson = json_encode($visibleColumns);
@endsection
@section('scripts')
<script src="{{ asset('js/column-manager.js') }}"></script>
<script>
function switchView(mode) {
window.location.href = '?view=' + mode;
@@ -532,48 +453,7 @@ function getColumnIndex(column) {
return 1;
}
function showSaveVistaModal() {
document.getElementById('vista-nome').value = '';
document.getElementById('vista-default').checked = false;
$('#saveVistaModal').modal('show');
}
function saveVista() {
const nome = document.getElementById('vista-nome').value.trim();
if (!nome) {
alert('Inserisci un nome per la vista');
return;
}
const colonneVisibili = Array.from(document.querySelectorAll('.col-checkbox:checked')).map(cb => cb.value);
const isDefault = document.getElementById('vista-default').checked;
fetch('/gruppi/save-vista', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
nome: nome,
colonne_visibili: colonneVisibili,
is_default: isDefault,
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
$('#saveVistaModal').modal('hide');
window.location.reload();
} else {
alert('Errore nel salvataggio della vista');
}
})
.catch(error => {
alert('Errore: ' + error.message);
});
}
</script>
<style>
.tree-item { margin-bottom: 5px; }
@@ -599,10 +479,31 @@ function saveVista() {
font-size: 0.7em;
padding: 2px 5px;
}
.btn-group .btn.active {
background-color: #007bff;
color: white;
border-color: #007bff;
}
.btn-group .btn.active {
background-color: #007bff;
color: white;
border-color: #007bff;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
const table = document.getElementById('gruppi-table');
if (table) {
window.columnManager = new ColumnManager('gruppi-table', {
entityType: 'gruppi',
storageKey: 'cm_gruppi',
resizable: true,
reorderable: true,
});
initTableSettings();
}
});
window.printWithCurrentSettings = function () {
if (window.columnManager) {
window.columnManager.printWithSettings();
}
};
</script>
@endsection
+108 -143
View File
@@ -16,6 +16,7 @@ $allColumnsJson = json_encode($allColumns);
$visibleColumnsJson = json_encode($visibleColumns);
$canWriteIndividui = Auth::user()->canManage('individui');
$canDeleteIndividui = Auth::user()->canDelete('individui');
$entityType = $entityType ?? 'individui';
@endphp
@section('content')
@@ -23,6 +24,9 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
<div class="card-header">
<h3 class="card-title"><i class="fas fa-users mr-2"></i>Lista Individui</h3>
<div class="card-tools">
<button type="button" class="btn btn-secondary btn-sm mr-1" onclick="$('#table-settings-modal').modal('show')" title="Impostazioni tabella">
<i class="fas fa-cog"></i>
</button>
<button type="button" class="btn btn-info btn-sm mr-1" onclick="toggleSearchPanel()">
<i class="fas fa-search mr-1"></i> Ricerca/Filtri
</button>
@@ -61,9 +65,6 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
@endif
</div>
</div>
<button type="button" class="btn btn-primary btn-sm" onclick="showSaveVistaModal()">
<i class="fas fa-save mr-1"></i> Salva Vista
</button>
@if($canWriteIndividui)
<a href="{{ route('individui.import') }}" class="btn btn-warning btn-sm ml-2">
<i class="fas fa-file-import mr-1"></i> Importa
@@ -133,12 +134,16 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
@include('partials._tag-filter-bar', ['filterTags' => $allTags])
@include('partials.table-settings')
<div id="vista-data" style="display:none;">{{ $vistaDefaultJson }}</div>
<div class="card-body p-0">
@if($individui->count() > 0)
<table class="table table-bordered table-hover table-striped mb-0" id="individui-table">
<thead class="thead-light">
<tr>
<th style="width: 40px;">
<th style="width: 40px;" data-column="select">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="select-all" onchange="toggleSelectAll(this)">
<label class="custom-control-label" for="select-all"></label>
@@ -153,6 +158,39 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
@if(in_array('nome', $visibleColumns))
<th data-sortable="true" data-column="nome" onclick="sortTable('nome')">Nome <i class="fas fa-sort float-right"></i></th>
@endif
@if(in_array('data_nascita', $visibleColumns))
<th data-column="data_nascita">Data Nascita</th>
@endif
@if(in_array('genere', $visibleColumns))
<th data-column="genere">Genere</th>
@endif
@if(in_array('indirizzo', $visibleColumns))
<th data-column="indirizzo">Indirizzo</th>
@endif
@if(in_array('cap', $visibleColumns))
<th data-column="cap">CAP</th>
@endif
@if(in_array('citta', $visibleColumns))
<th data-column="citta">Città</th>
@endif
@if(in_array('provincia', $visibleColumns))
<th data-column="provincia">Prov.</th>
@endif
@if(in_array('tipo_documento', $visibleColumns))
<th data-column="tipo_documento">Tipo Doc.</th>
@endif
@if(in_array('numero_documento', $visibleColumns))
<th data-column="numero_documento">N. Documento</th>
@endif
@if(in_array('scadenza_documento', $visibleColumns))
<th data-column="scadenza_documento">Scad. Doc.</th>
@endif
@if(in_array('note', $visibleColumns))
<th data-column="note">Note</th>
@endif
@if(in_array('created_at', $visibleColumns))
<th data-column="created_at">Creato il</th>
@endif
@if(in_array('email', $visibleColumns))
<th data-sortable="true" data-column="email" onclick="sortTable('email')">Email <i class="fas fa-sort float-right"></i></th>
@endif
@@ -160,9 +198,9 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
<th data-sortable="true" data-column="telefono" onclick="sortTable('telefono')">Telefono <i class="fas fa-sort float-right"></i></th>
@endif
@if(in_array('tag', $visibleColumns))
<th>Tag</th>
<th data-column="tag">Tag</th>
@endif
<th style="width: 120px;">Azioni</th>
<th style="width: 120px;" data-column="azioni">Azioni</th>
</tr>
</thead>
<tbody id="table-body">
@@ -187,6 +225,39 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
<a href="{{ url('/individui/' . $ind->id . '/edit') }}">{{ $ind->nome }}</a>
</td>
@endif
@if(in_array('data_nascita', $visibleColumns))
<td>{{ $ind->data_nascita ? $ind->data_nascita->format('d/m/Y') : '-' }}</td>
@endif
@if(in_array('genere', $visibleColumns))
<td>{{ $ind->genere ?? '-' }}</td>
@endif
@if(in_array('indirizzo', $visibleColumns))
<td>{{ $ind->indirizzo ?? '-' }}</td>
@endif
@if(in_array('cap', $visibleColumns))
<td>{{ $ind->cap ?? '-' }}</td>
@endif
@if(in_array('citta', $visibleColumns))
<td>{{ $ind->città ?? '-' }}</td>
@endif
@if(in_array('provincia', $visibleColumns))
<td>{{ $ind->sigla_provincia ?? '-' }}</td>
@endif
@if(in_array('tipo_documento', $visibleColumns))
<td>{{ $ind->tipo_documento ? str_replace(['carta_identita', 'patente'], ['C.I.', 'Patente'], $ind->tipo_documento) : '-' }}</td>
@endif
@if(in_array('numero_documento', $visibleColumns))
<td>{{ $ind->numero_documento ?? '-' }}</td>
@endif
@if(in_array('scadenza_documento', $visibleColumns))
<td>{{ $ind->scadenza_documento ? $ind->scadenza_documento->format('d/m/Y') : '-' }}</td>
@endif
@if(in_array('note', $visibleColumns))
<td class="text-truncate" style="max-width: 200px;">{{ $ind->note ? Str::limit($ind->note, 50) : '-' }}</td>
@endif
@if(in_array('created_at', $visibleColumns))
<td>{{ $ind->created_at ? $ind->created_at->format('d/m/Y') : '-' }}</td>
@endif
@if(in_array('email', $visibleColumns))
<td>{{ $ind->getEmailPrimariaAttribute() ?: '-' }}</td>
@endif
@@ -266,75 +337,7 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
@endif
</div>
<div class="modal fade" id="saveVistaModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-save mr-2"></i>Salva Vista</h5>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label>Nome Vista *</label>
<input type="text" id="vista-nome" class="form-control" placeholder="Es. Elenco soci attivi">
</div>
<div class="form-group">
<label>Colonne visibili</label>
<div class="row" id="colonne-checkboxes">
@foreach($allColumns as $col)
<div class="col-md-6">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input col-visibile" id="col-vis-{{ $col['key'] }}" value="{{ $col['key'] }}" {{ in_array($col['key'], $visibleColumns) ? 'checked' : '' }}>
<label class="custom-control-label" for="col-vis-{{ $col['key'] }}">{{ $col['label'] }}</label>
</div>
</div>
@endforeach
</div>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="vista-default" value="true">
<label class="custom-control-label" for="vista-default">Imposta come vista predefinita</label>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annulla</button>
<button type="button" class="btn btn-primary" onclick="saveVista()">Salva</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="colonneModal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fas fa-columns mr-2"></i>Colonne Visibili</h5>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<div class="modal-body">
<div class="row">
@foreach($allColumns as $col)
<div class="col-md-6">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input col-toggle" id="col-toggle-{{ $col['key'] }}" value="{{ $col['key'] }}" {{ in_array($col['key'], $visibleColumns) ? 'checked' : '' }} onchange="toggleColumn('{{ $col['key'] }}', this.checked)">
<label class="custom-control-label" for="col-toggle-{{ $col['key'] }}">{{ $col['label'] }}</label>
</div>
</div>
@endforeach
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Chiudi</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog">
@if($canDeleteIndividui)
@@ -519,6 +522,7 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
@endsection
@section('scripts')
<script src="{{ asset('js/column-manager.js') }}"></script>
<script>
let currentSort = { column: null, direction: 'asc' };
let columnVisibility = {!! json_encode(array_column($allColumns, 'key')) !!};
@@ -537,6 +541,16 @@ function applyGlobalSearch(value) {
});
}
function getColumnIndex(colKey) {
const ths = document.querySelectorAll('#individui-table thead th');
let idx = 0;
for (const th of ths) {
if (th.dataset.column === colKey) return idx;
idx++;
}
return -1;
}
function applyColumnFilter() {
const col = document.getElementById('filter-column').value;
const op = document.getElementById('filter-operator').value;
@@ -545,7 +559,8 @@ function applyColumnFilter() {
if (!col || !val) return;
const rows = document.querySelectorAll('#table-body tr');
const colIndex = { codice: 1, cognome: 2, nome: 3, email: 4, telefono: 5, tag: 6, azioni: 7 }[col];
const colIndex = getColumnIndex(col);
if (colIndex < 0) return;
rows.forEach(row => {
const cells = row.querySelectorAll('td');
@@ -577,7 +592,7 @@ function sortTable(column) {
currentSort = { column: column, direction: 'asc' };
}
const colIndex = { codice: 1, cognome: 2, nome: 3, email: 4, telefono: 5, tag: 6, azioni: 7 }[column];
const colIndex = getColumnIndex(column);
const tbody = document.getElementById('table-body');
const rows = Array.from(tbody.querySelectorAll('tr'));
@@ -600,63 +615,7 @@ function sortTable(column) {
});
}
function toggleColumn(column, visible) {
const colIndex = { codice: 1, cognome: 2, nome: 3, email: 4, telefono: 5, tag: 6, azioni: 7 }[column];
const ths = document.querySelectorAll('#individui-table thead th');
const rows = document.querySelectorAll('#table-body tr');
if (visible) {
ths[colIndex].style.display = '';
rows.forEach(row => {
row.querySelectorAll('td')[colIndex].style.display = '';
});
} else {
ths[colIndex].style.display = 'none';
rows.forEach(row => {
row.querySelectorAll('td')[colIndex].style.display = 'none';
});
}
}
async function saveVista() {
let nome = document.getElementById('vista-nome').value.trim();
if (!nome) {
const now = new Date();
nome = 'Vista ' + now.toLocaleDateString('it-IT') + ' ' + now.toLocaleTimeString('it-IT', { hour: '2-digit', minute: '2-digit' });
}
const colonneVisibili = Array.from(document.querySelectorAll('#colonne-checkboxes input:checked')).map(i => i.value);
const isDefault = document.getElementById('vista-default').checked;
const data = {
nome: nome,
tipo: 'individui',
colonne_visibili: colonneVisibili,
colonne_ordinamento: currentSort.column ? [[currentSort.column, currentSort.direction]] : [],
ricerca: document.getElementById('global-search').value,
is_default: isDefault
};
try {
const response = await fetch('/viste', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content || ''
},
body: JSON.stringify(data)
});
if (response.ok) {
alert('Vista salvata!');
$('#saveVistaModal').modal('hide');
} else {
alert('Errore salvataggio');
}
} catch(e) {
alert('Errore: ' + e.message);
}
}
function toggleSelectAll(source) {
const checkboxes = document.querySelectorAll('.row-checkbox');
@@ -863,24 +822,30 @@ document.addEventListener('DOMContentLoaded', function() {
document.head.appendChild(meta);
}
const visibleCols = {!! $visibleColumnsJson !!};
const allCols = {!! $allColumnsJson !!};
allCols.forEach(function(col) {
const visibile = visibleCols.includes(col.key);
const saveCheckbox = document.querySelector(`#col-vis-${col.key}`);
const toggleCheckbox = document.querySelector(`#col-toggle-${col.key}`);
if (saveCheckbox) saveCheckbox.checked = visibile;
if (toggleCheckbox) toggleCheckbox.checked = visibile;
});
const vistaDefault = {!! $vistaDefaultJson !!};
if (vistaDefault && vistaDefault.ricerca) {
document.getElementById('global-search').value = vistaDefault.ricerca;
applyGlobalSearch(vistaDefault.ricerca);
}
const table = document.getElementById('individui-table');
if (table) {
window.columnManager = new ColumnManager('individui-table', {
entityType: 'individui',
storageKey: 'cm_individui',
resizable: true,
reorderable: true,
});
initTableSettings();
}
});
window.printWithCurrentSettings = function () {
if (window.columnManager) {
window.columnManager.printWithSettings();
}
};
function showCreateMailingListModal() {
document.getElementById('ml-nome').value = '';
document.getElementById('ml-descrizione').value = '';
+62 -8
View File
@@ -5,6 +5,9 @@
@php
$canWriteMailing = Auth::user()->canManage('mailing');
$canDeleteMailing = Auth::user()->canDelete('mailing');
$vistaDefaultJson = isset($vista) ? $vista->toJson() : 'null';
$visibleColumnsJson = json_encode($visibleColumns ?? []);
$tableColumnsJson = json_encode($tableColumns ?? []);
@endphp
@section('content')
@@ -20,6 +23,9 @@ $canDeleteMailing = Auth::user()->canDelete('mailing');
<div class="card-header">
<h3 class="card-title"><i class="fas fa-list mr-2"></i>Elenco Mailing Lists</h3>
<div class="card-tools">
<button type="button" class="btn btn-sm btn-secondary mr-1" onclick="$('#table-settings-modal').modal('show')" title="Impostazioni tabella">
<i class="fas fa-cog"></i>
</button>
@if($canDeleteMailing)
<div class="btn-group mr-2">
<button type="button" class="btn btn-sm btn-danger" onclick="deleteSelected()">
@@ -37,10 +43,10 @@ $canDeleteMailing = Auth::user()->canDelete('mailing');
@include('partials._tag-filter-bar', ['filterTags' => $allTags ?? collect()])
<div class="card-body p-0">
@if($mailingLists->count() > 0)
<table class="table table-bordered table-hover table-striped mb-0">
<table class="table table-bordered table-hover table-striped mb-0" id="mailing-liste-table">
<thead class="thead-light">
<tr>
<th style="width: 40px;">
<th style="width: 40px;" data-column="select">
@if($canDeleteMailing)
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="select-all" onchange="toggleSelectAll(this)">
@@ -48,12 +54,24 @@ $canDeleteMailing = Auth::user()->canDelete('mailing');
</div>
@endif
</th>
<th>Nome</th>
<th>Descrizione</th>
<th style="width: 100px;">Contatti</th>
<th>Tag</th>
<th style="width: 100px;">Stato</th>
<th style="width: 130px;">Creata il</th>
@if(in_array('nome', $visibleColumns))
<th data-column="nome">Nome</th>
@endif
@if(in_array('descrizione', $visibleColumns))
<th data-column="descrizione">Descrizione</th>
@endif
@if(in_array('contatti_count', $visibleColumns))
<th style="width: 100px;" data-column="contatti_count">Contatti</th>
@endif
@if(in_array('tag', $visibleColumns))
<th data-column="tag">Tag</th>
@endif
@if(in_array('attiva', $visibleColumns))
<th style="width: 100px;" data-column="attiva">Stato</th>
@endif
@if(in_array('created_at', $visibleColumns))
<th style="width: 130px;" data-column="created_at">Creata il</th>
@endif
<th style="width: 100px;">Azioni</th>
</tr>
</thead>
@@ -68,13 +86,20 @@ $canDeleteMailing = Auth::user()->canDelete('mailing');
</div>
@endif
</td>
@if(in_array('nome', $visibleColumns))
<td>
<a href="{{ route('mailing-liste.show', $lista->id) }}">{{ $lista->nome }}</a>
</td>
@endif
@if(in_array('descrizione', $visibleColumns))
<td>{{ $lista->descrizione ?: '-' }}</td>
@endif
@if(in_array('contatti_count', $visibleColumns))
<td>
<span class="badge badge-info">{{ $lista->contatti->count() }}</span>
</td>
@endif
@if(in_array('tag', $visibleColumns))
<td>
@foreach($lista->tags as $tag)
<a href="{{ request()->url() }}?tag[]={{ $tag->slug }}" class="badge" style="background-color:{{ $tag->color ?? '#6c757d' }};color:#fff;font-size:0.75rem;text-decoration:none;">
@@ -85,6 +110,8 @@ $canDeleteMailing = Auth::user()->canDelete('mailing');
<span class="text-muted small">-</span>
@endif
</td>
@endif
@if(in_array('attiva', $visibleColumns))
<td>
@if($lista->attiva)
<span class="badge badge-success">Attiva</span>
@@ -92,7 +119,10 @@ $canDeleteMailing = Auth::user()->canDelete('mailing');
<span class="badge badge-secondary">Disattiva</span>
@endif
</td>
@endif
@if(in_array('created_at', $visibleColumns))
<td>{{ $lista->created_at->format('d/m/Y') }}</td>
@endif
<td>
<a href="{{ route('mailing-liste.show', $lista->id) }}" class="btn btn-xs btn-info" title="Visualizza">
<i class="fas fa-eye"></i>
@@ -125,6 +155,10 @@ $canDeleteMailing = Auth::user()->canDelete('mailing');
</div>
</div>
@include('partials.table-settings')
<div id="vista-data" style="display:none;">{{ $vistaDefaultJson }}</div>
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog">
@if($canDeleteMailing)
<div class="modal-dialog" role="document">
@@ -150,6 +184,7 @@ $canDeleteMailing = Auth::user()->canDelete('mailing');
@endsection
@section('scripts')
<script src="{{ asset('js/column-manager.js') }}"></script>
<script>
function toggleSelectAll(source) {
document.querySelectorAll('.row-checkbox').forEach(cb => cb.checked = source.checked);
@@ -187,5 +222,24 @@ function deleteSelected() {
$('#deleteModal').modal('show');
}
document.addEventListener('DOMContentLoaded', function () {
const table = document.getElementById('mailing-liste-table');
if (table) {
window.columnManager = new ColumnManager('mailing-liste-table', {
entityType: 'mailing-liste',
storageKey: 'cm_mailing',
resizable: true,
reorderable: true,
});
initTableSettings();
}
});
window.printWithCurrentSettings = function () {
if (window.columnManager) {
window.columnManager.printWithSettings();
}
};
</script>
@endsection
+283 -110
View File
@@ -15,105 +15,159 @@
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="filters-tab" data-toggle="tab" href="#filters-panel" role="tab">
<i class="fas fa-filter mr-1"></i> Filtri
<a class="nav-link" id="vistas-tab" data-toggle="tab" href="#vistas-panel" role="tab">
<i class="fas fa-save mr-1"></i> Viste
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="search-tab" data-toggle="tab" href="#search-panel" role="tab">
<i class="fas fa-search mr-1"></i> Ricerca
</a>
</li>
<li class="nav-item">
<a class="nav-link" id="export-tab" data-toggle="tab" href="#export-panel" role="tab">
<i class="fas fa-download mr-1"></i> Esporta
<a class="nav-link" id="print-tab" data-toggle="tab" href="#print-panel" role="tab">
<i class="fas fa-print mr-1"></i> Stampa
</a>
</li>
</ul>
<div class="tab-content p-3">
<div class="tab-pane fade show active" id="columns-panel" role="tabpanel">
<h6>Seleziona le colonne da visualizzare:</h6>
<div class="row">
<h6 class="mb-3">Seleziona colonne, regola larghezza e riordina:</h6>
<p class="small text-muted mb-3">
<i class="fas fa-arrows-alt mr-1"></i> Trascina le intestazioni per riordinare &middot;
<i class="fas fa-arrows-alt-h mr-1"></i> Trascina il bordo destro di una cella per ridimensionare
</p>
<div id="column-list" class="mb-3">
@foreach($tableColumns as $col)
<div class="col-md-4">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input column-toggle"
id="col-{{ $col['key'] }}" data-column="{{ $col['key'] }}"
{{ ($col['visible'] ?? true) ? 'checked' : '' }}>
<label class="custom-control-label" for="col-{{ $col['key'] }}">
{{ $col['label'] }}
</label>
<div class="form-row align-items-center mb-2 column-config-item" data-column="{{ $col['key'] }}">
<div class="col-auto">
<i class="fas fa-grip-vertical text-muted" style="cursor:grab;"></i>
</div>
<div class="col-auto">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input column-toggle"
id="col-{{ $col['key'] }}" data-column="{{ $col['key'] }}"
{{ ($col['visible'] ?? true) ? 'checked' : '' }}>
<label class="custom-control-label" for="col-{{ $col['key'] }}">
{{ $col['label'] }}
</label>
</div>
</div>
<div class="col">
<div class="input-group input-group-sm">
<input type="number" class="form-control form-control-sm column-width-input"
data-column="{{ $col['key'] }}"
value="{{ isset($columnWidths[$col['key']]) ? intval(str_replace('px', '', $columnWidths[$col['key']])) : '' }}"
placeholder="auto" min="30" style="max-width:80px;">
<div class="input-group-append">
<span class="input-group-text">px</span>
</div>
<button type="button" class="btn btn-sm btn-outline-secondary ml-1 reset-col-width" data-column="{{ $col['key'] }}"
title="Reset larghezza">
<i class="fas fa-undo-alt"></i>
</button>
</div>
</div>
</div>
@endforeach
</div>
<button type="button" class="btn btn-sm btn-secondary" onclick="resetAllColumnWidths()">
<i class="fas fa-undo-alt mr-1"></i> Reset tutte larghezze
</button>
</div>
<div class="tab-pane fade" id="filters-panel" role="tabpanel">
<h6>Aggiungi filtri:</h6>
<div id="filters-container">
<div class="filter-row mb-2">
<div class="row">
<div class="col-md-4">
<select class="form-control form-control-sm filter-column">
<option value="">Seleziona colonna...</option>
@foreach($tableColumns as $col)
<option value="{{ $col['key'] }}">{{ $col['label'] }}</option>
@endforeach
</select>
<div class="tab-pane fade" id="vistas-panel" role="tabpanel">
<h6 class="mb-2">Viste salvate</h6>
<div id="vistas-list" class="mb-3">
@php
$entityType = $entityType ?? 'individui';
$userVistas = $userVistas ?? collect();
@endphp
@if($userVistas->count() > 0)
<div class="list-group mb-3" id="vistas-list-group">
@foreach($userVistas as $v)
<div class="list-group-item list-group-item-action d-flex align-items-center py-2 {{ $vista && $vista->id === $v->id ? 'active' : '' }}">
<div class="mr-auto">
@if($v->is_default)
<i class="fas fa-star text-warning mr-1" title="Predefinita"></i>
@else
<i class="fas fa-file-alt text-muted mr-1"></i>
@endif
<span>{{ $v->nome }}</span>
@if($v->colonne_visibili)
<span class="badge badge-light ml-1">{{ count($v->colonne_visibili) }} col.</span>
@endif
</div>
<div class="col-md-2">
<select class="form-control form-control-sm filter-operator">
<option value="contains">Contiene</option>
<option value="equals">Uguale a</option>
<option value="starts">Inizia con</option>
<option value="ends">Finisce con</option>
<option value="gt">Maggiore di</option>
<option value="lt">Minore di</option>
</select>
</div>
<div class="col-md-4">
<input type="text" class="form-control form-control-sm filter-value" placeholder="Valore">
<div class="ml-2">
<a href="?vista_id={{ $v->id }}" class="btn btn-xs btn-primary" title="Applica vista">
<i class="fas fa-play"></i>
</a>
@if(!$v->is_default)
<form action="{{ url('/viste/' . $v->id . '/default') }}" method="POST" class="d-inline">
@csrf @method('PATCH')
<button type="submit" class="btn btn-xs btn-warning" title="Imposta predefinita">
<i class="fas fa-star"></i>
</button>
</form>
@endif
<button type="button" class="btn btn-xs btn-info edit-vista-btn" title="Modifica vista"
data-vista-id="{{ $v->id }}"
data-vista-nome="{{ $v->nome }}"
data-vista-default="{{ $v->is_default ? 'true' : 'false' }}"
data-vista-colonne="{{ json_encode($v->colonne_visibili ?? []) }}">
<i class="fas fa-pencil-alt"></i>
</button>
<form action="{{ url('/viste/' . $v->id) }}" method="POST" class="d-inline">
@csrf @method('DELETE')
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questa vista?')" title="Elimina">
<i class="fas fa-trash"></i>
</button>
</form>
</div>
</div>
@endforeach
</div>
@else
<p class="text-muted text-center py-2">Nessuna vista salvata</p>
@endif
</div>
<hr>
<h6 class="mb-2">Salva vista corrente</h6>
<div class="form-row align-items-end">
<div class="col">
<input type="text" id="vista-nome" class="form-control" placeholder="Nome vista...">
</div>
<div class="col-auto">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="vista-default" value="true">
<label class="custom-control-label" for="vista-default">Predefinita</label>
</div>
</div>
<div class="col-auto">
<button type="button" class="btn btn-primary" id="save-vista-btn">
<i class="fas fa-save mr-1"></i> Salva
</button>
</div>
</div>
<button type="button" class="btn btn-xs btn-success mt-2" onclick="addFilterRow()">
<i class="fas fa-plus"></i> Aggiungi filtro
</button>
<button type="button" class="btn btn-xs btn-primary mt-2" id="apply-filters-btn">
<i class="fas fa-check"></i> Applica filtri
</button>
</div>
<div class="tab-pane fade" id="search-panel" role="tabpanel">
<div class="form-group">
<label>cerca in tutte le colonne:</label>
<input type="text" id="table-search" class="form-control" placeholder="Testo da cercare...">
<div class="tab-pane fade" id="print-panel" role="tabpanel">
<h6 class="mb-3">Stampa con impostazioni correnti</h6>
<p>La stampa rispetterà le colonne visibili, la larghezza e l'ordine impostati.</p>
<button type="button" class="btn btn-primary btn-lg" onclick="window.printWithCurrentSettings ? window.printWithCurrentSettings() : alert('ColumnManager non inizializzato'); return false;">
<i class="fas fa-print mr-2"></i> Stampa
</button>
<hr>
<h6 class="mb-2">Opzioni aggiuntive</h6>
<div class="custom-control custom-checkbox mb-2">
<input type="checkbox" class="custom-control-input" id="print-page-breaks" checked>
<label class="custom-control-label" for="print-page-breaks">Interruzione di pagina tra gli elementi</label>
</div>
</div>
<div class="tab-pane fade" id="export-panel" role="tabpanel">
<h6>Esporta i dati visualizzati:</h6>
<div class="btn-group">
<button type="button" class="btn btn-success" id="export-csv-btn">
<i class="fas fa-file-csv mr-1"></i> Esporta CSV
</button>
<button type="button" class="btn btn-danger" id="export-pdf-btn">
<i class="fas fa-file-pdf mr-1"></i> Esporta PDF
</button>
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="print-background">
<label class="custom-control-label" for="print-background">Stampa sfondi e colori</label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<div class="mr-auto">
<input type="text" id="vista-nome" class="form-control form-control-sm" placeholder="Nome vista..." style="display: inline-block; width: auto;">
<button type="button" class="btn btn-primary btn-sm" id="save-vista-btn">
<i class="fas fa-save mr-1"></i> Salva Vista
</button>
</div>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Chiudi</button>
</div>
</div>
@@ -121,41 +175,160 @@
</div>
<script>
function addFilterRow() {
const container = document.getElementById('filters-container');
const columns = @json($tableColumns);
const options = columns.map(c => `<option value="${c.key}">${c.label}</option>`).join('');
const div = document.createElement('div');
div.className = 'filter-row mb-2';
div.innerHTML = `
<div class="row">
<div class="col-md-4">
<select class="form-control form-control-sm filter-column">
<option value="">Seleziona colonna...</option>
${options}
</select>
</div>
<div class="col-md-2">
<select class="form-control form-control-sm filter-operator">
<option value="contains">Contiene</option>
<option value="equals">Uguale a</option>
<option value="starts">Inizia con</option>
<option value="ends">Finisce con</option>
<option value="gt">Maggiore di</option>
<option value="lt">Minore di</option>
</select>
</div>
<div class="col-md-4">
<input type="text" class="form-control form-control-sm filter-value" placeholder="Valore">
</div>
<div class="col-md-2">
<button type="button" class="btn btn-danger btn-sm" onclick="this.closest('.filter-row').remove()">
<i class="fas fa-times"></i>
</button>
</div>
</div>
`;
container.appendChild(div);
function resetAllColumnWidths() {
document.querySelectorAll('.column-width-input').forEach(inp => { inp.value = ''; });
document.querySelectorAll('.column-config-item').forEach(item => {
const th = document.querySelector(`th[data-column="${item.dataset.column}"]`);
if (th) th.style.width = '';
});
if (window.columnManager) {
window.columnManager.columnWidths = {};
window.columnManager.persistState();
}
}
</script>
function syncColumnListToTable() {
const items = document.querySelectorAll('#column-list .column-config-item');
const keys = Array.from(items).map(i => i.dataset.column);
if (window.columnManager && keys.length > 0) {
window.columnManager.reorderColumns(keys);
}
}
let editingVistaId = null;
function editVista(id, nome, isDefault, colonneVisibili) {
editingVistaId = id;
document.getElementById('vista-nome').value = nome;
document.getElementById('vista-default').checked = isDefault;
document.querySelectorAll('.column-toggle').forEach(cb => {
cb.checked = colonneVisibili.includes(cb.dataset.column);
if (window.columnManager) window.columnManager.setVisible(cb.dataset.column, cb.checked);
});
document.getElementById('save-vista-btn').innerHTML = '<i class="fas fa-save mr-1"></i> Aggiorna';
document.getElementById('vistas-tab').click();
}
function initTableSettings() {
const columnList = document.getElementById('column-list');
if (!columnList) return;
let dragItem = null;
columnList.querySelectorAll('.column-config-item').forEach(item => {
const grip = item.querySelector('.fa-grip-vertical');
if (!grip) return;
grip.addEventListener('mousedown', function () { item.draggable = true; });
item.addEventListener('dragstart', function (e) {
dragItem = this; e.dataTransfer.effectAllowed = 'move'; this.classList.add('dragging');
});
item.addEventListener('dragend', function () {
this.classList.remove('dragging'); this.draggable = false; dragItem = null;
});
item.addEventListener('dragover', function (e) {
e.preventDefault();
if (!dragItem || dragItem === this) return;
const rect = this.getBoundingClientRect();
const midY = rect.top + rect.height / 2;
if (e.clientY < midY) columnList.insertBefore(dragItem, this);
else columnList.insertBefore(dragItem, this.nextSibling);
});
});
columnList.addEventListener('drop', function () {
const items = Array.from(columnList.querySelectorAll('.column-config-item'));
const newOrder = items.map(i => i.dataset.column);
if (window.columnManager && newOrder.length > 0) window.columnManager.reorderColumns(newOrder);
syncColumnListToTable();
});
columnList.querySelectorAll('.column-toggle').forEach(cb => {
cb.addEventListener('change', function () {
if (window.columnManager) window.columnManager.setVisible(this.dataset.column, this.checked);
});
});
columnList.querySelectorAll('.column-width-input').forEach(inp => {
inp.addEventListener('change', function () {
const key = this.dataset.column;
const val = parseInt(this.value);
const th = document.querySelector(`th[data-column="${key}"]`);
if (th) {
if (val && val >= 30) {
th.style.width = val + 'px';
if (window.columnManager) {
window.columnManager.columnWidths[key] = val + 'px';
window.columnManager.persistState();
}
} else {
th.style.width = '';
if (window.columnManager) {
delete window.columnManager.columnWidths[key];
window.columnManager.persistState();
}
}
}
});
});
columnList.querySelectorAll('.reset-col-width').forEach(btn => {
btn.addEventListener('click', function () {
const key = this.dataset.column;
const inp = document.querySelector(`.column-width-input[data-column="${key}"]`);
if (inp) inp.value = '';
const th = document.querySelector(`th[data-column="${key}"]`);
if (th) th.style.width = '';
if (window.columnManager) {
delete window.columnManager.columnWidths[key];
window.columnManager.persistState();
}
});
});
document.querySelectorAll('.edit-vista-btn').forEach(btn => {
btn.addEventListener('click', function () {
editVista(
this.dataset.vistaId,
this.dataset.vistaNome,
this.dataset.vistaDefault === 'true',
JSON.parse(this.dataset.vistaColonne)
);
});
});
document.getElementById('save-vista-btn')?.addEventListener('click', function() {
const nome = document.getElementById('vista-nome').value.trim();
if (!nome) { alert('Inserisci un nome per la vista'); return; }
const colonneVisibili = Array.from(document.querySelectorAll('.column-toggle:checked')).map(cb => cb.dataset.column);
const larghezze = {};
document.querySelectorAll('.column-width-input').forEach(inp => {
const val = parseInt(inp.value);
if (val && val >= 30) larghezze[inp.dataset.column] = val + 'px';
});
const ordine = Array.from(document.querySelectorAll('#column-list .column-config-item')).map(i => i.dataset.column);
const isDefault = document.getElementById('vista-default')?.checked || false;
const entityType = '{{ $entityType ?? 'individui' }}';
const isEditing = editingVistaId !== null;
const url = isEditing ? '/viste/' + editingVistaId : '/viste';
const method = isEditing ? 'PUT' : 'POST';
fetch(url, {
method: method,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.content || ''
},
body: JSON.stringify({
nome, tipo: entityType, colonne_visibili: colonneVisibili,
colonne_larghezze: larghezze, colonne_ordine: ordine,
colonne_ordinamento: [], is_default: isDefault,
})
})
.then(response => response.ok ? (alert(isEditing ? 'Vista aggiornata!' : 'Vista salvata!'), window.location.reload()) : response.text().then(t => { throw new Error(t); }))
.catch(err => alert('Errore salvataggio vista: ' + err.message));
});
}
</script>