2321 lines
130 KiB
PHP
2321 lines
130 KiB
PHP
@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="#repository" class="list-group-item list-group-item-action" data-toggle="tab">
|
|
<i class="nav-icon fas fa-cloud mr-2"></i> Repository Remoti
|
|
</a>
|
|
<a href="#documenti" class="list-group-item list-group-item-action" data-toggle="tab">
|
|
<i class="nav-icon fas fa-file mr-2"></i> Repository Documenti
|
|
</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="#email" class="list-group-item list-group-item-action" data-toggle="tab">
|
|
<i class="nav-icon fas fa-envelope mr-2"></i> Email
|
|
</a>
|
|
<a href="#calendario" class="list-group-item list-group-item-action" data-toggle="tab">
|
|
<i class="nav-icon fas fa-calendar-alt mr-2"></i> Calendario
|
|
</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="#tipologie-eventi" class="list-group-item list-group-item-action" data-toggle="tab">
|
|
<i class="nav-icon fas fa-calendar mr-2"></i> Tipologie Eventi
|
|
</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">
|
|
{{-- === APPLICAZIONE === --}}
|
|
<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
|
|
<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>
|
|
|
|
{{-- === REPOSITORY REMOTI (Opzione B) === --}}
|
|
<div class="tab-pane" id="repository">
|
|
<div class="card card-primary">
|
|
<div class="card-header">
|
|
<h3 class="card-title"><i class="fas fa-cloud mr-2"></i> Repository Remoti</h3>
|
|
<div class="card-tools">
|
|
<button type="button" class="btn btn-sm btn-success" data-toggle="modal" data-target="#repoModal" onclick="resetRepoForm()">
|
|
<i class="fas fa-plus mr-1"></i> Nuovo Repository
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-muted">Configura repository remoti (WebDAV, Google Drive) per archiviare documenti. Ogni repository appare come root separata nell'albero delle cartelle in Gestione Documenti.</p>
|
|
|
|
@if(session('success'))
|
|
<div class="alert alert-success">{{ session('success') }}</div>
|
|
@endif
|
|
@if(session('error'))
|
|
<div class="alert alert-danger">{{ session('error') }}</div>
|
|
@endif
|
|
|
|
@if($repositories->count() > 0)
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-hover table-sm" id="repoTable">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:40px;">#</th>
|
|
<th>Nome</th>
|
|
<th>Tipo</th>
|
|
<th>Stato</th>
|
|
<th style="width:180px;">Azioni</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="repoSortable">
|
|
@foreach($repositories as $repo)
|
|
<tr data-id="{{ $repo->id }}">
|
|
<td class="text-center handle" style="cursor:grab;"><i class="fas fa-grip-vertical text-muted"></i></td>
|
|
<td><strong>{{ $repo->nome }}</strong></td>
|
|
<td>
|
|
<span class="badge badge-{{ $repo->tipo === 'google_drive' ? 'danger' : 'secondary' }}">
|
|
<i class="fas {{ $repo->tipo === 'google_drive' ? 'fa-google-drive' : 'fa-server' }} mr-1"></i>
|
|
{{ \App\Models\StorageRepository::etichettaTipo($repo->tipo) }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
@if($repo->is_active)
|
|
<span class="badge badge-success">Attivo</span>
|
|
@else
|
|
<span class="badge badge-secondary">Disattivo</span>
|
|
@endif
|
|
</td>
|
|
<td>
|
|
<button type="button" class="btn btn-xs btn-info" onclick="editRepo({{ $repo->id }})" title="Modifica">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-xs btn-success test-repo-btn" onclick="testRepo({{ $repo->id }}, this)" title="Test connessione">
|
|
<i class="fas fa-plug"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-xs btn-danger" onclick="deleteRepo({{ $repo->id }}, '{{ addslashes($repo->nome) }}')" title="Elimina">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@else
|
|
<div class="text-center text-muted py-4">
|
|
<i class="fas fa-cloud fa-3x mb-3"></i>
|
|
<p class="mb-0">Nessun repository remoto configurato.</p>
|
|
<p class="mb-0">Clicca "Nuovo Repository" per aggiungere un server WebDAV o Google Drive.</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
@if($repositories->count() > 0)
|
|
<div class="card card-secondary">
|
|
<div class="card-header">
|
|
<h3 class="card-title"><i class="fas fa-info-circle mr-2"></i> Come funziona</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="mb-0">
|
|
<li>Ogni repository configurato apparirà come <strong>root separata</strong> nella sidebar <strong>Gestione Documenti</strong>, sotto "Repository Remoti".</li>
|
|
<li>Puoi navigare i file e le cartelle di ogni repository remoto direttamente dall'interfaccia documenti.</li>
|
|
<li><strong>WebDAV</strong>: supporta server che parlano il protocollo WebDAV (es. NextCloud, ownCloud, Synology).</li>
|
|
<li><strong>Google Drive</strong>: richiede OAuth 2.0 con client ID, client secret e refresh token configurati nella <a href="https://console.cloud.google.com/" target="_blank">Google Cloud Console</a>.</li>
|
|
<li>Usa il pulsante <i class="fas fa-plug text-success"></i> <strong>Test</strong> per verificare la connettività dopo la configurazione.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- === REPOSITORY DOCUMENTI === --}}
|
|
<div class="tab-pane" id="documenti">
|
|
<div class="card card-primary">
|
|
<div class="card-header">
|
|
<h3 class="card-title">Repository Documenti</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-muted">Configura il repository filesystem dove vengono salvati i documenti caricati.</p>
|
|
|
|
@php
|
|
$currentDisk = $appSettings->documenti_storage_disk ?? 'local';
|
|
$currentPath = $appSettings->documenti_storage_path ?? 'documenti';
|
|
$absPath = storage_path('app/' . ($currentDisk === 'local' ? 'private/' : '') . $currentPath);
|
|
@endphp
|
|
|
|
<form method="POST" action="{{ route('impostazioni.app-settings.save') }}">
|
|
@csrf
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="documenti_storage_disk">Disco di archiviazione</label>
|
|
<select name="documenti_storage_disk" id="documenti_storage_disk" class="form-control">
|
|
<option value="local" {{ $currentDisk === 'local' ? 'selected' : '' }}>Locale (storage/app/private)</option>
|
|
<option value="public" {{ $currentDisk === 'public' ? 'selected' : '' }}>Pubblico (storage/app/public)</option>
|
|
</select>
|
|
<small class="text-muted">Il disco predefinito per salvare i documenti</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="documenti_storage_path">Percorso relativo</label>
|
|
<input type="text" name="documenti_storage_path" id="documenti_storage_path" class="form-control"
|
|
value="{{ $currentPath }}" placeholder="es. documenti">
|
|
<small class="text-muted">Sottocartella all'interno del disco</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alert alert-info">
|
|
<i class="fas fa-info-circle mr-2"></i>
|
|
<strong>Percorso assoluto calcolato:</strong>
|
|
<code>{{ $absPath }}</code>
|
|
<br>
|
|
<small class="text-muted">I nuovi documenti verranno salvati in questa directory.</small>
|
|
</div>
|
|
|
|
@if(session('path_changed'))
|
|
@php
|
|
$migrateCount = session('migration_count', 0);
|
|
$oldD = session('old_storage_disk', 'local');
|
|
$oldP = session('old_storage_path', 'documenti');
|
|
$newD = session('new_storage_disk', 'local');
|
|
$newP = session('new_storage_path', 'documenti');
|
|
@endphp
|
|
<div class="alert alert-warning">
|
|
<i class="fas fa-exclamation-triangle mr-2"></i>
|
|
<strong>Percorso modificato!</strong>
|
|
<p class="mb-2">La nuova directory <code>{{ $newD }}://{{ $newP }}</code> è stata creata.
|
|
@if($migrateCount > 0)
|
|
Ci sono <strong>{{ $migrateCount }}</strong> file nella vecchia posizione <code>{{ $oldD }}://{{ $oldP }}</code>.</p>
|
|
<form method="POST" action="{{ route('impostazioni.migrate-percorso') }}" class="d-inline" onsubmit="return confirm('Spostare {{ $migrateCount }} file? Questa operazione potrebbe richiedere alcuni secondi.');">
|
|
@csrf
|
|
<input type="hidden" name="old_storage_disk" value="{{ $oldD }}">
|
|
<input type="hidden" name="old_storage_path" value="{{ $oldP }}">
|
|
<input type="hidden" name="new_storage_disk" value="{{ $newD }}">
|
|
<input type="hidden" name="new_storage_path" value="{{ $newP }}">
|
|
<button type="submit" class="btn btn-warning">
|
|
<i class="fas fa-exchange-alt mr-1"></i> Sposta {{ $migrateCount }} file nel nuovo percorso
|
|
</button>
|
|
<a href="/impostazioni#documenti" class="btn btn-secondary ml-2">
|
|
<i class="fas fa-times mr-1"></i> Ignora
|
|
</a>
|
|
</form>
|
|
@else
|
|
<p class="mb-0">Nessun file da spostare.</p>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-save mr-1"></i> Salva Impostazioni
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- === LOGO === --}}
|
|
<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>
|
|
|
|
{{-- === EMAIL === --}}
|
|
<div class="tab-pane" id="email">
|
|
<div class="card card-secondary">
|
|
<div class="card-header">
|
|
<h3 class="card-title">Sezioni Email</h3>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<ul class="nav nav-pills p-2">
|
|
<li class="nav-item"><a href="#email-imap" class="nav-link active" data-toggle="tab">Server IMAP</a></li>
|
|
<li class="nav-item"><a href="#email-smtp" class="nav-link" data-toggle="tab">Server SMTP</a></li>
|
|
<li class="nav-item"><a href="#email-account" class="nav-link" data-toggle="tab">Account</a></li>
|
|
<li class="nav-item"><a href="#email-sync" class="nav-link" data-toggle="tab">Sincronizzazione</a></li>
|
|
<li class="nav-item"><a href="#email-firma" class="nav-link" data-toggle="tab">Firma</a></li>
|
|
<li class="nav-item"><a href="#email-mittenti" class="nav-link" data-toggle="tab">Mittenti Aggiuntivi</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<form method="POST" action="{{ route('impostazioni.email.save') }}">
|
|
@csrf
|
|
<input type="hidden" name="id" value="{{ $emailSettings->id ?? 1 }}">
|
|
|
|
<div class="tab-content">
|
|
<div class="tab-pane active" id="email-imap">
|
|
<div class="card card-primary">
|
|
<div class="card-header">
|
|
<h3 class="card-title">Configurazione Server IMAP</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<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="{{ $emailSettings->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="{{ $emailSettings->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" {{ ($emailSettings->imap_encryption ?? 'ssl') == 'ssl' ? 'selected' : '' }}>SSL</option>
|
|
<option value="tls" {{ ($emailSettings->imap_encryption ?? '') == 'tls' ? 'selected' : '' }}>TLS</option>
|
|
<option value="none" {{ ($emailSettings->imap_encryption ?? '') == 'none' ? 'selected' : '' }}>Nessuna</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane" id="email-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="{{ $emailSettings->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="{{ $emailSettings->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" {{ ($emailSettings->smtp_encryption ?? 'tls') == 'tls' ? 'selected' : '' }}>TLS</option>
|
|
<option value="ssl" {{ ($emailSettings->smtp_encryption ?? '') == 'ssl' ? 'selected' : '' }}>SSL</option>
|
|
<option value="none" {{ ($emailSettings->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="{{ $emailSettings->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="{{ $emailSettings->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="email-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="{{ $emailSettings->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="{{ $emailSettings->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="{{ $emailSettings->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="{{ $emailSettings->reply_to ?? '' }}">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane" id="email-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" {{ ($emailSettings->sync_interval_minutes ?? 5) == 1 ? 'selected' : '' }}>1 minuto</option>
|
|
<option value="5" {{ ($emailSettings->sync_interval_minutes ?? 5) == 5 ? 'selected' : '' }}>5 minuti</option>
|
|
<option value="15" {{ ($emailSettings->sync_interval_minutes ?? 5) == 15 ? 'selected' : '' }}>15 minuti</option>
|
|
<option value="30" {{ ($emailSettings->sync_interval_minutes ?? 5) == 30 ? 'selected' : '' }}>30 minuti</option>
|
|
<option value="60" {{ ($emailSettings->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($emailSettings->last_sync_at)
|
|
<span class="badge badge-success">
|
|
<i class="fas fa-check-circle"></i> Ultimo sync: {{ $emailSettings->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="hidden" name="is_active" value="0">
|
|
<input type="checkbox" class="custom-control-input" id="is_active"
|
|
name="is_active" value="1" {{ ($emailSettings->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="email-firma">
|
|
<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="hidden" name="signature_enabled" value="0">
|
|
<input type="checkbox" class="custom-control-input" id="signature_enabled"
|
|
name="signature_enabled" value="1" {{ ($emailSettings->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="{{ $emailSettings->signature ?? '' }}">
|
|
<small class="text-muted">Usa il riquadro sopra per comporre la tua firma. HTML supportato.</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="tab-pane" id="email-mittenti">
|
|
<div class="card card-primary">
|
|
<div class="card-header">
|
|
<h3 class="card-title">Mittenti Aggiuntivi</h3>
|
|
<div class="card-tools">
|
|
<button type="button" class="btn btn-sm btn-success" data-toggle="modal" data-target="#senderModal" onclick="resetSenderForm()">
|
|
<i class="fas fa-plus mr-1"></i> Nuovo Mittente
|
|
</button>
|
|
</div>
|
|
</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
|
|
|
|
@if($senderAccounts->count() > 0)
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-hover table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th>Email</th>
|
|
<th>Nome</th>
|
|
<th>SMTP</th>
|
|
<th>Reply-To</th>
|
|
<th>Verify</th>
|
|
<th>Stato</th>
|
|
<th style="width: 160px;">Azioni</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($senderAccounts as $sender)
|
|
<tr>
|
|
<td><strong>{{ $sender->email_address }}</strong></td>
|
|
<td>{{ $sender->email_name ?: '-' }}</td>
|
|
<td><small>{{ $sender->smtp_host ?: '-' }}:{{ $sender->smtp_port ?: '-' }}</small></td>
|
|
<td><small class="text-muted">{{ $sender->reply_to ?: '-' }}</small></td>
|
|
<td><small class="text-muted">{{ $sender->verify_email ?: '-' }}</small></td>
|
|
<td>
|
|
@if($sender->is_active)
|
|
<span class="badge badge-success">Attivo</span>
|
|
@else
|
|
<span class="badge badge-secondary">Disattivo</span>
|
|
@endif
|
|
</td>
|
|
<td>
|
|
<button type="button" class="btn btn-xs btn-info" onclick="editSender({{ $sender->id }})" title="Modifica">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-xs btn-success" onclick="testSenderSmtp({{ $sender->id }})" title="Test SMTP">
|
|
<i class="fas fa-paper-plane"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-xs btn-danger" onclick="deleteSender({{ $sender->id }}, '{{ addslashes($sender->email_address) }}')" title="Elimina">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@else
|
|
<div class="text-center text-muted py-4">
|
|
<i class="fas fa-user-plus fa-2x mb-2"></i>
|
|
<p class="mb-0">Nessun mittente aggiuntivo configurato.</p>
|
|
<p class="mb-0">Usa il pulsante "Nuovo Mittente" per aggiungere un account di invio dedicato (es. Newsletter).</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
@if($senderAccounts->count() > 0)
|
|
<div class="card card-secondary">
|
|
<div class="card-header">
|
|
<h3 class="card-title"><i class="fas fa-info-circle mr-2"></i>Come funziona</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="mb-0">
|
|
<li>I mittenti aggiuntivi sono account <strong>solo invio</strong> (SMTP) senza IMAP.</li>
|
|
<li>Puoi selezionarli nel <strong>compose email</strong> o negli <strong>invii mailing</strong>.</li>
|
|
<li>Il campo <strong>Reply-To</strong> imposta l'indirizzo di risposta (es. <code>no-reply@parrocchia.it</code>).</li>
|
|
<li>Il campo <strong>Verify Email</strong> riceve un report di riepilogo dopo ogni invio massivo con l'esito (ok/falliti).</li>
|
|
<li>Se Reply-To non è impostato, viene usato Verify Email come fallback.</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-footer d-flex flex-wrap align-items-center gap-2">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-save mr-1"></i> Salva Impostazioni Email
|
|
</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 class="d-flex justify-content-end mt-2">
|
|
<form action="{{ route('impostazioni.email.destroy') }}" method="POST" onsubmit="return confirm('Eliminare definitivamente la configurazione email? Tutti i dati di connessione verranno rimossi.')">
|
|
@csrf @method('DELETE')
|
|
<button type="submit" class="btn btn-danger btn-sm">
|
|
<i class="fas fa-trash mr-1"></i> Elimina Configurazione
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- === CALENDARIO === --}}
|
|
<div class="tab-pane" id="calendario">
|
|
<div class="card card-primary">
|
|
<div class="card-header">
|
|
<h3 class="card-title"><i class="fas fa-calendar-alt mr-2"></i> Calendari Remoti</h3>
|
|
<div class="card-tools">
|
|
<button type="button" class="btn btn-sm btn-success" data-toggle="modal" data-target="#calendarioModal" onclick="resetCalendarioForm()">
|
|
<i class="fas fa-plus mr-1"></i> Nuova Connessione
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-muted">Configura la sincronizzazione con calendari esterni (Google Calendar, CalDAV come Infomaniak/NextCloud).</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
|
|
|
|
@php
|
|
$calendarioConnessioni = \App\Models\CalendarioConnessione::orderBy('ordine')->get();
|
|
@endphp
|
|
|
|
@if($calendarioConnessioni->count() > 0)
|
|
<div class="table-responsive">
|
|
<table class="table table-bordered table-hover table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:40px;">#</th>
|
|
<th>Nome</th>
|
|
<th>Tipo</th>
|
|
<th>Direzione Sync</th>
|
|
<th>Stato</th>
|
|
<th>Ultimo Sync</th>
|
|
<th style="width:200px;">Azioni</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="calendarioSortable">
|
|
@foreach($calendarioConnessioni as $cal)
|
|
<tr data-id="{{ $cal->id }}">
|
|
<td class="text-center handle" style="cursor:grab;"><i class="fas fa-grip-vertical text-muted"></i></td>
|
|
<td><strong>{{ $cal->nome }}</strong></td>
|
|
<td>
|
|
<span class="badge badge-{{ $cal->tipo === 'google_calendar' ? 'danger' : 'info' }}">
|
|
<i class="fas {{ $cal->tipo === 'google_calendar' ? 'fa-google' : 'fa-calendar-alt' }} mr-1"></i>
|
|
{{ \App\Models\CalendarioConnessione::etichettaTipo($cal->tipo) }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
@switch($cal->sync_direction)
|
|
@case('import') <span class="badge badge-primary"><i class="fas fa-download mr-1"></i> Solo Import</span> @break
|
|
@case('export') <span class="badge badge-warning"><i class="fas fa-upload mr-1"></i> Solo Export</span> @break
|
|
@default <span class="badge badge-success"><i class="fas fa-exchange-alt mr-1"></i> Bidirezionale</span>
|
|
@endswitch
|
|
</td>
|
|
<td>
|
|
@if($cal->stato === 'connesso')
|
|
<span class="badge badge-success"><i class="fas fa-check-circle mr-1"></i> Connesso</span>
|
|
@elseif($cal->stato === 'errore')
|
|
<span class="badge badge-danger" title="{{ $cal->last_error_message ?? '' }}"><i class="fas fa-exclamation-circle mr-1"></i> Errore</span>
|
|
@else
|
|
<span class="badge badge-secondary"><i class="fas fa-circle mr-1"></i> {{ ucfirst($cal->stato) }}</span>
|
|
@endif
|
|
</td>
|
|
<td>
|
|
@if($cal->last_sync_at)
|
|
<small class="text-muted">{{ $cal->last_sync_at->format('d/m/Y H:i') }}</small>
|
|
@else
|
|
<small class="text-muted">Mai</small>
|
|
@endif
|
|
</td>
|
|
<td>
|
|
<button type="button" class="btn btn-xs btn-info" onclick="editCalendario({{ $cal->id }})" title="Modifica">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-xs btn-success" onclick="testCalendario({{ $cal->id }}, this)" title="Test Connessione">
|
|
<i class="fas fa-plug"></i>
|
|
</button>
|
|
<form action="{{ route('calendario-connessioni.sync', $cal->id) }}" method="POST" class="d-inline" onsubmit="return confirm('Avviare la sincronizzazione ora?')">
|
|
@csrf
|
|
<button type="submit" class="btn btn-xs btn-primary" title="Sincronizza Ora">
|
|
<i class="fas fa-sync"></i>
|
|
</button>
|
|
</form>
|
|
<button type="button" class="btn btn-xs btn-danger" onclick="deleteCalendario({{ $cal->id }}, '{{ addslashes($cal->nome) }}')" title="Elimina">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@else
|
|
<div class="text-center text-muted py-4">
|
|
<i class="fas fa-calendar-plus fa-3x mb-3"></i>
|
|
<p class="mb-0">Nessun calendario remoto configurato.</p>
|
|
<p class="mb-0">Clicca "Nuova Connessione" per collegare Google Calendar o un server CalDAV (Infomaniak, NextCloud).</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- === TIPOLOGIE DOCUMENTI === --}}
|
|
<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>
|
|
|
|
{{-- === TIPOLOGIE EVENTI === --}}
|
|
<div class="tab-pane" id="tipologie-eventi">
|
|
<div class="card card-primary">
|
|
<div class="card-header">
|
|
<h3 class="card-title">Tipologie Eventi</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-muted">Gestisci le tipologie disponibili per gli eventi del calendario.</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="#addTipologiaEventoModal">
|
|
<i class="fas fa-plus"></i> Nuova Tipologia
|
|
</button>
|
|
</div>
|
|
|
|
<table class="table table-bordered table-striped" id="tipologie-eventi-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-eventi-sortable">
|
|
@foreach($tipologieEventi 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-evento-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="editTipologiaEvento({{ $tipologia->id }}, '{{ addslashes($tipologia->nome) }}', '{{ addslashes($tipologia->descrizione ?? '') }}', {{ $tipologia->attiva ? 'true' : 'false' }})">
|
|
<i class="fas fa-edit"></i>
|
|
</button>
|
|
@if($tipologia->eventi()->count() == 0)
|
|
<form method="POST" action="{{ route('impostazioni.tipologie-eventi.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="Eventi associati: {{ $tipologia->eventi()->count() }}">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- === TIPI DI RUOLO === --}}
|
|
<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>
|
|
|
|
{{-- ===== MODALI TIPOLOGIE DOCUMENTI ===== --}}
|
|
<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>
|
|
|
|
{{-- ===== MODALI TIPI DI RUOLO ===== --}}
|
|
<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>
|
|
|
|
{{-- ===== MODALI TIPOLOGIE EVENTI ===== --}}
|
|
<div class="modal fade" id="addTipologiaEventoModal">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form method="POST" action="{{ route('impostazioni.tipologie-eventi.store') }}">
|
|
@csrf
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">Nuova Tipologia Evento</h4>
|
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="tipologia_evento_nome">Nome *</label>
|
|
<input type="text" name="nome" id="tipologia_evento_nome" class="form-control" required maxlength="50" placeholder="es. catechesi">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="tipologia_evento_descrizione">Descrizione</label>
|
|
<input type="text" name="descrizione" id="tipologia_evento_descrizione" class="form-control" maxlength="255" placeholder="es. Catechesi">
|
|
</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="editTipologiaEventoModal">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<form method="POST" action="" id="editTipologiaEventoForm">
|
|
@csrf
|
|
@method('PUT')
|
|
<input type="hidden" name="id" id="edit_tipologia_evento_id">
|
|
<div class="modal-header">
|
|
<h4 class="modal-title">Modifica Tipologia Evento</h4>
|
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="edit_tipologia_evento_nome">Nome *</label>
|
|
<input type="text" name="nome" id="edit_tipologia_evento_nome" class="form-control" required maxlength="50">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit_tipologia_evento_descrizione">Descrizione</label>
|
|
<input type="text" name="descrizione" id="edit_tipologia_evento_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_evento_attiva" name="attiva" value="1">
|
|
<label class="custom-control-label" for="edit_tipologia_evento_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>
|
|
|
|
{{-- ===== MODALE MITTENTI AGGIUNTIVI ===== --}}
|
|
<div class="modal fade" id="senderModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<form id="senderForm" method="POST" action="{{ route('impostazioni.sender.store') }}">
|
|
@csrf
|
|
<input type="hidden" name="_method" id="senderMethod" value="POST">
|
|
<input type="hidden" name="id" id="senderId" value="">
|
|
<div class="modal-header bg-primary">
|
|
<h5 class="modal-title" id="senderModalTitle"><i class="fas fa-user-plus mr-2"></i>Nuovo Mittente</h5>
|
|
<button type="button" class="close text-white" data-dismiss="modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="sender_email">Email *</label>
|
|
<input type="email" name="email_address" id="sender_email" class="form-control" required placeholder="newsletter@parrocchia.it">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="sender_name">Nome visualizzato</label>
|
|
<input type="text" name="email_name" id="sender_name" class="form-control" placeholder="Newsletter Parrocchiale">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<hr>
|
|
<h6>Configurazione SMTP</h6>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="sender_smtp_host">Host SMTP</label>
|
|
<input type="text" name="smtp_host" id="sender_smtp_host" class="form-control" placeholder="smtp.gmail.com">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="form-group">
|
|
<label for="sender_smtp_port">Porta</label>
|
|
<input type="number" name="smtp_port" id="sender_smtp_port" class="form-control" value="587">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="form-group">
|
|
<label for="sender_smtp_encryption">Crittografia</label>
|
|
<select name="smtp_encryption" id="sender_smtp_encryption" class="form-control">
|
|
<option value="tls">TLS</option>
|
|
<option value="ssl">SSL</option>
|
|
<option value="none">Nessuna</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="sender_smtp_username">Username SMTP</label>
|
|
<input type="text" name="smtp_username" id="sender_smtp_username" class="form-control" placeholder="Lascia vuoto per usare l'email">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="sender_smtp_password">Password SMTP</label>
|
|
<input type="password" name="smtp_password" id="sender_smtp_password" class="form-control" placeholder="......">
|
|
<small class="text-muted">Lascia vuoto per non cambiare</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<hr>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="sender_reply_to">Reply-To <small class="text-muted">(no-reply)</small></label>
|
|
<input type="email" name="reply_to" id="sender_reply_to" class="form-control" placeholder="no-reply@parrocchia.it">
|
|
<small class="text-muted">Indirizzo di risposta per le email inviate</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="sender_verify_email">Verify Email <small class="text-muted">(report)</small></label>
|
|
<input type="email" name="verify_email" id="sender_verify_email" class="form-control" placeholder="verifica@parrocchia.it">
|
|
<small class="text-muted">Riceve report riepilogo dopo invii massivi</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="sender_note">Nota interna</label>
|
|
<textarea name="note" id="sender_note" class="form-control" rows="2" placeholder="Es. Newsletter settimanale"></textarea>
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="custom-control custom-switch">
|
|
<input type="checkbox" class="custom-control-input" id="sender_is_active" name="is_active" value="1" checked>
|
|
<label class="custom-control-label" for="sender_is_active">Mittente attivo</label>
|
|
</div>
|
|
</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-save mr-1"></i> Salva Mittente
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- ===== MODALE CONNESSIONE CALENDARIO ===== --}}
|
|
<div class="modal fade" id="calendarioModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
<div class="modal-content">
|
|
<form id="calendarioForm" method="POST" action="{{ route('calendario-connessioni.store') }}">
|
|
@csrf
|
|
<input type="hidden" name="_method" id="calendarioMethod" value="POST">
|
|
<input type="hidden" name="id" id="calendarioId" value="">
|
|
<div class="modal-header bg-primary">
|
|
<h5 class="modal-title" id="calendarioModalTitle"><i class="fas fa-calendar-plus mr-2"></i>Nuova Connessione Calendario</h5>
|
|
<button type="button" class="close text-white" data-dismiss="modal">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="cal_nome">Nome *</label>
|
|
<input type="text" name="nome" id="cal_nome" class="form-control" required placeholder="es. Calendario Parrocchia">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="cal_tipo">Tipo *</label>
|
|
<select name="tipo" id="cal_tipo" class="form-control" required onchange="toggleCalendarioConfigFields()">
|
|
<option value="caldav">CalDAV (Infomaniak/NextCloud)</option>
|
|
<option value="google_calendar">Google Calendar</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="cal_sync_direction">Direzione Sync</label>
|
|
<select name="sync_direction" id="cal_sync_direction" class="form-control">
|
|
<option value="bidirectional">Bidirezionale</option>
|
|
<option value="import">Solo Import (da remoto a locale)</option>
|
|
<option value="export">Solo Export (da locale a remoto)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="cal_sync_interval">Intervallo Sync (minuti)</label>
|
|
<select name="sync_interval_minutes" id="cal_sync_interval" class="form-control">
|
|
<option value="5">5 minuti</option>
|
|
<option value="15">15 minuti</option>
|
|
<option value="30">30 minuti</option>
|
|
<option value="60" selected>1 ora</option>
|
|
<option value="360">6 ore</option>
|
|
<option value="1440">24 ore</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="custom-control custom-switch">
|
|
<input type="checkbox" class="custom-control-input" id="cal_is_active" name="is_active" value="1" checked>
|
|
<label class="custom-control-label" for="cal_is_active">Connessione attiva</label>
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
<h6>Configurazione</h6>
|
|
|
|
{{-- CalDAV Fields --}}
|
|
<div id="caldavFields">
|
|
<div class="form-group">
|
|
<label for="cal_caldav_url">URL Server CalDAV *</label>
|
|
<input type="url" name="config[url]" id="cal_caldav_url" class="form-control" placeholder="es. https://nextcloud.parrocchia.it/remote.php/dav/calendars/utente/">
|
|
<small class="text-muted">URL principale del server CalDAV (es. NextCloud, Infomaniak, Synology)</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="cal_caldav_calendar_url">URL Calendario (opzionale)</label>
|
|
<input type="url" name="config[calendar_url]" id="cal_caldav_calendar_url" class="form-control" placeholder="Lascia vuoto per usare l'URL principale">
|
|
<small class="text-muted">URL specifico del calendario, se diverso dall'URL del server</small>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="cal_caldav_username">Username</label>
|
|
<input type="text" name="config[username]" id="cal_caldav_username" class="form-control">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="cal_caldav_password">Password / App Password</label>
|
|
<input type="password" name="config[password]" id="cal_caldav_password" class="form-control">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Google Calendar Fields --}}
|
|
<div id="googleCalendarFields" style="display:none;">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="cal_gc_client_id">Client ID *</label>
|
|
<input type="text" name="config[client_id]" id="cal_gc_client_id" class="form-control" placeholder="Google Client ID">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="cal_gc_client_secret">Client Secret *</label>
|
|
<input type="password" name="config[client_secret]" id="cal_gc_client_secret" class="form-control" placeholder="Google Client Secret">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="cal_gc_refresh_token">Refresh Token</label>
|
|
<input type="text" name="config[refresh_token]" id="cal_gc_refresh_token" class="form-control" placeholder="Generato dopo l'autorizzazione OAuth">
|
|
<small class="text-muted">Dopo aver configurato Client ID e Client Secret, salva la connessione e usa il test per verificare.</small>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="cal_gc_calendar_id">Calendar ID</label>
|
|
<input type="text" name="config[calendar_id]" id="cal_gc_calendar_id" class="form-control" value="primary" placeholder="primary">
|
|
<small class="text-muted">ID del calendario Google (default: primary per il calendario principale)</small>
|
|
</div>
|
|
<div class="alert alert-info">
|
|
<i class="fas fa-info-circle mr-2"></i>
|
|
<strong>Google Calendar:</strong> Crea il progetto in <a href="https://console.cloud.google.com/" target="_blank">Google Cloud Console</a>
|
|
→ API & Services → Credentials → Crea OAuth Client ID (Web Application).
|
|
Abilita <strong>Google Calendar API</strong> e configura l'URI di redirect.
|
|
</div>
|
|
</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-save mr-1"></i> Salva Connessione
|
|
</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 activateTabFromHash() {
|
|
var hash = window.location.hash;
|
|
if (hash) {
|
|
var tabLink = document.querySelector('.list-group a[href="' + hash + '"]');
|
|
if (tabLink) {
|
|
$(tabLink).tab('show');
|
|
}
|
|
}
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
activateTabFromHash();
|
|
});
|
|
|
|
$(window).on('hashchange', function() {
|
|
activateTabFromHash();
|
|
});
|
|
|
|
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');
|
|
}
|
|
|
|
function editTipologiaEvento(id, nome, descrizione, attiva) {
|
|
$('#edit_tipologia_evento_id').val(id);
|
|
$('#edit_tipologia_evento_nome').val(nome);
|
|
$('#edit_tipologia_evento_descrizione').val(descrizione);
|
|
$('#edit_tipologia_evento_attiva').prop('checked', attiva);
|
|
$('#editTipologiaEventoForm').attr('action', '/impostazioni/tipologie-eventi/' + id);
|
|
$('#editTipologiaEventoModal').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 })
|
|
});
|
|
}
|
|
});
|
|
|
|
var sortableTipologieEventi = Sortable.create(document.getElementById('tipologie-eventi-sortable'), {
|
|
handle: '.handle',
|
|
animation: 150,
|
|
onEnd: function(evt) {
|
|
var order = [];
|
|
$('#tipologie-eventi-sortable tr').each(function() {
|
|
order.push($(this).data('id'));
|
|
});
|
|
fetch('{{ route('impostazioni.tipologie-eventi.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();
|
|
}
|
|
if (target === '#tipologie-eventi') {
|
|
sortableTipologieEventi.refresh();
|
|
}
|
|
if (target === '#calendario') {
|
|
if (typeof sortableCalendario !== 'undefined') {
|
|
sortableCalendario.refresh();
|
|
}
|
|
}
|
|
if (target === '#email-firma') {
|
|
setTimeout(initSignatureEditor, 100);
|
|
}
|
|
});
|
|
|
|
// ===== MITTENTI AGGIUNTIVI =====
|
|
let senderAccounts = @json($senderAccounts);
|
|
|
|
function resetSenderForm() {
|
|
document.getElementById('senderForm').action = '{{ route('impostazioni.sender.store') }}';
|
|
document.getElementById('senderMethod').value = 'POST';
|
|
document.getElementById('senderModalTitle').textContent = 'Nuovo Mittente';
|
|
document.getElementById('senderForm').reset();
|
|
document.getElementById('senderId').value = '';
|
|
document.getElementById('sender_is_active').checked = true;
|
|
document.getElementById('sender_smtp_password').placeholder = '';
|
|
document.getElementById('sender_smtp_password').required = false;
|
|
}
|
|
|
|
function editSender(id) {
|
|
const sender = senderAccounts.find(s => s.id === id);
|
|
if (!sender) return;
|
|
|
|
document.getElementById('senderForm').action = '{{ route('impostazioni.sender.update', '__ID__') }}'.replace('__ID__', id);
|
|
document.getElementById('senderMethod').value = 'PUT';
|
|
document.getElementById('senderModalTitle').textContent = 'Modifica Mittente - ' + sender.email_address;
|
|
document.getElementById('senderId').value = sender.id;
|
|
document.getElementById('sender_email').value = sender.email_address;
|
|
document.getElementById('sender_name').value = sender.email_name || '';
|
|
document.getElementById('sender_smtp_host').value = sender.smtp_host || '';
|
|
document.getElementById('sender_smtp_port').value = sender.smtp_port || 587;
|
|
document.getElementById('sender_smtp_encryption').value = sender.smtp_encryption || 'tls';
|
|
document.getElementById('sender_smtp_username').value = sender.smtp_username || '';
|
|
document.getElementById('sender_smtp_password').value = '';
|
|
document.getElementById('sender_smtp_password').placeholder = '......';
|
|
document.getElementById('sender_smtp_password').required = false;
|
|
document.getElementById('sender_reply_to').value = sender.reply_to || '';
|
|
document.getElementById('sender_verify_email').value = sender.verify_email || '';
|
|
document.getElementById('sender_note').value = sender.note || '';
|
|
document.getElementById('sender_is_active').checked = sender.is_active;
|
|
|
|
$('#senderModal').modal('show');
|
|
}
|
|
|
|
function deleteSender(id, email) {
|
|
if (!confirm('Eliminare il mittente ' + email + '?')) return;
|
|
var url = '{{ route('impostazioni.sender.destroy', '__ID__') }}'.replace('__ID__', id);
|
|
fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'X-HTTP-Method-Override': 'DELETE'
|
|
}
|
|
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('❌ ' + (data.message || 'Errore'));
|
|
}
|
|
}).catch(function() {
|
|
alert('❌ Errore di rete');
|
|
});
|
|
}
|
|
|
|
function testSenderSmtp(id) {
|
|
const email = prompt('Inserisci l\'indirizzo email a cui inviare il test:');
|
|
if (!email || !email.includes('@')) return;
|
|
|
|
const btn = event.target;
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
|
|
|
$.ajax({
|
|
url: '{{ route('impostazioni.sender.test', '__ID__') }}'.replace('__ID__', id),
|
|
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"></i>';
|
|
alert(response.success ? '✅ ' + response.message : '❌ ' + response.message);
|
|
},
|
|
error: function(xhr) {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fas fa-paper-plane"></i>';
|
|
const msg = xhr.responseJSON?.message || 'Errore di connessione';
|
|
alert('❌ ' + msg);
|
|
}
|
|
});
|
|
}
|
|
// ===== EMAIL FUNCTIONS =====
|
|
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);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
var quillEditorInstance = null;
|
|
|
|
function createEditor() {
|
|
var container = document.querySelector('#signature-editor');
|
|
if (!container || quillEditorInstance) 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;
|
|
}
|
|
});
|
|
|
|
quillEditorInstance = quill;
|
|
}
|
|
|
|
function initSignatureEditor() {
|
|
if (quillEditorInstance) 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) {
|
|
var target = $(e.target).attr('href');
|
|
if (target === '#email-firma' || target === '#email') {
|
|
setTimeout(initSignatureEditor, 100);
|
|
}
|
|
});
|
|
|
|
if (window.location.hash === '#email') {
|
|
$(document).ready(function() {
|
|
setTimeout(initSignatureEditor, 300);
|
|
});
|
|
}
|
|
|
|
// ===== REPOSITORY REMOTI =====
|
|
let repositories = @json($repositories);
|
|
let newToken = @json($googleDriveNewToken);
|
|
|
|
function resetRepoForm() {
|
|
document.getElementById('repoForm').action = '{{ route('storage-repositories.store') }}';
|
|
document.getElementById('repoMethod').value = 'POST';
|
|
document.getElementById('repoModalTitle').textContent = 'Nuovo Repository Remoto';
|
|
document.getElementById('repoForm').reset();
|
|
document.getElementById('repoId').value = '';
|
|
document.getElementById('repo_is_active').checked = true;
|
|
setGdAuthStatus(false, '');
|
|
toggleRepoConfigFields();
|
|
}
|
|
|
|
function toggleRepoConfigFields() {
|
|
const tipo = document.getElementById('repo_tipo').value;
|
|
document.getElementById('webdavFields').style.display = tipo === 'webdav' ? '' : 'none';
|
|
document.getElementById('googleDriveFields').style.display = tipo === 'google_drive' ? '' : 'none';
|
|
if (tipo === 'google_drive') {
|
|
updateGdAuthUrl();
|
|
}
|
|
}
|
|
|
|
function setGdAuthStatus(authorized, clientId) {
|
|
const statusEl = document.getElementById('gdAuthStatus');
|
|
const btn = document.getElementById('gdAuthBtn');
|
|
if (authorized) {
|
|
statusEl.innerHTML = '<span class="text-success"><i class="fas fa-check-circle mr-1"></i> Autorizzato</span>';
|
|
btn.innerHTML = '<i class="fas fa-refresh mr-1"></i> Ri-autorizza con Google';
|
|
} else {
|
|
statusEl.innerHTML = '<span class="text-muted"><i class="fas fa-circle mr-1"></i> Non autorizzato</span>';
|
|
btn.innerHTML = '<i class="fas fa-google mr-1"></i> Autorizza con Google';
|
|
}
|
|
}
|
|
|
|
function updateGdAuthUrl() {
|
|
const clientId = document.getElementById('gd_client_id').value.trim();
|
|
const clientSecret = document.getElementById('gd_client_secret').value.trim();
|
|
const repoId = document.getElementById('repoId').value;
|
|
const nome = document.getElementById('repo_nome').value.trim();
|
|
const isActive = document.getElementById('repo_is_active').checked ? '1' : '0';
|
|
const rootFolderId = document.getElementById('gd_root_folder_id').value.trim();
|
|
const btn = document.getElementById('gdAuthBtn');
|
|
if (clientId && clientSecret) {
|
|
const params = new URLSearchParams({
|
|
client_id: clientId,
|
|
client_secret: clientSecret,
|
|
repo_id: repoId || '0',
|
|
nome: nome,
|
|
is_active: isActive,
|
|
root_folder_id: rootFolderId || 'root',
|
|
});
|
|
btn.href = '{{ route('google-drive.redirect') }}?' + params.toString();
|
|
btn.classList.remove('disabled');
|
|
} else {
|
|
btn.href = '#';
|
|
btn.classList.add('disabled');
|
|
}
|
|
}
|
|
|
|
function startGdAuth() {
|
|
const clientId = document.getElementById('gd_client_id').value.trim();
|
|
const clientSecret = document.getElementById('gd_client_secret').value.trim();
|
|
if (!clientId || !clientSecret) {
|
|
alert('Inserisci Client ID e Client Secret prima di autorizzare.');
|
|
return false;
|
|
}
|
|
// Open in same window (full redirect flow)
|
|
updateGdAuthUrl();
|
|
window.location.href = document.getElementById('gdAuthBtn').href;
|
|
return false;
|
|
}
|
|
|
|
function editRepo(id) {
|
|
const repo = repositories.find(r => r.id === id);
|
|
if (!repo) return;
|
|
|
|
document.getElementById('repoForm').action = '{{ route('storage-repositories.update', '__ID__') }}'.replace('__ID__', id);
|
|
document.getElementById('repoMethod').value = 'PUT';
|
|
document.getElementById('repoModalTitle').textContent = 'Modifica Repository - ' + repo.nome;
|
|
document.getElementById('repoId').value = repo.id;
|
|
document.getElementById('repo_nome').value = repo.nome;
|
|
document.getElementById('repo_tipo').value = repo.tipo;
|
|
document.getElementById('repo_is_active').checked = repo.is_active;
|
|
|
|
const config = repo.config || {};
|
|
document.getElementById('webdav_base_uri').value = config.base_uri || '';
|
|
document.getElementById('webdav_username').value = config.username || '';
|
|
document.getElementById('webdav_password').value = '';
|
|
document.getElementById('webdav_password').placeholder = config.password ? '......' : '';
|
|
document.getElementById('webdav_root').value = config.root || '/';
|
|
document.getElementById('webdav_auth_type').value = config.auth_type || 'basic';
|
|
document.getElementById('gd_client_id').value = config.client_id || '';
|
|
document.getElementById('gd_client_secret').value = '';
|
|
document.getElementById('gd_client_secret').placeholder = config.client_secret ? '......' : '';
|
|
document.getElementById('gd_refresh_token').value = config.refresh_token || '';
|
|
document.getElementById('gd_root_folder_id').value = config.root_folder_id || 'root';
|
|
|
|
const hasRefreshToken = !!(config.refresh_token && config.refresh_token !== '');
|
|
setGdAuthStatus(hasRefreshToken, config.client_id || '');
|
|
toggleRepoConfigFields();
|
|
$('#repoModal').modal('show');
|
|
}
|
|
|
|
function deleteRepo(id, nome) {
|
|
if (!confirm('Eliminare il repository "' + nome + '"?')) return;
|
|
var url = '{{ route('storage-repositories.destroy', '__ID__') }}'.replace('__ID__', id);
|
|
fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'X-HTTP-Method-Override': 'DELETE'
|
|
}
|
|
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('❌ ' + (data.message || 'Errore'));
|
|
}
|
|
}).catch(function() {
|
|
alert('❌ Errore di rete');
|
|
});
|
|
}
|
|
|
|
function testRepo(id, btn) {
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Test...';
|
|
|
|
$.ajax({
|
|
url: '{{ route('storage-repositories.test', '__ID__') }}'.replace('__ID__', id),
|
|
method: 'POST',
|
|
headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}' },
|
|
timeout: 30000,
|
|
success: function(response) {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fas fa-plug"></i>';
|
|
alert(response.success ? 'Connessione OK: ' + response.message : 'Errore: ' + response.message);
|
|
},
|
|
error: function(xhr) {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fas fa-plug"></i>';
|
|
const msg = xhr.responseJSON?.message || 'Errore di connessione';
|
|
alert('Errore: ' + msg);
|
|
}
|
|
});
|
|
}
|
|
|
|
// ===== CONNESSIONI CALENDARIO =====
|
|
var calendarioConnessioni = @json($calendarioConnessioni ?? []);
|
|
|
|
function resetCalendarioForm() {
|
|
document.getElementById('calendarioForm').action = '{{ route('calendario-connessioni.store') }}';
|
|
document.getElementById('calendarioMethod').value = 'POST';
|
|
document.getElementById('calendarioModalTitle').textContent = 'Nuova Connessione Calendario';
|
|
document.getElementById('calendarioForm').reset();
|
|
document.getElementById('calendarioId').value = '';
|
|
document.getElementById('cal_is_active').checked = true;
|
|
document.getElementById('cal_caldav_password').placeholder = '';
|
|
document.getElementById('cal_gc_client_secret').placeholder = '';
|
|
toggleCalendarioConfigFields();
|
|
}
|
|
|
|
function toggleCalendarioConfigFields() {
|
|
const tipo = document.getElementById('cal_tipo').value;
|
|
document.getElementById('caldavFields').style.display = tipo === 'caldav' ? '' : 'none';
|
|
document.getElementById('googleCalendarFields').style.display = tipo === 'google_calendar' ? '' : 'none';
|
|
}
|
|
|
|
function editCalendario(id) {
|
|
const cal = calendarioConnessioni.find(c => c.id === id);
|
|
if (!cal) return;
|
|
|
|
document.getElementById('calendarioForm').action = '{{ route('calendario-connessioni.update', '__ID__') }}'.replace('__ID__', id);
|
|
document.getElementById('calendarioMethod').value = 'PUT';
|
|
document.getElementById('calendarioModalTitle').textContent = 'Modifica Connessione - ' + cal.nome;
|
|
document.getElementById('calendarioId').value = cal.id;
|
|
document.getElementById('cal_nome').value = cal.nome;
|
|
document.getElementById('cal_tipo').value = cal.tipo;
|
|
document.getElementById('cal_sync_direction').value = cal.sync_direction || 'bidirectional';
|
|
document.getElementById('cal_sync_interval').value = cal.sync_interval_minutes || 60;
|
|
document.getElementById('cal_is_active').checked = cal.is_active;
|
|
|
|
const cfg = cal.config || {};
|
|
document.getElementById('cal_caldav_url').value = cfg.url || '';
|
|
document.getElementById('cal_caldav_calendar_url').value = cfg.calendar_url || '';
|
|
document.getElementById('cal_caldav_username').value = cfg.username || '';
|
|
document.getElementById('cal_caldav_password').value = '';
|
|
document.getElementById('cal_caldav_password').placeholder = cfg.password ? '......' : '';
|
|
document.getElementById('cal_gc_client_id').value = cfg.client_id || '';
|
|
document.getElementById('cal_gc_client_secret').value = '';
|
|
document.getElementById('cal_gc_client_secret').placeholder = cfg.client_secret ? '......' : '';
|
|
document.getElementById('cal_gc_refresh_token').value = cfg.refresh_token || '';
|
|
document.getElementById('cal_gc_calendar_id').value = cfg.calendar_id || 'primary';
|
|
|
|
toggleCalendarioConfigFields();
|
|
$('#calendarioModal').modal('show');
|
|
}
|
|
|
|
function deleteCalendario(id, nome) {
|
|
if (!confirm('Eliminare la connessione "' + nome + '"?')) return;
|
|
var url = '{{ route('calendario-connessioni.destroy', '__ID__') }}'.replace('__ID__', id);
|
|
fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'X-HTTP-Method-Override': 'DELETE'
|
|
}
|
|
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('❌ ' + (data.message || 'Errore'));
|
|
}
|
|
}).catch(function() {
|
|
alert('❌ Errore di rete');
|
|
});
|
|
}
|
|
|
|
function testCalendario(id, btn) {
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Test...';
|
|
|
|
$.ajax({
|
|
url: '{{ route('calendario-connessioni.test', '__ID__') }}'.replace('__ID__', id),
|
|
method: 'POST',
|
|
headers: { 'X-CSRF-TOKEN': '{{ csrf_token() }}' },
|
|
timeout: 30000,
|
|
success: function(response) {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fas fa-plug"></i>';
|
|
alert(response.success ? '✅ ' + response.message : '❌ ' + response.message);
|
|
},
|
|
error: function(xhr) {
|
|
btn.disabled = false;
|
|
btn.innerHTML = '<i class="fas fa-plug"></i>';
|
|
const msg = xhr.responseJSON?.message || 'Errore di connessione';
|
|
alert('❌ ' + msg);
|
|
}
|
|
});
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
if (document.getElementById('calendarioSortable')) {
|
|
var sortableCalendario = Sortable.create(document.getElementById('calendarioSortable'), {
|
|
handle: '.handle',
|
|
animation: 150,
|
|
onEnd: function(evt) {
|
|
var order = [];
|
|
$('#calendarioSortable tr').each(function() {
|
|
order.push($(this).data('id'));
|
|
});
|
|
fetch('{{ route('calendario-connessioni.reorder') }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ order: order })
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
if (document.getElementById('repoSortable')) {
|
|
var sortableRepo = Sortable.create(document.getElementById('repoSortable'), {
|
|
handle: '.handle',
|
|
animation: 150,
|
|
onEnd: function(evt) {
|
|
var order = [];
|
|
$('#repoSortable tr').each(function() {
|
|
order.push($(this).data('id'));
|
|
});
|
|
fetch('{{ route('storage-repositories.reorder') }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ order: order })
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
if (newToken && newToken.refresh_token) {
|
|
// Auto-open modal after OAuth callback — preserve nome and other fields
|
|
document.getElementById('repoForm').action = '{{ route('storage-repositories.store') }}';
|
|
document.getElementById('repoMethod').value = 'POST';
|
|
document.getElementById('repoModalTitle').textContent = 'Nuovo Repository Remoto';
|
|
document.getElementById('repoForm').reset();
|
|
document.getElementById('repoId').value = '';
|
|
document.getElementById('repo_nome').value = newToken.nome || '';
|
|
document.getElementById('repo_tipo').value = 'google_drive';
|
|
document.getElementById('repo_is_active').checked = newToken.is_active !== '0';
|
|
document.getElementById('gd_client_id').value = newToken.client_id || '';
|
|
document.getElementById('gd_client_secret').value = newToken.client_secret || '';
|
|
document.getElementById('gd_refresh_token').value = newToken.refresh_token;
|
|
document.getElementById('gd_root_folder_id').value = newToken.root_folder_id || 'root';
|
|
setGdAuthStatus(true, newToken.client_id || '');
|
|
toggleRepoConfigFields();
|
|
$('#repoModal').modal('show');
|
|
}
|
|
});
|
|
</script>
|
|
|
|
{{-- MODAL: Repository Remoto --}}
|
|
<div class="modal fade" id="repoModal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog modal-lg" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="repoModalTitle"><i class="fas fa-cloud mr-2"></i>Nuovo Repository Remoto</h5>
|
|
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
|
</div>
|
|
<form id="repoForm" method="POST" action="{{ route('storage-repositories.store') }}">
|
|
@csrf
|
|
<input type="hidden" name="_method" id="repoMethod" value="POST">
|
|
<input type="hidden" name="id" id="repoId" value="">
|
|
<div class="modal-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="repo_nome">Nome *</label>
|
|
<input type="text" name="nome" id="repo_nome" class="form-control" placeholder="es. Server Parrocchia" required>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="repo_tipo">Tipo *</label>
|
|
<select name="tipo" id="repo_tipo" class="form-control" required onchange="toggleRepoConfigFields()">
|
|
<option value="webdav">WebDAV</option>
|
|
<option value="google_drive">Google Drive</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<div class="custom-control custom-switch">
|
|
<input type="checkbox" class="custom-control-input" id="repo_is_active" name="is_active" value="1" checked>
|
|
<label class="custom-control-label" for="repo_is_active">Repository attivo</label>
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
<h6>Configurazione</h6>
|
|
|
|
{{-- WebDAV Fields --}}
|
|
<div id="webdavFields">
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<div class="form-group">
|
|
<label for="webdav_base_uri">URL Base WebDAV *</label>
|
|
<input type="url" name="config[base_uri]" id="webdav_base_uri" class="form-control" placeholder="es. https://nextcloud.parrocchia.it/remote.php/dav/files/utente/">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-group">
|
|
<label for="webdav_root">Root path</label>
|
|
<input type="text" name="config[root]" id="webdav_root" class="form-control" value="/" placeholder="/">
|
|
<small class="text-muted">Percorso base nel server</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<div class="form-group">
|
|
<label for="webdav_username">Username</label>
|
|
<input type="text" name="config[username]" id="webdav_username" class="form-control">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-group">
|
|
<label for="webdav_password">Password</label>
|
|
<input type="password" name="config[password]" id="webdav_password" class="form-control">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-group">
|
|
<label for="webdav_auth_type">Auth Type</label>
|
|
<select name="config[auth_type]" id="webdav_auth_type" class="form-control">
|
|
<option value="basic">Basic</option>
|
|
<option value="digest">Digest</option>
|
|
<option value="ntlm">NTLM</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Google Drive Fields --}}
|
|
<div id="googleDriveFields" style="display:none;">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="gd_client_id">Client ID *</label>
|
|
<input type="text" name="config[client_id]" id="gd_client_id" class="form-control" placeholder="Google Client ID" oninput="updateGdAuthUrl()">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="gd_client_secret">Client Secret *</label>
|
|
<input type="password" name="config[client_secret]" id="gd_client_secret" class="form-control" placeholder="Google Client Secret" oninput="updateGdAuthUrl()">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<input type="hidden" name="config[refresh_token]" id="gd_refresh_token" value="">
|
|
<div class="mb-3">
|
|
<label class="d-block">Autorizzazione Google</label>
|
|
<div id="gdAuthStatus" class="mb-2">
|
|
<span class="text-muted"><i class="fas fa-circle mr-1"></i> Non autorizzato</span>
|
|
</div>
|
|
<a id="gdAuthBtn" href="#" class="btn btn-danger btn-sm" target="_blank" onclick="return startGdAuth()">
|
|
<i class="fas fa-google mr-1"></i> Autorizza con Google
|
|
</a>
|
|
<small class="d-block text-muted mt-1">
|
|
Dopo aver cliccato, verrai reindirizzato a Google per autorizzare l'app.
|
|
Al termine, il Refresh Token verrà salvato automaticamente.
|
|
</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="gd_root_folder_id">Root Folder ID</label>
|
|
<input type="text" name="config[root_folder_id]" id="gd_root_folder_id" class="form-control" value="root" placeholder="root" oninput="updateGdAuthUrl()">
|
|
<small class="text-muted">ID della cartella Google Drive, default: root</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="alert alert-info">
|
|
<i class="fas fa-info-circle mr-2"></i>
|
|
<strong>Prima volta?</strong> Crea il progetto in <a href="https://console.cloud.google.com/" target="_blank">Google Cloud Console</a>
|
|
→ API & Services → Credentials → Crea OAuth Client ID (Web Application).
|
|
Aggiungi <code>{{ url('/auth/google-drive/callback') }}</code> come <strong>Authorized Redirect URI</strong>.
|
|
</div>
|
|
</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-save mr-1"></i> Salva Repository
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|