519 lines
26 KiB
PHP
519 lines
26 KiB
PHP
@extends('layouts.admin')
|
|
|
|
@section('title', 'Personalizzazione Tema - NetGesCon')
|
|
|
|
@section('content')
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title">
|
|
<i class="fas fa-palette me-2"></i>
|
|
Personalizzazione Tema NetGesCon
|
|
</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Alert per messaggi -->
|
|
<div id="theme-alert" class="alert" style="display: none;"></div>
|
|
|
|
<!-- Tab Navigation -->
|
|
<ul class="nav nav-tabs" id="themeTab" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="custom-tab" data-bs-toggle="tab" data-bs-target="#custom" type="button" role="tab">
|
|
<i class="fas fa-paint-brush me-2"></i>Personalizzato
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="presets-tab" data-bs-toggle="tab" data-bs-target="#presets" type="button" role="tab">
|
|
<i class="fas fa-swatchbook me-2"></i>Temi Predefiniti
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="import-export-tab" data-bs-toggle="tab" data-bs-target="#import-export" type="button" role="tab">
|
|
<i class="fas fa-exchange-alt me-2"></i>Import/Export
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
<!-- Tab Content -->
|
|
<div class="tab-content mt-3" id="themeTabContent">
|
|
|
|
<!-- Personalizzazione Custom -->
|
|
<div class="tab-pane fade show active" id="custom" role="tabpanel">
|
|
<form id="theme-form">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h5>Colori Principali</h5>
|
|
|
|
<div class="mb-3">
|
|
<label for="primary_color" class="form-label">Colore Primario</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="primary_color"
|
|
name="primary_color" value="{{ $currentTheme['primary_color'] }}" title="Scegli colore primario">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['primary_color'] }}"
|
|
data-color-input="primary_color">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="secondary_color" class="form-label">Colore Secondario</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="secondary_color"
|
|
name="secondary_color" value="{{ $currentTheme['secondary_color'] }}" title="Scegli colore secondario">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['secondary_color'] }}"
|
|
data-color-input="secondary_color">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="success_color" class="form-label">Colore Successo</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="success_color"
|
|
name="success_color" value="{{ $currentTheme['success_color'] }}" title="Scegli colore successo">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['success_color'] }}"
|
|
data-color-input="success_color">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="danger_color" class="form-label">Colore Pericolo</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="danger_color"
|
|
name="danger_color" value="{{ $currentTheme['danger_color'] }}" title="Scegli colore pericolo">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['danger_color'] }}"
|
|
data-color-input="danger_color">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="warning_color" class="form-label">Colore Avviso</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="warning_color"
|
|
name="warning_color" value="{{ $currentTheme['warning_color'] }}" title="Scegli colore avviso">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['warning_color'] }}"
|
|
data-color-input="warning_color">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="info_color" class="form-label">Colore Info</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="info_color"
|
|
name="info_color" value="{{ $currentTheme['info_color'] }}" title="Scegli colore info">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['info_color'] }}"
|
|
data-color-input="info_color">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-md-6">
|
|
<h5>Sidebar e Layout</h5>
|
|
|
|
<div class="mb-3">
|
|
<label for="sidebar_bg" class="form-label">Sfondo Sidebar</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="sidebar_bg"
|
|
name="sidebar_bg" value="{{ $currentTheme['sidebar_bg'] }}" title="Scegli sfondo sidebar">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['sidebar_bg'] }}"
|
|
data-color-input="sidebar_bg">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="sidebar_text" class="form-label">Testo Sidebar</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="sidebar_text"
|
|
name="sidebar_text" value="{{ $currentTheme['sidebar_text'] }}" title="Scegli colore testo sidebar">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['sidebar_text'] }}"
|
|
data-color-input="sidebar_text">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="header_bg" class="form-label">Sfondo Header</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="header_bg"
|
|
name="header_bg" value="{{ $currentTheme['header_bg'] }}" title="Scegli sfondo header">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['header_bg'] }}"
|
|
data-color-input="header_bg">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="header_text" class="form-label">Testo Header</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="header_text"
|
|
name="header_text" value="{{ $currentTheme['header_text'] }}" title="Scegli colore testo header">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['header_text'] }}"
|
|
data-color-input="header_text">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="light_color" class="form-label">Colore Chiaro</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="light_color"
|
|
name="light_color" value="{{ $currentTheme['light_color'] }}" title="Scegli colore chiaro">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['light_color'] }}"
|
|
data-color-input="light_color">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="dark_color" class="form-label">Colore Scuro</label>
|
|
<div class="input-group">
|
|
<input type="color" class="form-control form-control-color" id="dark_color"
|
|
name="dark_color" value="{{ $currentTheme['dark_color'] }}" title="Scegli colore scuro">
|
|
<input type="text" class="form-control" value="{{ $currentTheme['dark_color'] }}"
|
|
data-color-input="dark_color">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="theme_mode" class="form-label">Modalità Tema</label>
|
|
<select class="form-select" id="theme_mode" name="theme_mode">
|
|
<option value="light" {{ $currentTheme['theme_mode'] == 'light' ? 'selected' : '' }}>Chiaro</option>
|
|
<option value="dark" {{ $currentTheme['theme_mode'] == 'dark' ? 'selected' : '' }}>Scuro</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mt-4">
|
|
<div class="col-12">
|
|
<button type="submit" class="btn btn-primary me-2">
|
|
<i class="fas fa-save me-2"></i>Salva Tema
|
|
</button>
|
|
<button type="button" class="btn btn-secondary me-2" onclick="previewTheme()">
|
|
<i class="fas fa-eye me-2"></i>Anteprima
|
|
</button>
|
|
<button type="button" class="btn btn-outline-danger" onclick="resetTheme()">
|
|
<i class="fas fa-undo me-2"></i>Ripristina Default
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Temi Predefiniti -->
|
|
<div class="tab-pane fade" id="presets" role="tabpanel">
|
|
<div class="row">
|
|
@foreach($presetThemes as $key => $preset)
|
|
<div class="col-md-6 col-lg-4 mb-3">
|
|
<div class="card preset-theme-card" data-preset="{{ $key }}">
|
|
<div class="card-header d-flex justify-content-between align-items-center"
|
|
style="background-color: {{ $preset['colors']['primary_color'] }}; color: white;">
|
|
<h6 class="mb-0">{{ $preset['name'] }}</h6>
|
|
<i class="fas fa-paint-brush"></i>
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="card-text small">{{ $preset['description'] }}</p>
|
|
<div class="d-flex mb-2">
|
|
<div class="color-preview me-1" style="background-color: {{ $preset['colors']['primary_color'] }};" title="Primario"></div>
|
|
<div class="color-preview me-1" style="background-color: {{ $preset['colors']['secondary_color'] }};" title="Secondario"></div>
|
|
<div class="color-preview me-1" style="background-color: {{ $preset['colors']['sidebar_bg'] }};" title="Sidebar"></div>
|
|
<div class="color-preview me-1" style="background-color: {{ $preset['colors']['header_bg'] }};" title="Header"></div>
|
|
</div>
|
|
<button class="btn btn-sm btn-outline-primary w-100" onclick="applyPreset('{{ $key }}')">
|
|
<i class="fas fa-download me-1"></i>Applica
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Import/Export -->
|
|
<div class="tab-pane fade" id="import-export" role="tabpanel">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<h5>Esporta Tema Corrente</h5>
|
|
<p class="text-muted">Salva le tue impostazioni di tema in un file JSON per backup o condivisione.</p>
|
|
<button class="btn btn-outline-primary" onclick="exportTheme()">
|
|
<i class="fas fa-download me-2"></i>Esporta Tema
|
|
</button>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h5>Importa Tema</h5>
|
|
<p class="text-muted">Carica un file JSON di tema precedentemente esportato.</p>
|
|
<form id="import-form" enctype="multipart/form-data">
|
|
<div class="mb-3">
|
|
<input type="file" class="form-control" id="theme_file" name="theme_file" accept=".json">
|
|
</div>
|
|
<button type="submit" class="btn btn-outline-success">
|
|
<i class="fas fa-upload me-2"></i>Importa Tema
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@push('styles')
|
|
<style>
|
|
.color-preview {
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 3px;
|
|
border: 1px solid #ddd;
|
|
}
|
|
|
|
.preset-theme-card {
|
|
cursor: pointer;
|
|
transition: transform 0.2s;
|
|
}
|
|
|
|
.preset-theme-card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.form-control-color {
|
|
width: 50px;
|
|
height: 38px;
|
|
padding: 2px;
|
|
}
|
|
|
|
#custom-theme-preview {
|
|
border: 1px solid #ddd;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
}
|
|
</style>
|
|
@endpush
|
|
|
|
@push('scripts')
|
|
<script>
|
|
$(document).ready(function() {
|
|
// Sincronizza color picker con input text
|
|
$('input[type="color"]').on('change', function() {
|
|
const colorValue = $(this).val();
|
|
const textInput = $(`input[data-color-input="${$(this).attr('name')}"]`);
|
|
textInput.val(colorValue);
|
|
updatePreview();
|
|
});
|
|
|
|
// Sincronizza input text con color picker
|
|
$('input[data-color-input]').on('input', function() {
|
|
const colorValue = $(this).val();
|
|
if (isValidHexColor(colorValue)) {
|
|
const colorPickerName = $(this).attr('data-color-input');
|
|
$(`input[name="${colorPickerName}"]`).val(colorValue);
|
|
updatePreview();
|
|
}
|
|
});
|
|
|
|
// Aggiorna anteprima quando cambia la modalità tema
|
|
$('#theme_mode').on('change', updatePreview);
|
|
|
|
// Form submission
|
|
$('#theme-form').on('submit', function(e) {
|
|
e.preventDefault();
|
|
saveTheme();
|
|
});
|
|
|
|
// Import form submission
|
|
$('#import-form').on('submit', function(e) {
|
|
e.preventDefault();
|
|
importTheme();
|
|
});
|
|
});
|
|
|
|
function updatePreview() {
|
|
// Implementa anteprima in tempo reale
|
|
const formData = new FormData(document.getElementById('theme-form'));
|
|
const themeData = Object.fromEntries(formData);
|
|
|
|
// Applica temporaneamente il CSS per l'anteprima
|
|
applyTemporaryCSS(themeData);
|
|
}
|
|
|
|
function applyTemporaryCSS(themeData) {
|
|
// Rimuove il CSS temporaneo precedente
|
|
$('#temp-theme-css').remove();
|
|
|
|
// Crea nuovo CSS temporaneo
|
|
const css = generateCSS(themeData);
|
|
$('<style id="temp-theme-css">' + css + '</style>').appendTo('head');
|
|
}
|
|
|
|
function generateCSS(theme) {
|
|
return `
|
|
:root {
|
|
--netgescon-primary: ${theme.primary_color};
|
|
--netgescon-secondary: ${theme.secondary_color};
|
|
--netgescon-success: ${theme.success_color};
|
|
--netgescon-danger: ${theme.danger_color};
|
|
--netgescon-warning: ${theme.warning_color};
|
|
--netgescon-info: ${theme.info_color};
|
|
--netgescon-light: ${theme.light_color};
|
|
--netgescon-dark: ${theme.dark_color};
|
|
--netgescon-sidebar-bg: ${theme.sidebar_bg};
|
|
--netgescon-sidebar-text: ${theme.sidebar_text};
|
|
--netgescon-header-bg: ${theme.header_bg};
|
|
--netgescon-header-text: ${theme.header_text};
|
|
}
|
|
|
|
.netgescon-sidebar {
|
|
background-color: var(--netgescon-sidebar-bg) !important;
|
|
color: var(--netgescon-sidebar-text) !important;
|
|
}
|
|
|
|
.netgescon-sidebar .nav-link {
|
|
color: var(--netgescon-sidebar-text) !important;
|
|
}
|
|
|
|
.btn-primary {
|
|
background-color: var(--netgescon-primary) !important;
|
|
border-color: var(--netgescon-primary) !important;
|
|
}
|
|
`;
|
|
}
|
|
|
|
function saveTheme() {
|
|
const formData = new FormData(document.getElementById('theme-form'));
|
|
|
|
$.ajax({
|
|
url: '{{ route("admin.theme.save") }}',
|
|
method: 'POST',
|
|
data: formData,
|
|
processData: false,
|
|
contentType: false,
|
|
headers: {
|
|
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
|
|
},
|
|
success: function(response) {
|
|
showAlert('success', response.message);
|
|
// Applica il CSS definitivo
|
|
$('#temp-theme-css').remove();
|
|
$('<style id="user-theme-css">' + response.css + '</style>').appendTo('head');
|
|
},
|
|
error: function(xhr) {
|
|
const message = xhr.responseJSON?.message || 'Errore nel salvataggio del tema';
|
|
showAlert('danger', message);
|
|
}
|
|
});
|
|
}
|
|
|
|
function applyPreset(presetName) {
|
|
$.ajax({
|
|
url: '{{ route("admin.theme.preset") }}',
|
|
method: 'POST',
|
|
data: {
|
|
preset: presetName
|
|
},
|
|
headers: {
|
|
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
|
|
},
|
|
success: function(response) {
|
|
showAlert('success', response.message);
|
|
// Aggiorna i campi del form
|
|
updateFormFields(response.theme);
|
|
// Applica il CSS
|
|
$('#temp-theme-css, #user-theme-css').remove();
|
|
$('<style id="user-theme-css">' + response.css + '</style>').appendTo('head');
|
|
},
|
|
error: function(xhr) {
|
|
const message = xhr.responseJSON?.message || 'Errore nell\'applicazione del tema';
|
|
showAlert('danger', message);
|
|
}
|
|
});
|
|
}
|
|
|
|
function resetTheme() {
|
|
if (confirm('Sei sicuro di voler ripristinare il tema ai valori di default?')) {
|
|
$.ajax({
|
|
url: '{{ route("admin.theme.reset") }}',
|
|
method: 'POST',
|
|
headers: {
|
|
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
|
|
},
|
|
success: function(response) {
|
|
showAlert('success', response.message);
|
|
updateFormFields(response.theme);
|
|
$('#temp-theme-css, #user-theme-css').remove();
|
|
$('<style id="user-theme-css">' + response.css + '</style>').appendTo('head');
|
|
},
|
|
error: function(xhr) {
|
|
const message = xhr.responseJSON?.message || 'Errore nel ripristino del tema';
|
|
showAlert('danger', message);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function exportTheme() {
|
|
window.location.href = '{{ route("admin.theme.export") }}';
|
|
}
|
|
|
|
function importTheme() {
|
|
const formData = new FormData(document.getElementById('import-form'));
|
|
|
|
$.ajax({
|
|
url: '{{ route("admin.theme.import") }}',
|
|
method: 'POST',
|
|
data: formData,
|
|
processData: false,
|
|
contentType: false,
|
|
headers: {
|
|
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
|
|
},
|
|
success: function(response) {
|
|
showAlert('success', response.message);
|
|
updateFormFields(response.theme);
|
|
$('#temp-theme-css, #user-theme-css').remove();
|
|
$('<style id="user-theme-css">' + response.css + '</style>').appendTo('head');
|
|
$('#import-form')[0].reset();
|
|
},
|
|
error: function(xhr) {
|
|
const message = xhr.responseJSON?.message || 'Errore nell\'importazione del tema';
|
|
showAlert('danger', message);
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateFormFields(theme) {
|
|
Object.keys(theme).forEach(key => {
|
|
const input = $(`#${key}`);
|
|
const textInput = $(`input[data-color-input="${key}"]`);
|
|
|
|
if (input.length) {
|
|
input.val(theme[key]);
|
|
}
|
|
if (textInput.length) {
|
|
textInput.val(theme[key]);
|
|
}
|
|
});
|
|
}
|
|
|
|
function showAlert(type, message) {
|
|
const alert = $('#theme-alert');
|
|
alert.removeClass('alert-success alert-danger alert-warning alert-info')
|
|
.addClass(`alert-${type}`)
|
|
.text(message)
|
|
.show();
|
|
|
|
setTimeout(() => {
|
|
alert.hide();
|
|
}, 5000);
|
|
}
|
|
|
|
function isValidHexColor(hex) {
|
|
return /^#([a-f0-9]{3}){1,2}$/i.test(hex);
|
|
}
|
|
|
|
function previewTheme() {
|
|
updatePreview();
|
|
showAlert('info', 'Anteprima applicata! Salva per rendere permanenti le modifiche.');
|
|
}
|
|
</script>
|
|
@endpush
|