808 lines
25 KiB
Markdown
Executable File
808 lines
25 KiB
Markdown
Executable File
# 📄 CONTENT AREA - Documentazione Completa NetGescon
|
|
|
|
> **Componente Content Area dell'interfaccia universale**
|
|
> **📅 Creato:** 21/07/2025
|
|
> **🔄 Ultimo aggiornamento:** 21/07/2025
|
|
> **🎯 Priorità:** 🔥 CRITICA
|
|
|
|
---
|
|
|
|
## 📋 **OVERVIEW CONTENT AREA**
|
|
|
|
Il Content Area NetGescon è la **ZONA PRINCIPALE** dove vengono visualizzati:
|
|
- 🗺️ **Breadcrumb Navigation**
|
|
- 📝 **Page Headers con Actions**
|
|
- 📊 **Dashboard Cards**
|
|
- 📋 **Forms & Tables**
|
|
- 🔔 **Alert Messages**
|
|
- 📄 **Main Page Content**
|
|
|
|
### 📐 **Specifiche Layout**
|
|
- **Posizione:** `margin-left: 280px` desktop, `margin-left: 0` mobile
|
|
- **Altezza:** `calc(100vh - 140px)` (header + footer esclusi)
|
|
- **Padding:** `24px` standard, `16px` mobile
|
|
- **Overflow:** `scroll` quando contenuto > viewport
|
|
|
|
---
|
|
|
|
## 🎨 **DESIGN SPECIFICATIONS**
|
|
|
|
### 🌈 **Color Palette**
|
|
```css
|
|
/* Content Background */
|
|
.content-area {
|
|
background: var(--netgescon-gray-50);
|
|
min-height: calc(100vh - 140px);
|
|
}
|
|
|
|
/* Page Headers */
|
|
.page-header {
|
|
background: white;
|
|
border-bottom: 1px solid var(--netgescon-gray-200);
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
/* Content Containers */
|
|
.content-container {
|
|
background: white;
|
|
border: 1px solid var(--netgescon-gray-200);
|
|
border-radius: 12px;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
}
|
|
```
|
|
|
|
### 📏 **Dimensions**
|
|
```css
|
|
.content-area {
|
|
margin-left: 280px; /* Desktop con sidebar */
|
|
margin-top: 70px; /* Header height */
|
|
margin-bottom: 70px; /* Footer height */
|
|
padding: 24px;
|
|
min-height: calc(100vh - 140px);
|
|
}
|
|
|
|
/* Mobile */
|
|
@media (max-width: 767px) {
|
|
.content-area {
|
|
margin-left: 0;
|
|
padding: 16px;
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🧩 **STRUTTURA CONTENT AREA**
|
|
|
|
### 📁 **Layout Template**
|
|
```blade
|
|
<!-- resources/views/layouts/content.blade.php -->
|
|
<main class="content-area">
|
|
<!-- Breadcrumb Navigation -->
|
|
@if(isset($breadcrumbs) && count($breadcrumbs) > 0)
|
|
@include('components.layout.breadcrumb', ['breadcrumbs' => $breadcrumbs])
|
|
@endif
|
|
|
|
<!-- Page Header -->
|
|
@if(isset($pageTitle) || isset($pageActions))
|
|
@include('components.layout.page-header', [
|
|
'title' => $pageTitle ?? '',
|
|
'subtitle' => $pageSubtitle ?? '',
|
|
'actions' => $pageActions ?? []
|
|
])
|
|
@endif
|
|
|
|
<!-- Alert Messages -->
|
|
@include('components.layout.alerts')
|
|
|
|
<!-- Main Content -->
|
|
<div class="main-content">
|
|
@yield('content')
|
|
</div>
|
|
</main>
|
|
```
|
|
|
|
---
|
|
|
|
## 🗺️ **BREADCRUMB COMPONENT**
|
|
|
|
### 📁 **File:** `components/layout/breadcrumb.blade.php`
|
|
```blade
|
|
<nav class="breadcrumb-nav mb-6" aria-label="breadcrumb">
|
|
<ol class="breadcrumb flex items-center space-x-2 text-sm">
|
|
<!-- Home sempre presente -->
|
|
<li class="breadcrumb-item">
|
|
<a href="{{ route('dashboard') }}"
|
|
class="text-gray-600 hover:text-blue-600 transition-colors">
|
|
<i class="fas fa-home mr-1"></i>
|
|
Dashboard
|
|
</a>
|
|
</li>
|
|
|
|
@foreach($breadcrumbs as $breadcrumb)
|
|
<li class="breadcrumb-item flex items-center">
|
|
<i class="fas fa-chevron-right text-gray-400 mx-2"></i>
|
|
@if(isset($breadcrumb['url']) && !$loop->last)
|
|
<a href="{{ $breadcrumb['url'] }}"
|
|
class="text-gray-600 hover:text-blue-600 transition-colors">
|
|
@if(isset($breadcrumb['icon']))
|
|
<i class="{{ $breadcrumb['icon'] }} mr-1"></i>
|
|
@endif
|
|
{{ $breadcrumb['title'] }}
|
|
</a>
|
|
@else
|
|
<span class="text-gray-900 font-medium">
|
|
@if(isset($breadcrumb['icon']))
|
|
<i class="{{ $breadcrumb['icon'] }} mr-1"></i>
|
|
@endif
|
|
{{ $breadcrumb['title'] }}
|
|
</span>
|
|
@endif
|
|
</li>
|
|
@endforeach
|
|
</ol>
|
|
</nav>
|
|
```
|
|
|
|
### 💻 **Usage Examples**
|
|
```php
|
|
// In Controller
|
|
$breadcrumbs = [
|
|
['title' => 'Gestione Stabili', 'url' => route('admin.stabili.index'), 'icon' => 'fas fa-building'],
|
|
['title' => 'Nuovo Stabile', 'icon' => 'fas fa-plus']
|
|
];
|
|
|
|
return view('admin.stabili.create', compact('breadcrumbs'));
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 **PAGE HEADER COMPONENT**
|
|
|
|
### 📁 **File:** `components/layout/page-header.blade.php`
|
|
```blade
|
|
<div class="page-header bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-6">
|
|
<div class="flex flex-col lg:flex-row lg:items-center lg:justify-between">
|
|
<!-- Title Section -->
|
|
<div class="mb-4 lg:mb-0">
|
|
@if(isset($title))
|
|
<h1 class="text-2xl font-bold text-gray-900">
|
|
{{ $title }}
|
|
</h1>
|
|
@endif
|
|
|
|
@if(isset($subtitle))
|
|
<p class="text-gray-600 mt-1">
|
|
{{ $subtitle }}
|
|
</p>
|
|
@endif
|
|
|
|
<!-- Stats or Meta Info -->
|
|
@if(isset($stats))
|
|
<div class="flex items-center space-x-4 mt-3 text-sm text-gray-500">
|
|
@foreach($stats as $stat)
|
|
<span class="flex items-center">
|
|
@if(isset($stat['icon']))
|
|
<i class="{{ $stat['icon'] }} mr-1"></i>
|
|
@endif
|
|
{{ $stat['label'] }}:
|
|
<strong class="ml-1 text-gray-900">{{ $stat['value'] }}</strong>
|
|
</span>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Actions Section -->
|
|
@if(isset($actions) && count($actions) > 0)
|
|
<div class="flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-3">
|
|
@foreach($actions as $action)
|
|
@if($action['type'] === 'primary')
|
|
<a href="{{ $action['url'] }}"
|
|
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg
|
|
flex items-center justify-center transition-colors">
|
|
@if(isset($action['icon']))
|
|
<i class="{{ $action['icon'] }} mr-2"></i>
|
|
@endif
|
|
{{ $action['label'] }}
|
|
</a>
|
|
@elseif($action['type'] === 'secondary')
|
|
<a href="{{ $action['url'] }}"
|
|
class="bg-gray-100 hover:bg-gray-200 text-gray-700 px-4 py-2 rounded-lg
|
|
flex items-center justify-center transition-colors border border-gray-300">
|
|
@if(isset($action['icon']))
|
|
<i class="{{ $action['icon'] }} mr-2"></i>
|
|
@endif
|
|
{{ $action['label'] }}
|
|
</a>
|
|
@elseif($action['type'] === 'danger')
|
|
<button onclick="{{ $action['onclick'] ?? '' }}"
|
|
class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-lg
|
|
flex items-center justify-center transition-colors">
|
|
@if(isset($action['icon']))
|
|
<i class="{{ $action['icon'] }} mr-2"></i>
|
|
@endif
|
|
{{ $action['label'] }}
|
|
</button>
|
|
@endif
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### 💻 **Usage Examples**
|
|
```php
|
|
// In Controller
|
|
$pageTitle = 'Gestione Stabili';
|
|
$pageSubtitle = 'Visualizza e gestisci tutti gli stabili del sistema';
|
|
$pageActions = [
|
|
[
|
|
'type' => 'primary',
|
|
'label' => 'Nuovo Stabile',
|
|
'url' => route('admin.stabili.create'),
|
|
'icon' => 'fas fa-plus'
|
|
],
|
|
[
|
|
'type' => 'secondary',
|
|
'label' => 'Esporta Excel',
|
|
'url' => route('admin.stabili.export'),
|
|
'icon' => 'fas fa-download'
|
|
]
|
|
];
|
|
$stats = [
|
|
['label' => 'Totale Stabili', 'value' => '45', 'icon' => 'fas fa-building'],
|
|
['label' => 'Condomini', 'value' => '128', 'icon' => 'fas fa-home'],
|
|
['label' => 'Unità', 'value' => '1,247', 'icon' => 'fas fa-door-open']
|
|
];
|
|
|
|
return view('admin.stabili.index', compact('pageTitle', 'pageSubtitle', 'pageActions', 'stats'));
|
|
```
|
|
|
|
---
|
|
|
|
## 🔔 **ALERT MESSAGES COMPONENT**
|
|
|
|
### 📁 **File:** `components/layout/alerts.blade.php`
|
|
```blade
|
|
<!-- Flash Messages -->
|
|
@if(session('success'))
|
|
<div class="alert alert-success flex items-center p-4 mb-6 bg-green-50 border border-green-200 rounded-lg">
|
|
<i class="fas fa-check-circle text-green-600 mr-3"></i>
|
|
<div class="flex-1">
|
|
<h4 class="font-medium text-green-800">Operazione completata!</h4>
|
|
<p class="text-green-700 text-sm mt-1">{{ session('success') }}</p>
|
|
</div>
|
|
<button class="alert-close text-green-600 hover:text-green-800 ml-4">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
@endif
|
|
|
|
@if(session('error'))
|
|
<div class="alert alert-error flex items-center p-4 mb-6 bg-red-50 border border-red-200 rounded-lg">
|
|
<i class="fas fa-exclamation-circle text-red-600 mr-3"></i>
|
|
<div class="flex-1">
|
|
<h4 class="font-medium text-red-800">Errore!</h4>
|
|
<p class="text-red-700 text-sm mt-1">{{ session('error') }}</p>
|
|
</div>
|
|
<button class="alert-close text-red-600 hover:text-red-800 ml-4">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
@endif
|
|
|
|
@if(session('warning'))
|
|
<div class="alert alert-warning flex items-center p-4 mb-6 bg-yellow-50 border border-yellow-200 rounded-lg">
|
|
<i class="fas fa-exclamation-triangle text-yellow-600 mr-3"></i>
|
|
<div class="flex-1">
|
|
<h4 class="font-medium text-yellow-800">Attenzione!</h4>
|
|
<p class="text-yellow-700 text-sm mt-1">{{ session('warning') }}</p>
|
|
</div>
|
|
<button class="alert-close text-yellow-600 hover:text-yellow-800 ml-4">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
@endif
|
|
|
|
@if(session('info'))
|
|
<div class="alert alert-info flex items-center p-4 mb-6 bg-blue-50 border border-blue-200 rounded-lg">
|
|
<i class="fas fa-info-circle text-blue-600 mr-3"></i>
|
|
<div class="flex-1">
|
|
<h4 class="font-medium text-blue-800">Informazione</h4>
|
|
<p class="text-blue-700 text-sm mt-1">{{ session('info') }}</p>
|
|
</div>
|
|
<button class="alert-close text-blue-600 hover:text-blue-800 ml-4">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Validation Errors -->
|
|
@if($errors->any())
|
|
<div class="alert alert-validation flex items-start p-4 mb-6 bg-red-50 border border-red-200 rounded-lg">
|
|
<i class="fas fa-exclamation-circle text-red-600 mr-3 mt-0.5"></i>
|
|
<div class="flex-1">
|
|
<h4 class="font-medium text-red-800">Errori di validazione</h4>
|
|
<ul class="text-red-700 text-sm mt-2 space-y-1">
|
|
@foreach($errors->all() as $error)
|
|
<li>• {{ $error }}</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
<button class="alert-close text-red-600 hover:text-red-800 ml-4">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
@endif
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 **DASHBOARD CARDS PATTERN**
|
|
|
|
### 🃏 **Stats Cards**
|
|
```blade
|
|
<!-- Stats Grid -->
|
|
<div class="stats-grid grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
|
<!-- Card Stabili -->
|
|
<div class="stat-card bg-gradient-to-br from-blue-50 to-blue-100 border border-blue-200 rounded-lg p-6
|
|
hover:shadow-lg transition-shadow duration-200">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="text-blue-600 text-sm font-medium uppercase tracking-wide">Stabili</p>
|
|
<p class="text-3xl font-bold text-blue-900 mt-1">{{ $stats['stabili'] ?? 0 }}</p>
|
|
<p class="text-blue-700 text-sm mt-1">
|
|
<i class="fas fa-arrow-up mr-1"></i>
|
|
+3 questo mese
|
|
</p>
|
|
</div>
|
|
<div class="bg-blue-500 rounded-full p-3">
|
|
<i class="fas fa-building text-white text-xl"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Card Condomini -->
|
|
<div class="stat-card bg-gradient-to-br from-green-50 to-green-100 border border-green-200 rounded-lg p-6
|
|
hover:shadow-lg transition-shadow duration-200">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="text-green-600 text-sm font-medium uppercase tracking-wide">Condomini</p>
|
|
<p class="text-3xl font-bold text-green-900 mt-1">{{ $stats['condomini'] ?? 0 }}</p>
|
|
<p class="text-green-700 text-sm mt-1">
|
|
<i class="fas fa-arrow-up mr-1"></i>
|
|
+8 questo mese
|
|
</p>
|
|
</div>
|
|
<div class="bg-green-500 rounded-full p-3">
|
|
<i class="fas fa-home text-white text-xl"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Card Unità -->
|
|
<div class="stat-card bg-gradient-to-br from-purple-50 to-purple-100 border border-purple-200 rounded-lg p-6
|
|
hover:shadow-lg transition-shadow duration-200">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="text-purple-600 text-sm font-medium uppercase tracking-wide">Unità</p>
|
|
<p class="text-3xl font-bold text-purple-900 mt-1">{{ $stats['unita'] ?? 0 }}</p>
|
|
<p class="text-purple-700 text-sm mt-1">
|
|
<i class="fas fa-minus mr-1"></i>
|
|
Nessuna variazione
|
|
</p>
|
|
</div>
|
|
<div class="bg-purple-500 rounded-full p-3">
|
|
<i class="fas fa-door-open text-white text-xl"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Card Revenue -->
|
|
<div class="stat-card bg-gradient-to-br from-yellow-50 to-yellow-100 border border-yellow-200 rounded-lg p-6
|
|
hover:shadow-lg transition-shadow duration-200">
|
|
<div class="flex items-center justify-between">
|
|
<div>
|
|
<p class="text-yellow-600 text-sm font-medium uppercase tracking-wide">Fatturato</p>
|
|
<p class="text-3xl font-bold text-yellow-900 mt-1">€{{ number_format($stats['fatturato'] ?? 0, 0, ',', '.') }}</p>
|
|
<p class="text-yellow-700 text-sm mt-1">
|
|
<i class="fas fa-arrow-up mr-1"></i>
|
|
+12% vs mese scorso
|
|
</p>
|
|
</div>
|
|
<div class="bg-yellow-500 rounded-full p-3">
|
|
<i class="fas fa-euro-sign text-white text-xl"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 **FORMS PATTERN**
|
|
|
|
### 📝 **Standard Form Container**
|
|
```blade
|
|
<div class="form-container bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
|
<form method="POST" action="{{ $formAction }}" enctype="multipart/form-data">
|
|
@csrf
|
|
@if(isset($method) && $method !== 'POST')
|
|
@method($method)
|
|
@endif
|
|
|
|
<!-- Form Header -->
|
|
<div class="form-header pb-4 border-b border-gray-200 mb-6">
|
|
<h2 class="text-xl font-semibold text-gray-900">{{ $formTitle }}</h2>
|
|
@if(isset($formDescription))
|
|
<p class="text-gray-600 mt-1">{{ $formDescription }}</p>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Form Fields -->
|
|
<div class="form-fields space-y-6">
|
|
@yield('form-fields')
|
|
</div>
|
|
|
|
<!-- Form Actions -->
|
|
<div class="form-actions flex justify-between items-center pt-6 border-t border-gray-200 mt-8">
|
|
<div class="form-info text-sm text-gray-500">
|
|
<i class="fas fa-info-circle mr-1"></i>
|
|
I campi con * sono obbligatori
|
|
</div>
|
|
|
|
<div class="form-buttons flex space-x-3">
|
|
<a href="{{ $cancelUrl ?? url()->previous() }}"
|
|
class="btn btn-secondary">
|
|
<i class="fas fa-times mr-2"></i>
|
|
Annulla
|
|
</a>
|
|
|
|
@if(isset($draftButton) && $draftButton)
|
|
<button type="button" onclick="saveDraft()"
|
|
class="btn btn-outline">
|
|
<i class="fas fa-save mr-2"></i>
|
|
Salva Bozza
|
|
</button>
|
|
@endif
|
|
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-check mr-2"></i>
|
|
{{ $submitLabel ?? 'Salva' }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 **TABLES PATTERN**
|
|
|
|
### 📋 **Data Table Container**
|
|
```blade
|
|
<div class="table-container bg-white rounded-lg shadow-sm border border-gray-200">
|
|
<!-- Table Header -->
|
|
<div class="table-header flex flex-col lg:flex-row lg:items-center lg:justify-between p-6 border-b border-gray-200">
|
|
<div class="table-title">
|
|
<h3 class="text-lg font-semibold text-gray-900">{{ $tableTitle }}</h3>
|
|
@if(isset($tableDescription))
|
|
<p class="text-gray-600 text-sm mt-1">{{ $tableDescription }}</p>
|
|
@endif
|
|
</div>
|
|
|
|
<div class="table-actions flex items-center space-x-3 mt-4 lg:mt-0">
|
|
<!-- Search -->
|
|
<div class="relative">
|
|
<input type="text" id="table-search" placeholder="Cerca..."
|
|
class="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
|
|
<i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
|
|
</div>
|
|
|
|
<!-- Filters -->
|
|
@if(isset($filters))
|
|
<select id="table-filter" class="border border-gray-300 rounded-lg px-3 py-2">
|
|
<option value="">Tutti</option>
|
|
@foreach($filters as $filter)
|
|
<option value="{{ $filter['value'] }}">{{ $filter['label'] }}</option>
|
|
@endforeach
|
|
</select>
|
|
@endif
|
|
|
|
<!-- Export -->
|
|
<button class="btn btn-outline btn-sm">
|
|
<i class="fas fa-download mr-2"></i>
|
|
Esporta
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Table Content -->
|
|
<div class="table-content overflow-x-auto">
|
|
<table class="table w-full">
|
|
<thead class="table-header bg-gray-50">
|
|
@yield('table-header')
|
|
</thead>
|
|
<tbody class="table-body">
|
|
@yield('table-body')
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Table Footer (Pagination) -->
|
|
@if(isset($pagination))
|
|
<div class="table-footer p-6 border-t border-gray-200">
|
|
{{ $pagination->links('components.pagination') }}
|
|
</div>
|
|
@endif
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## ⚡ **JAVASCRIPT FUNCTIONALITY**
|
|
|
|
### 📁 **File:** `/public/js/components/content.js`
|
|
|
|
```javascript
|
|
/**
|
|
* Content Area - JavaScript Functions
|
|
*/
|
|
|
|
// Inizializza content area
|
|
function initializeContentArea() {
|
|
initializeAlerts();
|
|
initializeScrollToTop();
|
|
initializeStickyHeaders();
|
|
initializeInfiniteScroll();
|
|
}
|
|
|
|
// Alert management
|
|
function initializeAlerts() {
|
|
// Auto-hide alerts
|
|
const alerts = document.querySelectorAll('.alert');
|
|
alerts.forEach(alert => {
|
|
const closeBtn = alert.querySelector('.alert-close');
|
|
|
|
// Close button
|
|
if (closeBtn) {
|
|
closeBtn.addEventListener('click', () => {
|
|
hideAlert(alert);
|
|
});
|
|
}
|
|
|
|
// Auto-hide after 5 seconds
|
|
setTimeout(() => {
|
|
hideAlert(alert);
|
|
}, 5000);
|
|
});
|
|
}
|
|
|
|
function hideAlert(alert) {
|
|
alert.style.opacity = '0';
|
|
alert.style.transform = 'translateY(-10px)';
|
|
setTimeout(() => {
|
|
if (alert.parentNode) {
|
|
alert.parentNode.removeChild(alert);
|
|
}
|
|
}, 300);
|
|
}
|
|
|
|
// Scroll to top functionality
|
|
function initializeScrollToTop() {
|
|
const scrollBtn = document.createElement('button');
|
|
scrollBtn.className = 'scroll-to-top fixed bottom-20 right-6 bg-blue-600 text-white p-3 rounded-full shadow-lg opacity-0 transition-opacity duration-300 z-50';
|
|
scrollBtn.innerHTML = '<i class="fas fa-arrow-up"></i>';
|
|
scrollBtn.onclick = () => window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
document.body.appendChild(scrollBtn);
|
|
|
|
window.addEventListener('scroll', () => {
|
|
if (window.scrollY > 300) {
|
|
scrollBtn.style.opacity = '1';
|
|
} else {
|
|
scrollBtn.style.opacity = '0';
|
|
}
|
|
});
|
|
}
|
|
|
|
// Sticky headers for long content
|
|
function initializeStickyHeaders() {
|
|
const headers = document.querySelectorAll('.page-header');
|
|
headers.forEach(header => {
|
|
const observer = new IntersectionObserver(
|
|
([entry]) => {
|
|
if (!entry.isIntersecting) {
|
|
header.classList.add('sticky-header');
|
|
} else {
|
|
header.classList.remove('sticky-header');
|
|
}
|
|
},
|
|
{ threshold: 0.1 }
|
|
);
|
|
observer.observe(header);
|
|
});
|
|
}
|
|
|
|
// Infinite scroll for tables/lists
|
|
function initializeInfiniteScroll() {
|
|
const container = document.querySelector('[data-infinite-scroll]');
|
|
if (!container) return;
|
|
|
|
let loading = false;
|
|
let page = 2;
|
|
|
|
window.addEventListener('scroll', () => {
|
|
if (loading) return;
|
|
|
|
const { scrollTop, scrollHeight, clientHeight } = document.documentElement;
|
|
|
|
if (scrollTop + clientHeight >= scrollHeight - 5) {
|
|
loading = true;
|
|
loadMoreContent(page++).then(() => {
|
|
loading = false;
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// Load more content via AJAX
|
|
async function loadMoreContent(page) {
|
|
try {
|
|
const response = await fetch(`${window.location.pathname}?page=${page}`, {
|
|
headers: {
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
}
|
|
});
|
|
|
|
const html = await response.text();
|
|
const container = document.querySelector('[data-infinite-scroll]');
|
|
container.insertAdjacentHTML('beforeend', html);
|
|
} catch (error) {
|
|
console.error('Error loading more content:', error);
|
|
}
|
|
}
|
|
|
|
// Initialize on DOM ready
|
|
document.addEventListener('DOMContentLoaded', initializeContentArea);
|
|
```
|
|
|
|
---
|
|
|
|
## 📱 **RESPONSIVE BEHAVIOR**
|
|
|
|
### 🖥️ **Desktop (≥1024px)**
|
|
```css
|
|
.content-area {
|
|
margin-left: 280px;
|
|
padding: 24px;
|
|
}
|
|
|
|
.page-header {
|
|
flex-direction: row;
|
|
align-items: center;
|
|
}
|
|
|
|
.stats-grid {
|
|
grid-template-columns: repeat(4, 1fr);
|
|
}
|
|
```
|
|
|
|
### 📱 **Tablet (768px - 1023px)**
|
|
```css
|
|
.content-area {
|
|
margin-left: 0;
|
|
padding: 20px;
|
|
}
|
|
|
|
.stats-grid {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
|
|
.page-header .flex {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
```
|
|
|
|
### 📱 **Mobile (≤767px)**
|
|
```css
|
|
.content-area {
|
|
padding: 16px;
|
|
}
|
|
|
|
.stats-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.table-container {
|
|
overflow-x: scroll;
|
|
}
|
|
|
|
.form-actions {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 **TESTING CHECKLIST**
|
|
|
|
### ✅ **Layout Tests**
|
|
- [ ] Content area positioning correct
|
|
- [ ] Responsive margins working
|
|
- [ ] Scroll behavior smooth
|
|
- [ ] Cards grid responsive
|
|
- [ ] Forms layout consistent
|
|
|
|
### ✅ **Component Tests**
|
|
- [ ] Breadcrumb navigation functional
|
|
- [ ] Page header actions working
|
|
- [ ] Alerts display/hide correctly
|
|
- [ ] Tables search/filter working
|
|
- [ ] Forms validation display
|
|
|
|
### ✅ **Interaction Tests**
|
|
- [ ] Scroll to top button
|
|
- [ ] Infinite scroll loading
|
|
- [ ] Alert auto-hide
|
|
- [ ] Mobile form usability
|
|
|
|
---
|
|
|
|
## 🐛 **COMMON ISSUES & FIXES**
|
|
|
|
### 🚨 **Content nascosto sotto header/footer**
|
|
**Causa:** Margin/padding calculations incorrect
|
|
**Fix:** Verifica calcoli height e margins CSS
|
|
|
|
### 🚨 **Cards non responsive**
|
|
**Causa:** Grid breakpoints non configurati
|
|
**Fix:** Aggiorna grid-template-columns per ogni breakpoint
|
|
|
|
### 🚨 **Alerts non si chiudono**
|
|
**Causa:** JavaScript event listeners non agganciati
|
|
**Fix:** Verifica inizializzazione initializeAlerts()
|
|
|
|
### 🚨 **Forms troppo larghi su mobile**
|
|
**Causa:** Responsive form layout non implementato
|
|
**Fix:** Aggiorna CSS per form mobile-first
|
|
|
|
---
|
|
|
|
## 📚 **FILES CORRELATI**
|
|
|
|
### 🧩 **Templates**
|
|
- `/resources/views/layouts/content.blade.php`
|
|
- `/resources/views/components/layout/` (directory)
|
|
|
|
### 🎨 **Styles**
|
|
- `/public/css/components/content.css`
|
|
- `/public/css/components/forms.css`
|
|
- `/public/css/components/tables.css`
|
|
|
|
### ⚡ **JavaScript**
|
|
- `/public/js/components/content.js`
|
|
- `/public/js/components/forms.js`
|
|
- `/public/js/components/tables.js`
|
|
|
|
---
|
|
|
|
## 🎯 **NEXT STEPS**
|
|
|
|
### 🔮 **Planned Improvements**
|
|
- [ ] **Virtual Scrolling** - Performance per grandi dataset
|
|
- [ ] **Advanced Filters** - Multi-criteria filtering
|
|
- [ ] **Bulk Actions** - Multiple selections
|
|
- [ ] **Export Templates** - Custom export formats
|
|
- [ ] **Real-time Updates** - WebSocket integration
|
|
|
|
---
|
|
|
|
*📝 Documento mantenuto da: GitHub Copilot AI Assistant*
|
|
*🔗 Componente: Content Area NetGescon Interface v2.1.0*
|