glastree_on_gitea
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
|
||||
@source '../../storage/framework/views/*.php';
|
||||
@source '../**/*.blade.php';
|
||||
@source '../**/*.js';
|
||||
|
||||
@theme {
|
||||
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
//
|
||||
@@ -0,0 +1,97 @@
|
||||
@extends('admin.layout')
|
||||
|
||||
@section('title', 'Log Attività')
|
||||
@section('page_title', 'Log Attività')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item active">Admin</li>
|
||||
<li class="breadcrumb-item active">Log Attività</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Attività di Sistema</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="GET" class="mb-3">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<select name="user_id" class="form-control">
|
||||
<option value="">Tutti gli utenti</option>
|
||||
@foreach($users as $u)
|
||||
<option value="{{ $u->id }}" {{ request('user_id') == $u->id ? 'selected' : '' }}>{{ $u->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select name="module" class="form-control">
|
||||
<option value="">Tutti i moduli</option>
|
||||
@foreach($modules as $module)
|
||||
<option value="{{ $module }}" {{ request('module') == $module ? 'selected' : '' }}>{{ $module }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select name="action" class="form-control">
|
||||
<option value="">Tutte le azioni</option>
|
||||
<option value="create" {{ request('action') == 'create' ? 'selected' : '' }}>Create</option>
|
||||
<option value="update" {{ request('action') == 'update' ? 'selected' : '' }}>Update</option>
|
||||
<option value="delete" {{ request('action') == 'delete' ? 'selected' : '' }}>Delete</option>
|
||||
<option value="login" {{ request('action') == 'login' ? 'selected' : '' }}>Login</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="date" name="from" class="form-control" value="{{ request('from') }}" placeholder="Da">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="date" name="to" class="form-control" value="{{ request('to') }}" placeholder="A">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary">Filtra</button>
|
||||
<a href="/admin/activity-logs" class="btn btn-secondary">Reset</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Data/Ora</th>
|
||||
<th>Utente</th>
|
||||
<th>Azione</th>
|
||||
<th>Modulo</th>
|
||||
<th>Descrizione</th>
|
||||
<th>IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($logs as $log)
|
||||
<tr>
|
||||
<td><small>{{ $log->created_at->format('d/m/Y H:i:s') }}</small></td>
|
||||
<td>{{ $log->user->name ?? 'Sistema' }}</td>
|
||||
<td>
|
||||
<span class="badge badge-{{ $log->action == 'delete' ? 'danger' : ($log->action == 'create' ? 'success' : ($log->action == 'login' ? 'info' : 'warning')) }}">
|
||||
{{ $log->action }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ $log->module }}</td>
|
||||
<td>{{ $log->description }}</td>
|
||||
<td><small>{{ $log->ip_address }}</small></td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">Nessun log presente</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ $logs->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,443 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Impostazioni Email')
|
||||
@section('page_title', 'Configurazione Email')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item"><a href="{{ route('dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="/impostazioni">Impostazioni</a></li>
|
||||
<li class="breadcrumb-item active">Email Settings</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="card card-secondary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Sezioni</h3>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<a href="#imap" class="list-group-item list-group-item-action active" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-server"></i> Server IMAP
|
||||
</a>
|
||||
<a href="#smtp" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-paper-plane"></i> Server SMTP (Invio)
|
||||
</a>
|
||||
<a href="#account" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-user"></i> Account
|
||||
</a>
|
||||
<a href="#sync" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-sync"></i> Sincronizzazione
|
||||
</a>
|
||||
<a href="#signature" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-signature"></i> Firma
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<form method="POST" action="{{ route('impostazioni.email.save') }}">
|
||||
@csrf
|
||||
<input type="hidden" name="id" value="{{ $settings->id ?? 1 }}">
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="imap">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Configurazione Server IMAP</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@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="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="imap_host">Host IMAP</label>
|
||||
<input type="text" name="imap_host" id="imap_host" class="form-control"
|
||||
value="{{ $settings->imap_host ?? 'imap.gmail.com' }}"
|
||||
placeholder="es. imap.gmail.com">
|
||||
<small class="text-muted">Gmail: imap.gmail.com | Outlook: outlook.office365.com</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label for="imap_port">Porta</label>
|
||||
<input type="number" name="imap_port" id="imap_port" class="form-control"
|
||||
value="{{ $settings->imap_port ?? 993 }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label for="imap_encryption">Crittografia</label>
|
||||
<select name="imap_encryption" id="imap_encryption" class="form-control">
|
||||
<option value="ssl" {{ ($settings->imap_encryption ?? 'ssl') == 'ssl' ? 'selected' : '' }}>SSL</option>
|
||||
<option value="tls" {{ ($settings->imap_encryption ?? '') == 'tls' ? 'selected' : '' }}>TLS</option>
|
||||
<option value="none" {{ ($settings->imap_encryption ?? '') == 'none' ? 'selected' : '' }}>Nessuna</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="smtp">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Configurazione Server SMTP (per invio email)</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<strong>Nota:</strong> Per inviare email devi configurare il server SMTP.
|
||||
Per Gmail, usa <code>smtp.gmail.com</code> sulla porta <code>587</code> con TLS
|
||||
e genera una <a href="https://support.google.com/accounts/answer/185833" target="_blank">App Password</a>.
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="smtp_host">Host SMTP</label>
|
||||
<input type="text" name="smtp_host" id="smtp_host" class="form-control"
|
||||
value="{{ $settings->smtp_host ?? 'smtp.gmail.com' }}"
|
||||
placeholder="es. smtp.gmail.com">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label for="smtp_port">Porta</label>
|
||||
<input type="number" name="smtp_port" id="smtp_port" class="form-control"
|
||||
value="{{ $settings->smtp_port ?? 587 }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group">
|
||||
<label for="smtp_encryption">Crittografia</label>
|
||||
<select name="smtp_encryption" id="smtp_encryption" class="form-control">
|
||||
<option value="tls" {{ ($settings->smtp_encryption ?? 'tls') == 'tls' ? 'selected' : '' }}>TLS</option>
|
||||
<option value="ssl" {{ ($settings->smtp_encryption ?? '') == 'ssl' ? 'selected' : '' }}>SSL</option>
|
||||
<option value="none" {{ ($settings->smtp_encryption ?? '') == 'none' ? 'selected' : '' }}>Nessuna</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="smtp_username">Username SMTP</label>
|
||||
<input type="text" name="smtp_username" id="smtp_username" class="form-control"
|
||||
value="{{ $settings->smtp_username ?? '' }}"
|
||||
placeholder="lascia vuoto per usare l'account IMAP">
|
||||
<small class="text-muted">Generalmente uguale all'account email</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="smtp_password">Password SMTP / App Password</label>
|
||||
<input type="password" name="smtp_password" id="smtp_password" class="form-control"
|
||||
placeholder="{{ $settings->smtp_password ? '••••••••' : '' }}">
|
||||
<small class="text-muted">Lascia vuoto per usare la password IMAP</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="account">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Credenziali Account</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="imap_username">Username/Email</label>
|
||||
<input type="text" name="imap_username" id="imap_username" class="form-control"
|
||||
value="{{ $settings->imap_username ?? '' }}"
|
||||
placeholder="es. tuo@gmail.com">
|
||||
<small class="text-muted">Per Gmail usa l'indirizzo completo</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="imap_password">Password / App Password</label>
|
||||
<input type="password" name="imap_password" id="imap_password" class="form-control"
|
||||
placeholder="">
|
||||
<small class="text-muted">
|
||||
<a href="https://support.google.com/accounts/answer/185833" target="_blank">
|
||||
Per Gmail: genera una App Password
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="email_address">Email Indirizzo</label>
|
||||
<input type="email" name="email_address" id="email_address" class="form-control"
|
||||
value="{{ $settings->email_address ?? '' }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="email_name">Nome Visualizzato</label>
|
||||
<input type="text" name="email_name" id="email_name" class="form-control"
|
||||
value="{{ $settings->email_name ?? '' }}"
|
||||
placeholder="es. Nome Cognome">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="reply_to">Email Risposta (Reply-To)</label>
|
||||
<input type="email" name="reply_to" id="reply_to" class="form-control"
|
||||
value="{{ $settings->reply_to ?? '' }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="sync">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Sincronizzazione</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="sync_interval_minutes">Intervallo Sync (minuti)</label>
|
||||
<select name="sync_interval_minutes" id="sync_interval_minutes" class="form-control">
|
||||
<option value="1" {{ ($settings->sync_interval_minutes ?? 5) == 1 ? 'selected' : '' }}>1 minuto</option>
|
||||
<option value="5" {{ ($settings->sync_interval_minutes ?? 5) == 5 ? 'selected' : '' }}>5 minuti</option>
|
||||
<option value="15" {{ ($settings->sync_interval_minutes ?? 5) == 15 ? 'selected' : '' }}>15 minuti</option>
|
||||
<option value="30" {{ ($settings->sync_interval_minutes ?? 5) == 30 ? 'selected' : '' }}>30 minuti</option>
|
||||
<option value="60" {{ ($settings->sync_interval_minutes ?? 5) == 60 ? 'selected' : '' }}>1 ora</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>Stato Connessione</label>
|
||||
<div class="mt-2">
|
||||
@if($settings->last_sync_at)
|
||||
<span class="badge badge-success">
|
||||
<i class="fas fa-check-circle"></i> Ultimo sync: {{ $settings->last_sync_at->format('d/m/Y H:i') }}
|
||||
</span>
|
||||
@else
|
||||
<span class="badge badge-secondary">
|
||||
<i class="fas fa-clock"></i> Mai sincronizzato
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="is_active"
|
||||
name="is_active" value="1" {{ ($settings->is_active ?? false) ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="is_active">Account Email Attivo</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="signature">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Firma Email</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="signature_enabled"
|
||||
name="signature_enabled" value="1" {{ ($settings->signature_enabled ?? true) ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="signature_enabled">Attiva firma automatica</label>
|
||||
</div>
|
||||
<small class="text-muted">Quando attivo, la firma verrà aggiunta in fondo a ogni email inviata</small>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<label for="signature">Testo Firma (formato HTML)</label>
|
||||
<div id="signature-editor" style="height: 200px;"></div>
|
||||
<input type="hidden" name="signature" id="signature_input" value="{{ $settings->signature ?? '' }}">
|
||||
<small class="text-muted">Usa il riquadro sopra per comporre la tua firma. HTML supportato.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save mr-1"></i> Salva Impostazioni
|
||||
</button>
|
||||
<button type="button" class="btn btn-info ml-2" onclick="testConnection()">
|
||||
<i class="fas fa-plug mr-1"></i> Test Connessione IMAP
|
||||
</button>
|
||||
<button type="button" class="btn btn-success ml-2" onclick="testSmtp()">
|
||||
<i class="fas fa-paper-plane mr-1"></i> Test Invio Email (SMTP)
|
||||
</button>
|
||||
<a href="{{ route('impostazioni.email.sync') }}" class="btn btn-warning ml-2">
|
||||
<i class="fas fa-sync mr-1"></i> Sincronizza Ora
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function testConnection() {
|
||||
$.ajax({
|
||||
url: '{{ route('impostazioni.email.test') }}',
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}' },
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
alert('✅ ' + response.message);
|
||||
} else {
|
||||
alert('❌ ' + response.message);
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
alert('❌ Errore nella richiesta');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function testSmtp() {
|
||||
var email = prompt('Inserisci l\'indirizzo email a cui inviare il test:');
|
||||
if (!email) return;
|
||||
|
||||
if (!email.includes('@')) {
|
||||
alert('⚠️ Indirizzo email non valido');
|
||||
return;
|
||||
}
|
||||
|
||||
var btn = event.target;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Invio...';
|
||||
|
||||
$.ajax({
|
||||
url: '{{ route('impostazioni.email.testSmtp') }}',
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}' },
|
||||
data: { email: email },
|
||||
timeout: 30000,
|
||||
success: function(response) {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-paper-plane mr-1"></i> Test Invio Email (SMTP)';
|
||||
if (response.success) {
|
||||
alert('✅ ' + response.message);
|
||||
} else {
|
||||
alert('❌ ' + response.message);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-paper-plane mr-1"></i> Test Invio Email (SMTP)';
|
||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
alert('❌ ' + xhr.responseJSON.message);
|
||||
} else {
|
||||
alert('❌ Errore: ' + status + ' - ' + error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createEditor() {
|
||||
var container = document.querySelector('#signature-editor');
|
||||
if (!container || container.querySelector('.ql-toolbar')) return;
|
||||
|
||||
var quill = new Quill('#signature-editor', {
|
||||
theme: 'snow',
|
||||
modules: {
|
||||
toolbar: [
|
||||
['bold', 'italic', 'underline'],
|
||||
[{ 'color': [] }, { 'background': [] }],
|
||||
[{ 'header': 1 }, { 'header': 2 }, { 'header': 3 }],
|
||||
[{ 'align': [] }],
|
||||
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
|
||||
['link', 'image'],
|
||||
['clean', 'code']
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
var signatureInput = document.getElementById('signature_input');
|
||||
var isEditingHtml = false;
|
||||
|
||||
quill.getModule('toolbar').addHandler('code', function() {
|
||||
if (!isEditingHtml) {
|
||||
var textarea = document.createElement('textarea');
|
||||
textarea.className = 'ql-editor';
|
||||
textarea.style.cssText = 'font-family: monospace; min-height: 150px; width: 100%;';
|
||||
textarea.value = signatureInput.value;
|
||||
quill.root.innerHTML = '';
|
||||
quill.root.appendChild(textarea);
|
||||
textarea.focus();
|
||||
isEditingHtml = true;
|
||||
} else {
|
||||
var textarea = quill.root.querySelector('textarea');
|
||||
if (textarea) {
|
||||
signatureInput.value = textarea.value;
|
||||
quill.root.innerHTML = textarea.value;
|
||||
}
|
||||
isEditingHtml = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (signatureInput.value) {
|
||||
quill.root.innerHTML = signatureInput.value;
|
||||
}
|
||||
|
||||
quill.on('text-change', function() {
|
||||
if (!isEditingHtml) {
|
||||
signatureInput.value = quill.root.innerHTML;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initSignatureEditor() {
|
||||
if (document.querySelector('#signature-editor .ql-toolbar')) return;
|
||||
|
||||
if (typeof Quill === 'undefined') {
|
||||
var script = document.createElement('script');
|
||||
script.src = 'https://cdn.quilljs.com/1.3.7/quill.min.js';
|
||||
script.onload = createEditor;
|
||||
document.head.appendChild(script);
|
||||
|
||||
var link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'https://cdn.quilljs.com/1.3.7/quill.snow.css';
|
||||
document.head.appendChild(link);
|
||||
} else {
|
||||
createEditor();
|
||||
}
|
||||
}
|
||||
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function(e) {
|
||||
if ($(e.target).attr('href') === '#signature') {
|
||||
setTimeout(initSignatureEditor, 100);
|
||||
}
|
||||
});
|
||||
|
||||
if ($('#signature').hasClass('active')) {
|
||||
initSignatureEditor();
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>@yield('title', 'Admin') - Glastree</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@3.2/dist/css/adminlte.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
@yield('styles')
|
||||
</head>
|
||||
<body class="hold-transition sidebar-mini layout-fixed">
|
||||
<div class="wrapper">
|
||||
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-widget="pushmenu" href="#"><i class="fas fa-bars"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ route('dashboard') }}">
|
||||
<i class="fas fa-home"></i> Torna al Sito
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link" data-toggle="dropdown" href="#">
|
||||
<i class="fas fa-user"></i> {{ Auth::user()->name }}
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button type="submit" class="dropdown-item">
|
||||
<i class="fas fa-sign-out-alt mr-2"></i> Esci
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<aside class="main-sidebar sidebar-dark-primary elevation-4">
|
||||
<a href="{{ route('dashboard') }}" class="brand-link">
|
||||
<i class="fas fa-shield-alt brand-image ml-3 mr-2"></i>
|
||||
<span class="brand-text font-weight-light">Admin</span>
|
||||
</a>
|
||||
|
||||
<div class="sidebar">
|
||||
<nav class="mt-2">
|
||||
<ul class="nav nav-pills nav-sidebar flex-column">
|
||||
<li class="nav-item">
|
||||
<a href="/admin/utenti" class="nav-link {{ request()->is('admin/utenti*') ? 'active' : '' }}">
|
||||
<i class="nav-icon fas fa-users"></i>
|
||||
<p>Utenti</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/ruoli" class="nav-link {{ request()->is('admin/ruoli*') ? 'active' : '' }}">
|
||||
<i class="nav-icon fas fa-user-tag"></i>
|
||||
<p>Ruoli</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/activity-logs" class="nav-link {{ request()->is('admin/activity-logs*') ? 'active' : '' }}">
|
||||
<i class="nav-icon fas fa-history"></i>
|
||||
<p>Log Attività</p>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="content-wrapper">
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0">@yield('page_title', 'Admin')</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
<li class="breadcrumb-item"><a href="{{ route('dashboard') }}">Dashboard</a></li>
|
||||
@yield('breadcrumbs')
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="container-fluid">
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">{{ session('success') }}</div>
|
||||
@endif
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger">{{ session('error') }}</div>
|
||||
@endif
|
||||
@yield('content')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="main-footer">
|
||||
<strong>{{ \App\Models\AppSetting::getFooterText() }}</strong>
|
||||
@if(\App\Models\AppSetting::shouldShowVersion() && \App\Models\AppSetting::getAppVersion())
|
||||
<span class="ml-2">v{{ \App\Models\AppSetting::getAppVersion() }}</span>
|
||||
@endif
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
|
||||
<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>
|
||||
@yield('scripts')
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,64 @@
|
||||
@extends('admin.layout')
|
||||
|
||||
@section('title', 'Crea Ruolo')
|
||||
@section('page_title', 'Crea Nuovo Ruolo')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item"><a href="/admin/ruoli">Admin</a></li>
|
||||
<li class="breadcrumb-item"><a href="/admin/ruoli">Ruoli</a></li>
|
||||
<li class="breadcrumb-item active">Nuovo</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Nuovo Ruolo</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="/admin/ruoli">
|
||||
@csrf
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="name" class="form-control" required maxlength="50" placeholder="es. Segretario">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione</label>
|
||||
<input type="text" name="description" class="form-control" maxlength="255" placeholder="es. Accesso per segretari">
|
||||
</div>
|
||||
|
||||
<h5 class="mt-4">Permessi</h5>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Modulo</th>
|
||||
<th>Nessuno</th>
|
||||
<th>Lettura</th>
|
||||
<th>Completo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(['individui', 'gruppi', 'eventi', 'documenti', 'mailing', 'viste'] as $module)
|
||||
<tr>
|
||||
<td><strong>{{ ucfirst($module) }}</strong></td>
|
||||
<td class="text-center"><input type="radio" name="permissions[{{ $module }}]" value="0" checked></td>
|
||||
<td class="text-center"><input type="radio" name="permissions[{{ $module }}]" value="1"></td>
|
||||
<td class="text-center"><input type="radio" name="permissions[{{ $module }}]" value="2"></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Salva
|
||||
</button>
|
||||
<a href="/admin/ruoli" class="btn btn-secondary">Annulla</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,83 @@
|
||||
@extends('admin.layout')
|
||||
|
||||
@section('title', 'Modifica Ruolo')
|
||||
@section('page_title', 'Modifica Ruolo: ' . $ruolo->name)
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item"><a href="/admin/ruoli">Admin</a></li>
|
||||
<li class="breadcrumb-item"><a href="/admin/ruoli">Ruoli</a></li>
|
||||
<li class="breadcrumb-item active">{{ $ruolo->name }}</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Modifica Ruolo: {{ $ruolo->name }}</h3>
|
||||
@if($usersCount > 0)
|
||||
<span class="badge badge-info ml-2">{{ $usersCount }} utenti</span>
|
||||
@endif
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">{{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ url('/admin/ruoli/' . $ruolo->id) }}">
|
||||
@csrf @method('PUT')
|
||||
<div class="form-group">
|
||||
<label>Nome</label>
|
||||
<input type="text" class="form-control" value="{{ $ruolo->name }}" disabled>
|
||||
<small class="text-muted">Il nome del ruolo non può essere modificato</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione</label>
|
||||
<input type="text" name="description" class="form-control" value="{{ old('description', $ruolo->description) }}">
|
||||
</div>
|
||||
|
||||
<h5 class="mt-4">Permessi</h5>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Modulo</th>
|
||||
<th>Nessuno</th>
|
||||
<th>Lettura</th>
|
||||
<th>Completo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(['individui', 'gruppi', 'eventi', 'documenti', 'mailing', 'viste'] as $module)
|
||||
<tr>
|
||||
<td><strong>{{ ucfirst($module) }}</strong></td>
|
||||
<td><input type="radio" name="permissions[{{ $module }}]" value="0" {{ ($ruolo->permissions[$module] ?? 0) == 0 ? 'checked' : '' }}></td>
|
||||
<td><input type="radio" name="permissions[{{ $module }}]" value="1" {{ ($ruolo->permissions[$module] ?? 0) == 1 ? 'checked' : '' }}></td>
|
||||
<td><input type="radio" name="permissions[{{ $module }}]" value="2" {{ ($ruolo->permissions[$module] ?? 0) == 2 ? 'checked' : '' }}></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@if($usersCount > 0)
|
||||
<div class="form-group mt-3">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="updateUsers" name="update_users" value="1">
|
||||
<label class="custom-control-label" for="updateUsers">
|
||||
Applica queste modifiche a tutti i {{ $usersCount }} utenti con questo ruolo
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Salva
|
||||
</button>
|
||||
<a href="/admin/ruoli" class="btn btn-secondary">Annulla</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,70 @@
|
||||
@extends('admin.layout')
|
||||
|
||||
@section('title', 'Gestione Ruoli')
|
||||
@section('page_title', 'Gestione Ruoli')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item active">Admin</li>
|
||||
<li class="breadcrumb-item active">Ruoli</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Ruoli Predefiniti</h3>
|
||||
<a href="/admin/ruoli/create" class="btn btn-success btn-sm float-right">
|
||||
<i class="fas fa-plus"></i> Nuovo Ruolo
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Descrizione</th>
|
||||
<th>Utenti</th>
|
||||
<th>Permessi</th>
|
||||
<th style="width: 100px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($ruoli as $ruolo)
|
||||
<tr>
|
||||
<td><strong>{{ $ruolo->name }}</strong></td>
|
||||
<td>{{ $ruolo->description }}</td>
|
||||
<td><span class="badge badge-info">{{ $ruolo->users_count }}</span></td>
|
||||
<td>
|
||||
@foreach($ruolo->permissions as $module => $level)
|
||||
<span class="badge badge-{{ $level == 2 ? 'success' : ($level == 1 ? 'info' : 'secondary') }}">
|
||||
{{ ucfirst($module) }}: {{ $level == 2 ? 'Full' : ($level == 1 ? 'Read' : 'None') }}
|
||||
</span>
|
||||
@endforeach
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ url('/admin/ruoli/' . $ruolo->id . '/edit') }}" class="btn btn-xs btn-warning">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
@if($ruolo->users_count == 0)
|
||||
<form method="POST" action="{{ url('/admin/ruoli/' . $ruolo->id) }}" style="display: inline;">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo ruolo?')">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<button type="button" class="btn btn-xs btn-secondary" disabled title="Utenti associati: {{ $ruolo->users_count }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,101 @@
|
||||
@extends('admin.layout')
|
||||
|
||||
@section('title', 'Crea Utente')
|
||||
@section('page_title', 'Crea Nuovo Utente')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item"><a href="/admin/utenti">Admin</a></li>
|
||||
<li class="breadcrumb-item"><a href="/admin/utenti">Utenti</a></li>
|
||||
<li class="breadcrumb-item active">Nuovo</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Dati Utente</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="/admin/utenti">
|
||||
@csrf
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="name" class="form-control" required value="{{ old('name') }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Email *</label>
|
||||
<input type="email" name="email" class="form-control" required value="{{ old('email') }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password *</label>
|
||||
<input type="password" name="password" class="form-control" required minlength="8">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Conferma Password *</label>
|
||||
<input type="password" name="password_confirmation" class="form-control" required minlength="8">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Ruolo Predefinito</label>
|
||||
<select name="role_preset_id" class="form-control" id="rolePresetSelect">
|
||||
<option value="">Personalizzato</option>
|
||||
@foreach($rolePresets as $preset)
|
||||
<option value="{{ $preset->id }}">{{ $preset->name }} - {{ $preset->description }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="isAdmin" name="is_admin" value="1">
|
||||
<label class="custom-control-label" for="isAdmin">Superadmin (bypass ACL)</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="permissionsSection" class="mt-4">
|
||||
<h5>Permessi Granulari</h5>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Modulo</th>
|
||||
<th>Nessuno</th>
|
||||
<th>Lettura</th>
|
||||
<th>Completo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(['individui', 'gruppi', 'eventi', 'documenti', 'mailing', 'viste'] as $module)
|
||||
<tr>
|
||||
<td><strong>{{ ucfirst($module) }}</strong></td>
|
||||
<td><input type="radio" name="permissions[{{ $module }}]" value="0" {{ old('permissions.' . $module, '1') == '0' ? 'checked' : '' }}></td>
|
||||
<td><input type="radio" name="permissions[{{ $module }}]" value="1" {{ old('permissions.' . $module, '1') == '1' ? 'checked' : '' }}></td>
|
||||
<td><input type="radio" name="permissions[{{ $module }}]" value="2" {{ old('permissions.' . $module, '1') == '2' ? 'checked' : '' }}></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Salva
|
||||
</button>
|
||||
<a href="/admin/utenti" class="btn btn-secondary">Annulla</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
$('#rolePresetSelect').change(function() {
|
||||
if ($(this).val()) {
|
||||
$('#permissionsSection').hide();
|
||||
} else {
|
||||
$('#permissionsSection').show();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,111 @@
|
||||
@extends('admin.layout')
|
||||
|
||||
@section('title', 'Modifica Utente')
|
||||
@section('page_title', 'Modifica Utente: ' . $user->name)
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item"><a href="/admin/utenti">Admin</a></li>
|
||||
<li class="breadcrumb-item"><a href="/admin/utenti">Utenti</a></li>
|
||||
<li class="breadcrumb-item active">{{ $user->name }}</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Dati Utente</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url('/admin/utenti/' . $user->id) }}">
|
||||
@csrf @method('PUT')
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="name" class="form-control" required value="{{ old('name', $user->name) }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Email *</label>
|
||||
<input type="email" name="email" class="form-control" required value="{{ old('email', $user->email) }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Nuova Password <small>(lascia vuoto per non cambiare)</small></label>
|
||||
<input type="password" name="password" class="form-control" minlength="8">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Conferma Password</label>
|
||||
<input type="password" name="password_confirmation" class="form-control" minlength="8">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Stato</label>
|
||||
<select name="status" class="form-control" required>
|
||||
<option value="active" {{ $user->status === 'active' ? 'selected' : '' }}>Attivo</option>
|
||||
<option value="suspended" {{ $user->status === 'suspended' ? 'selected' : '' }}>Sospeso</option>
|
||||
<option value="pending" {{ $user->status === 'pending' ? 'selected' : '' }}>In attesa</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Ruolo Predefinito</label>
|
||||
<select name="role_preset_id" class="form-control" id="rolePresetSelect">
|
||||
<option value="">Personalizzato</option>
|
||||
@foreach($rolePresets as $preset)
|
||||
<option value="{{ $preset->id }}" {{ $user->role_preset_id == $preset->id ? 'selected' : '' }}>
|
||||
{{ $preset->name }} - {{ $preset->description }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="isAdmin" name="is_admin" value="1" {{ $user->isSuperAdmin() ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="isAdmin">Superadmin (bypass ACL)</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="permissionsSection" class="mt-4">
|
||||
<h5>Permessi Granulari</h5>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Modulo</th>
|
||||
<th>Nessuno</th>
|
||||
<th>Lettura</th>
|
||||
<th>Completo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach(['individui', 'gruppi', 'eventi', 'documenti', 'mailing', 'viste'] as $module)
|
||||
<tr>
|
||||
<td><strong>{{ ucfirst($module) }}</strong></td>
|
||||
<td><input type="radio" name="permissions[{{ $module }}]" value="0" {{ ($user->permissions[$module] ?? 0) == 0 ? 'checked' : '' }}></td>
|
||||
<td><input type="radio" name="permissions[{{ $module }}]" value="1" {{ ($user->permissions[$module] ?? 0) == 1 ? 'checked' : '' }}></td>
|
||||
<td><input type="radio" name="permissions[{{ $module }}]" value="2" {{ ($user->permissions[$module] ?? 0) == 2 ? 'checked' : '' }}></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Salva
|
||||
</button>
|
||||
<a href="/admin/utenti" class="btn btn-secondary">Annulla</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
$('#rolePresetSelect').change(function() {
|
||||
if ($(this).val()) {
|
||||
$('#permissionsSection').hide();
|
||||
} else {
|
||||
$('#permissionsSection').show();
|
||||
}
|
||||
}).trigger('change');
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,109 @@
|
||||
@extends('admin.layout')
|
||||
|
||||
@section('title', 'Gestione Utenti')
|
||||
@section('page_title', 'Gestione Utenti')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item active">Admin</li>
|
||||
<li class="breadcrumb-item active">Utenti</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Utenti Registrati</h3>
|
||||
<a href="/admin/utenti/create" class="btn btn-success btn-sm float-right">
|
||||
<i class="fas fa-plus"></i> Nuovo Utente
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="GET" class="mb-3">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<input type="text" name="search" class="form-control" placeholder="Cerca nome o email..." value="{{ request('search') }}">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="status" class="form-control">
|
||||
<option value="">Tutti gli stati</option>
|
||||
<option value="active" {{ request('status') == 'active' ? 'selected' : '' }}>Attivo</option>
|
||||
<option value="suspended" {{ request('status') == 'suspended' ? 'selected' : '' }}>Sospeso</option>
|
||||
<option value="pending" {{ request('status') == 'pending' ? 'selected' : '' }}>In attesa</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary">Filtra</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="table table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Email</th>
|
||||
<th>Stato</th>
|
||||
<th>Permessi</th>
|
||||
<th>Creato</th>
|
||||
<th style="width: 150px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($utenti as $utente)
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ $utente->name }}</strong>
|
||||
@if($utente->isSuperAdmin())
|
||||
<span class="badge badge-danger ml-1">Admin</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $utente->email }}</td>
|
||||
<td>
|
||||
@switch($utente->status)
|
||||
@case('active')
|
||||
<span class="badge badge-success">Attivo</span>
|
||||
@break
|
||||
@case('suspended')
|
||||
<span class="badge badge-secondary">Sospeso</span>
|
||||
@break
|
||||
@case('pending')
|
||||
<span class="badge badge-warning">In attesa</span>
|
||||
@break
|
||||
@endswitch
|
||||
</td>
|
||||
<td>
|
||||
@php $summary = $utente->getPermissionsSummary(); @endphp
|
||||
<small>
|
||||
@foreach($summary as $module => $level)
|
||||
@if($level !== 'Nessuno')
|
||||
<span class="text-{{ $level === 'Completo' ? 'success' : 'info' }}">{{ ucfirst($module) }}: {{ $level }}</span><br>
|
||||
@endif
|
||||
@endforeach
|
||||
</small>
|
||||
</td>
|
||||
<td>{{ $utente->created_at->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
<a href="{{ url('/admin/utenti/' . $utente->id . '/edit') }}" class="btn btn-xs btn-warning">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
@if($utente->id !== auth()->id())
|
||||
<form method="POST" action="{{ url('/admin/utenti/' . $utente->id) }}" style="display: inline;">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo utente?')">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{{ $utenti->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
use App\Models\AppSetting;
|
||||
$appLogo = AppSetting::getLogoUrl();
|
||||
$appName = AppSetting::getAppName() ?? 'Glastree';
|
||||
$appOrgName = AppSetting::getOrgName() ?? '';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Accedi - {{ e($appName) }}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@3.2/dist/css/adminlte.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
</head>
|
||||
<body class="hold-transition login-page">
|
||||
<div class="login-box">
|
||||
<div class="login-logo">
|
||||
<a href="/">
|
||||
@if($appLogo)
|
||||
<img src="{{ $appLogo }}" alt="Logo" style="max-height: 80px; max-width: 200px;">
|
||||
@else
|
||||
<i class="fas fa-tree text-success"></i> <b>{{ e($appName) }}</b>
|
||||
@endif
|
||||
</a>
|
||||
</div>
|
||||
@if($appOrgName)
|
||||
<div class="text-center mb-3">
|
||||
<small class="text-muted">{{ e($appOrgName) }}</small>
|
||||
</div>
|
||||
@endif
|
||||
<div class="card">
|
||||
<div class="card-body login-card-body">
|
||||
<p class="login-box-msg">Accedi per continuare</p>
|
||||
<form action="{{ route('login') }}" method="POST">
|
||||
@csrf
|
||||
<div class="input-group mb-3">
|
||||
<input type="email" name="email" class="form-control @error('email') is-invalid @enderror" placeholder="Email" required>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<span class="fas fa-envelope"></span>
|
||||
</div>
|
||||
</div>
|
||||
@error('email')
|
||||
<span class="invalid-feedback">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<input type="password" name="password" class="form-control @error('password') is-invalid @enderror" placeholder="Password" required>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<span class="fas fa-lock"></span>
|
||||
</div>
|
||||
</div>
|
||||
@error('password')
|
||||
<span class="invalid-feedback">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<div class="icheck-primary">
|
||||
<input type="checkbox" id="remember">
|
||||
<label for="remember">Ricordami</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<button type="submit" class="btn btn-success btn-block">Accedi</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<p class="mb-0 text-center mt-3">
|
||||
<a href="{{ route('register') }}" class="text-center">Non hai un account? Registrati</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
use App\Models\AppSetting;
|
||||
$appName = AppSetting::getAppName() ?? 'Glastree';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Registrati - {{ e($appName) }}</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@3.2/dist/css/adminlte.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
</head>
|
||||
<body class="hold-transition register-page">
|
||||
<div class="register-box">
|
||||
<div class="login-logo">
|
||||
<a href="/"><i class="fas fa-tree text-success"></i> <b>{{ e($appName) }}</b></a>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body register-card-body">
|
||||
<p class="login-box-msg">Crea un nuovo account</p>
|
||||
<form action="{{ route('register') }}" method="POST">
|
||||
@csrf
|
||||
<div class="input-group mb-3">
|
||||
<input type="text" name="name" class="form-control @error('name') is-invalid @enderror" placeholder="Nome completo" value="{{ old('name') }}" required>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<span class="fas fa-user"></span>
|
||||
</div>
|
||||
</div>
|
||||
@error('name')
|
||||
<span class="invalid-feedback">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<input type="email" name="email" class="form-control @error('email') is-invalid @enderror" placeholder="Email" value="{{ old('email') }}" required>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<span class="fas fa-envelope"></span>
|
||||
</div>
|
||||
</div>
|
||||
@error('email')
|
||||
<span class="invalid-feedback">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<input type="password" name="password" class="form-control @error('password') is-invalid @enderror" placeholder="Password" required>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<span class="fas fa-lock"></span>
|
||||
</div>
|
||||
</div>
|
||||
@error('password')
|
||||
<span class="invalid-feedback">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="input-group mb-3">
|
||||
<input type="password" name="password_confirmation" class="form-control" placeholder="Conferma password" required>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text">
|
||||
<span class="fas fa-lock"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<button type="submit" class="btn btn-success btn-block">Registrati</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<p class="mb-0 text-center mt-3">
|
||||
<a href="{{ route('login') }}" class="text-center">Hai già un account? Accedi</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,149 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Modifica Documento')
|
||||
@section('page_title', 'Modifica Documento')
|
||||
|
||||
@section('content')
|
||||
<form action="/documenti/{{ $documento->id }}" method="POST">
|
||||
@csrf @method('PUT')
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-file mr-2"></i>Dati Documento</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="nome_file" class="form-control" value="{{ old('nome_file', $documento->nome_file) }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Tipologia *</label>
|
||||
<select name="tipologia" class="form-control" required>
|
||||
@foreach($tipologie as $tip)
|
||||
<option value="{{ $tip->nome }}" {{ $documento->tipologia === $tip->nome ? 'selected' : '' }}>{{ $tip->descrizione ?? ucfirst($tip->nome) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Contesto *</label>
|
||||
<select name="contesto_tipo" id="contesto_tipo" class="form-control" onchange="aggiornaTarget()">
|
||||
<option value="">Seleziona tipo...</option>
|
||||
<option value="individuo" {{ $documento->visibilita === 'individuo' ? 'selected' : '' }}>Individuo</option>
|
||||
<option value="gruppo" {{ $documento->visibilita === 'gruppo' ? 'selected' : '' }}>Gruppo</option>
|
||||
<option value="evento" {{ $documento->visibilita === 'evento' ? 'selected' : '' }}>Evento</option>
|
||||
<option value="mailing" {{ $documento->visibilita === 'mailing' ? 'selected' : '' }}>Mailing List</option>
|
||||
</select>
|
||||
<input type="hidden" name="visibilita" id="visibilita" value="{{ $documento->visibilita }}">
|
||||
</div>
|
||||
<div class="form-group" id="target_container" style="{{ in_array($documento->visibilita, ['individuo', 'gruppo', 'evento', 'mailing']) ? '' : 'display:none;' }}">
|
||||
<label>Riferimento *</label>
|
||||
<select name="visibilita_target_id" id="visibilita_target_id" class="form-control">
|
||||
@if($documento->visibilita === 'individuo')
|
||||
@foreach($individui as $ind)
|
||||
<option value="{{ $ind->id }}" {{ $documento->visibilita_target_id == $ind->id ? 'selected' : '' }}>{{ $ind->cognome }} {{ $ind->nome }}</option>
|
||||
@endforeach
|
||||
@elseif($documento->visibilita === 'gruppo')
|
||||
@foreach($gruppi as $grp)
|
||||
<option value="{{ $grp->id }}" {{ $documento->visibilita_target_id == $grp->id ? 'selected' : '' }}>{{ $grp->nome }}</option>
|
||||
@endforeach
|
||||
@elseif($documento->visibilita === 'evento')
|
||||
@foreach($eventi as $evt)
|
||||
<option value="{{ $evt->id }}" {{ $documento->visibilita_target_id == $evt->id ? 'selected' : '' }}>{{ $evt->nome_evento }}</option>
|
||||
@endforeach
|
||||
@elseif($documento->visibilita === 'mailing')
|
||||
@foreach($mailingLists as $ml)
|
||||
<option value="{{ $ml->id }}" {{ $documento->visibilita_target_id == $ml->id ? 'selected' : '' }}>{{ $ml->nome }}</option>
|
||||
@endforeach
|
||||
@endif
|
||||
</select>
|
||||
<input type="hidden" name="visibilita_target_type" id="visibilita_target_type" value="">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Note</label>
|
||||
<textarea name="note" class="form-control" rows="3">{{ old('note', $documento->note) }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-info-circle mr-2"></i>Informazioni</h3>
|
||||
</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>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva Modifiche
|
||||
</button>
|
||||
<a href="/documenti" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left mr-1"></i> Torna all'elenco
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function aggiornaTarget() {
|
||||
var tipo = document.getElementById('contesto_tipo').value;
|
||||
var container = document.getElementById('target_container');
|
||||
var targetSelect = document.getElementById('visibilita_target_id');
|
||||
var visibilita = document.getElementById('visibilita');
|
||||
var targetType = document.getElementById('visibilita_target_type');
|
||||
|
||||
visibilita.value = tipo;
|
||||
targetType.value = '';
|
||||
|
||||
if (tipo) {
|
||||
container.style.display = 'block';
|
||||
targetSelect.innerHTML = '<option value="">Caricamento...</option>';
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', '/documenti/options?tipo=' + tipo, true);
|
||||
xhr.onload = function() {
|
||||
if (xhr.status === 200) {
|
||||
targetSelect.innerHTML = '<option value="">Seleziona...</option>' + xhr.responseText;
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
} else {
|
||||
container.style.display = 'none';
|
||||
targetSelect.innerHTML = '<option value="">Seleziona prima il tipo...</option>';
|
||||
targetSelect.value = '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,624 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Documenti')
|
||||
@section('page_title', 'Gestione Documenti')
|
||||
|
||||
@php
|
||||
$canWriteDocumenti = Auth::user()->canManage('documenti');
|
||||
$canDeleteDocumenti = Auth::user()->canDelete('documenti');
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
<div class="card card-primary card-outline">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-file mr-2"></i>Elenco Documenti
|
||||
</h3>
|
||||
@if($canWriteDocumenti)
|
||||
<button type="button" class="btn btn-sm btn-success float-right" onclick="$('#uploadModal').modal('show')">
|
||||
<i class="fas fa-plus mr-1"></i> Nuovo Documento
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<form id="massForm" method="POST">
|
||||
@csrf
|
||||
<input type="hidden" name="_method" id="formMethod" value="POST">
|
||||
@if($canDeleteDocumenti)
|
||||
<div class="p-3 bg-light border-bottom d-flex align-items-center flex-wrap gap-2">
|
||||
<span class="font-weight-bold mr-2">Azioni di massa:</span>
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="massAction('delete')">
|
||||
<i class="fas fa-trash mr-1"></i> Elimina selezionati
|
||||
</button>
|
||||
@if($canWriteDocumenti)
|
||||
<button type="button" class="btn btn-sm btn-warning" onclick="$('#associateModal').modal('show')">
|
||||
<i class="fas fa-link mr-1"></i> Associa a...
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-info" onclick="$('#updateModal').modal('show')">
|
||||
<i class="fas fa-edit mr-1"></i> Cambia tipo/contesto
|
||||
</button>
|
||||
@endif
|
||||
<span class="ml-auto text-muted small" id="selectedCount">0 selezionati</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 40px;">
|
||||
@if($canDeleteDocumenti)
|
||||
<input type="checkbox" id="selectAll" onchange="toggleAll(this)">
|
||||
@endif
|
||||
</th>
|
||||
<th style="width: 200px;">Nome</th>
|
||||
<th style="width: 180px;">Nome File</th>
|
||||
<th style="width: 100px;">Tipologia</th>
|
||||
<th style="width: 100px;">Visibilità</th>
|
||||
<th>Riferimento</th>
|
||||
<th style="width: 130px;">Data Inserimento</th>
|
||||
<th style="width: 120px;">Inserito da</th>
|
||||
<th style="width: 130px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($documenti as $documento)
|
||||
<tr>
|
||||
<td>
|
||||
@if($canDeleteDocumenti)
|
||||
<input type="checkbox" name="ids[]" value="{{ $documento->id }}" class="doc-checkbox" onchange="updateCount()">
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<i class="fas fa-file text-secondary mr-1"></i>
|
||||
{{ $documento->nome_file }}
|
||||
</td>
|
||||
<td class="small text-muted">{{ $documento->file_path }}</td>
|
||||
<td>
|
||||
<span class="badge badge-{{ $documento->tipologia === 'avatar' ? 'primary' : ($documento->tipologia === 'galleria' ? 'success' : 'secondary') }}">
|
||||
{{ ucfirst($documento->tipologia) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
@switch($documento->visibilita)
|
||||
@case('individuo')
|
||||
<span class="badge badge-info">Individuo</span>
|
||||
@break
|
||||
@case('gruppo')
|
||||
<span class="badge badge-success">Gruppo</span>
|
||||
@break
|
||||
@case('evento')
|
||||
<span class="badge badge-warning">Evento</span>
|
||||
@break
|
||||
@case('pubblico')
|
||||
<span class="badge badge-light">Pubblico</span>
|
||||
@break
|
||||
@case('associazione')
|
||||
<span class="badge badge-dark">Associazione</span>
|
||||
@break
|
||||
@case('federazione')
|
||||
<span class="badge badge-purple">Federazione</span>
|
||||
@break
|
||||
@default
|
||||
<span class="text-muted">-</span>
|
||||
@endswitch
|
||||
</td>
|
||||
<td>
|
||||
@if($documento->target)
|
||||
@if($documento->target instanceof \App\Models\Individuo)
|
||||
<a href="/individui/{{ $documento->target->id }}">
|
||||
<i class="fas fa-user text-info mr-1"></i>
|
||||
{{ $documento->target->cognome }} {{ $documento->target->nome }}
|
||||
</a>
|
||||
@elseif($documento->target instanceof \App\Models\Gruppo)
|
||||
<a href="/gruppi/{{ $documento->target->id }}">
|
||||
<i class="fas fa-folder text-warning mr-1"></i>
|
||||
{{ $documento->target->nome }}
|
||||
</a>
|
||||
@elseif($documento->target instanceof \App\Models\Evento)
|
||||
<a href="/eventi/{{ $documento->target->id }}">
|
||||
<i class="fas fa-calendar text-primary mr-1"></i>
|
||||
{{ $documento->target->nome_evento }}
|
||||
</a>
|
||||
@endif
|
||||
@else
|
||||
<span class="text-muted">-</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $documento->created_at->format('d/m/Y H:i') }}</td>
|
||||
<td>
|
||||
@if($documento->user)
|
||||
{{ $documento->user->name }}
|
||||
@else
|
||||
<span class="text-muted">-</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if($documento->file_path)
|
||||
<button type="button" class="btn btn-xs btn-primary" onclick="previewDoc({{ $documento->id }}, '{{ $documento->mime_type }}')" title="Anteprima">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
@endif
|
||||
@if($canWriteDocumenti)
|
||||
<a href="/documenti/{{ $documento->id }}/edit" class="btn btn-xs btn-warning" title="Modifica">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
@endif
|
||||
@if($canDeleteDocumenti)
|
||||
<form action="/documenti/{{ $documento->id }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="button" class="btn btn-xs btn-danger" onclick="deleteDocumento({{ $documento->id }}, '{{ e($documento->nome_file) }}')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="9" class="text-center text-muted py-4">
|
||||
<i class="fas fa-file fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun documento presente</p>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<div class="p-3 border-top">
|
||||
{{ $documenti->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="associateModal" 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-link mr-2"></i>Associa a</h5>
|
||||
<button type="button" class="close" onclick="$('#associateModal').modal('hide')">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="associateForm" method="POST" action="/documenti/mass-associate">
|
||||
@csrf
|
||||
<div class="form-group">
|
||||
<label>Tipo collegamento</label>
|
||||
<select name="target_type" class="form-control" required onchange="loadTargets(this.value)">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="individuo">Individuo</option>
|
||||
<option value="gruppo">Gruppo</option>
|
||||
<option value="evento">Evento</option>
|
||||
<option value="mailing">Mailing List</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Riferimento</label>
|
||||
<select name="target_id" class="form-control" required id="targetSelect">
|
||||
<option value="">Seleziona prima il tipo...</option>
|
||||
</select>
|
||||
</div>
|
||||
<input type="hidden" name="ids" id="associateIds">
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="$('#associateModal').modal('hide')">Annulla</button>
|
||||
<button type="button" class="btn btn-primary" onclick="submitAssociate()">Associa</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="updateModal" 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-edit mr-2"></i>Cambia tipo/contesto</h5>
|
||||
<button type="button" class="close" onclick="$('#updateModal').modal('hide')">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="updateForm" method="POST" action="/documenti/mass-update">
|
||||
@csrf
|
||||
<div class="form-group">
|
||||
<label>Nuova tipologia</label>
|
||||
<select name="tipologia" class="form-control">
|
||||
<option value="">Non modificare</option>
|
||||
@foreach($tipologie as $tip)
|
||||
<option value="{{ $tip->nome }}">{{ $tip->descrizione ?? ucfirst($tip->nome) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Nuovo contesto</label>
|
||||
<select name="visibilita" class="form-control">
|
||||
<option value="">Non modificare</option>
|
||||
<option value="pubblico">Pubblico</option>
|
||||
<option value="individuo">Individuo</option>
|
||||
<option value="gruppo">Gruppo</option>
|
||||
<option value="evento">Evento</option>
|
||||
<option value="associazione">Associazione</option>
|
||||
<option value="federazione">Federazione</option>
|
||||
<option value="mailing">Mailing List</option>
|
||||
</select>
|
||||
</div>
|
||||
<input type="hidden" name="ids" id="updateIds">
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="$('#updateModal').modal('hide')">Annulla</button>
|
||||
<button type="button" class="btn btn-primary" onclick="submitUpdate()">Aggiorna</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="previewModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" style="max-width: 90vw;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-eye mr-2"></i>Anteprima</h5>
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body p-0" style="min-height: 400px;">
|
||||
<iframe id="previewFrame" src="" style="width: 100%; height: 70vh; border: none;"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a id="previewDownloadBtn" href="#" class="btn btn-primary" download>
|
||||
<i class="fas fa-download mr-1"></i> Scarica
|
||||
</a>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Chiudi</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="uploadModal" 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-upload mr-2"></i>Carica Nuovo Documento</h5>
|
||||
<button type="button" class="close" onclick="$('#uploadModal').modal('hide')">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="/documenti" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="nome_file" class="form-control" placeholder="Nome del documento" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Tipologia *</label>
|
||||
<select name="tipologia" class="form-control" required>
|
||||
@foreach($tipologie as $tip)
|
||||
<option value="{{ $tip->nome }}">{{ $tip->descrizione ?? ucfirst($tip->nome) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Visibilità *</label>
|
||||
<select name="visibilita" class="form-control" required id="uploadVisibilita" onchange="loadUploadTargets()">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="pubblico">Pubblico</option>
|
||||
<option value="individuo">Individuo</option>
|
||||
<option value="gruppo">Gruppo</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="uploadTargetGroup" style="display:none;">
|
||||
<label>Riferimento</label>
|
||||
<select name="visibilita_target_id" class="form-control" id="uploadTargetId">
|
||||
<option value="">Seleziona...</option>
|
||||
</select>
|
||||
<input type="hidden" name="visibilita_target_type" id="uploadTargetType">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>File *</label>
|
||||
<input type="file" name="file" class="form-control" required>
|
||||
<small class="text-muted">Max 10MB</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="$('#uploadModal').modal('hide')">Annulla</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-upload mr-1"></i> Carica
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="deleteConfirmModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-danger text-white">
|
||||
<h5 class="modal-title"><i class="fas fa-exclamation-triangle mr-2"></i>Conferma Eliminazione</h5>
|
||||
<button type="button" class="close text-white" onclick="$('#deleteConfirmModal').modal('hide')">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p id="deleteConfirmMessage"></p>
|
||||
<div id="deleteConfirmLinks" class="mt-3"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="$('#deleteConfirmModal').modal('hide')">Annulla</button>
|
||||
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">
|
||||
<i class="fas fa-trash mr-1"></i> Elimina comunque
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="massDeleteConfirmModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-danger text-white">
|
||||
<h5 class="modal-title"><i class="fas fa-exclamation-triangle mr-2"></i>Conferma Eliminazione Multipla</h5>
|
||||
<button type="button" class="close text-white" onclick="$('#massDeleteConfirmModal').modal('hide')">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Stai per eliminare <strong id="massDeleteCount">0</strong> documenti.</p>
|
||||
<div id="massDeleteLinks" class="mt-3"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="$('#massDeleteConfirmModal').modal('hide')">Annulla</button>
|
||||
<button type="button" class="btn btn-danger" id="confirmMassDeleteBtn">
|
||||
<i class="fas fa-trash mr-1"></i> Elimina comunque
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<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]));
|
||||
const eventi = @json($eventi->map(fn($e) => ['id' => $e->id, 'label' => $e->nome_evento]));
|
||||
const mailingLists = @json($mailingLists ?? []);
|
||||
|
||||
function toggleAll(source) {
|
||||
document.querySelectorAll('.doc-checkbox').forEach(cb => cb.checked = source.checked);
|
||||
updateCount();
|
||||
}
|
||||
|
||||
function updateCount() {
|
||||
const count = document.querySelectorAll('.doc-checkbox:checked').length;
|
||||
document.getElementById('selectedCount').textContent = count + ' selezionati';
|
||||
}
|
||||
|
||||
function getSelectedIds() {
|
||||
return Array.from(document.querySelectorAll('.doc-checkbox:checked')).map(cb => cb.value);
|
||||
}
|
||||
|
||||
let pendingMassDeleteIds = [];
|
||||
|
||||
function massAction(action) {
|
||||
const ids = getSelectedIds();
|
||||
if (ids.length === 0) {
|
||||
alert('Seleziona almeno un documento');
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'delete') {
|
||||
fetch('/documenti/check-links', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ ids: ids })
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
pendingMassDeleteIds = ids;
|
||||
document.getElementById('massDeleteCount').textContent = ids.length;
|
||||
|
||||
if (data.linked_count > 0) {
|
||||
let linksHtml = '<div class="alert alert-warning mb-0"><p><strong>Attenzione:</strong> ' + data.linked_count + ' documento(i) sono collegati a elementi nel database:</p><ul class="mb-0">';
|
||||
data.links.forEach(function(link) {
|
||||
if (link.url) {
|
||||
linksHtml += '<li><a href="' + link.url + '" target="_blank">' + link.name + '</a></li>';
|
||||
} else {
|
||||
linksHtml += '<li>' + link.name + '</li>';
|
||||
}
|
||||
});
|
||||
linksHtml += '</ul></div>';
|
||||
document.getElementById('massDeleteLinks').innerHTML = linksHtml;
|
||||
} else {
|
||||
document.getElementById('massDeleteLinks').innerHTML = '<div class="alert alert-info mb-0">Nessun documento risulta collegato.</div>';
|
||||
}
|
||||
|
||||
document.getElementById('confirmMassDeleteBtn').onclick = function() { performMassDelete(); };
|
||||
$('#massDeleteConfirmModal').modal('show');
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Errore controllo collegamenti: ' + error.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function performMassDelete() {
|
||||
const ids = pendingMassDeleteIds;
|
||||
$('#massDeleteConfirmModal').modal('hide');
|
||||
|
||||
fetch('/documenti/mass-destroy', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: 'ids=' + ids.join(',')
|
||||
})
|
||||
.then(response => {
|
||||
if (response.redirected) {
|
||||
window.location.href = response.url;
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Errore: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteDocumento(id, nomeFile) {
|
||||
fetch('/documenti/' + id + '/check-links', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.has_links) {
|
||||
let linksHtml = '<div class="alert alert-warning mb-0"><p class="mb-2"><strong>Attenzione:</strong> Questo documento è collegato a:</p><ul class="mb-0">';
|
||||
data.links.forEach(function(link) {
|
||||
if (link.url) {
|
||||
linksHtml += '<li><a href="' + link.url + '" target="_blank">' + link.name + '</a></li>';
|
||||
} else {
|
||||
linksHtml += '<li>' + link.name + '</li>';
|
||||
}
|
||||
});
|
||||
linksHtml += '</ul></div>';
|
||||
document.getElementById('deleteConfirmLinks').innerHTML = linksHtml;
|
||||
document.getElementById('deleteConfirmMessage').textContent = 'Eliminare il documento "' + nomeFile + '"?';
|
||||
document.getElementById('confirmDeleteBtn').onclick = function() { performDelete(id); };
|
||||
$('#deleteConfirmModal').modal('show');
|
||||
} else {
|
||||
if (confirm('Eliminare il documento "' + nomeFile + '"?')) {
|
||||
performDelete(id);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Errore: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function performDelete(id) {
|
||||
fetch('/documenti/' + id, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: '_method=DELETE'
|
||||
})
|
||||
.then(response => {
|
||||
if (response.redirected) {
|
||||
window.location.href = response.url;
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Errore: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function previewDoc(id, mimeType) {
|
||||
document.getElementById('previewFrame').src = '/documenti/' + id + '/preview';
|
||||
document.getElementById('previewDownloadBtn').href = '/documenti/' + id + '/download';
|
||||
$('#previewModal').modal('show');
|
||||
}
|
||||
|
||||
function loadUploadTargets() {
|
||||
const visibilita = document.getElementById('uploadVisibilita').value;
|
||||
const group = document.getElementById('uploadTargetGroup');
|
||||
const select = document.getElementById('uploadTargetId');
|
||||
const type = document.getElementById('uploadTargetType');
|
||||
|
||||
if (visibilita === 'pubblico') {
|
||||
group.style.display = 'none';
|
||||
select.innerHTML = '<option value="">Seleziona...</option>';
|
||||
type.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
group.style.display = 'block';
|
||||
let data = [];
|
||||
if (visibilita === 'individuo') {
|
||||
data = individui;
|
||||
type.value = 'individuo';
|
||||
} else if (visibilita === 'gruppo') {
|
||||
data = gruppi;
|
||||
type.value = 'gruppo';
|
||||
}
|
||||
|
||||
select.innerHTML = '<option value="">Seleziona...</option>';
|
||||
data.forEach(item => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = item.id;
|
||||
opt.textContent = item.label;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
function loadTargets(type) {
|
||||
const select = document.getElementById('targetSelect');
|
||||
select.innerHTML = '<option value="">Caricamento...</option>';
|
||||
|
||||
let data = [];
|
||||
if (type === 'individuo') data = individui;
|
||||
else if (type === 'gruppo') data = gruppi;
|
||||
else if (type === 'evento') data = eventi;
|
||||
else if (type === 'mailing') data = mailingLists;
|
||||
|
||||
select.innerHTML = '<option value="">Seleziona...</option>';
|
||||
data.forEach(item => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = item.id;
|
||||
opt.textContent = item.label;
|
||||
select.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
function submitAssociate() {
|
||||
const ids = getSelectedIds();
|
||||
if (ids.length === 0) {
|
||||
alert('Seleziona almeno un documento');
|
||||
return;
|
||||
}
|
||||
const targetType = document.querySelector('#associateForm select[name="target_type"]').value;
|
||||
const targetId = document.getElementById('targetSelect').value;
|
||||
if (!targetType || !targetId) {
|
||||
alert('Seleziona tipo e riferimento');
|
||||
return;
|
||||
}
|
||||
document.getElementById('associateIds').value = ids.join(',');
|
||||
document.getElementById('associateForm').submit();
|
||||
}
|
||||
|
||||
function submitUpdate() {
|
||||
const ids = getSelectedIds();
|
||||
console.log('Selected IDs:', ids);
|
||||
if (ids.length === 0) {
|
||||
alert('Seleziona almeno un documento');
|
||||
return;
|
||||
}
|
||||
const tipologia = document.querySelector('#updateForm select[name="tipologia"]').value;
|
||||
const visibilita = document.querySelector('#updateForm select[name="visibilita"]').value;
|
||||
console.log('Tipologia:', tipologia, 'Visibilita:', visibilita);
|
||||
if (!tipologia && !visibilita) {
|
||||
alert('Seleziona almeno una tipologia o un contesto da modificare');
|
||||
return;
|
||||
}
|
||||
document.getElementById('updateIds').value = ids.join(',');
|
||||
console.log('Form submitting with IDs:', document.getElementById('updateIds').value);
|
||||
document.getElementById('updateForm').submit();
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,165 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Nuova Email')
|
||||
@section('page_title', 'Composizione Email')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-edit mr-2"></i>Nuova Email</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('email.send') }}" id="emailForm">
|
||||
@csrf
|
||||
|
||||
<div class="form-group">
|
||||
<label>Destinatari *</label>
|
||||
<div class="mb-2">
|
||||
<select name="destinatario_tipo" id="destinatario_tipo" class="form-control" onchange="toggleDestinatari()">
|
||||
<option value="manuale">Email Manuale</option>
|
||||
<option value="individui">Singoli Individui</option>
|
||||
<option value="gruppo">Membri di un Gruppo</option>
|
||||
<option value="mailing_list">Mailing List</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="manual-input" class="destinatari-section">
|
||||
<input type="text" name="to" id="to_manual" class="form-control"
|
||||
placeholder="destinatario@email.it" value="{{ $prefill['to'] ?? '' }}">
|
||||
</div>
|
||||
|
||||
<div id="individui-input" class="destinatari-section" style="display: none;">
|
||||
<select name="individui[]" id="individui-select" class="form-control" multiple size="5">
|
||||
@foreach($individui as $ind)
|
||||
<option value="{{ $ind->id }}">{{ $ind->cognito }} {{ $ind->nome }} ({{ $ind->email_primaria ?? 'senza email' }})</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Tieni premuto Ctrl per selezionare più individui</small>
|
||||
</div>
|
||||
|
||||
<div id="gruppo-input" class="destinatari-section" style="display: none;">
|
||||
<select name="gruppo_id" id="gruppo-select" class="form-control mb-2" onchange="loadGruppiContatti()">
|
||||
<option value="">Seleziona gruppo...</option>
|
||||
@foreach($gruppi as $gruppo)
|
||||
<option value="{{ $gruppo->id }}">{{ $gruppo->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<div id="gruppo-contatti" class="border rounded p-2" style="max-height: 150px; overflow-y: auto;">
|
||||
<em class="text-muted">Seleziona un gruppo per vedere i contatti</em>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mailing-list-input" class="destinatari-section" style="display: none;">
|
||||
<select name="mailing_list_id" id="mailing-list-select" class="form-control">
|
||||
<option value="">Seleziona mailing list...</option>
|
||||
@foreach($mailingLists as $list)
|
||||
<option value="{{ $list->id }}">{{ $list->nome }} ({{ $list->contatti->count() }} contatti)</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="subject">Oggetto *</label>
|
||||
<input type="text" name="subject" id="subject" class="form-control"
|
||||
value="{{ $prefill['subject'] ?? '' }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="body">Messaggio *</label>
|
||||
<textarea name="body" id="body" class="form-control" rows="15" required>{{ $prefill['body'] ?? '' }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Allegati</label>
|
||||
<div class="border rounded p-3 bg-light">
|
||||
<label class="btn btn-sm btn-success mb-0">
|
||||
<i class="fas fa-upload mr-1"></i> Carica file
|
||||
<input type="file" name="allegati[]" multiple class="d-none">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-paper-plane mr-1"></i> Invia
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary ml-2" onclick="saveDraft()">
|
||||
<i class="fas fa-save mr-1"></i> Salva Bozze
|
||||
</button>
|
||||
<a href="{{ route('email.index') }}" class="btn btn-default ml-2">
|
||||
Annulla
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function toggleDestinatari() {
|
||||
const tipo = document.getElementById('destinatario_tipo').value;
|
||||
|
||||
document.querySelectorAll('.destinatari-section').forEach(el => el.style.display = 'none');
|
||||
|
||||
if (tipo === 'manuale') {
|
||||
document.getElementById('manual-input').style.display = 'block';
|
||||
} else if (tipo === 'individui') {
|
||||
document.getElementById('individui-input').style.display = 'block';
|
||||
} else if (tipo === 'gruppo') {
|
||||
document.getElementById('gruppo-input').style.display = 'block';
|
||||
} else if (tipo === 'mailing_list') {
|
||||
document.getElementById('mailing-list-input').style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
function loadGruppiContatti() {
|
||||
const gruppoId = document.getElementById('gruppo-select').value;
|
||||
const container = document.getElementById('gruppo-contatti');
|
||||
|
||||
if (!gruppoId) {
|
||||
container.innerHTML = '<em class="text-muted">Seleziona un gruppo per vedere i contatti</em>';
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/gruppi/' + gruppoId + '/individui-email')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.length === 0) {
|
||||
container.innerHTML = '<em class="text-muted">Nessun contatto email nel gruppo</em>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
data.forEach(ind => {
|
||||
if (ind.email) {
|
||||
html += `<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="gruppo_emails[]" value="${ind.email}" id="email-${ind.id}">
|
||||
<label class="form-check-label" for="email-${ind.id}">${ind.cogname} ${ind.nome} - ${ind.email}</label>
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
container.innerHTML = html || '<em class="text-muted">Nessun contatto email nel gruppo</em>';
|
||||
});
|
||||
}
|
||||
|
||||
function saveDraft() {
|
||||
const form = document.getElementById('emailForm');
|
||||
const formData = new FormData(form);
|
||||
|
||||
fetch('{{ route('email.draft') }}', {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}' },
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
alert('Bozza salvata!');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,340 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Email - ' . ucfirst($folder))
|
||||
@section('page_title', 'Email')
|
||||
|
||||
@php
|
||||
$currentFolder = $folders->firstWhere('type', $folder);
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-envelope mr-2"></i>Email</h3>
|
||||
<div class="card-tools">
|
||||
<div class="btn-group mr-2">
|
||||
@foreach($folders as $f)
|
||||
<a href="{{ route('email.index', $f->type) }}"
|
||||
class="btn btn-xs {{ $folder === $f->type ? 'btn-primary' : 'btn-default' }}">
|
||||
<i class="fas {{ $f->icon }}"></i>
|
||||
{{ $f->name }}
|
||||
@if($f->type === 'inbox' && $messages->where('is_read', false)->count() > 0)
|
||||
<span class="badge badge-light">{{ $messages->where('is_read', false)->count() }}</span>
|
||||
@endif
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
<a href="{{ route('email.compose') }}" class="btn btn-success btn-xs">
|
||||
<i class="fas fa-plus"></i> Nuova Email
|
||||
</a>
|
||||
<button class="btn btn-primary btn-xs" onclick="syncEmails()" id="syncBtn">
|
||||
<i class="fas fa-sync"></i> Ricevi/Invia
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div class="card-header pb-0">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6">
|
||||
<div class="form-inline">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="selectAll" onchange="toggleAll(this)">
|
||||
<label class="custom-control-label" for="selectAll">Seleziona tutto</label>
|
||||
</div>
|
||||
<span class="ml-3 text-muted" id="selectedCount">0 selezionati</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="input-group input-group-sm float-right" style="width: 220px;">
|
||||
<select class="form-control" id="bulkAction">
|
||||
<option value="">Azioni di massa...</option>
|
||||
@if($folder !== 'trash')
|
||||
<option value="mark_read">Marca come letto</option>
|
||||
<option value="mark_unread">Marca come non letto</option>
|
||||
<option value="star">Aggiungi a preferiti</option>
|
||||
<option value="unstar">Rimuovi da preferiti</option>
|
||||
<option value="move_trash">Sposta nel cestino</option>
|
||||
@else
|
||||
<option value="empty_trash">Svuota cestino</option>
|
||||
@endif
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary" type="button" onclick="executeBulkAction()">Applica</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 30px;"></th>
|
||||
<th style="width: 30px;"></th>
|
||||
<th style="width: 150px;">
|
||||
@php $sort = request('sort'); $dir = request('direction'); @endphp
|
||||
@if($folder === 'sent' || $folder === 'inviate')
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'to_email', 'direction' => $sort === 'to_email' && $dir === 'asc' ? 'desc' : 'asc']) }}" class="text-dark text-decoration-none">
|
||||
Destinatario {!! $sort === 'to_email' ? ($dir === 'asc' ? '▲' : '▼') : '' !!}
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'from_email', 'direction' => $sort === 'from_email' && $dir === 'asc' ? 'desc' : 'asc']) }}" class="text-dark text-decoration-none">
|
||||
Mittente {!! $sort === 'from_email' ? ($dir === 'asc' ? '▲' : '▼') : '' !!}
|
||||
</a>
|
||||
@endif
|
||||
</th>
|
||||
<th>
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'subject', 'direction' => $sort === 'subject' && $dir === 'asc' ? 'desc' : 'asc']) }}" class="text-dark text-decoration-none">
|
||||
Oggetto {!! $sort === 'subject' ? ($dir === 'asc' ? '▲' : '▼') : '' !!}
|
||||
</a>
|
||||
</th>
|
||||
<th style="width: 120px;">
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'received_at', 'direction' => $sort === 'received_at' && $dir === 'asc' ? 'desc' : 'asc']) }}" class="text-dark text-decoration-none">
|
||||
Data {!! $sort === 'received_at' ? ($dir === 'asc' ? '▲' : '▼') : '' !!}
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($messages as $message)
|
||||
<tr class="{{ $message->is_read ? '' : 'font-weight-bold' }}">
|
||||
<td style="width: 30px;" onclick="event.stopPropagation()">
|
||||
<input type="checkbox" class="email-checkbox" value="{{ $message->id }}" onchange="updateCount()">
|
||||
</td>
|
||||
<td style="width: 30px;" onclick="event.stopPropagation(); window.location='{{ route('email.show', [$folder, $message->id]) }}'">
|
||||
@if($message->is_starred)
|
||||
<i class="fas fa-star text-warning"></i>
|
||||
@endif
|
||||
</td>
|
||||
<td onclick="window.location='{{ route('email.show', [$folder, $message->id]) }}'">
|
||||
{{ $message->is_sent ? 'A: ' . $message->to_email : ($message->from_name ?: $message->from_email) }}
|
||||
</td>
|
||||
<td onclick="window.location='{{ route('email.show', [$folder, $message->id]) }}'">
|
||||
{{ $message->subject ?? '(senza oggetto)' }}
|
||||
</td>
|
||||
<td>
|
||||
@if($message->received_at)
|
||||
{{ $message->received_at->format('d/m/Y') }}
|
||||
@elseif($message->sent_at)
|
||||
{{ $message->sent_at->format('d/m/Y') }}
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted py-4">
|
||||
<i class="fas fa-inbox fa-2x mb-2"></i>
|
||||
<p>Nessuna email</p>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
{{ $messages->withQueryString()->links('vendor.pagination.simple-bootstrap-4') }}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('styles')
|
||||
<style>
|
||||
/* Bootstrap 4 Pagination override */
|
||||
.pagination {
|
||||
display: flex !important;
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
border-radius: 0.25rem;
|
||||
margin: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.page-item {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.page-link {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 0.5rem 0.75rem;
|
||||
margin-left: -1px;
|
||||
line-height: 1.25;
|
||||
color: #007bff;
|
||||
background-color: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
text-decoration: none;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.page-item.active .page-link {
|
||||
z-index: 3;
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
.page-item.disabled .page-link {
|
||||
color: #6c757d;
|
||||
pointer-events: none;
|
||||
background-color: #fff;
|
||||
border-color: #dee2e6;
|
||||
}
|
||||
|
||||
.page-link:hover {
|
||||
color: #0056b3;
|
||||
background-color: #e9ecef;
|
||||
border-color: #dee2e6;
|
||||
}
|
||||
|
||||
/* Icons as text characters */
|
||||
.pagination .page-link::before,
|
||||
.pagination .page-link::after {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
|
||||
@if($syncInterval > 0 && $folder === 'inbox')
|
||||
@push('scripts')
|
||||
<script>
|
||||
let syncInterval = {{ $syncInterval * 60 * 1000 }};
|
||||
let syncTimeout;
|
||||
|
||||
function autoSync() {
|
||||
fetch('{{ url('email/sync/quick') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}).then(() => {
|
||||
console.log('Auto sync completed');
|
||||
}).catch(() => {
|
||||
console.log('Auto sync failed');
|
||||
});
|
||||
}
|
||||
|
||||
function startAutoSync() {
|
||||
if (syncInterval > 0) {
|
||||
syncTimeout = setInterval(autoSync, syncInterval);
|
||||
}
|
||||
}
|
||||
|
||||
function stopAutoSync() {
|
||||
if (syncTimeout) {
|
||||
clearInterval(syncTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
startAutoSync();
|
||||
|
||||
document.addEventListener('visibilitychange', function() {
|
||||
if (document.hidden) {
|
||||
stopAutoSync();
|
||||
} else {
|
||||
startAutoSync();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endif
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function toggleAll(checkbox) {
|
||||
const checkboxes = document.querySelectorAll('.email-checkbox');
|
||||
checkboxes.forEach(function(cb) {
|
||||
cb.checked = checkbox.checked;
|
||||
});
|
||||
updateCount();
|
||||
}
|
||||
|
||||
function updateCount() {
|
||||
const checkboxes = document.querySelectorAll('.email-checkbox:checked');
|
||||
document.getElementById('selectedCount').textContent = checkboxes.length + ' selezionati';
|
||||
}
|
||||
|
||||
function executeBulkAction() {
|
||||
const select = document.getElementById('bulkAction');
|
||||
const action = select.value;
|
||||
if (!action) {
|
||||
alert('Seleziona un\'azione');
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === 'empty_trash') {
|
||||
if (!confirm('Sei sicuro di voler svuotare il cestino? Questa azione non è reversibile.')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const checkboxes = document.querySelectorAll('.email-checkbox:checked');
|
||||
if (action !== 'empty_trash' && checkboxes.length === 0) {
|
||||
alert('Seleziona almeno una email');
|
||||
return;
|
||||
}
|
||||
|
||||
let ids = [];
|
||||
if (action !== 'empty_trash') {
|
||||
ids = Array.from(checkboxes).map(function(cb) {
|
||||
return cb.value;
|
||||
});
|
||||
}
|
||||
|
||||
fetch('{{ route('email.bulk') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ ids: ids, action: action })
|
||||
}).then(function(response) {
|
||||
return response.json();
|
||||
}).then(function(data) {
|
||||
if (data.success) {
|
||||
// Reset checkboxes before reload to avoid ghost selection
|
||||
document.querySelectorAll('.email-checkbox').forEach(function(cb) {
|
||||
cb.checked = false;
|
||||
});
|
||||
document.getElementById('selectAll').checked = false;
|
||||
updateCount();
|
||||
window.location.href = window.location.href;
|
||||
} else {
|
||||
alert(data.message || 'Errore');
|
||||
}
|
||||
}).catch(function() {
|
||||
alert('Errore');
|
||||
});
|
||||
}
|
||||
|
||||
function syncEmails() {
|
||||
const btn = document.getElementById('syncBtn');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Sincronizzazione...';
|
||||
|
||||
fetch('{{ url('email/sync/quick') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}).then(function(response) {
|
||||
return response.json();
|
||||
}).then(function(data) {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.message || 'Errore durante la sincronizzazione');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-sync"></i> Ricevi/Invia';
|
||||
}
|
||||
}).catch(function(error) {
|
||||
alert('Errore di connessione');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-sync"></i> Ricevi/Invia';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,124 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', $message->subject ?: 'Email')
|
||||
@section('page_title', 'Visualizza Email')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="card card-primary">
|
||||
<div class="card-body p-0">
|
||||
<div class="list-group list-group-flush">
|
||||
<a href="{{ route('email.compose') }}" class="list-group-item list-group-item-action">
|
||||
<i class="fas fa-plus"></i> Nuova Email
|
||||
</a>
|
||||
@foreach($folders as $f)
|
||||
<a href="{{ route('email.index', $f->type) }}"
|
||||
class="list-group-item list-group-item-action {{ $folder === $f->type ? 'active' : '' }}">
|
||||
<i class="fas {{ $f->icon }}"></i> {{ $f->name }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-9">
|
||||
<div class="card card-primary card-outline">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ $message->subject ?: '(senza oggetto)' }}</h3>
|
||||
<div class="card-tools">
|
||||
@if($message->is_starred)
|
||||
<button class="btn btn-xs btn-warning" onclick="toggleStar({{ $message->id }})">
|
||||
<i class="fas fa-star"></i>
|
||||
</button>
|
||||
@else
|
||||
<button class="btn btn-xs btn-default" onclick="toggleStar({{ $message->id }})">
|
||||
<i class="far fa-star"></i>
|
||||
</button>
|
||||
@endif
|
||||
<a href="{{ route('email.compose', ['reply_to' => $message->id]) }}" class="btn btn-xs btn-default">
|
||||
<i class="fas fa-reply"></i> Rispondi
|
||||
</a>
|
||||
<a href="{{ route('email.compose', ['forward' => $message->id]) }}" class="btn btn-xs btn-default">
|
||||
<i class="fas fa-forward"></i> Inoltra
|
||||
</a>
|
||||
<form method="POST" action="{{ route('email.destroy', $message->id) }}" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Sposta nel cestino?')">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mailbox-read-info">
|
||||
<h6>
|
||||
<strong>Da:</strong> {{ $message->from_name }} <{{ $message->from_email }}>
|
||||
<span class="mailbox-read-time float-right">
|
||||
@if($message->received_at)
|
||||
{{ $message->received_at->format('d/m/Y H:i') }}
|
||||
@elseif($message->sent_at)
|
||||
{{ $message->sent_at->format('d/m/Y H:i') }}
|
||||
@endif
|
||||
</span>
|
||||
</h6>
|
||||
<h6>
|
||||
<strong>A:</strong> {{ $message->to_email }}
|
||||
@if($message->cc)
|
||||
<br><strong>CC:</strong> {{ $message->cc }}
|
||||
@endif
|
||||
</h6>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="mailbox-read-message">
|
||||
{!! $message->body_for_display !!}
|
||||
</div>
|
||||
@if($message->attachments->count() > 0)
|
||||
<hr>
|
||||
<div class="mailbox-attachments">
|
||||
<h4>Allegati:</h4>
|
||||
<ul class="mailbox-attachments-links">
|
||||
@foreach($message->attachments as $attachment)
|
||||
<li>
|
||||
<span class="mailbox-attachment-icon">
|
||||
<i class="fas {{ $attachment->icon }}"></i>
|
||||
</span>
|
||||
<div class="mailbox-attachment-info">
|
||||
<a href="{{ route('email.attachment.download', $attachment->id) }}" class="mailbox-attachment-name">
|
||||
<i class="fas fa-paperclip"></i> {{ $attachment->filename }}
|
||||
</a>
|
||||
<span class="mailbox-attachment-size">
|
||||
{{ number_format($attachment->size / 1024, 1) }} KB
|
||||
</span>
|
||||
@if(!$attachment->is_saved_to_documenti)
|
||||
<form method="POST" action="{{ route('email.attachment.save', [$message->id, $attachment->id]) }}" class="d-inline">
|
||||
@csrf
|
||||
<button type="submit" class="btn btn-xs btn-success" onclick="return confirm('Salvare allegato nei documenti?')">
|
||||
<i class="fas fa-save"></i> Salva in Documenti
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<span class="badge badge-success"><i class="fas fa-check"></i> Salvato</span>
|
||||
@endif
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function toggleStar(id) {
|
||||
fetch('/email/' + id + '/star', {
|
||||
method: 'POST',
|
||||
headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}' }
|
||||
}).then(() => location.reload());
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,97 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Calendario Eventi')
|
||||
@section('page_title', 'Calendario Eventi')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-calendar-alt mr-2"></i>Calendario Eventi
|
||||
</h3>
|
||||
<div class="card-tools">
|
||||
<a href="{{ route('eventi.index') }}" class="btn btn-secondary btn-sm">
|
||||
<i class="fas fa-list mr-1"></i> Elenco Eventi
|
||||
</a>
|
||||
<a href="{{ route('eventi.create') }}" class="btn btn-success btn-sm">
|
||||
<i class="fas fa-plus mr-1"></i> Nuovo Evento
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="calendar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('styles')
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.css">
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.10/index.global.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var calendarEl = document.getElementById('calendar');
|
||||
|
||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
initialView: 'dayGridMonth',
|
||||
locale: 'it',
|
||||
headerToolbar: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'dayGridMonth,timeGridWeek,listWeek'
|
||||
},
|
||||
buttonText: {
|
||||
today: 'Oggi',
|
||||
month: 'Mese',
|
||||
week: 'Settimana',
|
||||
list: 'Lista'
|
||||
},
|
||||
eventSources: [
|
||||
{
|
||||
url: '/eventi/calendar/events',
|
||||
method: 'GET',
|
||||
extraParams: function() {
|
||||
return {
|
||||
_token: document.querySelector('meta[name="csrf-token"]')?.content || ''
|
||||
};
|
||||
},
|
||||
startParam: 'start',
|
||||
endParam: 'end',
|
||||
success: function(response) {
|
||||
console.log('Eventi caricati:', response.length, response);
|
||||
},
|
||||
failure: function(error) {
|
||||
console.error('Errore:', error);
|
||||
console.log('XHR:', error.xhr);
|
||||
alert('Errore nel caricamento degli eventi. Controlla la console per dettagli.');
|
||||
}
|
||||
}
|
||||
],
|
||||
eventClick: function(info) {
|
||||
info.jsEvent.preventDefault();
|
||||
window.location.href = info.event.url;
|
||||
},
|
||||
eventDisplay: 'block',
|
||||
displayEventTime: true,
|
||||
displayEventEnd: true,
|
||||
nowIndicator: true,
|
||||
height: 'auto',
|
||||
expandRows: true,
|
||||
stickyHeaderDates: true,
|
||||
dayMaxEvents: true,
|
||||
eventTimeFormat: {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
}
|
||||
});
|
||||
|
||||
calendar.render();
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,250 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Nuovo Evento')
|
||||
@section('page_title', 'Nuovo Evento')
|
||||
|
||||
@section('content')
|
||||
<form action="/eventi" method="POST">
|
||||
@csrf
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-calendar mr-2"></i>Dati Evento</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Nome Evento *</label>
|
||||
<input type="text" name="nome_evento" class="form-control @error('nome_evento') is-invalid @enderror" value="{{ old('nome_evento') }}" required>
|
||||
@error('nome_evento')
|
||||
<span class="invalid-feedback">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione Breve</label>
|
||||
<input type="text" name="descrizione_evento" class="form-control" value="{{ old('descrizione_evento') }}" placeholder="Breve descrizione">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione Completa</label>
|
||||
<textarea name="descrizione" class="form-control" rows="3">{{ old('descrizione') }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="is_incontro_gruppo" value="1" {{ old('is_incontro_gruppo') ? 'checked' : '' }}>
|
||||
Questo evento è il giorno di incontro del gruppo
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-clock mr-2"></i>Data e Orario</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Tipo Ricorrenza</label>
|
||||
<select name="tipo_recorrenza" id="tipo_recorrenza" class="form-control" onchange="toggleRicorrenza()">
|
||||
<option value="singolo" {{ old('tipo_recorrenza', 'singolo') === 'singolo' ? 'selected' : '' }}>Singolo</option>
|
||||
<option value="settimanale" {{ old('tipo_recorrenza') === 'settimanale' ? 'selected' : '' }}>Settimanale</option>
|
||||
<option value="mensile" {{ old('tipo_recorrenza') === 'mensile' ? 'selected' : '' }}>Mensile</option>
|
||||
<option value="annuale" {{ old('tipo_recorrenza') === 'annuale' ? 'selected' : '' }}>Annuale</option>
|
||||
<option value="altro" {{ old('tipo_recorrenza') === 'altro' ? 'selected' : '' }}>Altro</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="data_specifica_field" style="{{ old('tipo_recorrenza') === 'singolo' || old('tipo_recorrenza') === null ? '' : 'display:none;' }}">
|
||||
<div class="form-group">
|
||||
<label>Data Evento</label>
|
||||
<input type="date" name="data_specifica" class="form-control" value="{{ old('data_specifica') }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="settimanale_field" style="{{ old('tipo_recorrenza') === 'settimanale' ? '' : 'display:none;' }}">
|
||||
<div class="form-group">
|
||||
<label>Giorno della Settimana</label>
|
||||
<select name="giorno_settimana" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="0" {{ old('giorno_settimana') === '0' ? 'selected' : '' }}>Domenica</option>
|
||||
<option value="1" {{ old('giorno_settimana') == '1' ? 'selected' : '' }}>Lunedì</option>
|
||||
<option value="2" {{ old('giorno_settimana') == '2' ? 'selected' : '' }}>Martedì</option>
|
||||
<option value="3" {{ old('giorno_settimana') == '3' ? 'selected' : '' }}>Mercoledì</option>
|
||||
<option value="4" {{ old('giorno_settimana') == '4' ? 'selected' : '' }}>Giovedì</option>
|
||||
<option value="5" {{ old('giorno_settimana') == '5' ? 'selected' : '' }}>Venerdì</option>
|
||||
<option value="6" {{ old('giorno_settimana') == '6' ? 'selected' : '' }}>Sabato</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mensile_field" style="{{ old('tipo_recorrenza') === 'mensile' ? '' : 'display:none;' }}">
|
||||
<div class="form-group">
|
||||
<label>Occorrenza nel mese</label>
|
||||
<select name="occorrenza_mese" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="1,1" {{ old('occorrenza_mese') === '1,1' ? 'selected' : '' }}>1° Lunedì</option>
|
||||
<option value="1,2" {{ old('occorrenza_mese') === '1,2' ? 'selected' : '' }}>1° Martedì</option>
|
||||
<option value="1,3" {{ old('occorrenza_mese') === '1,3' ? 'selected' : '' }}>1° Mercoledì</option>
|
||||
<option value="1,4" {{ old('occorrenza_mese') === '1,4' ? 'selected' : '' }}>1° Giovedì</option>
|
||||
<option value="1,5" {{ old('occorrenza_mese') === '1,5' ? 'selected' : '' }}>1° Venerdì</option>
|
||||
<option value="1,6" {{ old('occorrenza_mese') === '1,6' ? 'selected' : '' }}>1° Sabato</option>
|
||||
<option value="1,0" {{ old('occorrenza_mese') === '1,0' ? 'selected' : '' }}>1° Domenica</option>
|
||||
<option value="2,1" {{ old('occorrenza_mese') === '2,1' ? 'selected' : '' }}>2° Lunedì</option>
|
||||
<option value="2,2" {{ old('occorrenza_mese') === '2,2' ? 'selected' : '' }}>2° Martedì</option>
|
||||
<option value="2,3" {{ old('occorrenza_mese') === '2,3' ? 'selected' : '' }}>2° Mercoledì</option>
|
||||
<option value="2,4" {{ old('occorrenza_mese') === '2,4' ? 'selected' : '' }}>2° Giovedì</option>
|
||||
<option value="2,5" {{ old('occorrenza_mese') === '2,5' ? 'selected' : '' }}>2° Venerdì</option>
|
||||
<option value="2,6" {{ old('occorrenza_mese') === '2,6' ? 'selected' : '' }}>2° Sabato</option>
|
||||
<option value="2,0" {{ old('occorrenza_mese') === '2,0' ? 'selected' : '' }}>2° Domenica</option>
|
||||
<option value="3,1" {{ old('occorrenza_mese') === '3,1' ? 'selected' : '' }}>3° Lunedì</option>
|
||||
<option value="3,2" {{ old('occorrenza_mese') === '3,2' ? 'selected' : '' }}>3° Martedì</option>
|
||||
<option value="3,3" {{ old('occorrenza_mese') === '3,3' ? 'selected' : '' }}>3° Mercoledì</option>
|
||||
<option value="3,4" {{ old('occorrenza_mese') === '3,4' ? 'selected' : '' }}>3° Giovedì</option>
|
||||
<option value="3,5" {{ old('occorrenza_mese') === '3,5' ? 'selected' : '' }}>3° Venerdì</option>
|
||||
<option value="3,6" {{ old('occorrenza_mese') === '3,6' ? 'selected' : '' }}>3° Sabato</option>
|
||||
<option value="3,0" {{ old('occorrenza_mese') === '3,0' ? 'selected' : '' }}>3° Domenica</option>
|
||||
<option value="4,1" {{ old('occorrenza_mese') === '4,1' ? 'selected' : '' }}>4° Lunedì</option>
|
||||
<option value="4,2" {{ old('occorrenza_mese') === '4,2' ? 'selected' : '' }}>4° Martedì</option>
|
||||
<option value="4,3" {{ old('occorrenza_mese') === '4,3' ? 'selected' : '' }}>4° Mercoledì</option>
|
||||
<option value="4,4" {{ old('occorrenza_mese') === '4,4' ? 'selected' : '' }}>4° Giovedì</option>
|
||||
<option value="4,5" {{ old('occorrenza_mese') === '4,5' ? 'selected' : '' }}>4° Venerdì</option>
|
||||
<option value="4,6" {{ old('occorrenza_mese') === '4,6' ? 'selected' : '' }}>4° Sabato</option>
|
||||
<option value="4,0" {{ old('occorrenza_mese') === '4,0' ? 'selected' : '' }}>4° Domenica</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Mesi (multipli)</label>
|
||||
<select name="mesi_recorrenza[]" class="form-control" multiple size="4">
|
||||
<option value="1" {{ is_array(old('mesi_recorrenza')) && in_array('1', old('mesi_recorrenza')) ? 'selected' : '' }}>Gennaio</option>
|
||||
<option value="2" {{ is_array(old('mesi_recorrenza')) && in_array('2', old('mesi_recorrenza')) ? 'selected' : '' }}>Febbraio</option>
|
||||
<option value="3" {{ is_array(old('mesi_recorrenza')) && in_array('3', old('mesi_recorrenza')) ? 'selected' : '' }}>Marzo</option>
|
||||
<option value="4" {{ is_array(old('mesi_recorrenza')) && in_array('4', old('mesi_recorrenza')) ? 'selected' : '' }}>Aprile</option>
|
||||
<option value="5" {{ is_array(old('mesi_recorrenza')) && in_array('5', old('mesi_recorrenza')) ? 'selected' : '' }}>Maggio</option>
|
||||
<option value="6" {{ is_array(old('mesi_recorrenza')) && in_array('6', old('mesi_recorrenza')) ? 'selected' : '' }}>Giugno</option>
|
||||
<option value="7" {{ is_array(old('mesi_recorrenza')) && in_array('7', old('mesi_recorrenza')) ? 'selected' : '' }}>Luglio</option>
|
||||
<option value="8" {{ is_array(old('mesi_recorrenza')) && in_array('8', old('mesi_recorrenza')) ? 'selected' : '' }}>Agosto</option>
|
||||
<option value="9" {{ is_array(old('mesi_recorrenza')) && in_array('9', old('mesi_recorrenza')) ? 'selected' : '' }}>Settembre</option>
|
||||
<option value="10" {{ is_array(old('mesi_recorrenza')) && in_array('10', old('mesi_recorrenza')) ? 'selected' : '' }}>Ottobre</option>
|
||||
<option value="11" {{ is_array(old('mesi_recorrenza')) && in_array('11', old('mesi_recorrenza')) ? 'selected' : '' }}>Novembre</option>
|
||||
<option value="12" {{ is_array(old('mesi_recorrenza')) && in_array('12', old('mesi_recorrenza')) ? 'selected' : '' }}>Dicembre</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">Ctrl+clic per selezionare più mesi</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="annuale_field" style="{{ old('tipo_recorrenza') === 'annuale' ? '' : 'display:none;' }}">
|
||||
<div class="form-group">
|
||||
<label>Mese</label>
|
||||
<select name="mese_annuale" class="form-control">
|
||||
<option value="">Seleziona mese...</option>
|
||||
<option value="1" {{ old('mese_annuale') == '1' ? 'selected' : '' }}>Gennaio</option>
|
||||
<option value="2" {{ old('mese_annuale') == '2' ? 'selected' : '' }}>Febbraio</option>
|
||||
<option value="3" {{ old('mese_annuale') == '3' ? 'selected' : '' }}>Marzo</option>
|
||||
<option value="4" {{ old('mese_annuale') == '4' ? 'selected' : '' }}>Aprile</option>
|
||||
<option value="5" {{ old('mese_annuale') == '5' ? 'selected' : '' }}>Maggio</option>
|
||||
<option value="6" {{ old('mese_annuale') == '6' ? 'selected' : '' }}>Giugno</option>
|
||||
<option value="7" {{ old('mese_annuale') == '7' ? 'selected' : '' }}>Luglio</option>
|
||||
<option value="8" {{ old('mese_annuale') == '8' ? 'selected' : '' }}>Agosto</option>
|
||||
<option value="9" {{ old('mese_annuale') == '9' ? 'selected' : '' }}>Settembre</option>
|
||||
<option value="10" {{ old('mese_annuale') == '10' ? 'selected' : '' }}>Ottobre</option>
|
||||
<option value="11" {{ old('mese_annuale') == '11' ? 'selected' : '' }}>Novembre</option>
|
||||
<option value="12" {{ old('mese_annuale') == '12' ? 'selected' : '' }}>Dicembre</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Ora Inizio</label>
|
||||
<input type="time" name="ora_inizio" class="form-control" value="{{ old('ora_inizio') }}">
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<label>Durata (minuti)</label>
|
||||
<input type="number" name="durata_minuti" class="form-control" value="{{ old('durata_minuti', 60) }}" min="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-map-marker-alt mr-2"></i>Dove</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Indirizzo</label>
|
||||
<input type="text" name="luogo_indirizzo" class="form-control" value="{{ old('luogo_indirizzo') }}" placeholder="Via, numero civico, CAP, città">
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<label>URL Google Maps</label>
|
||||
<input type="url" name="luogo_url_maps" class="form-control" value="{{ old('luogo_url_maps') }}" placeholder="https://maps.google.com/...">
|
||||
<small class="form-text text-muted">Incolla il link di Google Maps per attivare l'anteprima</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-folder mr-2"></i>Gruppi</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group mb-0">
|
||||
<select name="gruppi[]" class="form-control" multiple size="8">
|
||||
@foreach($gruppi as $g)
|
||||
<option value="{{ $g->id }}" {{ (old('gruppi') && in_array($g->id, old('gruppi'))) || ($selectedGruppo && $selectedGruppo->id == $g->id) ? 'selected' : '' }}>
|
||||
{{ $g->full_path }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="form-text text-muted">Tieni premuto Ctrl per selezionare più gruppi</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-user mr-2"></i>Responsabili</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group mb-0">
|
||||
<select name="responsabili[]" class="form-control" multiple size="8">
|
||||
@foreach($individui as $i)
|
||||
<option value="{{ $i->id }}" {{ old('responsabili') && in_array($i->id, old('responsabili')) ? 'selected' : '' }}>
|
||||
{{ $i->cognome }} {{ $i->nome }}
|
||||
@if($i->telefono_primario)
|
||||
({{ $i->telefono_primario }})
|
||||
@endif
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="form-text text-muted">Tieni premuto Ctrl per selezionare più responsabili</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<div class="card-body">
|
||||
<textarea name="note" class="form-control" rows="2">{{ old('note') }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva
|
||||
</button>
|
||||
<a href="/eventi" class="btn btn-secondary">
|
||||
<i class="fas fa-times mr-1"></i> Annulla
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function toggleRicorrenza() {
|
||||
var tipo = document.getElementById('tipo_recorrenza').value;
|
||||
document.getElementById('data_specifica_field').style.display = tipo === 'singolo' ? 'block' : 'none';
|
||||
document.getElementById('settimanale_field').style.display = tipo === 'settimanale' ? 'block' : 'none';
|
||||
document.getElementById('mensile_field').style.display = tipo === 'mensile' ? 'block' : 'none';
|
||||
document.getElementById('annuale_field').style.display = tipo === 'annuale' ? 'block' : 'none';
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,372 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Modifica Evento')
|
||||
@section('page_title', 'Modifica Evento')
|
||||
|
||||
@section('content')
|
||||
<form action="/eventi/{{ $evento->id }}" method="POST" id="mainForm">
|
||||
@csrf @method('PUT')
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-calendar mr-2"></i>Dati Evento</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Nome Evento *</label>
|
||||
<input type="text" name="nome_evento" class="form-control" value="{{ old('nome_evento', $evento->nome_evento) }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione Breve</label>
|
||||
<input type="text" name="descrizione_evento" class="form-control" value="{{ old('descrizione_evento', $evento->descrizione_evento) }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Tipo Evento</label>
|
||||
<input type="text" name="tipo_evento" class="form-control" value="{{ old('tipo_evento', $evento->tipo_evento) }}" placeholder="es. Catechesi, Liturgia, Animazione">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione Completa</label>
|
||||
<textarea name="descrizione" class="form-control" rows="3">{{ old('descrizione', $evento->descrizione) }}</textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Note</label>
|
||||
<textarea name="note" class="form-control" rows="2">{{ old('note', $evento->note) }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>
|
||||
<input type="checkbox" name="is_incontro_gruppo" value="1" {{ $evento->is_incontro_gruppo ? 'checked' : '' }}>
|
||||
Questo evento è il giorno di incontro del gruppo
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-clock mr-2"></i>Data e Orario</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Tipo Ricorrenza</label>
|
||||
<select name="tipo_recorrenza" id="tipo_recorrenza" class="form-control" onchange="toggleRicorrenza()">
|
||||
<option value="singolo" {{ old('tipo_recorrenza', $evento->tipo_recorrenza) === 'singolo' || old('tipo_recorrenza', $evento->tipo_recorrenza) === null ? 'selected' : '' }}>Singolo</option>
|
||||
<option value="settimanale" {{ old('tipo_recorrenza', $evento->tipo_recorrenza) === 'settimanale' ? 'selected' : '' }}>Settimanale</option>
|
||||
<option value="mensile" {{ old('tipo_recorrenza', $evento->tipo_recorrenza) === 'mensile' ? 'selected' : '' }}>Mensile</option>
|
||||
<option value="annuale" {{ old('tipo_recorrenza', $evento->tipo_recorrenza) === 'annuale' ? 'selected' : '' }}>Annuale</option>
|
||||
<option value="altro" {{ old('tipo_recorrenza', $evento->tipo_recorrenza) === 'altro' ? 'selected' : '' }}>Altro</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div id="data_specifica_field" style="{{ in_array($evento->tipo_recorrenza, [null, 'singolo']) ? '' : 'display:none;' }}">
|
||||
<div class="form-group">
|
||||
<label>Data Evento</label>
|
||||
<input type="date" name="data_specifica" class="form-control" value="{{ old('data_specifica', $evento->data_specifica?->format('Y-m-d')) }}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="settimanale_field" style="{{ $evento->tipo_recorrenza === 'settimanale' ? '' : 'display:none;' }}">
|
||||
<div class="form-group">
|
||||
<label>Giorno della Settimana</label>
|
||||
<select name="giorno_settimana" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="0" {{ $evento->giorno_settimana == 0 ? 'selected' : '' }}>Domenica</option>
|
||||
<option value="1" {{ $evento->giorno_settimana == 1 ? 'selected' : '' }}>Lunedì</option>
|
||||
<option value="2" {{ $evento->giorno_settimana == 2 ? 'selected' : '' }}>Martedì</option>
|
||||
<option value="3" {{ $evento->giorno_settimana == 3 ? 'selected' : '' }}>Mercoledì</option>
|
||||
<option value="4" {{ $evento->giorno_settimana == 4 ? 'selected' : '' }}>Giovedì</option>
|
||||
<option value="5" {{ $evento->giorno_settimana == 5 ? 'selected' : '' }}>Venerdì</option>
|
||||
<option value="6" {{ $evento->giorno_settimana == 6 ? 'selected' : '' }}>Sabato</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mensile_field" style="{{ $evento->tipo_recorrenza === 'mensile' ? '' : 'display:none;' }}">
|
||||
<div class="form-group">
|
||||
<label>Occorrenza nel mese</label>
|
||||
@php
|
||||
$occorrenzaSelezionata = old('occorrenza_mese', $evento->occorrenza_mese);
|
||||
@endphp
|
||||
<select name="occorrenza_mese" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="1,1" {{ $occorrenzaSelezionata === '1,1' ? 'selected' : '' }}>1° Lunedì</option>
|
||||
<option value="1,2" {{ $occorrenzaSelezionata === '1,2' ? 'selected' : '' }}>1° Martedì</option>
|
||||
<option value="1,3" {{ $occorrenzaSelezionata === '1,3' ? 'selected' : '' }}>1° Mercoledì</option>
|
||||
<option value="1,4" {{ $occorrenzaSelezionata === '1,4' ? 'selected' : '' }}>1° Giovedì</option>
|
||||
<option value="1,5" {{ $occorrenzaSelezionata === '1,5' ? 'selected' : '' }}>1° Venerdì</option>
|
||||
<option value="1,6" {{ $occorrenzaSelezionata === '1,6' ? 'selected' : '' }}>1° Sabato</option>
|
||||
<option value="1,0" {{ $occorrenzaSelezionata === '1,0' ? 'selected' : '' }}>1° Domenica</option>
|
||||
<option value="2,1" {{ $occorrenzaSelezionata === '2,1' ? 'selected' : '' }}>2° Lunedì</option>
|
||||
<option value="2,2" {{ $occorrenzaSelezionata === '2,2' ? 'selected' : '' }}>2° Martedì</option>
|
||||
<option value="2,3" {{ $occorrenzaSelezionata === '2,3' ? 'selected' : '' }}>2° Mercoledì</option>
|
||||
<option value="2,4" {{ $occorrenzaSelezionata === '2,4' ? 'selected' : '' }}>2° Giovedì</option>
|
||||
<option value="2,5" {{ $occorrenzaSelezionata === '2,5' ? 'selected' : '' }}>2° Venerdì</option>
|
||||
<option value="2,6" {{ $occorrenzaSelezionata === '2,6' ? 'selected' : '' }}>2° Sabato</option>
|
||||
<option value="2,0" {{ $occorrenzaSelezionata === '2,0' ? 'selected' : '' }}>2° Domenica</option>
|
||||
<option value="3,1" {{ $occorrenzaSelezionata === '3,1' ? 'selected' : '' }}>3° Lunedì</option>
|
||||
<option value="3,2" {{ $occorrenzaSelezionata === '3,2' ? 'selected' : '' }}>3° Martedì</option>
|
||||
<option value="3,3" {{ $occorrenzaSelezionata === '3,3' ? 'selected' : '' }}>3° Mercoledì</option>
|
||||
<option value="3,4" {{ $occorrenzaSelezionata === '3,4' ? 'selected' : '' }}>3° Giovedì</option>
|
||||
<option value="3,5" {{ $occorrenzaSelezionata === '3,5' ? 'selected' : '' }}>3° Venerdì</option>
|
||||
<option value="3,6" {{ $occorrenzaSelezionata === '3,6' ? 'selected' : '' }}>3° Sabato</option>
|
||||
<option value="3,0" {{ $occorrenzaSelezionata === '3,0' ? 'selected' : '' }}>3° Domenica</option>
|
||||
<option value="4,1" {{ $occorrenzaSelezionata === '4,1' ? 'selected' : '' }}>4° Lunedì</option>
|
||||
<option value="4,2" {{ $occorrenzaSelezionata === '4,2' ? 'selected' : '' }}>4° Martedì</option>
|
||||
<option value="4,3" {{ $occorrenzaSelezionata === '4,3' ? 'selected' : '' }}>4° Mercoledì</option>
|
||||
<option value="4,4" {{ $occorrenzaSelezionata === '4,4' ? 'selected' : '' }}>4° Giovedì</option>
|
||||
<option value="4,5" {{ $occorrenzaSelezionata === '4,5' ? 'selected' : '' }}>4° Venerdì</option>
|
||||
<option value="4,6" {{ $occorrenzaSelezionata === '4,6' ? 'selected' : '' }}>4° Sabato</option>
|
||||
<option value="4,0" {{ $occorrenzaSelezionata === '4,0' ? 'selected' : '' }}>4° Domenica</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Mesi (multipli)</label>
|
||||
@php
|
||||
$mesiSelezionati = $evento->mesi_recorrenza ? explode(',', $evento->mesi_recorrenza) : [];
|
||||
@endphp
|
||||
<select name="mesi_recorrenza[]" class="form-control" multiple size="4">
|
||||
<option value="1" {{ in_array('1', $mesiSelezionati) ? 'selected' : '' }}>Gennaio</option>
|
||||
<option value="2" {{ in_array('2', $mesiSelezionati) ? 'selected' : '' }}>Febbraio</option>
|
||||
<option value="3" {{ in_array('3', $mesiSelezionati) ? 'selected' : '' }}>Marzo</option>
|
||||
<option value="4" {{ in_array('4', $mesiSelezionati) ? 'selected' : '' }}>Aprile</option>
|
||||
<option value="5" {{ in_array('5', $mesiSelezionati) ? 'selected' : '' }}>Maggio</option>
|
||||
<option value="6" {{ in_array('6', $mesiSelezionati) ? 'selected' : '' }}>Giugno</option>
|
||||
<option value="7" {{ in_array('7', $mesiSelezionati) ? 'selected' : '' }}>Luglio</option>
|
||||
<option value="8" {{ in_array('8', $mesiSelezionati) ? 'selected' : '' }}>Agosto</option>
|
||||
<option value="9" {{ in_array('9', $mesiSelezionati) ? 'selected' : '' }}>Settembre</option>
|
||||
<option value="10" {{ in_array('10', $mesiSelezionati) ? 'selected' : '' }}>Ottobre</option>
|
||||
<option value="11" {{ in_array('11', $mesiSelezionati) ? 'selected' : '' }}>Novembre</option>
|
||||
<option value="12" {{ in_array('12', $mesiSelezionati) ? 'selected' : '' }}>Dicembre</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">Ctrl+clic per selezionare più mesi</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="annuale_field" style="{{ $evento->tipo_recorrenza === 'annuale' ? '' : 'display:none;' }}">
|
||||
<div class="form-group">
|
||||
<label>Mese</label>
|
||||
<select name="mese_annuale" class="form-control">
|
||||
<option value="">Seleziona mese...</option>
|
||||
<option value="1" {{ old('mese_annuale', $evento->mese_annuale) == '1' ? 'selected' : '' }}>Gennaio</option>
|
||||
<option value="2" {{ old('mese_annuale', $evento->mese_annuale) == '2' ? 'selected' : '' }}>Febbraio</option>
|
||||
<option value="3" {{ old('mese_annuale', $evento->mese_annuale) == '3' ? 'selected' : '' }}>Marzo</option>
|
||||
<option value="4" {{ old('mese_annuale', $evento->mese_annuale) == '4' ? 'selected' : '' }}>Aprile</option>
|
||||
<option value="5" {{ old('mese_annuale', $evento->mese_annuale) == '5' ? 'selected' : '' }}>Maggio</option>
|
||||
<option value="6" {{ old('mese_annuale', $evento->mese_annuale) == '6' ? 'selected' : '' }}>Giugno</option>
|
||||
<option value="7" {{ old('mese_annuale', $evento->mese_annuale) == '7' ? 'selected' : '' }}>Luglio</option>
|
||||
<option value="8" {{ old('mese_annuale', $evento->mese_annuale) == '8' ? 'selected' : '' }}>Agosto</option>
|
||||
<option value="9" {{ old('mese_annuale', $evento->mese_annuale) == '9' ? 'selected' : '' }}>Settembre</option>
|
||||
<option value="10" {{ old('mese_annuale', $evento->mese_annuale) == '10' ? 'selected' : '' }}>Ottobre</option>
|
||||
<option value="11" {{ old('mese_annuale', $evento->mese_annuale) == '11' ? 'selected' : '' }}>Novembre</option>
|
||||
<option value="12" {{ old('mese_annuale', $evento->mese_annuale) == '12' ? 'selected' : '' }}>Dicembre</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Ora Inizio</label>
|
||||
<input type="time" name="ora_inizio" class="form-control" value="{{ old('ora_inizio', $evento->ora_inizio?->format('H:i')) }}">
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<label>Durata (minuti)</label>
|
||||
<input type="number" name="durata_minuti" class="form-control" value="{{ old('durata_minuti', $evento->durata_minuti ?? 60) }}" min="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-map-marker-alt mr-2"></i>Dove</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Indirizzo</label>
|
||||
<input type="text" name="luogo_indirizzo" class="form-control" value="{{ old('luogo_indirizzo', $evento->luogo_indirizzo) }}" placeholder="Via, numero civico, CAP, città">
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<label>URL Google Maps</label>
|
||||
<input type="url" name="luogo_url_maps" class="form-control" value="{{ old('luogo_url_maps', $evento->luogo_url_maps) }}" placeholder="https://maps.google.com/...">
|
||||
<small class="form-text text-muted">Incolla il link di Google Maps per attivare l'anteprima</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-folder mr-2"></i>Gruppi</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group mb-0">
|
||||
<select name="gruppi[]" class="form-control" multiple size="8">
|
||||
@foreach($gruppi as $g)
|
||||
<option value="{{ $g->id }}" {{ $evento->gruppi->contains($g->id) ? 'selected' : '' }}>
|
||||
{{ $g->full_path }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="form-text text-muted">Tieni premuto Ctrl per selezionare più gruppi</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-user mr-2"></i>Responsabili</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group mb-0">
|
||||
<select name="responsabili[]" class="form-control" multiple size="8">
|
||||
@foreach($individui as $i)
|
||||
<option value="{{ $i->id }}" {{ $evento->responsabili->contains($i->id) ? 'selected' : '' }}>
|
||||
{{ $i->cognome }} {{ $i->nome }}
|
||||
@if($i->telefono_primario)
|
||||
({{ $i->telefono_primario }})
|
||||
@endif
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="form-text text-muted">Tieni premuto Ctrl per selezionare più responsabili</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva Modifiche
|
||||
</button>
|
||||
<a href="/eventi/{{ $evento->id }}" class="btn btn-secondary">
|
||||
<i class="fas fa-times mr-1"></i> Annulla
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-file mr-2"></i>Documenti</h3>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="showDocumentoForm()">
|
||||
<i class="fas fa-plus mr-1"></i> Carica
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="documento-add-form" style="display:none;" class="p-3 bg-light border-bottom">
|
||||
<form action="/eventi/{{ $evento->id }}/documenti" method="POST" enctype="multipart/form-data" class="mb-0">
|
||||
@csrf
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<input type="text" name="nome_file" class="form-control form-control-sm" placeholder="Nome documento" required>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="tipologia" class="form-control form-control-sm" required>
|
||||
<option value="">Tipologia...</option>
|
||||
<option value="documento">Documento</option>
|
||||
<option value="programma">Programma</option>
|
||||
<option value="locandina">Locandina</option>
|
||||
<option value="altro">Altro</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<input type="file" name="file" class="form-control form-control-sm" required>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<button type="submit" class="btn btn-xs btn-success"><i class="fas fa-upload mr-1"></i>Carica</button>
|
||||
<button type="button" class="btn btn-xs btn-secondary" onclick="hideDocumentoForm()"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@if($evento->documenti && $evento->documenti->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th style="width: 120px;">Tipologia</th>
|
||||
<th style="width: 80px;">Dimensione</th>
|
||||
<th style="width: 130px;">Data Upload</th>
|
||||
<th style="width: 80px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($evento->documenti as $documento)
|
||||
<tr>
|
||||
<td>
|
||||
<i class="fas fa-file text-secondary mr-1"></i>
|
||||
{{ $documento->nome_file }}
|
||||
</td>
|
||||
<td>{{ ucfirst($documento->tipologia) }}</td>
|
||||
<td>{{ number_format($documento->dimensione / 1024, 1) }} KB</td>
|
||||
<td>{{ $documento->created_at->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
@if($documento->file_path)
|
||||
<button type="button" class="btn btn-xs btn-primary" onclick="previewDocumento({{ $documento->id }})" title="Anteprima">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
@endif
|
||||
<form action="/eventi/{{ $evento->id }}/documenti/{{ $documento->id }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo documento?')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-file fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun documento</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
<div class="modal fade" id="previewModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document" style="max-width: 90vw;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-file mr-2"></i><span id="previewModalTitle">Anteprima</span></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Chiudi">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-0" style="min-height: 400px;">
|
||||
<iframe id="previewFrame" src="" style="width: 100%; height: 70vh; border: none;"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a id="previewDownloadBtn" href="#" class="btn btn-primary" download>
|
||||
<i class="fas fa-download mr-1"></i> Scarica
|
||||
</a>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Chiudi</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function toggleRicorrenza() {
|
||||
var tipo = document.getElementById('tipo_recorrenza').value;
|
||||
document.getElementById('data_specifica_field').style.display = tipo === 'singolo' ? 'block' : 'none';
|
||||
document.getElementById('settimanale_field').style.display = tipo === 'settimanale' ? 'block' : 'none';
|
||||
document.getElementById('mensile_field').style.display = tipo === 'mensile' ? 'block' : 'none';
|
||||
document.getElementById('annuale_field').style.display = tipo === 'annuale' ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function showDocumentoForm() {
|
||||
document.getElementById('documento-add-form').style.display = 'block';
|
||||
}
|
||||
|
||||
function hideDocumentoForm() {
|
||||
document.getElementById('documento-add-form').style.display = 'none';
|
||||
}
|
||||
|
||||
function previewDocumento(id) {
|
||||
document.getElementById('previewFrame').src = '/documenti/' + id + '/preview';
|
||||
document.getElementById('previewDownloadBtn').href = '/documenti/' + id + '/download';
|
||||
$('#previewModal').modal('show');
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,288 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Eventi')
|
||||
@section('page_title', 'Eventi')
|
||||
|
||||
@php
|
||||
$canWriteEventi = Auth::user()->canManage('eventi');
|
||||
$canDeleteEventi = Auth::user()->canDelete('eventi');
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Tutti gli Eventi</h3>
|
||||
<div class="card-tools">
|
||||
@if($canDeleteEventi)
|
||||
<div class="btn-group mr-2">
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="deleteSelected()">
|
||||
<i class="fas fa-trash mr-1"></i> Elimina Selezionati
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
@if($canWriteEventi)
|
||||
<a href="/eventi/create" class="btn btn-xs btn-success">
|
||||
<i class="fas fa-plus mr-1"></i> Nuovo Evento
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="GET" class="mb-3">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<input type="text" name="search" class="form-control" placeholder="Cerca evento..." value="{{ request('search') }}">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="gruppo_id" class="form-control" onchange="this.form.submit()">
|
||||
<option value="">Tutti i gruppi</option>
|
||||
@foreach($gruppi as $g)
|
||||
<option value="{{ $g->id }}" {{ request('gruppo_id') == $g->id ? 'selected' : '' }}>{{ $g->full_path }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select name="tipo" class="form-control" onchange="this.form.submit()">
|
||||
<option value="">Tutte le ricorrenze</option>
|
||||
<option value="singolo" {{ request('tipo') == 'singolo' ? 'selected' : '' }}>Singolo</option>
|
||||
<option value="settimanale" {{ request('tipo') == 'settimanale' ? 'selected' : '' }}>Settimanale</option>
|
||||
<option value="mensile" {{ request('tipo') == 'mensile' ? 'selected' : '' }}>Mensile</option>
|
||||
<option value="annuale" {{ request('tipo') == 'annuale' ? 'selected' : '' }}>Annuale</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary"><i class="fas fa-search mr-1"></i> Cerca</button>
|
||||
<a href="/eventi" class="btn btn-secondary"><i class="fas fa-times"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 40px;">
|
||||
@if($canDeleteEventi)
|
||||
<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>
|
||||
@endif
|
||||
</th>
|
||||
<th>
|
||||
<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')
|
||||
<i class="fas fa-sort-{{ request('direction') === 'asc' ? 'up' : 'down' }} ml-1"></i>
|
||||
@else
|
||||
<i class="fas fa-sort ml-1 text-muted"></i>
|
||||
@endif
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<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')
|
||||
<i class="fas fa-sort-{{ request('direction') === 'asc' ? 'up' : 'down' }} ml-1"></i>
|
||||
@else
|
||||
<i class="fas fa-sort ml-1 text-muted"></i>
|
||||
@endif
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'tipo_recorrenza', 'direction' => request('sort') === 'tipo_recorrenza' && request('direction') === 'asc' ? 'desc' : 'asc']) }}" class="text-dark">
|
||||
Tipo
|
||||
@if(request('sort') === 'tipo_recorrenza')
|
||||
<i class="fas fa-sort-{{ request('direction') === 'asc' ? 'up' : 'down' }} ml-1"></i>
|
||||
@else
|
||||
<i class="fas fa-sort ml-1 text-muted"></i>
|
||||
@endif
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'gruppi', 'direction' => request('sort') === 'gruppi' && request('direction') === 'asc' ? 'desc' : 'asc']) }}" class="text-dark">
|
||||
Gruppi
|
||||
@if(request('sort') === 'gruppi')
|
||||
<i class="fas fa-sort-{{ request('direction') === 'asc' ? 'up' : 'down' }} ml-1"></i>
|
||||
@else
|
||||
<i class="fas fa-sort ml-1 text-muted"></i>
|
||||
@endif
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="{{ request()->fullUrlWithQuery(['sort' => 'responsabili', 'direction' => request('sort') === 'responsabili' && request('direction') === 'asc' ? 'desc' : 'asc']) }}" class="text-dark">
|
||||
Responsabili
|
||||
@if(request('sort') === 'responsabili')
|
||||
<i class="fas fa-sort-{{ request('direction') === 'asc' ? 'up' : 'down' }} ml-1"></i>
|
||||
@else
|
||||
<i class="fas fa-sort ml-1 text-muted"></i>
|
||||
@endif
|
||||
</a>
|
||||
</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
|
||||
@if(request('sort') === 'created_at')
|
||||
<i class="fas fa-sort-{{ request('direction') === 'asc' ? 'up' : 'down' }} ml-1"></i>
|
||||
@else
|
||||
<i class="fas fa-sort ml-1 text-muted"></i>
|
||||
@endif
|
||||
</a>
|
||||
</th>
|
||||
<th style="width: 120px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($eventi as $evento)
|
||||
<tr data-id="{{ $evento->id }}">
|
||||
<td>
|
||||
@if($canDeleteEventi)
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input row-checkbox" id="row-checkbox-{{ $evento->id }}" value="{{ $evento->id }}">
|
||||
<label class="custom-control-label" for="row-checkbox-{{ $evento->id }}"></label>
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<a href="/eventi/{{ $evento->id }}">
|
||||
<i class="fas fa-calendar mr-1 text-primary"></i>
|
||||
{{ $evento->nome_evento }}
|
||||
</a>
|
||||
@if($evento->is_incontro_gruppo)
|
||||
<span class="badge badge-success ml-1">Incluso</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ Str::limit($evento->descrizione_evento, 50) ?: '-' }}</td>
|
||||
<td>
|
||||
@if($evento->tipo_recorrenza && $evento->tipo_recorrenza !== 'singolo')
|
||||
<span class="badge badge-info">{{ $evento->periodicita_label }}</span>
|
||||
@else
|
||||
<span class="badge badge-secondary">Singolo</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@forelse($evento->gruppi->take(2) as $gruppo)
|
||||
<a href="/gruppi/{{ $gruppo->id }}">
|
||||
<span class="badge badge-warning">{{ $gruppo->nome }}</span>
|
||||
</a>
|
||||
@empty
|
||||
<span class="text-muted">-</span>
|
||||
@endforelse
|
||||
@if($evento->gruppi->count() > 2)
|
||||
<small class="text-muted">+{{ $evento->gruppi->count() - 2 }}</small>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@forelse($evento->responsabili->take(2) as $resp)
|
||||
<small>{{ $resp->nome_completo }}</small><br>
|
||||
@empty
|
||||
<span class="text-muted">-</span>
|
||||
@endforelse
|
||||
@if($evento->responsabili->count() > 2)
|
||||
<small class="text-muted">+{{ $evento->responsabili->count() - 2 }} altri</small>
|
||||
@endif
|
||||
</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">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
@if($canWriteEventi)
|
||||
<a href="{{ url('/eventi/' . $evento->id . '/edit') }}" class="btn btn-xs btn-warning" title="Modifica">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<form action="{{ url('/eventi/' . $evento->id) }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Confermi l\'eliminazione?')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="8" 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>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
{{ $eventi->withQueryString()->links() }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog">
|
||||
@if($canDeleteEventi)
|
||||
<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> eventi selezionati.</p>
|
||||
<p class="text-muted small">Verranno eliminati anche i collegamenti con gruppi e responsabili.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annulla</button>
|
||||
<button type="button" class="btn btn-danger" onclick="confirmDelete()">Elimina</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function toggleSelectAll(source) {
|
||||
document.querySelectorAll('.row-checkbox').forEach(cb => cb.checked = source.checked);
|
||||
}
|
||||
|
||||
function getSelectedIds() {
|
||||
return Array.from(document.querySelectorAll('.row-checkbox:checked')).map(cb => cb.value);
|
||||
}
|
||||
|
||||
function deleteSelected() {
|
||||
const selectedIds = getSelectedIds();
|
||||
if (selectedIds.length === 0) {
|
||||
alert('Seleziona almeno un evento');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('delete-count').textContent = selectedIds.length;
|
||||
|
||||
document.querySelector('#deleteModal .btn-danger').onclick = function() {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '/eventi/mass-elimina';
|
||||
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '{{ csrf_token() }}';
|
||||
|
||||
let html = `<input type="hidden" name="_token" value="${csrfToken}">`;
|
||||
selectedIds.forEach(function(id) {
|
||||
html += `<input type="hidden" name="ids[]" value="${id}">`;
|
||||
});
|
||||
|
||||
form.innerHTML = html;
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
};
|
||||
|
||||
$('#deleteModal').modal('show');
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,340 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', $evento->nome_evento)
|
||||
@section('page_title', 'Dettaglio Evento')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item"><a href="/eventi">Eventi</a></li>
|
||||
<li class="breadcrumb-item active">{{ $evento->nome_evento }}</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-calendar mr-2"></i>
|
||||
{{ $evento->nome_evento }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($evento->descrizione_evento)
|
||||
<p class="lead">{{ $evento->descrizione_evento }}</p>
|
||||
@endif
|
||||
|
||||
@if($evento->descrizione)
|
||||
<div class="mt-3">
|
||||
<h5>Descrizione Completa</h5>
|
||||
<p>{!! nl2br(e($evento->descrizione)) !!}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($evento->is_incontro_gruppo)
|
||||
<div class="alert alert-success mt-3">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
<strong>Evento di incontro gruppo</strong>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-clock mr-2"></i>Data e Orario</h3></div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr>
|
||||
<td style="width: 150px;"><strong>Tipo:</strong></td>
|
||||
<td>
|
||||
@if($evento->tipo_recorrenza && $evento->tipo_recorrenza !== 'singolo')
|
||||
<span class="badge badge-info">{{ $evento->periodicita_label }}</span>
|
||||
@else
|
||||
<span class="badge badge-secondary">Singolo</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@if($evento->tipo_recorrenza === 'settimanale')
|
||||
<tr>
|
||||
<td><strong>Giorno:</strong></td>
|
||||
<td>{{ $evento->giorno_settimana_label }}</td>
|
||||
</tr>
|
||||
@elseif($evento->tipo_recorrenza === 'mensile')
|
||||
<tr>
|
||||
<td><strong>Occorrenza:</strong></td>
|
||||
<td>{{ $evento->occorrenza_mensile_label }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Mesi:</strong></td>
|
||||
<td>{{ $evento->mesi_recorrenza_label }}</td>
|
||||
</tr>
|
||||
@elseif($evento->tipo_recorrenza === 'annuale')
|
||||
<tr>
|
||||
<td><strong>Mese:</strong></td>
|
||||
<td>{{ $evento->mese_annuale_label }}</td>
|
||||
</tr>
|
||||
@elseif($evento->tipo_recorrenza === 'altro')
|
||||
<tr>
|
||||
<td><strong>Giorno:</strong></td>
|
||||
<td>{{ $evento->giorno_settimana_label }}</td>
|
||||
</tr>
|
||||
@else
|
||||
<tr>
|
||||
<td><strong>Data:</strong></td>
|
||||
<td>{{ $evento->data_specifica?->format('d/m/Y') ?: '-' }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
@if($evento->ora_inizio)
|
||||
<tr>
|
||||
<td><strong>Ora Inizio:</strong></td>
|
||||
<td>{{ $evento->ora_inizio->format('H:i') }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
@if($evento->durata_minuti)
|
||||
<tr>
|
||||
<td><strong>Durata:</strong></td>
|
||||
<td>{{ $evento->durata_minuti }} minuti</td>
|
||||
</tr>
|
||||
@endif
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($evento->luogo_indirizzo || $evento->luogo_url_maps)
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-map-marker-alt mr-2"></i>Luogo</h3></div>
|
||||
<div class="card-body">
|
||||
@if($evento->luogo_indirizzo)
|
||||
<p class="mb-2"><i class="fas fa-location-dot text-danger mr-1"></i> <strong>{{ $evento->luogo_indirizzo }}</strong></p>
|
||||
@endif
|
||||
@if($evento->luogo_url_maps)
|
||||
<div class="mb-2">
|
||||
<a href="{{ $evento->luogo_url_maps }}" target="_blank" class="btn btn-sm btn-outline-primary">
|
||||
<i class="fas fa-external-link-alt mr-1"></i> Apri in Google Maps
|
||||
</a>
|
||||
</div>
|
||||
<iframe
|
||||
src="https://www.google.com/maps?q={{ urlencode($evento->luogo_indirizzo ?: $evento->luogo_url_maps) }}&output=embed"
|
||||
width="100%" height="200" style="border:0; border-radius: 8px;" allowfullscreen loading="lazy">
|
||||
</iframe>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-folder mr-2"></i>Gruppi</h3></div>
|
||||
<div class="card-body p-0">
|
||||
@if($evento->gruppi->count() > 0)
|
||||
<table class="table table-sm mb-0">
|
||||
<tbody>
|
||||
@foreach($evento->gruppi as $gruppo)
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/gruppi/{{ $gruppo->id }}">
|
||||
<i class="fas fa-folder text-warning mr-1"></i>
|
||||
{{ $gruppo->nome }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-3 mb-0">
|
||||
<span>Nessun gruppo associato</span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-user-tie mr-2"></i>Responsabili</h3></div>
|
||||
<div class="card-body p-0">
|
||||
@if($evento->responsabili->count() > 0)
|
||||
<table class="table table-sm mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Contatto</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($evento->responsabili as $resp)
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/individui/{{ $resp->id }}">
|
||||
<i class="fas fa-user text-info mr-1"></i>
|
||||
{{ $resp->cognome }} {{ $resp->nome }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
@if($resp->telefono_primario)
|
||||
<small class="text-success">{{ $resp->telefono_primario }}</small>
|
||||
@else
|
||||
<span class="text-muted">-</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-3 mb-0">
|
||||
<span>Nessun responsabile</span>
|
||||
</div>
|
||||
@endif
|
||||
</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>
|
||||
<div class="card-body">
|
||||
<p class="mb-0">{!! nl2br(e($evento->note)) !!}</p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-file mr-2"></i>Documenti</h3>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="showDocumentoForm()">
|
||||
<i class="fas fa-plus mr-1"></i> Carica
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="documento-add-form" style="display:none;" class="p-3 bg-light border-bottom">
|
||||
<form action="/eventi/{{ $evento->id }}/documenti" method="POST" enctype="multipart/form-data" class="mb-0">
|
||||
@csrf
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<input type="text" name="nome_file" class="form-control form-control-sm" placeholder="Nome documento" required>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="tipologia" class="form-control form-control-sm" required>
|
||||
<option value="">Tipologia...</option>
|
||||
<option value="documento">Documento</option>
|
||||
<option value="programma">Programma</option>
|
||||
<option value="locandina">Locandina</option>
|
||||
<option value="altro">Altro</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<input type="file" name="file" class="form-control form-control-sm" required>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<button type="submit" class="btn btn-xs btn-success"><i class="fas fa-upload mr-1"></i>Carica</button>
|
||||
<button type="button" class="btn btn-xs btn-secondary" onclick="hideDocumentoForm()"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@if($evento->documenti && $evento->documenti->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th style="width: 120px;">Tipologia</th>
|
||||
<th style="width: 80px;">Dimensione</th>
|
||||
<th style="width: 130px;">Data Upload</th>
|
||||
<th style="width: 80px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($evento->documenti as $documento)
|
||||
<tr>
|
||||
<td>
|
||||
<i class="fas fa-file text-secondary mr-1"></i>
|
||||
{{ $documento->nome_file }}
|
||||
</td>
|
||||
<td>{{ ucfirst($documento->tipologia) }}</td>
|
||||
<td>{{ number_format($documento->dimensione / 1024, 1) }} KB</td>
|
||||
<td>{{ $documento->created_at->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
@if($documento->file_path)
|
||||
<button type="button" class="btn btn-xs btn-primary" onclick="previewDocumento({{ $documento->id }})" title="Anteprima">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
@endif
|
||||
<form action="/eventi/{{ $evento->id }}/documenti/{{ $documento->id }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo documento?')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-file fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun documento</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="/eventi/{{ $evento->id }}/edit" class="btn btn-warning">
|
||||
<i class="fas fa-edit mr-1"></i> Modifica
|
||||
</a>
|
||||
<form action="/eventi/{{ $evento->id }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-danger" onclick="return confirm('Confermi l\'eliminazione di {{ $evento->nome_evento }}?')">
|
||||
<i class="fas fa-trash mr-1"></i> Elimina
|
||||
</button>
|
||||
</form>
|
||||
<a href="/eventi" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left mr-1"></i> Torna all'elenco
|
||||
</a>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
<div class="modal fade" id="previewModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document" style="max-width: 90vw;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-file mr-2"></i><span id="previewModalTitle">Anteprima</span></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Chiudi">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-0" style="min-height: 400px;">
|
||||
<iframe id="previewFrame" src="" style="width: 100%; height: 70vh; border: none;"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a id="previewDownloadBtn" href="#" class="btn btn-primary" download>
|
||||
<i class="fas fa-download mr-1"></i> Scarica
|
||||
</a>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Chiudi</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function showDocumentoForm() {
|
||||
document.getElementById('documento-add-form').style.display = 'block';
|
||||
}
|
||||
|
||||
function hideDocumentoForm() {
|
||||
document.getElementById('documento-add-form').style.display = 'none';
|
||||
}
|
||||
|
||||
function previewDocumento(id) {
|
||||
document.getElementById('previewFrame').src = '/documenti/' + id + '/preview';
|
||||
document.getElementById('previewDownloadBtn').href = '/documenti/' + id + '/download';
|
||||
$('#previewModal').modal('show');
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,244 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Nuovo Gruppo')
|
||||
@section('page_title', 'Nuovo Gruppo')
|
||||
|
||||
@section('content')
|
||||
<form action="/gruppi" method="POST">
|
||||
@csrf
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-folder mr-2"></i>Dati Gruppo</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="nome" class="form-control @error('nome') is-invalid @enderror" value="{{ old('nome') }}" required>
|
||||
@error('nome')
|
||||
<span class="invalid-feedback">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Gruppo Padre</label>
|
||||
<select name="parent_id" class="form-control">
|
||||
<option value="">Nessuno (gruppo radice)</option>
|
||||
@foreach($allGruppi as $g)
|
||||
<option value="{{ $g->id }}" {{ old('parent_id') == $g->id || ($selectedParent && $selectedParent->id == $g->id) ? 'selected' : '' }}>
|
||||
{{ $g->full_path }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@if($selectedParent)
|
||||
<small class="form-text text-muted">
|
||||
Verrà creato come sottogruppo di: <strong>{{ $selectedParent->nome }}</strong>
|
||||
</small>
|
||||
@endif
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Diocesi</label>
|
||||
<select name="diocesi_id" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
@foreach($diocesi as $d)
|
||||
<option value="{{ $d->id }}" {{ old('diocesi_id') == $d->id ? 'selected' : '' }}>{{ $d->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Responsabili</label>
|
||||
<select name="responsabile_ids[]" class="form-control" multiple size="4" id="responsabile-select">
|
||||
<option value="">-- Nessuno --</option>
|
||||
</select>
|
||||
<small class="text-muted">Ctrl+click per selezionare più responsabili</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione</label>
|
||||
<textarea name="descrizione" class="form-control" rows="3">{{ old('descrizione') }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-map-marker-alt mr-2"></i>Luogo Incontro</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Indirizzo</label>
|
||||
<input type="text" name="indirizzo_incontro" class="form-control" value="{{ old('indirizzo_incontro') }}">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-4">
|
||||
<label>CAP</label>
|
||||
<input type="text" name="cap_incontro" class="form-control" value="{{ old('cap_incontro') }}">
|
||||
</div>
|
||||
<div class="form-group col-md-5">
|
||||
<label>Città</label>
|
||||
<input type="text" name="città_incontro" class="form-control" value="{{ old('città_incontro') }}">
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label>Provincia</label>
|
||||
<input type="text" name="sigla_provincia_incontro" class="form-control" maxlength="2" value="{{ old('sigla_provincia_incontro') }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<label>URL Mappa</label>
|
||||
<input type="url" name="mappa_posizione" class="form-control" value="{{ old('mappa_posizione') }}" placeholder="https://...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-users mr-2"></i>Membri</h3>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="aggiungiMembro()">
|
||||
<i class="fas fa-plus mr-1"></i> Aggiungi Membro
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-bordered mb-0" id="membri-table">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 30%;">Individuo</th>
|
||||
<th>Codice</th>
|
||||
<th>Contatti</th>
|
||||
<th style="width: 20%;">Ruolo</th>
|
||||
<th style="width: 100px;">Data Adesione</th>
|
||||
<th style="width: 50px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="membri-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="no-membri-msg" class="text-center text-muted py-4">
|
||||
<p class="mb-0">Nessun membro aggiunto. Clicca su "Aggiungi Membro" per iniziare.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva
|
||||
</button>
|
||||
<a href="/gruppi" class="btn btn-secondary">
|
||||
<i class="fas fa-times mr-1"></i> Annulla
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
let membriIndex = 0;
|
||||
const membriResponsabiliMap = new Map();
|
||||
|
||||
function aggiungiMembro() {
|
||||
const tbody = document.getElementById('membri-tbody');
|
||||
const noMsg = document.getElementById('no-membri-msg');
|
||||
if (noMsg) noMsg.style.display = 'none';
|
||||
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>
|
||||
<select name="individui[${membriIndex}][individuo_id]" class="form-control" onchange="updateMembroInfo(this); updateResponsabiliFromMembri();" required>
|
||||
<option value="">Seleziona...</option>
|
||||
@foreach($individui as $i)
|
||||
<option value="{{ $i->id }}" data-codice="{{ $i->codice_id }}" data-email="{{ $i->email_primaria }}" data-telefono="{{ $i->telefono_primario }}">
|
||||
{{ $i->cognome }} {{ $i->nome }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</td>
|
||||
<td><span class="text-muted membro-codice">-</span></td>
|
||||
<td><small class="text-muted membro-contatti">-</small></td>
|
||||
<td><input type="text" name="individui[${membriIndex}][ruolo_nel_gruppo]" class="form-control" placeholder="Es. Membro"></td>
|
||||
<td><input type="date" name="individui[${membriIndex}][data_adesione]" class="form-control"></td>
|
||||
<td><button type="button" class="btn btn-danger btn-xs" onclick="rimuoviMembro(this); updateResponsabiliFromMembri();"><i class="fas fa-trash"></i></button></td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
membriIndex++;
|
||||
}
|
||||
|
||||
function updateMembroInfo(select) {
|
||||
const row = select.closest('tr');
|
||||
const option = select.options[select.selectedIndex];
|
||||
const codice = option.dataset.codice || '-';
|
||||
const email = option.dataset.email || '';
|
||||
const telefono = option.dataset.telefono || '';
|
||||
|
||||
row.querySelector('.membro-codice').textContent = codice;
|
||||
|
||||
let contatti = [];
|
||||
if (email) contatti.push('<span class="text-primary">'+email+'</span>');
|
||||
if (telefono) contatti.push('<span class="text-success">'+telefono+'</span>');
|
||||
row.querySelector('.membro-contatti').innerHTML = contatti.length ? contatti.join(' | ') : '-';
|
||||
}
|
||||
|
||||
function rimuoviMembro(btn) {
|
||||
const row = btn.closest('tr');
|
||||
row.remove();
|
||||
if (document.getElementById('membri-tbody').children.length === 0) {
|
||||
document.getElementById('no-membri-msg').style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
function updateResponsabiliFromMembri() {
|
||||
const respSelect = document.getElementById('responsabile-select');
|
||||
const currentSelected = Array.from(respSelect.selectedOptions).map(o => o.value);
|
||||
const tbody = document.getElementById('membri-tbody');
|
||||
const rows = tbody.querySelectorAll('tr');
|
||||
|
||||
const membroIds = new Set();
|
||||
const membroNames = new Map();
|
||||
|
||||
rows.forEach(row => {
|
||||
const select = row.querySelector('select[name*="individuo_id"]');
|
||||
if (select && select.value) {
|
||||
membroIds.add(select.value);
|
||||
const option = select.options[select.selectedIndex];
|
||||
membroNames.set(select.value, option.text);
|
||||
}
|
||||
});
|
||||
|
||||
respSelect.innerHTML = '';
|
||||
|
||||
if (membroIds.size === 0) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = '';
|
||||
opt.textContent = '-- Aggiungi prima dei membri --';
|
||||
opt.disabled = true;
|
||||
respSelect.appendChild(opt);
|
||||
} else {
|
||||
membroIds.forEach(id => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = id;
|
||||
opt.textContent = membroNames.get(id);
|
||||
if (currentSelected.includes(id)) opt.selected = true;
|
||||
respSelect.appendChild(opt);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const respSelect = document.getElementById('responsabile-select');
|
||||
const individuiList = @json($individui->map(fn($i) => ['id' => $i->id, 'text' => $i->cognome . ' ' . $i->nome]));
|
||||
const initialResponsabili = @json(old('responsabile_ids', []));
|
||||
|
||||
const optPlaceholder = document.createElement('option');
|
||||
optPlaceholder.value = '';
|
||||
optPlaceholder.textContent = '-- Aggiungi prima dei membri --';
|
||||
optPlaceholder.disabled = true;
|
||||
respSelect.appendChild(optPlaceholder);
|
||||
|
||||
initialResponsabili.forEach(id => {
|
||||
const match = individuiList.find(i => i.id == id);
|
||||
if (match) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = match.id;
|
||||
opt.textContent = match.text;
|
||||
opt.selected = true;
|
||||
respSelect.appendChild(opt);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,477 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Modifica Gruppo')
|
||||
@section('page_title', 'Modifica Gruppo')
|
||||
|
||||
@section('content')
|
||||
<form action="/gruppi/{{ $gruppo->id }}" method="POST">
|
||||
@csrf @method('PUT')
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-folder mr-2"></i>Dati Gruppo</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="nome" class="form-control" value="{{ old('nome', $gruppo->nome) }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Gruppo Padre</label>
|
||||
<select name="parent_id" class="form-control">
|
||||
<option value="">Nessuno (gruppo radice)</option>
|
||||
@foreach($gruppi as $g)
|
||||
@if($g->id !== $gruppo->id)
|
||||
<option value="{{ $g->id }}" {{ old('parent_id', $gruppo->parent_id) == $g->id ? 'selected' : '' }}>
|
||||
{{ $g->full_path }}
|
||||
</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Diocesi</label>
|
||||
<select name="diocesi_id" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
@foreach($diocesi as $d)
|
||||
<option value="{{ $d->id }}" {{ old('diocesi_id', $gruppo->diocesi_id) == $d->id ? 'selected' : '' }}>{{ $d->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Responsabili (solo membri)</label>
|
||||
<select name="responsabile_ids[]" class="form-control" multiple size="4">
|
||||
@foreach($membri as $m)
|
||||
<option value="{{ $m->id }}" {{ in_array($m->id, old('responsabile_ids', $gruppo->getResponsabiliIds() ?? [])) ? 'selected' : '' }}>
|
||||
{{ $m->cognome }} {{ $m->nome }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@if($membri->isEmpty())
|
||||
<small class="text-muted">Prima aggiungi dei membri al gruppo</small>
|
||||
@else
|
||||
<small class="text-muted">Ctrl+click per selezionare più responsabili</small>
|
||||
@endif
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione</label>
|
||||
<textarea name="descrizione" class="form-control" rows="3">{{ old('descrizione', $gruppo->descrizione) }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-map-marker-alt mr-2"></i>Luogo Incontro</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Indirizzo</label>
|
||||
<input type="text" name="indirizzo_incontro" class="form-control" value="{{ old('indirizzo_incontro', $gruppo->indirizzo_incontro) }}">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-4">
|
||||
<label>CAP</label>
|
||||
<input type="text" name="cap_incontro" class="form-control" value="{{ old('cap_incontro', $gruppo->cap_incontro) }}">
|
||||
</div>
|
||||
<div class="form-group col-md-5">
|
||||
<label>Città</label>
|
||||
<input type="text" name="città_incontro" class="form-control" value="{{ old('città_incontro', $gruppo->città_incontro) }}">
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label>Provincia</label>
|
||||
<input type="text" name="sigla_provincia_incontro" class="form-control" maxlength="2" value="{{ old('sigla_provincia_incontro', $gruppo->sigla_provincia_incontro) }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<label>URL Mappa</label>
|
||||
<input type="url" name="mappa_posizione" class="form-control" value="{{ old('mappa_posizione', $gruppo->mappa_posizione) }}" placeholder="https://...">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-users mr-2"></i>Membri</h3>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="showMembroForm()">
|
||||
<i class="fas fa-plus mr-1"></i> Aggiungi
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
|
||||
@if($gruppo->individui->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 25%;">Individuo</th>
|
||||
<th style="width: 80px;">Codice</th>
|
||||
<th style="width: 25%;">Contatti</th>
|
||||
<th style="width: 130px;">Ruolo</th>
|
||||
<th style="width: 110px;">Data Adesione</th>
|
||||
<th style="width: 80px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($gruppo->individui as $membro)
|
||||
<tr id="membro-row-{{ $membro->id }}">
|
||||
<td>
|
||||
<i class="fas fa-user text-primary mr-1"></i>
|
||||
<a href="/individui/{{ $membro->id }}">{{ $membro->cognome }} {{ $membro->nome }}</a>
|
||||
</td>
|
||||
<td>{{ $membro->codice_id }}</td>
|
||||
<td>
|
||||
<small>
|
||||
@if($membro->email_primaria)
|
||||
<span class="text-primary">{{ $membro->email_primaria }}</span><br>
|
||||
@endif
|
||||
@if($membro->telefono_primario)
|
||||
<span class="text-success">{{ $membro->telefono_primario }}</span>
|
||||
@endif
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
@php $ruoliMembro = $membro->getRuoliForGruppo($gruppo->id) @endphp
|
||||
@if($ruoliMembro->count() > 0)
|
||||
@foreach($ruoliMembro as $ruolo)
|
||||
<span class="badge badge-info mr-1">{{ $ruolo->nome }}</span>
|
||||
@endforeach
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $membro->pivot->data_adesione ? \Carbon\Carbon::parse($membro->pivot->data_adesione)->format('d/m/Y') : '-' }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-warning" onclick="editMembroInline({{ $membro->id }})" title="Modifica">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-xs btn-danger" onclick="deleteMembro({{ $membro->id }})" title="Rimuovi">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="membro-edit-{{ $membro->id }}" style="display:none;">
|
||||
<td colspan="6">
|
||||
<form action="/gruppi/{{ $gruppo->id }}/membri/{{ $membro->id }}" method="POST" class="mb-0">
|
||||
@csrf @method('PUT')
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control form-control-sm" value="{{ $membro->cognome }} {{ $membro->nome }}" disabled>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="text" class="form-control form-control-sm" value="{{ $membro->codice_id }}" disabled>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="ruolo_ids[]" class="form-control form-control-sm" multiple size="3">
|
||||
@foreach(\App\Models\Ruolo::attive() as $ruolo)
|
||||
@php $selected = in_array($ruolo->id, $membro->getRuoloIdsForGruppo($gruppo->id)) @endphp
|
||||
<option value="{{ $ruolo->id }}" {{ $selected ? 'selected' : '' }}>{{ $ruolo->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="date" name="data_adesione" class="form-control form-control-sm" value="{{ $membro->pivot->data_adesione }}">
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<button type="submit" class="btn btn-xs btn-success"><i class="fas fa-check"></i></button>
|
||||
<button type="button" class="btn btn-xs btn-secondary" onclick="cancelEditMembro({{ $membro->id }})"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-users fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun membro associato</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-file mr-2"></i>Documenti</h3>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="showDocumentoForm()">
|
||||
<i class="fas fa-plus mr-1"></i> Carica
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="documento-add-form" style="display:none;" class="p-3 bg-light border-bottom">
|
||||
<form action="/documenti" method="POST" enctype="multipart/form-data" class="mb-0">
|
||||
@csrf
|
||||
<input type="hidden" name="visibilita" value="gruppo">
|
||||
<input type="hidden" name="visibilita_target_id" value="{{ $gruppo->id }}">
|
||||
<input type="hidden" name="visibilita_target_type" value="App\Models\Gruppo">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<input type="text" name="nome_file" class="form-control form-control-sm" placeholder="Nome documento" required>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="tipologia" class="form-control form-control-sm" required>
|
||||
<option value="">Tipologia...</option>
|
||||
<option value="documento">Documento</option>
|
||||
<option value="statuto">Statuto</option>
|
||||
<option value="avatar">Avatar</option>
|
||||
<option value="galleria">Galleria</option>
|
||||
<option value="altro">Altro</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<input type="file" name="file" class="form-control form-control-sm" required>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-xs btn-success"><i class="fas fa-upload mr-1"></i>Carica</button>
|
||||
<button type="button" class="btn btn-xs btn-secondary" onclick="hideDocumentoForm()"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@if($gruppo->documenti && $gruppo->documenti->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th style="width: 120px;">Tipologia</th>
|
||||
<th style="width: 80px;">Dimensione</th>
|
||||
<th style="width: 130px;">Data Upload</th>
|
||||
<th style="width: 80px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($gruppo->documenti as $documento)
|
||||
<tr>
|
||||
<td>
|
||||
<i class="fas fa-file text-secondary mr-1"></i>
|
||||
{{ $documento->nome_file }}
|
||||
</td>
|
||||
<td>{{ ucfirst(str_replace('_', ' ', $documento->tipologia)) }}</td>
|
||||
<td>{{ number_format($documento->dimensione / 1024, 1) }} KB</td>
|
||||
<td>{{ $documento->created_at->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
@if($documento->file_path)
|
||||
<button type="button" class="btn btn-xs btn-primary" onclick="previewDocumento({{ $documento->id }}, '{{ $documento->mime_type }}')" title="Anteprima">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
@endif
|
||||
<form action="/documenti/{{ $documento->id }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo documento?')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-file fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun documento</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva Modifiche
|
||||
</button>
|
||||
<a href="/gruppi/{{ $gruppo->id }}" class="btn btn-secondary">
|
||||
<i class="fas fa-times mr-1"></i> Annulla
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="card mt-3" id="membro-card" style="display:none; background-color: #fff3cd; border-color: #ffc107;">
|
||||
<div class="card-header bg-warning">
|
||||
<h3 class="card-title"><i class="fas fa-user-plus mr-2"></i>Aggiungi Membro</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="membro-form-container">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<select id="new-membro-individuo" class="form-control form-control-sm">
|
||||
<option value="">Seleziona individuo...</option>
|
||||
@foreach(\App\Models\Individuo::whereNotIn('id', $membri->pluck('id'))->orderBy('cognome')->orderBy('nome')->get() as $i)
|
||||
<option value="{{ $i->id }}">{{ $i->cognome }} {{ $i->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select id="new-membro-ruolo" class="form-control form-control-sm" multiple size="3">
|
||||
@foreach(\App\Models\Ruolo::attive() as $ruolo)
|
||||
<option value="{{ $ruolo->id }}">{{ $ruolo->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Ctrl+click per selezionare più ruoli</small>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="date" id="new-membro-data" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="button" class="btn btn-success btn-sm" onclick="submitMembroForm()">
|
||||
<i class="fas fa-check mr-1"></i> Associa
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="hideMembroForm()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="previewModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document" style="max-width: 90vw;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-file mr-2"></i><span id="previewModalTitle">Anteprima</span></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Chiudi">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-0" style="min-height: 400px;">
|
||||
<iframe id="previewFrame" src="" style="width: 100%; height: 70vh; border: none;"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a id="previewDownloadBtn" href="#" class="btn btn-primary" download>
|
||||
<i class="fas fa-download mr-1"></i> Scarica
|
||||
</a>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Chiudi</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function showMembroForm() {
|
||||
console.log('showMembroForm called');
|
||||
var card = document.getElementById('membro-card');
|
||||
console.log('Card element:', card);
|
||||
if (card) {
|
||||
card.style.display = 'block';
|
||||
card.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
console.log('Card shown');
|
||||
} else {
|
||||
console.error('Card not found');
|
||||
}
|
||||
}
|
||||
|
||||
function hideMembroForm() {
|
||||
var card = document.getElementById('membro-card');
|
||||
if (card) {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function submitMembroForm() {
|
||||
var individuoId = document.getElementById('new-membro-individuo').value;
|
||||
var ruoloSelect = document.getElementById('new-membro-ruolo');
|
||||
var dataAdesione = document.getElementById('new-membro-data').value;
|
||||
|
||||
if (!individuoId) {
|
||||
alert('Seleziona un individuo');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('new-membro-individuo').removeAttribute('required');
|
||||
|
||||
var formData = new URLSearchParams();
|
||||
formData.append('individuo_id', individuoId);
|
||||
|
||||
var selectedRuoli = Array.from(ruoloSelect.selectedOptions).map(opt => opt.value).filter(v => v);
|
||||
selectedRuoli.forEach(function(ruoloId) {
|
||||
formData.append('ruolo_ids[]', ruoloId);
|
||||
});
|
||||
|
||||
formData.append('data_adesione', dataAdesione);
|
||||
|
||||
fetch('/gruppi/{{ $gruppo->id }}/membri', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.text().then(text => { throw new Error(text); });
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
hideMembroForm();
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(data.error || 'Errore');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Errore: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function editMembroInline(id) {
|
||||
document.getElementById('membro-row-' + id).style.display = 'none';
|
||||
document.getElementById('membro-edit-' + id).style.display = 'table-row';
|
||||
}
|
||||
|
||||
function cancelEditMembro(id) {
|
||||
document.getElementById('membro-row-' + id).style.display = 'table-row';
|
||||
document.getElementById('membro-edit-' + id).style.display = 'none';
|
||||
}
|
||||
|
||||
function deleteMembro(individuoId) {
|
||||
if (!confirm('Rimuovere questo membro dal gruppo?')) return;
|
||||
|
||||
fetch('/gruppi/{{ $gruppo->id }}/membri/' + individuoId, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.text().then(text => { throw new Error(text); });
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(data.error || 'Errore');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Errore: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function showDocumentoForm() {
|
||||
document.getElementById('documento-add-form').style.display = 'block';
|
||||
}
|
||||
|
||||
function hideDocumentoForm() {
|
||||
document.getElementById('documento-add-form').style.display = 'none';
|
||||
}
|
||||
|
||||
function previewDocumento(id, mimeType) {
|
||||
document.getElementById('previewFrame').src = '/documenti/' + id + '/preview';
|
||||
document.getElementById('previewDownloadBtn').href = '/documenti/' + id + '/download';
|
||||
$('#previewModal').modal('show');
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,434 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Gruppi')
|
||||
@section('page_title', 'Elenco Gruppi')
|
||||
|
||||
@php
|
||||
$canWriteGruppi = Auth::user()->canManage('gruppi');
|
||||
$canDeleteGruppi = Auth::user()->canDelete('gruppi');
|
||||
$columnLabels = [
|
||||
'nome' => 'Nome',
|
||||
'descrizione' => 'Descrizione',
|
||||
'diocesi' => 'Diocesi',
|
||||
'livello' => 'Livello',
|
||||
'padre' => 'Gruppo Padre',
|
||||
'membri' => 'Membri',
|
||||
'responsabili' => 'Responsabili',
|
||||
'indirizzo' => 'Indirizzo',
|
||||
'citta' => 'Città',
|
||||
'telefono' => 'Telefono',
|
||||
'email' => 'Email',
|
||||
];
|
||||
$vistaDefaultJson = $vista ? $vista->toJson() : 'null';
|
||||
$allColumnsJson = json_encode($allColumns);
|
||||
$visibleColumnsJson = json_encode($visibleColumns);
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-sitemap mr-2"></i>Elenco Gruppi</h3>
|
||||
<div class="card-tools">
|
||||
<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
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-primary {{ $viewMode === 'tree' ? 'active' : '' }}" onclick="switchView('tree')" id="btn-tree-view">
|
||||
<i class="fas fa-sitemap mr-1"></i> Albero
|
||||
</button>
|
||||
</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.create') }}" class="btn btn-success btn-sm">
|
||||
<i class="fas fa-plus"></i> Nuovo Gruppo
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</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>
|
||||
@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
|
||||
@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>
|
||||
@endif
|
||||
@if(in_array('diocesi', $visibleColumns))
|
||||
<th>Diocesi</th>
|
||||
@endif
|
||||
@if(in_array('livello', $visibleColumns))
|
||||
<th style="width: 80px;">Livello</th>
|
||||
@endif
|
||||
@if(in_array('padre', $visibleColumns))
|
||||
<th>Gruppo Padre</th>
|
||||
@endif
|
||||
@if(in_array('membri', $visibleColumns))
|
||||
<th style="width: 100px;">Membri</th>
|
||||
@endif
|
||||
@if(in_array('responsabili', $visibleColumns))
|
||||
<th>Responsabili</th>
|
||||
@endif
|
||||
@if(in_array('indirizzo', $visibleColumns))
|
||||
<th>Indirizzo</th>
|
||||
@endif
|
||||
@if(in_array('citta', $visibleColumns))
|
||||
<th>Città</th>
|
||||
@endif
|
||||
@if(in_array('telefono', $visibleColumns))
|
||||
<th>Telefono</th>
|
||||
@endif
|
||||
@if(in_array('email', $visibleColumns))
|
||||
<th>Email</th>
|
||||
@endif
|
||||
<th style="width: 120px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="table-body">
|
||||
@foreach($gruppi as $gruppo)
|
||||
<tr data-id="{{ $gruppo->id }}">
|
||||
@if(in_array('nome', $visibleColumns))
|
||||
<td>
|
||||
<span style="padding-left: {{ ($gruppo->depth ?? 0) * 20 }}px;">
|
||||
<i class="fas fa-{{ $gruppo->children->count() > 0 ? 'folder' : 'folder-open' }} text-{{ ($gruppo->depth ?? 0) == 0 ? 'warning' : 'secondary' }} mr-1"></i>
|
||||
<a href="{{ route('gruppi.show', $gruppo->id) }}">{{ $gruppo->nome }}</a>
|
||||
</span>
|
||||
</td>
|
||||
@endif
|
||||
@if(in_array('descrizione', $visibleColumns))
|
||||
<td>{{ Str::limit($gruppo->descrizione, 50) ?: '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('diocesi', $visibleColumns))
|
||||
<td>{{ $gruppo->diocesi?->nome ?? '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('livello', $visibleColumns))
|
||||
<td><span class="badge badge-{{ ($gruppo->depth ?? 0) == 0 ? 'primary' : 'secondary' }}">{{ $gruppo->depth ?? 0 }}</span></td>
|
||||
@endif
|
||||
@if(in_array('padre', $visibleColumns))
|
||||
<td>{{ $gruppo->parent?->nome ?? '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('membri', $visibleColumns))
|
||||
<td><span class="badge badge-info">{{ $gruppo->individui->count() }}</span></td>
|
||||
@endif
|
||||
@if(in_array('responsabili', $visibleColumns))
|
||||
<td>
|
||||
@php $resp = $gruppo->getResponsabili() @endphp
|
||||
@if($resp->count() > 0)
|
||||
{{ $resp->pluck('cognome')->implode(', ') }}
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
</td>
|
||||
@endif
|
||||
@if(in_array('indirizzo', $visibleColumns))
|
||||
<td>{{ $gruppo->indirizzo_incontro ?: '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('citta', $visibleColumns))
|
||||
<td>{{ $gruppo->città_incontro ?: '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('telefono', $visibleColumns))
|
||||
<td>-</td>
|
||||
@endif
|
||||
@if(in_array('email', $visibleColumns))
|
||||
<td>-</td>
|
||||
@endif
|
||||
<td>
|
||||
<a href="{{ route('gruppi.show', $gruppo->id) }}" class="btn btn-xs btn-info" title="Visualizza">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
@if($canWriteGruppi)
|
||||
<a href="{{ url('/gruppi/' . $gruppo->id . '/edit') }}" class="btn btn-xs btn-warning" title="Modifica">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
@endif
|
||||
@if($canDeleteGruppi)
|
||||
<form action="{{ url('/gruppi/' . $gruppo->id) }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Confermi l\'eliminazione?')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<div class="card-body p-0">
|
||||
<div class="tree-view" style="padding: 15px;">
|
||||
@php $rootGruppi = $gruppi->filter(fn($g) => $g->parent_id === null) @endphp
|
||||
@forelse($rootGruppi as $gruppo)
|
||||
@include('gruppi.partials.tree-item', ['gruppo' => $gruppo, 'isRoot' => true])
|
||||
@empty
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-folder-open fa-2x mb-2"></i>
|
||||
<p>Nessun gruppo presente.
|
||||
@if($canWriteGruppi)
|
||||
<a href="{{ route('gruppi.create') }}">Crea il primo gruppo</a>
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
@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>×</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>×</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>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function switchView(mode) {
|
||||
window.location.href = '?view=' + mode;
|
||||
}
|
||||
|
||||
function toggleItem(id) {
|
||||
const el = document.getElementById('group-' + id);
|
||||
const icon = document.getElementById('icon-' + id);
|
||||
if (el.style.display === 'none') {
|
||||
el.style.display = 'block';
|
||||
icon.classList.remove('fa-chevron-right');
|
||||
icon.classList.add('fa-chevron-down');
|
||||
} else {
|
||||
el.style.display = 'none';
|
||||
icon.classList.remove('fa-chevron-down');
|
||||
icon.classList.add('fa-chevron-right');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAllGroups() {
|
||||
const collapsed = document.querySelectorAll('.tree-children[style*="display: none"]').length > 0;
|
||||
document.querySelectorAll('.tree-children').forEach(el => {
|
||||
el.style.display = collapsed ? 'block' : 'none';
|
||||
});
|
||||
document.querySelectorAll('.tree-toggle i').forEach(icon => {
|
||||
if (collapsed) {
|
||||
icon.classList.remove('fa-chevron-right');
|
||||
icon.classList.add('fa-chevron-down');
|
||||
} else {
|
||||
icon.classList.remove('fa-chevron-down');
|
||||
icon.classList.add('fa-chevron-right');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let sortDirection = {};
|
||||
|
||||
function sortTable(column) {
|
||||
const tbody = document.getElementById('table-body');
|
||||
const rows = Array.from(tbody.querySelectorAll('tr'));
|
||||
|
||||
sortDirection[column] = sortDirection[column] === 'asc' ? 'desc' : 'asc';
|
||||
const dir = sortDirection[column];
|
||||
|
||||
rows.sort(function(a, b) {
|
||||
const cellA = a.querySelector(`td:nth-child(${getColumnIndex(column)})`)?.textContent.trim() || '';
|
||||
const cellB = b.querySelector(`td:nth-child(${getColumnIndex(column)})`)?.textContent.trim() || '';
|
||||
|
||||
if (dir === 'asc') {
|
||||
return cellA.localeCompare(cellB, 'it');
|
||||
}
|
||||
return cellB.localeCompare(cellA, 'it');
|
||||
});
|
||||
|
||||
rows.forEach(row => tbody.appendChild(row));
|
||||
|
||||
document.querySelectorAll('th i.fa-sort').forEach(icon => {
|
||||
icon.className = 'fas fa-sort float-right text-muted';
|
||||
});
|
||||
const th = document.querySelector(`th[data-sortable][onclick="sortTable('${column}')"] i`);
|
||||
if (th) {
|
||||
th.className = `fas fa-sort-${dir === 'asc' ? 'up' : 'down'} float-right`;
|
||||
}
|
||||
}
|
||||
|
||||
function getColumnIndex(column) {
|
||||
const th = document.querySelector(`th[onclick="sortTable('${column}')"]`);
|
||||
if (th) {
|
||||
return Array.from(th.parentNode.children).indexOf(th) + 1;
|
||||
}
|
||||
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; }
|
||||
.tree-children {
|
||||
margin-left: 25px;
|
||||
border-left: 1px dashed #dee2e6;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.tree-toggle {
|
||||
cursor: pointer;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.tree-toggle:hover {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.tree-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.badge-level {
|
||||
font-size: 0.7em;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
.btn-group .btn.active {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border-color: #007bff;
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
@@ -0,0 +1,72 @@
|
||||
@php
|
||||
$children = $gruppo->children()->with(['diocesi', 'individui'])->orderBy('nome')->get();
|
||||
$hasChildren = $children->count() > 0;
|
||||
$canWriteGruppi = Auth::user()->canManage('gruppi');
|
||||
$responsabili = $gruppo->getResponsabili();
|
||||
@endphp
|
||||
|
||||
<div class="tree-item" style="margin-left: {{ $gruppo->depth ?? 0 }}px;">
|
||||
<div class="tree-label">
|
||||
@if($hasChildren)
|
||||
<span class="tree-toggle" onclick="toggleItem({{ $gruppo->id }})">
|
||||
<i id="icon-{{ $gruppo->id }}" class="fas fa-chevron-right text-muted"></i>
|
||||
</span>
|
||||
@else
|
||||
<span style="width: 22px; display: inline-block;"></span>
|
||||
@endif
|
||||
<i class="fas fa-{{ $hasChildren ? 'folder' : 'folder-open' }} text-{{ $gruppo->depth == 0 ? 'warning' : 'secondary' }} mr-1"></i>
|
||||
<a href="{{ route('gruppi.show', $gruppo->id) }}" class="{{ ($gruppo->depth ?? 0) == 0 ? 'font-weight-bold' : '' }}">
|
||||
{{ $gruppo->nome }}
|
||||
</a>
|
||||
<span class="badge badge-{{ ($gruppo->depth ?? 0) == 0 ? 'primary' : 'secondary' }} badge-level">{{ $gruppo->depth ?? 0 }}</span>
|
||||
<small class="text-muted ml-2">
|
||||
{{ $gruppo->diocesi?->nome ?? '' }}
|
||||
@if($responsabili->count() > 0)
|
||||
· <i class="fas fa-user text-info"></i> {{ $responsabili->pluck('cognome')->implode(', ') }}
|
||||
@endif
|
||||
</small>
|
||||
<span class="ml-auto">
|
||||
@if($gruppo->individui_count > 0)
|
||||
<span class="badge badge-info">{{ $gruppo->individui_count }} membri</span>
|
||||
@endif
|
||||
@if($hasChildren)
|
||||
<span class="badge badge-warning">{{ $children->count() }} figli</span>
|
||||
@endif
|
||||
</span>
|
||||
<span class="ml-2">
|
||||
<a href="{{ route('gruppi.show', $gruppo->id) }}" class="btn btn-xs btn-info" title="Visualizza">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
@if($canWriteGruppi)
|
||||
<a href="{{ url('/gruppi/' . $gruppo->id . '/edit') }}" class="btn btn-xs btn-warning" title="Modifica">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
@php
|
||||
$isParent = $hasChildren;
|
||||
$isSuperAdmin = Auth::user()->isSuperAdmin();
|
||||
$canDelete = !$isParent || $isSuperAdmin;
|
||||
@endphp
|
||||
@if($canDelete)
|
||||
<form action="{{ url('/gruppi/' . $gruppo->id) }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Confermi l\'eliminazione? Verranno eliminati anche tutti i sottogruppi.')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<button type="button" class="btn btn-xs btn-danger disabled" title="Solo il superamministratore può eliminare gruppi con sottogruppi">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
@endif
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($hasChildren)
|
||||
<div id="group-{{ $gruppo->id }}" class="tree-children" style="display: none;">
|
||||
@foreach($children as $child)
|
||||
@include('gruppi.partials.tree-item', ['gruppo' => $child])
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
@@ -0,0 +1,515 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Dettaglio Gruppo')
|
||||
@section('page_title', 'Dettaglio Gruppo')
|
||||
|
||||
@php
|
||||
$canWriteGruppi = Auth::user()->canManage('gruppi');
|
||||
$canDeleteGruppi = Auth::user()->canDelete('gruppi');
|
||||
@endphp
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item"><a href="{{ route('gruppi.index') }}">Gruppi</a></li>
|
||||
<li class="breadcrumb-item active">{{ $gruppo->nome }}</li>
|
||||
@endsection
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card card-success">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-folder text-warning mr-2"></i>
|
||||
{{ $gruppo->nome }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm table-borderless">
|
||||
<tr>
|
||||
<td style="width: 130px;"><strong>Path:</strong></td>
|
||||
<td class="text-muted small">{{ $gruppo->full_path }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Gruppo Padre:</strong></td>
|
||||
<td>
|
||||
@if($gruppo->parent)
|
||||
<a href="{{ route('gruppi.show', $gruppo->parent->id) }}">{{ $gruppo->parent->nome }}</a>
|
||||
@else
|
||||
<span class="badge badge-success">Gruppo radice</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Diocesi:</strong></td>
|
||||
<td>{{ $gruppo->diocesi?->nome ?? '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Responsabili:</strong></td>
|
||||
<td>
|
||||
@php $responsabili = $gruppo->getResponsabili() @endphp
|
||||
@if($responsabili->count() > 0)
|
||||
@foreach($responsabili as $resp)
|
||||
<a href="{{ route('individui.show', $resp->id) }}">
|
||||
<i class="fas fa-user text-info mr-1"></i>
|
||||
{{ $resp->nome_completo }}
|
||||
</a><br>
|
||||
@endforeach
|
||||
@else
|
||||
<span class="text-muted">-</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-map-marker-alt mr-2"></i>Luogo Incontro</h3></div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr>
|
||||
<td style="width: 100px;"><strong>Indirizzo:</strong></td>
|
||||
<td>{{ $gruppo->indirizzo_incontro ?: '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>CAP:</strong></td>
|
||||
<td>{{ $gruppo->cap_incontro ?: '-' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Città:</strong></td>
|
||||
<td>{{ $gruppo->città_incontro ?: '-' }} {{ $gruppo->sigla_provincia_incontro ? "({$gruppo->sigla_provincia_incontro})" : '' }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
@if($gruppo->mappa_posizione)
|
||||
<a href="{{ $gruppo->mappa_posizione }}" target="_blank" class="btn btn-sm btn-outline-primary mt-2">
|
||||
<i class="fas fa-external-link-alt mr-1"></i> Visualizza su mappa
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-chart-pie mr-2"></i>Statistiche</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="row text-center">
|
||||
<div class="col-6">
|
||||
<div class="border-right">
|
||||
<h2 class="mb-0 text-primary">{{ $gruppo->individui->count() }}</h2>
|
||||
<small class="text-muted">Membri</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h2 class="mb-0 text-warning">{{ $gruppo->children->count() }}</h2>
|
||||
<small class="text-muted">Sottogruppi</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$incontroEvento = $gruppo->eventi->where('is_incontro_gruppo', true)->first();
|
||||
@endphp
|
||||
@if($incontroEvento)
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card card-success">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-calendar-check mr-2"></i>
|
||||
Giorno di Incontro
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr>
|
||||
<td style="width: 100px;"><strong>Evento:</strong></td>
|
||||
<td>{{ $incontroEvento->nome_evento }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Quando:</strong></td>
|
||||
<td>
|
||||
@if($incontroEvento->tipo_recorrenza === 'settimanale')
|
||||
{{ $incontroEvento->giorno_settimana_label }}
|
||||
<span class="badge badge-info ml-1">Settimanale</span>
|
||||
@elseif($incontroEvento->tipo_recorrenza === 'mensile')
|
||||
{{ $incontroEvento->occorrenza_mensile_label }}
|
||||
@if($incontroEvento->mesi_recorrenza)
|
||||
<small class="text-muted">({{ $incontroEvento->mesi_recorrenza_label }})</small>
|
||||
@endif
|
||||
<span class="badge badge-info ml-1">Mensile</span>
|
||||
@elseif($incontroEvento->tipo_recorrenza === 'annuale')
|
||||
{{ $incontroEvento->mese_annuale_label }}
|
||||
<span class="badge badge-info ml-1">Annuale</span>
|
||||
@elseif($incontroEvento->tipo_recorrenza === 'altro')
|
||||
{{ $incontroEvento->giorno_settimana_label }}
|
||||
<span class="badge badge-info ml-1">Altro</span>
|
||||
@else
|
||||
{{ $incontroEvento->data_specifica?->format('d/m/Y') ?: '-' }}
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@if($incontroEvento->ora_inizio)
|
||||
<tr>
|
||||
<td><strong>Ora:</strong></td>
|
||||
<td>ore {{ $incontroEvento->ora_inizio->format('H:i') }}</td>
|
||||
</tr>
|
||||
@endif
|
||||
</table>
|
||||
@if($incontroEvento->responsabili->count() > 0)
|
||||
<hr>
|
||||
<small class="text-muted"><strong>Responsabili:</strong></small>
|
||||
@foreach($incontroEvento->responsabili as $resp)
|
||||
<div class="mt-1">
|
||||
<i class="fas fa-user text-info mr-1"></i>
|
||||
<a href="/individui/{{ $resp->id }}">{{ $resp->cognome }} {{ $resp->nome }}</a>
|
||||
@if($resp->telefono_primario)
|
||||
<br><small class="text-success ml-3">{{ $resp->telefono_primario }}</small>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
<div class="mt-2">
|
||||
<a href="/eventi/{{ $incontroEvento->id }}" class="btn btn-xs btn-outline-primary">
|
||||
<i class="fas fa-eye mr-1"></i> Vedi evento
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($gruppo->descrizione)
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-align-left mr-2"></i>Descrizione</h3></div>
|
||||
<div class="card-body">
|
||||
<p class="mb-0">{!! nl2br(e($gruppo->descrizione)) !!}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-users mr-2"></i>Membri ({{ $gruppo->individui->count() }})
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if($gruppo->individui->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 100px;">Codice</th>
|
||||
<th>Nome Completo</th>
|
||||
<th>Email</th>
|
||||
<th>Telefono</th>
|
||||
<th style="width: 150px;">Ruolo</th>
|
||||
<th style="width: 110px;">Data Adesione</th>
|
||||
<th style="width: 80px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($gruppo->individui as $individuo)
|
||||
<tr>
|
||||
<td><span class="badge badge-secondary">{{ $individuo->codice_id }}</span></td>
|
||||
<td>
|
||||
<a href="{{ route('individui.show', $individuo->id) }}">
|
||||
<i class="fas fa-user text-info mr-1"></i>
|
||||
{{ $individuo->cognome }} {{ $individuo->nome }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ $individuo->email_primaria ?? '-' }}</td>
|
||||
<td>{{ $individuo->telefono_primario ?? '-' }}</td>
|
||||
<td>
|
||||
@php
|
||||
$ruoloIds = $individuo->pivot->ruolo_ids ? json_decode($individuo->pivot->ruolo_ids, true) : [];
|
||||
$ruoli = \App\Models\Ruolo::findByIds($ruoloIds);
|
||||
@endphp
|
||||
@if($ruoli->count() > 0)
|
||||
@foreach($ruoli as $ruolo)
|
||||
<span class="badge badge-info mr-1">{{ $ruolo->nome }}</span>
|
||||
@endforeach
|
||||
@else
|
||||
<span class="text-muted">-</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if($individuo->pivot->data_adesione)
|
||||
{{ \Carbon\Carbon::parse($individuo->pivot->data_adesione)->format('d/m/Y') }}
|
||||
@else
|
||||
<span class="text-muted">-</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ route('individui.show', $individuo->id) }}" class="btn btn-xs btn-info" title="Visualizza">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-users fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun membro</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-file mr-2"></i>Documenti ({{ $gruppo->documenti->count() }})
|
||||
</h3>
|
||||
@if($canWriteGruppi)
|
||||
<button type="button" class="btn btn-xs btn-primary float-right" data-toggle="modal" data-target="#uploadDocumentoModal">
|
||||
<i class="fas fa-upload mr-1"></i> Carica
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if($gruppo->documenti->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th style="width: 120px;">Tipologia</th>
|
||||
<th style="width: 80px;">Dimensione</th>
|
||||
<th style="width: 130px;">Data Upload</th>
|
||||
<th style="width: 100px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($gruppo->documenti as $documento)
|
||||
<tr>
|
||||
<td>
|
||||
<a href="#" onclick="event.preventDefault(); previewDocumento({{ $documento->id }}, '{{ $documento->mime_type }}');">
|
||||
<i class="fas fa-file text-secondary mr-1"></i>
|
||||
{{ $documento->nome_file }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ ucfirst(str_replace('_', ' ', $documento->tipologia)) }}</td>
|
||||
<td>{{ number_format($documento->dimensione / 1024, 1) }} KB</td>
|
||||
<td>{{ $documento->created_at->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
@if($documento->file_path)
|
||||
<a href="{{ url('/documenti/' . $documento->id . '/download') }}" class="btn btn-xs btn-primary" title="Scarica">
|
||||
<i class="fas fa-download"></i>
|
||||
</a>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-file fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun documento</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">
|
||||
<i class="fas fa-sitemap mr-2"></i>Sottogruppi ({{ $gruppo->children->count() }})
|
||||
</h3>
|
||||
<a href="{{ route('gruppi.create', ['parent_id' => $gruppo->id]) }}" class="btn btn-xs btn-success float-right">
|
||||
<i class="fas fa-plus mr-1"></i> Aggiungi Sottogruppo
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if($gruppo->children->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Diocesi</th>
|
||||
<th>Responsabile</th>
|
||||
<th style="text-align: center;">Membri</th>
|
||||
<th style="width: 100px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($gruppo->children->sortBy('nome') as $child)
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ route('gruppi.show', $child->id) }}">
|
||||
<i class="fas fa-folder-open text-warning mr-1"></i>
|
||||
{{ $child->nome }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ $child->diocesi?->nome ?? '-' }}</td>
|
||||
<td>{{ $child->getResponsabili()->isNotEmpty() ? $child->getResponsabili()->first()->nome_completo : '-' }}</td>
|
||||
<td class="text-center">
|
||||
@if($child->individui()->count() > 0)
|
||||
<span class="badge badge-info">{{ $child->individui()->count() }}</span>
|
||||
@else
|
||||
<span class="text-muted">-</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ route('gruppi.show', $child->id) }}" class="btn btn-xs btn-info" title="Visualizza">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
@if($canWriteGruppi)
|
||||
<a href="{{ route('gruppi.edit', $child->id) }}" class="btn btn-xs btn-warning" title="Modifica">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
@endif
|
||||
@if($canDeleteGruppi)
|
||||
<form action="{{ route('gruppi.destroy', $child->id) }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare {{ $child->nome }}?')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-folder-open fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun sottogruppo</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
@if($canWriteGruppi)
|
||||
<a href="{{ url('/gruppi/' . $gruppo->id . '/edit') }}" class="btn btn-warning">
|
||||
<i class="fas fa-edit mr-1"></i> Modifica
|
||||
</a>
|
||||
@endif
|
||||
@if($canDeleteGruppi)
|
||||
<form action="{{ url('/gruppi/' . $gruppo->id) }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-danger" onclick="return confirm('Confermi l\'eliminazione di {{ $gruppo->nome }}? Verranno eliminati anche tutti i sottogruppi.')">
|
||||
<i class="fas fa-trash mr-1"></i> Elimina
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
<a href="{{ route('gruppi.index') }}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left mr-1"></i> Torna all'elenco
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="uploadDocumentoModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-primary text-white">
|
||||
<h5 class="modal-title"><i class="fas fa-upload mr-2"></i>Carica Documento</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<form action="/documenti" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<input type="hidden" name="visibilita" value="gruppo">
|
||||
<input type="hidden" name="visibilita_target_id" value="{{ $gruppo->id }}">
|
||||
<input type="hidden" name="visibilita_target_type" value="App\Models\Gruppo">
|
||||
<input type="hidden" name="_redirect" value="{{ url()->current() }}">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="nome_file" class="form-control" placeholder="Nome del documento" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Tipologia *</label>
|
||||
<select name="tipologia" class="form-control" required>
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="documento">Documento</option>
|
||||
<option value="statuto">Statuto</option>
|
||||
<option value="avatar">Avatar</option>
|
||||
<option value="galleria">Galleria</option>
|
||||
<option value="altro">Altro</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>File *</label>
|
||||
<input type="file" name="file" class="form-control" required>
|
||||
<small class="text-muted">Max 10MB</small>
|
||||
</div>
|
||||
</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-upload mr-1"></i> Carica
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="previewModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document" style="max-width: 90vw;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-file mr-2"></i><span id="previewModalTitle">Anteprima</span></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Chiudi">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-0" style="min-height: 400px;">
|
||||
<iframe id="previewFrame" src="" style="width: 100%; height: 70vh; border: none;"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a id="previewDownloadBtn" href="#" class="btn btn-primary" download>
|
||||
<i class="fas fa-download mr-1"></i> Scarica
|
||||
</a>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Chiudi</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function previewDocumento(id, mimeType) {
|
||||
var previewUrl = '/documenti/' + id + '/preview';
|
||||
var downloadUrl = '/documenti/' + id + '/download';
|
||||
|
||||
document.getElementById('previewFrame').src = previewUrl;
|
||||
document.getElementById('previewDownloadBtn').href = downloadUrl;
|
||||
document.getElementById('previewModalTitle').textContent = 'Anteprima';
|
||||
|
||||
if (mimeType && mimeType.startsWith('image/')) {
|
||||
document.getElementById('previewDownloadBtn').style.display = 'inline-block';
|
||||
} else {
|
||||
document.getElementById('previewDownloadBtn').style.display = 'none';
|
||||
}
|
||||
|
||||
$('#previewModal').modal('show');
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,101 @@
|
||||
@extends('layouts.adminlte')
|
||||
|
||||
@section('title', 'Dashboard')
|
||||
@section('page_title', 'Dashboard')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-lg-3 col-6">
|
||||
<div class="small-box bg-info">
|
||||
<div class="inner">
|
||||
<h3>{{ $stats['individui'] }}</h3>
|
||||
<p>Individui</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-users"></i>
|
||||
</div>
|
||||
<a href="{{ route('individui.index') }}" class="small-box-footer">Visualizza <i class="fas fa-arrow-circle-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-6">
|
||||
<div class="small-box bg-success">
|
||||
<div class="inner">
|
||||
<h3>{{ $stats['gruppi'] }}</h3>
|
||||
<p>Gruppi</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-folder"></i>
|
||||
</div>
|
||||
<a href="{{ route('gruppi.index') }}" class="small-box-footer">Visualizza <i class="fas fa-arrow-circle-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3 col-6">
|
||||
<div class="small-box bg-warning">
|
||||
<div class="inner">
|
||||
<h3>{{ $stats['notifiche'] }}</h3>
|
||||
<p>Notifiche</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fas fa-bell"></i>
|
||||
</div>
|
||||
<a href="#" class="small-box-footer">Visualizza <i class="fas fa-arrow-circle-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-bell"></i> Ultime Notifiche</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($notifiche->count() > 0)
|
||||
<ul class="todo-list" data-widget="todo-list">
|
||||
@foreach($notifiche as $notifica)
|
||||
<li>
|
||||
<span class="text">{{ $notifica->titolo }}</span>
|
||||
<small class="badge badge-info"><i class="far fa-clock"></i> {{ $notifica->created_at->diffForHumans() }}</small>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@else
|
||||
<p class="text-muted">Nessuna notifica</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-info-circle"></i> Benvenuto</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@php
|
||||
$appName = \App\Models\AppSetting::getAppName() ?? 'Glastree';
|
||||
$orgName = \App\Models\AppSetting::getOrgName();
|
||||
$welcome = \App\Models\AppSetting::getDashboardWelcome();
|
||||
@endphp
|
||||
@if($welcome)
|
||||
<p>{!! nl2br(e($welcome)) !!}</p>
|
||||
@else
|
||||
<p>Benvenuto in <strong>{{ e($appName) }}</strong>{{ $orgName ? ', ' . e($orgName) : '' }}.</p>
|
||||
@endif
|
||||
<p>Dal menu laterale puoi navigare tra le sezioni:</p>
|
||||
<ul>
|
||||
<li><strong>Individui</strong>: Gestione delle persone registrate</li>
|
||||
<li><strong>Gruppi</strong>: Gestione della struttura organizzativa</li>
|
||||
<li><strong>Documenti</strong>: Archivio documentale</li>
|
||||
<li><strong>Eventi</strong>: Calendario eventi</li>
|
||||
<li><strong>Mailing</strong>: Comunicazioni email</li>
|
||||
</ul>
|
||||
@if(Auth::user()->is_admin)
|
||||
<div class="alert alert-success mt-3">
|
||||
<i class="fas fa-crown"></i> <strong>Amministratore</strong>: Hai accesso completo al sistema.
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,519 @@
|
||||
@extends('layouts.adminlte')
|
||||
|
||||
@section('title', 'Impostazioni')
|
||||
|
||||
@section('page_title', 'Impostazioni')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item"><a href="{{ route('dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item active">Impostazioni</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="card card-secondary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Sezioni</h3>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<a href="#applicazione" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-cog mr-2"></i> Applicazione
|
||||
</a>
|
||||
<a href="#logo" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-image mr-2"></i> Logo
|
||||
</a>
|
||||
<a href="#tipologie" class="list-group-item list-group-item-action" data-toggle="tab">
|
||||
<i class="nav-icon fas fa-file mr-2"></i> Tipologie Documenti
|
||||
</a>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane" id="applicazione">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Configurazione Applicazione</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">Configura il nome e il testo visualizzato dall'applicazione.</p>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">{{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('impostazioni.app-settings.save') }}">
|
||||
@csrf
|
||||
@method('POST')
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="nome_applicazione">Nome Applicazione</label>
|
||||
<input type="text" name="nome_applicazione" id="nome_applicazione" class="form-control"
|
||||
value="{{ old('nome_applicazione', $appSettings->nome_applicazione ?? '') }}"
|
||||
placeholder="es. Glastree">
|
||||
<small class="text-muted">Nome mostrato nel titolo, sidebar e login</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="nome_organizzazione">Nome Organizzazione</label>
|
||||
<input type="text" name="nome_organizzazione" id="nome_organizzazione" class="form-control"
|
||||
value="{{ old('nome_organizzazione', $appSettings->nome_organizzazione ?? '') }}"
|
||||
placeholder="es. Parrocchia Santa Maria">
|
||||
<small class="text-muted">Nome organizzazione mostrato sotto il logo nel login</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="footer_text">Testo Footer</label>
|
||||
<input type="text" name="footer_text" id="footer_text" class="form-control"
|
||||
value="{{ old('footer_text', $appSettings->footer_text ?? '') }}"
|
||||
placeholder="es. © 2026 Parrocchia Santa Maria">
|
||||
<small class="text-muted">Lascia vuoto per usare il formato predefinito: © {anno} {nome_applicazione}</small>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="app_version">Versione Applicazione</label>
|
||||
<input type="text" name="app_version" id="app_version" class="form-control"
|
||||
value="{{ old('app_version', $appSettings->app_version ?? '') }}"
|
||||
placeholder="es. 1.0.0">
|
||||
<small class="text-muted">Versione mostrata nel footer</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label> </label>
|
||||
<div class="custom-control custom-switch mt-2">
|
||||
<input type="checkbox" class="custom-control-input" id="show_version" name="show_version" value="1"
|
||||
{{ ($appSettings->show_version ?? false) ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="show_version">Mostra versione nel footer</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="dashboard_welcome">Messaggio di Benvenuto (Dashboard)</label>
|
||||
<textarea name="dashboard_welcome" id="dashboard_welcome" class="form-control" rows="3"
|
||||
placeholder="Lascia vuoto per usare il messaggio predefinito">{{ old('dashboard_welcome', $appSettings->dashboard_welcome ?? '') }}</textarea>
|
||||
<small class="text-muted">Messaggio mostrato nella dashboard. Lascia vuoto per usare il testo predefinito.</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save mr-1"></i> Salva Impostazioni
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="logo">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Logo</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">Carica il logo dell'applicazione. Verrà visualizzato nella pagina di login e nella sidebar.</p>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">{{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('impostazioni.logo.upload') }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>Logo Grande (pagina login)</label>
|
||||
<div class="text-center p-3 border rounded bg-light mb-2" style="min-height: 120px;">
|
||||
@if($appSettings && $appSettings->getLogoPath())
|
||||
<img src="{{ $appSettings->getLogoPath() }}" alt="Logo" style="max-height: 100px; max-width: 100%;">
|
||||
@else
|
||||
<span class="text-muted">Nessun logo caricato</span>
|
||||
@endif
|
||||
</div>
|
||||
<input type="file" name="logo" class="form-control" accept="image/*">
|
||||
<small class="text-muted">PNG, JPG, GIF, SVG - Max 2MB</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>Logo Piccolo (sidebar)</label>
|
||||
<div class="text-center p-3 border rounded bg-light mb-2" style="min-height: 80px;">
|
||||
@if($appSettings && $appSettings->getLogoSmallPath())
|
||||
<img src="{{ $appSettings->getLogoSmallPath() }}" alt="Logo Small" style="max-height: 60px; max-width: 100%;">
|
||||
@else
|
||||
<span class="text-muted">Nessun logo caricato</span>
|
||||
@endif
|
||||
</div>
|
||||
<input type="file" name="logo_small" class="form-control" accept="image/*">
|
||||
<small class="text-muted">PNG, JPG, GIF, SVG - Max 512KB</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save mr-1"></i> Salva Logo
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane active" id="tipologie">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Tipologie Documenti</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">Gestisci le tipologie disponibili per i documenti caricati nel sistema.</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="#addTipologiaModal">
|
||||
<i class="fas fa-plus"></i> Nuova Tipologia
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-bordered table-striped" id="tipologie-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40px;">Ordine</th>
|
||||
<th>Nome</th>
|
||||
<th>Descrizione</th>
|
||||
<th style="width: 100px;">Stato</th>
|
||||
<th style="width: 150px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tipologie-sortable">
|
||||
@foreach($tipologie as $tipologia)
|
||||
<tr data-id="{{ $tipologia->id }}">
|
||||
<td class="text-center">
|
||||
<i class="fas fa-arrows-alt handle" style="cursor: grab;"></i>
|
||||
</td>
|
||||
<td>
|
||||
<span class="tipologia-nome">{{ $tipologia->nome }}</span>
|
||||
</td>
|
||||
<td>{{ $tipologia->descrizione ?? '-' }}</td>
|
||||
<td>
|
||||
@if($tipologia->attiva)
|
||||
<span class="badge badge-success">Attiva</span>
|
||||
@else
|
||||
<span class="badge badge-secondary">Disattivata</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-warning" onclick="editTipologia({{ $tipologia->id }}, '{{ addslashes($tipologia->nome) }}', '{{ addslashes($tipologia->descrizione ?? '') }}', {{ $tipologia->attiva ? 'true' : 'false' }})">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
@if($tipologia->documenti()->count() == 0)
|
||||
<form method="POST" action="{{ route('impostazioni.tipologie.destroy', $tipologia->id) }}" style="display: inline;">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questa tipologia?')">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<button type="button" class="btn btn-xs btn-danger" disabled title="Documenti associati: {{ $tipologia->documenti()->count() }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="ruoli">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Tipi di Ruolo</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">Gestisci i ruoli che possono essere assegnati agli individui nei gruppi.</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="#addRuoloModal">
|
||||
<i class="fas fa-plus"></i> Nuovo Ruolo
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-bordered table-striped" id="ruoli-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40px;">Ordine</th>
|
||||
<th>Nome</th>
|
||||
<th>Descrizione</th>
|
||||
<th style="width: 100px;">Stato</th>
|
||||
<th style="width: 150px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="ruoli-sortable">
|
||||
@foreach($ruoli as $ruolo)
|
||||
<tr data-id="{{ $ruolo->id }}">
|
||||
<td class="text-center">
|
||||
<i class="fas fa-arrows-alt handle" style="cursor: grab;"></i>
|
||||
</td>
|
||||
<td>
|
||||
<span class="ruolo-nome">{{ $ruolo->nome }}</span>
|
||||
</td>
|
||||
<td>{{ $ruolo->descrizione ?? '-' }}</td>
|
||||
<td>
|
||||
@if($ruolo->attiva)
|
||||
<span class="badge badge-success">Attivo</span>
|
||||
@else
|
||||
<span class="badge badge-secondary">Disattivato</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-warning" onclick="editRuolo({{ $ruolo->id }}, '{{ addslashes($ruolo->nome) }}', '{{ addslashes($ruolo->descrizione ?? '') }}', {{ $ruolo->attiva ? 'true' : 'false' }})">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
@php $membriCount = $ruolo->getMembriCount() @endphp
|
||||
@if($membriCount == 0)
|
||||
<form method="POST" action="{{ route('impostazioni.ruoli.destroy', $ruolo->id) }}" style="display: inline;">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo ruolo?')">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<button type="button" class="btn btn-xs btn-danger" disabled title="Membri associati: {{ $membriCount }}">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="addTipologiaModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form method="POST" action="{{ route('impostazioni.tipologie.store') }}">
|
||||
@csrf
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Nuova Tipologia</h4>
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="nome">Nome *</label>
|
||||
<input type="text" name="nome" id="nome" class="form-control" required maxlength="50" placeholder="es. certificato">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="descrizione">Descrizione</label>
|
||||
<input type="text" name="descrizione" id="descrizione" class="form-control" maxlength="255" placeholder="es. Certificato di formazione">
|
||||
</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="editTipologiaModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form method="POST" action="" id="editTipologiaForm">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<input type="hidden" name="id" id="edit_tipologia_id">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Modifica Tipologia</h4>
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="edit_tipologia_nome">Nome *</label>
|
||||
<input type="text" name="nome" id="edit_tipologia_nome" class="form-control" required maxlength="50">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="edit_tipologia_descrizione">Descrizione</label>
|
||||
<input type="text" name="descrizione" id="edit_tipologia_descrizione" class="form-control" maxlength="255">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="edit_tipologia_attiva" name="attiva" value="1">
|
||||
<label class="custom-control-label" for="edit_tipologia_attiva">Attiva</label>
|
||||
</div>
|
||||
</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="addRuoloModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form method="POST" action="{{ route('impostazioni.ruoli.store') }}">
|
||||
@csrf
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Nuovo Ruolo</h4>
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="ruolo_nome">Nome *</label>
|
||||
<input type="text" name="nome" id="ruolo_nome" class="form-control" required maxlength="50" placeholder="es. Catechista">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ruolo_descrizione">Descrizione</label>
|
||||
<input type="text" name="descrizione" id="ruolo_descrizione" class="form-control" maxlength="255" placeholder="es. Catechista del gruppo">
|
||||
</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="editRuoloModal">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form method="POST" action="" id="editRuoloForm">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<input type="hidden" name="id" id="edit_ruolo_id">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Modifica Ruolo</h4>
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="edit_ruolo_nome">Nome *</label>
|
||||
<input type="text" name="nome" id="edit_ruolo_nome" class="form-control" required maxlength="50">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="edit_ruolo_descrizione">Descrizione</label>
|
||||
<input type="text" name="descrizione" id="edit_ruolo_descrizione" class="form-control" maxlength="255">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-switch">
|
||||
<input type="checkbox" class="custom-control-input" id="edit_ruolo_attiva" name="attiva" value="1">
|
||||
<label class="custom-control-label" for="edit_ruolo_attiva">Attivo</label>
|
||||
</div>
|
||||
</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>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
||||
<script>
|
||||
function editTipologia(id, nome, descrizione, attiva) {
|
||||
$('#edit_tipologia_id').val(id);
|
||||
$('#edit_tipologia_nome').val(nome);
|
||||
$('#edit_tipologia_descrizione').val(descrizione);
|
||||
$('#edit_tipologia_attiva').prop('checked', attiva);
|
||||
$('#editTipologiaForm').attr('action', '/impostazioni/tipologie/' + id);
|
||||
$('#editTipologiaModal').modal('show');
|
||||
}
|
||||
|
||||
function editRuolo(id, nome, descrizione, attiva) {
|
||||
$('#edit_ruolo_id').val(id);
|
||||
$('#edit_ruolo_nome').val(nome);
|
||||
$('#edit_ruolo_descrizione').val(descrizione);
|
||||
$('#edit_ruolo_attiva').prop('checked', attiva);
|
||||
$('#editRuoloForm').attr('action', '/impostazioni/ruoli/' + id);
|
||||
$('#editRuoloModal').modal('show');
|
||||
}
|
||||
|
||||
var sortableTipologie = Sortable.create(document.getElementById('tipologie-sortable'), {
|
||||
handle: '.handle',
|
||||
animation: 150,
|
||||
onEnd: function(evt) {
|
||||
var order = [];
|
||||
$('#tipologie-sortable tr').each(function() {
|
||||
order.push($(this).data('id'));
|
||||
});
|
||||
|
||||
fetch('{{ route('impostazioni.tipologie.reorder') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ order: order })
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var sortableRuoli = Sortable.create(document.getElementById('ruoli-sortable'), {
|
||||
handle: '.handle',
|
||||
animation: 150,
|
||||
onEnd: function(evt) {
|
||||
var order = [];
|
||||
$('#ruoli-sortable tr').each(function() {
|
||||
order.push($(this).data('id'));
|
||||
});
|
||||
|
||||
fetch('{{ route('impostazioni.ruoli.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') {
|
||||
sortableRuoli.refresh();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,191 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Nuovo Individuo')
|
||||
@section('page_title', 'Nuovo Individuo')
|
||||
|
||||
@section('content')
|
||||
<form action="/individui" method="POST">
|
||||
@csrf
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-user mr-2"></i>Dati Anagrafici</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Codice ID</label>
|
||||
<input type="text" class="form-control" value="sarà generato automaticamente" disabled>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Cognome *</label>
|
||||
<input type="text" name="cognome" class="form-control @error('cognome') is-invalid @enderror" value="{{ old('cognome') }}" required>
|
||||
@error('cognome')
|
||||
<span class="invalid-feedback">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="nome" class="form-control @error('nome') is-invalid @enderror" value="{{ old('nome') }}" required>
|
||||
@error('nome')
|
||||
<span class="invalid-feedback">{{ $message }}</span>
|
||||
@enderror
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Data di nascita</label>
|
||||
<input type="date" name="data_nascita" class="form-control" value="{{ old('data_nascita') }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Genere</label>
|
||||
<select name="genere" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="M" {{ old('genere') === 'M' ? 'selected' : '' }}>Maschio</option>
|
||||
<option value="F" {{ old('genere') === 'F' ? 'selected' : '' }}>Femmina</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-map-marker-alt mr-2"></i>Residenza</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Indirizzo</label>
|
||||
<input type="text" name="indirizzo" class="form-control" value="{{ old('indirizzo') }}">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-4">
|
||||
<label>CAP</label>
|
||||
<input type="text" name="cap" class="form-control" value="{{ old('cap') }}">
|
||||
</div>
|
||||
<div class="form-group col-md-5">
|
||||
<label>Città</label>
|
||||
<input type="text" name="città" class="form-control" value="{{ old('città') }}">
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label>Provincia</label>
|
||||
<input type="text" name="sigla_provincia" class="form-control" maxlength="2" value="{{ old('sigla_provincia') }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-id-card mr-2"></i>Documento di identità</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Tipo documento</label>
|
||||
<select name="tipo_documento" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="carta_identita" {{ old('tipo_documento') === 'carta_identita' ? 'selected' : '' }}>Carta d'Identità</option>
|
||||
<option value="patente" {{ old('tipo_documento') === 'patente' ? 'selected' : '' }}>Patente</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Numero documento</label>
|
||||
<input type="text" name="numero_documento" class="form-control" value="{{ old('numero_documento') }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Scadenza documento</label>
|
||||
<input type="date" name="scadenza_documento" class="form-control" value="{{ old('scadenza_documento') }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-sticky-note mr-2"></i>Note</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group mb-0">
|
||||
<textarea name="note" class="form-control" rows="4">{{ old('note') }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="aggiungiRigaContatto()">
|
||||
<i class="fas fa-plus mr-1"></i> Aggiungi
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-bordered mb-0" id="contatti-table">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 20%;">Tipo</th>
|
||||
<th style="width: 30%;">Valore</th>
|
||||
<th style="width: 20%;">Etichetta</th>
|
||||
<th style="width: 10%;">Primario</th>
|
||||
<th style="width: 50px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="contatti-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="no-contatti-msg" class="text-center text-muted py-4">
|
||||
<p class="mb-0">Nessun contatto. Clicca su "Aggiungi" per aggiungerne uno.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva
|
||||
</button>
|
||||
<a href="/individui" class="btn btn-secondary">
|
||||
<i class="fas fa-times mr-1"></i> Annulla
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
let contattoIndex = 0;
|
||||
|
||||
function aggiungiRigaContatto() {
|
||||
const tbody = document.getElementById('contatti-tbody');
|
||||
const noMsg = document.getElementById('no-contatti-msg');
|
||||
if (noMsg) noMsg.style.display = 'none';
|
||||
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>
|
||||
<select name="contatti[${contattoIndex}][tipo]" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="telefono">Telefono</option>
|
||||
<option value="cellulare">Cellulare</option>
|
||||
<option value="email">Email</option>
|
||||
<option value="fax">Fax</option>
|
||||
<option value="web">Web</option>
|
||||
<option value="telegram">Telegram</option>
|
||||
<option value="whatsapp">WhatsApp</option>
|
||||
<option value="altro">Altro</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" name="contatti[${contattoIndex}][valore]" class="form-control" placeholder="Valore"></td>
|
||||
<td><input type="text" name="contatti[${contattoIndex}][etichetta]" class="form-control" placeholder="Etichetta"></td>
|
||||
<td class="text-center"><input type="checkbox" name="contatti[${contattoIndex}][is_primary]" value="1"></td>
|
||||
<td><button type="button" class="btn btn-danger btn-xs" onclick="rimuoviRiga(this)"><i class="fas fa-trash"></i></button></td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
contattoIndex++;
|
||||
}
|
||||
|
||||
function rimuoviRiga(btn) {
|
||||
const row = btn.closest('tr');
|
||||
row.remove();
|
||||
if (document.getElementById('contatti-tbody').children.length === 0) {
|
||||
const noMsg = document.getElementById('no-contatti-msg');
|
||||
if (noMsg) noMsg.style.display = 'block';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,718 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Modifica Individuo')
|
||||
@section('page_title', 'Modifica Individuo')
|
||||
|
||||
@section('content')
|
||||
<form action="/individui/{{ $individuo->id }}" method="POST" id="main-form">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<input type="hidden" name="individuo_id" value="{{ $individuo->id }}">
|
||||
<input type="hidden" name="_avatar_token" value="{{ csrf_token() }}">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-user mr-2"></i>Dati Anagrafici</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Codice ID</label>
|
||||
<input type="text" class="form-control" value="{{ $individuo->codice_id }}" disabled>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Cognome *</label>
|
||||
<input type="text" name="cognome" class="form-control" value="{{ $individuo->cognome }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="nome" class="form-control" value="{{ $individuo->nome }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Data di nascita</label>
|
||||
<input type="date" name="data_nascita" class="form-control" value="{{ $individuo->data_nascita?->format('Y-m-d') }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Genere</label>
|
||||
<select name="genere" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="M" {{ $individuo->genere === 'M' ? 'selected' : '' }}>Maschio</option>
|
||||
<option value="F" {{ $individuo->genere === 'F' ? 'selected' : '' }}>Femmina</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card card-secondary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-camera mr-2"></i>Avatar</h3>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
@if($individuo->avatar)
|
||||
<img src="/individui/{{ $individuo->id }}/avatar"
|
||||
alt="Avatar"
|
||||
class="img-thumbnail mb-2"
|
||||
style="max-width: 120px; max-height: 120px; object-fit: cover; border-radius: 50%;">
|
||||
<br>
|
||||
<button type="button" class="btn btn-danger btn-sm" onclick="deleteAvatar()">
|
||||
<i class="fas fa-trash mr-1"></i>Rimuovi
|
||||
</button>
|
||||
@else
|
||||
<div class="bg-secondary d-flex align-items-center justify-content-center mb-3"
|
||||
style="width: 120px; height: 120px; margin: 0 auto; border-radius: 50%;">
|
||||
<i class="fas fa-user fa-4x text-white"></i>
|
||||
</div>
|
||||
@endif
|
||||
<div class="custom-file mb-2">
|
||||
<input type="file" class="custom-file-input" id="avatar-input" name="avatar" accept="image/jpeg,image/png,image/gif">
|
||||
<label class="custom-file-label" for="avatar-input">Scegli immagine...</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="uploadAvatar()">
|
||||
<i class="fas fa-upload mr-1"></i>Carica Avatar
|
||||
</button>
|
||||
<div id="avatar-message" class="mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-map-marker-alt mr-2"></i>Residenza</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Indirizzo</label>
|
||||
<input type="text" name="indirizzo" class="form-control" value="{{ $individuo->indirizzo }}">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-4">
|
||||
<label>CAP</label>
|
||||
<input type="text" name="cap" class="form-control" value="{{ $individuo->cap }}">
|
||||
</div>
|
||||
<div class="form-group col-md-5">
|
||||
<label>Città</label>
|
||||
<input type="text" name="città" class="form-control" value="{{ $individuo->città }}">
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<label>Provincia</label>
|
||||
<input type="text" name="sigla_provincia" class="form-control" maxlength="2" value="{{ $individuo->sigla_provincia }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-id-card mr-2"></i>Documento di identità</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>Tipo documento</label>
|
||||
<select name="tipo_documento" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="carta_identita" {{ $individuo->tipo_documento === 'carta_identita' ? 'selected' : '' }}>Carta d'Identità</option>
|
||||
<option value="patente" {{ $individuo->tipo_documento === 'patente' ? 'selected' : '' }}>Patente</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Numero documento</label>
|
||||
<input type="text" name="numero_documento" class="form-control" value="{{ $individuo->numero_documento }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Scadenza documento</label>
|
||||
<input type="date" name="scadenza_documento" class="form-control" value="{{ $individuo->scadenza_documento?->format('Y-m-d') }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-sticky-note mr-2"></i>Note</h3></div>
|
||||
<div class="card-body">
|
||||
<div class="form-group mb-0">
|
||||
<textarea name="note" class="form-control" rows="4">{{ $individuo->note }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="aggiungiRigaContatto()">
|
||||
<i class="fas fa-plus mr-1"></i> Aggiungi
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<table class="table table-bordered mb-0" id="contatti-table">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 20%;">Tipo</th>
|
||||
<th style="width: 30%;">Valore</th>
|
||||
<th style="width: 20%;">Etichetta</th>
|
||||
<th style="width: 10%;">Primario</th>
|
||||
<th style="width: 50px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="contatti-tbody">
|
||||
@foreach($individuo->contatti as $contatto)
|
||||
<tr>
|
||||
<td>
|
||||
<select name="contatti[{{ $loop->index }}][tipo]" class="form-control">
|
||||
<option value="telefono" {{ $contatto->tipo === 'telefono' ? 'selected' : '' }}>Telefono</option>
|
||||
<option value="cellulare" {{ $contatto->tipo === 'cellulare' ? 'selected' : '' }}>Cellulare</option>
|
||||
<option value="email" {{ $contatto->tipo === 'email' ? 'selected' : '' }}>Email</option>
|
||||
<option value="fax" {{ $contatto->tipo === 'fax' ? 'selected' : '' }}>Fax</option>
|
||||
<option value="web" {{ $contatto->tipo === 'web' ? 'selected' : '' }}>Web</option>
|
||||
<option value="telegram" {{ $contatto->tipo === 'telegram' ? 'selected' : '' }}>Telegram</option>
|
||||
<option value="whatsapp" {{ $contatto->tipo === 'whatsapp' ? 'selected' : '' }}>WhatsApp</option>
|
||||
<option value="altro" {{ $contatto->tipo === 'altro' ? 'selected' : '' }}>Altro</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" name="contatti[{{ $loop->index }}][valore]" class="form-control" value="{{ $contatto->valore }}"></td>
|
||||
<td><input type="text" name="contatti[{{ $loop->index }}][etichetta]" class="form-control" value="{{ $contatto->etichetta }}"></td>
|
||||
<td class="text-center"><input type="checkbox" name="contatti[{{ $loop->index }}][is_primary]" value="1" {{ $contatto->is_primary ? 'checked' : '' }}></td>
|
||||
<td><button type="button" class="btn btn-danger btn-xs" onclick="this.closest('tr').remove()"><i class="fas fa-trash"></i></button></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@if($individuo->contatti->isEmpty())
|
||||
<div id="no-contatti-msg" class="text-center text-muted py-4">
|
||||
<p class="mb-0">Nessun contatto. Clicca su "Aggiungi" per aggiungerne uno.</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-folder mr-2"></i>Gruppi</h3>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="showGruppoForm()">
|
||||
<i class="fas fa-plus mr-1"></i> Aggiungi
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
|
||||
@if($individuo->gruppi->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Diocesi</th>
|
||||
<th style="width: 130px;">Ruolo</th>
|
||||
<th style="width: 110px;">Data Adesione</th>
|
||||
<th style="width: 80px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($individuo->gruppi as $gruppo)
|
||||
<tr id="gruppo-row-{{ $gruppo->id }}">
|
||||
<td>
|
||||
<i class="fas fa-folder text-warning mr-1"></i>
|
||||
<a href="/gruppi/{{ $gruppo->id }}">{{ $gruppo->nome }}</a>
|
||||
</td>
|
||||
<td>{{ $gruppo->diocesi?->nome ?: '-' }}</td>
|
||||
<td>
|
||||
@php $ruoliIndividuo = $individuo->getRuoliForGruppo($gruppo->id) @endphp
|
||||
@if($ruoliIndividuo->count() > 0)
|
||||
@foreach($ruoliIndividuo as $ruolo)
|
||||
<span class="badge badge-info mr-1">{{ $ruolo->nome }}</span>
|
||||
@endforeach
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $gruppo->pivot->data_adesione ? \Carbon\Carbon::parse($gruppo->pivot->data_adesione)->format('d/m/Y') : '-' }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-warning" onclick="editGruppoInline({{ $gruppo->id }})" title="Modifica">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<form action="/individui/{{ $individuo->id }}/gruppi/{{ $gruppo->id }}" method="POST" class="mb-0">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Rimuovere da questo gruppo?')" title="Rimuovi">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="gruppo-edit-{{ $gruppo->id }}" style="display:none;">
|
||||
<td colspan="5">
|
||||
<form action="/individui/{{ $individuo->id }}/gruppi/{{ $gruppo->id }}" method="POST" class="mb-0">
|
||||
@csrf @method('PUT')
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control form-control-sm" value="{{ $gruppo->nome }}" disabled>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="ruolo_ids[]" class="form-control form-control-sm" multiple size="3">
|
||||
@php $selectedRuoliIds = $individuo->getRuoloIdsForGruppo($gruppo->id) @endphp
|
||||
@foreach(\App\Models\Ruolo::attive() as $ruolo)
|
||||
<option value="{{ $ruolo->id }}" {{ in_array($ruolo->id, $selectedRuoliIds) ? 'selected' : '' }}>{{ $ruolo->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="date" name="data_adesione" class="form-control form-control-sm" value="{{ $gruppo->pivot->data_adesione }}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-xs btn-success"><i class="fas fa-check mr-1"></i>Salva</button>
|
||||
<button type="button" class="btn btn-xs btn-secondary" onclick="cancelEditGruppo({{ $gruppo->id }})"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-users fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun gruppo associato</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-file mr-2"></i>Documenti</h3>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="showDocumentoForm()">
|
||||
<i class="fas fa-plus mr-1"></i> Carica
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="documento-add-form" style="display:none;" class="p-3 bg-light border-bottom">
|
||||
<form action="/documenti" method="POST" enctype="multipart/form-data" class="mb-0">
|
||||
@csrf
|
||||
<input type="hidden" name="_redirect" value="{{ url()->current() }}">
|
||||
<input type="hidden" name="visibilita" value="individuo">
|
||||
<input type="hidden" name="visibilita_target_id" value="{{ $individuo->id }}">
|
||||
<input type="hidden" name="visibilita_target_type" value="App\Models\Individuo">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<input type="text" name="nome_file" class="form-control form-control-sm" placeholder="Nome documento" required>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="tipologia" class="form-control form-control-sm" required>
|
||||
<option value="">Tipologia...</option>
|
||||
<option value="documento">Documento</option>
|
||||
<option value="avatar">Avatar</option>
|
||||
<option value="galleria">Galleria</option>
|
||||
<option value="statuto">Statuto</option>
|
||||
<option value="altro">Altro</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<input type="file" name="file" class="form-control form-control-sm" required>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-xs btn-success"><i class="fas fa-upload mr-1"></i>Carica</button>
|
||||
<button type="button" class="btn btn-xs btn-secondary" onclick="hideDocumentoForm()"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@if($individuo->documenti->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th style="width: 120px;">Tipologia</th>
|
||||
<th style="width: 80px;">Dimensione</th>
|
||||
<th style="width: 130px;">Data Upload</th>
|
||||
<th style="width: 80px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($individuo->documenti as $documento)
|
||||
<tr>
|
||||
<td>
|
||||
<a href="#" onclick="event.preventDefault(); previewDocumento({{ $documento->id }}, '{{ $documento->mime_type }}');">
|
||||
<i class="fas fa-file text-secondary mr-1"></i>
|
||||
{{ $documento->nome_file }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ ucfirst(str_replace('_', ' ', $documento->tipologia)) }}</td>
|
||||
<td>{{ number_format($documento->dimensione / 1024, 1) }} KB</td>
|
||||
<td>{{ $documento->created_at->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
@if($documento->file_path)
|
||||
<button type="button" class="btn btn-xs btn-primary" onclick="previewDocumento({{ $documento->id }}, '{{ $documento->mime_type }}')" title="Anteprima">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
@endif
|
||||
<form action="/documenti/{{ $documento->id }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo documento?')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-file fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun documento</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 mb-4">
|
||||
<button type="button" class="btn btn-success btn-lg" id="save-all-btn" onclick="submitMainForm()">
|
||||
<i class="fas fa-save mr-2"></i> Salva Tutte le Modifiche
|
||||
</button>
|
||||
<a href="/individui/{{ $individuo->id }}" class="btn btn-secondary btn-lg ml-2">
|
||||
<i class="fas fa-times mr-1"></i> Annulla
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="card mt-3" id="gruppo-add-card" style="display:none; background-color: #fff3cd; border-color: #ffc107;">
|
||||
<div class="card-header bg-warning">
|
||||
<h3 class="card-title"><i class="fas fa-folder-plus mr-2"></i>Aggiungi a Gruppo</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<select id="new-gruppo-id" class="form-control form-control-sm">
|
||||
<option value="">Seleziona gruppo...</option>
|
||||
@foreach(\App\Models\Gruppo::orderBy('nome')->get() as $g)
|
||||
@if(!$individuo->gruppi->contains($g->id))
|
||||
<option value="{{ $g->id }}">{{ $g->full_path }}</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select id="new-gruppo-ruolo" class="form-control form-control-sm" multiple size="3">
|
||||
@foreach(\App\Models\Ruolo::attive() as $ruolo)
|
||||
<option value="{{ $ruolo->id }}">{{ $ruolo->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Ctrl+click per selezionare più ruoli</small>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="date" id="new-gruppo-data" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="button" class="btn btn-success btn-sm" onclick="submitGruppoForm()">
|
||||
<i class="fas fa-check mr-1"></i> Associa
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="hideGruppoForm()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="previewModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document" style="max-width: 90vw;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-file mr-2"></i><span id="previewModalTitle">Anteprima</span></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Chiudi">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-0" style="min-height: 400px;">
|
||||
<iframe id="previewFrame" src="" style="width: 100%; height: 70vh; border: none;"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a id="previewDownloadBtn" href="#" class="btn btn-primary" download>
|
||||
<i class="fas fa-download mr-1"></i> Scarica
|
||||
</a>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Chiudi</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('All form fields:');
|
||||
var form = document.getElementById('main-form');
|
||||
var fields = form.querySelectorAll('input, textarea, select');
|
||||
fields.forEach(function(f) {
|
||||
console.log(f.name + ' [' + f.type + ']: value="' + f.value.substring(0,30) + '" disabled=' + f.disabled);
|
||||
});
|
||||
|
||||
// Debug note field specifically
|
||||
var noteField = form.querySelector('[name="note"]');
|
||||
console.log('Note field found:', !!noteField);
|
||||
if (noteField) {
|
||||
console.log('Note value:', noteField.value);
|
||||
}
|
||||
|
||||
var saveBtn = document.getElementById('save-btn');
|
||||
if (saveBtn) {
|
||||
saveBtn.addEventListener('click', function() {
|
||||
console.log('=== SAVE CLICKED ===');
|
||||
var note = form.querySelector('[name="note"]');
|
||||
console.log('Note at save time:', note ? note.value : 'NOT FOUND');
|
||||
});
|
||||
}
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
console.log('=== FORM SUBMIT ===');
|
||||
var note = form.querySelector('[name="note"]');
|
||||
console.log('Note at submit:', note ? note.value : 'NOT FOUND');
|
||||
|
||||
var contattiFields = form.querySelectorAll('[name^="contatti"]');
|
||||
console.log('Contatti fields count:', contattiFields.length);
|
||||
contattiFields.forEach(function(f, i) {
|
||||
console.log('Contatto field ' + i + ':', f.name, '=', f.value);
|
||||
});
|
||||
|
||||
var formData = new FormData(form);
|
||||
console.log('FormData contatti:', formData.getAll('contatti'));
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
function submitMainForm() {
|
||||
var form = document.getElementById('main-form');
|
||||
console.log('=== DEBUG SUBMIT ===');
|
||||
|
||||
var contattiFields = form.querySelectorAll('[name^="contatti"]');
|
||||
console.log('Contatti fields found:', contattiFields.length);
|
||||
contattiFields.forEach(function(f, i) {
|
||||
console.log('Field ' + i + ': name=' + f.name + ', value=' + f.value);
|
||||
});
|
||||
|
||||
form.action = '/individui/{{ $individuo->id }}';
|
||||
var methodInput = form.querySelector('input[name="_method"]');
|
||||
if (!methodInput) {
|
||||
var m = document.createElement('input');
|
||||
m.type = 'hidden';
|
||||
m.name = '_method';
|
||||
m.value = 'PUT';
|
||||
form.appendChild(m);
|
||||
}
|
||||
var csrfInput = form.querySelector('input[name="_token"]');
|
||||
if (!csrfInput) {
|
||||
var c = document.createElement('input');
|
||||
c.type = 'hidden';
|
||||
c.name = '_token';
|
||||
c.value = '{{ csrf_token() }}';
|
||||
form.appendChild(c);
|
||||
}
|
||||
|
||||
var formData = new FormData(form);
|
||||
console.log('FormData contatti keys:');
|
||||
for (var pair of formData.entries()) {
|
||||
if (pair[0].startsWith('contatti')) {
|
||||
console.log(' ' + pair[0] + ' = ' + pair[1]);
|
||||
}
|
||||
}
|
||||
|
||||
form.submit();
|
||||
}
|
||||
|
||||
let contattoIndex = {{ $individuo->contatti->count() }};
|
||||
|
||||
function aggiungiRigaContatto() {
|
||||
const tbody = document.getElementById('contatti-tbody');
|
||||
const noMsg = document.getElementById('no-contatti-msg');
|
||||
if (noMsg) noMsg.style.display = 'none';
|
||||
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>
|
||||
<select name="contatti[${contattoIndex}][tipo]" class="form-control">
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="telefono">Telefono</option>
|
||||
<option value="cellulare">Cellulare</option>
|
||||
<option value="email">Email</option>
|
||||
<option value="fax">Fax</option>
|
||||
<option value="web">Web</option>
|
||||
<option value="telegram">Telegram</option>
|
||||
<option value="whatsapp">WhatsApp</option>
|
||||
<option value="altro">Altro</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" name="contatti[${contattoIndex}][valore]" class="form-control" placeholder="Valore"></td>
|
||||
<td><input type="text" name="contatti[${contattoIndex}][etichetta]" class="form-control" placeholder="Etichetta"></td>
|
||||
<td class="text-center"><input type="checkbox" name="contatti[${contattoIndex}][is_primary]" value="1"></td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-success btn-xs" title="Salva" onclick="submitMainForm()">
|
||||
<i class="fas fa-save"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger btn-xs" title="Elimina" onclick="this.closest('tr').remove()">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
contattoIndex++;
|
||||
}
|
||||
|
||||
function showGruppoForm() {
|
||||
var card = document.getElementById('gruppo-add-card');
|
||||
if (card) {
|
||||
card.style.display = 'block';
|
||||
card.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
}
|
||||
|
||||
function hideGruppoForm() {
|
||||
var card = document.getElementById('gruppo-add-card');
|
||||
if (card) {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function submitGruppoForm() {
|
||||
var gruppoId = document.getElementById('new-gruppo-id').value;
|
||||
var ruoloSelect = document.getElementById('new-gruppo-ruolo');
|
||||
var dataAdesione = document.getElementById('new-gruppo-data').value;
|
||||
|
||||
if (!gruppoId) {
|
||||
alert('Seleziona un gruppo');
|
||||
return;
|
||||
}
|
||||
|
||||
var formData = new URLSearchParams();
|
||||
formData.append('gruppo_id', gruppoId);
|
||||
|
||||
var selectedRuoli = Array.from(ruoloSelect.selectedOptions).map(opt => opt.value).filter(v => v);
|
||||
selectedRuoli.forEach(function(ruoloId) {
|
||||
formData.append('ruolo_ids[]', ruoloId);
|
||||
});
|
||||
|
||||
formData.append('data_adesione', dataAdesione);
|
||||
|
||||
fetch('/individui/{{ $individuo->id }}/gruppi', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.text().then(text => { throw new Error(text); });
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
hideGruppoForm();
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(data.error || 'Errore');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Errore: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function editGruppoInline(id) {
|
||||
document.getElementById('gruppo-row-' + id).style.display = 'none';
|
||||
document.getElementById('gruppo-edit-' + id).style.display = 'table-row';
|
||||
}
|
||||
|
||||
function cancelEditGruppo(id) {
|
||||
document.getElementById('gruppo-row-' + id).style.display = 'table-row';
|
||||
document.getElementById('gruppo-edit-' + id).style.display = 'none';
|
||||
}
|
||||
|
||||
function showDocumentoForm() {
|
||||
document.getElementById('documento-add-form').style.display = 'block';
|
||||
}
|
||||
|
||||
function hideDocumentoForm() {
|
||||
document.getElementById('documento-add-form').style.display = 'none';
|
||||
}
|
||||
|
||||
function previewDocumento(id, mimeType) {
|
||||
var previewUrl = '/documenti/' + id + '/preview';
|
||||
var downloadUrl = '/documenti/' + id + '/download';
|
||||
|
||||
document.getElementById('previewFrame').src = previewUrl;
|
||||
document.getElementById('previewDownloadBtn').href = downloadUrl;
|
||||
document.getElementById('previewModalTitle').textContent = 'Anteprima';
|
||||
|
||||
if (mimeType && mimeType.startsWith('image/')) {
|
||||
document.getElementById('previewDownloadBtn').style.display = 'inline-block';
|
||||
} else {
|
||||
document.getElementById('previewDownloadBtn').style.display = 'none';
|
||||
}
|
||||
|
||||
$('#previewModal').modal('show');
|
||||
}
|
||||
|
||||
document.getElementById('avatar-input').addEventListener('change', function(e) {
|
||||
var fileName = e.target.files[0]?.name || 'Scegli file...';
|
||||
e.target.nextElementSibling.textContent = fileName;
|
||||
});
|
||||
|
||||
function uploadAvatar() {
|
||||
var input = document.getElementById('avatar-input');
|
||||
if (!input.files[0]) {
|
||||
document.getElementById('avatar-message').innerHTML = '<span class="text-danger">Seleziona un\'immagine</span>';
|
||||
return;
|
||||
}
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('avatar', input.files[0]);
|
||||
formData.append('_token', '{{ csrf_token() }}');
|
||||
|
||||
document.getElementById('avatar-message').innerHTML = '<span class="text-info"><i class="fas fa-spinner fa-spin"></i> Caricamento...</span>';
|
||||
|
||||
fetch('/individui/{{ $individuo->id }}/avatar', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
document.getElementById('avatar-message').innerHTML = '<span class="text-success">Avatar caricato!</span>';
|
||||
setTimeout(function() { window.location.reload(); }, 500);
|
||||
} else {
|
||||
document.getElementById('avatar-message').innerHTML = '<span class="text-danger">' + (data.message || 'Errore') + '</span>';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
document.getElementById('avatar-message').innerHTML = '<span class="text-danger">Errore: ' + error + '</span>';
|
||||
});
|
||||
}
|
||||
|
||||
function deleteAvatar() {
|
||||
if (!confirm('Rimuovere l\'avatar?')) return;
|
||||
|
||||
fetch('/individui/{{ $individuo->id }}/avatar', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(data.message || 'Errore');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Errore: ' + error);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,78 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Importa Individui')
|
||||
@section('page_title', 'Importa Individui da CSV')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-upload mr-2"></i>Carica file CSV</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('individui.import.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<div class="form-group">
|
||||
<label for="file">Seleziona file CSV *</label>
|
||||
<input type="file" name="file" id="file" class="form-control" accept=".csv,.txt" required>
|
||||
<small class="form-text text-muted">
|
||||
Il file deve essere in formato CSV con separatore virgola.
|
||||
</small>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-upload mr-1"></i> Importa
|
||||
</button>
|
||||
<a href="{{ route('individui.index') }}" class="btn btn-secondary ml-2">
|
||||
Annulla
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-info">
|
||||
<h3 class="card-title text-white"><i class="fas fa-download mr-2"></i>Template</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>Scarica il template CSV per l'importazione:</p>
|
||||
<a href="{{ route('individui.template') }}" class="btn btn-success btn-block">
|
||||
<i class="fas fa-file-csv mr-1"></i> Scarica Template
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-info-circle mr-2"></i>Formato CSV</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h6>Campi obbligatori:</h6>
|
||||
<ul>
|
||||
<li><code>cognome</code></li>
|
||||
<li><code>nome</code></li>
|
||||
</ul>
|
||||
<h6>Campi opzionali:</h6>
|
||||
<ul>
|
||||
<li><code>data_nascita</code> (formato YYYY-MM-DD)</li>
|
||||
<li><code>indirizzo</code></li>
|
||||
<li><code>cap</code></li>
|
||||
<li><code>città</code></li>
|
||||
<li><code>sigla_provincia</code> (2 lettere)</li>
|
||||
<li><code>genere</code> (M o F)</li>
|
||||
<li><code>tipo_documento</code> (carta_identita, patente)</li>
|
||||
<li><code>numero_documento</code></li>
|
||||
<li><code>scadenza_documento</code> (YYYY-MM-DD)</li>
|
||||
<li><code>note</code></li>
|
||||
</ul>
|
||||
<h6>Contatti (fino a 2):</h6>
|
||||
<ul>
|
||||
<li><code>contatto_1_tipo</code> (email, telefono, cellulare)</li>
|
||||
<li><code>contatto_1_valore</code></li>
|
||||
<li><code>contatto_1_etichetta</code> (personale, lavoro)</li>
|
||||
<li><code>contatto_2_*</code> (secondo contatto)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,921 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Individui')
|
||||
@section('page_title', 'Elenco Individui')
|
||||
|
||||
@php
|
||||
$columnLabels = [
|
||||
'codice' => 'Codice',
|
||||
'cognome' => 'Cognome',
|
||||
'nome' => 'Nome',
|
||||
'email' => 'Email',
|
||||
'telefono' => 'Telefono',
|
||||
];
|
||||
$vistaDefaultJson = $vista ? $vista->toJson() : 'null';
|
||||
$allColumnsJson = json_encode($allColumns);
|
||||
$visibleColumnsJson = json_encode($visibleColumns);
|
||||
$canWriteIndividui = Auth::user()->canManage('individui');
|
||||
$canDeleteIndividui = Auth::user()->canDelete('individui');
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
<div class="card">
|
||||
<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-info btn-sm mr-1" onclick="toggleSearchPanel()">
|
||||
<i class="fas fa-search mr-1"></i> Ricerca/Filtri
|
||||
</button>
|
||||
<div class="btn-group mr-1">
|
||||
<button type="button" class="btn btn-secondary btn-sm dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fas fa-cog mr-1"></i> Azioni
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="#" onclick="exportCSV(); return false;">
|
||||
<i class="fas fa-file-csv mr-2"></i>Esporta Tutti CSV
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" onclick="exportSelectedCSV(); return false;">
|
||||
<i class="fas fa-file-csv mr-2"></i>Esporta Selezionati CSV
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item" href="#" onclick="printSelected(); return false;">
|
||||
<i class="fas fa-print mr-2"></i>Stampa Selezionati
|
||||
</a>
|
||||
<a class="dropdown-item" href="#" onclick="emailSelected(); return false;">
|
||||
<i class="fas fa-envelope mr-2"></i>Email Selezionati
|
||||
</a>
|
||||
@if($canWriteIndividui)
|
||||
<a class="dropdown-item" href="#" onclick="showCreateMailingListModal(); return false;">
|
||||
<i class="fas fa-list mr-2"></i>Crea Mailing List
|
||||
</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" 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
|
||||
</a>
|
||||
<a href="/individui/create" class="btn btn-success btn-sm ml-2">
|
||||
<i class="fas fa-plus mr-1"></i> Nuovo
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-panel" class="p-3 bg-light border-bottom" style="display: none;">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="form-group mb-2">
|
||||
<label class="small font-weight-bold">Cerca</label>
|
||||
<input type="text" id="global-search" class="form-control form-control-sm" placeholder="Cerca in tutte le colonne..." oninput="applyGlobalSearch(this.value)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-group mb-2">
|
||||
<label class="small font-weight-bold">Colonna</label>
|
||||
<select id="filter-column" class="form-control form-control-sm">
|
||||
<option value="">Tutte le colonne</option>
|
||||
@foreach($allColumns as $col)
|
||||
<option value="{{ $col['key'] }}">{{ $col['label'] }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group mb-2">
|
||||
<label class="small font-weight-bold">Operatore</label>
|
||||
<select id="filter-operator" class="form-control form-control-sm">
|
||||
<option value="contains">Contiene</option>
|
||||
<option value="equals">Uguale a</option>
|
||||
<option value="starts">Inizia con</option>
|
||||
<option value="ends">Finisce con</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-group mb-2">
|
||||
<label class="small font-weight-bold">Valore</label>
|
||||
<input type="text" id="filter-value" class="form-control form-control-sm" placeholder="Valore filtro">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<label class="small"> </label>
|
||||
<div>
|
||||
<button type="button" class="btn btn-xs btn-primary" onclick="applyColumnFilter()">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-xs btn-secondary" onclick="clearFilters()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<small class="text-muted">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
Ordina cliccando sulle intestazioni delle colonne
|
||||
</small>
|
||||
</div>
|
||||
</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;">
|
||||
<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('codice', $visibleColumns))
|
||||
<th style="width: 100px;" data-sortable="true" data-column="codice" onclick="sortTable('codice')">Codice <i class="fas fa-sort float-right"></i></th>
|
||||
@endif
|
||||
@if(in_array('cognome', $visibleColumns))
|
||||
<th data-sortable="true" data-column="cognome" onclick="sortTable('cognome')">Cognome <i class="fas fa-sort float-right"></i></th>
|
||||
@endif
|
||||
@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('email', $visibleColumns))
|
||||
<th data-sortable="true" data-column="email" onclick="sortTable('email')">Email <i class="fas fa-sort float-right"></i></th>
|
||||
@endif
|
||||
@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
|
||||
<th style="width: 120px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="table-body">
|
||||
@foreach($individui as $ind)
|
||||
<tr data-id="{{ $ind->id }}">
|
||||
<td>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input row-checkbox" id="select-{{ $ind->id }}" value="{{ $ind->id }}">
|
||||
<label class="custom-control-label" for="select-{{ $ind->id }}"></label>
|
||||
</div>
|
||||
</td>
|
||||
@if(in_array('codice', $visibleColumns))
|
||||
<td><span class="badge badge-secondary">{{ $ind->codice_id }}</span></td>
|
||||
@endif
|
||||
@if(in_array('cognome', $visibleColumns))
|
||||
<td class="font-weight-bold">
|
||||
<a href="{{ url('/individui/' . $ind->id . '/edit') }}">{{ $ind->cognome }}</a>
|
||||
</td>
|
||||
@endif
|
||||
@if(in_array('nome', $visibleColumns))
|
||||
<td>
|
||||
<a href="{{ url('/individui/' . $ind->id . '/edit') }}">{{ $ind->nome }}</a>
|
||||
</td>
|
||||
@endif
|
||||
@if(in_array('email', $visibleColumns))
|
||||
<td>{{ $ind->getEmailPrimariaAttribute() ?: '-' }}</td>
|
||||
@endif
|
||||
@if(in_array('telefono', $visibleColumns))
|
||||
<td>{{ $ind->getTelefonoPrimarioAttribute() ?: '-' }}</td>
|
||||
@endif
|
||||
<td>
|
||||
@if($canWriteIndividui)
|
||||
<a href="{{ url('/individui/' . $ind->id . '/edit') }}" class="btn btn-xs btn-warning" title="Modifica">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
@endif
|
||||
@if($canDeleteIndividui)
|
||||
<button type="button" class="btn btn-xs btn-danger" onclick="showDeleteModal({{ $ind->id }}, '{{ $ind->cognome }} {{ $ind->nome }}')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
@endif
|
||||
<button type="button" class="btn btn-xs btn-secondary" onclick="printIndividuo({{ $ind->id }})" title="Stampa">
|
||||
<i class="fas fa-print"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-xs btn-primary" onclick="exportIndividuoCSV({{ $ind->id }})" title="Esporta CSV">
|
||||
<i class="fas fa-download"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-xs btn-info" onclick="sendEmail({{ $ind->id }})" title="Invia Email">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-xs btn-dark" onclick="generateReport({{ $ind->id }})" title="Genera Report">
|
||||
<i class="fas fa-chart-bar"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="fas fa-users fa-3x mb-3"></i>
|
||||
<p>Nessun individuo presente</p>
|
||||
@if($canWriteIndividui)
|
||||
<a href="/individui/create" class="btn btn-success">Crea il primo individuo</a>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@if($individui->count() > 0)
|
||||
<div class="card-footer">
|
||||
{{ $individui->links() }}
|
||||
</div>
|
||||
@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>×</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>×</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)
|
||||
<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 l'individuo: <strong id="delete-individuo-name"></strong></p>
|
||||
<p class="text-muted">Seleziona gli elementi collegati da eliminare:</p>
|
||||
<div class="ml-3">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="delete-contatti" checked>
|
||||
<label class="custom-control-label" for="delete-contatti">Contatti (<span id="count-contatti">0</span>)</label>
|
||||
</div>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="delete-gruppi">
|
||||
<label class="custom-control-label" for="delete-gruppi">Gruppi (<span id="count-gruppi">0</span>) - solo scollegamento</label>
|
||||
</div>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="delete-documenti" checked>
|
||||
<label class="custom-control-label" for="delete-documenti">Documenti (<span id="count-documenti">0</span>)</label>
|
||||
</div>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="delete-eventi">
|
||||
<label class="custom-control-label" for="delete-eventi">Eventi (<span id="count-eventi">0</span>) - solo scollegamento</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-warning mt-3 mb-0">
|
||||
<small><i class="fas fa-info-circle mr-1"></i>Se non selezionato, l'elemento verrà scollegato ma non eliminato.</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" onclick="confirmDelete()">Elimina</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="createMailingListModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-info">
|
||||
<h5 class="modal-title text-white"><i class="fas fa-list mr-2"></i>Crea Mailing List</h5>
|
||||
<button type="button" class="close text-white" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label>Nome Lista *</label>
|
||||
<input type="text" id="ml-nome" class="form-control" placeholder="Es. Newsletter Maggio 2026">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione</label>
|
||||
<textarea id="ml-descrizione" class="form-control" rows="2" placeholder="Descrizione opzionale"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Contatti inclusi (<span id="ml-count">0</span>)</label>
|
||||
<div class="table-responsive" style="max-height: 300px; overflow-y: auto;">
|
||||
<table class="table table-bordered table-sm" id="ml-contatti-table">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 40px;">
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="ml-select-all" onchange="toggleMlSelectAll(this)">
|
||||
<label class="custom-control-label" for="ml-select-all"></label>
|
||||
</div>
|
||||
</th>
|
||||
<th>Codice</th>
|
||||
<th>Cognome</th>
|
||||
<th>Nome</th>
|
||||
<th>Email</th>
|
||||
<th style="width: 50px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="ml-contatti-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
</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-success" onclick="createMailingList()">
|
||||
<i class="fas fa-save mr-1"></i> Salva Lista
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
let currentSort = { column: null, direction: 'asc' };
|
||||
let columnVisibility = {!! json_encode(array_column($allColumns, 'key')) !!};
|
||||
|
||||
function toggleSearchPanel() {
|
||||
const panel = document.getElementById('search-panel');
|
||||
panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
|
||||
}
|
||||
|
||||
function applyGlobalSearch(value) {
|
||||
const rows = document.querySelectorAll('#table-body tr');
|
||||
const searchTerm = value.toLowerCase();
|
||||
rows.forEach(row => {
|
||||
const text = row.textContent.toLowerCase();
|
||||
row.style.display = text.includes(searchTerm) ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function applyColumnFilter() {
|
||||
const col = document.getElementById('filter-column').value;
|
||||
const op = document.getElementById('filter-operator').value;
|
||||
const val = document.getElementById('filter-value').value.toLowerCase();
|
||||
|
||||
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];
|
||||
|
||||
rows.forEach(row => {
|
||||
const cells = row.querySelectorAll('td');
|
||||
const cellText = cells[colIndex]?.textContent.toLowerCase() || '';
|
||||
let matches = false;
|
||||
|
||||
switch(op) {
|
||||
case 'contains': matches = cellText.includes(val); break;
|
||||
case 'equals': matches = cellText === val; break;
|
||||
case 'starts': matches = cellText.startsWith(val); break;
|
||||
case 'ends': matches = cellText.endsWith(val); break;
|
||||
}
|
||||
|
||||
row.style.display = matches ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function clearFilters() {
|
||||
document.getElementById('global-search').value = '';
|
||||
document.getElementById('filter-column').value = '';
|
||||
document.getElementById('filter-value').value = '';
|
||||
document.querySelectorAll('#table-body tr').forEach(row => row.style.display = '');
|
||||
}
|
||||
|
||||
function sortTable(column) {
|
||||
if (currentSort.column === column) {
|
||||
currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
currentSort = { column: column, direction: 'asc' };
|
||||
}
|
||||
|
||||
const colIndex = { codice: 0, cognome: 1, nome: 2, email: 3, telefono: 4, azioni: 5 }[column];
|
||||
const tbody = document.getElementById('table-body');
|
||||
const rows = Array.from(tbody.querySelectorAll('tr'));
|
||||
|
||||
rows.sort((a, b) => {
|
||||
const aVal = a.querySelectorAll('td')[colIndex]?.textContent.trim() || '';
|
||||
const bVal = b.querySelectorAll('td')[colIndex]?.textContent.trim() || '';
|
||||
const cmp = aVal.localeCompare(bVal, undefined, { numeric: true });
|
||||
return currentSort.direction === 'asc' ? cmp : -cmp;
|
||||
});
|
||||
|
||||
rows.forEach(row => tbody.appendChild(row));
|
||||
|
||||
document.querySelectorAll('th[data-sortable]').forEach(th => {
|
||||
const icon = th.querySelector('i');
|
||||
if (th.dataset.column === column) {
|
||||
icon.className = currentSort.direction === 'asc' ? 'fas fa-sort-up float-right' : 'fas fa-sort-down float-right';
|
||||
} else {
|
||||
icon.className = 'fas fa-sort float-right';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleColumn(column, visible) {
|
||||
const colIndex = { codice: 1, cognome: 2, nome: 3, email: 4, telefono: 5, azioni: 6 }[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');
|
||||
checkboxes.forEach(cb => {
|
||||
cb.checked = source.checked;
|
||||
});
|
||||
}
|
||||
|
||||
function exportCSV() {
|
||||
window.location.href = '/individui/export';
|
||||
}
|
||||
|
||||
function exportSelectedCSV() {
|
||||
const selectedIds = getSelectedIds();
|
||||
if (selectedIds.length === 0) {
|
||||
alert('Seleziona almeno un individuo');
|
||||
return;
|
||||
}
|
||||
|
||||
const params = new URLSearchParams();
|
||||
selectedIds.forEach(function(id) {
|
||||
params.append('ids[]', id);
|
||||
});
|
||||
|
||||
window.location.href = '/individui/export?' + params.toString();
|
||||
}
|
||||
|
||||
let deleteIndividuoId = null;
|
||||
|
||||
async function showDeleteModal(id, nome) {
|
||||
deleteIndividuoId = id;
|
||||
document.getElementById('delete-individuo-name').textContent = nome;
|
||||
|
||||
document.getElementById('count-contatti').textContent = '...';
|
||||
document.getElementById('count-gruppi').textContent = '...';
|
||||
document.getElementById('count-documenti').textContent = '...';
|
||||
document.getElementById('count-eventi').textContent = '...';
|
||||
|
||||
try {
|
||||
const response = await fetch(`/individui/${id}/collegati`);
|
||||
const data = await response.json();
|
||||
document.getElementById('count-contatti').textContent = data.contatti;
|
||||
document.getElementById('count-gruppi').textContent = data.gruppi;
|
||||
document.getElementById('count-documenti').textContent = data.documenti;
|
||||
document.getElementById('count-eventi').textContent = data.eventi;
|
||||
} catch(e) {
|
||||
console.error('Errore recupero dati:', e);
|
||||
}
|
||||
|
||||
$('#deleteModal').modal('show');
|
||||
}
|
||||
|
||||
function confirmDelete() {
|
||||
const eliminaContatti = document.getElementById('delete-contatti').checked;
|
||||
const eliminaDocumenti = document.getElementById('delete-documenti').checked;
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = `/individui/${deleteIndividuoId}/elimina`;
|
||||
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '{{ csrf_token() }}';
|
||||
form.innerHTML = `
|
||||
<input type="hidden" name="_token" value="${csrfToken}">
|
||||
<input type="hidden" name="elimina_contatti" value="${eliminaContatti}">
|
||||
<input type="hidden" name="elimina_documenti" value="${eliminaDocumenti}">
|
||||
`;
|
||||
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
|
||||
function printIndividuo(id) {
|
||||
window.open('/individui/' + id + '/report', '_blank');
|
||||
}
|
||||
|
||||
function exportIndividuoCSV(id) {
|
||||
const row = document.querySelector(`tr[data-id="${id}"]`);
|
||||
if (!row) return;
|
||||
|
||||
const cells = Array.from(row.querySelectorAll('td'));
|
||||
const data = {
|
||||
codice: cells[1].textContent.trim(),
|
||||
cognome: cells[2].textContent.trim(),
|
||||
nome: cells[3].textContent.trim(),
|
||||
email: cells[4].textContent.trim(),
|
||||
telefono: cells[5].textContent.trim()
|
||||
};
|
||||
|
||||
const csv = `Codice,Cognome,Nome,Email,Telefono\n"${data.codice}","${data.cognome}","${data.nome}","${data.email}","${data.telefono}"`;
|
||||
const blob = new Blob([csv], { type: 'text/csv' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `individuo_${data.codice}.csv`;
|
||||
a.click();
|
||||
}
|
||||
|
||||
function sendEmail(id) {
|
||||
window.location.href = `/mailing/nuovo?individui=${id}`;
|
||||
}
|
||||
|
||||
function generateReport(id) {
|
||||
window.open('/individui/' + id + '/report', '_blank');
|
||||
}
|
||||
|
||||
function getSelectedIds() {
|
||||
return Array.from(document.querySelectorAll('.row-checkbox:checked')).map(cb => cb.value);
|
||||
}
|
||||
|
||||
function exportSelectedCSV() {
|
||||
const selectedIds = getSelectedIds();
|
||||
if (selectedIds.length === 0) {
|
||||
alert('Seleziona almeno un individuo');
|
||||
return;
|
||||
}
|
||||
|
||||
const rows = [];
|
||||
rows.push('Codice,Cognome,Nome,Email,Telefono');
|
||||
|
||||
document.querySelectorAll('#table-body tr').forEach(row => {
|
||||
const checkbox = row.querySelector('.row-checkbox');
|
||||
if (checkbox && checkbox.checked) {
|
||||
const cells = Array.from(row.querySelectorAll('td'));
|
||||
const codice = cells[1].textContent.trim();
|
||||
const cognome = cells[2].textContent.trim();
|
||||
const nome = cells[3].textContent.trim();
|
||||
const email = cells[4].textContent.trim();
|
||||
const telefono = cells[5].textContent.trim();
|
||||
rows.push(`"${codice}","${cognome}","${nome}","${email}","${telefono}"`);
|
||||
}
|
||||
});
|
||||
|
||||
const blob = new Blob([rows.join('\n')], { type: 'text/csv' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `individui_selezionati_${new Date().toISOString().slice(0,10)}.csv`;
|
||||
a.click();
|
||||
}
|
||||
|
||||
function printSelected() {
|
||||
const selectedIds = getSelectedIds();
|
||||
if (selectedIds.length === 0) {
|
||||
alert('Seleziona almeno un individuo');
|
||||
return;
|
||||
}
|
||||
window.open(`/individui/report/stampa?ids=${selectedIds.join(',')}`, '_blank');
|
||||
}
|
||||
|
||||
function emailSelected() {
|
||||
const selectedIds = getSelectedIds();
|
||||
if (selectedIds.length === 0) {
|
||||
alert('Seleziona almeno un individuo');
|
||||
return;
|
||||
}
|
||||
window.location.href = `/mailing/nuovo?individui=${selectedIds.join(',')}`;
|
||||
}
|
||||
|
||||
function deleteSelected() {
|
||||
const selectedIds = getSelectedIds();
|
||||
if (selectedIds.length === 0) {
|
||||
alert('Seleziona almeno un individuo');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('delete-individuo-name').textContent = selectedIds.length + ' individui selezionati';
|
||||
document.getElementById('count-contatti').textContent = '...';
|
||||
document.getElementById('count-gruppi').textContent = '...';
|
||||
document.getElementById('count-documenti').textContent = '...';
|
||||
document.getElementById('count-eventi').textContent = '...';
|
||||
|
||||
document.getElementById('confirm-delete-btn').onclick = function() {
|
||||
const eliminaContatti = document.getElementById('delete-contatti').checked;
|
||||
const eliminaDocumenti = document.getElementById('delete-documenti').checked;
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '/individui/mass-elimina';
|
||||
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '{{ csrf_token() }}';
|
||||
|
||||
let html = `<input type="hidden" name="_token" value="${csrfToken}">`;
|
||||
html += `<input type="hidden" name="elimina_contatti" value="${eliminaContatti}">`;
|
||||
html += `<input type="hidden" name="elimina_documenti" value="${eliminaDocumenti}">`;
|
||||
|
||||
selectedIds.forEach(function(id) {
|
||||
html += `<input type="hidden" name="ids[]" value="${id}">`;
|
||||
});
|
||||
|
||||
form.innerHTML = html;
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
};
|
||||
|
||||
$('#deleteModal').modal('show');
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]');
|
||||
if (!csrfToken) {
|
||||
const meta = document.createElement('meta');
|
||||
meta.name = 'csrf-token';
|
||||
meta.content = '{{ csrf_token() }}';
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
function showCreateMailingListModal() {
|
||||
document.getElementById('ml-nome').value = '';
|
||||
document.getElementById('ml-descrizione').value = '';
|
||||
document.getElementById('ml-contatti-tbody').innerHTML = '';
|
||||
document.getElementById('ml-count').textContent = '0';
|
||||
|
||||
const selectedIds = getSelectedIds();
|
||||
console.log('Selected IDs:', selectedIds);
|
||||
if (selectedIds.length > 0) {
|
||||
const url = '/individui/email-list?ids=' + selectedIds.join(',');
|
||||
console.log('Fetching URL:', url);
|
||||
fetch(url, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Response status:', response.status);
|
||||
console.log('Response type:', response.headers.get('content-type'));
|
||||
if (!response.ok) {
|
||||
return response.text().then(text => {
|
||||
console.log('Full error response:', text.substring(0, 500));
|
||||
const contentType = response.headers.get('content-type');
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
const json = JSON.parse(text);
|
||||
throw new Error(json.message || 'Errore HTTP ' + response.status);
|
||||
} else {
|
||||
throw new Error('Server returned HTML (status ' + response.status + ') - probabilmente sessione scaduta. Ricarica la pagina.');
|
||||
}
|
||||
});
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Data:', data);
|
||||
updateMlTable(data);
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error('Error:', err);
|
||||
alert('Errore caricamento contatti: ' + err.message + '\nURL: ' + url);
|
||||
});
|
||||
} else {
|
||||
alert('Seleziona almeno un individuo dalla tabella');
|
||||
}
|
||||
|
||||
$('#createMailingListModal').modal('show');
|
||||
}
|
||||
|
||||
function updateMlTable(data) {
|
||||
const tbody = document.getElementById('ml-contatti-tbody');
|
||||
const existingIds = new Set();
|
||||
|
||||
document.querySelectorAll('.ml-row-checkbox').forEach(function(cb) {
|
||||
existingIds.add(parseInt(cb.value));
|
||||
});
|
||||
|
||||
data.forEach(function(item) {
|
||||
if (existingIds.has(item.id)) return;
|
||||
|
||||
const tr = document.createElement('tr');
|
||||
const checked = item.email_count === 1 ? 'checked' : '';
|
||||
tr.innerHTML = `
|
||||
<td>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input ml-row-checkbox" id="ml-${item.id}" value="${item.id}" data-email="${item.email || ''}" ${checked}>
|
||||
<label class="custom-control-label" for="ml-${item.id}"></label>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="badge badge-secondary">${item.codice_id}</span></td>
|
||||
<td>${item.cognome}</td>
|
||||
<td>${item.nome}</td>
|
||||
<td>
|
||||
${item.email_count === 1 ? (item.email || '-') :
|
||||
'<select class="form-control form-control-sm" onchange="updateMlCheckboxEmail(' + item.id + ', this.value)">' +
|
||||
'<option value="">Seleziona email...</option>' +
|
||||
(item.emails || []).map(function(e) { return '<option value="' + e + '">' + e + '</option>'; }).join('') +
|
||||
'</select>'}
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-danger" onclick="this.closest('tr').remove(); updateMlCount();">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
|
||||
updateMlCount();
|
||||
}
|
||||
|
||||
function updateMlCount() {
|
||||
document.getElementById('ml-count').textContent = document.querySelectorAll('.ml-row-checkbox').length;
|
||||
}
|
||||
|
||||
function toggleMlSelectAll(source) {
|
||||
document.querySelectorAll('.ml-row-checkbox').forEach(function(cb) {
|
||||
cb.checked = source.checked;
|
||||
});
|
||||
}
|
||||
|
||||
function updateMlCheckboxEmail(id, email) {
|
||||
const checkbox = document.getElementById('ml-' + id);
|
||||
if (checkbox) {
|
||||
checkbox.dataset.email = email;
|
||||
checkbox.checked = email !== '';
|
||||
}
|
||||
}
|
||||
|
||||
async function createMailingList() {
|
||||
const nome = document.getElementById('ml-nome').value.trim();
|
||||
if (!nome) {
|
||||
alert('Inserisci un nome per la lista');
|
||||
return;
|
||||
}
|
||||
|
||||
const selected = [];
|
||||
document.querySelectorAll('.ml-row-checkbox:checked').forEach(function(cb) {
|
||||
if (cb.dataset.email) {
|
||||
selected.push({
|
||||
individuo_id: parseInt(cb.value),
|
||||
email: cb.dataset.email
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (selected.length === 0) {
|
||||
alert('Seleziona almeno un contatto con email');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
nome: nome,
|
||||
descrizione: document.getElementById('ml-descrizione').value.trim(),
|
||||
contatti: selected
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('/mailing-liste/create-from-individui', {
|
||||
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('Mailing list creata con successo!');
|
||||
$('#createMailingListModal').modal('hide');
|
||||
} else {
|
||||
alert('Errore nella creazione della lista');
|
||||
}
|
||||
} catch(e) {
|
||||
alert('Errore: ' + e.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,301 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Report Individui Multipli - {{ now()->format('d/m/Y') }}</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: #333;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 2px solid #2c3e50;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 24px;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.header .subtitle {
|
||||
font-size: 14px;
|
||||
color: #7f8c8d;
|
||||
}
|
||||
.individuo-block {
|
||||
margin-bottom: 30px;
|
||||
page-break-after: always;
|
||||
}
|
||||
.individuo-block:last-child {
|
||||
page-break-after: avoid;
|
||||
}
|
||||
.section {
|
||||
margin-bottom: 15px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
background: #2c3e50;
|
||||
padding: 8px 12px;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 6px 8px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background: #f8f9fa;
|
||||
font-weight: bold;
|
||||
width: 150px;
|
||||
}
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
background: #6c757d;
|
||||
color: #fff;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.badge-success { background: #28a745; }
|
||||
.badge-warning { background: #ffc107; color: #333; }
|
||||
.badge-danger { background: #dc3545; }
|
||||
.badge-info { background: #17a2b8; }
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 8px;
|
||||
}
|
||||
.info-item {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
}
|
||||
.info-item label {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.empty {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #ddd;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
color: #999;
|
||||
}
|
||||
.totale {
|
||||
background: #f8f9fa;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
border: 2px solid #2c3e50;
|
||||
font-weight: bold;
|
||||
}
|
||||
@media print {
|
||||
body { padding: 0; }
|
||||
.individuo-block { page-break-after: always; }
|
||||
.individuo-block:last-child { page-break-after: avoid; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>Report Individui Multipli</h1>
|
||||
<div class="subtitle">Totale: {{ $individui->count() }} individui</div>
|
||||
</div>
|
||||
|
||||
<div class="totale">
|
||||
Report generato il {{ now()->format('d/m/Y H:i') }}
|
||||
</div>
|
||||
|
||||
@foreach($individui as $ind)
|
||||
<div class="individuo-block">
|
||||
<div class="section">
|
||||
<div class="section-title">
|
||||
@if($ind->avatar)
|
||||
<img src="{{ url('/individui/' . $ind->id . '/avatar') }}"
|
||||
alt="Avatar"
|
||||
style="width: 30px; height: 30px; object-fit: cover; border-radius: 50%; margin-right: 8px; vertical-align: middle;">
|
||||
@endif
|
||||
{{ $ind->cognome }} {{ $ind->nome }} - Codice: {{ $ind->codice_id }}
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Codice</th>
|
||||
<td><span class="badge">{{ $ind->codice_id }}</span></td>
|
||||
<th>Genere</th>
|
||||
<td>{{ $ind->genere === 'M' ? 'Maschile' : ($ind->genere === 'F' ? 'Femminile' : '-') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Data di nascita</th>
|
||||
<td>{{ $ind->data_nascita ? $ind->data_nascita->format('d/m/Y') : '-' }}</td>
|
||||
<th>Luogo di nascita</th>
|
||||
<td>{{ $ind->città ?: '-' }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Residenza</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<label>Indirizzo</label>
|
||||
{{ $ind->indirizzo ?: '-' }}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>CAP</label>
|
||||
{{ $ind->cap ?: '-' }}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>Città</label>
|
||||
{{ $ind->città ?: '-' }}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>Provincia</label>
|
||||
{{ $ind->sigla_provincia ?: '-' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Documento di Identità</div>
|
||||
@if($ind->tipo_documento || $ind->numero_documento)
|
||||
<table>
|
||||
<tr>
|
||||
<th>Tipo</th>
|
||||
<td>
|
||||
@if($ind->tipo_documento === 'carta_identita')
|
||||
<span class="badge badge-info">Carta d'Identità</span>
|
||||
@elseif($ind->tipo_documento === 'patente')
|
||||
<span class="badge badge-warning">Patente</span>
|
||||
@else
|
||||
{{ $ind->tipo_documento }}
|
||||
@endif
|
||||
</td>
|
||||
<th>Numero</th>
|
||||
<td>{{ $ind->numero_documento }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Scadenza</th>
|
||||
<td>
|
||||
@if($ind->scadenza_documento)
|
||||
@if($ind->scadenza_documento->isPast())
|
||||
<span class="badge badge-danger">SCADUTO</span>
|
||||
@else
|
||||
{{ $ind->scadenza_documento->format('d/m/Y') }}
|
||||
@endif
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
</td>
|
||||
<th></th>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
@else
|
||||
<p class="empty">Nessun documento registrato</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Contatti ({{ $ind->contatti->count() }})</div>
|
||||
@if($ind->contatti->count() > 0)
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tipo</th>
|
||||
<th>Valore</th>
|
||||
<th>Etichetta</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($ind->contatti as $contatto)
|
||||
<tr>
|
||||
<td>
|
||||
@switch($contatto->tipo)
|
||||
@case('email')
|
||||
<span class="badge badge-info">Email</span>
|
||||
@break
|
||||
@case('telefono')
|
||||
<span class="badge">Telefono</span>
|
||||
@break
|
||||
@case('cellulare')
|
||||
<span class="badge badge-success">Cellulare</span>
|
||||
@break
|
||||
@default
|
||||
{{ $contatto->tipo }}
|
||||
@endswitch
|
||||
</td>
|
||||
<td>{{ $contatto->valore }}</td>
|
||||
<td>{{ $contatto->etichetta ?: '-' }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<p class="empty">Nessun contatto registrato</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Gruppi di Appartenenza ({{ $ind->gruppi->count() }})</div>
|
||||
@if($ind->gruppi->count() > 0)
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Gruppo</th>
|
||||
<th>Ruolo</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($ind->gruppi as $gruppo)
|
||||
<tr>
|
||||
<td>{{ $gruppo->nome }}</td>
|
||||
<td>{{ $gruppo->pivot->ruolo_nel_gruppo ?: '-' }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<p class="empty">Nessun gruppo associato</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<div class="footer">
|
||||
<p>Glastree - Report generato automaticamente</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.onload = function() {
|
||||
window.print();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,355 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Scheda Individuo - {{ $individuo->cognome }} {{ $individuo->nome }}</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: #333;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 2px solid #2c3e50;
|
||||
}
|
||||
.header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.header h1 {
|
||||
font-size: 24px;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.header .subtitle {
|
||||
font-size: 14px;
|
||||
color: #7f8c8d;
|
||||
}
|
||||
.section {
|
||||
margin-bottom: 20px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
background: #2c3e50;
|
||||
padding: 8px 12px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background: #f8f9fa;
|
||||
font-weight: bold;
|
||||
width: 150px;
|
||||
}
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
background: #6c757d;
|
||||
color: #fff;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.badge-success { background: #28a745; }
|
||||
.badge-warning { background: #ffc107; color: #333; }
|
||||
.badge-danger { background: #dc3545; }
|
||||
.badge-info { background: #17a2b8; }
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
.info-item {
|
||||
border: 1px solid #ddd;
|
||||
padding: 10px;
|
||||
}
|
||||
.info-item label {
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
color: #666;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.empty {
|
||||
color: #999;
|
||||
font-style: italic;
|
||||
}
|
||||
.footer {
|
||||
margin-top: 30px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #ddd;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
color: #999;
|
||||
}
|
||||
@media print {
|
||||
body { padding: 0; }
|
||||
.section { page-break-inside: avoid; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="header-content">
|
||||
@if($individuo->avatar)
|
||||
<img src="{{ url('/individui/' . $individuo->id . '/avatar') }}"
|
||||
alt="Avatar"
|
||||
style="width: 80px; height: 80px; object-fit: cover; border-radius: 50%; margin-right: 20px; border: 3px solid #2c3e50;">
|
||||
@endif
|
||||
<div>
|
||||
<h1>{{ $individuo->cognome }} {{ $individuo->nome }}</h1>
|
||||
<div class="subtitle">Scheda Individuo - Codice: {{ $individuo->codice_id }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Dati Anagrafici</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Codice</th>
|
||||
<td><span class="badge">{{ $individuo->codice_id }}</span></td>
|
||||
<th>Cognome</th>
|
||||
<td>{{ $individuo->cognome }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<td>{{ $individuo->nome }}</td>
|
||||
<th>Genere</th>
|
||||
<td>{{ $individuo->genere === 'M' ? 'Maschile' : ($individuo->genere === 'F' ? 'Femminile' : '-') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Data di nascita</th>
|
||||
<td>{{ $individuo->data_nascita ? $individuo->data_nascita->format('d/m/Y') : '-' }}</td>
|
||||
<th>Luogo di nascita</th>
|
||||
<td>{{ $individuo->città ?: '-' }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Residenza</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<label>Indirizzo</label>
|
||||
{{ $individuo->indirizzo ?: '-' }}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>CAP</label>
|
||||
{{ $individuo->cap ?: '-' }}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>Città</label>
|
||||
{{ $individuo->città ?: '-' }}
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<label>Provincia</label>
|
||||
{{ $individuo->sigla_provincia ?: '-' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Documento di Identità</div>
|
||||
@if($individuo->tipo_documento || $individuo->numero_documento)
|
||||
<table>
|
||||
<tr>
|
||||
<th>Tipo</th>
|
||||
<td>
|
||||
@if($individuo->tipo_documento === 'carta_identita')
|
||||
<span class="badge badge-info">Carta d'Identità</span>
|
||||
@elseif($individuo->tipo_documento === 'patente')
|
||||
<span class="badge badge-warning">Patente</span>
|
||||
@else
|
||||
{{ $individuo->tipo_documento }}
|
||||
@endif
|
||||
</td>
|
||||
<th>Numero</th>
|
||||
<td>{{ $individuo->numero_documento }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Scadenza</th>
|
||||
<td>
|
||||
@if($individuo->scadenza_documento)
|
||||
@if($individuo->scadenza_documento->isPast())
|
||||
<span class="badge badge-danger">SCADUTO</span>
|
||||
@else
|
||||
{{ $individuo->scadenza_documento->format('d/m/Y') }}
|
||||
@endif
|
||||
@else
|
||||
-
|
||||
@endif
|
||||
</td>
|
||||
<th></th>
|
||||
<td></td>
|
||||
</tr>
|
||||
</table>
|
||||
@else
|
||||
<p class="empty">Nessun documento registrato</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Contatti ({{ $individuo->contatti->count() }})</div>
|
||||
@if($individuo->contatti->count() > 0)
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Tipo</th>
|
||||
<th>Valore</th>
|
||||
<th>Etichetta</th>
|
||||
<th>Primario</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($individuo->contatti as $contatto)
|
||||
<tr>
|
||||
<td>
|
||||
@switch($contatto->tipo)
|
||||
@case('email')
|
||||
<span class="badge badge-info">Email</span>
|
||||
@break
|
||||
@case('telefono')
|
||||
<span class="badge">Telefono</span>
|
||||
@break
|
||||
@case('cellulare')
|
||||
<span class="badge badge-success">Cellulare</span>
|
||||
@break
|
||||
@default
|
||||
{{ $contatto->tipo }}
|
||||
@endswitch
|
||||
</td>
|
||||
<td>{{ $contatto->valore }}</td>
|
||||
<td>{{ $contatto->etichetta ?: '-' }}</td>
|
||||
<td>{{ $contatto->is_primary ? '✓' : '-' }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<p class="empty">Nessun contatto registrato</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Gruppi di Appartenenza ({{ $individuo->gruppi->count() }})</div>
|
||||
@if($individuo->gruppi->count() > 0)
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Gruppo</th>
|
||||
<th>Ruolo</th>
|
||||
<th>Data Adesione</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($individuo->gruppi as $gruppo)
|
||||
<tr>
|
||||
<td>{{ $gruppo->nome }}</td>
|
||||
<td>{{ $gruppo->pivot->ruolo_nel_gruppo ?: '-' }}</td>
|
||||
<td>{{ $gruppo->pivot->data_adesione ? \Carbon\Carbon::parse($gruppo->pivot->data_adesione)->format('d/m/Y') : '-' }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<p class="empty">Nessun gruppo associato</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Documenti ({{ $individuo->documenti->count() }})</div>
|
||||
@if($individuo->documenti->count() > 0)
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Tipologia</th>
|
||||
<th>Dimensione</th>
|
||||
<th>Data Upload</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($individuo->documenti as $documento)
|
||||
<tr>
|
||||
<td>{{ $documento->nome }}</td>
|
||||
<td>{{ $documento->tipologia }}</td>
|
||||
<td>{{ $documento->dimensione ? number_format($documento->dimensione / 1024, 1) . ' KB' : '-' }}</td>
|
||||
<td>{{ $documento->created_at->format('d/m/Y') }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<p class="empty">Nessun documento caricato</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-title">Eventi come Responsabile ({{ $individuo->eventiResponsabili->count() }})</div>
|
||||
@if($individuo->eventiResponsabili->count() > 0)
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Evento</th>
|
||||
<th>Data</th>
|
||||
<th>Tipo Ricorrenza</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($individuo->eventiResponsabili as $evento)
|
||||
<tr>
|
||||
<td>{{ $evento->nome }}</td>
|
||||
<td>{{ $evento->data_specifica ? $evento->data_specifica->format('d/m/Y') : '-' }}</td>
|
||||
<td>{{ $evento->tipo_recorrenza ?: '-' }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<p class="empty">Nessun evento assegnato come responsabile</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if($individuo->note)
|
||||
<div class="section">
|
||||
<div class="section-title">Note</div>
|
||||
<p>{{ $individuo->note }}</p>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="footer">
|
||||
<p>Report generato il {{ now()->format('d/m/Y H:i') }} - Glastree</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.onload = function() {
|
||||
window.print();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,671 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Dettaglio Individuo')
|
||||
@section('page_title', 'Dettaglio Individuo')
|
||||
|
||||
@section('breadcrumbs')
|
||||
<li class="breadcrumb-item"><a href="{{ route('individui.index') }}">Individui</a></li>
|
||||
<li class="breadcrumb-item active">{{ $individuo->nome_completo }}</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card card-success">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-user mr-2"></i>{{ $individuo->nome_completo }}</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p><strong>Codice:</strong> <span class="badge badge-secondary">{{ $individuo->codice_id }}</span></p>
|
||||
<p><strong>Data di nascita:</strong> {{ $individuo->data_nascita?->format('d/m/Y') ?: '-' }}</p>
|
||||
<p><strong>Genere:</strong> {{ $individuo->genere === 'M' ? 'Maschio' : ($individuo->genere === 'F' ? 'Femmina' : '-') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card card-secondary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-camera mr-2"></i>Avatar</h3>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
@if($individuo->avatar)
|
||||
<img src="{{ url('/individui/' . $individuo->id . '/avatar') }}"
|
||||
alt="Avatar"
|
||||
class="img-thumbnail mb-2"
|
||||
style="max-width: 120px; max-height: 120px; object-fit: cover; border-radius: 50%;">
|
||||
<br>
|
||||
<button type="button" class="btn btn-danger btn-sm" onclick="deleteAvatar()">
|
||||
<i class="fas fa-trash mr-1"></i>Rimuovi
|
||||
</button>
|
||||
@else
|
||||
<div class="bg-secondary d-flex align-items-center justify-content-center mb-3"
|
||||
style="width: 120px; height: 120px; margin: 0 auto; border-radius: 50%;">
|
||||
<i class="fas fa-user fa-4x text-white"></i>
|
||||
</div>
|
||||
@endif
|
||||
<form id="avatar-upload-form" enctype="multipart/form-data">
|
||||
@csrf
|
||||
<div class="custom-file mb-2">
|
||||
<input type="file" class="custom-file-input" id="avatar-input" name="avatar" accept="image/jpeg,image/png,image/gif">
|
||||
<label class="custom-file-label" for="avatar-input">Scegli immagine...</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="uploadAvatar()">
|
||||
<i class="fas fa-upload mr-1"></i>Carica
|
||||
</button>
|
||||
</form>
|
||||
<div id="avatar-message" class="mt-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-map-marker-alt mr-2"></i>Residenza</h3></div>
|
||||
<div class="card-body">
|
||||
<p><strong>Indirizzo:</strong> {{ $individuo->indirizzo ?: '-' }}</p>
|
||||
<p><strong>CAP:</strong> {{ $individuo->cap ?: '-' }}</p>
|
||||
<p><strong>Città:</strong> {{ $individuo->città ?: '-' }}</p>
|
||||
<p><strong>Provincia:</strong> {{ $individuo->sigla_provincia ?: '-' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-id-card mr-2"></i>Documento di identità</h3></div>
|
||||
<div class="card-body">
|
||||
<p><strong>Tipo:</strong> {{ $individuo->tipo_documento ? ucfirst(str_replace('_', ' ', $individuo->tipo_documento)) : '-' }}</p>
|
||||
<p><strong>Numero:</strong> {{ $individuo->numero_documento ?: '-' }}</p>
|
||||
<p><strong>Scadenza:</strong> {{ $individuo->scadenza_documento?->format('d/m/Y') ?: '-' }}</p>
|
||||
@if($individuo->hasDocumentoScaduto())
|
||||
<p><span class="badge badge-danger"><i class="fas fa-exclamation-triangle mr-1"></i> Documento scaduto da {{ $individuo->giorni_scadenza_documento }} giorni</span></p>
|
||||
@elseif($individuo->hasDocumentoScadeEntroGiorni(30))
|
||||
<p><span class="badge badge-warning"><i class="fas fa-exclamation-circle mr-1"></i> Scade tra {{ $individuo->giorni_scadenza_documento }} giorni</span></p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($individuo->note)
|
||||
<div class="row mt-3">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header"><h3 class="card-title"><i class="fas fa-sticky-note mr-2"></i>Note</h3></div>
|
||||
<div class="card-body">
|
||||
<p class="mb-0">{!! nl2br(e($individuo->note)) !!}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-address-book mr-2"></i>Contatti</h3>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if($individuo->contatti->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 20%;">Tipo</th>
|
||||
<th style="width: 30%;">Valore</th>
|
||||
<th style="width: 20%;">Etichetta</th>
|
||||
<th style="width: 10%;">Primario</th>
|
||||
<th style="width: 80px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($individuo->contatti as $contatto)
|
||||
@php
|
||||
$protocols = ['http://', 'https://', 'ftp://', 'ssh://', 'sftp://', 'telnet://'];
|
||||
$isUrl = false;
|
||||
if (in_array($contatto->tipo, ['web', 'telegram'])) {
|
||||
foreach ($protocols as $p) {
|
||||
if (str_starts_with(strtolower($contatto->valore), $p)) {
|
||||
$isUrl = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
<tr id="contatto-row-{{ $contatto->id }}">
|
||||
<td><span class="text-capitalize">{{ $contatto->tipo }}</span></td>
|
||||
<td>
|
||||
@if($contatto->tipo === 'email')
|
||||
<a href="mailto:{{ $contatto->valore }}">{{ $contatto->valore }}</a>
|
||||
@elseif(in_array($contatto->tipo, ['telefono', 'cellulare', 'whatsapp']))
|
||||
<a href="tel:{{ $contatto->valore }}">{{ $contatto->valore }}</a>
|
||||
@elseif($isUrl)
|
||||
<a href="{{ $contatto->valore }}" target="_blank">{{ $contatto->valore }}</a>
|
||||
@else
|
||||
{{ $contatto->valore }}
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $contatto->etichetta ?: '-' }}</td>
|
||||
<td>
|
||||
@if($contatto->is_primary)
|
||||
<span class="badge badge-success"><i class="fas fa-check mr-1"></i> Sì</span>
|
||||
@else
|
||||
<span class="text-muted">No</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-warning" onclick="editContattoInline({{ $contatto->id }})" title="Modifica">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<form action="{{ url('/contatti/' . $contatto->id) }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<input type="hidden" name="_redirect" value="{{ url('/individui/' . $individuo->id) }}">
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo contatto?')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="contatto-edit-{{ $contatto->id }}" style="display:none;">
|
||||
<td colspan="5">
|
||||
<form action="{{ url('/contatti/' . $contatto->id) }}" method="POST" class="mb-0">
|
||||
@csrf @method('PUT')
|
||||
<input type="hidden" name="_redirect" value="{{ url('/individui/' . $individuo->id) }}">
|
||||
<input type="hidden" name="individuo_id" value="{{ $individuo->id }}">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<select name="tipo" class="form-control form-control-sm" required>
|
||||
<option value="telefono" {{ $contatto->tipo === 'telefono' ? 'selected' : '' }}>Telefono</option>
|
||||
<option value="cellulare" {{ $contatto->tipo === 'cellulare' ? 'selected' : '' }}>Cellulare</option>
|
||||
<option value="email" {{ $contatto->tipo === 'email' ? 'selected' : '' }}>Email</option>
|
||||
<option value="fax" {{ $contatto->tipo === 'fax' ? 'selected' : '' }}>Fax</option>
|
||||
<option value="web" {{ $contatto->tipo === 'web' ? 'selected' : '' }}>Web</option>
|
||||
<option value="telegram" {{ $contatto->tipo === 'telegram' ? 'selected' : '' }}>Telegram</option>
|
||||
<option value="whatsapp" {{ $contatto->tipo === 'whatsapp' ? 'selected' : '' }}>WhatsApp</option>
|
||||
<option value="altro" {{ $contatto->tipo === 'altro' ? 'selected' : '' }}>Altro</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<input type="text" name="valore" class="form-control form-control-sm" value="{{ $contatto->valore }}" required>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="text" name="etichetta" class="form-control form-control-sm" value="{{ $contatto->etichetta }}" placeholder="Etichetta">
|
||||
</div>
|
||||
<div class="col-md-1 text-center">
|
||||
<input type="checkbox" name="is_primary" value="1" {{ $contatto->is_primary ? 'checked' : '' }}>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-xs btn-success"><i class="fas fa-check mr-1"></i>Salva</button>
|
||||
<button type="button" class="btn btn-xs btn-secondary" onclick="cancelEditContatto({{ $contatto->id }})"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-address-book fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun contatto</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-folder mr-2"></i>Gruppi</h3>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="showGruppoForm()">
|
||||
<i class="fas fa-plus mr-1"></i> Aggiungi
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
|
||||
@if($individuo->gruppi->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Diocesi</th>
|
||||
<th>Responsabile</th>
|
||||
<th style="width: 130px;">Ruolo</th>
|
||||
<th style="width: 110px;">Data Adesione</th>
|
||||
<th style="width: 80px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($individuo->gruppi as $gruppo)
|
||||
<tr id="gruppo-row-{{ $gruppo->id }}">
|
||||
<td>
|
||||
<i class="fas fa-folder text-warning mr-1"></i>
|
||||
<a href="{{ url('/gruppi/' . $gruppo->id) }}">{{ $gruppo->nome }}</a>
|
||||
</td>
|
||||
<td>{{ $gruppo->diocesi?->nome ?: '-' }}</td>
|
||||
<td>{{ $gruppo->getResponsabili()->isNotEmpty() ? $gruppo->getResponsabili()->first()->nome_completo : '-' }}</td>
|
||||
<td>
|
||||
@php
|
||||
$ruoloIds = $gruppo->pivot->ruolo_ids ? json_decode($gruppo->pivot->ruolo_ids, true) : [];
|
||||
$ruoli = \App\Models\Ruolo::findByIds($ruoloIds);
|
||||
@endphp
|
||||
@if($ruoli->count() > 0)
|
||||
@foreach($ruoli as $ruolo)
|
||||
<span class="badge badge-info mr-1">{{ $ruolo->nome }}</span>
|
||||
@endforeach
|
||||
@else
|
||||
<span class="text-muted">-</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $gruppo->pivot->data_adesione ? \Carbon\Carbon::parse($gruppo->pivot->data_adesione)->format('d/m/Y') : '-' }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-warning" onclick="editGruppoInline({{ $gruppo->id }})" title="Modifica ruolo">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-xs btn-danger" onclick="deleteGruppo({{ $gruppo->id }})" title="Rimuovi">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="gruppo-edit-{{ $gruppo->id }}" style="display:none;">
|
||||
<td colspan="6">
|
||||
<form action="{{ url('/individui/' . $individuo->id . '/gruppi/' . $gruppo->id) }}" method="POST" class="mb-0">
|
||||
@csrf @method('PUT')
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<input type="text" class="form-control form-control-sm" value="{{ $gruppo->nome }}" disabled>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="ruolo_ids[]" class="form-control form-control-sm" multiple size="3">
|
||||
@php $selectedRuoliIds = $gruppo->pivot->ruolo_ids ? json_decode($gruppo->pivot->ruolo_ids, true) : [] @endphp
|
||||
@foreach(\App\Models\Ruolo::attive() as $ruolo)
|
||||
<option value="{{ $ruolo->id }}" {{ in_array($ruolo->id, $selectedRuoliIds) ? 'selected' : '' }}>{{ $ruolo->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="date" name="data_adesione" class="form-control form-control-sm" value="{{ $gruppo->pivot->data_adesione }}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-xs btn-success"><i class="fas fa-check mr-1"></i>Salva</button>
|
||||
<button type="button" class="btn btn-xs btn-secondary" onclick="cancelEditGruppo({{ $gruppo->id }})"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-users fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun gruppo associato</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3" id="gruppo-add-card" style="display:none; background-color: #fff3cd; border-color: #ffc107;">
|
||||
<div class="card-header bg-warning">
|
||||
<h3 class="card-title"><i class="fas fa-folder-plus mr-2"></i>Aggiungi a Gruppo</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<select id="new-gruppo-id" class="form-control form-control-sm">
|
||||
<option value="">Seleziona gruppo...</option>
|
||||
@foreach(\App\Models\Gruppo::orderBy('nome')->get() as $g)
|
||||
@if(!$individuo->gruppi->contains($g->id))
|
||||
<option value="{{ $g->id }}">{{ $g->full_path }}</option>
|
||||
@endif
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select id="new-gruppo-ruolo" class="form-control form-control-sm" multiple size="3">
|
||||
@foreach(\App\Models\Ruolo::attive() as $ruolo)
|
||||
<option value="{{ $ruolo->id }}">{{ $ruolo->nome }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<small class="text-muted">Ctrl+click per selezionare più ruoli</small>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<input type="date" id="new-gruppo-data" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="button" class="btn btn-success btn-sm" onclick="submitGruppoForm()">
|
||||
<i class="fas fa-check mr-1"></i> Associa
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary btn-sm" onclick="hideGruppoForm()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-3">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-file mr-2"></i>Documenti</h3>
|
||||
<button type="button" class="btn btn-xs btn-success float-right" onclick="showDocumentoForm()">
|
||||
<i class="fas fa-plus mr-1"></i> Carica
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="documento-add-form" style="display:none;" class="p-3 bg-light border-bottom">
|
||||
<form action="/documenti" method="POST" enctype="multipart/form-data" class="mb-0">
|
||||
@csrf
|
||||
<input type="hidden" name="visibilita" value="individuo">
|
||||
<input type="hidden" name="visibilita_target_id" value="{{ $individuo->id }}">
|
||||
<input type="hidden" name="visibilita_target_type" value="App\Models\Individuo">
|
||||
<input type="hidden" name="redirect_to" value="{{ url('/individui/' . $individuo->id) }}">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<input type="text" name="nome_file" class="form-control form-control-sm" placeholder="Nome documento" required>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select name="tipologia" class="form-control form-control-sm" required>
|
||||
<option value="">Tipologia...</option>
|
||||
<option value="documento">Documento</option>
|
||||
<option value="avatar">Avatar</option>
|
||||
<option value="galleria">Galleria</option>
|
||||
<option value="statuto">Statuto</option>
|
||||
<option value="altro">Altro</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<input type="file" name="file" class="form-control form-control-sm" required>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-xs btn-success"><i class="fas fa-upload mr-1"></i>Carica</button>
|
||||
<button type="button" class="btn btn-xs btn-secondary" onclick="hideDocumentoForm()"><i class="fas fa-times"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@if($individuo->documenti->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th style="width: 120px;">Tipologia</th>
|
||||
<th style="width: 80px;">Dimensione</th>
|
||||
<th style="width: 130px;">Data Upload</th>
|
||||
<th style="width: 80px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($individuo->documenti as $documento)
|
||||
<tr>
|
||||
<td>
|
||||
<a href="#" onclick="event.preventDefault(); previewDocumento({{ $documento->id }}, '{{ $documento->mime_type }}');">
|
||||
<i class="fas fa-file text-secondary mr-1"></i>
|
||||
{{ $documento->nome_file }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ ucfirst(str_replace('_', ' ', $documento->tipologia)) }}</td>
|
||||
<td>{{ number_format($documento->dimensione / 1024, 1) }} KB</td>
|
||||
<td>{{ $documento->created_at->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
@if($documento->file_path)
|
||||
<button type="button" class="btn btn-xs btn-primary" onclick="previewDocumento({{ $documento->id }}, '{{ $documento->mime_type }}')" title="Anteprima">
|
||||
<i class="fas fa-eye"></i>
|
||||
</button>
|
||||
@endif
|
||||
<form action="{{ url('/documenti/' . $documento->id) }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<input type="hidden" name="_redirect" value="{{ url('/individui/' . $individuo->id) }}">
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo documento?')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-file fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessun documento</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<a href="{{ url('/individui/' . $individuo->id . '/edit') }}" class="btn btn-warning">
|
||||
<i class="fas fa-edit mr-1"></i> Modifica
|
||||
</a>
|
||||
<form action="{{ url('/individui/' . $individuo->id) }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-danger" onclick="return confirm('Confermi l\'eliminazione di {{ $individuo->nome_completo }}?')">
|
||||
<i class="fas fa-trash mr-1"></i> Elimina
|
||||
</button>
|
||||
</form>
|
||||
<a href="{{ route('individui.index') }}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left mr-1"></i> Torna all'elenco
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="previewModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document" style="max-width: 90vw;">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-file mr-2"></i><span id="previewModalTitle">Anteprima</span></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Chiudi">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body text-center p-0" style="min-height: 400px;">
|
||||
<iframe id="previewFrame" src="" style="width: 100%; height: 70vh; border: none;"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a id="previewDownloadBtn" href="#" class="btn btn-primary" download>
|
||||
<i class="fas fa-download mr-1"></i> Scarica
|
||||
</a>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Chiudi</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function editContattoInline(id) {
|
||||
document.getElementById('contatto-row-' + id).style.display = 'none';
|
||||
document.getElementById('contatto-edit-' + id).style.display = 'table-row';
|
||||
}
|
||||
|
||||
function cancelEditContatto(id) {
|
||||
document.getElementById('contatto-row-' + id).style.display = 'table-row';
|
||||
document.getElementById('contatto-edit-' + id).style.display = 'none';
|
||||
}
|
||||
|
||||
function showGruppoForm() {
|
||||
var card = document.getElementById('gruppo-add-card');
|
||||
if (card) {
|
||||
card.style.display = 'block';
|
||||
card.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
}
|
||||
|
||||
function hideGruppoForm() {
|
||||
var card = document.getElementById('gruppo-add-card');
|
||||
if (card) {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function submitGruppoForm() {
|
||||
var gruppoId = document.getElementById('new-gruppo-id').value;
|
||||
var ruoloSelect = document.getElementById('new-gruppo-ruolo');
|
||||
var dataAdesione = document.getElementById('new-gruppo-data').value;
|
||||
|
||||
if (!gruppoId) {
|
||||
alert('Seleziona un gruppo');
|
||||
return;
|
||||
}
|
||||
|
||||
var formData = new URLSearchParams();
|
||||
formData.append('gruppo_id', gruppoId);
|
||||
|
||||
var selectedRuoli = Array.from(ruoloSelect.selectedOptions).map(opt => opt.value).filter(v => v);
|
||||
selectedRuoli.forEach(function(ruoloId) {
|
||||
formData.append('ruolo_ids[]', ruoloId);
|
||||
});
|
||||
|
||||
formData.append('data_adesione', dataAdesione);
|
||||
|
||||
fetch('{{ url('/individui/' . $individuo->id . '/gruppi') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.text().then(text => { throw new Error(text); });
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
hideGruppoForm();
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(data.error || 'Errore');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Errore: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function editGruppoInline(id) {
|
||||
document.getElementById('gruppo-row-' + id).style.display = 'none';
|
||||
document.getElementById('gruppo-edit-' + id).style.display = 'table-row';
|
||||
}
|
||||
|
||||
function cancelEditGruppo(id) {
|
||||
document.getElementById('gruppo-row-' + id).style.display = 'table-row';
|
||||
document.getElementById('gruppo-edit-' + id).style.display = 'none';
|
||||
}
|
||||
|
||||
function deleteGruppo(gruppoId) {
|
||||
if (!confirm('Rimuovere da questo gruppo?')) return;
|
||||
|
||||
fetch('{{ url('/individui/' . $individuo->id . '/gruppi') }}/' + gruppoId, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.text().then(text => { throw new Error(text); });
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(data.error || 'Errore');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Errore: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function showDocumentoForm() {
|
||||
document.getElementById('documento-add-form').style.display = 'block';
|
||||
}
|
||||
|
||||
function hideDocumentoForm() {
|
||||
document.getElementById('documento-add-form').style.display = 'none';
|
||||
}
|
||||
|
||||
function previewDocumento(id, mimeType) {
|
||||
var previewUrl = '/documenti/' + id + '/preview';
|
||||
var downloadUrl = '/documenti/' + id + '/download';
|
||||
|
||||
document.getElementById('previewFrame').src = previewUrl;
|
||||
document.getElementById('previewDownloadBtn').href = downloadUrl;
|
||||
document.getElementById('previewModalTitle').textContent = 'Anteprima';
|
||||
|
||||
if (mimeType && mimeType.startsWith('image/')) {
|
||||
document.getElementById('previewDownloadBtn').style.display = 'inline-block';
|
||||
} else {
|
||||
document.getElementById('previewDownloadBtn').style.display = 'none';
|
||||
}
|
||||
|
||||
$('#previewModal').modal('show');
|
||||
}
|
||||
|
||||
document.getElementById('avatar-input').addEventListener('change', function(e) {
|
||||
var fileName = e.target.files[0]?.name || 'Scegli file...';
|
||||
e.target.nextElementSibling.textContent = fileName;
|
||||
});
|
||||
|
||||
function uploadAvatar() {
|
||||
var input = document.getElementById('avatar-input');
|
||||
if (!input.files[0]) {
|
||||
document.getElementById('avatar-message').innerHTML = '<span class="text-danger">Seleziona un\'immagine</span>';
|
||||
return;
|
||||
}
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('avatar', input.files[0]);
|
||||
formData.append('_token', '{{ csrf_token() }}');
|
||||
|
||||
document.getElementById('avatar-message').innerHTML = '<span class="text-info"><i class="fas fa-spinner fa-spin"></i> Caricamento...</span>';
|
||||
|
||||
fetch('{{ url('/individui/' . $individuo->id . '/avatar') }}', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
document.getElementById('avatar-message').innerHTML = '<span class="text-success">Avatar caricato!</span>';
|
||||
setTimeout(function() { window.location.reload(); }, 500);
|
||||
} else {
|
||||
document.getElementById('avatar-message').innerHTML = '<span class="text-danger">' + (data.message || 'Errore') + '</span>';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
document.getElementById('avatar-message').innerHTML = '<span class="text-danger">Errore: ' + error + '</span>';
|
||||
});
|
||||
}
|
||||
|
||||
function deleteAvatar() {
|
||||
if (!confirm('Rimuovere l\'avatar?')) return;
|
||||
|
||||
fetch('{{ url('/individui/' . $individuo->id . '/avatar') }}', {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(data.message || 'Errore');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Errore: ' + error);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,281 @@
|
||||
@php
|
||||
$appLogoSmall = \App\Models\AppSetting::getLogoSmallUrl();
|
||||
$appName = \App\Models\AppSetting::getAppName() ?? 'Glastree';
|
||||
@endphp
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>@yield('title', e($appName))</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/admin-lte@3.2/dist/css/adminlte.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@300;400;600;700&display=swap">
|
||||
@yield('styles')
|
||||
</head>
|
||||
<body class="hold-transition sidebar-mini layout-fixed">
|
||||
<div class="wrapper">
|
||||
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link" data-toggle="dropdown" href="#">
|
||||
<i class="far fa-bell"></i>
|
||||
@php $unread = Auth::user()->unreadNotificheCount() ?? 0 @endphp
|
||||
@if($unread > 0)
|
||||
<span class="badge badge-warning navbar-badge">{{ $unread }}</span>
|
||||
@endif
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<span class="dropdown-header">Notifiche ({{ $unread }})</span>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a href="#" class="dropdown-item">Visualizza tutte</a>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link" data-toggle="dropdown" href="#">
|
||||
<i class="fas fa-user"></i> {{ Auth::user()->name }}
|
||||
</a>
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a href="#" class="dropdown-item">
|
||||
<i class="fas fa-user-cog mr-2"></i> Profilo
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button type="submit" class="dropdown-item">
|
||||
<i class="fas fa-sign-out-alt mr-2"></i> Esci
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@php
|
||||
$appLogoSmall = \App\Models\AppSetting::getLogoSmallUrl();
|
||||
$appName = \App\Models\AppSetting::getAppName() ?? 'Glastree';
|
||||
@endphp
|
||||
<aside class="main-sidebar sidebar-dark-primary elevation-4">
|
||||
<a href="{{ route('dashboard') }}" class="brand-link">
|
||||
@if($appLogoSmall)
|
||||
<img src="{{ $appLogoSmall }}" alt="Logo" class="brand-image" style="max-height: 35px; max-width: 40px;">
|
||||
@else
|
||||
<i class="fas fa-tree brand-image ml-3 mr-2"></i>
|
||||
@endif
|
||||
<span class="brand-text font-weight-light">{{ $appName }}</span>
|
||||
</a>
|
||||
|
||||
<div class="sidebar">
|
||||
<nav class="mt-2">
|
||||
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu">
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('dashboard') }}" class="nav-link {{ request()->routeIs('dashboard') ? 'active' : '' }}">
|
||||
<i class="nav-icon fas fa-tachometer-alt"></i>
|
||||
<p>Dashboard</p>
|
||||
</a>
|
||||
</li>
|
||||
@if(Auth::user()->canAccess('individui'))
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('individui.index') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-users"></i>
|
||||
<p>Individui</p>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@if(Auth::user()->canAccess('gruppi'))
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('gruppi.index') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-folder"></i>
|
||||
<p>Gruppi</p>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@if(Auth::user()->canAccess('documenti'))
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('documenti.index') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-file"></i>
|
||||
<p>Documenti</p>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@if(Auth::user()->canAccess('eventi'))
|
||||
<li class="nav-item has-treeview">
|
||||
<a href="{{ route('eventi.calendar') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-calendar"></i>
|
||||
<p>Eventi <i class="right fas fa-angle-left" style="font-size: 0.8rem;"></i></p>
|
||||
</a>
|
||||
<ul class="nav nav-treeview">
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('eventi.calendar') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-calendar-alt"></i>
|
||||
<p>Calendario</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('eventi.index') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-list"></i>
|
||||
<p>Elenco</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('eventi.create') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-plus"></i>
|
||||
<p>Nuovo Evento</p>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@endif
|
||||
@if(Auth::user()->canAccess('email') || Auth::user()->canAccess('mailing'))
|
||||
<li class="nav-item has-treeview">
|
||||
<a href="{{ route('email.index', 'inbox') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-envelope"></i>
|
||||
<p>Email <i class="right fas fa-angle-left" style="font-size: 0.8rem;"></i></p>
|
||||
</a>
|
||||
<ul class="nav nav-treeview">
|
||||
@if(Auth::user()->canAccess('email'))
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('email.compose') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-plus"></i>
|
||||
<p>Nuova Email</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('email.index', 'inbox') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-inbox"></i>
|
||||
<p>In arrivo</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('email.index', 'starred') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-star"></i>
|
||||
<p>Preferiti</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('email.index', 'sent') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-paper-plane"></i>
|
||||
<p>Inviate</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('email.index', 'drafts') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-edit"></i>
|
||||
<p>Bozze</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('email.index', 'archive') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-archive"></i>
|
||||
<p>Archivio</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('email.index', 'trash') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-trash"></i>
|
||||
<p>Cestino</p>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</li>
|
||||
@endif
|
||||
@if(Auth::user()->canAccess('mailing'))
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('mailing-liste.index') }}" class="nav-link">
|
||||
<i class="nav-icon fas fa-list"></i>
|
||||
<p>Mailing List</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' : '' }}">
|
||||
<i class="nav-icon fas fa-chart-bar"></i>
|
||||
<p>Report</p>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@if(Auth::user()->isSuperAdmin())
|
||||
<li class="nav-item has-treeview">
|
||||
<a href="#" class="nav-link">
|
||||
<i class="nav-icon fas fa-cog"></i>
|
||||
<p>Impostazioni <i class="right fas fa-angle-left"></i></p>
|
||||
</a>
|
||||
<ul class="nav nav-treeview">
|
||||
<li class="nav-item">
|
||||
<a href="/impostazioni#tipologie" class="nav-link">
|
||||
<i class="nav-icon fas fa-file"></i>
|
||||
<p>Tipologie Documenti</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/impostazioni#ruoli" class="nav-link">
|
||||
<i class="nav-icon fas fa-users-cog"></i>
|
||||
<p>Tipi di Ruolo</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/impostazioni/email" class="nav-link">
|
||||
<i class="nav-icon fas fa-envelope"></i>
|
||||
<p>Impostazione Email</p>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/admin/utenti" class="nav-link">
|
||||
<i class="nav-icon fas fa-users-cog"></i>
|
||||
<p>Admin</p>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="content-wrapper" style="min-height: calc(100vh - 120px);">
|
||||
<div class="content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row mb-2">
|
||||
<div class="col-sm-6">
|
||||
<h1 class="m-0">@yield('page_title', 'Dashboard')</h1>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<ol class="breadcrumb float-sm-right">
|
||||
@yield('breadcrumbs')
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="container-fluid">
|
||||
{!! $slot ?? '' !!}
|
||||
@yield('content')
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="main-footer">
|
||||
<strong>{{ \App\Models\AppSetting::getFooterText() }}</strong>
|
||||
@if(\App\Models\AppSetting::shouldShowVersion() && \App\Models\AppSetting::getAppVersion())
|
||||
<span class="ml-2">v{{ \App\Models\AppSetting::getAppVersion() }}</span>
|
||||
@endif
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
|
||||
<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>
|
||||
@yield('scripts')
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,44 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Nuova Mailing List')
|
||||
@section('page_title', 'Nuova Mailing List')
|
||||
|
||||
@section('content')
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-plus mr-2"></i>Crea Mailing List</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('mailing-liste.store') }}" method="POST">
|
||||
@csrf
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="nome" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox mt-4">
|
||||
<input type="checkbox" class="custom-control-input" id="attiva" name="attiva" value="1" checked>
|
||||
<label class="custom-control-label" for="attiva">Lista attiva</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione</label>
|
||||
<textarea name="descrizione" class="form-control" rows="2"></textarea>
|
||||
</div>
|
||||
<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.
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva Lista
|
||||
</button>
|
||||
<a href="{{ route('mailing-liste.index') }}" class="btn btn-secondary">Annulla</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,351 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Modifica Mailing List')
|
||||
@section('page_title', 'Modifica Mailing List')
|
||||
|
||||
@section('content')
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-edit mr-2"></i>Modifica Mailing List</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ url('/mailing-liste/' . $mailingList->id) }}" method="POST" id="ml-form">
|
||||
@csrf @method('PUT')
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>Nome *</label>
|
||||
<input type="text" name="nome" class="form-control" value="{{ $mailingList->nome }}" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="custom-control custom-checkbox mt-4">
|
||||
<input type="checkbox" class="custom-control-input" id="attiva" name="attiva" value="1" {{ $mailingList->attiva ? 'checked' : '' }}>
|
||||
<label class="custom-control-label" for="attiva">Lista attiva</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione</label>
|
||||
<textarea name="descrizione" class="form-control" rows="2">{{ $mailingList->descrizione }}</textarea>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>Seleziona Gruppi</label>
|
||||
<select name="gruppi[]" id="gruppi-select" class="form-control" multiple size="6">
|
||||
</select>
|
||||
<small class="text-muted">Seleziona con Ctrl/Cmd (ogni membro del gruppo sarà aggiunto)</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>Seleziona Individui</label>
|
||||
<select name="individui[]" id="individui-select" class="form-control" multiple size="6">
|
||||
</select>
|
||||
<small class="text-muted">Seleziona con Ctrl/Cmd</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="button" class="btn btn-primary" onclick="caricaSelezionati()">
|
||||
<i class="fas fa-search mr-1"></i> Carica contatti
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" onclick="selezionaTutti()">
|
||||
<i class="fas fa-check-square mr-1"></i> Seleziona tutti
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning" onclick="deselezionaTutti()">
|
||||
<i class="fas fa-square mr-1"></i> Deseleziona tutti
|
||||
</button>
|
||||
</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;">
|
||||
<table class="table table-bordered table-sm">
|
||||
<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>
|
||||
<th>Codice</th>
|
||||
<th>Cognome</th>
|
||||
<th>Nome</th>
|
||||
<th>Email</th>
|
||||
<th>Stato</th>
|
||||
<th style="width: 50px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="contatti-tbody">
|
||||
@foreach($mailingList->contatti as $contact)
|
||||
@if($contact->individuo)
|
||||
<tr>
|
||||
<td>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input contatto-checkbox" id="contatto-{{ $contact->id }}" value="{{ $contact->individuo->id }}" data-email="{{ $contact->individuo->contatti->where('tipo', 'email')->first()?->valore }}" checked>
|
||||
<label class="custom-control-label" for="contatto-{{ $contact->id }}"></label>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="badge badge-secondary">{{ $contact->individuo->codice_id }}</span></td>
|
||||
<td>{{ $contact->individuo->cognome }}</td>
|
||||
<td>{{ $contact->individuo->nome }}</td>
|
||||
<td>{{ $contact->individuo->contatti->where('tipo', 'email')->first()?->valore ?: '-' }}</td>
|
||||
<td>
|
||||
@if($contact->opt_in)
|
||||
<span class="badge badge-success">Iscritto</span>
|
||||
@else
|
||||
<span class="badge badge-danger">Disiscritto</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-outline-danger" onclick="rimuoviContatto(this)">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<small class="text-muted">Deseleziona le righe che non vuoi includere nella lista</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save mr-1"></i> Salva
|
||||
</button>
|
||||
<a href="{{ route('mailing-liste.index') }}" class="btn btn-secondary">Annulla</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
let contattiCaricati = [];
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
||||
|
||||
fetch('/gruppi/all', {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': csrfToken,
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('HTTP ' + response.status);
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
const select = document.getElementById('gruppi-select');
|
||||
console.log('Gruppi caricati:', data.length);
|
||||
data.forEach(function(g) {
|
||||
const option = document.createElement('option');
|
||||
option.value = g.id;
|
||||
option.textContent = g.full_path || g.nome;
|
||||
select.appendChild(option);
|
||||
});
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.error('Errore gruppi:', err);
|
||||
});
|
||||
|
||||
fetch('/individui/all', {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': csrfToken,
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('HTTP ' + response.status);
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
const select = document.getElementById('individui-select');
|
||||
console.log('Individui caricati:', data.length);
|
||||
data.forEach(function(ind) {
|
||||
const option = document.createElement('option');
|
||||
option.value = ind.id;
|
||||
option.textContent = ind.codice_id + ' - ' + ind.cognome + ' ' + ind.nome;
|
||||
select.appendChild(option);
|
||||
});
|
||||
})
|
||||
.catch(function(err) {
|
||||
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
|
||||
}));
|
||||
});
|
||||
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
||||
|
||||
function caricaSelezionati() {
|
||||
const gruppiIds = Array.from(document.getElementById('gruppi-select').selectedOptions).map(opt => parseInt(opt.value));
|
||||
const individuiIds = Array.from(document.getElementById('individui-select').selectedOptions).map(opt => parseInt(opt.value));
|
||||
|
||||
if (gruppiIds.length === 0 && individuiIds.length === 0) {
|
||||
alert('Seleziona almeno un gruppo o un individuo');
|
||||
return;
|
||||
}
|
||||
|
||||
const promises = [];
|
||||
|
||||
gruppiIds.forEach(function(gruppoId) {
|
||||
promises.push(
|
||||
fetch('/gruppi/' + gruppoId + '/individui-email', {
|
||||
credentials: 'include',
|
||||
headers: { 'X-CSRF-TOKEN': csrfToken }
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('HTTP ' + response.status);
|
||||
return response.json();
|
||||
})
|
||||
.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 (individuiIds.length > 0) {
|
||||
promises.push(
|
||||
fetch('/individui/email-list?ids=' + individuiIds.join(','), {
|
||||
credentials: 'include',
|
||||
headers: { 'X-CSRF-TOKEN': csrfToken }
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error('HTTP ' + response.status);
|
||||
return response.json();
|
||||
})
|
||||
.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']
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
Promise.all(promises).then(function() {
|
||||
renderContattiTable();
|
||||
}).catch(function() {
|
||||
alert('Errore caricamento contatti');
|
||||
});
|
||||
}
|
||||
|
||||
function renderContattiTable() {
|
||||
const tbody = document.getElementById('contatti-tbody');
|
||||
tbody.innerHTML = '';
|
||||
|
||||
contattiCaricati.forEach(function(c, index) {
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input contatto-checkbox" id="contatto-${index}" value="${c.individuo_id}" data-email="${c.email}" checked>
|
||||
<label class="custom-control-label" for="contatto-${index}"></label>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="badge badge-secondary">${c.codice_id || ''}</span></td>
|
||||
<td>${c.cognome || ''}</td>
|
||||
<td>${c.nome || ''}</td>
|
||||
<td>${c.email || ''}</td>
|
||||
<td><small class="text-muted">${c.origini ? c.origini.join(', ') : 'Esistente'}</small></td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-outline-danger" onclick="rimuoviContatto(this)">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
|
||||
document.getElementById('contatti-count').textContent = contattiCaricati.length;
|
||||
}
|
||||
|
||||
function rimuoviContatto(btn) {
|
||||
btn.closest('tr').remove();
|
||||
const count = document.querySelectorAll('.contatto-checkbox').length;
|
||||
document.getElementById('contatti-count').textContent = count;
|
||||
}
|
||||
|
||||
function toggleSelectAll(source) {
|
||||
document.querySelectorAll('.contatto-checkbox').forEach(function(cb) {
|
||||
cb.checked = source.checked;
|
||||
});
|
||||
}
|
||||
|
||||
function selezionaTutti() {
|
||||
document.querySelectorAll('.contatto-checkbox').forEach(function(cb) {
|
||||
cb.checked = true;
|
||||
});
|
||||
document.getElementById('select-all').checked = true;
|
||||
}
|
||||
|
||||
function deselezionaTutti() {
|
||||
document.querySelectorAll('.contatto-checkbox').forEach(function(cb) {
|
||||
cb.checked = false;
|
||||
});
|
||||
document.getElementById('select-all').checked = false;
|
||||
}
|
||||
|
||||
document.getElementById('ml-form').addEventListener('submit', function(e) {
|
||||
const selected = [];
|
||||
document.querySelectorAll('.contatto-checkbox:checked').forEach(function(cb) {
|
||||
selected.push({
|
||||
individuo_id: parseInt(cb.value),
|
||||
email: cb.dataset.email
|
||||
});
|
||||
});
|
||||
|
||||
if (selected.length === 0) {
|
||||
e.preventDefault();
|
||||
alert('Seleziona almeno un contatto con email');
|
||||
return;
|
||||
}
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'contatti_json';
|
||||
input.value = JSON.stringify(selected);
|
||||
this.appendChild(input);
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,179 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Mailing Lists')
|
||||
@section('page_title', 'Mailing Lists')
|
||||
|
||||
@php
|
||||
$canWriteMailing = Auth::user()->canManage('mailing');
|
||||
$canDeleteMailing = Auth::user()->canDelete('mailing');
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-list mr-2"></i>Elenco Mailing Lists</h3>
|
||||
<div class="card-tools">
|
||||
@if($canDeleteMailing)
|
||||
<div class="btn-group mr-2">
|
||||
<button type="button" class="btn btn-sm btn-danger" onclick="deleteSelected()">
|
||||
<i class="fas fa-trash mr-1"></i> Elimina Selezionati
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
@if($canWriteMailing)
|
||||
<a href="{{ route('mailing-liste.create') }}" class="btn btn-success btn-sm">
|
||||
<i class="fas fa-plus mr-1"></i> Nuova Lista
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if($mailingLists->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th style="width: 40px;">
|
||||
@if($canDeleteMailing)
|
||||
<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>
|
||||
@endif
|
||||
</th>
|
||||
<th>Nome</th>
|
||||
<th>Descrizione</th>
|
||||
<th style="width: 100px;">Contatti</th>
|
||||
<th style="width: 100px;">Stato</th>
|
||||
<th style="width: 130px;">Creata il</th>
|
||||
<th style="width: 100px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($mailingLists as $lista)
|
||||
<tr data-id="{{ $lista->id }}">
|
||||
<td>
|
||||
@if($canDeleteMailing)
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input row-checkbox" id="row-checkbox-{{ $lista->id }}" value="{{ $lista->id }}">
|
||||
<label class="custom-control-label" for="row-checkbox-{{ $lista->id }}"></label>
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ route('mailing-liste.show', $lista->id) }}">{{ $lista->nome }}</a>
|
||||
</td>
|
||||
<td>{{ $lista->descrizione ?: '-' }}</td>
|
||||
<td>
|
||||
<span class="badge badge-info">{{ $lista->contatti->count() }}</span>
|
||||
</td>
|
||||
<td>
|
||||
@if($lista->attiva)
|
||||
<span class="badge badge-success">Attiva</span>
|
||||
@else
|
||||
<span class="badge badge-secondary">Disattiva</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $lista->created_at->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
<a href="{{ route('mailing-liste.show', $lista->id) }}" class="btn btn-xs btn-info" title="Visualizza">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
@if($canWriteMailing)
|
||||
<a href="{{ route('mailing-liste.edit', $lista->id) }}" class="btn btn-xs btn-warning" title="Modifica">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<form action="{{ route('mailing-liste.destroy', $lista->id) }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questa lista?')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-list fa-3x mb-3"></i>
|
||||
<p>Nessuna mailing list</p>
|
||||
@if($canWriteMailing)
|
||||
<a href="{{ route('mailing-liste.create') }}" class="btn btn-success">Crea la prima lista</a>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="deleteModal" tabindex="-1" role="dialog">
|
||||
@if($canDeleteMailing)
|
||||
<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> mailing list selezionate.</p>
|
||||
<p class="text-muted small">Verranno eliminati anche tutti i contatti associati.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annulla</button>
|
||||
<button type="button" class="btn btn-danger" onclick="confirmDelete()">Elimina</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
function toggleSelectAll(source) {
|
||||
document.querySelectorAll('.row-checkbox').forEach(cb => cb.checked = source.checked);
|
||||
}
|
||||
|
||||
function getSelectedIds() {
|
||||
return Array.from(document.querySelectorAll('.row-checkbox:checked')).map(cb => cb.value);
|
||||
}
|
||||
|
||||
function deleteSelected() {
|
||||
const selectedIds = getSelectedIds();
|
||||
if (selectedIds.length === 0) {
|
||||
alert('Seleziona almeno una mailing list');
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('delete-count').textContent = selectedIds.length;
|
||||
|
||||
document.querySelector('#deleteModal .btn-danger').onclick = function() {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '/mailing-liste/mass-elimina';
|
||||
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '{{ csrf_token() }}';
|
||||
|
||||
let html = `<input type="hidden" name="_token" value="${csrfToken}">`;
|
||||
selectedIds.forEach(function(id) {
|
||||
html += `<input type="hidden" name="ids[]" value="${id}">`;
|
||||
});
|
||||
|
||||
form.innerHTML = html;
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
};
|
||||
|
||||
$('#deleteModal').modal('show');
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,103 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', $mailingList->nome)
|
||||
@section('page_title', $mailingList->nome)
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-info-circle mr-2"></i>Dettagli Lista</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-4">Nome</dt>
|
||||
<dd class="col-sm-8">{{ $mailingList->nome }}</dd>
|
||||
|
||||
<dt class="col-sm-4">Descrizione</dt>
|
||||
<dd class="col-sm-8">{{ $mailingList->descrizione ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-4">Stato</dt>
|
||||
<dd class="col-sm-8">
|
||||
@if($mailingList->attiva)
|
||||
<span class="badge badge-success">Attiva</span>
|
||||
@else
|
||||
<span class="badge badge-secondary">Disattiva</span>
|
||||
@endif
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-4">Creata</dt>
|
||||
<dd class="col-sm-8">{{ $mailingList->created_at ? $mailingList->created_at->format('d/m/Y H:i') : '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-4">Creato da</dt>
|
||||
<dd class="col-sm-8">{{ $mailingList->user?->name ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-4">Contatti</dt>
|
||||
<dd class="col-sm-8">
|
||||
<span class="badge badge-info">{{ $mailingList->contatti->count() }}</span>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<a href="{{ route('mailing-liste.edit', $mailingList->id) }}" class="btn btn-warning btn-sm">
|
||||
<i class="fas fa-edit mr-1"></i> Modifica
|
||||
</a>
|
||||
<form action="{{ route('mailing-liste.destroy', $mailingList->id) }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Eliminare questa lista?')">
|
||||
<i class="fas fa-trash mr-1"></i> Elimina
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-users mr-2"></i>Contatti ({{ $mailingList->contatti->count() }})</h3>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if($mailingList->contatti->count() > 0)
|
||||
<table class="table table-bordered table-hover mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Codice</th>
|
||||
<th>Nome</th>
|
||||
<th>Email</th>
|
||||
<th>Stato</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($mailingList->contatti as $contact)
|
||||
@if($contact->individuo)
|
||||
<tr>
|
||||
<td><span class="badge badge-secondary">{{ $contact->individuo->codice_id }}</span></td>
|
||||
<td>{{ $contact->individuo->cognome }} {{ $contact->individuo->nome }}</td>
|
||||
<td>
|
||||
@php
|
||||
$email = $contact->individuo->contatti->where('tipo', 'email')->first()?->valore;
|
||||
@endphp
|
||||
{{ $email ?: '-' }}
|
||||
</td>
|
||||
<td>
|
||||
@if($contact->opt_in)
|
||||
<span class="badge badge-success">Iscritto</span>
|
||||
@else
|
||||
<span class="badge badge-danger">Disiscritto</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<p>Nessun contatto in questa lista</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,165 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Invio Mailing')
|
||||
@section('page_title', 'Invio Mailing')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-paper-plane mr-2"></i>Composizione Messaggio</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('mailing.invio.elabora') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<div class="form-group">
|
||||
<label>Liste Destinatari *</label>
|
||||
<select name="liste[]" id="liste" class="form-control" multiple required size="4">
|
||||
@forelse($liste as $lista)
|
||||
<option value="{{ $lista->id }}">{{ $lista->nome }} ({{ $lista->contatti->count() }} contatti)</option>
|
||||
@empty
|
||||
<option value="">Nessuna lista disponibile</option>
|
||||
@endforelse
|
||||
</select>
|
||||
<small class="text-muted">Seleziona una o più liste (tieni premuto Ctrl per selezioni multiple)</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="oggetto">Oggetto *</label>
|
||||
<input type="text" name="oggetto" id="oggetto" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="corpo">Messaggio *</label>
|
||||
<textarea name="corpo" id="corpo" class="form-control" rows="12" required></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Allegati</label>
|
||||
<div class="border rounded p-3 bg-light">
|
||||
<div class="mb-3">
|
||||
<button type="button" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#documentiModal">
|
||||
<i class="fas fa-folder-open mr-1"></i> Seleziona da Documenti
|
||||
</button>
|
||||
<label class="btn btn-sm btn-success mb-0">
|
||||
<i class="fas fa-upload mr-1"></i> Carica nuovo file
|
||||
<input type="file" name="allegato" id="allegato" class="d-none" onchange="handleFileUpload(this)">
|
||||
</label>
|
||||
</div>
|
||||
<div id="allegati-list"></div>
|
||||
<input type="hidden" name="documenti_selezionati" id="documenti_selezionati" value="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-paper-plane mr-1"></i> Prepara Invio
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="documentiModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-folder-open mr-2"></i>Seleziona Documenti</h5>
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@if($documenti->count() > 0)
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40px;"></th>
|
||||
<th>Nome File</th>
|
||||
<th>Tipologia</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($documenti as $doc)
|
||||
<tr>
|
||||
<td>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input doc-checkbox" id="doc-{{ $doc->id }}" value="{{ $doc->id }}" data-nome="{{ $doc->nome_file }}">
|
||||
<label class="custom-control-label" for="doc-{{ $doc->id }}"></label>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ $doc->nome_file }}</td>
|
||||
<td><span class="badge badge-info">{{ $doc->tipologia ?: $doc->tipo }}</span></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted">Nessun documento disponibile.</p>
|
||||
@endif
|
||||
</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="aggiungiDocumenti()">
|
||||
<i class="fas fa-plus mr-1"></i> Aggiungi
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
let allegatiSelezionati = [];
|
||||
|
||||
function aggiungiDocumenti() {
|
||||
const checkboxes = document.querySelectorAll('.doc-checkbox:checked');
|
||||
checkboxes.forEach(function(cb) {
|
||||
const id = parseInt(cb.value);
|
||||
const nome = cb.dataset.nome;
|
||||
|
||||
if (!allegatiSelezionati.includes(id)) {
|
||||
allegatiSelezionati.push(id);
|
||||
|
||||
const container = document.getElementById('allegati-list');
|
||||
const div = document.createElement('div');
|
||||
div.className = 'd-flex align-items-center justify-content-between bg-white p-2 mb-2 rounded';
|
||||
div.id = 'allegato-' + id;
|
||||
div.innerHTML = '<span><i class="fas fa-file mr-2"></i>' + nome + '</span>' +
|
||||
'<button type="button" class="btn btn-xs btn-danger" onclick="removeAllegato(' + id + ')">' +
|
||||
'<i class="fas fa-times"></i></button>';
|
||||
container.appendChild(div);
|
||||
}
|
||||
|
||||
cb.checked = false;
|
||||
});
|
||||
|
||||
document.getElementById('documenti_selezionati').value = allegatiSelezionati.join(',');
|
||||
$('#documentiModal').modal('hide');
|
||||
}
|
||||
|
||||
function removeAllegato(id) {
|
||||
allegatiSelezionati = allegatiSelezionati.filter(function(a) { return a !== id; });
|
||||
const el = document.getElementById('allegato-' + id);
|
||||
if (el) el.remove();
|
||||
document.getElementById('documenti_selezionati').value = allegatiSelezionati.join(',');
|
||||
}
|
||||
|
||||
function handleFileUpload(input) {
|
||||
if (input.files && input.files[0]) {
|
||||
const file = input.files[0];
|
||||
const container = document.getElementById('allegati-list');
|
||||
const div = document.createElement('div');
|
||||
div.className = 'd-flex align-items-center justify-content-between bg-white p-2 mb-2 rounded';
|
||||
div.innerHTML = '<span><i class="fas fa-file mr-2"></i>' + file.name + ' (' + (file.size / 1024).toFixed(1) + ' KB)</span>' +
|
||||
'<button type="button" class="btn btn-xs btn-danger" onclick="this.parentElement.remove(); input.value=\'\';">' +
|
||||
'<i class="fas fa-times"></i></button>';
|
||||
container.appendChild(div);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,244 @@
|
||||
@extends('layouts.adminlte')
|
||||
|
||||
@section('title', 'Nuovo Messaggio')
|
||||
@section('page_title', 'Nuovo Messaggio Email')
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="card card-primary">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-envelope mr-2"></i>Componi Messaggio</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('mailing.invia') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
@if($individui->count() > 0)
|
||||
<div class="form-group" id="destinatari-form-group">
|
||||
<label>Destinatari selezionati</label>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Codice</th>
|
||||
<th>Nome</th>
|
||||
<th>Email</th>
|
||||
<th style="width: 50px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="destinatari-tbody">
|
||||
@foreach($individui as $ind)
|
||||
<tr id="dest-{{ $ind->id }}">
|
||||
<td><span class="badge badge-secondary">{{ $ind->codice_id }}</span></td>
|
||||
<td><strong>{{ $ind->cognito }}</strong> {{ $ind->nome }}</td>
|
||||
<td>{{ $ind->contatti->where('tipo', 'email')->first()?->valore ?: 'Nessuna email' }}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-xs btn-outline-danger" onclick="removeDestinatario({{ $ind->id }})" title="Rimuovi">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<input type="hidden" name="destinatari_ids" id="destinatari_ids" value="{{ $individui->pluck('id')->join(',') }}">
|
||||
<small class="text-muted" id="destinatari-count">{{ $individui->count() }} destinatari</small>
|
||||
</div>
|
||||
@else
|
||||
<div class="form-group">
|
||||
<label for="lista_id">Lista Destinatari</label>
|
||||
<select name="lista_id" id="lista_id" class="form-control">
|
||||
<option value="">Seleziona lista...</option>
|
||||
@forelse($liste as $lista)
|
||||
<option value="{{ $lista->id }}">{{ $lista->nome }}</option>
|
||||
@empty
|
||||
<option value="">Nessuna lista disponibile</option>
|
||||
@endforelse
|
||||
</select>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="form-group">
|
||||
<label for="oggetto">Oggetto *</label>
|
||||
<input type="text" name="oggetto" id="oggetto" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="corpo">Messaggio *</label>
|
||||
<textarea name="corpo" id="corpo" class="form-control" rows="10" required></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Allegati</label>
|
||||
<div class="border rounded p-3">
|
||||
<div class="mb-3">
|
||||
<button type="button" class="btn btn-sm btn-primary" data-toggle="modal" data-target="#documentiModal">
|
||||
<i class="fas fa-folder-open mr-1"></i> Seleziona da Documenti
|
||||
</button>
|
||||
<label class="btn btn-sm btn-success mb-0">
|
||||
<i class="fas fa-upload mr-1"></i> Carica nuovo file
|
||||
<input type="file" name="allegato" id="allegato" class="d-none" onchange="handleFileUpload(this)">
|
||||
</label>
|
||||
</div>
|
||||
<div id="allegati-list">
|
||||
@if(!empty($documentiSelezionati))
|
||||
@foreach($documentiSelezionati as $doc)
|
||||
<div class="d-flex align-items-center justify-content-between bg-light p-2 mb-2 rounded" id="allegato-{{ $doc->id }}">
|
||||
<span><i class="fas fa-file mr-2"></i>{{ $doc->nome_file }}</span>
|
||||
<button type="button" class="btn btn-xs btn-danger" onclick="removeAllegato({{ $doc->id }})">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
</div>
|
||||
<input type="hidden" name="documenti_selezionati" id="documenti_selezionati" value="">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-paper-plane mr-1"></i> Invia
|
||||
</button>
|
||||
<a href="{{ route('individui.index') }}" class="btn btn-secondary">
|
||||
Annulla
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="documentiModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-folder-open mr-2"></i>Seleziona Documenti</h5>
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@if($documenti->count() > 0)
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40px;"></th>
|
||||
<th>Nome File</th>
|
||||
<th>Tipologia</th>
|
||||
<th>Data</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($documenti as $doc)
|
||||
<tr>
|
||||
<td>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input doc-checkbox" id="doc-{{ $doc->id }}" value="{{ $doc->id }}" data-nome="{{ $doc->nome_file }}">
|
||||
<label class="custom-control-label" for="doc-{{ $doc->id }}"></label>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ $doc->nome_file }}</td>
|
||||
<td><span class="badge badge-info">{{ $doc->tipologia ?: $doc->tipo }}</span></td>
|
||||
<td>{{ $doc->created_at->format('d/m/Y') }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted">Nessun documento disponibile.</p>
|
||||
@endif
|
||||
</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="aggiungiDocumenti()">
|
||||
<i class="fas fa-plus mr-1"></i> Aggiungi
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
let allegatiSelezionati = [];
|
||||
|
||||
function removeDestinatario(id) {
|
||||
const row = document.getElementById('dest-' + id);
|
||||
if (!row) return;
|
||||
|
||||
row.remove();
|
||||
|
||||
const input = document.getElementById('destinatari_ids');
|
||||
if (input) {
|
||||
let ids = input.value.split(',').filter(function(i) {
|
||||
return i && parseInt(i) !== id;
|
||||
});
|
||||
input.value = ids.join(',');
|
||||
|
||||
const countEl = document.getElementById('destinatari-count');
|
||||
if (countEl) {
|
||||
countEl.textContent = ids.length + ' destinatari';
|
||||
}
|
||||
|
||||
if (ids.length === 0) {
|
||||
document.getElementById('destinatari-form-group').style.display = 'none';
|
||||
document.getElementById('lista-form-group').style.display = 'block';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function aggiungiDocumenti() {
|
||||
const checkboxes = document.querySelectorAll('.doc-checkbox:checked');
|
||||
checkboxes.forEach(function(cb) {
|
||||
const id = parseInt(cb.value);
|
||||
const nome = cb.dataset.nome;
|
||||
|
||||
if (!allegatiSelezionati.includes(id)) {
|
||||
allegatiSelezionati.push(id);
|
||||
|
||||
const container = document.getElementById('allegati-list');
|
||||
const div = document.createElement('div');
|
||||
div.className = 'd-flex align-items-center justify-content-between bg-light p-2 mb-2 rounded';
|
||||
div.id = 'allegato-' + id;
|
||||
div.innerHTML = '<span><i class="fas fa-file mr-2"></i>' + nome + '</span>' +
|
||||
'<button type="button" class="btn btn-xs btn-danger" onclick="removeAllegato(' + id + ')">' +
|
||||
'<i class="fas fa-times"></i></button>';
|
||||
container.appendChild(div);
|
||||
}
|
||||
|
||||
cb.checked = false;
|
||||
});
|
||||
|
||||
document.getElementById('documenti_esistenti').value = allegatiSelezionati.join(',');
|
||||
$('#documentiModal').modal('hide');
|
||||
}
|
||||
|
||||
function removeAllegato(id) {
|
||||
allegatiSelezionati = allegatiSelezionati.filter(function(a) { return a !== id; });
|
||||
const el = document.getElementById('allegato-' + id);
|
||||
if (el) el.remove();
|
||||
document.getElementById('documenti_selezionati').value = allegatiSelezionati.join(',');
|
||||
}
|
||||
|
||||
function handleFileUpload(input) {
|
||||
if (input.files && input.files[0]) {
|
||||
const file = input.files[0];
|
||||
const container = document.getElementById('allegati-list');
|
||||
const div = document.createElement('div');
|
||||
div.className = 'd-flex align-items-center justify-content-between bg-light p-2 mb-2 rounded';
|
||||
div.id = 'allegato-upload';
|
||||
div.innerHTML = '<span><i class="fas fa-file mr-2"></i>' + file.name + ' (' + (file.size / 1024).toFixed(1) + ' KB)</span>' +
|
||||
'<button type="button" class="btn btn-xs btn-danger" onclick="this.parentElement.remove(); input.value=\'\';">' +
|
||||
'<i class="fas fa-times"></i></button>';
|
||||
container.appendChild(div);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,161 @@
|
||||
<div class="modal fade" id="table-settings-modal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-cog mr-2"></i>Impostazioni Tabella</h5>
|
||||
<button type="button" class="close" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ul class="nav nav-tabs" id="settings-tabs" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="columns-tab" data-toggle="tab" href="#columns-panel" role="tab">
|
||||
<i class="fas fa-columns mr-1"></i> Colonne
|
||||
</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>
|
||||
</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>
|
||||
</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">
|
||||
@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>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</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>
|
||||
<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>
|
||||
</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>
|
||||
</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>
|
||||
</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>
|
||||
</div>
|
||||
</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);
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,249 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Report')
|
||||
@section('page_title', 'Report e Statistiche')
|
||||
|
||||
@section('content')
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger alert-dismissible">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-chart-bar mr-2"></i>Panoramica Database</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-2 col-4 text-center">
|
||||
<div class="info-box">
|
||||
<span class="info-box-icon bg-info"><i class="fas fa-users"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Individui</span>
|
||||
<span class="info-box-number">{{ number_format($stats['individui']) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2 col-4 text-center">
|
||||
<div class="info-box">
|
||||
<span class="info-box-icon bg-warning"><i class="fas fa-folder"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Gruppi</span>
|
||||
<span class="info-box-number">{{ number_format($stats['gruppi']) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2 col-4 text-center">
|
||||
<div class="info-box">
|
||||
<span class="info-box-icon bg-danger"><i class="fas fa-calendar"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Eventi</span>
|
||||
<span class="info-box-number">{{ number_format($stats['eventi']) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2 col-4 text-center">
|
||||
<div class="info-box">
|
||||
<span class="info-box-icon bg-secondary"><i class="fas fa-file"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Documenti</span>
|
||||
<span class="info-box-number">{{ number_format($stats['documenti']) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2 col-4 text-center">
|
||||
<div class="info-box">
|
||||
<span class="info-box-icon bg-purple"><i class="fas fa-list"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Mailing List</span>
|
||||
<span class="info-box-number">{{ number_format($stats['mailing_liste']) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2 col-4 text-center">
|
||||
<div class="info-box">
|
||||
<span class="info-box-icon bg-teal"><i class="fas fa-address-book"></i></span>
|
||||
<div class="info-box-content">
|
||||
<span class="info-box-text">Contatti</span>
|
||||
<span class="info-box-number">{{ number_format($stats['contatti']) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-chart-pie mr-2"></i>Report Predefiniti</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
@foreach($prebuiltReports as $report)
|
||||
<div class="col-md-4 col-lg-3 mb-3">
|
||||
<div class="card card-{{ $report['colore'] }} card-outline h-100">
|
||||
<div class="card-body text-center">
|
||||
<i class="fas {{ $report['icona'] }} fa-3x text-{{ $report['colore'] }} mb-3"></i>
|
||||
<h5>{{ $report['nome'] }}</h5>
|
||||
<p class="text-muted small mb-3">{{ $report['descrizione'] }}</p>
|
||||
<a href="{{ route('report.run', ['report' => $report['id']]) }}" class="btn btn-sm btn-{{ $report['colore'] }}">
|
||||
<i class="fas fa-play mr-1"></i> Genera
|
||||
</a>
|
||||
<a href="{{ route('report.export', ['report' => $report['id']]) }}" class="btn btn-sm btn-outline-{{ $report['colore'] }}">
|
||||
<i class="fas fa-download mr-1"></i> CSV
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-bookmark mr-2"></i>Report Personalizzati</h3>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if($customReports->count() > 0)
|
||||
<table class="table table-striped mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Tipo</th>
|
||||
<th>Creato il</th>
|
||||
<th style="width: 120px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($customReports as $report)
|
||||
<tr>
|
||||
<td>
|
||||
<i class="fas fa-chart-line text-primary mr-1"></i>
|
||||
{{ $report->nome }}
|
||||
</td>
|
||||
<td><span class="badge badge-info">{{ ucfirst($report->tipo_report) }}</span></td>
|
||||
<td>{{ $report->created_at->format('d/m/Y H:i') }}</td>
|
||||
<td>
|
||||
<a href="{{ route('report.run-custom', $report->id) }}" class="btn btn-xs btn-success" title="Esegui">
|
||||
<i class="fas fa-play"></i>
|
||||
</a>
|
||||
<form action="{{ route('report.destroy-custom', $report->id) }}" method="POST" class="d-inline">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo report?')" title="Elimina">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-chart-line fa-3x mb-3"></i>
|
||||
<p>Nessun report personalizzato creato.</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary">
|
||||
<h3 class="card-title text-white"><i class="fas fa-plus mr-2"></i>Crea Report Personalizzato</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('report.store-custom') }}" method="POST">
|
||||
@csrf
|
||||
<div class="form-group">
|
||||
<label>Nome Report *</label>
|
||||
<input type="text" name="nome" class="form-control" required placeholder="Es: Individui attivi">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Descrizione</label>
|
||||
<textarea name="descrizione" class="form-control" rows="2" placeholder="Descrizione opzionale..."></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Tipo Dato *</label>
|
||||
<select name="tipo_report" class="form-control" required>
|
||||
<option value="">Seleziona...</option>
|
||||
<option value="individui">Individui</option>
|
||||
<option value="gruppi">Gruppi</option>
|
||||
<option value="eventi">Eventi</option>
|
||||
<option value="documenti">Documenti</option>
|
||||
<option value="contatti">Contatti</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Colonne (separate da virgola)</label>
|
||||
<input type="text" name="columns" class="form-control" placeholder="Es: cognome, nome, email">
|
||||
<small class="text-muted">Lascia vuoto per tutte le colonne</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Ordina per</label>
|
||||
<select name="sort_by" class="form-control">
|
||||
<option value="">Nessun ordinamento</option>
|
||||
<option value="created_at">Data creazione</option>
|
||||
<option value="updated_at">Ultimo aggiornamento</option>
|
||||
<option value="nome">Nome</option>
|
||||
<option value="cognome">Cognome</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Limite risultati</label>
|
||||
<input type="number" name="limit" class="form-control" placeholder="Es: 100" min="1">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block">
|
||||
<i class="fas fa-save mr-1"></i> Salva Report
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tipoSelect = document.querySelector('select[name="tipo_report"]');
|
||||
const columnsInput = document.querySelector('input[name="columns"]');
|
||||
const sortBySelect = document.querySelector('select[name="sort_by"]');
|
||||
|
||||
const columns = {
|
||||
'individui': 'codice_id, cognome, nome, data_nascita, genere, email, telefono',
|
||||
'gruppi': 'nome, descrizione, diocesi_id',
|
||||
'eventi': 'nome_evento, data_inizio, tipo_recorrenza',
|
||||
'documenti': 'nome_file, tipologia, visibilita, created_at',
|
||||
'contatti': 'tipo, valore, etichetta',
|
||||
};
|
||||
|
||||
tipoSelect.addEventListener('change', function() {
|
||||
if (columns[this.value]) {
|
||||
columnsInput.placeholder = 'Suggerite: ' + columns[this.value];
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,68 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', $data['title'] ?? 'Report')
|
||||
@section('page_title', $data['title'] ?? 'Risultato Report')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-chart-bar mr-2"></i>{{ $data['title'] ?? 'Report' }}</h3>
|
||||
<div class="card-tools">
|
||||
<a href="{{ route('report.index') }}" class="btn btn-sm btn-secondary mr-2">
|
||||
<i class="fas fa-arrow-left mr-1"></i> Torna ai Report
|
||||
</a>
|
||||
<a href="{{ route('report.export', ['report' => $reportType]) }}" class="btn btn-sm btn-success">
|
||||
<i class="fas fa-file-csv mr-1"></i> Esporta CSV
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if(!empty($data['summary']))
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle mr-2"></i>
|
||||
<strong>{{ $data['summary'] }}</strong>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(!empty($data['headers']) && !empty($data['rows']))
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-striped table-hover">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
@foreach($data['headers'] as $header)
|
||||
<th>{{ $header }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($data['rows'] as $row)
|
||||
<tr>
|
||||
@foreach($row as $cell)
|
||||
<td>{{ is_array($cell) ? json_encode($cell) : $cell }}</td>
|
||||
@endforeach
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="fas fa-inbox fa-3x mb-3"></i>
|
||||
<p>Nessun dato disponibile per questo report.</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<a href="{{ route('report.index') }}" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left mr-1"></i> Torna ai Report
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6 text-right">
|
||||
<small class="text-muted">Generato il {{ now()->format('d/m/Y H:i:s') }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,44 @@
|
||||
@if ($paginator->hasPages())
|
||||
<ul class="pagination justify-content-center" role="navigation">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||
<span class="page-link" aria-hidden="true">‹</span>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Pagination Elements --}}
|
||||
@foreach ($elements as $element)
|
||||
{{-- "Three Dots" Separator --}}
|
||||
@if (is_string($element))
|
||||
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
|
||||
@endif
|
||||
|
||||
{{-- Array Of Links --}}
|
||||
@if (is_array($element))
|
||||
@foreach ($element as $page => $url)
|
||||
@if ($page == $paginator->currentPage())
|
||||
<li class="page-item active" aria-current="page"><span class="page-link">{{ $page }}</span></li>
|
||||
@elseif ($page >= $paginator->currentPage() - 2 && $page <= $paginator->currentPage() + 2)
|
||||
<li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">›</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||
<span class="page-link" aria-hidden="true">›</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
@endif
|
||||
@@ -0,0 +1,86 @@
|
||||
@extends('layouts.adminlte')
|
||||
@section('title', 'Viste Report')
|
||||
@section('page_title', 'Viste e Report Salvati')
|
||||
|
||||
@section('content')
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title"><i class="fas fa-table mr-2"></i>Le Tue Viste</h3>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if($viste->count() > 0)
|
||||
<table class="table table-bordered table-hover table-striped mb-0">
|
||||
<thead class="thead-light">
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th style="width: 120px;">Tipo</th>
|
||||
<th style="width: 100px;">Colonne</th>
|
||||
<th style="width: 150px;">Creata il</th>
|
||||
<th style="width: 120px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($viste as $vista)
|
||||
<tr>
|
||||
<td>
|
||||
@if($vista->is_default)
|
||||
<i class="fas fa-star text-warning mr-1" title="Predefinita"></i>
|
||||
@else
|
||||
<i class="fas fa-file-alt text-primary mr-1"></i>
|
||||
@endif
|
||||
{{ $vista->nome }}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-{{ $vista->tipo === 'individui' ? 'info' : ($vista->tipo === 'gruppi' ? 'warning' : ($vista->tipo === 'documenti' ? 'success' : 'primary')) }}">
|
||||
{{ ucfirst($vista->tipo) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
@if($vista->colonne_visibili)
|
||||
<span class="badge badge-secondary">{{ count($vista->colonne_visibili) }}</span>
|
||||
@else
|
||||
<span class="text-muted">Tutte</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $vista->created_at->format('d/m/Y H:i') }}</td>
|
||||
<td>
|
||||
<a href="{{ url('/' . $vista->tipo . '?vista_id=' . $vista->id) }}" class="btn btn-xs btn-primary" title="Applica">
|
||||
<i class="fas fa-play"></i>
|
||||
</a>
|
||||
@if(!$vista->is_default)
|
||||
<form action="{{ url('/viste/' . $vista->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>
|
||||
@else
|
||||
<span class="btn btn-xs btn-secondary" title="Predefinita">
|
||||
<i class="fas fa-check"></i>
|
||||
</span>
|
||||
@endif
|
||||
<form action="{{ url('/viste/' . $vista->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>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@else
|
||||
<div class="text-center text-muted py-4">
|
||||
<i class="fas fa-table fa-2x mb-2"></i>
|
||||
<p class="mb-0">Nessuna vista salvata.<br>Configura una tabella e salva la vista.</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 text-muted small">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
Per salvare una vista, configura le colonne, i filtri e l'ordinamento in una delle pagine elenco (Individui, Gruppi, Documenti, Eventi) e clicca su "Salva Vista".
|
||||
</div>
|
||||
@endsection
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user