versione 2.0.0
This commit is contained in:
@@ -103,6 +103,42 @@
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('partials._tag-selector', ['selectedTags' => $selectedTags ?? [], 'label' => 'Etichette'])
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm table-borderless">
|
||||
<tr>
|
||||
<td style="width: 100px;"><strong>File:</strong></td>
|
||||
<td>{{ $documento->file_path }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Tipo MIME:</strong></td>
|
||||
<td>{{ $documento->mime_type ?? '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Dimensione:</strong></td>
|
||||
<td>{{ $documento->dimensione ? number_format($documento->dimensione / 1024, 1) . ' KB' : '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Creato:</strong></td>
|
||||
<td>{{ $documento->created_at->format('d/m/Y H:i') }}</td>
|
||||
</tr>
|
||||
@if($documento->user)
|
||||
<tr>
|
||||
<td><strong>Utente:</strong></td>
|
||||
<td>{{ $documento->user->name }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
</table>
|
||||
@if($documento->file_path)
|
||||
<hr>
|
||||
<a href="/documenti/{{ $documento->id }}/download" class="btn btn-primary btn-block">
|
||||
<i class="fas fa-download mr-1"></i> Scarica
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -123,6 +123,8 @@ $canDeleteDocumenti = Auth::user()->canDelete('documenti');
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('partials._tag-filter-bar', ['filterTags' => $allTags])
|
||||
|
||||
<div class="card-body p-0">
|
||||
{{-- Toolbar azioni massive --}}
|
||||
<form id="massForm" method="POST">
|
||||
@@ -144,6 +146,9 @@ $canDeleteDocumenti = Auth::user()->canDelete('documenti');
|
||||
<button type="button" class="btn btn-sm btn-secondary" onclick="$('#massMoveModal').modal('show')">
|
||||
<i class="fas fa-folder-open mr-1"></i> Sposta selezionati in...
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-info" onclick="$('#massTagModal').modal('show')">
|
||||
<i class="fas fa-tags mr-1"></i> Tag
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-success" onclick="massDownload()">
|
||||
<i class="fas fa-download mr-1"></i> Scarica
|
||||
</button>
|
||||
@@ -226,6 +231,11 @@ $canDeleteDocumenti = Auth::user()->canDelete('documenti');
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
<div class="text-center mb-1" style="display:flex;flex-wrap:wrap;gap:2px;justify-content:center;">
|
||||
@foreach($documento->tags as $tag)
|
||||
<a href="{{ route('documenti.index', ['tag[]' => $tag->slug]) }}" class="badge" style="background-color: {{ $tag->color ?? '#6c757d' }}; color: #fff; font-size:0.7rem;">{{ $tag->name }}</a>
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="small text-muted text-center">
|
||||
@if($documento->user)
|
||||
{{ $documento->user->name }}
|
||||
@@ -295,6 +305,7 @@ $canDeleteDocumenti = Auth::user()->canDelete('documenti');
|
||||
<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>
|
||||
</tr>
|
||||
@@ -314,6 +325,7 @@ $canDeleteDocumenti = Auth::user()->canDelete('documenti');
|
||||
<td class="small text-muted">-</td>
|
||||
<td class="small text-muted">-</td>
|
||||
<td class="small text-muted">-</td>
|
||||
<td class="small text-muted">-</td>
|
||||
<td>
|
||||
<a href="/documenti?folder_id={{ $cartella->id }}" class="btn btn-xs btn-warning" title="Apri cartella">
|
||||
<i class="fas fa-folder-open"></i>
|
||||
@@ -423,6 +435,11 @@ $canDeleteDocumenti = Auth::user()->canDelete('documenti');
|
||||
<span class="text-muted small">-</span>
|
||||
@endswitch
|
||||
</td>
|
||||
<td>
|
||||
@foreach($documento->tags as $tag)
|
||||
<a href="{{ route('documenti.index', ['tag[]' => $tag->slug]) }}" class="badge" style="background-color: {{ $tag->color ?? '#6c757d' }}; color: #fff;">{{ $tag->name }}</a>
|
||||
@endforeach
|
||||
</td>
|
||||
<td class="small">{{ $documento->created_at->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
@if($documento->file_path)
|
||||
@@ -834,6 +851,50 @@ $canDeleteDocumenti = Auth::user()->canDelete('documenti');
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- MODAL: Gestione Tag massiva --}}
|
||||
<div class="modal fade" id="massTagModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-tags mr-2"></i>Gestione Tag</h5>
|
||||
<button type="button" class="close" onclick="$('#massTagModal').modal('hide')">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form id="massTagForm" method="POST" action="/documenti/mass-tag">
|
||||
@csrf
|
||||
<div class="modal-body">
|
||||
<p class="mb-2">Operazione su <strong id="massTagCount">0</strong> documenti selezionati.</p>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label class="font-weight-bold">Azione</label>
|
||||
<div class="d-flex" style="gap:1.5rem;">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="mode" id="mtModeAssign" value="assign" checked>
|
||||
<label class="form-check-label" for="mtModeAssign">Assegna tag</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="mode" id="mtModeRemove" value="remove">
|
||||
<label class="form-check-label" for="mtModeRemove">Rimuovi tag</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('partials._tag-selector', ['label' => 'Seleziona tag'])
|
||||
|
||||
<input type="hidden" name="ids" id="massTagIds" value="">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="$('#massTagModal').modal('hide')">Annulla</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-tags mr-1"></i> Applica
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- MODAL: Sposta documento --}}
|
||||
<div class="modal fade" id="moveModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
@@ -1218,6 +1279,12 @@ document.getElementById('massMoveModal')?.addEventListener('show.bs.modal', func
|
||||
document.getElementById('massMoveCount').textContent = ids.length;
|
||||
});
|
||||
|
||||
document.getElementById('massTagModal')?.addEventListener('show.bs.modal', function() {
|
||||
const ids = getSelectedIds();
|
||||
document.getElementById('massTagIds').value = ids.join(',');
|
||||
document.getElementById('massTagCount').textContent = ids.length;
|
||||
});
|
||||
|
||||
document.getElementById('massMoveNewFolderBtn')?.addEventListener('click', function() {
|
||||
Swal.fire({
|
||||
title: 'Nuova cartella',
|
||||
|
||||
@@ -247,6 +247,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-tags mr-2"></i>Tag</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@include('partials._tag-selector', ['selectedTags' => [], 'label' => 'Etichette'])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva
|
||||
|
||||
@@ -252,6 +252,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-tags mr-2"></i>Tag</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@include('partials._tag-selector', ['selectedTags' => $selectedTags ?? [], 'label' => 'Etichette'])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva Modifiche
|
||||
|
||||
@@ -43,6 +43,8 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@include('partials._tag-filter-bar', ['filterTags' => $allTags])
|
||||
|
||||
<div class="card-body">
|
||||
<form method="GET" class="mb-3">
|
||||
<div class="row">
|
||||
@@ -144,6 +146,7 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
|
||||
@endif
|
||||
</a>
|
||||
</th>
|
||||
<th>Tag</th>
|
||||
<th style="width: 120px;">
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'created_at', 'direction' => request('sort') === 'created_at' && request('direction') === 'asc' ? 'desc' : 'asc']) }}" class="text-dark">
|
||||
Creato
|
||||
@@ -221,6 +224,11 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
|
||||
<small class="text-muted">+{{ $evento->responsabili->count() - 2 }} altri</small>
|
||||
@endif
|
||||
</td>
|
||||
<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>
|
||||
<td><small class="text-muted">{{ $evento->created_at->format('d/m/Y') }}</small></td>
|
||||
<td>
|
||||
<a href="{{ url('/eventi/' . $evento->id) }}" class="btn btn-xs btn-info" title="Visualizza">
|
||||
@@ -241,7 +249,7 @@ $canDeleteEventi = Auth::user()->canDelete('eventi');
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="9" class="text-center text-muted py-4">
|
||||
<td colspan="10" 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>
|
||||
|
||||
@@ -202,6 +202,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-tags mr-2"></i>Tag</h3></div>
|
||||
<div class="card-body">
|
||||
@forelse($evento->tags as $tag)
|
||||
<span class="badge" style="background-color:{{ $tag->color ?? '#6c757d' }};color:#fff;padding:4px 10px;border-radius:12px;font-size:0.85rem;">{{ $tag->name }}</span>
|
||||
@empty
|
||||
<p class="text-muted mb-0"><i class="fas fa-info-circle mr-1"></i>Nessun tag assegnato.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($evento->note)
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-sticky-note mr-2"></i>Note</h3></div>
|
||||
|
||||
@@ -117,6 +117,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-tags mr-2"></i>Tag</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@include('partials._tag-selector', ['selectedTags' => [], 'label' => 'Etichette'])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva
|
||||
|
||||
@@ -270,6 +270,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-tags mr-2"></i>Tag</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@include('partials._tag-selector', ['selectedTags' => $selectedTags ?? [], 'label' => 'Etichette'])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva Modifiche
|
||||
|
||||
@@ -17,6 +17,7 @@ $columnLabels = [
|
||||
'citta' => 'Città',
|
||||
'telefono' => 'Telefono',
|
||||
'email' => 'Email',
|
||||
'tag' => 'Tag',
|
||||
];
|
||||
$vistaDefaultJson = $vista ? $vista->toJson() : 'null';
|
||||
$allColumnsJson = json_encode($allColumns);
|
||||
@@ -51,6 +52,22 @@ $visibleColumnsJson = json_encode($visibleColumns);
|
||||
<i class="fas fa-sitemap mr-1"></i> Albero
|
||||
</button>
|
||||
</div>
|
||||
<div class="btn-group mr-1">
|
||||
<button type="button" class="btn btn-sm btn-secondary dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fas fa-cog mr-1"></i> Azioni
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
@if($canWriteGruppi)
|
||||
<a class="dropdown-item" href="#" onclick="showMassTagModal(); return false;">
|
||||
<i class="fas fa-tags mr-2"></i>Assegna Tag
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger" href="#" onclick="deleteSelected(); return false;">
|
||||
<i class="fas fa-trash mr-2"></i>Elimina Selezionati
|
||||
</a>
|
||||
@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>
|
||||
@@ -65,11 +82,19 @@ $visibleColumnsJson = json_encode($visibleColumns);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('partials._tag-filter-bar', ['filterTags' => $allTags])
|
||||
|
||||
@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;">
|
||||
<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>
|
||||
@endif
|
||||
@@ -103,12 +128,21 @@ $visibleColumnsJson = json_encode($visibleColumns);
|
||||
@if(in_array('email', $visibleColumns))
|
||||
<th>Email</th>
|
||||
@endif
|
||||
@if(in_array('tag', $visibleColumns))
|
||||
<th>Tag</th>
|
||||
@endif
|
||||
<th style="width: 120px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="table-body">
|
||||
@foreach($gruppi as $gruppo)
|
||||
<tr data-id="{{ $gruppo->id }}">
|
||||
<td>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input row-checkbox" id="select-{{ $gruppo->id }}" value="{{ $gruppo->id }}">
|
||||
<label class="custom-control-label" for="select-{{ $gruppo->id }}"></label>
|
||||
</div>
|
||||
</td>
|
||||
@if(in_array('nome', $visibleColumns))
|
||||
<td>
|
||||
<span style="padding-left: {{ ($gruppo->depth ?? 0) * 20 }}px;">
|
||||
@@ -154,6 +188,13 @@ $visibleColumnsJson = json_encode($visibleColumns);
|
||||
@if(in_array('email', $visibleColumns))
|
||||
<td>-</td>
|
||||
@endif
|
||||
@if(in_array('tag', $visibleColumns))
|
||||
<td>
|
||||
@foreach($gruppo->tags as $tag)
|
||||
<a href="{{ route('gruppi.index', ['tag[]' => $tag->slug]) }}" class="badge" style="background-color: {{ $tag->color ?? '#6c757d' }}; color: #fff;">{{ $tag->name }}</a>
|
||||
@endforeach
|
||||
</td>
|
||||
@endif
|
||||
<td>
|
||||
<a href="{{ route('gruppi.show', $gruppo->id) }}" class="btn btn-xs btn-info" title="Visualizza">
|
||||
<i class="fas fa-eye"></i>
|
||||
@@ -309,6 +350,68 @@ $visibleColumnsJson = json_encode($visibleColumns);
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- MODAL: Gestione Tag massiva --}}
|
||||
<div class="modal fade" id="massTagModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-info">
|
||||
<h5 class="modal-title text-white"><i class="fas fa-tags mr-2"></i>Gestione Tag</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal"><span>×</span></button>
|
||||
</div>
|
||||
<form id="massTagForm" method="POST" action="/gruppi/mass-tag">
|
||||
@csrf
|
||||
<div class="modal-body">
|
||||
<p class="mb-2">Operazione su <strong id="massTagCount">0</strong> gruppi selezionati.</p>
|
||||
<div class="form-group mb-3">
|
||||
<label class="font-weight-bold">Azione</label>
|
||||
<div class="d-flex" style="gap:1.5rem;">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="mode" id="grMtModeAssign" value="assign" checked>
|
||||
<label class="form-check-label" for="grMtModeAssign">Assegna tag</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="mode" id="grMtModeRemove" value="remove">
|
||||
<label class="form-check-label" for="grMtModeRemove">Rimuovi tag</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@include('partials._tag-selector', ['label' => 'Seleziona tag'])
|
||||
<input type="hidden" name="ids" id="massTagIds" value="">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annulla</button>
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-tags mr-1"></i> Applica</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- MODAL: Conferma Eliminazione massiva --}}
|
||||
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog">
|
||||
@if($canDeleteGruppi)
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-danger">
|
||||
<h5 class="modal-title text-white"><i class="fas fa-trash-alt mr-2"></i>Conferma Eliminazione</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal"><span>×</span></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Stai per eliminare <strong id="delete-count">0</strong> gruppi.</p>
|
||||
<div class="alert alert-warning mt-3 mb-0">
|
||||
<small><i class="fas fa-info-circle mr-1"></i>I membri verranno scollegati ma non eliminati.</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annulla</button>
|
||||
<button type="button" class="btn btn-danger" id="confirm-delete-btn"><i class="fas fa-trash mr-1"></i> Elimina</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
@@ -317,6 +420,50 @@ function switchView(mode) {
|
||||
window.location.href = '?view=' + mode;
|
||||
}
|
||||
|
||||
function toggleSelectAll(source) {
|
||||
document.querySelectorAll('.row-checkbox').forEach(function(cb) {
|
||||
cb.checked = source.checked;
|
||||
});
|
||||
}
|
||||
|
||||
function getSelectedIds() {
|
||||
return Array.from(document.querySelectorAll('.row-checkbox:checked')).map(cb => cb.value);
|
||||
}
|
||||
|
||||
function showMassTagModal() {
|
||||
const ids = getSelectedIds();
|
||||
if (ids.length === 0) {
|
||||
alert('Seleziona almeno un gruppo');
|
||||
return;
|
||||
}
|
||||
document.getElementById('massTagIds').value = ids.join(',');
|
||||
document.getElementById('massTagCount').textContent = ids.length;
|
||||
$('#massTagModal').modal('show');
|
||||
}
|
||||
|
||||
function deleteSelected() {
|
||||
const ids = getSelectedIds();
|
||||
if (ids.length === 0) {
|
||||
alert('Seleziona almeno un gruppo');
|
||||
return;
|
||||
}
|
||||
document.getElementById('delete-count').textContent = ids.length;
|
||||
document.getElementById('confirm-delete-btn').onclick = function() {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '/gruppi/mass-elimina';
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '{{ csrf_token() }}';
|
||||
let html = '<input type="hidden" name="_token" value="' + csrfToken + '">';
|
||||
ids.forEach(function(id) {
|
||||
html += '<input type="hidden" name="ids[]" value="' + id + '">';
|
||||
});
|
||||
form.innerHTML = html;
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
};
|
||||
$('#deleteModal').modal('show');
|
||||
}
|
||||
|
||||
function toggleItem(id) {
|
||||
const el = document.getElementById('group-' + id);
|
||||
const icon = document.getElementById('icon-' + id);
|
||||
|
||||
@@ -247,6 +247,17 @@ $canDeleteGruppi = Auth::user()->canDelete('gruppi');
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-tags mr-2"></i>Tag</h3></div>
|
||||
<div class="card-body">
|
||||
@forelse($gruppo->tags as $tag)
|
||||
<span class="badge" style="background-color:{{ $tag->color ?? '#6c757d' }};color:#fff;padding:4px 10px;border-radius:12px;font-size:0.85rem;">{{ $tag->name }}</span>
|
||||
@empty
|
||||
<p class="text-muted mb-0"><i class="fas fa-info-circle mr-1"></i>Nessun tag assegnato.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
<a href="#ruoli" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-users-cog mr-2"></i> Tipi di Ruolo
|
||||
</a>
|
||||
<a href="#tag" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-tags mr-2"></i> Tag
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1068,6 +1071,136 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- === TAG === --}}
|
||||
<div class="tab-pane" id="tag">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Tag</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">Gestisci i tag (etichette) per classificare individui, gruppi, eventi e documenti.</p>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">{{ session('success') }}</div>
|
||||
@endif
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger">{{ session('error') }}</div>
|
||||
@endif
|
||||
|
||||
<div class="mb-3">
|
||||
<button type="button" class="btn btn-success btn-sm" data-toggle="modal" data-target="#addTagModal">
|
||||
<i class="fas fa-plus"></i> Nuovo Tag
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-bordered table-striped" id="tag-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40px;">Ordine</th>
|
||||
<th>Tag</th>
|
||||
<th>Nome</th>
|
||||
<th>Slug</th>
|
||||
<th style="width: 100px;">Colore</th>
|
||||
<th style="width: 150px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tag-sortable">
|
||||
@foreach($tags as $tag)
|
||||
<tr data-id="{{ $tag->id }}">
|
||||
<td class="text-center">
|
||||
<i class="fas fa-arrows-alt handle" style="cursor: grab;"></i>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge" style="background-color: {{ $tag->color ?? '#6c757d' }}; color: #fff;">
|
||||
{{ $tag->name }}
|
||||
</span>
|
||||
</td>
|
||||
<td><span class="tag-nome">{{ $tag->name }}</span></td>
|
||||
<td><code>{{ $tag->slug }}</code></td>
|
||||
<td>
|
||||
<span class="tag-color-preview" style="display:inline-block;width:24px;height:24px;border-radius:4px;background:{{ $tag->color ?? '#6c757d' }};border:1px solid #dee2e6;vertical-align:middle;"></span>
|
||||
<span class="tag-color-value">{{ $tag->color ?? '-' }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-warning" onclick="editTag({{ $tag->id }}, '{{ addslashes($tag->name) }}', '{{ $tag->color ?? '' }}')">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<form method="POST" action="{{ route('impostazioni.tag.destroy', $tag->id) }}" style="display: inline;">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo tag?')">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ===== MODALI TAG ===== --}}
|
||||
<div class="modal fade" id="addTagModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form method="POST" action="{{ route('impostazioni.tag.store') }}">
|
||||
@csrf
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Nuovo Tag</h4>
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="tagName">Nome *</label>
|
||||
<input type="text" name="name" id="tagName" class="form-control" required maxlength="100" placeholder="es. giovani">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="tagColor">Colore</label>
|
||||
<input type="color" name="color" id="tagColor" class="form-control" style="height:38px;padding:4px;" value="#007bff">
|
||||
<small class="text-muted">Scegli un colore per il badge del tag</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Annulla</button>
|
||||
<button type="submit" class="btn btn-primary">Salva</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="editTagModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form method="POST" action="" id="editTagForm">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Modifica Tag</h4>
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="editTagName">Nome *</label>
|
||||
<input type="text" name="name" id="editTagName" class="form-control" required maxlength="100">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editTagColor">Colore</label>
|
||||
<input type="color" name="color" id="editTagColor" class="form-control" style="height:38px;padding:4px;">
|
||||
<small class="text-muted">Scegli un colore per il badge del tag</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Annulla</button>
|
||||
<button type="submit" class="btn btn-primary">Salva</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1618,6 +1751,32 @@ var sortableTipologieEventi = Sortable.create(document.getElementById('tipologie
|
||||
}
|
||||
});
|
||||
|
||||
function editTag(id, name, color) {
|
||||
$('#editTagName').val(name);
|
||||
$('#editTagColor').val(color || '#007bff');
|
||||
$('#editTagForm').attr('action', '/impostazioni/tag/' + id);
|
||||
$('#editTagModal').modal('show');
|
||||
}
|
||||
|
||||
var sortableTag = Sortable.create(document.getElementById('tag-sortable'), {
|
||||
handle: '.handle',
|
||||
animation: 150,
|
||||
onEnd: function(evt) {
|
||||
var order = [];
|
||||
$('#tag-sortable tr').each(function() {
|
||||
order.push($(this).data('id'));
|
||||
});
|
||||
fetch('{{ route('impostazioni.tag.reorder') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ order: order })
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
var target = $(e.target).attr('href');
|
||||
if (target === '#ruoli') {
|
||||
@@ -1626,6 +1785,9 @@ $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
if (target === '#tipologie-eventi') {
|
||||
sortableTipologieEventi.refresh();
|
||||
}
|
||||
if (target === '#tag') {
|
||||
sortableTag.refresh();
|
||||
}
|
||||
if (target === '#calendario') {
|
||||
if (typeof sortableCalendario !== 'undefined') {
|
||||
sortableCalendario.refresh();
|
||||
|
||||
@@ -108,6 +108,15 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-tags mr-2"></i>Tag</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@include('partials._tag-selector', ['selectedTags' => [], 'label' => 'Etichette'])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-address-book mr-2"></i>Contatti</h3>
|
||||
|
||||
@@ -139,6 +139,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-tags mr-2"></i>Tag</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@include('partials._tag-selector', ['selectedTags' => $selectedTags ?? [], 'label' => 'Etichette'])
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-address-book mr-2"></i>Contatti</h3>
|
||||
|
||||
@@ -9,6 +9,7 @@ $columnLabels = [
|
||||
'nome' => 'Nome',
|
||||
'email' => 'Email',
|
||||
'telefono' => 'Telefono',
|
||||
'tag' => 'Tag',
|
||||
];
|
||||
$vistaDefaultJson = $vista ? $vista->toJson() : 'null';
|
||||
$allColumnsJson = json_encode($allColumns);
|
||||
@@ -47,6 +48,12 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
|
||||
<a class="dropdown-item" href="#" onclick="showCreateMailingListModal(); return false;">
|
||||
<i class="fas fa-list mr-2"></i>Crea Mailing List
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" onclick="showMassGruppoModal(); return false;">
|
||||
<i class="fas fa-users mr-2"></i>Assegna Gruppo
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" onclick="showMassTagModal(); return false;">
|
||||
<i class="fas fa-tags mr-2"></i>Assegna Tag
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item text-danger" href="#" onclick="deleteSelected(); return false;">
|
||||
<i class="fas fa-trash mr-2"></i>Elimina Selezionati
|
||||
@@ -124,6 +131,8 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('partials._tag-filter-bar', ['filterTags' => $allTags])
|
||||
|
||||
<div class="card-body p-0">
|
||||
@if($individui->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0" id="individui-table">
|
||||
@@ -150,6 +159,9 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
|
||||
@if(in_array('telefono', $visibleColumns))
|
||||
<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>
|
||||
@endif
|
||||
<th style="width: 120px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -181,6 +193,13 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
|
||||
@if(in_array('telefono', $visibleColumns))
|
||||
<td>{{ $ind->getTelefonoPrimarioAttribute() ?: '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('tag', $visibleColumns))
|
||||
<td>
|
||||
@foreach($ind->tags as $tag)
|
||||
<a href="{{ route('individui.index', ['tag[]' => $tag->slug]) }}" class="badge" style="background-color: {{ $tag->color ?? '#6c757d' }}; color: #fff;">{{ $tag->name }}</a>
|
||||
@endforeach
|
||||
</td>
|
||||
@endif
|
||||
<td>
|
||||
@if($canWriteIndividui)
|
||||
<a href="{{ url('/individui/' . $ind->id . '/edit') }}" class="btn btn-xs btn-warning" title="Modifica">
|
||||
@@ -415,6 +434,88 @@ $canDeleteIndividui = Auth::user()->canDelete('individui');
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- MODAL: Assegna Gruppo massivo --}}
|
||||
<div class="modal fade" id="massGruppoModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-info">
|
||||
<h5 class="modal-title text-white"><i class="fas fa-users mr-2"></i>Assegna Gruppo</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal"><span>×</span></button>
|
||||
</div>
|
||||
<form id="massGruppoForm" method="POST" action="/individui/mass-gruppo">
|
||||
@csrf
|
||||
<div class="modal-body">
|
||||
<p class="mb-2">Operazione su <strong id="massGruppoCount">0</strong> individui selezionati.</p>
|
||||
<div class="form-group mb-3">
|
||||
<label class="font-weight-bold">Azione</label>
|
||||
<div class="d-flex" style="gap:1.5rem;">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="mode" id="mgModeAssign" value="assign" checked>
|
||||
<label class="form-check-label" for="mgModeAssign">Assegna a gruppo</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="mode" id="mgModeRemove" value="remove">
|
||||
<label class="form-check-label" for="mgModeRemove">Rimuovi da gruppo</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Seleziona Gruppi</label>
|
||||
<select name="gruppi[]" class="form-control" multiple size="8">
|
||||
@foreach($gruppi as $g)
|
||||
<option value="{{ $g->id }}">{{ $g->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Seleziona con Ctrl/Cmd per scegliere più gruppi</small>
|
||||
</div>
|
||||
<input type="hidden" name="ids" id="massGruppoIds" value="">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annulla</button>
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-users mr-1"></i> Applica</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- MODAL: Gestione Tag massiva --}}
|
||||
<div class="modal fade" id="massTagModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-info">
|
||||
<h5 class="modal-title text-white"><i class="fas fa-tags mr-2"></i>Gestione Tag</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal"><span>×</span></button>
|
||||
</div>
|
||||
<form id="massTagForm" method="POST" action="/individui/mass-tag">
|
||||
@csrf
|
||||
<div class="modal-body">
|
||||
<p class="mb-2">Operazione su <strong id="massTagCount">0</strong> individui selezionati.</p>
|
||||
<div class="form-group mb-3">
|
||||
<label class="font-weight-bold">Azione</label>
|
||||
<div class="d-flex" style="gap:1.5rem;">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="mode" id="indMtModeAssign" value="assign" checked>
|
||||
<label class="form-check-label" for="indMtModeAssign">Assegna tag</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="mode" id="indMtModeRemove" value="remove">
|
||||
<label class="form-check-label" for="indMtModeRemove">Rimuovi tag</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@include('partials._tag-selector', ['label' => 'Seleziona tag'])
|
||||
<input type="hidden" name="ids" id="massTagIds" value="">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annulla</button>
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-tags mr-1"></i> Applica</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
@@ -444,7 +545,7 @@ 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, azioni: 6 }[col];
|
||||
const colIndex = { codice: 1, cognome: 2, nome: 3, email: 4, telefono: 5, tag: 6, azioni: 7 }[col];
|
||||
|
||||
rows.forEach(row => {
|
||||
const cells = row.querySelectorAll('td');
|
||||
@@ -476,7 +577,7 @@ function sortTable(column) {
|
||||
currentSort = { column: column, direction: 'asc' };
|
||||
}
|
||||
|
||||
const colIndex = { codice: 0, cognome: 1, nome: 2, email: 3, telefono: 4, azioni: 5 }[column];
|
||||
const colIndex = { codice: 1, cognome: 2, nome: 3, email: 4, telefono: 5, tag: 6, azioni: 7 }[column];
|
||||
const tbody = document.getElementById('table-body');
|
||||
const rows = Array.from(tbody.querySelectorAll('tr'));
|
||||
|
||||
@@ -500,7 +601,7 @@ function sortTable(column) {
|
||||
}
|
||||
|
||||
function toggleColumn(column, visible) {
|
||||
const colIndex = { codice: 1, cognome: 2, nome: 3, email: 4, telefono: 5, azioni: 6 }[column];
|
||||
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');
|
||||
|
||||
@@ -890,6 +991,28 @@ function updateMlCheckboxEmail(id, email) {
|
||||
}
|
||||
}
|
||||
|
||||
function showMassGruppoModal() {
|
||||
const ids = getSelectedIds();
|
||||
if (ids.length === 0) {
|
||||
alert('Seleziona almeno un individuo');
|
||||
return;
|
||||
}
|
||||
document.getElementById('massGruppoIds').value = ids.join(',');
|
||||
document.getElementById('massGruppoCount').textContent = ids.length;
|
||||
$('#massGruppoModal').modal('show');
|
||||
}
|
||||
|
||||
function showMassTagModal() {
|
||||
const ids = getSelectedIds();
|
||||
if (ids.length === 0) {
|
||||
alert('Seleziona almeno un individuo');
|
||||
return;
|
||||
}
|
||||
document.getElementById('massTagIds').value = ids.join(',');
|
||||
document.getElementById('massTagCount').textContent = ids.length;
|
||||
$('#massTagModal').modal('show');
|
||||
}
|
||||
|
||||
async function createMailingList() {
|
||||
const nome = document.getElementById('ml-nome').value.trim();
|
||||
if (!nome) {
|
||||
|
||||
@@ -88,6 +88,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-12">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-tags mr-2"></i>Tag</h3></div>
|
||||
<div class="card-body">
|
||||
@forelse($individuo->tags as $tag)
|
||||
<span class="badge" style="background-color:{{ $tag->color ?? '#6c757d' }};color:#fff;padding:4px 10px;border-radius:12px;font-size:0.85rem;">{{ $tag->name }}</span>
|
||||
@empty
|
||||
<p class="text-muted mb-0"><i class="fas fa-info-circle mr-1"></i>Nessun tag assegnato.</p>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($individuo->note)
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-8">
|
||||
|
||||
@@ -199,6 +199,14 @@ $appName = \App\Models\AppSetting::getAppName() ?? 'Glastree';
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@if(Auth::user()->canAccess('individui'))
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('ricerca.index') }}" class="nav-link {{ request()->routeIs('ricerca.*') ? 'active' : '' }}">
|
||||
<i class="nav-icon fas fa-tags"></i>
|
||||
<p>Ricerca per Tag</p>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@if(Auth::user()->canAccess('report'))
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('report.index') }}" class="nav-link {{ request()->routeIs('report.*') ? 'active' : '' }}">
|
||||
@@ -294,6 +302,7 @@ $appName = \App\Models\AppSetting::getAppName() ?? 'Glastree';
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/admin-lte@3.2/dist/js/adminlte.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
@stack('scripts')
|
||||
@yield('scripts')
|
||||
</body>
|
||||
</html>
|
||||
@@ -30,6 +30,9 @@
|
||||
<label>Descrizione</label>
|
||||
<textarea name="descrizione" class="form-control" rows="2"></textarea>
|
||||
</div>
|
||||
<hr>
|
||||
@include('partials._tag-selector', ['allTags' => $tags ?? collect(), 'selectedTags' => old('tags', []), 'label' => 'Tag'])
|
||||
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
<strong>Come aggiungere contatti:</strong> vai su <em>Individui</em>, seleziona le righe desired, e usa il menu <em>Azioni > Crea Mailing List</em> per creare una lista dai contatti selezionati.
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
<textarea name="descrizione" class="form-control" rows="2">{{ $mailingList->descrizione }}</textarea>
|
||||
</div>
|
||||
|
||||
@include('partials._tag-selector', ['allTags' => $tags ?? collect(), 'selectedTags' => $selectedTags ?? [], 'label' => 'Tag'])
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
@@ -64,6 +66,13 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="no-email-warning" class="alert alert-warning alert-dismissible" role="alert" style="display:none;">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<i class="fas fa-exclamation-triangle mr-2"></i>
|
||||
<strong>Attenzione:</strong> I seguenti individui non hanno un indirizzo email nei contatti:
|
||||
<ul id="no-email-list" class="mb-0 mt-1"></ul>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Contatti da includere (<span id="contatti-count">{{ $mailingList->contatti->count() }}</span>)</label>
|
||||
<div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
|
||||
@@ -185,11 +194,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
console.error('Errore individui:', err);
|
||||
});
|
||||
|
||||
contattiCaricati = Array.from(document.querySelectorAll('.contatto-checkbox')).map(cb => ({
|
||||
individuo_id: parseInt(cb.value),
|
||||
email: cb.dataset.email,
|
||||
existing: true
|
||||
}));
|
||||
contattiCaricati = Array.from(document.querySelectorAll('.contatto-checkbox')).map(cb => {
|
||||
const row = cb.closest('tr');
|
||||
return {
|
||||
individuo_id: parseInt(cb.value),
|
||||
email: cb.dataset.email || '',
|
||||
existing: true,
|
||||
codice_id: row ? (row.querySelector('td:nth-child(2)')?.textContent?.trim() || '') : '',
|
||||
cognome: row ? (row.querySelector('td:nth-child(3)')?.textContent?.trim() || '') : '',
|
||||
nome: row ? (row.querySelector('td:nth-child(4)')?.textContent?.trim() || '') : ''
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
||||
@@ -217,17 +232,15 @@ function caricaSelezionati() {
|
||||
})
|
||||
.then(data => {
|
||||
data.forEach(function(ind) {
|
||||
if (ind.emails && ind.emails.length > 0) {
|
||||
if (!contattiCaricati.find(c => c.individuo_id === ind.id)) {
|
||||
contattiCaricati.push({
|
||||
individuo_id: ind.id,
|
||||
codice_id: ind.codice_id,
|
||||
cognome: ind.cogname,
|
||||
nome: ind.nome,
|
||||
email: ind.emails[0],
|
||||
origini: ['Gruppo']
|
||||
});
|
||||
}
|
||||
if (!contattiCaricati.find(c => c.individuo_id === ind.id)) {
|
||||
contattiCaricati.push({
|
||||
individuo_id: ind.id,
|
||||
codice_id: ind.codice_id,
|
||||
cognome: ind.cognome,
|
||||
nome: ind.nome,
|
||||
email: (ind.emails && ind.emails.length > 0) ? ind.emails[0] : '',
|
||||
origini: ['Gruppo']
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
@@ -246,17 +259,15 @@ function caricaSelezionati() {
|
||||
})
|
||||
.then(data => {
|
||||
data.forEach(function(ind) {
|
||||
if (ind.emails && ind.emails.length > 0) {
|
||||
if (!contattiCaricati.find(c => c.individuo_id === ind.id)) {
|
||||
contattiCaricati.push({
|
||||
individuo_id: ind.id,
|
||||
codice_id: ind.codice_id,
|
||||
cognome: ind.cogname,
|
||||
nome: ind.nome,
|
||||
email: ind.emails[0],
|
||||
origini: [' Individuo']
|
||||
});
|
||||
}
|
||||
if (!contattiCaricati.find(c => c.individuo_id === ind.id)) {
|
||||
contattiCaricati.push({
|
||||
individuo_id: ind.id,
|
||||
codice_id: ind.codice_id,
|
||||
cognome: ind.cognome,
|
||||
nome: ind.nome,
|
||||
email: (ind.emails && ind.emails.length > 0) ? ind.emails[0] : '',
|
||||
origini: [' Individuo']
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
@@ -327,25 +338,87 @@ function deselezionaTutti() {
|
||||
}
|
||||
|
||||
document.getElementById('ml-form').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const form = this;
|
||||
const selected = [];
|
||||
|
||||
document.querySelectorAll('.contatto-checkbox:checked').forEach(function(cb) {
|
||||
const row = cb.closest('tr');
|
||||
selected.push({
|
||||
individuo_id: parseInt(cb.value),
|
||||
email: cb.dataset.email
|
||||
email: cb.dataset.email || '',
|
||||
_label: row
|
||||
? (row.querySelector('td:nth-child(2)')?.textContent?.trim() || '') + ' - ' +
|
||||
(row.querySelector('td:nth-child(3)')?.textContent?.trim() || '') + ' ' +
|
||||
(row.querySelector('td:nth-child(4)')?.textContent?.trim() || '')
|
||||
: 'ID ' + cb.value
|
||||
});
|
||||
});
|
||||
|
||||
const indSelect = document.getElementById('individui-select');
|
||||
if (indSelect) {
|
||||
Array.from(indSelect.selectedOptions).forEach(function(opt) {
|
||||
const id = parseInt(opt.value);
|
||||
if (!selected.find(function(s) { return s.individuo_id === id; })) {
|
||||
selected.push({
|
||||
individuo_id: id,
|
||||
email: '',
|
||||
_label: opt.textContent
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (selected.length === 0) {
|
||||
e.preventDefault();
|
||||
alert('Seleziona almeno un contatto con email');
|
||||
const gruppiSelect = document.getElementById('gruppi-select');
|
||||
if (gruppiSelect && gruppiSelect.selectedOptions.length > 0) {
|
||||
alert('Devi cliccare "Carica contatti" per caricare i membri dei gruppi prima di salvare.');
|
||||
} else {
|
||||
alert('Seleziona almeno un contatto.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'contatti_json';
|
||||
input.value = JSON.stringify(selected);
|
||||
this.appendChild(input);
|
||||
function completaInvio() {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'contatti_json';
|
||||
input.value = JSON.stringify(selected.map(function(s) {
|
||||
return { individuo_id: s.individuo_id, email: s.email };
|
||||
}));
|
||||
form.appendChild(input);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
const senzaEmail = selected.filter(function(s) { return !s.email; });
|
||||
|
||||
if (senzaEmail.length > 0) {
|
||||
const elencoHtml = '<ul class="text-left mb-0">' +
|
||||
senzaEmail.map(function(s) {
|
||||
return '<li>' + s._label + '</li>';
|
||||
}).join('') +
|
||||
'</ul>';
|
||||
|
||||
Swal.fire({
|
||||
title: 'Contatti senza email',
|
||||
html: '<p class="mb-2">I seguenti ' + senzaEmail.length + ' contatti non hanno un indirizzo email:</p>' +
|
||||
elencoHtml +
|
||||
'<p class="mt-3 mb-0 text-muted">La email verrà risolta automaticamente quando l\'individuo verrà aggiornato. Salvare comunque?</p>',
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Salva lo stesso',
|
||||
cancelButtonText: 'Annulla',
|
||||
confirmButtonColor: '#28a745',
|
||||
cancelButtonColor: '#6c757d'
|
||||
}).then(function(result) {
|
||||
if (result.isConfirmed) {
|
||||
completaInvio();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
completaInvio();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -34,6 +34,7 @@ $canDeleteMailing = Auth::user()->canDelete('mailing');
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@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">
|
||||
@@ -50,6 +51,7 @@ $canDeleteMailing = Auth::user()->canDelete('mailing');
|
||||
<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>
|
||||
<th style="width: 100px;">Azioni</th>
|
||||
@@ -73,6 +75,16 @@ $canDeleteMailing = Auth::user()->canDelete('mailing');
|
||||
<td>
|
||||
<span class="badge badge-info">{{ $lista->contatti->count() }}</span>
|
||||
</td>
|
||||
<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;">
|
||||
{{ $tag->name }}
|
||||
</a>
|
||||
@endforeach
|
||||
@if($lista->tags->isEmpty())
|
||||
<span class="text-muted small">-</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if($lista->attiva)
|
||||
<span class="badge badge-success">Attiva</span>
|
||||
|
||||
@@ -49,6 +49,23 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-tags mr-2"></i>Tag</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($mailingList->tags->isNotEmpty())
|
||||
@foreach($mailingList->tags as $tag)
|
||||
<span class="badge" style="background-color:{{ $tag->color ?? '#6c757d' }};color:#fff;font-size:0.85rem;padding:6px 12px;border-radius:12px;margin:2px;">
|
||||
{{ $tag->name }}
|
||||
</span>
|
||||
@endforeach
|
||||
@else
|
||||
<p class="text-muted mb-0">Nessun tag assegnato</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
|
||||
@@ -295,6 +295,22 @@
|
||||
<label>Limite risultati</label>
|
||||
<input type="number" name="limit" class="form-control" placeholder="Es: 100" min="1">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Filtra per Tag</label>
|
||||
<select name="tag_filter[]" id="tag_filter" class="form-control select2-tags" multiple style="width: 100%;">
|
||||
@foreach($allTags as $tag)
|
||||
<option value="{{ $tag->id }}" style="background-color:{{ $tag->color ?? '#6c757d' }};">{{ $tag->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Filtra i risultati per tag</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Modalità filtro tag</label>
|
||||
<select name="tag_filter_mode" class="form-control">
|
||||
<option value="any">Almeno un tag (OR)</option>
|
||||
<option value="all">Tutti i tag selezionati (AND)</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<i class="fas fa-save mr-1"></i> Salva Report
|
||||
</button>
|
||||
@@ -346,6 +362,37 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
columnsSelectInitialized = false;
|
||||
}
|
||||
|
||||
let tagFilterSelect2Initialized = false;
|
||||
|
||||
function initTagFilterSelect2() {
|
||||
if (tagFilterSelect2Initialized) return;
|
||||
$('#tag_filter').select2({
|
||||
theme: 'bootstrap4',
|
||||
placeholder: 'Seleziona tag...',
|
||||
allowClear: true,
|
||||
width: '100%',
|
||||
templateResult: function(opt) {
|
||||
if (!opt.id) return opt.text;
|
||||
var color = $(opt.element).css('background-color');
|
||||
var $span = $('<span style="padding:2px 8px;border-radius:10px;color:#fff;background-color:' + color + '">' + opt.text + '</span>');
|
||||
return $span;
|
||||
},
|
||||
templateSelection: function(opt) {
|
||||
if (!opt.id) return opt.text;
|
||||
var color = $(opt.element).css('background-color');
|
||||
var $span = $('<span style="padding:2px 8px;border-radius:10px;color:#fff;background-color:' + color + '">' + opt.text + '</span>');
|
||||
return $span;
|
||||
}
|
||||
});
|
||||
tagFilterSelect2Initialized = true;
|
||||
}
|
||||
|
||||
function destroyTagFilterSelect2() {
|
||||
if (!tagFilterSelect2Initialized) return;
|
||||
$('#tag_filter').select2('destroy');
|
||||
tagFilterSelect2Initialized = false;
|
||||
}
|
||||
|
||||
function getSortOptions(tipoReport) {
|
||||
const cols = columnOptions[tipoReport] || {};
|
||||
const options = [];
|
||||
@@ -365,8 +412,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
$('#collapseThree').on('shown.bs.collapse', function () {
|
||||
initColumnsSelect2();
|
||||
initTagFilterSelect2();
|
||||
}).on('hidden.bs.collapse', function () {
|
||||
destroyColumnsSelect2();
|
||||
destroyTagFilterSelect2();
|
||||
});
|
||||
|
||||
if ($('#collapseTwo').hasClass('show')) {
|
||||
@@ -375,6 +424,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
if ($('#collapseThree').hasClass('show')) {
|
||||
initColumnsSelect2();
|
||||
initTagFilterSelect2();
|
||||
}
|
||||
|
||||
$('#tipo_report').on('change', function() {
|
||||
|
||||
Reference in New Issue
Block a user