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
137 lines
5.6 KiB
PHP
Executable File
137 lines
5.6 KiB
PHP
Executable File
@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
|