fix error
This commit is contained in:
@@ -1,48 +1,60 @@
|
|||||||
# 🧠 MEMORY.md — Stato Progetto
|
# 🧠 MEMORY.md — Stato Progetto
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
- Stabilizzare il modale "Carica Documento" e migliorare la gestione cartelle durante l'upload.
|
- Correggere tutti i bug critici identificati nell'applicazione Glastree, a partire dal bug segnalato "rimuovendo un individuo da un gruppo, l'individuo perdeva tutti i contatti"
|
||||||
|
|
||||||
## Constraints & Preferences
|
## Constraints & Preferences
|
||||||
- (none)
|
- Laravel 13, PHP 8.4, AdminLTE 4, MySQL/MariaDB
|
||||||
|
- Zero placeholder, zero TODO, codice funzionante
|
||||||
|
- Compatibilità cross-DB MySQL ↔ SQLite
|
||||||
|
|
||||||
## Progress
|
## Progress
|
||||||
### Done
|
### Done
|
||||||
- Aggiunto pulsante "Scarica Guida PDF" nella sidebar di `/help` con nuovo metodo `HelpController::downloadPdf()` e route `GET /help/pdf/download`
|
- **Cancellata cache Laravel** (`php artisan optimize:clear`)
|
||||||
- Aggiunta card "Backup" in dashboard (small-box bg-navy, icona fa-hdd) con conteggio backup, visibile solo a utenti con `canManage('settings')`
|
- **Fix BUG CRITICO 3** – `IndividuoController::update()` (riga 216): avvolto `$individuo->contatti()->delete()` dentro `if ($request->has('contatti'))`. Se il request non contiene il campo `contatti` (es. submit da form annidato), i contatti NON vengono eliminati.
|
||||||
- Corretto `modal-body` nel modale `#uploadModal` che chiudeva prematuramente (pulsanti Annulla/Carica finivano fuori dal popup)
|
- **Fix BUG CRITICO 1** – `individui/edit.blade.php`: convertiti 4 form annidati in pulsanti `type="button"` + `fetch()` AJAX:
|
||||||
- Aggiunto campo `visibilita` (select Pubblico/Individuo/Gruppo) nel form di upload documento, mancante e richiesto dal controller
|
- Linea 235-240: rimozione gruppo → `deleteGruppoFromEdit()`
|
||||||
- Aggiunto selettore cartelle visibile (`<select name="cartella_id" id="uploadCartellaId">`) nel modale di upload (sostituisce il vecchio hidden input)
|
- Linea 245-267: modifica gruppo inline → `submitEditGruppo()`
|
||||||
- Aggiunto pulsante "Nuova cartella" sia nel modale upload (`#uploadNewFolderBtn`) che nel modale "Sposta" (`#massMoveNewFolderBtn`)
|
- Linea 291-319: upload documento → `submitUploadDocumentoIndividuo()`
|
||||||
- Sostituito SweetAlert2 per creazione cartella con il modale Bootstrap `#newFolderModal` già esistente (risolve focus trap Bootstrap 4 che bloccava l'input)
|
- Linea 350-355: eliminazione documento → `deleteDocumentoFromEdit()`
|
||||||
- Aggiornato `toggleUploadRepo()` per nascondere/mostrare il gruppo cartella in base alla selezione del repository remoto
|
- **Fix BUG CRITICO 2** – `gruppi/edit.blade.php`: convertiti 3 form annidati in pulsanti `type="button"` + `fetch()` AJAX:
|
||||||
- Aggiunto flag `window._uploadNewFolder` per comportamento differenziato: da upload aggiunge opzione al select senza ricaricare, da tooltip ricarica la pagina
|
- Linea 155-180: modifica membro inline → `submitEditMembro()`
|
||||||
- **Fix backdrop grigio persistente dopo creazione cartella**: ora `#uploadModal` viene prima nascosto (quando si clicca "Nuova cartella") e poi ri-mostrato dopo la creazione nel success handler, risolvendo il conflitto di backdrop tra modali annidati Bootstrap 4
|
- Linea 204-231: upload documento → `submitUploadDocumentoGruppo()`
|
||||||
- **Fix scroll modal upload**: ristrutturato `<form>` per non avvolgere `.modal-body` e `.modal-footer` (ora è dentro `.modal-body`), così `modal-dialog-scrollable` funziona correttamente. Pulsante submit ha `form="uploadForm"` per collegamento cross-DOM
|
- Linea 261-266: eliminazione documento → `deleteDocumentoFromEdit()`
|
||||||
|
- **Fix BUG CRITICO 4** – `gruppi/show.blade.php`: spostati i flash messages (session success/error) DENTRO `@section('content')` invece di essere fuori (non venivano mai visualizzati)
|
||||||
|
- **Fix BUG CRITICO 5** – `eventi/show.blade.php`: rimossi 2 tag `</div>` extra alle linee 214-217 che rompevano la struttura HTML
|
||||||
|
- **Fix BUG CRITICO 6** – `eventi/show.blade.php`: escape JS di `$evento->nome_evento` nella `confirm()` (riga 307) per prevenire stored XSS
|
||||||
|
- **Fix BUG CRITICO 7** – `ImpostazioniController.php`: aggiunto `use App\Models\Documento;` mancante (causava Fatal error in `saveAppSettings()` e `migrateStoragePath()`)
|
||||||
|
- **Fix BUG CRITICO 8** – `ImpostazioniController.php`: sostituito `JSON_CONTAINS()` (non supportato da SQLite) con iterazione PHP su `json_decode()` — compatibile MySQL e SQLite
|
||||||
|
- **Fix BUG CRITICO 9** – `ReportController.php`: corretto `$contatto->email` → `$contatto->valore` (riga 605, colonna `email` non esiste)
|
||||||
|
- **Fix BUG CRITICO 10** – `DocumentoController.php`: prevenuta morph injection in `massUpdate()` sostituendo il fallback `?? $data['visibilita_target_type']` con un `isset()` check sul typeMap + `return back()->with('error')` se il tipo non è valido
|
||||||
|
|
||||||
### In Progress
|
### In Progress
|
||||||
- (none)
|
- *(nessuno)*
|
||||||
|
|
||||||
### Blocked
|
### Blocked
|
||||||
- (none)
|
- *(nessuno)*
|
||||||
|
|
||||||
## Key Decisions
|
## Key Decisions
|
||||||
- Replicato il codice di creazione cartella con `SweetAlert2` → sostituito con modale Bootstrap `#newFolderModal` perché Bootstrap 4 impedisce il focus su elementi fuori dal `.modal` (focus trap)
|
- Per i form annidati si è scelto l'approccio `type="button"` + `fetch()` AJAX, già utilizzato in `individui/show.blade.php` e `gruppi/edit.blade.php` per funzioni esistenti (`deleteMembro`, `submitGruppoForm`, `uploadAvatar`, etc.)
|
||||||
- Pulsante "Nuova cartella" nello stesso `<form>` del modal di upload riceve `e.preventDefault()` per sicurezza
|
- Per documenti (upload/delete) il controller torna `redirect()`, non JSON. Le funzioni AJAX eseguono `window.location.reload()` dopo il fetch per aggiornare la vista.
|
||||||
- Per evitare conflitti di backdrop Bootstrap 4 con modali annidati, si nasconde `#uploadModal` prima di mostrare `#newFolderModal` e lo si ripristina dopo il success
|
- `JSON_CONTAINS` sostituito con approccio PHP+json_decode per garantire compatibilità SQLite senza modificare lo schema DB.
|
||||||
|
- `$request->has('contatti')` è la guardia corretta: se il form principale non invia il campo `contatti` (perché il submit proviene da un form annidato), non si cancella nulla.
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
- (none)
|
- *(nessuno)* - Tutti i bug critici risolti
|
||||||
|
|
||||||
## Critical Context
|
## Critical Context
|
||||||
- Bootstrap 4.6.2 con focus trap: impedisce focus su elementi fuori dal `.modal` — SweetAlert2 aggiunge dialog al `<body>`, quindi il suo input non riceve focus
|
- **CAUSA DEL BUG SEGNALATO**: I form annidati in `individui/edit.blade.php` facevano sì che il browser ignorasse i form interni. I pulsanti "Rimuovi" inviavano il form principale → `IndividuoController::update()` → `$individuo->contatti()->delete()` incondizionato.
|
||||||
- `#newFolderModal` già esisteva con submit handler AJAX a `/documenti/cartelle` e ricaricava pagina — ora con `window._uploadNewFolder` evita reload quando chiamato dall'upload
|
- **Tutti i 10 bug critici sono stati risolti**
|
||||||
- Bug: backdrop grigio non spariva dopo creazione cartella perché Bootstrap 4 gestisce male i modali annidati (due backdrop concorrenti)
|
- I controller di rimozione membro (`GruppoMembroController::destroy()` e `GruppoIndividuoController::destroy()`) usano già `detach()` correttamente — nessun problema lato backend
|
||||||
|
|
||||||
## Relevant Files
|
## Relevant Files
|
||||||
- `app/Http/Controllers/HelpController.php`: metodo `downloadPdf()` + route `/help/pdf/download`
|
- `app/Http/Controllers/IndividuoController.php`: Fix BUG 3 – contatti condizionale
|
||||||
- `app/Http/Controllers/HomeController.php`: `$stats['backups']` via `BackupService::list()`
|
- `app/Http/Controllers/ImpostazioniController.php`: Fix BUG 7 (use Documento) + BUG 8 (JSON_CONTAINS → PHP)
|
||||||
- `routes/web.php`: route `/help/pdf/download`
|
- `app/Http/Controllers/ReportController.php`: Fix BUG 9 (email → valore)
|
||||||
- `resources/views/help/index.blade.php`: card-footer con pulsante download PDF
|
- `app/Http/Controllers/DocumentoController.php`: Fix BUG 10 (morph injection prevention)
|
||||||
- `resources/views/home/dashboard.blade.php`: small-box Backup condizionale
|
- `resources/views/individui/edit.blade.php`: Fix BUG 1 (4 nested forms → AJAX)
|
||||||
- `resources/views/documenti/index.blade.php`: intero modale `#uploadModal` riscritto (visibilita, cartella select, nuova cartella via `#newFolderModal`, toggleUploadRepo); fix backdrop persistente nascondendo/rimostrando `#uploadModal`
|
- `resources/views/gruppi/edit.blade.php`: Fix BUG 2 (3 nested forms → AJAX)
|
||||||
|
- `resources/views/gruppi/show.blade.php`: Fix BUG 4 (flash inside @section)
|
||||||
|
- `resources/views/eventi/show.blade.php`: Fix BUG 5 (extra divs) + BUG 6 (XSS escape)
|
||||||
|
|||||||
@@ -661,9 +661,13 @@ class DocumentoController extends Controller
|
|||||||
'evento' => Evento::class,
|
'evento' => Evento::class,
|
||||||
'mailing' => \App\Models\MailingList::class,
|
'mailing' => \App\Models\MailingList::class,
|
||||||
];
|
];
|
||||||
$updateData['visibilita'] = $data['visibilita_target_type'];
|
$targetType = $data['visibilita_target_type'];
|
||||||
|
if (!isset($typeMap[$targetType])) {
|
||||||
|
return back()->with('error', 'Tipo destinazione non valido.');
|
||||||
|
}
|
||||||
|
$updateData['visibilita'] = $targetType;
|
||||||
$updateData['visibilita_target_id'] = (int) $data['visibilita_target_id'];
|
$updateData['visibilita_target_id'] = (int) $data['visibilita_target_id'];
|
||||||
$updateData['visibilita_target_type'] = $typeMap[$data['visibilita_target_type']] ?? $data['visibilita_target_type'];
|
$updateData['visibilita_target_type'] = $typeMap[$targetType];
|
||||||
}
|
}
|
||||||
|
|
||||||
Documento::whereIn('id', $ids)->update($updateData);
|
Documento::whereIn('id', $ids)->update($updateData);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Models\AppSetting;
|
use App\Models\AppSetting;
|
||||||
|
use App\Models\Documento;
|
||||||
use App\Models\EmailSetting;
|
use App\Models\EmailSetting;
|
||||||
use App\Models\Ruolo;
|
use App\Models\Ruolo;
|
||||||
use App\Models\SenderAccount;
|
use App\Models\SenderAccount;
|
||||||
@@ -268,10 +269,17 @@ class ImpostazioniController extends Controller
|
|||||||
|
|
||||||
$ruolo = Ruolo::findOrFail($id);
|
$ruolo = Ruolo::findOrFail($id);
|
||||||
|
|
||||||
$count = \DB::table('gruppo_individuo')
|
$allRows = \DB::table('gruppo_individuo')
|
||||||
->whereNotNull('ruolo_ids')
|
->whereNotNull('ruolo_ids')
|
||||||
->whereRaw("JSON_CONTAINS(ruolo_ids, ?)", [json_encode($id)])
|
->get(['id', 'ruolo_ids']);
|
||||||
->count();
|
|
||||||
|
$count = 0;
|
||||||
|
foreach ($allRows as $row) {
|
||||||
|
$ids = json_decode($row->ruolo_ids, true);
|
||||||
|
if (is_array($ids) && in_array((int) $id, $ids)) {
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($count > 0) {
|
if ($count > 0) {
|
||||||
return redirect('/impostazioni#ruoli')->with('error', 'Impossibile eliminare: ' . $count . ' membri associati a questo ruolo.');
|
return redirect('/impostazioni#ruoli')->with('error', 'Impossibile eliminare: ' . $count . ' membri associati a questo ruolo.');
|
||||||
|
|||||||
@@ -210,11 +210,15 @@ public function create()
|
|||||||
|
|
||||||
\Log::info('Individuo dati salvati', ['id' => $individuo->id]);
|
\Log::info('Individuo dati salvati', ['id' => $individuo->id]);
|
||||||
|
|
||||||
$contattiData = $request->input('contatti');
|
if ($request->has('contatti')) {
|
||||||
\Log::info('Contatti ricevuti:', ['contatti' => $contattiData, 'tipo' => gettype($contattiData)]);
|
$contattiData = $request->input('contatti');
|
||||||
|
\Log::info('Contatti ricevuti:', ['contatti' => $contattiData, 'tipo' => gettype($contattiData)]);
|
||||||
|
|
||||||
$individuo->contatti()->delete();
|
$individuo->contatti()->delete();
|
||||||
\Log::info('Contatti precedenti eliminati, count ora:', ['count' => $individuo->contatti()->count()]);
|
\Log::info('Contatti precedenti eliminati, count ora:', ['count' => $individuo->contatti()->count()]);
|
||||||
|
} else {
|
||||||
|
$contattiData = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($contattiData) && is_array($contattiData)) {
|
if (!empty($contattiData) && is_array($contattiData)) {
|
||||||
$created = 0;
|
$created = 0;
|
||||||
|
|||||||
@@ -602,7 +602,7 @@ class ReportController extends Controller
|
|||||||
'lista' => $lista->nome,
|
'lista' => $lista->nome,
|
||||||
'stato' => $lista->attiva ? 'Attiva' : 'Disattiva',
|
'stato' => $lista->attiva ? 'Attiva' : 'Disattiva',
|
||||||
'individuo' => $contatto->individuo?->nome_completo ?? 'N/D',
|
'individuo' => $contatto->individuo?->nome_completo ?? 'N/D',
|
||||||
'email' => $contatto->email ?? '-',
|
'email' => $contatto->valore ?? '-',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,11 +211,6 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card mt-3">
|
<div class="card mt-3">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3 class="card-title"><i class="fas fa-file mr-2"></i>Documenti</h3>
|
<h3 class="card-title"><i class="fas fa-file mr-2"></i>Documenti</h3>
|
||||||
@@ -304,7 +299,7 @@
|
|||||||
</a>
|
</a>
|
||||||
<form action="/eventi/{{ $evento->id }}" method="POST" class="d-inline">
|
<form action="/eventi/{{ $evento->id }}" method="POST" class="d-inline">
|
||||||
@csrf @method('DELETE')
|
@csrf @method('DELETE')
|
||||||
<button type="submit" class="btn btn-danger" onclick="return confirm('Confermi l\'eliminazione di {{ $evento->nome_evento }}?')">
|
<button type="submit" class="btn btn-danger" onclick="return confirm('Confermi l\'eliminazione di {{ $evento->nome_evento }}?'.replace(/'/g, '\\\''))">
|
||||||
<i class="fas fa-trash mr-1"></i> Elimina
|
<i class="fas fa-trash mr-1"></i> Elimina
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -152,8 +152,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr id="membro-edit-{{ $membro->id }}" style="display:none;">
|
<tr id="membro-edit-{{ $membro->id }}" style="display:none;">
|
||||||
<td colspan="6">
|
<td colspan="6">
|
||||||
<form action="/gruppi/{{ $gruppo->id }}/membri/{{ $membro->id }}" method="POST" class="mb-0">
|
<div class="mb-0">
|
||||||
@csrf @method('PUT')
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<input type="text" class="form-control form-control-sm" value="{{ $membro->cognome }} {{ $membro->nome }}" disabled>
|
<input type="text" class="form-control form-control-sm" value="{{ $membro->cognome }} {{ $membro->nome }}" disabled>
|
||||||
@@ -162,7 +161,7 @@
|
|||||||
<input type="text" class="form-control form-control-sm" value="{{ $membro->codice_id }}" disabled>
|
<input type="text" class="form-control form-control-sm" value="{{ $membro->codice_id }}" disabled>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<select name="ruolo_ids[]" class="form-control form-control-sm" multiple size="3">
|
<select id="edit-membro-ruoli-{{ $membro->id }}" class="form-control form-control-sm" multiple size="3">
|
||||||
@foreach(\App\Models\Ruolo::attive() as $ruolo)
|
@foreach(\App\Models\Ruolo::attive() as $ruolo)
|
||||||
@php $selected = in_array($ruolo->id, $membro->getRuoloIdsForGruppo($gruppo->id)) @endphp
|
@php $selected = in_array($ruolo->id, $membro->getRuoloIdsForGruppo($gruppo->id)) @endphp
|
||||||
<option value="{{ $ruolo->id }}" {{ $selected ? 'selected' : '' }}>{{ $ruolo->nome }}</option>
|
<option value="{{ $ruolo->id }}" {{ $selected ? 'selected' : '' }}>{{ $ruolo->nome }}</option>
|
||||||
@@ -170,14 +169,14 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<input type="date" name="data_adesione" class="form-control form-control-sm" value="{{ $membro->pivot->data_adesione }}">
|
<input type="date" id="edit-membro-data-{{ $membro->id }}" class="form-control form-control-sm" value="{{ $membro->pivot->data_adesione }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-1">
|
<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-success" onclick="submitEditMembro({{ $membro->id }})"><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>
|
<button type="button" class="btn btn-xs btn-secondary" onclick="cancelEditMembro({{ $membro->id }})"><i class="fas fa-times"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -201,17 +200,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body p-0">
|
<div class="card-body p-0">
|
||||||
<div id="documento-add-form" style="display:none;" class="p-3 bg-light border-bottom">
|
<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">
|
<div 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="row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<input type="text" name="nome_file" class="form-control form-control-sm" placeholder="Nome documento" required>
|
<input type="text" id="doc-nome-file" class="form-control form-control-sm" placeholder="Nome documento">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<select name="tipologia" class="form-control form-control-sm" required>
|
<select id="doc-tipologia" class="form-control form-control-sm">
|
||||||
<option value="">Tipologia...</option>
|
<option value="">Tipologia...</option>
|
||||||
<option value="documento">Documento</option>
|
<option value="documento">Documento</option>
|
||||||
<option value="statuto">Statuto</option>
|
<option value="statuto">Statuto</option>
|
||||||
@@ -221,14 +216,14 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<input type="file" name="file" class="form-control form-control-sm" required>
|
<input type="file" id="doc-file" class="form-control form-control-sm">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2">
|
<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-success" onclick="submitUploadDocumentoGruppo()"><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>
|
<button type="button" class="btn btn-xs btn-secondary" onclick="hideDocumentoForm()"><i class="fas fa-times"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if($gruppo->documenti && $gruppo->documenti->count() > 0)
|
@if($gruppo->documenti && $gruppo->documenti->count() > 0)
|
||||||
@@ -258,12 +253,9 @@
|
|||||||
<i class="fas fa-eye"></i>
|
<i class="fas fa-eye"></i>
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
<form action="/documenti/{{ $documento->id }}" method="POST" class="d-inline">
|
<button type="button" class="btn btn-xs btn-danger" onclick="deleteDocumentoFromEdit({{ $documento->id }})" title="Elimina">
|
||||||
@csrf @method('DELETE')
|
<i class="fas fa-trash"></i>
|
||||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo documento?')" title="Elimina">
|
</button>
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -473,5 +465,83 @@ function previewDocumento(id, mimeType) {
|
|||||||
document.getElementById('previewDownloadBtn').href = '/documenti/' + id + '/download';
|
document.getElementById('previewDownloadBtn').href = '/documenti/' + id + '/download';
|
||||||
$('#previewModal').modal('show');
|
$('#previewModal').modal('show');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function submitEditMembro(membroId) {
|
||||||
|
var ruoloSelect = document.getElementById('edit-membro-ruoli-' + membroId);
|
||||||
|
var dataInput = document.getElementById('edit-membro-data-' + membroId);
|
||||||
|
var formData = new URLSearchParams();
|
||||||
|
|
||||||
|
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', dataInput ? dataInput.value : '');
|
||||||
|
|
||||||
|
fetch('/gruppi/{{ $gruppo->id }}/membri/' + membroId, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
body: formData.toString()
|
||||||
|
})
|
||||||
|
.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 submitUploadDocumentoGruppo() {
|
||||||
|
var nomeFile = document.getElementById('doc-nome-file');
|
||||||
|
var tipologia = document.getElementById('doc-tipologia');
|
||||||
|
var fileInput = document.getElementById('doc-file');
|
||||||
|
|
||||||
|
if (!nomeFile.value || !tipologia.value || !fileInput.files[0]) {
|
||||||
|
alert('Compila tutti i campi');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append('_token', '{{ csrf_token() }}');
|
||||||
|
formData.append('visibilita', 'gruppo');
|
||||||
|
formData.append('visibilita_target_id', '{{ $gruppo->id }}');
|
||||||
|
formData.append('visibilita_target_type', 'App\\Models\\Gruppo');
|
||||||
|
formData.append('nome_file', nomeFile.value);
|
||||||
|
formData.append('tipologia', tipologia.value);
|
||||||
|
formData.append('file', fileInput.files[0]);
|
||||||
|
|
||||||
|
document.getElementById('documento-add-form').querySelector('.btn-success').disabled = true;
|
||||||
|
|
||||||
|
fetch('/documenti', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: { 'Accept': 'application/json' }
|
||||||
|
})
|
||||||
|
.then(function() { window.location.reload(); })
|
||||||
|
.catch(function() { window.location.reload(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteDocumentoFromEdit(documentoId) {
|
||||||
|
if (!confirm('Eliminare questo documento?')) return;
|
||||||
|
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append('_token', '{{ csrf_token() }}');
|
||||||
|
formData.append('_method', 'DELETE');
|
||||||
|
formData.append('_redirect', '{{ url()->current() }}');
|
||||||
|
|
||||||
|
fetch('/documenti/' + documentoId, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: { 'Accept': 'application/json' }
|
||||||
|
})
|
||||||
|
.then(function() { window.location.reload(); })
|
||||||
|
.catch(function() { window.location.reload(); });
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
@@ -12,6 +12,7 @@ $canDeleteGruppi = Auth::user()->canDelete('gruppi');
|
|||||||
<li class="breadcrumb-item active">{{ $gruppo->nome }}</li>
|
<li class="breadcrumb-item active">{{ $gruppo->nome }}</li>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
|
@section('content')
|
||||||
@if(session('success'))
|
@if(session('success'))
|
||||||
<div class="alert alert-success alert-dismissible">
|
<div class="alert alert-success alert-dismissible">
|
||||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||||
@@ -24,8 +25,6 @@ $canDeleteGruppi = Auth::user()->canDelete('gruppi');
|
|||||||
{{ session('error') }}
|
{{ session('error') }}
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
@section('content')
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="card card-success">
|
<div class="card card-success">
|
||||||
|
|||||||
@@ -232,24 +232,20 @@
|
|||||||
<button type="button" class="btn btn-xs btn-warning" onclick="editGruppoInline({{ $gruppo->id }})" title="Modifica">
|
<button type="button" class="btn btn-xs btn-warning" onclick="editGruppoInline({{ $gruppo->id }})" title="Modifica">
|
||||||
<i class="fas fa-edit"></i>
|
<i class="fas fa-edit"></i>
|
||||||
</button>
|
</button>
|
||||||
<form action="/individui/{{ $individuo->id }}/gruppi/{{ $gruppo->id }}" method="POST" class="mb-0">
|
<button type="button" class="btn btn-xs btn-danger" onclick="deleteGruppoFromEdit({{ $individuo->id }}, {{ $gruppo->id }})" title="Rimuovi">
|
||||||
@csrf @method('DELETE')
|
<i class="fas fa-trash"></i>
|
||||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Rimuovere da questo gruppo?')" title="Rimuovi">
|
</button>
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="gruppo-edit-{{ $gruppo->id }}" style="display:none;">
|
<tr id="gruppo-edit-{{ $gruppo->id }}" style="display:none;">
|
||||||
<td colspan="5">
|
<td colspan="5">
|
||||||
<form action="/individui/{{ $individuo->id }}/gruppi/{{ $gruppo->id }}" method="POST" class="mb-0">
|
<div class="mb-0">
|
||||||
@csrf @method('PUT')
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<input type="text" class="form-control form-control-sm" value="{{ $gruppo->nome }}" disabled>
|
<input type="text" class="form-control form-control-sm" value="{{ $gruppo->nome }}" disabled>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<select name="ruolo_ids[]" class="form-control form-control-sm" multiple size="3">
|
<select id="edit-ruoli-{{ $gruppo->id }}" class="form-control form-control-sm" multiple size="3">
|
||||||
@php $selectedRuoliIds = $individuo->getRuoloIdsForGruppo($gruppo->id) @endphp
|
@php $selectedRuoliIds = $individuo->getRuoloIdsForGruppo($gruppo->id) @endphp
|
||||||
@foreach(\App\Models\Ruolo::attive() as $ruolo)
|
@foreach(\App\Models\Ruolo::attive() as $ruolo)
|
||||||
<option value="{{ $ruolo->id }}" {{ in_array($ruolo->id, $selectedRuoliIds) ? 'selected' : '' }}>{{ $ruolo->nome }}</option>
|
<option value="{{ $ruolo->id }}" {{ in_array($ruolo->id, $selectedRuoliIds) ? 'selected' : '' }}>{{ $ruolo->nome }}</option>
|
||||||
@@ -257,14 +253,14 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<input type="date" name="data_adesione" class="form-control form-control-sm" value="{{ $gruppo->pivot->data_adesione }}">
|
<input type="date" id="edit-data-{{ $gruppo->id }}" class="form-control form-control-sm" value="{{ $gruppo->pivot->data_adesione }}">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2">
|
<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-success" onclick="submitEditGruppo({{ $gruppo->id }})"><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>
|
<button type="button" class="btn btn-xs btn-secondary" onclick="cancelEditGruppo({{ $gruppo->id }})"><i class="fas fa-times"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -288,18 +284,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body p-0">
|
<div class="card-body p-0">
|
||||||
<div id="documento-add-form" style="display:none;" class="p-3 bg-light border-bottom">
|
<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">
|
<div 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="row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<input type="text" name="nome_file" class="form-control form-control-sm" placeholder="Nome documento" required>
|
<input type="text" id="doc-nome-file" class="form-control form-control-sm" placeholder="Nome documento">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<select name="tipologia" class="form-control form-control-sm" required>
|
<select id="doc-tipologia" class="form-control form-control-sm">
|
||||||
<option value="">Tipologia...</option>
|
<option value="">Tipologia...</option>
|
||||||
<option value="documento">Documento</option>
|
<option value="documento">Documento</option>
|
||||||
<option value="avatar">Avatar</option>
|
<option value="avatar">Avatar</option>
|
||||||
@@ -309,14 +300,14 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<input type="file" name="file" class="form-control form-control-sm" required>
|
<input type="file" id="doc-file" class="form-control form-control-sm">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-2">
|
<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-success" onclick="submitUploadDocumentoIndividuo()"><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>
|
<button type="button" class="btn btn-xs btn-secondary" onclick="hideDocumentoForm()"><i class="fas fa-times"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@if($individuo->documenti->count() > 0)
|
@if($individuo->documenti->count() > 0)
|
||||||
<table class="table table-bordered table-hover table-striped mb-0">
|
<table class="table table-bordered table-hover table-striped mb-0">
|
||||||
@@ -347,12 +338,9 @@
|
|||||||
<i class="fas fa-eye"></i>
|
<i class="fas fa-eye"></i>
|
||||||
</button>
|
</button>
|
||||||
@endif
|
@endif
|
||||||
<form action="/documenti/{{ $documento->id }}" method="POST" class="d-inline">
|
<button type="button" class="btn btn-xs btn-danger" onclick="deleteDocumentoFromEdit({{ $documento->id }})" title="Elimina">
|
||||||
@csrf @method('DELETE')
|
<i class="fas fa-trash"></i>
|
||||||
<button type="submit" class="btn btn-xs btn-danger" onclick="return confirm('Eliminare questo documento?')" title="Elimina">
|
</button>
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@endforeach
|
@endforeach
|
||||||
@@ -714,5 +702,105 @@ function deleteAvatar() {
|
|||||||
alert('Errore: ' + error);
|
alert('Errore: ' + error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deleteGruppoFromEdit(individuoId, gruppoId) {
|
||||||
|
if (!confirm('Rimuovere da questo gruppo?')) return;
|
||||||
|
|
||||||
|
fetch('/individui/' + individuoId + '/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 submitEditGruppo(gruppoId) {
|
||||||
|
var ruoloSelect = document.getElementById('edit-ruoli-' + gruppoId);
|
||||||
|
var dataInput = document.getElementById('edit-data-' + gruppoId);
|
||||||
|
var formData = new URLSearchParams();
|
||||||
|
|
||||||
|
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', dataInput ? dataInput.value : '');
|
||||||
|
|
||||||
|
fetch('/individui/{{ $individuo->id }}/gruppi/' + gruppoId, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
body: formData.toString()
|
||||||
|
})
|
||||||
|
.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 submitUploadDocumentoIndividuo() {
|
||||||
|
var nomeFile = document.getElementById('doc-nome-file');
|
||||||
|
var tipologia = document.getElementById('doc-tipologia');
|
||||||
|
var fileInput = document.getElementById('doc-file');
|
||||||
|
|
||||||
|
if (!nomeFile.value || !tipologia.value || !fileInput.files[0]) {
|
||||||
|
alert('Compila tutti i campi');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append('_token', '{{ csrf_token() }}');
|
||||||
|
formData.append('_redirect', '{{ url()->current() }}');
|
||||||
|
formData.append('visibilita', 'individuo');
|
||||||
|
formData.append('visibilita_target_id', '{{ $individuo->id }}');
|
||||||
|
formData.append('visibilita_target_type', 'App\\Models\\Individuo');
|
||||||
|
formData.append('nome_file', nomeFile.value);
|
||||||
|
formData.append('tipologia', tipologia.value);
|
||||||
|
formData.append('file', fileInput.files[0]);
|
||||||
|
|
||||||
|
document.getElementById('documento-add-form').querySelector('.btn-success').disabled = true;
|
||||||
|
|
||||||
|
fetch('/documenti', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: { 'Accept': 'application/json' }
|
||||||
|
})
|
||||||
|
.then(function() { window.location.reload(); })
|
||||||
|
.catch(function() { window.location.reload(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteDocumentoFromEdit(documentoId) {
|
||||||
|
if (!confirm('Eliminare questo documento?')) return;
|
||||||
|
|
||||||
|
var formData = new FormData();
|
||||||
|
formData.append('_token', '{{ csrf_token() }}');
|
||||||
|
formData.append('_method', 'DELETE');
|
||||||
|
formData.append('_redirect', '{{ url()->current() }}');
|
||||||
|
|
||||||
|
fetch('/documenti/' + documentoId, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
headers: { 'Accept': 'application/json' }
|
||||||
|
})
|
||||||
|
.then(function() { window.location.reload(); })
|
||||||
|
.catch(function() { window.location.reload(); });
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
@@ -1,720 +0,0 @@
|
|||||||
<?php $__env->startSection('title', 'Modifica Individuo'); ?>
|
|
||||||
<?php $__env->startSection('page_title', 'Modifica Individuo'); ?>
|
|
||||||
|
|
||||||
<?php $__env->startSection('content'); ?>
|
|
||||||
<form action="/individui/<?php echo e($individuo->id); ?>" method="POST" id="main-form">
|
|
||||||
<?php echo csrf_field(); ?>
|
|
||||||
<?php echo method_field('PUT'); ?>
|
|
||||||
<input type="hidden" name="individuo_id" value="<?php echo e($individuo->id); ?>">
|
|
||||||
<input type="hidden" name="_avatar_token" value="<?php echo e(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="<?php echo e($individuo->codice_id); ?>" disabled>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Cognome *</label>
|
|
||||||
<input type="text" name="cognome" class="form-control" value="<?php echo e($individuo->cognome); ?>" required>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Nome *</label>
|
|
||||||
<input type="text" name="nome" class="form-control" value="<?php echo e($individuo->nome); ?>" required>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Data di nascita</label>
|
|
||||||
<input type="date" name="data_nascita" class="form-control" value="<?php echo e($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" <?php echo e($individuo->genere === 'M' ? 'selected' : ''); ?>>Maschio</option>
|
|
||||||
<option value="F" <?php echo e($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">
|
|
||||||
<?php if($individuo->avatar): ?>
|
|
||||||
<img src="/individui/<?php echo e($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>
|
|
||||||
<?php 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>
|
|
||||||
<?php 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="<?php echo e($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="<?php echo e($individuo->cap); ?>">
|
|
||||||
</div>
|
|
||||||
<div class="form-group col-md-5">
|
|
||||||
<label>Città</label>
|
|
||||||
<input type="text" name="città" class="form-control" value="<?php echo e($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="<?php echo e($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" <?php echo e($individuo->tipo_documento === 'carta_identita' ? 'selected' : ''); ?>>Carta d'Identità</option>
|
|
||||||
<option value="patente" <?php echo e($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="<?php echo e($individuo->numero_documento); ?>">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Scadenza documento</label>
|
|
||||||
<input type="date" name="scadenza_documento" class="form-control" value="<?php echo e($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"><?php echo e($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">
|
|
||||||
<?php $__currentLoopData = $individuo->contatti; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $contatto): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<select name="contatti[<?php echo e($loop->index); ?>][tipo]" class="form-control">
|
|
||||||
<option value="telefono" <?php echo e($contatto->tipo === 'telefono' ? 'selected' : ''); ?>>Telefono</option>
|
|
||||||
<option value="cellulare" <?php echo e($contatto->tipo === 'cellulare' ? 'selected' : ''); ?>>Cellulare</option>
|
|
||||||
<option value="email" <?php echo e($contatto->tipo === 'email' ? 'selected' : ''); ?>>Email</option>
|
|
||||||
<option value="fax" <?php echo e($contatto->tipo === 'fax' ? 'selected' : ''); ?>>Fax</option>
|
|
||||||
<option value="web" <?php echo e($contatto->tipo === 'web' ? 'selected' : ''); ?>>Web</option>
|
|
||||||
<option value="telegram" <?php echo e($contatto->tipo === 'telegram' ? 'selected' : ''); ?>>Telegram</option>
|
|
||||||
<option value="whatsapp" <?php echo e($contatto->tipo === 'whatsapp' ? 'selected' : ''); ?>>WhatsApp</option>
|
|
||||||
<option value="altro" <?php echo e($contatto->tipo === 'altro' ? 'selected' : ''); ?>>Altro</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td><input type="text" name="contatti[<?php echo e($loop->index); ?>][valore]" class="form-control" value="<?php echo e($contatto->valore); ?>"></td>
|
|
||||||
<td><input type="text" name="contatti[<?php echo e($loop->index); ?>][etichetta]" class="form-control" value="<?php echo e($contatto->etichetta); ?>"></td>
|
|
||||||
<td class="text-center"><input type="checkbox" name="contatti[<?php echo e($loop->index); ?>][is_primary]" value="1" <?php echo e($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>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php 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>
|
|
||||||
<?php 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">
|
|
||||||
|
|
||||||
<?php 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>
|
|
||||||
<?php $__currentLoopData = $individuo->gruppi; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $gruppo): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<tr id="gruppo-row-<?php echo e($gruppo->id); ?>">
|
|
||||||
<td>
|
|
||||||
<i class="fas fa-folder text-warning mr-1"></i>
|
|
||||||
<a href="/gruppi/<?php echo e($gruppo->id); ?>"><?php echo e($gruppo->nome); ?></a>
|
|
||||||
</td>
|
|
||||||
<td><?php echo e($gruppo->diocesi?->nome ?: '-'); ?></td>
|
|
||||||
<td>
|
|
||||||
<?php $ruoliIndividuo = $individuo->getRuoliForGruppo($gruppo->id) ?>
|
|
||||||
<?php if($ruoliIndividuo->count() > 0): ?>
|
|
||||||
<?php $__currentLoopData = $ruoliIndividuo; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $ruolo): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<span class="badge badge-info mr-1"><?php echo e($ruolo->nome); ?></span>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<?php else: ?>
|
|
||||||
-
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
<td><?php echo e($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(<?php echo e($gruppo->id); ?>)" title="Modifica">
|
|
||||||
<i class="fas fa-edit"></i>
|
|
||||||
</button>
|
|
||||||
<form action="/individui/<?php echo e($individuo->id); ?>/gruppi/<?php echo e($gruppo->id); ?>" method="POST" class="mb-0">
|
|
||||||
<?php echo csrf_field(); ?> <?php echo method_field('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-<?php echo e($gruppo->id); ?>" style="display:none;">
|
|
||||||
<td colspan="5">
|
|
||||||
<form action="/individui/<?php echo e($individuo->id); ?>/gruppi/<?php echo e($gruppo->id); ?>" method="POST" class="mb-0">
|
|
||||||
<?php echo csrf_field(); ?> <?php echo method_field('PUT'); ?>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<input type="text" class="form-control form-control-sm" value="<?php echo e($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) ?>
|
|
||||||
<?php $__currentLoopData = \App\Models\Ruolo::attive(); $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $ruolo): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<option value="<?php echo e($ruolo->id); ?>" <?php echo e(in_array($ruolo->id, $selectedRuoliIds) ? 'selected' : ''); ?>><?php echo e($ruolo->nome); ?></option>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<input type="date" name="data_adesione" class="form-control form-control-sm" value="<?php echo e($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(<?php echo e($gruppo->id); ?>)"><i class="fas fa-times"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php 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>
|
|
||||||
<?php 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">
|
|
||||||
<?php echo csrf_field(); ?>
|
|
||||||
<input type="hidden" name="_redirect" value="<?php echo e(url()->current()); ?>">
|
|
||||||
<input type="hidden" name="visibilita" value="individuo">
|
|
||||||
<input type="hidden" name="visibilita_target_id" value="<?php echo e($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>
|
|
||||||
<?php 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>
|
|
||||||
<?php $__currentLoopData = $individuo->documenti; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $documento): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href="#" onclick="event.preventDefault(); previewDocumento(<?php echo e($documento->id); ?>, '<?php echo e($documento->mime_type); ?>');">
|
|
||||||
<i class="fas fa-file text-secondary mr-1"></i>
|
|
||||||
<?php echo e($documento->nome_file); ?>
|
|
||||||
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td><?php echo e(ucfirst(str_replace('_', ' ', $documento->tipologia))); ?></td>
|
|
||||||
<td><?php echo e(number_format($documento->dimensione / 1024, 1)); ?> KB</td>
|
|
||||||
<td><?php echo e($documento->created_at->format('d/m/Y')); ?></td>
|
|
||||||
<td>
|
|
||||||
<?php if($documento->file_path): ?>
|
|
||||||
<button type="button" class="btn btn-xs btn-primary" onclick="previewDocumento(<?php echo e($documento->id); ?>, '<?php echo e($documento->mime_type); ?>')" title="Anteprima">
|
|
||||||
<i class="fas fa-eye"></i>
|
|
||||||
</button>
|
|
||||||
<?php endif; ?>
|
|
||||||
<form action="/documenti/<?php echo e($documento->id); ?>" method="POST" class="d-inline">
|
|
||||||
<?php echo csrf_field(); ?> <?php echo method_field('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>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php 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>
|
|
||||||
<?php 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/<?php echo e($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>
|
|
||||||
<?php $__currentLoopData = \App\Models\Gruppo::orderBy('nome')->get(); $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $g): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<?php if(!$individuo->gruppi->contains($g->id)): ?>
|
|
||||||
<option value="<?php echo e($g->id); ?>"><?php echo e($g->full_path); ?></option>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<select id="new-gruppo-ruolo" class="form-control form-control-sm" multiple size="3">
|
|
||||||
<?php $__currentLoopData = \App\Models\Ruolo::attive(); $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $ruolo): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<option value="<?php echo e($ruolo->id); ?>"><?php echo e($ruolo->nome); ?></option>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</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>
|
|
||||||
<?php $__env->stopSection(); ?>
|
|
||||||
|
|
||||||
<?php $__env->startSection('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/<?php echo e($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 = '<?php echo e(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 = <?php echo e($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/<?php echo e($individuo->id); ?>/gruppi', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'X-CSRF-TOKEN': '<?php echo e(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', '<?php echo e(csrf_token()); ?>');
|
|
||||||
|
|
||||||
document.getElementById('avatar-message').innerHTML = '<span class="text-info"><i class="fas fa-spinner fa-spin"></i> Caricamento...</span>';
|
|
||||||
|
|
||||||
fetch('/individui/<?php echo e($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/<?php echo e($individuo->id); ?>/avatar', {
|
|
||||||
method: 'DELETE',
|
|
||||||
headers: {
|
|
||||||
'X-CSRF-TOKEN': '<?php echo e(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>
|
|
||||||
<?php $__env->stopSection(); ?>
|
|
||||||
|
|
||||||
<?php echo $__env->make('layouts.adminlte', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?><?php /**PATH /var/www/html/glastree/resources/views/individui/edit.blade.php ENDPATH**/ ?>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?php $__env->startSection('title', __('Not Found')); ?>
|
|
||||||
<?php $__env->startSection('code', '404'); ?>
|
|
||||||
<?php $__env->startSection('message', __('Not Found')); ?>
|
|
||||||
|
|
||||||
<?php echo $__env->make('errors::minimal', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?><?php /**PATH /var/www/html/glastree/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/views/404.blade.php ENDPATH**/ ?>
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="it">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title><?php echo $__env->yieldContent('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">
|
|
||||||
<?php echo $__env->yieldContent('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="<?php echo e(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> <?php echo e(Auth::user()->name); ?>
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
|
||||||
<form method="POST" action="<?php echo e(route('logout')); ?>">
|
|
||||||
<?php echo csrf_field(); ?>
|
|
||||||
<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="<?php echo e(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 <?php echo e(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 <?php echo e(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/backup" class="nav-link <?php echo e(request()->is('admin/backup*') ? 'active' : ''); ?>">
|
|
||||||
<i class="nav-icon fas fa-hdd"></i>
|
|
||||||
<p>Backup</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="/admin/activity-logs" class="nav-link <?php echo e(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"><?php echo $__env->yieldContent('page_title', 'Admin'); ?></h1>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<ol class="breadcrumb float-sm-right">
|
|
||||||
<li class="breadcrumb-item"><a href="<?php echo e(route('dashboard')); ?>">Dashboard</a></li>
|
|
||||||
<?php echo $__env->yieldContent('breadcrumbs'); ?>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<?php if(session('success')): ?>
|
|
||||||
<div class="alert alert-success"><?php echo e(session('success')); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(session('error')): ?>
|
|
||||||
<div class="alert alert-danger"><?php echo e(session('error')); ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php echo $__env->yieldContent('content'); ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer class="main-footer">
|
|
||||||
<strong><?php echo e(\App\Models\AppSetting::getFooterText()); ?></strong>
|
|
||||||
<?php if(\App\Models\AppSetting::shouldShowVersion() && \App\Models\AppSetting::getAppVersion()): ?>
|
|
||||||
<span class="ml-2">v<?php echo e(\App\Models\AppSetting::getAppVersion()); ?></span>
|
|
||||||
<?php 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>
|
|
||||||
<?php echo $__env->yieldContent('scripts'); ?>
|
|
||||||
</body>
|
|
||||||
</html><?php /**PATH /var/www/html/glastree/resources/views/admin/layout.blade.php ENDPATH**/ ?>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<?php if($paginator->hasPages()): ?>
|
|
||||||
<ul class="pagination justify-content-center" role="navigation">
|
|
||||||
|
|
||||||
<?php if($paginator->onFirstPage()): ?>
|
|
||||||
<li class="page-item disabled" aria-disabled="true" aria-label="<?php echo app('translator')->get('pagination.previous'); ?>">
|
|
||||||
<span class="page-link" aria-hidden="true">‹</span>
|
|
||||||
</li>
|
|
||||||
<?php else: ?>
|
|
||||||
<li class="page-item">
|
|
||||||
<a class="page-link" href="<?php echo e($paginator->previousPageUrl()); ?>" rel="prev" aria-label="<?php echo app('translator')->get('pagination.previous'); ?>">‹</a>
|
|
||||||
</li>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
|
|
||||||
<?php $__currentLoopData = $elements; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $element): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
|
|
||||||
<?php if(is_string($element)): ?>
|
|
||||||
<li class="page-item disabled" aria-disabled="true"><span class="page-link"><?php echo e($element); ?></span></li>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
|
|
||||||
<?php if(is_array($element)): ?>
|
|
||||||
<?php $__currentLoopData = $element; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $page => $url): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<?php if($page == $paginator->currentPage()): ?>
|
|
||||||
<li class="page-item active" aria-current="page"><span class="page-link"><?php echo e($page); ?></span></li>
|
|
||||||
<?php elseif($page >= $paginator->currentPage() - 2 && $page <= $paginator->currentPage() + 2): ?>
|
|
||||||
<li class="page-item"><a class="page-link" href="<?php echo e($url); ?>"><?php echo e($page); ?></a></li>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
|
|
||||||
|
|
||||||
<?php if($paginator->hasMorePages()): ?>
|
|
||||||
<li class="page-item">
|
|
||||||
<a class="page-link" href="<?php echo e($paginator->nextPageUrl()); ?>" rel="next" aria-label="<?php echo app('translator')->get('pagination.next'); ?>">›</a>
|
|
||||||
</li>
|
|
||||||
<?php else: ?>
|
|
||||||
<li class="page-item disabled" aria-disabled="true" aria-label="<?php echo app('translator')->get('pagination.next'); ?>">
|
|
||||||
<span class="page-link" aria-hidden="true">›</span>
|
|
||||||
</li>
|
|
||||||
<?php endif; ?>
|
|
||||||
</ul>
|
|
||||||
<?php endif; ?><?php /**PATH /var/www/html/glastree/resources/views/vendor/pagination/simple-bootstrap-4.blade.php ENDPATH**/ ?>
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
<?php $appName = \App\Models\AppSetting::getAppName() ?? 'Glastree'; ?>
|
|
||||||
<?php $__env->startSection('title', 'Backup - ' . $appName); ?>
|
|
||||||
<?php $__env->startSection('page_title', 'Backup e Migrazione'); ?>
|
|
||||||
<?php $__env->startSection('breadcrumbs'); ?>
|
|
||||||
<li class="breadcrumb-item"><a href="/">Home</a></li>
|
|
||||||
<li class="breadcrumb-item"><a href="/admin/utenti">Admin</a></li>
|
|
||||||
<li class="breadcrumb-item active">Backup</li>
|
|
||||||
<?php $__env->stopSection(); ?>
|
|
||||||
|
|
||||||
<?php $__env->startSection('content'); ?>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-8">
|
|
||||||
|
|
||||||
<div class="card card-outline card-primary">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fas fa-hdd mr-2"></i>Backup Disponibili</h3>
|
|
||||||
<div class="card-tools">
|
|
||||||
<form method="POST" action="<?php echo e(route('admin.backup.run')); ?>" style="display:inline">
|
|
||||||
<?php echo csrf_field(); ?>
|
|
||||||
<button type="submit" class="btn btn-primary btn-sm">
|
|
||||||
<i class="fas fa-play mr-1"></i> Esegui Backup Ora
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-0">
|
|
||||||
<?php if(count($backups) > 0): ?>
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-striped mb-0">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>File</th>
|
|
||||||
<th>Data</th>
|
|
||||||
<th>Dimensione</th>
|
|
||||||
<th>Contenuto</th>
|
|
||||||
<th class="text-center">Azioni</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php $__currentLoopData = $backups; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $backup): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<tr>
|
|
||||||
<td><code><?php echo e($backup['filename']); ?></code></td>
|
|
||||||
<td><?php echo e(date('d/m/Y H:i', $backup['last_modified'])); ?></td>
|
|
||||||
<td><?php echo e($backup['size_formatted']); ?></td>
|
|
||||||
<td>
|
|
||||||
<?php if($backup['manifest']): ?>
|
|
||||||
<span class="badge badge-info" title="PHP <?php echo e($backup['manifest']['php_version'] ?? '?'); ?>, Laravel <?php echo e($backup['manifest']['laravel_version'] ?? '?'); ?>">
|
|
||||||
<i class="fas fa-database mr-1"></i>DB
|
|
||||||
</span>
|
|
||||||
<?php if(isset($backup['manifest']['db_name'])): ?>
|
|
||||||
<small class="text-muted"><?php echo e($backup['manifest']['db_name']); ?></small>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php else: ?>
|
|
||||||
<span class="badge badge-secondary">N/A</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<a href="<?php echo e(route('admin.backup.download', $backup['filename'])); ?>" class="btn btn-success btn-sm" title="Scarica">
|
|
||||||
<i class="fas fa-download"></i>
|
|
||||||
</a>
|
|
||||||
<form method="POST" action="<?php echo e(route('admin.backup.destroy', $backup['filename'])); ?>" style="display:inline" onsubmit="return confirm('Eliminare definitivamente questo backup?')">
|
|
||||||
<?php echo csrf_field(); ?>
|
|
||||||
<?php echo method_field('DELETE'); ?>
|
|
||||||
<button type="submit" class="btn btn-danger btn-sm" title="Elimina">
|
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="text-center py-5">
|
|
||||||
<i class="fas fa-hdd fa-3x text-muted mb-3"></i>
|
|
||||||
<p class="text-muted mb-0">Nessun backup disponibile.</p>
|
|
||||||
<p class="text-muted">Clicca "Esegui Backup Ora" per creare il primo backup.</p>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="card card-outline card-info">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fas fa-info-circle mr-2"></i>Cosa Include il Backup</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-0">
|
|
||||||
<table class="table table-bordered mb-0">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Componente</th>
|
|
||||||
<th>Incluso</th>
|
|
||||||
<th>Dettagli</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td><i class="fas fa-database text-primary mr-2"></i> Database (MySQL)</td>
|
|
||||||
<td><span class="badge badge-success">Sempre</span></td>
|
|
||||||
<td>Struttura, dati, stored procedure, eventi, trigger</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><i class="fas fa-file text-warning mr-2"></i> File caricati</td>
|
|
||||||
<td>
|
|
||||||
<?php if($config['backup_include_files']): ?>
|
|
||||||
<span class="badge badge-success">Sì</span>
|
|
||||||
<?php else: ?>
|
|
||||||
<span class="badge badge-secondary">No</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
<td>Documenti, avatar, gallerie — dalla cartella storage</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><i class="fas fa-cog text-secondary mr-2"></i> Configurazione (.env)</td>
|
|
||||||
<td>
|
|
||||||
<?php if($config['backup_include_env']): ?>
|
|
||||||
<span class="badge badge-success">Sì</span>
|
|
||||||
<?php else: ?>
|
|
||||||
<span class="badge badge-secondary">No</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
<td>APP_KEY, DB credentials, SMTP, OAuth token</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><i class="fas fa-tag text-info mr-2"></i> Metadati</td>
|
|
||||||
<td><span class="badge badge-success">Sempre</span></td>
|
|
||||||
<td>Manifest JSON con versione app, PHP, Laravel</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-4">
|
|
||||||
|
|
||||||
<div class="card card-outline card-secondary">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fas fa-sliders-h mr-2"></i>Configurazione Backup</h3>
|
|
||||||
</div>
|
|
||||||
<form method="POST" action="<?php echo e(route('admin.backup.config')); ?>">
|
|
||||||
<?php echo csrf_field(); ?>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="backup_path">Percorso backup</label>
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-prepend">
|
|
||||||
<span class="input-group-text"><code>storage/app/</code></span>
|
|
||||||
</div>
|
|
||||||
<input type="text" name="backup_path" id="backup_path" class="form-control" value="<?php echo e($config['backup_path']); ?>">
|
|
||||||
</div>
|
|
||||||
<small class="text-muted">Cartella dove salvare i backup (relativa a storage/app/)</small>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="backup_retention_days">Ritenzione (giorni)</label>
|
|
||||||
<input type="number" name="backup_retention_days" id="backup_retention_days" class="form-control" value="<?php echo e($config['backup_retention_days']); ?>" min="1" max="365">
|
|
||||||
<small class="text-muted">I backup più vecchi vengono eliminati automaticamente</small>
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb-0">
|
|
||||||
<div class="custom-control custom-switch">
|
|
||||||
<input type="checkbox" class="custom-control-input" id="backup_include_files" name="backup_include_files" value="1" <?php echo e($config['backup_include_files'] ? 'checked' : ''); ?>>
|
|
||||||
<label class="custom-control-label" for="backup_include_files">Includi file caricati</label>
|
|
||||||
</div>
|
|
||||||
<div class="custom-control custom-switch mt-2">
|
|
||||||
<input type="checkbox" class="custom-control-input" id="backup_include_env" name="backup_include_env" value="1" <?php echo e($config['backup_include_env'] ? 'checked' : ''); ?>>
|
|
||||||
<label class="custom-control-label" for="backup_include_env">Includi .env (configurazione)</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer">
|
|
||||||
<button type="submit" class="btn btn-secondary btn-block">
|
|
||||||
<i class="fas fa-save mr-1"></i> Salva Configurazione
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="card card-outline card-warning">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fas fa-clock mr-2"></i>Backup Automatico</h3>
|
|
||||||
<div class="card-tools">
|
|
||||||
<?php $autoEnabled = \App\Models\AppSetting::getSetting('backup_auto_enabled', false) ?>
|
|
||||||
<form method="POST" action="<?php echo e(route('admin.backup.toggle-auto')); ?>" style="display:inline">
|
|
||||||
<?php echo csrf_field(); ?>
|
|
||||||
<button type="submit" class="btn btn-sm <?php echo e($autoEnabled ? 'btn-success' : 'btn-secondary'); ?>">
|
|
||||||
<i class="fas <?php echo e($autoEnabled ? 'fa-check-circle' : 'fa-times-circle'); ?> mr-1"></i>
|
|
||||||
<?php echo e($autoEnabled ? 'Attivo' : 'Disattivo'); ?>
|
|
||||||
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<form method="POST" action="<?php echo e(route('admin.backup.save-auto')); ?>">
|
|
||||||
<?php echo csrf_field(); ?>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="backup_auto_frequency">Frequenza</label>
|
|
||||||
<select name="backup_auto_frequency" id="backup_auto_frequency" class="form-control">
|
|
||||||
<option value="daily" <?php echo e(\App\Models\AppSetting::getSetting('backup_auto_frequency', 'daily') === 'daily' ? 'selected' : ''); ?>>Giornaliero</option>
|
|
||||||
<option value="weekly" <?php echo e(\App\Models\AppSetting::getSetting('backup_auto_frequency', 'daily') === 'weekly' ? 'selected' : ''); ?>>Settimanale</option>
|
|
||||||
<option value="monthly" <?php echo e(\App\Models\AppSetting::getSetting('backup_auto_frequency', 'daily') === 'monthly' ? 'selected' : ''); ?>>Mensile</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="form-group mb-0">
|
|
||||||
<label for="backup_auto_hour">Ora di esecuzione</label>
|
|
||||||
<select name="backup_auto_hour" id="backup_auto_hour" class="form-control">
|
|
||||||
<?php for($h = 0; $h <= 23; $h++): ?>
|
|
||||||
<option value="<?php echo e($h); ?>" <?php echo e(\App\Models\AppSetting::getSetting('backup_auto_hour', 3) == $h ? 'selected' : ''); ?>><?php echo e(sprintf('%02d:00', $h)); ?></option>
|
|
||||||
<?php endfor; ?>
|
|
||||||
</select>
|
|
||||||
<small class="text-muted">Ora del giorno (formato 24h). Consigliato: 03:00 (notte)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer">
|
|
||||||
<button type="submit" class="btn btn-warning btn-block">
|
|
||||||
<i class="fas fa-save mr-1"></i> Salva Automatico
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="card card-outline card-danger">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fas fa-truck mr-2"></i>Migrazione Server</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<p class="text-muted">Scarica il backup, poi esegui lo script di setup sul nuovo server per una migrazione completa.</p>
|
|
||||||
<a href="<?php echo e(route('help')); ?>#help-backup" class="btn btn-danger btn-block" target="_blank">
|
|
||||||
<i class="fas fa-book mr-1"></i> Guida alla Migrazione
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="alert alert-info">
|
|
||||||
<i class="fas fa-info-circle mr-2"></i>
|
|
||||||
<strong>Importante:</strong> Per il backup automatico, assicurati che il cron di Laravel sia configurato sul server:
|
|
||||||
<code class="ml-2">* * * * * cd <?php echo e(base_path()); ?> && php artisan schedule:run >> /dev/null 2>&1</code>
|
|
||||||
</div>
|
|
||||||
<?php $__env->stopSection(); ?>
|
|
||||||
|
|
||||||
<?php echo $__env->make('admin.layout', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?><?php /**PATH /var/www/html/glastree/resources/views/admin/backup/index.blade.php ENDPATH**/ ?>
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,72 +0,0 @@
|
|||||||
<?php $attributes ??= new \Illuminate\View\ComponentAttributeBag;
|
|
||||||
|
|
||||||
$__newAttributes = [];
|
|
||||||
$__propNames = \Illuminate\View\ComponentAttributeBag::extractPropNames((['cartelle', 'currentFolderId' => null, 'level' => 0, 'canWrite' => false, 'canDelete' => false]));
|
|
||||||
|
|
||||||
foreach ($attributes->all() as $__key => $__value) {
|
|
||||||
if (in_array($__key, $__propNames)) {
|
|
||||||
$$__key = $$__key ?? $__value;
|
|
||||||
} else {
|
|
||||||
$__newAttributes[$__key] = $__value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$attributes = new \Illuminate\View\ComponentAttributeBag($__newAttributes);
|
|
||||||
|
|
||||||
unset($__propNames);
|
|
||||||
unset($__newAttributes);
|
|
||||||
|
|
||||||
foreach (array_filter((['cartelle', 'currentFolderId' => null, 'level' => 0, 'canWrite' => false, 'canDelete' => false]), 'is_string', ARRAY_FILTER_USE_KEY) as $__key => $__value) {
|
|
||||||
$$__key = $$__key ?? $__value;
|
|
||||||
}
|
|
||||||
|
|
||||||
$__defined_vars = get_defined_vars();
|
|
||||||
|
|
||||||
foreach ($attributes->all() as $__key => $__value) {
|
|
||||||
if (array_key_exists($__key, $__defined_vars)) unset($$__key);
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($__defined_vars, $__key, $__value); ?>
|
|
||||||
|
|
||||||
<?php $__currentLoopData = $cartelle; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $cartella): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<?php
|
|
||||||
$isActive = $currentFolderId && (int) $currentFolderId === (int) $cartella->id;
|
|
||||||
$hasChildren = $cartella->children->count() > 0;
|
|
||||||
?>
|
|
||||||
<div class="list-group-item d-flex align-items-center py-1 px-2 <?php echo e($isActive ? 'active' : ''); ?>"
|
|
||||||
style="padding-left: <?php echo e(20 + $level * 20); ?>px !important;">
|
|
||||||
<a href="/documenti?folder_id=<?php echo e($cartella->id); ?>"
|
|
||||||
class="d-flex align-items-center text-decoration-none flex-grow-1 <?php echo e($isActive ? 'text-white' : ''); ?>"
|
|
||||||
style="min-width: 0;">
|
|
||||||
<i class="fas fa-folder <?php echo e($isActive ? 'text-white' : 'text-warning'); ?> mr-2"></i>
|
|
||||||
<span class="small text-truncate"><?php echo e($cartella->nome); ?></span>
|
|
||||||
</a>
|
|
||||||
<?php if($canWrite || $canDelete): ?>
|
|
||||||
<div class="folder-actions ml-1 flex-shrink-0">
|
|
||||||
<?php if($canWrite): ?>
|
|
||||||
<button type="button" class="btn btn-xs btn-link p-0 mr-1" onclick='event.stopPropagation(); renameFolder(<?php echo e($cartella->id); ?>, <?php echo e(json_encode($cartella->nome)); ?>)' title="Rinomina">
|
|
||||||
<i class="fas fa-pen <?php echo e($isActive ? 'text-white' : 'text-muted'); ?>"></i>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-xs btn-link p-0 mr-1" onclick='event.stopPropagation(); moveFolder(<?php echo e($cartella->id); ?>, <?php echo e(json_encode($cartella->nome)); ?>)' title="Sposta">
|
|
||||||
<i class="fas fa-folder-open <?php echo e($isActive ? 'text-white' : 'text-info'); ?>"></i>
|
|
||||||
</button>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if($canDelete): ?>
|
|
||||||
<button type="button" class="btn btn-xs btn-link p-0" onclick='event.stopPropagation(); deleteFolder(<?php echo e($cartella->id); ?>, <?php echo e(json_encode($cartella->nome)); ?>)' title="Elimina">
|
|
||||||
<i class="fas fa-times <?php echo e($isActive ? 'text-white' : 'text-danger'); ?>"></i>
|
|
||||||
</button>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<?php if($hasChildren): ?>
|
|
||||||
<?php echo $__env->make('documenti._folder_tree', [
|
|
||||||
'cartelle' => $cartella->children,
|
|
||||||
'currentFolderId' => $currentFolderId,
|
|
||||||
'level' => $level + 1,
|
|
||||||
'canWrite' => $canWrite,
|
|
||||||
'canDelete' => $canDelete,
|
|
||||||
], array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<?php /**PATH /var/www/html/glastree/resources/views/documenti/_folder_tree.blade.php ENDPATH**/ ?>
|
|
||||||
@@ -1,972 +0,0 @@
|
|||||||
<?php $appName = \App\Models\AppSetting::getAppName() ?? 'Glastree'; ?>
|
|
||||||
<?php $__env->startSection('title', 'Guida - ' . $appName); ?>
|
|
||||||
<?php $__env->startSection('page_title', 'Guida alla Configurazione'); ?>
|
|
||||||
<?php $__env->startSection('breadcrumbs'); ?>
|
|
||||||
<li class="breadcrumb-item"><a href="/">Home</a></li>
|
|
||||||
<li class="breadcrumb-item active">Guida</li>
|
|
||||||
<?php $__env->stopSection(); ?>
|
|
||||||
|
|
||||||
<?php $__env->startSection('content'); ?>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-3">
|
|
||||||
<div class="card card-outline card-primary">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title">Argomenti</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-0">
|
|
||||||
<ul class="nav nav-pills flex-column" id="helpTabs">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link active" href="#help-webdav" data-toggle="tab">
|
|
||||||
<i class="fas fa-cloud mr-2"></i> WebDAV / Nextcloud
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#help-googledrive" data-toggle="tab">
|
|
||||||
<i class="fab fa-google-drive mr-2"></i> Google Drive
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#help-tipologie" data-toggle="tab">
|
|
||||||
<i class="fas fa-tags mr-2"></i> Tipologie e Dati
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#help-email" data-toggle="tab">
|
|
||||||
<i class="fas fa-envelope mr-2"></i> Email / SMTP
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#help-installazione" data-toggle="tab">
|
|
||||||
<i class="fas fa-terminal mr-2"></i> Installazione
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#help-backup" data-toggle="tab">
|
|
||||||
<i class="fas fa-hdd mr-2"></i> Backup e Migrazione
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="#help-docker" data-toggle="tab">
|
|
||||||
<i class="fab fa-docker mr-2"></i> Docker
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer">
|
|
||||||
<a href="<?php echo e(route('help.pdf.download')); ?>" class="btn btn-primary btn-block">
|
|
||||||
<i class="fas fa-file-pdf mr-2"></i> Scarica Guida PDF
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-9">
|
|
||||||
<div class="tab-content">
|
|
||||||
|
|
||||||
<div class="tab-pane active" id="help-webdav">
|
|
||||||
<div class="card card-outline card-info">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fas fa-cloud mr-2"></i> WebDAV / Nextcloud</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h5>Cos'è</h5>
|
|
||||||
<p>WebDAV è un protocollo che permette di accedere a file su server remoti. Nextcloud lo utilizza per la sincronizzazione dei file.</p>
|
|
||||||
|
|
||||||
<h5>Configurazione</h5>
|
|
||||||
<p>Vai in <strong>Impostazioni → Repository</strong> e crea un nuovo repository di tipo WebDAV.</p>
|
|
||||||
|
|
||||||
<div class="callout callout-info">
|
|
||||||
<h6>Campi obbligatori</h6>
|
|
||||||
<dl class="row mb-0">
|
|
||||||
<dt class="col-sm-3">Nome</dt>
|
|
||||||
<dd class="col-sm-9">Un nome descrittivo (es. "Nextcloud Lavoro")</dd>
|
|
||||||
<dt class="col-sm-3">Base URI</dt>
|
|
||||||
<dd class="col-sm-9">L'URL del tuo server WebDAV.
|
|
||||||
<br><em>Nextcloud:</em> <code>https://tuodominio.it/remote.php/dav/files/utente/</code>
|
|
||||||
<br><em>Altri:</em> <code>https://server.webdav.it/</code></dd>
|
|
||||||
<dt class="col-sm-3">Root</dt>
|
|
||||||
<dd class="col-sm-9">Percorso base all'interno del server (es. <code>/Documenti/</code>). Lascia <code>/</code> per usare la root.</dd>
|
|
||||||
<dt class="col-sm-3">Username</dt>
|
|
||||||
<dd class="col-sm-9">Il tuo nome utente</dd>
|
|
||||||
<dt class="col-sm-3">Password</dt>
|
|
||||||
<dd class="col-sm-9">La password dell'account</dd>
|
|
||||||
<dt class="col-sm-3">Auth Type</dt>
|
|
||||||
<dd class="col-sm-9"><code>Basic</code> per la maggior parte dei server (Nextcloud, ownCloud).</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5 class="mt-4">⚠️ Nome utente con @ (chiocciola)</h5>
|
|
||||||
<p>Se il tuo nome utente contiene il carattere <code>@</code> (es. <code>mario.rossi@azienda.com</code>), segui queste regole:</p>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Nel campo "Base URI":</strong> l'indirizzo email deve essere <strong>URL-encoded</strong>:
|
|
||||||
<br><code>https://server.it/remote.php/dav/files/mario.rossi%40azienda.com/</code>
|
|
||||||
<br><small>(il carattere <code>@</code> diventa <code>%40</code>)</small></li>
|
|
||||||
<li><strong>Nel campo "Username":</strong> inserisci l'indirizzo email <strong>normale</strong> con la <code>@</code> vera.</li>
|
|
||||||
</ul>
|
|
||||||
<div class="alert alert-info">
|
|
||||||
<i class="fas fa-info-circle"></i>
|
|
||||||
Il sistema gestisce automaticamente la differenza tra l'URI (che richiede <code>%40</code>) e l'autenticazione (che usa la <code>@</code> reale).
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Test connessione</h5>
|
|
||||||
<p>Dopo aver salvato, usa il pulsante <span class="badge badge-success"><i class="fas fa-plug"></i> Test</span> nella tabella dei repository per verificare che la connessione funzioni.</p>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Importare file</h5>
|
|
||||||
<p>Apri la sezione <strong>Documenti</strong>, clicca sul pulsante del repository nella toolbar per sfogliare i file remoti, poi clicca <span class="badge badge-success"><i class="fas fa-save"></i> Salva tra i documenti</span> su un file per importarlo come documento locale.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="tab-pane" id="help-googledrive">
|
|
||||||
<div class="card card-outline card-danger">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fab fa-google-drive mr-2"></i> Google Drive</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h5>Cos'è</h5>
|
|
||||||
<p>Integrazione con Google Drive per sfogliare e importare file direttamente dal tuo spazio Google Drive.</p>
|
|
||||||
|
|
||||||
<h5>Requisiti</h5>
|
|
||||||
<ol>
|
|
||||||
<li>Un account Google / Google Workspace</li>
|
|
||||||
<li>Un progetto sulla <strong>Google Cloud Console</strong> con l'API Google Drive abilitata</li>
|
|
||||||
<li>Credenziali OAuth 2.0 (Client ID e Client Secret)</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h5>Passo 1: Creare il progetto Google Cloud</h5>
|
|
||||||
<ol>
|
|
||||||
<li>Vai su <a href="https://console.developers.google.com/" target="_blank">Google Cloud Console</a></li>
|
|
||||||
<li>Crea un nuovo progetto o selezionane uno esistente</li>
|
|
||||||
<li>Vai a <strong>API e servizi → Libreria</strong></li>
|
|
||||||
<li>Cerca "<strong>Google Drive API</strong>" e <strong>abilitala</strong></li>
|
|
||||||
<li>Vai a <strong>API e servizi → Credenziali</strong></li>
|
|
||||||
<li>Clicca "<strong>Crea credenziali → ID client OAuth</strong>"</li>
|
|
||||||
<li>Tipo applicazione: "<strong>Applicazione Web</strong>"</li>
|
|
||||||
<li><strong>URI di reindirizzamento autorizzato:</strong> aggiungi <code><?php echo e(url('/auth/google-drive/callback')); ?></code></li>
|
|
||||||
<li>Completa la creazione e <strong>copia Client ID e Client Secret</strong></li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h5>Passo 2: Configurazione nell'app</h5>
|
|
||||||
<ol>
|
|
||||||
<li>Vai in <strong>Impostazioni → Repository</strong></li>
|
|
||||||
<li>Clicca "<strong>Nuovo Repository</strong>" e seleziona tipo <strong>Google Drive</strong></li>
|
|
||||||
<li>Inserisci un nome descrittivo (es. "Drive Lavoro")</li>
|
|
||||||
<li>Incolla <strong>Client ID</strong> e <strong>Client Secret</strong></li>
|
|
||||||
<li><strong>Folder ID (opzionale):</strong> se vuoi limitare l'accesso a una cartella specifica, inserisci il suo ID (lo trovi nell'URL quando apri la cartella in Google Drive: <code>https://drive.google.com/drive/folders/<strong>ID_QUI</strong></code>)</li>
|
|
||||||
<li>Clicca "<strong>Autorizza Google Drive</strong>" — verrai reindirizzato a Google per concedere i permessi</li>
|
|
||||||
<li>Dopo l'autorizzazione, il refresh token verrà salvato automaticamente</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h5>⚠️ Google Drive API non abilitata</h5>
|
|
||||||
<p>Se vedi l'errore <em>"Google Drive API non abilitata"</em> nel test connessione, significa che l'API Drive non è stata attivata nel tuo progetto Google Cloud.</p>
|
|
||||||
<div class="callout callout-danger">
|
|
||||||
<h6>Soluzione</h6>
|
|
||||||
<p>Vai su <a href="https://console.developers.google.com/apis/api/drive.googleapis.com/overview" target="_blank">
|
|
||||||
https://console.developers.google.com/apis/api/drive.googleapis.com/overview
|
|
||||||
</a></p>
|
|
||||||
<p>Seleziona il progetto corretto dall'header in alto, clicca <strong>"Enable"</strong> e attendi qualche minuto prima di riprovare.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5>Importare file</h5>
|
|
||||||
<p>Stessa procedura di WebDAV: apri <strong>Documenti</strong>, clicca sul repository Drive, sfoglia i file e usa <span class="badge badge-success"><i class="fas fa-save"></i> Salva tra i documenti</span>.</p>
|
|
||||||
|
|
||||||
<h5 class="mt-4">⚠️ File Google (Documenti, Fogli, Presentazioni)</h5>
|
|
||||||
<p>I file creati con Google Workspace (Documenti Google, Fogli Google, Presentazioni Google, Disegni Google) non possono essere scaricati direttamente perché non hanno un formato binario nativo.</p>
|
|
||||||
<p>Il sistema li converte automaticamente in formato Office Open XML durante l'import:</p>
|
|
||||||
<table class="table table-sm table-bordered">
|
|
||||||
<thead><tr><th>File Google</th><th>Formato convertito</th></tr></thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>Documento Google</td><td><code>.docx</code> (Word)</td></tr>
|
|
||||||
<tr><td>Foglio Google</td><td><code>.xlsx</code> (Excel)</td></tr>
|
|
||||||
<tr><td>Presentazione Google</td><td><code>.pptx</code> (PowerPoint)</td></tr>
|
|
||||||
<tr><td>Disegno Google</td><td><code>.png</code> (Immagine)</td></tr>
|
|
||||||
<tr><td>Altri (Moduli, Script)</td><td><code>.pdf</code> (PDF)</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="alert alert-info">
|
|
||||||
<i class="fas fa-info-circle"></i>
|
|
||||||
Il nome del file viene automaticamente esteso con l'estensione corretta (es. "Relazione" → "Relazione.docx").
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="tab-pane" id="help-tipologie">
|
|
||||||
<div class="card card-outline card-success">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fas fa-tags mr-2"></i> Tipologie e Gestione Dati</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h5>Tipologie Documenti</h5>
|
|
||||||
<p>Le tipologie classificano i documenti (avatar, galleria, documento, statuto, altro).</p>
|
|
||||||
<p>Si gestiscono in <strong>Impostazioni → Tipologie Documenti</strong>. Puoi aggiungere, rinominare, riordinare ed eliminare tipologie (tranne quelle con documenti associati).</p>
|
|
||||||
|
|
||||||
<h5>Tipologie Individui</h5>
|
|
||||||
<p>Classificano gli individui (es. socio, volontario, dipendente). Si gestiscono in <strong>Impostazioni → Tipologie</strong>.</p>
|
|
||||||
|
|
||||||
<h5>Tipologie Eventi</h5>
|
|
||||||
<p>Classificano gli eventi (es. riunione, assemblea, formazione). Si gestiscono in <strong>Impostazioni → Tipologie Eventi</strong>.</p>
|
|
||||||
|
|
||||||
<h5>Ruoli Individui</h5>
|
|
||||||
<p>Definiscono il ruolo o la carica di un individuo (es. presidente, segretario, tesoriere). Si gestiscono in <strong>Impostazioni → Ruoli</strong>.</p>
|
|
||||||
|
|
||||||
<h5>Visibilità Documenti</h5>
|
|
||||||
<p>Ogni documento può avere diversi livelli di visibilità:</p>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Pubblico</strong> — visibile a tutti gli utenti</li>
|
|
||||||
<li><strong>Individuo</strong> — visibile solo a uno specifico individuo</li>
|
|
||||||
<li><strong>Gruppo</strong> — visibile solo ai membri di un gruppo</li>
|
|
||||||
<li><strong>Evento</strong> — visibile solo ai partecipanti di un evento</li>
|
|
||||||
<li><strong>Mailing</strong> — visibile solo ai membri di una mailing list</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="tab-pane" id="help-email">
|
|
||||||
<div class="card card-outline card-warning">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fas fa-envelope mr-2"></i> Email / SMTP</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h5>Configurazione Email</h5>
|
|
||||||
<p>Vai in <strong>Impostazioni → Email</strong> per configurare la posta elettronica.</p>
|
|
||||||
|
|
||||||
<h5>Server IMAP (Ricezione)</h5>
|
|
||||||
<p>Usato per ricevere e leggere le email direttamente nell'app.</p>
|
|
||||||
<div class="callout callout-info">
|
|
||||||
<h6>Campi consigliati per provider comuni</h6>
|
|
||||||
<table class="table table-sm table-bordered">
|
|
||||||
<thead><tr><th>Provider</th><th>Host</th><th>Porta</th><th>Crittografia</th></tr></thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>Gmail</td><td><code>imap.gmail.com</code></td><td><code>993</code></td><td>SSL</td></tr>
|
|
||||||
<tr><td>Outlook/Office 365</td><td><code>outlook.office365.com</code></td><td><code>993</code></td><td>SSL</td></tr>
|
|
||||||
<tr><td>Libero</td><td><code>imapmail.libero.it</code></td><td><code>993</code></td><td>SSL</td></tr>
|
|
||||||
<tr><td>Aruba</td><td><code>imap.aruba.it</code></td><td><code>993</code></td><td>SSL</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5>Server SMTP (Invio)</h5>
|
|
||||||
<p>Usato per inviare email dall'app. Supporta account multipli (mittenti).</p>
|
|
||||||
<div class="callout callout-info">
|
|
||||||
<h6>Campi consigliati per provider comuni</h6>
|
|
||||||
<table class="table table-sm table-bordered">
|
|
||||||
<thead><tr><th>Provider</th><th>Host</th><th>Porta</th><th>Crittografia</th></tr></thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>Gmail</td><td><code>smtp.gmail.com</code></td><td><code>587</code></td><td>TLS</td></tr>
|
|
||||||
<tr><td>Outlook/Office 365</td><td><code>smtp.office365.com</code></td><td><code>587</code></td><td>TLS</td></tr>
|
|
||||||
<tr><td>Libero</td><td><code>smtp.libero.it</code></td><td><code>465</code></td><td>SSL</td></tr>
|
|
||||||
<tr><td>Aruba</td><td><code>smtp.aruba.it</code></td><td><code>465</code></td><td>SSL</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5>Gmail / Google Workspace</h5>
|
|
||||||
<div class="alert alert-warning">
|
|
||||||
<i class="fas fa-exclamation-triangle"></i>
|
|
||||||
Gmail richiede una <strong>Password per l'app</strong> (non la password normale) se usi la verifica in due passaggi. Puoi generarla su
|
|
||||||
<a href="https://myaccount.google.com/apppasswords" target="_blank">https://myaccount.google.com/apppasswords</a>.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5>Mailing</h5>
|
|
||||||
<p>Le <strong>Mailing List</strong> permettono di inviare email a gruppi predefiniti di destinatari.</p>
|
|
||||||
<ul>
|
|
||||||
<li>Crea una mailing list in <strong>Mailing List → Nuova</strong></li>
|
|
||||||
<li>Puoi popolare la lista da individui, gruppi o manualmente</li>
|
|
||||||
<li>Invia email alla lista da <strong>Email → Nuova Email</strong>, selezionando la mailing list come destinatario</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h5>Test connessione</h5>
|
|
||||||
<p>Dopo aver configurato i server, usa il pulsante <span class="badge badge-success"><i class="fas fa-vial"></i> Test Connessione</span> per verificare che IMAP e SMTP funzionino correttamente.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="tab-pane" id="help-backup">
|
|
||||||
<div class="card card-outline card-danger">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fas fa-hdd mr-2"></i> Backup e Migrazione Server</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h5>Cos'è</h5>
|
|
||||||
<p>Il sistema di backup crea un archivio ZIP contenente tutto il necessario per ripristinare l'applicazione su un nuovo server. Include database, file caricati e configurazione.</p>
|
|
||||||
|
|
||||||
<h5>Cosa include il backup</h5>
|
|
||||||
<table class="table table-sm table-bordered">
|
|
||||||
<thead><tr><th>Componente</th><th>Incluso</th><th>Dettagli</th></tr></thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>Database (MySQL)</td><td><span class="badge badge-success">Sempre</span></td><td>Struttura, dati, stored procedure, eventi, trigger</td></tr>
|
|
||||||
<tr><td>File caricati</td><td><span class="badge badge-success">Opzionale</span></td><td>Documenti, avatar, gallerie</td></tr>
|
|
||||||
<tr><td>Configurazione .env</td><td><span class="badge badge-success">Opzionale</span></td><td>APP_KEY, credenziali DB, SMTP, token OAuth</td></tr>
|
|
||||||
<tr><td>Metadati</td><td><span class="badge badge-success">Sempre</span></td><td>Versione app, PHP, Laravel</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Come eseguire un backup</h5>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="callout callout-info">
|
|
||||||
<h6>Via Interfaccia Web</h6>
|
|
||||||
<ol class="mb-0">
|
|
||||||
<li>Vai su <strong>Admin → Backup</strong></li>
|
|
||||||
<li>Verifica le impostazioni (includi files/.env)</li>
|
|
||||||
<li>Clicca <span class="badge badge-primary"><i class="fas fa-play"></i> Esegui Backup Ora</span></li>
|
|
||||||
<li>Scarica il file ZIP generato</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="callout callout-success">
|
|
||||||
<h6>Via Comando</h6>
|
|
||||||
<pre class="mb-0"><code>php artisan backup:run</code></pre>
|
|
||||||
<p class="mb-0 mt-2 small">Il file viene creato in <code>storage/app/backups/</code></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Backup automatico</h5>
|
|
||||||
<p>Puoi abilitare il backup automatico dalla pagina <strong>Admin → Backup</strong>:</p>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Frequenza:</strong> giornaliero, settimanale o mensile</li>
|
|
||||||
<li><strong>Ora:</strong> consigliata 03:00 (notte)</li>
|
|
||||||
<li><strong>Requisito:</strong> il cron di Laravel deve essere configurato sul server</li>
|
|
||||||
</ul>
|
|
||||||
<div class="alert alert-warning">
|
|
||||||
<i class="fas fa-clock mr-2"></i>
|
|
||||||
Per il backup automatico, aggiungi questo cron:
|
|
||||||
<pre class="mt-2 mb-0"><code>* * * * * cd <?php echo e(base_path()); ?> && php artisan schedule:run >> /dev/null 2>&1</code></pre>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr class="my-4">
|
|
||||||
|
|
||||||
<h4><i class="fas fa-truck mr-2"></i> Procedura di Migrazione Completa</h4>
|
|
||||||
<p>Segui questa procedura per migrare l'applicazione su un nuovo server LAMP (Linux, Apache, MySQL, PHP).</p>
|
|
||||||
|
|
||||||
<div class="alert alert-danger">
|
|
||||||
<i class="fas fa-exclamation-triangle mr-2"></i>
|
|
||||||
<strong>ATTENZIONE: APP_KEY</strong>
|
|
||||||
<p class="mb-0 mt-2">L'APP_KEY cripta i dati sensibili (password email SMTP, token OAuth Google Drive, ecc.).</p>
|
|
||||||
<ul class="mt-2">
|
|
||||||
<li><strong>Se hai il .env originale dal backup:</strong> NON rigenerare APP_KEY — mantieni i dati criptati accessibili.</li>
|
|
||||||
<li><strong>Se hai perso il .env:</strong> DEVI rigenerare APP_KEY, ma perderai l'accesso ai dati criptati esistenti. Dovrai riconfigurare SMTP e OAuth.</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 1: Preparazione del server</h5>
|
|
||||||
<pre><code>sudo apt update && sudo apt upgrade -y</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 2: Installa LAMP + PHP 8.4</h5>
|
|
||||||
<pre><code>sudo apt install -y apache2 mariadb-server php8.4 php8.4-cli \
|
|
||||||
php8.4-mysql php8.4-xml php8.4-mbstring php8.4-curl \
|
|
||||||
php8.4-zip php8.4-bcmath php8.4-gd php8.4-intl \
|
|
||||||
composer git unzip
|
|
||||||
|
|
||||||
sudo a2enmod rewrite</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 3: Ottieni l'applicazione</h5>
|
|
||||||
<div class="callout callout-info py-2">
|
|
||||||
<i class="fas fa-info-circle"></i>
|
|
||||||
Puoi clonare il repository con git oppure estrarre il file ZIP ricevuto nella cartella <code>/var/www/glastree</code>.
|
|
||||||
</div>
|
|
||||||
<pre><code>cd /var/www
|
|
||||||
|
|
||||||
# Opzione A — Git clone
|
|
||||||
git clone <URL_REPOSITORY> glastree
|
|
||||||
|
|
||||||
# Opzione B — ZIP (se non hai git)
|
|
||||||
# unzip /percorso/del/glastree.zip -d glastree
|
|
||||||
|
|
||||||
cd glastree
|
|
||||||
composer install --no-dev --optimize-autoloader
|
|
||||||
npm ci && npm run build</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 4: Carica il backup</h5>
|
|
||||||
<p>Copia il file <code>backup.zip</code> sul nuovo server e estrailo:</p>
|
|
||||||
<pre><code>mkdir -p /tmp/restore
|
|
||||||
unzip backup.zip -d /tmp/restore/
|
|
||||||
ls -la /tmp/restore/
|
|
||||||
# Dovresti vedere: database.sql .env files/ manifest.json</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 5: Crea database e importa</h5>
|
|
||||||
<pre><code>sudo mysql -u root <<EOF
|
|
||||||
CREATE DATABASE glastree CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
CREATE USER 'glastree'@'localhost' IDENTIFIED BY 'ScegliPasswordFortep4ss!';
|
|
||||||
GRANT ALL ON glastree.* TO 'glastree'@'localhost';
|
|
||||||
FLUSH PRIVILEGES;
|
|
||||||
EOF
|
|
||||||
|
|
||||||
mysql -u glastree -p glastree < /tmp/restore/database.sql</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 6: Configura .env</h5>
|
|
||||||
<pre><code>cp /tmp/restore/.env .env
|
|
||||||
# Modifica se necessario:
|
|
||||||
# APP_URL=https://nuovo-dominio.it
|
|
||||||
# DB_HOST=localhost
|
|
||||||
# DB_DATABASE=glastree
|
|
||||||
# DB_USERNAME=glastree
|
|
||||||
# DB_PASSWORD=ScegliPasswordFortep4ss!
|
|
||||||
|
|
||||||
# DECIDI SULL'APP_KEY (leggi avviso sopra!)
|
|
||||||
# php artisan key:generate ← SOLO se non hai il .env originale</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 7: Ripristina file e permessi</h5>
|
|
||||||
<pre><code>cp -r /tmp/restore/files/* storage/app/documenti/
|
|
||||||
|
|
||||||
sudo chown -R www-data:www-data storage bootstrap/cache
|
|
||||||
sudo chmod -R 755 storage bootstrap/cache
|
|
||||||
|
|
||||||
php artisan storage:link</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 8: Cache</h5>
|
|
||||||
<pre><code>php artisan optimize:clear
|
|
||||||
php artisan config:cache
|
|
||||||
php artisan route:cache
|
|
||||||
php artisan view:cache</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 9: Configura Apache</h5>
|
|
||||||
<pre><code>sudo tee /etc/apache2/sites-available/glastree.conf <<APACHE
|
|
||||||
<VirtualHost *:80>
|
|
||||||
ServerName glastree.esempio.it
|
|
||||||
DocumentRoot /var/www/glastree/public
|
|
||||||
|
|
||||||
<Directory /var/www/glastree/public>
|
|
||||||
Options -Indexes +FollowSymLinks
|
|
||||||
AllowOverride All
|
|
||||||
Require all granted
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
ErrorLog \${APACHE_LOG_DIR}/glastree_error.log
|
|
||||||
CustomLog \${APACHE_LOG_DIR}/glastree_access.log combined
|
|
||||||
</VirtualHost>
|
|
||||||
APACHE
|
|
||||||
|
|
||||||
sudo a2dissite 000-default.conf
|
|
||||||
sudo a2ensite glastree.conf
|
|
||||||
sudo systemctl reload apache2</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 10: SSL Let's Encrypt</h5>
|
|
||||||
<pre><code>sudo apt install -y certbot python3-certbot-apache
|
|
||||||
sudo certbot --apache -d glastree.esempio.it</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 11: Cron per backup automatico</h5>
|
|
||||||
<pre><code>sudo crontab -u www-data -e
|
|
||||||
# Inserisci:
|
|
||||||
* * * * * cd /var/www/glastree && php artisan schedule:run >> /dev/null 2>&1</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Passo 12: Verifica finale</h5>
|
|
||||||
<ol>
|
|
||||||
<li>Apri https://glastree.esempio.it nel browser</li>
|
|
||||||
<li>Fai login con le credenziali esistenti</li>
|
|
||||||
<li>Verifica: Dashboard, Documenti, Impostazioni, Email</li>
|
|
||||||
<li>Se hai rigenerato APP_KEY: riconfigura SMTP e OAuth Google Drive</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<hr class="my-4">
|
|
||||||
|
|
||||||
<h5>PHP Web Installer</h5>
|
|
||||||
<p>L'applicazione include un <strong>wizard di installazione web</strong> nella cartella <code>installer/</code>. Questo wizard gestisce sia installazioni fresh che ripristino da backup.</p>
|
|
||||||
<p>Vedi la sezione <strong>Installazione</strong> per la guida completa.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="tab-pane" id="help-docker">
|
|
||||||
<div class="card card-outline card-primary">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fab fa-docker mr-2"></i> Docker</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h5>Creare un'immagine Docker con dati di base</h5>
|
|
||||||
<p>Puoi creare un'immagine Docker pronta all'uso che include l'applicazione e i dati di base (tipologie documenti, tipologie eventi, ruoli). Scegli la configurazione in base alle tue esigenze:</p>
|
|
||||||
|
|
||||||
<div class="callout callout-info">
|
|
||||||
<i class="fas fa-info-circle"></i>
|
|
||||||
<strong>Ottenere il codice:</strong> puoi clonare il repository con <code>git clone <URL> glastree</code> oppure, se non hai git, estrarre il file ZIP ricevuto con <code>unzip glastree.zip -d glastree</code> e posizionarti nella cartella <code>glastree</code> prima di procedere.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row mb-4">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="callout callout-info">
|
|
||||||
<h6><i class="fas fa-database"></i> SQLite (Leggero)</h6>
|
|
||||||
<p class="mb-0 small">Database embedded, nessun servizio aggiuntivo. Ideale per demo, test, sviluppo locale o ambienti con poca concorrenza.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="callout callout-success">
|
|
||||||
<h6><i class="fas fa-server"></i> MySQL (Standard)</h6>
|
|
||||||
<p class="mb-0 small">Database separato in un container MySQL. Configurazione standard, ideale per produzione, staging e ambienti con piu utenti.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5 class="mt-4">1. Dockerfile (comune a entrambe le modalita)</h5>
|
|
||||||
<p>Crea un file <code>Dockerfile</code> nella root del progetto:</p>
|
|
||||||
<pre><code>FROM php:8.4-apache
|
|
||||||
|
|
||||||
# Abilita mod_rewrite
|
|
||||||
RUN docker-php-ext-enable opcache \
|
|
||||||
&& a2enmod rewrite
|
|
||||||
|
|
||||||
# Installa estensioni PHP (sia SQLite che MySQL)
|
|
||||||
RUN docker-php-ext-install pdo_mysql pdo_sqlite bcmath gd zip
|
|
||||||
|
|
||||||
# Installa Composer
|
|
||||||
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
|
||||||
|
|
||||||
# Configura DocumentRoot per Laravel
|
|
||||||
ENV APACHE_DOCUMENT_ROOT=/var/www/html/public
|
|
||||||
RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
|
|
||||||
&& sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
|
|
||||||
|
|
||||||
# Copia l'applicazione
|
|
||||||
COPY . /var/www/html/
|
|
||||||
|
|
||||||
# Installa dipendenze e compila asset
|
|
||||||
RUN composer install --no-dev --optimize-autoloader \
|
|
||||||
&& mkdir -p storage/app/public \
|
|
||||||
&& mkdir -p storage/framework/cache/data \
|
|
||||||
&& mkdir -p storage/framework/sessions \
|
|
||||||
&& mkdir -p storage/framework/views \
|
|
||||||
&& mkdir -p storage/logs \
|
|
||||||
&& mkdir -p bootstrap/cache \
|
|
||||||
&& chown -R www-data:www-data storage bootstrap/cache
|
|
||||||
|
|
||||||
# Entrypoint per inizializzazione
|
|
||||||
COPY docker-entrypoint.sh /usr/local/bin/
|
|
||||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
|
||||||
|
|
||||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
|
||||||
CMD ["apache2-foreground"]</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">2. Entrypoint Script (comune)</h5>
|
|
||||||
<p>Crea <code>docker-entrypoint.sh</code>. Lo script rileva automaticamente la configurazione in base alle variabili d'ambiente:</p>
|
|
||||||
<pre><code>#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Se .env non esiste, lo genera dalle variabili d'ambiente
|
|
||||||
if [ ! -f .env ]; then
|
|
||||||
cp .env.example .env
|
|
||||||
|
|
||||||
if [ "$DB_CONNECTION" = "mysql" ]; then
|
|
||||||
# Configura MySQL
|
|
||||||
sed -i "s/DB_CONNECTION=mysql/DB_CONNECTION=mysql/" .env
|
|
||||||
sed -i "s/DB_HOST=.*/DB_HOST=${DB_HOST:-mysql}/" .env
|
|
||||||
sed -i "s/DB_PORT=.*/DB_PORT=${DB_PORT:-3306}/" .env
|
|
||||||
sed -i "s/DB_DATABASE=.*/DB_DATABASE=${DB_DATABASE:-glastree}/" .env
|
|
||||||
sed -i "s/DB_USERNAME=.*/DB_USERNAME=${DB_USERNAME:-glastree}/" .env
|
|
||||||
sed -i "s/DB_PASSWORD=.*/DB_PASSWORD=${DB_PASSWORD:-secret}/" .env
|
|
||||||
|
|
||||||
# Attende che MySQL sia pronto
|
|
||||||
echo "Attendo MySQL..."
|
|
||||||
until php -r "new PDO('mysql:host=${DB_HOST:-mysql};dbname=${DB_DATABASE:-glastree}', '${DB_USERNAME:-glastree}', '${DB_PASSWORD:-secret}');" 2>/dev/null; do
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
echo "MySQL pronto."
|
|
||||||
else
|
|
||||||
# Configura SQLite
|
|
||||||
sed -i 's/DB_CONNECTION=mysql/DB_CONNECTION=sqlite/' .env
|
|
||||||
sed -i 's/DB_HOST=.*//' .env
|
|
||||||
sed -i 's/DB_PORT=.*//' .env
|
|
||||||
sed -i 's/DB_DATABASE=.*/DB_DATABASE=\/var\/www\/html\/database\/database.sqlite/' .env
|
|
||||||
sed -i 's/DB_USERNAME=.*//' .env
|
|
||||||
sed -i 's/DB_PASSWORD=.*//' .env
|
|
||||||
|
|
||||||
touch database/database.sqlite
|
|
||||||
chmod 664 database/database.sqlite
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Genera APP_KEY
|
|
||||||
php artisan key:generate --force
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Esegui migration e seed se il database e vuoto
|
|
||||||
if [ ! -f .db_initialized ]; then
|
|
||||||
php artisan migrate --force
|
|
||||||
php artisan db:seed --force
|
|
||||||
touch .db_initialized
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Configura storage link
|
|
||||||
php artisan storage:link --force 2>/dev/null || true
|
|
||||||
|
|
||||||
exec "$@"</code></pre>
|
|
||||||
<pre><code>chmod +x docker-entrypoint.sh</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">3. Seed Base Dati (comune)</h5>
|
|
||||||
<p>Per includere tipologie documenti, tipologie eventi e ruoli, crea un seeder dedicato:</p>
|
|
||||||
<pre><code>php artisan make:seeder DockerBaseDataSeeder</code></pre>
|
|
||||||
<p>Contenuto di <code>database/seeders/DockerBaseDataSeeder.php</code>:</p>
|
|
||||||
<pre><code><?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Database\Seeders;
|
|
||||||
|
|
||||||
use Illuminate\Database\Seeder;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class DockerBaseDataSeeder extends Seeder
|
|
||||||
{
|
|
||||||
public function run(): void
|
|
||||||
{
|
|
||||||
// Tipologie Documenti
|
|
||||||
$tipologieDoc = ['documento', 'avatar', 'galleria', 'statuto', 'programma', 'locandina', 'altro'];
|
|
||||||
foreach ($tipologieDoc as $ord => $nome) {
|
|
||||||
DB::table('tipologie_documenti')->updateOrInsert(
|
|
||||||
['nome' => $nome],
|
|
||||||
['descrizione' => ucfirst($nome), 'ordine' => $ord + 1, 'attiva' => true]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tipologie Eventi
|
|
||||||
$tipologieEventi = [
|
|
||||||
['nome' => 'catechesi', 'descrizione' => 'Catechesi'],
|
|
||||||
['nome' => 'liturgia', 'descrizione' => 'Liturgia'],
|
|
||||||
['nome' => 'animazione', 'descrizione' => 'Animazione'],
|
|
||||||
['nome' => 'formazione', 'descrizione' => 'Formazione'],
|
|
||||||
['nome' => 'incontro', 'descrizione' => 'Incontro'],
|
|
||||||
['nome' => 'riunione', 'descrizione' => 'Riunione'],
|
|
||||||
['nome' => 'festa', 'descrizione' => 'Festa'],
|
|
||||||
['nome' => 'altro', 'descrizione' => 'Altro'],
|
|
||||||
];
|
|
||||||
foreach ($tipologieEventi as $ord => $t) {
|
|
||||||
DB::table('tipologie_eventi')->updateOrInsert(
|
|
||||||
['nome' => $t['nome']],
|
|
||||||
['descrizione' => $t['descrizione'], 'ordine' => $ord + 1, 'attiva' => true]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ruoli
|
|
||||||
$ruoli = ['presidente', 'vicepresidente', 'segretario', 'tesoriere', 'consigliere', 'volontario', 'socio', 'altro'];
|
|
||||||
foreach ($ruoli as $ord => $nome) {
|
|
||||||
DB::table('ruoli')->updateOrInsert(
|
|
||||||
['nome' => $nome],
|
|
||||||
['descrizione' => ucfirst($nome), 'ordine' => $ord + 1, 'attiva' => true]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}</code></pre>
|
|
||||||
<p>Registralo in <code>database/seeders/DatabaseSeeder.php</code>:</p>
|
|
||||||
<pre><code>$this->call([
|
|
||||||
// ... altri seeder ...
|
|
||||||
DockerBaseDataSeeder::class,
|
|
||||||
]);</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">4. Docker Compose</h5>
|
|
||||||
|
|
||||||
<h6 class="text-info"><i class="fas fa-database mr-1"></i> Opzione A — SQLite (leggero)</h6>
|
|
||||||
<pre><code>services:
|
|
||||||
glastree:
|
|
||||||
build: .
|
|
||||||
ports:
|
|
||||||
- "8080:80"
|
|
||||||
volumes:
|
|
||||||
- glastree_storage:/var/www/html/storage
|
|
||||||
- glastree_db:/var/www/html/database
|
|
||||||
environment:
|
|
||||||
- APP_ENV=production
|
|
||||||
- APP_DEBUG=false
|
|
||||||
- APP_URL=http://localhost:8080
|
|
||||||
- DB_CONNECTION=sqlite
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
glastree_storage:
|
|
||||||
glastree_db:</code></pre>
|
|
||||||
|
|
||||||
<h6 class="mt-3 text-success"><i class="fas fa-server mr-1"></i> Opzione B — MySQL (standard)</h6>
|
|
||||||
<pre><code>services:
|
|
||||||
glastree:
|
|
||||||
build: .
|
|
||||||
ports:
|
|
||||||
- "8080:80"
|
|
||||||
volumes:
|
|
||||||
- glastree_storage:/var/www/html/storage
|
|
||||||
depends_on:
|
|
||||||
mysql:
|
|
||||||
condition: service_healthy
|
|
||||||
environment:
|
|
||||||
- APP_ENV=production
|
|
||||||
- APP_DEBUG=false
|
|
||||||
- APP_URL=http://localhost:8080
|
|
||||||
- DB_CONNECTION=mysql
|
|
||||||
- DB_HOST=mysql
|
|
||||||
- DB_PORT=3306
|
|
||||||
- DB_DATABASE=glastree
|
|
||||||
- DB_USERNAME=glastree
|
|
||||||
- DB_PASSWORD=secret
|
|
||||||
|
|
||||||
mysql:
|
|
||||||
image: mysql:8.0
|
|
||||||
volumes:
|
|
||||||
- mysql_data:/var/lib/mysql
|
|
||||||
environment:
|
|
||||||
- MYSQL_DATABASE=glastree
|
|
||||||
- MYSQL_USER=glastree
|
|
||||||
- MYSQL_PASSWORD=secret
|
|
||||||
- MYSQL_ROOT_PASSWORD=root_secret
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
|
||||||
interval: 5s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 10
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
glastree_storage:
|
|
||||||
mysql_data:</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">5. Build e avvio</h5>
|
|
||||||
<p>Assicurati di essere nella cartella del progetto (ottenuta via <code>git clone</code> o estraendo il ZIP), poi:</p>
|
|
||||||
<pre><code># Costruisci l'immagine (comune)
|
|
||||||
docker build -t glastree:latest .
|
|
||||||
|
|
||||||
# Avvia con SQLite
|
|
||||||
docker compose -f docker-compose.yml up -d
|
|
||||||
|
|
||||||
# Oppure avvia con MySQL
|
|
||||||
# docker compose -f docker-compose.mysql.yml up -d
|
|
||||||
|
|
||||||
# Apri nel browser
|
|
||||||
open http://localhost:8080</code></pre>
|
|
||||||
|
|
||||||
<h5 class="mt-4">6. Primo accesso</h5>
|
|
||||||
<p>Al primo avvio, l'entrypoint esegue automaticamente migration e seed. Troverai gia popolati:</p>
|
|
||||||
<ul>
|
|
||||||
<li><strong>Tipologie Documenti:</strong> documento, avatar, galleria, statuto, programma, locandina, altro</li>
|
|
||||||
<li><strong>Tipologie Eventi:</strong> catechesi, liturgia, animazione, formazione, incontro, riunione, festa, altro</li>
|
|
||||||
<li><strong>Ruoli:</strong> presidente, vicepresidente, segretario, tesoriere, consigliere, volontario, socio, altro</li>
|
|
||||||
</ul>
|
|
||||||
<p>Dovrai creare il primo utente amministratore via CLI:</p>
|
|
||||||
<pre><code>docker exec -it glastree php artisan tinker --execute="
|
|
||||||
\$u = new \App\Models\User;
|
|
||||||
\$u->name = 'Amministratore';
|
|
||||||
\$u->email = 'admin@esempio.it';
|
|
||||||
\$u->password = bcrypt('password-forte');
|
|
||||||
\$u->is_admin = true;
|
|
||||||
\$u->permissions = ['individui' => 2, 'gruppi' => 2, 'eventi' => 2, 'documenti' => 2, 'mailing' => 2, 'viste' => 2, 'report' => 2, 'settings' => 2];
|
|
||||||
\$u->save();
|
|
||||||
echo 'Utente creato con successo!';
|
|
||||||
"</code></pre>
|
|
||||||
|
|
||||||
<div class="callout callout-warning mt-4">
|
|
||||||
<h6><i class="fas fa-exclamation-triangle"></i> Note importanti</h6>
|
|
||||||
<ul class="mb-0">
|
|
||||||
<li><strong>Persistenza:</strong> i volumi Docker preservano storage e database tra i riavvii</li>
|
|
||||||
<li><strong>APP_KEY:</strong> viene generata al primo avvio — se elimini i volumi, la perdi e i dati criptati diventano inaccessibili</li>
|
|
||||||
<li><strong>MySQL in produzione:</strong> imposta password sicure per <code>MYSQL_PASSWORD</code> e <code>MYSQL_ROOT_PASSWORD</code></li>
|
|
||||||
<li><strong>File compose separati:</strong> puoi mantenere entrambi i file (<code>docker-compose.yml</code> per SQLite, <code>docker-compose.mysql.yml</code> per MySQL) e scegliere con <code>-f</code></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="tab-pane" id="help-installazione">
|
|
||||||
<div class="card card-outline card-dark">
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title"><i class="fas fa-terminal mr-2"></i> Guida all'Installazione</h3>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<h5>Panoramica</h5>
|
|
||||||
<p>l'applicazione include un <strong>wizard di installazione web</strong> che guida passo-passo nella configurazione su un nuovo server LAMP. Supporta due modalita:</p>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="callout callout-info">
|
|
||||||
<h6><i class="fas fa-rocket"></i> Fresh Install</h6>
|
|
||||||
<p class="mb-0">Installa l'applicazione da zero su un nuovo server: crea il database, esegue le migration, popola i dati di base e crea l'amministratore.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="callout callout-success">
|
|
||||||
<h6><i class="fas fa-upload"></i> Restore da Backup</h6>
|
|
||||||
<p class="mb-0">Ripristina un'installazione esistente da un file ZIP di backup: importa il database, ripristina i documenti e la configurazione.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Requisiti del Server</h5>
|
|
||||||
<table class="table table-sm table-bordered">
|
|
||||||
<thead><tr><th>Componente</th><th>Versione Minima</th><th>Note</th></tr></thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>Web Server</td><td>Apache 2.4+</td><td>Con mod_rewrite abilitato</td></tr>
|
|
||||||
<tr><td>PHP</td><td>8.2+ (consigliato 8.4)</td><td>Estensioni: pdo_mysql, mbstring, xml, curl, zip, gd, fileinfo</td></tr>
|
|
||||||
<tr><td>MySQL / MariaDB</td><td>MySQL 8.0+ / MariaDB 10.5+</td><td>Supporto utf8mb4</td></tr>
|
|
||||||
<tr><td>Composer</td><td>2.x</td><td>Per gestione dipendenze PHP</td></tr>
|
|
||||||
<tr><td>Node.js / npm</td><td>Node 20+ / npm 10+</td><td>Per compilazione asset frontend (opzionale se precompilati)</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Installazione Passo-Passo</h5>
|
|
||||||
|
|
||||||
<h6>Passo 1: Preparare il server LAMP</h6>
|
|
||||||
<pre><code>sudo apt update && sudo apt upgrade -y
|
|
||||||
|
|
||||||
sudo apt install -y apache2 mariadb-server php8.4 php8.4-cli \
|
|
||||||
php8.4-mysql php8.4-xml php8.4-mbstring php8.4-curl \
|
|
||||||
php8.4-zip php8.4-bcmath php8.4-gd php8.4-intl \
|
|
||||||
composer git unzip
|
|
||||||
|
|
||||||
sudo a2enmod rewrite
|
|
||||||
sudo systemctl restart apache2</code></pre>
|
|
||||||
|
|
||||||
<h6>Passo 2: Ottenere l'applicazione</h6>
|
|
||||||
<div class="callout callout-info py-2">
|
|
||||||
<i class="fas fa-info-circle"></i>
|
|
||||||
Puoi clonare il repository con git oppure estrarre il file ZIP ricevuto nella cartella <code>/var/www/glastree</code>.
|
|
||||||
</div>
|
|
||||||
<pre><code>cd /var/www
|
|
||||||
|
|
||||||
# Opzione A — Git clone
|
|
||||||
git clone <URL_REPOSITORY> glastree
|
|
||||||
|
|
||||||
# Opzione B — ZIP (se non hai git)
|
|
||||||
# unzip /percorso/del/glastree.zip -d glastree
|
|
||||||
|
|
||||||
cd glastree
|
|
||||||
composer install --no-dev --optimize-autoloader
|
|
||||||
npm ci && npm run build</code></pre>
|
|
||||||
<div class="alert alert-info">
|
|
||||||
<i class="fas fa-info-circle"></i>
|
|
||||||
Se non hai accesso a npm, puoi saltare il build. L'interfaccia usera comunque AdminLTE via CDN.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h6>Passo 3: Permessi cartelle</h6>
|
|
||||||
<pre><code>sudo chown -R www-data:www-data storage bootstrap/cache
|
|
||||||
sudo chmod -R 775 storage bootstrap/cache</code></pre>
|
|
||||||
|
|
||||||
<h6>Passo 4: Configurare Apache</h6>
|
|
||||||
<pre><code>sudo tee /etc/apache2/sites-available/glastree.conf <<APACHE
|
|
||||||
<VirtualHost *:80>
|
|
||||||
ServerName glastree.esempio.it
|
|
||||||
DocumentRoot /var/www/glastree/public
|
|
||||||
|
|
||||||
<Directory /var/www/glastree/public>
|
|
||||||
Options -Indexes +FollowSymLinks
|
|
||||||
AllowOverride All
|
|
||||||
Require all granted
|
|
||||||
</Directory>
|
|
||||||
|
|
||||||
ErrorLog \${APACHE_LOG_DIR}/glastree_error.log
|
|
||||||
CustomLog \${APACHE_LOG_DIR}/glastree_access.log combined
|
|
||||||
</VirtualHost>
|
|
||||||
APACHE
|
|
||||||
|
|
||||||
sudo a2dissite 000-default.conf
|
|
||||||
sudo a2ensite glastree.conf
|
|
||||||
sudo systemctl reload apache2</code></pre>
|
|
||||||
|
|
||||||
<h6>Passo 5: Avviare il wizard di installazione</h6>
|
|
||||||
<p>Apri il browser e visita l'indirizzo del tuo server:</p>
|
|
||||||
<pre><code>http://glastree.esempio.it/installer/</code></pre>
|
|
||||||
|
|
||||||
<p>Il wizard ti guidera attraverso 6 passi:</p>
|
|
||||||
|
|
||||||
<table class="table table-sm table-bordered">
|
|
||||||
<thead><tr><th>Passo</th><th>Descrizione</th></tr></thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>1. Benvenuto</td><td>Scegli Fresh Install o Restore da Backup</td></tr>
|
|
||||||
<tr><td>2. Requisiti</td><td>Verifica PHP, estensioni e permessi</td></tr>
|
|
||||||
<tr><td>3. Database</td><td>Configura MySQL (crea nuovo o usa esistente)</td></tr>
|
|
||||||
<tr><td>4. Installazione</td><td>Fresh: migration e seed. Backup: upload ZIP</td></tr>
|
|
||||||
<tr><td>5. Amministratore</td><td>Fresh: crea super-admin. Backup: verifica utenti</td></tr>
|
|
||||||
<tr><td>6. Finalizzazione</td><td>Cache, storage link, auto-eliminazione installer</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div class="alert alert-warning">
|
|
||||||
<i class="fas fa-exclamation-triangle"></i>
|
|
||||||
<strong>Attenzione:</strong> Al termine dell'installazione, la cartella <code>installer/</code> viene <strong>automaticamente eliminata</strong>. Non puoi rieseguire l'installazione se non ricreando manualmente la cartella.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Modalita Fresh Install</h5>
|
|
||||||
<p>Seleziona questa modalita quando installi l'applicazione per la prima volta su un server.</p>
|
|
||||||
<p>Cosa succede durante l'installazione:</p>
|
|
||||||
<ol>
|
|
||||||
<li>Genera <code>APP_KEY</code> per la crittografia</li>
|
|
||||||
<li>Esegue tutte le migration per creare le tabelle del database</li>
|
|
||||||
<li>Popola i dati di base (diocesi, comuni, ruoli, preset)</li>
|
|
||||||
<li>Crea il collegamento <code>storage → public/storage</code></li>
|
|
||||||
<li>Crea l'utente amministratore con i permessi completi</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Modalita Restore da Backup</h5>
|
|
||||||
<p>Seleziona questa modalita quando vuoi migrare un'installazione esistente su un nuovo server.</p>
|
|
||||||
<p>Cosa serve:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Un file ZIP generato dalla pagina <strong>Admin → Backup</strong> del server originale</li>
|
|
||||||
<li>Il wizard accetta upload diretto del file o un percorso sul server</li>
|
|
||||||
</ul>
|
|
||||||
<p>Cosa succede durante il restore:</p>
|
|
||||||
<ol>
|
|
||||||
<li>Estrae il file ZIP in una cartella temporanea</li>
|
|
||||||
<li>Importa <code>database.sql</code> nel database MySQL configurato</li>
|
|
||||||
<li>Copia i file documenti in <code>storage/app/documenti/</code></li>
|
|
||||||
<li>Ripristina il file <code>.env</code> dal backup (preservando le credenziali DB del nuovo server)</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Dopo l'Installazione</h5>
|
|
||||||
<ol>
|
|
||||||
<li><strong>Configura SSL</strong> con Let's Encrypt:
|
|
||||||
<pre><code>sudo apt install -y certbot python3-certbot-apache
|
|
||||||
sudo certbot --apache -d glastree.esempio.it</code></pre>
|
|
||||||
</li>
|
|
||||||
<li><strong>Accedi</strong> con le credenziali amministratore create durante l'installazione</li>
|
|
||||||
<li><strong>Configura l'applicazione</strong> in Impostazioni:
|
|
||||||
<ul>
|
|
||||||
<li>Nome applicazione e logo</li>
|
|
||||||
<li>Email (SMTP per invio, IMAP per ricezione)</li>
|
|
||||||
<li>Repository remoti (Google Drive, Nextcloud)</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><strong>Crea utenti</strong> dal pannello Admin → Utenti</li>
|
|
||||||
<li><strong>Configura il cron</strong> per backup automatico e sync email:
|
|
||||||
<pre><code>sudo crontab -u www-data -e
|
|
||||||
# Inserisci:
|
|
||||||
* * * * * cd /var/www/glastree && php artisan schedule:run >> /dev/null 2>&1</code></pre>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h5 class="mt-4">Risoluzione Problemi</h5>
|
|
||||||
<div class="callout callout-danger">
|
|
||||||
<h6>Errore "500 | Server Error" dopo l'installazione</h6>
|
|
||||||
<p>Probabilmente mancano permessi sulle cartelle <code>storage</code> o <code>bootstrap/cache</code>:</p>
|
|
||||||
<pre><code>sudo chown -R www-data:www-data storage bootstrap/cache
|
|
||||||
sudo chmod -R 775 storage bootstrap/cache</code></pre>
|
|
||||||
</div>
|
|
||||||
<div class="callout callout-danger">
|
|
||||||
<h6>Errore "No application encryption key"</h6>
|
|
||||||
<p>APP_KEY non generata. Nella cartella dell'app:</p>
|
|
||||||
<pre><code>php artisan key:generate</code></pre>
|
|
||||||
</div>
|
|
||||||
<div class="callout callout-warning">
|
|
||||||
<h6>mod_rewrite non funziona</h6>
|
|
||||||
<p>Se le rotte restituiscono 404, verifica che Apache abbia AllowOverride:</p>
|
|
||||||
<pre><code>sudo a2enmod rewrite
|
|
||||||
# Nel VirtualHost: AllowOverride All
|
|
||||||
sudo systemctl reload apache2</code></pre>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.nav-pills .nav-link { border-radius: 0; }
|
|
||||||
.nav-pills .nav-link.active { background-color: #007bff; }
|
|
||||||
.callout { border-left: 3px solid; padding: 12px 16px; margin: 16px 0; background: #f8f9fa; }
|
|
||||||
.callout-info { border-color: #17a2b8; }
|
|
||||||
.callout-danger { border-color: #dc3545; }
|
|
||||||
.callout-warning { border-color: #ffc107; }
|
|
||||||
</style>
|
|
||||||
<?php $__env->stopSection(); ?>
|
|
||||||
|
|
||||||
<?php echo $__env->make('layouts.adminlte', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?><?php /**PATH /var/www/html/glastree/resources/views/help/index.blade.php ENDPATH**/ ?>
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
<?php $__env->startSection('title', 'Dashboard'); ?>
|
|
||||||
<?php $__env->startSection('page_title', 'Dashboard'); ?>
|
|
||||||
|
|
||||||
<?php $__env->startSection('content'); ?>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-3 col-6">
|
|
||||||
<div class="small-box bg-info">
|
|
||||||
<div class="inner">
|
|
||||||
<h3><?php echo e($stats['individui']); ?></h3>
|
|
||||||
<p>Individui</p>
|
|
||||||
</div>
|
|
||||||
<div class="icon">
|
|
||||||
<i class="fas fa-users"></i>
|
|
||||||
</div>
|
|
||||||
<a href="<?php echo e(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><?php echo e($stats['gruppi']); ?></h3>
|
|
||||||
<p>Gruppi</p>
|
|
||||||
</div>
|
|
||||||
<div class="icon">
|
|
||||||
<i class="fas fa-folder"></i>
|
|
||||||
</div>
|
|
||||||
<a href="<?php echo e(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-primary">
|
|
||||||
<div class="inner">
|
|
||||||
<h3><?php echo e($stats['documenti']); ?></h3>
|
|
||||||
<p>Documenti</p>
|
|
||||||
</div>
|
|
||||||
<div class="icon">
|
|
||||||
<i class="fas fa-file"></i>
|
|
||||||
</div>
|
|
||||||
<a href="/documenti" 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-danger">
|
|
||||||
<div class="inner">
|
|
||||||
<h3><?php echo e($stats['eventi']); ?></h3>
|
|
||||||
<p>Eventi</p>
|
|
||||||
</div>
|
|
||||||
<div class="icon">
|
|
||||||
<i class="fas fa-calendar"></i>
|
|
||||||
</div>
|
|
||||||
<a href="/eventi" class="small-box-footer">Visualizza <i class="fas fa-arrow-circle-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-lg-3 col-6">
|
|
||||||
<div class="small-box bg-warning">
|
|
||||||
<div class="inner">
|
|
||||||
<h3><?php echo e($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 class="col-lg-3 col-6">
|
|
||||||
<div class="small-box bg-secondary">
|
|
||||||
<div class="inner">
|
|
||||||
<h3><?php echo e($stats['email_nuove']); ?></h3>
|
|
||||||
<p>Nuove Email</p>
|
|
||||||
</div>
|
|
||||||
<div class="icon">
|
|
||||||
<i class="fas fa-envelope"></i>
|
|
||||||
</div>
|
|
||||||
<a href="/email" 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-dark">
|
|
||||||
<div class="inner">
|
|
||||||
<h3><?php echo e($stats['mailing_liste']); ?></h3>
|
|
||||||
<p>Mailing List</p>
|
|
||||||
</div>
|
|
||||||
<div class="icon">
|
|
||||||
<i class="fas fa-list-alt"></i>
|
|
||||||
</div>
|
|
||||||
<a href="/mailing-liste" class="small-box-footer">Visualizza <i class="fas fa-arrow-circle-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php if(Auth::user()->canManage('settings')): ?>
|
|
||||||
<div class="col-lg-3 col-6">
|
|
||||||
<div class="small-box bg-navy">
|
|
||||||
<div class="inner">
|
|
||||||
<h3><?php echo e($stats['backups']); ?></h3>
|
|
||||||
<p>Backup</p>
|
|
||||||
</div>
|
|
||||||
<div class="icon">
|
|
||||||
<i class="fas fa-hdd"></i>
|
|
||||||
</div>
|
|
||||||
<a href="<?php echo e(route('admin.backup.index')); ?>" class="small-box-footer">Visualizza <i class="fas fa-arrow-circle-right"></i></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</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">
|
|
||||||
<?php if($notifiche->count() > 0): ?>
|
|
||||||
<ul class="todo-list" data-widget="todo-list">
|
|
||||||
<?php $__currentLoopData = $notifiche; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $notifica): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<li>
|
|
||||||
<span class="text"><?php echo e($notifica->titolo); ?></span>
|
|
||||||
<small class="badge badge-info"><i class="far fa-clock"></i> <?php echo e($notifica->created_at->diffForHumans()); ?></small>
|
|
||||||
</li>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</ul>
|
|
||||||
<?php else: ?>
|
|
||||||
<p class="text-muted">Nessuna notifica</p>
|
|
||||||
<?php 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();
|
|
||||||
?>
|
|
||||||
<?php if($welcome): ?>
|
|
||||||
<p><?php echo nl2br(e($welcome)); ?></p>
|
|
||||||
<?php else: ?>
|
|
||||||
<p>Benvenuto in <strong><?php echo e(e($appName)); ?></strong><?php echo e($orgName ? ', ' . e($orgName) : ''); ?>.</p>
|
|
||||||
<?php 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>
|
|
||||||
<?php 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>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php $__env->stopSection(); ?>
|
|
||||||
<?php echo $__env->make('layouts.adminlte', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?><?php /**PATH /var/www/html/glastree/resources/views/home/dashboard.blade.php ENDPATH**/ ?>
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="it">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Anteprima non disponibile</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
margin: 0;
|
|
||||||
background-color: #f4f6f9;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
.fallback-container {
|
|
||||||
text-align: center;
|
|
||||||
padding: 40px;
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
.fallback-icon {
|
|
||||||
font-size: 64px;
|
|
||||||
color: #6c757d;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
.fallback-title {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
.fallback-message {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
}
|
|
||||||
.fallback-file-info {
|
|
||||||
background: #f8f9fa;
|
|
||||||
padding: 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 24px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
.fallback-file-info strong {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
.download-btn {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 10px 24px;
|
|
||||||
background-color: #007bff;
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: background-color 0.2s;
|
|
||||||
}
|
|
||||||
.download-btn:hover {
|
|
||||||
background-color: #0056b3;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="fallback-container">
|
|
||||||
<div class="fallback-icon">📄</div>
|
|
||||||
<div class="fallback-title">Anteprima non disponibile</div>
|
|
||||||
<div class="fallback-message">
|
|
||||||
Questo tipo di file non può essere visualizzato nell'anteprima.
|
|
||||||
Scarica il file per visualizzarlo con l'applicazione appropriata.
|
|
||||||
</div>
|
|
||||||
<div class="fallback-file-info">
|
|
||||||
<strong><?php echo e($documento->nome_file); ?></strong>
|
|
||||||
<?php echo e(strtoupper(pathinfo($documento->file_path, PATHINFO_EXTENSION))); ?> ·
|
|
||||||
<?php echo e(number_format($documento->dimensione / 1024, 1)); ?> KB
|
|
||||||
</div>
|
|
||||||
<a href="/documenti/<?php echo e($documento->id); ?>/download" class="download-btn">
|
|
||||||
⬇️ Scarica File
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
<?php /**PATH /var/www/html/glastree/resources/views/documenti/preview-fallback.blade.php ENDPATH**/ ?>
|
|
||||||
@@ -1,922 +0,0 @@
|
|||||||
<?php $__env->startSection('title', 'Individui'); ?>
|
|
||||||
<?php $__env->startSection('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');
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php $__env->startSection('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>
|
|
||||||
<?php 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>
|
|
||||||
<?php 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>
|
|
||||||
<?php if($canWriteIndividui): ?>
|
|
||||||
<a href="<?php echo e(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>
|
|
||||||
<?php 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>
|
|
||||||
<?php $__currentLoopData = $allColumns; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $col): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<option value="<?php echo e($col['key']); ?>"><?php echo e($col['label']); ?></option>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</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">
|
|
||||||
<?php 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>
|
|
||||||
<?php 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>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php 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>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php 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>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php 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>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php 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>
|
|
||||||
<?php endif; ?>
|
|
||||||
<th style="width: 120px;">Azioni</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="table-body">
|
|
||||||
<?php $__currentLoopData = $individui; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $ind): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<tr data-id="<?php echo e($ind->id); ?>">
|
|
||||||
<td>
|
|
||||||
<div class="custom-control custom-checkbox">
|
|
||||||
<input type="checkbox" class="custom-control-input row-checkbox" id="select-<?php echo e($ind->id); ?>" value="<?php echo e($ind->id); ?>">
|
|
||||||
<label class="custom-control-label" for="select-<?php echo e($ind->id); ?>"></label>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<?php if(in_array('codice', $visibleColumns)): ?>
|
|
||||||
<td><span class="badge badge-secondary"><?php echo e($ind->codice_id); ?></span></td>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(in_array('cognome', $visibleColumns)): ?>
|
|
||||||
<td class="font-weight-bold">
|
|
||||||
<a href="<?php echo e(url('/individui/' . $ind->id . '/edit')); ?>"><?php echo e($ind->cognome); ?></a>
|
|
||||||
</td>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(in_array('nome', $visibleColumns)): ?>
|
|
||||||
<td>
|
|
||||||
<a href="<?php echo e(url('/individui/' . $ind->id . '/edit')); ?>"><?php echo e($ind->nome); ?></a>
|
|
||||||
</td>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(in_array('email', $visibleColumns)): ?>
|
|
||||||
<td><?php echo e($ind->getEmailPrimariaAttribute() ?: '-'); ?></td>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(in_array('telefono', $visibleColumns)): ?>
|
|
||||||
<td><?php echo e($ind->getTelefonoPrimarioAttribute() ?: '-'); ?></td>
|
|
||||||
<?php endif; ?>
|
|
||||||
<td>
|
|
||||||
<?php if($canWriteIndividui): ?>
|
|
||||||
<a href="<?php echo e(url('/individui/' . $ind->id . '/edit')); ?>" class="btn btn-xs btn-warning" title="Modifica">
|
|
||||||
<i class="fas fa-edit"></i>
|
|
||||||
</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if($canDeleteIndividui): ?>
|
|
||||||
<button type="button" class="btn btn-xs btn-danger" onclick="showDeleteModal(<?php echo e($ind->id); ?>, '<?php echo e($ind->cognome); ?> <?php echo e($ind->nome); ?>')" title="Elimina">
|
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</button>
|
|
||||||
<?php endif; ?>
|
|
||||||
<button type="button" class="btn btn-xs btn-secondary" onclick="printIndividuo(<?php echo e($ind->id); ?>)" title="Stampa">
|
|
||||||
<i class="fas fa-print"></i>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-xs btn-primary" onclick="exportIndividuoCSV(<?php echo e($ind->id); ?>)" title="Esporta CSV">
|
|
||||||
<i class="fas fa-download"></i>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-xs btn-info" onclick="sendEmail(<?php echo e($ind->id); ?>)" title="Invia Email">
|
|
||||||
<i class="fas fa-envelope"></i>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-xs btn-dark" onclick="generateReport(<?php echo e($ind->id); ?>)" title="Genera Report">
|
|
||||||
<i class="fas fa-chart-bar"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="text-center text-muted py-5">
|
|
||||||
<i class="fas fa-users fa-3x mb-3"></i>
|
|
||||||
<p>Nessun individuo presente</p>
|
|
||||||
<?php if($canWriteIndividui): ?>
|
|
||||||
<a href="/individui/create" class="btn btn-success">Crea il primo individuo</a>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<?php if($individui->count() > 0): ?>
|
|
||||||
<div class="card-footer">
|
|
||||||
<?php echo e($individui->links()); ?>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<?php 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">
|
|
||||||
<?php $__currentLoopData = $allColumns; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $col): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="custom-control custom-checkbox">
|
|
||||||
<input type="checkbox" class="custom-control-input col-visibile" id="col-vis-<?php echo e($col['key']); ?>" value="<?php echo e($col['key']); ?>" <?php echo e(in_array($col['key'], $visibleColumns) ? 'checked' : ''); ?>>
|
|
||||||
<label class="custom-control-label" for="col-vis-<?php echo e($col['key']); ?>"><?php echo e($col['label']); ?></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</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">
|
|
||||||
<?php $__currentLoopData = $allColumns; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $col): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div class="custom-control custom-checkbox">
|
|
||||||
<input type="checkbox" class="custom-control-input col-toggle" id="col-toggle-<?php echo e($col['key']); ?>" value="<?php echo e($col['key']); ?>" <?php echo e(in_array($col['key'], $visibleColumns) ? 'checked' : ''); ?> onchange="toggleColumn('<?php echo e($col['key']); ?>', this.checked)">
|
|
||||||
<label class="custom-control-label" for="col-toggle-<?php echo e($col['key']); ?>"><?php echo e($col['label']); ?></label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>
|
|
||||||
</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">
|
|
||||||
<?php 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>
|
|
||||||
<?php 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>
|
|
||||||
|
|
||||||
<?php $__env->stopSection(); ?>
|
|
||||||
|
|
||||||
<?php $__env->startSection('scripts'); ?>
|
|
||||||
<script>
|
|
||||||
let currentSort = { column: null, direction: 'asc' };
|
|
||||||
let columnVisibility = <?php echo 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 || '<?php echo e(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 || '<?php echo e(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 = '<?php echo e(csrf_token()); ?>';
|
|
||||||
document.head.appendChild(meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
const visibleCols = <?php echo $visibleColumnsJson; ?>;
|
|
||||||
const allCols = <?php echo $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 = <?php echo $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>
|
|
||||||
<?php $__env->stopSection(); ?>
|
|
||||||
<?php echo $__env->make('layouts.adminlte', array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1]))->render(); ?><?php /**PATH /var/www/html/glastree/resources/views/individui/index.blade.php ENDPATH**/ ?>
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
<?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 - <?php echo e(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="/">
|
|
||||||
<?php if($appLogo): ?>
|
|
||||||
<img src="<?php echo e($appLogo); ?>" alt="Logo" style="max-height: 80px; max-width: 200px;">
|
|
||||||
<?php else: ?>
|
|
||||||
<i class="fas fa-tree text-success"></i> <b><?php echo e(e($appName)); ?></b>
|
|
||||||
<?php endif; ?>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<?php if($appOrgName): ?>
|
|
||||||
<div class="text-center mb-3">
|
|
||||||
<small class="text-muted"><?php echo e(e($appOrgName)); ?></small>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-body login-card-body">
|
|
||||||
<p class="login-box-msg">Accedi per continuare</p>
|
|
||||||
<form action="<?php echo e(route('login')); ?>" method="POST">
|
|
||||||
<?php echo csrf_field(); ?>
|
|
||||||
<div class="input-group mb-3">
|
|
||||||
<input type="email" name="email" class="form-control <?php $__errorArgs = ['email'];
|
|
||||||
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
|
|
||||||
if ($__bag->has($__errorArgs[0])) :
|
|
||||||
if (isset($message)) { $__messageOriginal = $message; }
|
|
||||||
$message = $__bag->first($__errorArgs[0]); ?> is-invalid <?php unset($message);
|
|
||||||
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
|
|
||||||
endif;
|
|
||||||
unset($__errorArgs, $__bag); ?>" placeholder="Email" required>
|
|
||||||
<div class="input-group-append">
|
|
||||||
<div class="input-group-text">
|
|
||||||
<span class="fas fa-envelope"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php $__errorArgs = ['email'];
|
|
||||||
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
|
|
||||||
if ($__bag->has($__errorArgs[0])) :
|
|
||||||
if (isset($message)) { $__messageOriginal = $message; }
|
|
||||||
$message = $__bag->first($__errorArgs[0]); ?>
|
|
||||||
<span class="invalid-feedback"><?php echo e($message); ?></span>
|
|
||||||
<?php unset($message);
|
|
||||||
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
|
|
||||||
endif;
|
|
||||||
unset($__errorArgs, $__bag); ?>
|
|
||||||
</div>
|
|
||||||
<div class="input-group mb-3">
|
|
||||||
<input type="password" name="password" class="form-control <?php $__errorArgs = ['password'];
|
|
||||||
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
|
|
||||||
if ($__bag->has($__errorArgs[0])) :
|
|
||||||
if (isset($message)) { $__messageOriginal = $message; }
|
|
||||||
$message = $__bag->first($__errorArgs[0]); ?> is-invalid <?php unset($message);
|
|
||||||
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
|
|
||||||
endif;
|
|
||||||
unset($__errorArgs, $__bag); ?>" placeholder="Password" required>
|
|
||||||
<div class="input-group-append">
|
|
||||||
<div class="input-group-text">
|
|
||||||
<span class="fas fa-lock"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php $__errorArgs = ['password'];
|
|
||||||
$__bag = $errors->getBag($__errorArgs[1] ?? 'default');
|
|
||||||
if ($__bag->has($__errorArgs[0])) :
|
|
||||||
if (isset($message)) { $__messageOriginal = $message; }
|
|
||||||
$message = $__bag->first($__errorArgs[0]); ?>
|
|
||||||
<span class="invalid-feedback"><?php echo e($message); ?></span>
|
|
||||||
<?php unset($message);
|
|
||||||
if (isset($__messageOriginal)) { $message = $__messageOriginal; }
|
|
||||||
endif;
|
|
||||||
unset($__errorArgs, $__bag); ?>
|
|
||||||
</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="<?php echo e(route('password.request')); ?>" class="text-center">Password dimenticata?</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><?php /**PATH /var/www/html/glastree/resources/views/auth/login.blade.php ENDPATH**/ ?>
|
|
||||||
@@ -1,301 +0,0 @@
|
|||||||
<?php
|
|
||||||
$appLogoSmall = \App\Models\AppSetting::getLogoSmallUrl();
|
|
||||||
$appName = \App\Models\AppSetting::getAppName() ?? 'Glastree';
|
|
||||||
?>
|
|
||||||
<!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="<?php echo e(csrf_token()); ?>">
|
|
||||||
<title><?php echo $__env->yieldContent('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">
|
|
||||||
<?php echo $__env->yieldContent('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">
|
|
||||||
<a class="nav-link" href="<?php echo e(route('help')); ?>" title="Guida">
|
|
||||||
<i class="fas fa-question-circle"></i>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<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 ?>
|
|
||||||
<?php if($unread > 0): ?>
|
|
||||||
<span class="badge badge-warning navbar-badge"><?php echo e($unread); ?></span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</a>
|
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
|
||||||
<span class="dropdown-header">Notifiche (<?php echo e($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> <?php echo e(Auth::user()->name); ?>
|
|
||||||
|
|
||||||
</a>
|
|
||||||
<div class="dropdown-menu dropdown-menu-right">
|
|
||||||
<a href="<?php echo e(route('profile')); ?>" class="dropdown-item">
|
|
||||||
<i class="fas fa-user-cog mr-2"></i> Profilo
|
|
||||||
</a>
|
|
||||||
<div class="dropdown-divider"></div>
|
|
||||||
<form method="POST" action="<?php echo e(route('logout')); ?>">
|
|
||||||
<?php echo csrf_field(); ?>
|
|
||||||
<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';
|
|
||||||
?>
|
|
||||||
<aside class="main-sidebar sidebar-dark-primary elevation-4">
|
|
||||||
<a href="<?php echo e(route('dashboard')); ?>" class="brand-link">
|
|
||||||
<?php if($appLogoSmall): ?>
|
|
||||||
<img src="<?php echo e($appLogoSmall); ?>" alt="Logo" class="brand-image" style="max-height: 35px; max-width: 40px;">
|
|
||||||
<?php else: ?>
|
|
||||||
<i class="fas fa-tree brand-image ml-3 mr-2"></i>
|
|
||||||
<?php endif; ?>
|
|
||||||
<span class="brand-text font-weight-light"><?php echo e($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="<?php echo e(route('dashboard')); ?>" class="nav-link <?php echo e(request()->routeIs('dashboard') ? 'active' : ''); ?>">
|
|
||||||
<i class="nav-icon fas fa-tachometer-alt"></i>
|
|
||||||
<p>Dashboard</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<?php if(Auth::user()->canAccess('individui')): ?>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="<?php echo e(route('individui.index')); ?>" class="nav-link">
|
|
||||||
<i class="nav-icon fas fa-users"></i>
|
|
||||||
<p>Individui</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(Auth::user()->canAccess('gruppi')): ?>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="<?php echo e(route('gruppi.index')); ?>" class="nav-link">
|
|
||||||
<i class="nav-icon fas fa-folder"></i>
|
|
||||||
<p>Gruppi</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(Auth::user()->canAccess('documenti')): ?>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="<?php echo e(route('documenti.index')); ?>" class="nav-link">
|
|
||||||
<i class="nav-icon fas fa-file"></i>
|
|
||||||
<p>Documenti</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(Auth::user()->canAccess('eventi')): ?>
|
|
||||||
<li class="nav-item has-treeview">
|
|
||||||
<a href="<?php echo e(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="<?php echo e(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="<?php echo e(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="<?php echo e(route('eventi.create')); ?>" class="nav-link">
|
|
||||||
<i class="nav-icon fas fa-plus"></i>
|
|
||||||
<p>Nuovo Evento</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(Auth::user()->canAccess('email') || Auth::user()->canAccess('mailing')): ?>
|
|
||||||
<li class="nav-item has-treeview">
|
|
||||||
<a href="<?php echo e(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">
|
|
||||||
<?php if(Auth::user()->canAccess('email')): ?>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="<?php echo e(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="<?php echo e(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="<?php echo e(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="<?php echo e(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="<?php echo e(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="<?php echo e(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="<?php echo e(route('email.index', 'trash')); ?>" class="nav-link">
|
|
||||||
<i class="nav-icon fas fa-trash"></i>
|
|
||||||
<p>Cestino</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<?php endif; ?>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(Auth::user()->canAccess('mailing')): ?>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="<?php echo e(route('mailing-liste.index')); ?>" class="nav-link">
|
|
||||||
<i class="nav-icon fas fa-list"></i>
|
|
||||||
<p>Mailing List</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(Auth::user()->canAccess('report')): ?>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="<?php echo e(route('report.index')); ?>" class="nav-link <?php echo e(request()->routeIs('report.*') ? 'active' : ''); ?>">
|
|
||||||
<i class="nav-icon fas fa-chart-bar"></i>
|
|
||||||
<p>Report</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if(Auth::user()->canManage('settings')): ?>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="/impostazioni" class="nav-link <?php echo e(request()->is('impostazioni*') ? 'active' : ''); ?>">
|
|
||||||
<i class="nav-icon fas fa-cog"></i>
|
|
||||||
<p>Impostazioni</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item has-treeview <?php echo e(request()->is('admin/*') ? 'menu-open' : ''); ?>">
|
|
||||||
<a href="#" class="nav-link <?php echo e(request()->is('admin/*') ? 'active' : ''); ?>">
|
|
||||||
<i class="nav-icon fas fa-users-cog"></i>
|
|
||||||
<p>Admin <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="/admin/utenti" class="nav-link <?php echo e(request()->routeIs('admin.utenti.*') ? 'active' : ''); ?>">
|
|
||||||
<i class="nav-icon fas fa-user-shield"></i>
|
|
||||||
<p>Utenti</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="<?php echo e(route('admin.ruoli.index')); ?>" class="nav-link <?php echo e(request()->routeIs('admin.ruoli.*') ? 'active' : ''); ?>">
|
|
||||||
<i class="nav-icon fas fa-tag"></i>
|
|
||||||
<p>Ruoli</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="/admin/backup" class="nav-link <?php echo e(request()->routeIs('admin.backup.*') ? 'active' : ''); ?>">
|
|
||||||
<i class="nav-icon fas fa-hdd"></i>
|
|
||||||
<p>Backup</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="<?php echo e(route('admin.activity-logs.index')); ?>" class="nav-link <?php echo e(request()->routeIs('admin.activity-logs.*') ? 'active' : ''); ?>">
|
|
||||||
<i class="nav-icon fas fa-history"></i>
|
|
||||||
<p>Log Attività</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<?php endif; ?>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a href="<?php echo e(route('help')); ?>" class="nav-link <?php echo e(request()->routeIs('help') ? 'active' : ''); ?>">
|
|
||||||
<i class="nav-icon fas fa-question-circle"></i>
|
|
||||||
<p>Guida / Aiuto</p>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</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"><?php echo $__env->yieldContent('page_title', 'Dashboard'); ?></h1>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<ol class="breadcrumb float-sm-right">
|
|
||||||
<?php echo $__env->yieldContent('breadcrumbs'); ?>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<?php echo $slot ?? ''; ?>
|
|
||||||
|
|
||||||
<?php echo $__env->yieldContent('content'); ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer class="main-footer">
|
|
||||||
<strong><?php echo e(\App\Models\AppSetting::getFooterText()); ?></strong>
|
|
||||||
<?php if(\App\Models\AppSetting::shouldShowVersion() && \App\Models\AppSetting::getAppVersion()): ?>
|
|
||||||
<span class="ml-2">v<?php echo e(\App\Models\AppSetting::getAppVersion()); ?></span>
|
|
||||||
<?php 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>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
||||||
<?php echo $__env->yieldContent('scripts'); ?>
|
|
||||||
</body>
|
|
||||||
</html><?php /**PATH /var/www/html/glastree/resources/views/layouts/adminlte.blade.php ENDPATH**/ ?>
|
|
||||||
Reference in New Issue
Block a user