feat(cms): implement complete SysSiteCMS content management system

Initial implementation of the HomeSysPanel CMS built with Laravel 13.0.
Includes complete admin panel, content management, user authentication,
modular architecture for products/services/chatbot, and marketing tools.

Key features:
- User authentication with email verification and password reset
- Role-based access control (Admin/User with granular permissions)
- Content management (pages, news, FAQ, downloads, galleries, banners)
- Contact management system with subjects and responses
- Marketing tools (email campaigns, visitor analytics, social sharing)
- Modular architecture (Products, Services, Chatbot modules)
- AI Chatbot integration with OpenRouter.ai and LlamaCpp support
- Visitor tracking with geolocation
- SEO optimization with meta tags and Open Graph
- Responsive design with Bootstrap 5
- Dark mode support
- Maintenance mode
- Feature request system
This commit is contained in:
marco
2026-09-07 19:06:53 -03:00
commit 38295e7e88
279 changed files with 33691 additions and 0 deletions
+136
View File
@@ -0,0 +1,136 @@
@extends('layouts.app')
@section('title', $gallery->title . ' - Galeria')
@section('meta_description', $gallery->description ?? 'Galeria de fotos.')
@section('content')
<div class="container py-5">
<div class="text-center mb-5">
<h1 class="display-4 fw-bold text-primary">{{ $gallery->title }}</h1>
@if($gallery->description)
<p class="lead text-muted">{{ $gallery->description }}</p>
@endif
</div>
<div class="row g-4" id="gallery-container">
@forelse($gallery->images as $index => $image)
<div class="col-sm-6 col-md-4 col-lg-3">
<div class="card h-100 border-0 shadow-sm overflow-hidden gallery-card" style="cursor: pointer;" onclick="openCarousel({{ $index }})">
<img src="{{ asset('storage/' . $image->file_path) }}" class="card-img-top" alt="{{ $image->description }}" style="height: 200px; object-fit: cover; transition: transform 0.3s;">
@if($image->description)
<div class="card-body p-2 text-center bg-light">
<small class="text-muted">{{ $image->description }}</small>
</div>
@endif
</div>
</div>
@empty
<div class="col-12 text-center py-5">
<i class="bi bi-images fs-1 text-muted d-block mb-3"></i>
<p class="text-muted">Esta galeria ainda não possui fotos.</p>
</div>
@endforelse
</div>
<div class="text-center mt-5">
<a href="{{ route('index') }}" class="btn btn-outline-primary px-5 rounded-pill fw-bold">
<i class="bi bi-arrow-left me-2"></i> Voltar ao Início
</a>
</div>
</div>
<!-- Carousel Modal -->
<div class="modal fade" id="galleryCarouselModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content bg-dark border-0">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title text-white" id="carouselImageTitle"></h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Fechar"></button>
</div>
<div class="modal-body p-0">
<div id="galleryCarousel" class="carousel slide" data-bs-ride="false">
<div class="carousel-inner">
@foreach($gallery->images as $index => $image)
<div class="carousel-item {{ $index === 0 ? 'active' : '' }}" data-index="{{ $index }}" data-title="{{ $image->description ?? '' }}">
<div class="d-flex align-items-center justify-content-center" style="min-height: 400px; max-height: 75vh;">
<img src="{{ asset('storage/' . $image->file_path) }}" class="d-block img-fluid" alt="{{ $image->description }}" style="max-height: 75vh; object-fit: contain;">
</div>
</div>
@endforeach
</div>
@if($gallery->images->count() > 1)
<button class="carousel-control-prev" type="button" data-bs-target="#galleryCarousel" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Anterior</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#galleryCarousel" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Próxima</span>
</button>
@endif
</div>
</div>
<div class="modal-footer border-0 pt-0 justify-content-center">
<span class="text-white-50" id="carouselCounter"></span>
</div>
</div>
</div>
</div>
@endsection
@push('css')
<style>
.gallery-card:hover img {
transform: scale(1.05);
}
#galleryCarouselModal .modal-body {
padding: 0;
}
#galleryCarouselModal .carousel-item img {
transition: none;
}
</style>
@endpush
@push('js')
<script>
const totalImages = {{ $gallery->images->count() }};
let carouselInstance = null;
function openCarousel(index) {
const modal = new bootstrap.Modal(document.getElementById('galleryCarouselModal'));
const carouselEl = document.getElementById('galleryCarousel');
carouselInstance = new bootstrap.Carousel(carouselEl, {
ride: false,
wrap: true
});
carouselInstance.to(index);
updateCounter(index);
modal.show();
}
function updateCounter(index) {
const counter = document.getElementById('carouselCounter');
const title = document.getElementById('carouselImageTitle');
const items = document.querySelectorAll('#galleryCarousel .carousel-item');
if (items[index]) {
counter.textContent = (index + 1) + ' de ' + totalImages;
title.textContent = items[index].dataset.title || '';
}
}
document.getElementById('galleryCarousel')?.addEventListener('slid.bs.carousel', function(e) {
updateCounter(e.to);
});
document.getElementById('galleryCarouselModal')?.addEventListener('hidden.bs.modal', function() {
if (carouselInstance) {
carouselInstance.dispose();
carouselInstance = null;
}
});
</script>
@endpush