Initial commit: Lash Vanshy - Complete project with admin panel, gallery, products, and contact
This commit is contained in:
11
resources/css/app.css
Executable file
11
resources/css/app.css
Executable file
@@ -0,0 +1,11 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
|
||||
@source '../../storage/framework/views/*.php';
|
||||
@source '../**/*.blade.php';
|
||||
@source '../**/*.js';
|
||||
|
||||
@theme {
|
||||
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
1
resources/js/app.js
Executable file
1
resources/js/app.js
Executable file
@@ -0,0 +1 @@
|
||||
import './bootstrap';
|
||||
4
resources/js/bootstrap.js
vendored
Executable file
4
resources/js/bootstrap.js
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
import axios from 'axios';
|
||||
window.axios = axios;
|
||||
|
||||
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
|
||||
263
resources/views/admin/auth/login.blade.php
Executable file
263
resources/views/admin/auth/login.blade.php
Executable file
@@ -0,0 +1,263 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Login - Lash Vanshy Admin</title>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--primary: #F8B4C4;
|
||||
--primary-dark: #E89AAD;
|
||||
--secondary: #FFE4EC;
|
||||
--background: #F8F9FA;
|
||||
--text: #4A4A4A;
|
||||
--text-light: #6c757d;
|
||||
--border: #E9ECEF;
|
||||
--danger: #dc3545;
|
||||
--font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-family);
|
||||
background: linear-gradient(135deg, var(--secondary) 0%, var(--background) 50%, var(--secondary) 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login-wrapper {
|
||||
width: 100%;
|
||||
max-width: 450px;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(248, 180, 196, 0.3);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
|
||||
padding: 2.5rem 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-header .brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.login-header .brand-icon {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.login-header .brand-text {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.login-header p {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.login-body {
|
||||
padding: 2.5rem 2rem;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 0.875rem 1rem;
|
||||
font-family: var(--font-family);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 4px rgba(248, 180, 196, 0.2);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-control.is-invalid {
|
||||
border-color: var(--danger);
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 1rem;
|
||||
border-radius: 12px;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
width: 100%;
|
||||
transition: all 0.3s ease;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 30px rgba(248, 180, 196, 0.4);
|
||||
background: linear-gradient(135deg, var(--primary-dark), var(--primary));
|
||||
color: white;
|
||||
}
|
||||
|
||||
.input-group-text {
|
||||
background: transparent;
|
||||
border: 2px solid var(--border);
|
||||
border-left: none;
|
||||
border-radius: 0 12px 12px 0;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.input-group .form-control {
|
||||
border-right: none;
|
||||
border-radius: 12px 0 0 12px;
|
||||
}
|
||||
|
||||
.input-group .form-control:focus + .input-group-text {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.invalid-feedback {
|
||||
color: var(--danger);
|
||||
font-size: 0.85rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.alert-danger {
|
||||
background: linear-gradient(135deg, #f8d7da, #f5c6cb);
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
color: #721c24;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.back-home {
|
||||
text-align: center;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.back-home a {
|
||||
color: var(--text-light);
|
||||
text-decoration: none;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.back-home a:hover {
|
||||
color: var(--primary-dark);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-wrapper">
|
||||
<div class="login-card">
|
||||
<div class="login-header">
|
||||
<a href="{{ route('home') }}" class="brand">
|
||||
<span class="brand-icon">✨</span>
|
||||
<span class="brand-text">Lash Vanshy</span>
|
||||
</a>
|
||||
<p>Panel de Administración</p>
|
||||
</div>
|
||||
|
||||
<div class="login-body">
|
||||
@if($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
<i class="fas fa-exclamation-circle me-2"></i>
|
||||
@if($errors->has('email'))
|
||||
{{ $errors->first('email') }}
|
||||
@else
|
||||
Los datos proporcionados no son correctos. Por favor, inténtalo de nuevo.
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('admin.login') }}">
|
||||
@csrf
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Correo electrónico</label>
|
||||
<div class="input-group">
|
||||
<input type="email"
|
||||
class="form-control @error('email') is-invalid @enderror"
|
||||
id="email"
|
||||
name="email"
|
||||
value="{{ old('email') }}"
|
||||
placeholder="tu@email.com"
|
||||
required
|
||||
autofocus>
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</span>
|
||||
</div>
|
||||
@error('email')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Contraseña</label>
|
||||
<div class="input-group">
|
||||
<input type="password"
|
||||
class="form-control @error('password') is-invalid @enderror"
|
||||
id="password"
|
||||
name="password"
|
||||
placeholder="••••••••"
|
||||
required>
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-lock"></i>
|
||||
</span>
|
||||
</div>
|
||||
@error('password')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-login">
|
||||
<i class="fas fa-sign-in-alt me-2"></i>Iniciar Sesión
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="back-home">
|
||||
<a href="{{ route('home') }}">
|
||||
<i class="fas fa-arrow-left me-1"></i>Volver al sitio
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
195
resources/views/admin/configuracion/index.blade.php
Executable file
195
resources/views/admin/configuracion/index.blade.php
Executable file
@@ -0,0 +1,195 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Configuración - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Configuración del Sitio')
|
||||
|
||||
@section('content')
|
||||
<!-- Header -->
|
||||
<div class="mb-4">
|
||||
<h2 class="mb-0">Configuración del Sitio</h2>
|
||||
<p class="text-muted mb-0">Administra la información pública del negocio</p>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('admin.configuracion.update') }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Información del Negocio -->
|
||||
<div class="col-lg-6">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-store me-2"></i>Información del Negocio
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="nombre_sitio" class="form-label">Nombre del Sitio</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="nombre_sitio"
|
||||
name="nombre_sitio"
|
||||
value="{{ old('nombre_sitio', $configuracion['nombre_sitio'] ?? 'Lash Vanshy') }}"
|
||||
placeholder="Lash Vanshy">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="telefono" class="form-label">Teléfono</label>
|
||||
<input type="tel"
|
||||
class="form-control"
|
||||
id="telefono"
|
||||
name="telefono"
|
||||
value="{{ old('telefono', $configuracion['telefono'] ?? '') }}"
|
||||
placeholder="+34 000 000 000">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email"
|
||||
class="form-control"
|
||||
id="email"
|
||||
name="email"
|
||||
value="{{ old('email', $configuracion['email'] ?? '') }}"
|
||||
placeholder="contacto@lashvanshy.com">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="direccion" class="form-label">Dirección</label>
|
||||
<textarea class="form-control"
|
||||
id="direccion"
|
||||
name="direccion"
|
||||
rows="2"
|
||||
placeholder="Tu dirección">{{ old('direccion', $configuracion['direccion'] ?? '') }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="horario" class="form-label">Horario</label>
|
||||
<textarea class="form-control"
|
||||
id="horario"
|
||||
name="horario"
|
||||
rows="3"
|
||||
placeholder="Lunes - Viernes: 10:00 - 20:00 Sábado: 10:00 - 18:00">{{ old('horario', $configuracion['horario'] ?? '') }}</textarea>
|
||||
<small class="text-muted">Usa saltos de línea para cada día</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Redes Sociales -->
|
||||
<div class="col-lg-6">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-share-alt me-2"></i>Redes Sociales
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<label for="facebook" class="form-label">Facebook</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fab fa-facebook"></i></span>
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
id="facebook"
|
||||
name="facebook"
|
||||
value="{{ old('facebook', $configuracion['facebook'] ?? '') }}"
|
||||
placeholder="https://facebook.com/tupagina">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="instagram" class="form-label">Instagram</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fab fa-instagram"></i></span>
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
id="instagram"
|
||||
name="instagram"
|
||||
value="{{ old('instagram', $configuracion['instagram'] ?? '') }}"
|
||||
placeholder="https://instagram.com/tucuenta">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="whatsapp" class="form-label">WhatsApp</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fab fa-whatsapp"></i></span>
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
id="whatsapp"
|
||||
name="whatsapp"
|
||||
value="{{ old('whatsapp', $configuracion['whatsapp'] ?? '') }}"
|
||||
placeholder="https://wa.me/34600000000">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="tiktok" class="form-label">TikTok</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fab fa-tiktok"></i></span>
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
id="tiktok"
|
||||
name="tiktok"
|
||||
value="{{ old('tiktok', $configuracion['tiktok'] ?? '') }}"
|
||||
placeholder="https://tiktok.com/@tuusuario">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="youtube" class="form-label">YouTube</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="fab fa-youtube"></i></span>
|
||||
<input type="url"
|
||||
class="form-control"
|
||||
id="youtube"
|
||||
name="youtube"
|
||||
value="{{ old('youtube', $configuracion['youtube'] ?? '') }}"
|
||||
placeholder="https://youtube.com/@tucanal">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SEO -->
|
||||
<div class="col-12">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-search me-2"></i>Configuración SEO
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="seo_titulo" class="form-label">Título SEO</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="seo_titulo"
|
||||
name="seo_titulo"
|
||||
value="{{ old('seo_titulo', $configuracion['seo_titulo'] ?? 'Lash Vanshy - Extensiones de Pestañas Profesionales') }}"
|
||||
placeholder="Título para motores de búsqueda">
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="seo_descripcion" class="form-label">Descripción SEO</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="seo_descripcion"
|
||||
name="seo_descripcion"
|
||||
value="{{ old('seo_descripcion', $configuracion['seo_descripcion'] ?? '') }}"
|
||||
placeholder="Descripción para motores de búsqueda">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Save Button -->
|
||||
<div class="col-12">
|
||||
<div class="d-flex justify-content-end">
|
||||
<button type="submit" class="btn btn-primary-admin">
|
||||
<i class="fas fa-save me-2"></i>Guardar Configuración
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@endsection
|
||||
168
resources/views/admin/dashboard/index.blade.php
Executable file
168
resources/views/admin/dashboard/index.blade.php
Executable file
@@ -0,0 +1,168 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Dashboard - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Dashboard')
|
||||
|
||||
@section('content')
|
||||
<!-- Stats Cards -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon primary">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3>{{ $stats['mensajes_no_leidos'] }}</h3>
|
||||
<p>Mensajes nuevos</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon success">
|
||||
<i class="fas fa-images"></i>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3>{{ $stats['total_modelos'] }}</h3>
|
||||
<p>Total Modelos</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon warning">
|
||||
<i class="fas fa-spa"></i>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3>{{ $stats['total_productos'] }}</h3>
|
||||
<p>Total Servicios</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon info">
|
||||
<i class="fas fa-star"></i>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<h3>{{ $stats['productos_destacados'] }}</h3>
|
||||
<p>Servicios Destacados</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions & Recent Messages -->
|
||||
<div class="row g-4">
|
||||
<!-- Quick Actions -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-bolt me-2"></i>Accesos Rápidos
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-column gap-3">
|
||||
<a href="{{ route('admin.galeria.create') }}" class="btn btn-primary-admin">
|
||||
<i class="fas fa-plus me-2"></i>Nuevo Modelo
|
||||
</a>
|
||||
<a href="{{ route('admin.productos.create') }}" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-plus me-2"></i>Nuevo Servicio
|
||||
</a>
|
||||
<a href="{{ route('admin.mensajes.index') }}" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-envelope me-2"></i>Ver Mensajes
|
||||
@if($stats['mensajes_no_leidos'] > 0)
|
||||
<span class="badge bg-danger ms-2">{{ $stats['mensajes_no_leidos'] }}</span>
|
||||
@endif
|
||||
</a>
|
||||
<a href="{{ route('home') }}" target="_blank" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-external-link-alt me-2"></i>Ver Sitio Web
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Messages -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card-admin">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<i class="fas fa-envelope-open-text me-2"></i>Mensajes Recientes
|
||||
</span>
|
||||
<a href="{{ route('admin.mensajes.index') }}" class="btn btn-sm btn-primary-admin">
|
||||
Ver Todos
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
@if($mensajes_recientes->isNotEmpty())
|
||||
<div class="table-responsive">
|
||||
<table class="table table-admin mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nombre</th>
|
||||
<th>Asunto</th>
|
||||
<th>Fecha</th>
|
||||
<th>Estado</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($mensajes_recientes as $mensaje)
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ $mensaje->nombre }}</strong>
|
||||
<br>
|
||||
<small class="text-muted">{{ $mensaje->email }}</small>
|
||||
</td>
|
||||
<td>{{ Str::limit($mensaje->mensaje, 40) }}</td>
|
||||
<td>{{ $mensaje->created_at->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
@if($mensaje->leido)
|
||||
<span class="badge-admin bg-success">Leído</span>
|
||||
@else
|
||||
<span class="badge-admin bg-warning">Nuevo</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ route('admin.mensajes.show', $mensaje) }}" class="btn btn-sm btn-primary-admin">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-inbox"></i>
|
||||
<h4>No hay mensajes</h4>
|
||||
<p>Los mensajes de contacto aparecerán aquí</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Site Preview -->
|
||||
<div class="row g-4 mt-2">
|
||||
<div class="col-12">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-eye me-2"></i>Vista Previa del Sitio
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<p class="mb-3">Accede al sitio público y revisa los cambios realizados</p>
|
||||
<a href="{{ route('home') }}" target="_blank" class="btn btn-primary-admin">
|
||||
<i class="fas fa-external-link-alt me-2"></i>Ver Sitio Web
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
223
resources/views/admin/galeria/create.blade.php
Executable file
223
resources/views/admin/galeria/create.blade.php
Executable file
@@ -0,0 +1,223 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Nuevo Modelo - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Nuevo Modelo')
|
||||
|
||||
@section('content')
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.galeria.index') }}">Galería</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Nuevo Modelo</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-plus-circle me-2"></i>Nuevo Modelo
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('admin.galeria.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="titulo" class="form-label">Titulo *</label>
|
||||
<input type="text"
|
||||
class="form-control @error('titulo') is-invalid @endif"
|
||||
id="titulo"
|
||||
name="titulo"
|
||||
value="{{ old('titulo') }}"
|
||||
placeholder="Ej: Extension de pestanas volumen russo"
|
||||
required>
|
||||
@error('titulo')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="tipo" class="form-label">Tipo *</label>
|
||||
<select class="form-select @error('tipo') is-invalid @endif"
|
||||
id="tipo"
|
||||
name="tipo"
|
||||
required
|
||||
onchange="toggleFileInputs()">
|
||||
<option value="">Selecciona el tipo</option>
|
||||
<option value="imagen" {{ old('tipo') === 'imagen' ? 'selected' : '' }}>Imagen</option>
|
||||
<option value="video" {{ old('tipo') === 'video' ? 'selected' : '' }}>Video</option>
|
||||
</select>
|
||||
@error('tipo')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="descripcion" class="form-label">Descripcion</label>
|
||||
<textarea class="form-control @error('descripcion') is-invalid @endif"
|
||||
id="descripcion"
|
||||
name="descripcion"
|
||||
rows="3"
|
||||
placeholder="Descripcion opcional del trabajo">{{ old('descripcion') }}</textarea>
|
||||
@error('descripcion')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="archivo" class="form-label">Archivo *</label>
|
||||
<input type="file"
|
||||
class="form-control @error('archivo') is-invalid @endif"
|
||||
id="archivo"
|
||||
name="archivo">
|
||||
<small class="text-muted" id="file-hint">Selecciona primero el tipo para ver los formatos validos</small>
|
||||
<div class="mt-2">
|
||||
<img id="file-preview" class="image-preview" style="max-width: 300px; display: none;">
|
||||
<video id="video-preview" class="video-preview" style="max-width: 300px; display: none;" controls></video>
|
||||
</div>
|
||||
@error('archivo')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="mb-3" id="thumbnail-container" style="display: none;">
|
||||
<label for="thumbnail" class="form-label">Miniatura (Thumbnail)</label>
|
||||
<input type="file"
|
||||
class="form-control @error('thumbnail') is-invalid @endif"
|
||||
id="thumbnail"
|
||||
name="thumbnail"
|
||||
accept="image/*">
|
||||
<small class="text-muted">Imagen de previsualizacion para el video</small>
|
||||
<div class="mt-2">
|
||||
<img id="thumbnail-preview" class="image-preview" style="max-width: 150px; display: none;">
|
||||
</div>
|
||||
@error('thumbnail')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="orden" class="form-label">Orden</label>
|
||||
<input type="number"
|
||||
class="form-control"
|
||||
id="orden"
|
||||
name="orden"
|
||||
value="{{ old('orden', 0) }}"
|
||||
min="0"
|
||||
placeholder="0">
|
||||
<small class="text-muted">Orden de visualizacion (menor = primero)</small>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Estado</label>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input"
|
||||
type="checkbox"
|
||||
id="activo"
|
||||
name="activo"
|
||||
value="1"
|
||||
{{ old('activo', true) ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="activo">Activo (visible en web)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{{ route('admin.galeria.index') }}" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-arrow-left me-2"></i>Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary-admin">
|
||||
<i class="fas fa-save me-2"></i>Guardar Modelo
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-info-circle me-2"></i>Informacion
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
Los modelos son trabajos realizados que se mostraran en la galeria publica del sitio web.
|
||||
</p>
|
||||
<ul class="text-muted">
|
||||
<li class="mb-2">Puedes subir imagenes o videos de tus trabajos</li>
|
||||
<li class="mb-2">Los videos necesitan una miniatura para mostrarse en la galeria</li>
|
||||
<li>Usa el campo "orden" para controlar el orden de visualizacion</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function toggleFileInputs() {
|
||||
var tipo = document.getElementById('tipo').value;
|
||||
var archivo = document.getElementById('archivo');
|
||||
var fileHint = document.getElementById('file-hint');
|
||||
var thumbnailContainer = document.getElementById('thumbnail-container');
|
||||
|
||||
if (tipo === 'imagen') {
|
||||
archivo.accept = 'image/jpeg,image/png,image/gif,image/webp,image/svg+xml';
|
||||
fileHint.textContent = 'Formatos: JPG, PNG, GIF, WebP, SVG. Maximo: 100MB';
|
||||
thumbnailContainer.style.display = 'none';
|
||||
} else if (tipo === 'video') {
|
||||
archivo.accept = 'video/mp4,video/webm,video/ogg';
|
||||
fileHint.textContent = 'Formatos: MP4, WebM, OGG. Maximo: 100MB';
|
||||
thumbnailContainer.style.display = 'block';
|
||||
} else {
|
||||
archivo.accept = '';
|
||||
fileHint.textContent = 'Selecciona primero el tipo para ver los formatos validos';
|
||||
thumbnailContainer.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('archivo').addEventListener('change', function(e) {
|
||||
var file = e.target.files[0];
|
||||
var imagePreview = document.getElementById('file-preview');
|
||||
var videoPreview = document.getElementById('video-preview');
|
||||
|
||||
if (!file) {
|
||||
imagePreview.style.display = 'none';
|
||||
videoPreview.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.type.startsWith('image/')) {
|
||||
imagePreview.src = URL.createObjectURL(file);
|
||||
imagePreview.style.display = 'block';
|
||||
videoPreview.style.display = 'none';
|
||||
} else if (file.type.startsWith('video/')) {
|
||||
videoPreview.src = URL.createObjectURL(file);
|
||||
videoPreview.style.display = 'block';
|
||||
imagePreview.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById('thumbnail').addEventListener('change', function(e) {
|
||||
var file = e.target.files[0];
|
||||
var preview = document.getElementById('thumbnail-preview');
|
||||
|
||||
if (file && file.type.startsWith('image/')) {
|
||||
preview.src = URL.createObjectURL(file);
|
||||
preview.style.display = 'block';
|
||||
} else {
|
||||
preview.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
toggleFileInputs();
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
307
resources/views/admin/galeria/edit.blade.php
Executable file
307
resources/views/admin/galeria/edit.blade.php
Executable file
@@ -0,0 +1,307 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Editar Modelo - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Editar Modelo')
|
||||
|
||||
@section('content')
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.galeria.index') }}">Galería</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Editar Modelo</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-edit me-2"></i>Editar Modelo
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('admin.galeria.update', $galeria) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="titulo" class="form-label">Título *</label>
|
||||
<input type="text"
|
||||
class="form-control @error('titulo') is-invalid @enderror"
|
||||
id="titulo"
|
||||
name="titulo"
|
||||
value="{{ old('titulo', $galeria->titulo) }}"
|
||||
placeholder="Ej: Extensión de pestañas volumen ruso"
|
||||
required>
|
||||
@error('titulo')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="tipo" class="form-label">Tipo *</label>
|
||||
<select class="form-select @error('tipo') is-invalid @enderror"
|
||||
id="tipo"
|
||||
name="tipo"
|
||||
required
|
||||
onchange="toggleFileInputs()">
|
||||
<option value="">Selecciona el tipo</option>
|
||||
<option value="imagen" {{ $galeria->tipo === 'imagen' ? 'selected' : '' }}>Imagen</option>
|
||||
<option value="video" {{ $galeria->tipo === 'video' ? 'selected' : '' }}>Video</option>
|
||||
</select>
|
||||
@error('tipo')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="descripcion" class="form-label">Descripción</label>
|
||||
<textarea class="form-control @error('descripcion') is-invalid @enderror"
|
||||
id="descripcion"
|
||||
name="descripcion"
|
||||
rows="3"
|
||||
placeholder="Descripción opcional del trabajo">{{ old('descripcion', $galeria->descripcion) }}</textarea>
|
||||
@error('descripcion')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<!-- Current File Info -->
|
||||
@if($galeria->tipo === 'imagen' && $galeria->archivo)
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Imagen Actual</label>
|
||||
<div class="mb-2">
|
||||
<img src="{{ asset('storage/' . $galeria->archivo) }}"
|
||||
alt="{{ $galeria->titulo }}"
|
||||
class="image-preview"
|
||||
style="max-height: 200px;">
|
||||
</div>
|
||||
<small class="text-muted">Deja este campo vacío para mantener la imagen actual</small>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($galeria->tipo === 'video')
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Video Actual</label>
|
||||
@if($galeria->archivo)
|
||||
<div class="mb-2">
|
||||
<video controls style="max-width: 100%; max-height: 200px; border-radius: 10px;">
|
||||
<source src="{{ asset('storage/' . $galeria->archivo) }}" type="video/mp4">
|
||||
Tu navegador no soporta videos.
|
||||
</video>
|
||||
</div>
|
||||
<small class="text-muted">Deja este campo vacío para mantener el video actual</small>
|
||||
@else
|
||||
<p class="text-muted">No hay video cargado</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Miniatura Actual</label>
|
||||
@if($galeria->thumbnail)
|
||||
<div class="mb-2">
|
||||
<img src="{{ asset('storage/' . $galeria->thumbnail) }}"
|
||||
alt="Thumbnail"
|
||||
class="image-preview"
|
||||
style="max-height: 150px;">
|
||||
</div>
|
||||
<small class="text-muted">Deja este campo vacío para mantener la miniatura actual</small>
|
||||
@else
|
||||
<p class="text-muted">No hay miniatura</p>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- New File Input -->
|
||||
@if($galeria->tipo === 'imagen')
|
||||
<div id="imagen-input" class="mb-3">
|
||||
<label for="archivo" class="form-label">Nueva Imagen</label>
|
||||
<input type="file"
|
||||
class="form-control @error('archivo') is-invalid @enderror"
|
||||
id="archivo"
|
||||
name="archivo"
|
||||
accept="image/*"
|
||||
onchange="previewImage(event, 'imagen-preview-edit')">
|
||||
<small class="text-muted">Formats: JPG, PNG, GIF. Max: 5MB</small>
|
||||
<div class="mt-2">
|
||||
<img id="imagen-preview-edit" class="image-preview" style="display: none;">
|
||||
</div>
|
||||
@error('archivo')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
@else
|
||||
<div id="imagen-input" class="mb-3" style="display: none;">
|
||||
<label for="archivo" class="form-label">Nueva Imagen</label>
|
||||
<input type="file"
|
||||
class="form-control @error('archivo') is-invalid @enderror"
|
||||
id="archivo"
|
||||
name="archivo"
|
||||
accept="image/*"
|
||||
onchange="previewImage(event, 'imagen-preview-edit')">
|
||||
<div class="mt-2">
|
||||
<img id="imagen-preview-edit" class="image-preview" style="display: none;">
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($galeria->tipo === 'video')
|
||||
<div id="video-inputs">
|
||||
<div class="mb-3">
|
||||
<label for="archivo_video" class="form-label">Nuevo Video</label>
|
||||
<input type="file"
|
||||
class="form-control @error('archivo') is-invalid @enderror"
|
||||
id="archivo_video"
|
||||
name="archivo"
|
||||
accept="video/*">
|
||||
<small class="text-muted">Formats: MP4, WebM. Max: 50MB</small>
|
||||
@error('archivo')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="thumbnail_video" class="form-label">Nueva Miniatura</label>
|
||||
<input type="file"
|
||||
class="form-control @error('thumbnail') is-invalid @enderror"
|
||||
id="thumbnail_video"
|
||||
name="thumbnail"
|
||||
accept="image/*"
|
||||
onchange="previewImage(event, 'thumbnail-preview-edit')">
|
||||
<div class="mt-2">
|
||||
<img id="thumbnail-preview-edit" class="image-preview" style="display: none;">
|
||||
</div>
|
||||
@error('thumbnail')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<div id="video-inputs" style="display: none;">
|
||||
<div class="mb-3">
|
||||
<label for="archivo_video" class="form-label">Nuevo Video</label>
|
||||
<input type="file"
|
||||
class="form-control @error('archivo') is-invalid @enderror"
|
||||
id="archivo_video"
|
||||
name="archivo"
|
||||
accept="video/*">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="thumbnail_video" class="form-label">Nueva Miniatura</label>
|
||||
<input type="file"
|
||||
class="form-control @error('thumbnail') is-invalid @enderror"
|
||||
id="thumbnail_video"
|
||||
name="thumbnail"
|
||||
accept="image/*"
|
||||
onchange="previewImage(event, 'thumbnail-preview-edit')">
|
||||
<div class="mt-2">
|
||||
<img id="thumbnail-preview-edit" class="image-preview" style="display: none;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="orden" class="form-label">Orden</label>
|
||||
<input type="number"
|
||||
class="form-control"
|
||||
id="orden"
|
||||
name="orden"
|
||||
value="{{ old('orden', $galeria->orden) }}"
|
||||
min="0"
|
||||
placeholder="0">
|
||||
<small class="text-muted">Orden de visualización (menor = primero)</small>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Estado</label>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input"
|
||||
type="checkbox"
|
||||
id="activo"
|
||||
name="activo"
|
||||
value="1"
|
||||
{{ $galeria->activo ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="activo">Activo (visible en web)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{{ route('admin.galeria.index') }}" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-arrow-left me-2"></i>Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary-admin">
|
||||
<i class="fas fa-save me-2"></i>Actualizar Modelo
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-info-circle me-2"></i>Información
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
Los modelos son trabajos realizados que se mostrarán en la galería pública del sitio web.
|
||||
</p>
|
||||
<ul class="text-muted">
|
||||
<li class="mb-2">Puedes subir imágenes o videos de tus trabajos</li>
|
||||
<li class="mb-2">Los videos necesitan una miniatura para mostrarse en la galería</li>
|
||||
<li>Usa el campo "orden" para controlar el orden de visualización</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function toggleFileInputs() {
|
||||
const tipo = document.getElementById('tipo').value;
|
||||
const imagenInput = document.getElementById('imagen-input');
|
||||
const videoInputs = document.getElementById('video-inputs');
|
||||
|
||||
if (tipo === 'imagen') {
|
||||
imagenInput.style.display = 'block';
|
||||
videoInputs.style.display = 'none';
|
||||
} else if (tipo === 'video') {
|
||||
imagenInput.style.display = 'none';
|
||||
videoInputs.style.display = 'block';
|
||||
} else {
|
||||
imagenInput.style.display = 'none';
|
||||
videoInputs.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function previewImage(event, previewId) {
|
||||
const file = event.target.files[0];
|
||||
const preview = document.getElementById(previewId);
|
||||
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
preview.src = e.target.result;
|
||||
preview.style.display = 'block';
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
preview.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
toggleFileInputs();
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
165
resources/views/admin/galeria/index.blade.php
Executable file
165
resources/views/admin/galeria/index.blade.php
Executable file
@@ -0,0 +1,165 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Galería - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Gestión de Galería')
|
||||
|
||||
@section('content')
|
||||
<!-- Header Actions -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="mb-0">Modelos / Galería</h2>
|
||||
<p class="text-muted mb-0">Administra las imágenes y videos de tus trabajos</p>
|
||||
</div>
|
||||
<a href="{{ route('admin.galeria.create') }}" class="btn btn-primary-admin">
|
||||
<i class="fas fa-plus me-2"></i>Nuevo Modelo
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $galerias->total() }}</h3>
|
||||
<p>Total Modelos</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $galerias->where('tipo', 'imagen')->count() }}</h3>
|
||||
<p>Imágenes</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $galerias->where('tipo', 'video')->count() }}</h3>
|
||||
<p>Videos</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gallery Table -->
|
||||
<div class="card-admin">
|
||||
<div class="card-body">
|
||||
@if($galerias->isNotEmpty())
|
||||
<div class="table-responsive">
|
||||
<table class="table table-admin">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Orden</th>
|
||||
<th>Vista Previa</th>
|
||||
<th>Título</th>
|
||||
<th>Tipo</th>
|
||||
<th>Estado</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($galerias as $item)
|
||||
<tr>
|
||||
<td>
|
||||
<span class="badge-admin bg-info">{{ $item->orden }}</span>
|
||||
</td>
|
||||
<td>
|
||||
@if($item->tipo === 'video')
|
||||
@if($item->thumbnail)
|
||||
<img src="{{ asset('storage/' . $item->thumbnail) }}"
|
||||
alt="{{ $item->titulo }}"
|
||||
class="img-thumbnail"
|
||||
style="width: 60px; height: 40px; object-fit: cover;">
|
||||
@else
|
||||
<div class="placeholder-thumb">
|
||||
<i class="fas fa-video"></i>
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
@if($item->archivo)
|
||||
<img src="{{ asset('storage/' . $item->archivo) }}"
|
||||
alt="{{ $item->titulo }}"
|
||||
class="img-thumbnail"
|
||||
style="width: 60px; height: 40px; object-fit: cover;">
|
||||
@else
|
||||
<div class="placeholder-thumb">
|
||||
<i class="fas fa-image"></i>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<strong>{{ $item->titulo }}</strong>
|
||||
@if($item->descripcion)
|
||||
<br><small class="text-muted">{{ Str::limit($item->descripcion, 40) }}</small>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge-admin {{ $item->tipo === 'video' ? 'bg-warning' : 'bg-info' }}">
|
||||
<i class="fas {{ $item->tipo === 'video' ? 'fa-video' : 'fa-image' }} me-1"></i>
|
||||
{{ ucfirst($item->tipo) }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge-admin {{ $item->activo ? 'bg-success' : 'bg-danger' }}">
|
||||
{{ $item->activo ? 'Activo' : 'Inactivo' }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="actions">
|
||||
<a href="{{ route('admin.galeria.edit', $item) }}"
|
||||
class="btn btn-sm btn-primary-admin"
|
||||
title="Editar">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<form action="{{ route('admin.galeria.destroy', $item) }}"
|
||||
method="POST"
|
||||
class="d-inline"
|
||||
onsubmit="return confirm('¿Estás seguro de que deseas eliminar este modelo?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-sm btn-danger-admin" title="Eliminar">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="d-flex justify-content-center">
|
||||
{{ $galerias->links() }}
|
||||
</div>
|
||||
@else
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-images"></i>
|
||||
<h4>No hay modelos</h4>
|
||||
<p>Comienza agregando tu primer modelo a la galería</p>
|
||||
<a href="{{ route('admin.galeria.create') }}" class="btn btn-primary-admin mt-3">
|
||||
<i class="fas fa-plus me-2"></i>Agregar Modelo
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.placeholder-thumb {
|
||||
width: 60px;
|
||||
height: 40px;
|
||||
background: var(--secondary);
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--primary-dark);
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
161
resources/views/admin/layouts/master.blade.php
Executable file
161
resources/views/admin/layouts/master.blade.php
Executable file
@@ -0,0 +1,161 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Lash Vanshy - Panel de Administración">
|
||||
<title>@yield('title', 'Dashboard - Lash Vanshy')</title>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<!-- Bootstrap Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.2/font/bootstrap-icons.min.css">
|
||||
|
||||
<!-- Estilos personalizados -->
|
||||
<link rel="stylesheet" href="{{ asset('css/admin.css') }}">
|
||||
|
||||
@stack('styles')
|
||||
</head>
|
||||
<body>
|
||||
<div class="admin-wrapper">
|
||||
<!-- Sidebar -->
|
||||
<aside class="admin-sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a href="{{ route('admin.dashboard') }}" class="brand">
|
||||
<span class="brand-icon">✨</span>
|
||||
<span class="brand-text">Lash Vanshy</span>
|
||||
</a>
|
||||
<span class="badge bg-danger">Admin</span>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav">
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('admin.dashboard') }}"
|
||||
class="nav-link {{ request()->routeIs('admin.dashboard') ? 'active' : '' }}">
|
||||
<i class="fas fa-home"></i>
|
||||
<span>Dashboard</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('admin.galeria.index') }}"
|
||||
class="nav-link {{ request()->routeIs('admin.galeria.*') ? 'active' : '' }}">
|
||||
<i class="fas fa-images"></i>
|
||||
<span>Galería / Modelos</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('admin.productos.index') }}"
|
||||
class="nav-link {{ request()->routeIs('admin.productos.*') ? 'active' : '' }}">
|
||||
<i class="fas fa-spa"></i>
|
||||
<span>Productos / Servicios</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('admin.mensajes.index') }}"
|
||||
class="nav-link {{ request()->routeIs('admin.mensajes.*') ? 'active' : '' }}">
|
||||
<i class="fas fa-envelope"></i>
|
||||
<span>Mensajes</span>
|
||||
@php
|
||||
$noLeidos = \App\Models\Mensaje::noLeidos()->count();
|
||||
@endphp
|
||||
@if($noLeidos > 0)
|
||||
<span class="badge bg-danger ms-auto">{{ $noLeidos }}</span>
|
||||
@endif
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@if(Auth::guard('admin')->user()->rol === 'super_admin')
|
||||
<li class="nav-item">
|
||||
<a href="{{ route('admin.configuracion.index') }}"
|
||||
class="nav-link {{ request()->routeIs('admin.configuracion.*') ? 'active' : '' }}">
|
||||
<i class="fas fa-cog"></i>
|
||||
<span>Configuración</span>
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-footer">
|
||||
<div class="user-info">
|
||||
<div class="user-avatar">
|
||||
{{ strtoupper(substr(Auth::guard('admin')->user()->name, 0, 1)) }}
|
||||
</div>
|
||||
<div class="user-details">
|
||||
<span class="user-name">{{ Auth::guard('admin')->user()->name }}</span>
|
||||
<span class="user-role">{{ Auth::guard('admin')->user()->rol === 'super_admin' ? 'Super Admin' : 'Admin' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<form action="{{ route('admin.logout') }}" method="POST" class="mt-3">
|
||||
@csrf
|
||||
<button type="submit" class="btn btn-logout w-100">
|
||||
<i class="fas fa-sign-out-alt me-2"></i>Cerrar Sesión
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="admin-main">
|
||||
<!-- Top Header -->
|
||||
<header class="admin-header">
|
||||
<div class="header-left">
|
||||
<button class="btn-toggle-sidebar d-lg-none" onclick="toggleSidebar()">
|
||||
<i class="fas fa-bars"></i>
|
||||
</button>
|
||||
<h1 class="page-title">@yield('page-title', 'Dashboard')</h1>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<span class="current-date">
|
||||
<i class="fas fa-calendar-alt me-2"></i>
|
||||
{{ now()->format('d/m/Y') }}
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="admin-content">
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<i class="fas fa-check-circle me-2"></i>
|
||||
{{ session('success') }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
<i class="fas fa-exclamation-circle me-2"></i>
|
||||
{{ session('error') }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@yield('content')
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
@stack('scripts')
|
||||
|
||||
<script>
|
||||
function toggleSidebar() {
|
||||
document.querySelector('.admin-sidebar').classList.toggle('show');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
138
resources/views/admin/mensajes/index.blade.php
Executable file
138
resources/views/admin/mensajes/index.blade.php
Executable file
@@ -0,0 +1,138 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Mensajes - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Mensajes de Contacto')
|
||||
|
||||
@section('content')
|
||||
<!-- Header Actions -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="mb-0">Mensajes</h2>
|
||||
<p class="text-muted mb-0">Gestiona los mensajes recibidos del formulario de contacto</p>
|
||||
</div>
|
||||
@if($mensajes->where('leido', false)->count() > 0)
|
||||
<form action="{{ route('admin.mensajes.leer-todos') }}" method="POST" class="d-inline">
|
||||
@csrf
|
||||
<button type="submit" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-check-double me-2"></i>Marcar todos como leídos
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $mensajes->total() }}</h3>
|
||||
<p>Total Mensajes</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $mensajes->where('leido', false)->count() }}</h3>
|
||||
<p>Sin Leer</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $mensajes->where('leido', true)->count() }}</h3>
|
||||
<p>Leídos</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Messages Table -->
|
||||
<div class="card-admin">
|
||||
<div class="card-body">
|
||||
@if($mensajes->isNotEmpty())
|
||||
<div class="table-responsive">
|
||||
<table class="table table-admin">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nombre</th>
|
||||
<th>Email</th>
|
||||
<th>Mensaje</th>
|
||||
<th>Fecha</th>
|
||||
<th>Estado</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($mensajes as $mensaje)
|
||||
<tr class="{{ !$mensaje->leido ? 'table-warning' : '' }}">
|
||||
<td>
|
||||
<strong>{{ $mensaje->nombre }}</strong>
|
||||
@if($mensaje->telefono)
|
||||
<br><small class="text-muted">{{ $mensaje->telefono }}</small>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<a href="mailto:{{ $mensaje->email }}">{{ $mensaje->email }}</a>
|
||||
</td>
|
||||
<td>{{ Str::limit($mensaje->mensaje, 50) }}</td>
|
||||
<td>
|
||||
<small>{{ $mensaje->created_at->format('d/m/Y H:i') }}</small>
|
||||
</td>
|
||||
<td>
|
||||
@if($mensaje->leido)
|
||||
<span class="badge-admin bg-success">Leído</span>
|
||||
@else
|
||||
<span class="badge-admin bg-warning">Nuevo</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<div class="actions">
|
||||
<a href="{{ route('admin.mensajes.show', $mensaje) }}"
|
||||
class="btn btn-sm btn-primary-admin"
|
||||
title="Ver mensaje">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
@if(!$mensaje->leido)
|
||||
<form action="{{ route('admin.mensajes.leido', $mensaje) }}" method="POST" class="d-inline">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<button type="submit" class="btn btn-sm btn-secondary-admin" title="Marcar como leído">
|
||||
<i class="fas fa-check"></i>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
<form action="{{ route('admin.mensajes.destroy', $mensaje) }}"
|
||||
method="POST"
|
||||
class="d-inline"
|
||||
onsubmit="return confirm('¿Estás seguro de que deseas eliminar este mensaje?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-sm btn-danger-admin" title="Eliminar">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="d-flex justify-content-center">
|
||||
{{ $mensajes->links() }}
|
||||
</div>
|
||||
@else
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-envelope-open"></i>
|
||||
<h4>No hay mensajes</h4>
|
||||
<p>Los mensajes del formulario de contacto aparecerán aquí</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
119
resources/views/admin/mensajes/show.blade.php
Executable file
119
resources/views/admin/mensajes/show.blade.php
Executable file
@@ -0,0 +1,119 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Ver Mensaje - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Detalle del Mensaje')
|
||||
|
||||
@section('content')
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.mensajes.index') }}">Mensajes</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Ver Mensaje</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card-admin">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<i class="fas fa-envelope me-2"></i>Mensaje de {{ $mensaje->nombre }}
|
||||
</span>
|
||||
<span class="badge-admin {{ $mensaje->leido ? 'bg-success' : 'bg-warning' }}">
|
||||
{{ $mensaje->leido ? 'Leído' : 'Sin leer' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Message Info -->
|
||||
<div class="message-meta mb-4">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="text-muted small">Nombre</label>
|
||||
<p class="mb-0 fw-bold">{{ $mensaje->nombre }}</p>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="text-muted small">Email</label>
|
||||
<p class="mb-0">
|
||||
<a href="mailto:{{ $mensaje->email }}">{{ $mensaje->email }}</a>
|
||||
</p>
|
||||
</div>
|
||||
@if($mensaje->telefono)
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="text-muted small">Teléfono</label>
|
||||
<p class="mb-0">
|
||||
<a href="tel:{{ $mensaje->telefono }}">{{ $mensaje->telefono }}</a>
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="text-muted small">Fecha</label>
|
||||
<p class="mb-0">{{ $mensaje->created_at->format('d/m/Y H:i') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Message Content -->
|
||||
<div class="message-content">
|
||||
<label class="text-muted small d-block mb-2">Mensaje</label>
|
||||
<div class="p-3 bg-light rounded">
|
||||
{{ $mensaje->mensaje }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{{ route('admin.mensajes.index') }}" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-arrow-left me-2"></i>Volver
|
||||
</a>
|
||||
<div class="d-flex gap-2">
|
||||
@if(!$mensaje->leido)
|
||||
<form action="{{ route('admin.mensajes.leido', $mensaje) }}" method="POST">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<button type="submit" class="btn btn-primary-admin">
|
||||
<i class="fas fa-check me-2"></i>Marcar como leído
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
<form action="{{ route('admin.mensajes.destroy', $mensaje) }}" method="POST" onsubmit="return confirm('¿Estás seguro de que deseas eliminar este mensaje?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-danger-admin">
|
||||
<i class="fas fa-trash me-2"></i>Eliminar
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-info-circle me-2"></i>Información
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
Este mensaje fue enviado a través del formulario de contacto en el sitio web.
|
||||
</p>
|
||||
<ul class="text-muted">
|
||||
<li class="mb-2">Puedes responder directamente al email del cliente</li>
|
||||
<li class="mb-2">Los mensajes no leídos aparecen resaltados</li>
|
||||
<li>Puedes eliminar mensajes que ya no necesites</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
|
||||
<a href="mailto:{{ $mensaje->email }}?subject=Re: Tu mensaje desde Lash Vanshy" class="btn btn-primary-admin w-100">
|
||||
<i class="fas fa-reply me-2"></i>Responder por Email
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
197
resources/views/admin/productos/create.blade.php
Executable file
197
resources/views/admin/productos/create.blade.php
Executable file
@@ -0,0 +1,197 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Nuevo Servicio - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Nuevo Servicio')
|
||||
|
||||
@section('content')
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.productos.index') }}">Servicios</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Nuevo Servicio</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-plus-circle me-2"></i>Nuevo Servicio
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('admin.productos.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="nombre" class="form-label">Nombre del Servicio *</label>
|
||||
<input type="text"
|
||||
class="form-control @error('nombre') is-invalid @enderror"
|
||||
id="nombre"
|
||||
name="nombre"
|
||||
value="{{ old('nombre') }}"
|
||||
placeholder="Ej: Extensión de Pestañas Clásicas"
|
||||
required>
|
||||
@error('nombre')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="descripcion" class="form-label">Descripción</label>
|
||||
<textarea class="form-control @error('descripcion') is-invalid @enderror"
|
||||
id="descripcion"
|
||||
name="descripcion"
|
||||
rows="4"
|
||||
placeholder="Describe el servicio, duración, técnicas utilizadas...">{{ old('descripcion') }}</textarea>
|
||||
@error('descripcion')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="precio" class="form-label">Precio ({{ config('currency.symbol') }}) *</label>
|
||||
<input type="number"
|
||||
class="form-control @error('precio') is-invalid @enderror"
|
||||
id="precio"
|
||||
name="precio"
|
||||
value="{{ old('precio') }}"
|
||||
step="0.01"
|
||||
min="0"
|
||||
placeholder="0.00"
|
||||
required>
|
||||
@error('precio')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="categoria" class="form-label">Categoría *</label>
|
||||
<input type="text"
|
||||
class="form-control @error('categoria') is-invalid @enderror"
|
||||
id="categoria"
|
||||
name="categoria"
|
||||
value="{{ old('categoria') }}"
|
||||
placeholder="Ej: Extensiones, Lifting, Otros"
|
||||
list="categorias-list"
|
||||
required>
|
||||
<datalist id="categorias-list">
|
||||
<option value="Extensiones">
|
||||
<option value="Lifting">
|
||||
<option value="Coloración">
|
||||
<option value="Tratamientos">
|
||||
<option value="Otros">
|
||||
</datalist>
|
||||
@error('categoria')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="imagen" class="form-label">Imagen del Servicio</label>
|
||||
<input type="file"
|
||||
class="form-control @error('imagen') is-invalid @enderror"
|
||||
id="imagen"
|
||||
name="imagen"
|
||||
accept="image/*"
|
||||
onchange="previewImage(event, 'imagen-preview')">
|
||||
<small class="text-muted">Formats: JPG, PNG, GIF. Max: 5MB. Opcional</small>
|
||||
<div class="mt-2">
|
||||
<img id="imagen-preview" class="image-preview" style="display: none;">
|
||||
</div>
|
||||
@error('imagen')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="orden" class="form-label">Orden</label>
|
||||
<input type="number"
|
||||
class="form-control"
|
||||
id="orden"
|
||||
name="orden"
|
||||
value="{{ old('orden', 0) }}"
|
||||
min="0"
|
||||
placeholder="0">
|
||||
<small class="text-muted">Orden de visualización (menor = primero)</small>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Opciones</label>
|
||||
<div class="form-check form-switch mb-2">
|
||||
<input class="form-check-input"
|
||||
type="checkbox"
|
||||
id="destacado"
|
||||
name="destacado"
|
||||
value="1"
|
||||
{{ old('destacado') ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="destacado">⭐ Servicio destacado</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input"
|
||||
type="checkbox"
|
||||
id="activo"
|
||||
name="activo"
|
||||
value="1"
|
||||
{{ old('activo', true) ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="activo">Activo (visible en web)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{{ route('admin.productos.index') }}" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-arrow-left me-2"></i>Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary-admin">
|
||||
<i class="fas fa-save me-2"></i>Guardar Servicio
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-info-circle me-2"></i>Información
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
Los servicios son los tratamientos que ofreces en tu negocio. Cada servicio puede tener precio, descripción e imagen.
|
||||
</p>
|
||||
<ul class="text-muted">
|
||||
<li class="mb-2">Los servicios destacados aparecen en la página principal</li>
|
||||
<li class="mb-2">Las categorías te permiten filtrar los servicios</li>
|
||||
<li>Usa el campo "orden" para controlar el orden de visualización</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function previewImage(event, previewId) {
|
||||
const file = event.target.files[0];
|
||||
const preview = document.getElementById(previewId);
|
||||
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
preview.src = e.target.result;
|
||||
preview.style.display = 'block';
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
preview.style.display = 'none';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
214
resources/views/admin/productos/edit.blade.php
Executable file
214
resources/views/admin/productos/edit.blade.php
Executable file
@@ -0,0 +1,214 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Editar Servicio - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Editar Servicio')
|
||||
|
||||
@section('content')
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.productos.index') }}">Servicios</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Editar Servicio</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-edit me-2"></i>Editar Servicio
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('admin.productos.update', $producto) }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="nombre" class="form-label">Nombre del Servicio *</label>
|
||||
<input type="text"
|
||||
class="form-control @error('nombre') is-invalid @enderror"
|
||||
id="nombre"
|
||||
name="nombre"
|
||||
value="{{ old('nombre', $producto->nombre) }}"
|
||||
placeholder="Ej: Extensión de Pestañas Clásicas"
|
||||
required>
|
||||
@error('nombre')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="descripcion" class="form-label">Descripción</label>
|
||||
<textarea class="form-control @error('descripcion') is-invalid @enderror"
|
||||
id="descripcion"
|
||||
name="descripcion"
|
||||
rows="4"
|
||||
placeholder="Describe el servicio, duración, técnicas utilizadas...">{{ old('descripcion', $producto->descripcion) }}</textarea>
|
||||
@error('descripcion')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="precio" class="form-label">Precio ({{ config('currency.symbol') }}) *</label>
|
||||
<input type="number"
|
||||
class="form-control @error('precio') is-invalid @enderror"
|
||||
id="precio"
|
||||
name="precio"
|
||||
value="{{ old('precio', $producto->precio) }}"
|
||||
step="0.01"
|
||||
min="0"
|
||||
placeholder="0.00"
|
||||
required>
|
||||
@error('precio')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="categoria" class="form-label">Categoría *</label>
|
||||
<input type="text"
|
||||
class="form-control @error('categoria') is-invalid @enderror"
|
||||
id="categoria"
|
||||
name="categoria"
|
||||
value="{{ old('categoria', $producto->categoria) }}"
|
||||
placeholder="Ej: Extensiones, Lifting, Otros"
|
||||
list="categorias-list"
|
||||
required>
|
||||
<datalist id="categorias-list">
|
||||
<option value="Extensiones">
|
||||
<option value="Lifting">
|
||||
<option value="Coloración">
|
||||
<option value="Tratamientos">
|
||||
<option value="Otros">
|
||||
</datalist>
|
||||
@error('categoria')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Image -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Imagen Actual</label>
|
||||
@if($producto->imagen)
|
||||
<div class="mb-2">
|
||||
<img src="{{ asset('storage/' . $producto->imagen) }}"
|
||||
alt="{{ $producto->nombre }}"
|
||||
class="image-preview"
|
||||
style="max-height: 200px;">
|
||||
</div>
|
||||
<small class="text-muted">Deja este campo vacío para mantener la imagen actual</small>
|
||||
@else
|
||||
<p class="text-muted">No hay imagen cargada</p>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="imagen" class="form-label">Nueva Imagen</label>
|
||||
<input type="file"
|
||||
class="form-control @error('imagen') is-invalid @enderror"
|
||||
id="imagen"
|
||||
name="imagen"
|
||||
accept="image/*"
|
||||
onchange="previewImage(event, 'imagen-preview-edit')">
|
||||
<small class="text-muted">Formats: JPG, PNG, GIF. Max: 5MB</small>
|
||||
<div class="mt-2">
|
||||
<img id="imagen-preview-edit" class="image-preview" style="display: none;">
|
||||
</div>
|
||||
@error('imagen')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="orden" class="form-label">Orden</label>
|
||||
<input type="number"
|
||||
class="form-control"
|
||||
id="orden"
|
||||
name="orden"
|
||||
value="{{ old('orden', $producto->orden) }}"
|
||||
min="0"
|
||||
placeholder="0">
|
||||
<small class="text-muted">Orden de visualización (menor = primero)</small>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">Opciones</label>
|
||||
<div class="form-check form-switch mb-2">
|
||||
<input class="form-check-input"
|
||||
type="checkbox"
|
||||
id="destacado"
|
||||
name="destacado"
|
||||
value="1"
|
||||
{{ $producto->destacado ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="destacado">⭐ Servicio destacado</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input"
|
||||
type="checkbox"
|
||||
id="activo"
|
||||
name="activo"
|
||||
value="1"
|
||||
{{ $producto->activo ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="activo">Activo (visible en web)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{{ route('admin.productos.index') }}" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-arrow-left me-2"></i>Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary-admin">
|
||||
<i class="fas fa-save me-2"></i>Actualizar Servicio
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-info-circle me-2"></i>Información
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
Los servicios son los tratamientos que ofreces en tu negocio. Cada servicio puede tener precio, descripción e imagen.
|
||||
</p>
|
||||
<ul class="text-muted">
|
||||
<li class="mb-2">Los servicios destacados aparecen en la página principal</li>
|
||||
<li class="mb-2">Las categorías te permiten filtrar los servicios</li>
|
||||
<li>Usa el campo "orden" para controlar el orden de visualización</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function previewImage(event, previewId) {
|
||||
const file = event.target.files[0];
|
||||
const preview = document.getElementById(previewId);
|
||||
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
preview.src = e.target.result;
|
||||
preview.style.display = 'block';
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
} else {
|
||||
preview.style.display = 'none';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
158
resources/views/admin/productos/index.blade.php
Executable file
158
resources/views/admin/productos/index.blade.php
Executable file
@@ -0,0 +1,158 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Servicios - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Gestión de Servicios')
|
||||
|
||||
@section('content')
|
||||
<!-- Header Actions -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="mb-0">Servicios / Productos</h2>
|
||||
<p class="text-muted mb-0">Administra los servicios que ofreces</p>
|
||||
</div>
|
||||
<a href="{{ route('admin.productos.create') }}" class="btn btn-primary-admin">
|
||||
<i class="fas fa-plus me-2"></i>Nuevo Servicio
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $productos->total() }}</h3>
|
||||
<p>Total Servicios</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $productos->where('destacado', true)->count() }}</h3>
|
||||
<p>Destacados</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $productos->where('activo', true)->count() }}</h3>
|
||||
<p>Activos</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Products Table -->
|
||||
<div class="card-admin">
|
||||
<div class="card-body">
|
||||
@if($productos->isNotEmpty())
|
||||
<div class="table-responsive">
|
||||
<table class="table table-admin">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Orden</th>
|
||||
<th>Imagen</th>
|
||||
<th>Nombre</th>
|
||||
<th>Categoría</th>
|
||||
<th>Precio</th>
|
||||
<th>Estado</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($productos as $producto)
|
||||
<tr>
|
||||
<td>
|
||||
<span class="badge-admin bg-info">{{ $producto->orden }}</span>
|
||||
</td>
|
||||
<td>
|
||||
@if($producto->imagen)
|
||||
<img src="{{ asset('storage/' . $producto->imagen) }}"
|
||||
alt="{{ $producto->nombre }}"
|
||||
class="img-thumbnail"
|
||||
style="width: 60px; height: 40px; object-fit: cover;">
|
||||
@else
|
||||
<div class="placeholder-thumb">
|
||||
<i class="fas fa-spa"></i>
|
||||
</div>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<strong>{{ $producto->nombre }}</strong>
|
||||
@if($producto->destacado)
|
||||
<br><span class="badge bg-warning badge-sm">⭐ Destacado</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge-admin bg-primary">{{ $producto->categoria }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<strong>{{ $producto->precio_formateado }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge-admin {{ $producto->activo ? 'bg-success' : 'bg-danger' }}">
|
||||
{{ $producto->activo ? 'Activo' : 'Inactivo' }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="actions">
|
||||
<a href="{{ route('admin.productos.edit', $producto) }}"
|
||||
class="btn btn-sm btn-primary-admin"
|
||||
title="Editar">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<form action="{{ route('admin.productos.destroy', $producto) }}"
|
||||
method="POST"
|
||||
class="d-inline"
|
||||
onsubmit="return confirm('¿Estás seguro de que deseas eliminar este servicio?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-sm btn-danger-admin" title="Eliminar">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="d-flex justify-content-center">
|
||||
{{ $productos->links() }}
|
||||
</div>
|
||||
@else
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-spa"></i>
|
||||
<h4>No hay servicios</h4>
|
||||
<p>Comienza agregando tu primer servicio</p>
|
||||
<a href="{{ route('admin.productos.create') }}" class="btn btn-primary-admin mt-3">
|
||||
<i class="fas fa-plus me-2"></i>Agregar Servicio
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.placeholder-thumb {
|
||||
width: 60px;
|
||||
height: 40px;
|
||||
background: var(--secondary);
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--primary-dark);
|
||||
}
|
||||
|
||||
.badge-sm {
|
||||
font-size: 0.7rem;
|
||||
padding: 0.2rem 0.5rem;
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
125
resources/views/admin/usuarios/create.blade.php
Executable file
125
resources/views/admin/usuarios/create.blade.php
Executable file
@@ -0,0 +1,125 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Nuevo Usuario - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Nuevo Usuario')
|
||||
|
||||
@section('content')
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.users.index') }}">Usuarios</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Nuevo Usuario</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-user-plus me-2"></i>Nuevo Usuario
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('admin.users.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Nombre *</label>
|
||||
<input type="text"
|
||||
class="form-control @error('name') is-invalid @enderror"
|
||||
id="name"
|
||||
name="name"
|
||||
value="{{ old('name') }}"
|
||||
placeholder="Nombre completo"
|
||||
required>
|
||||
@error('name')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email *</label>
|
||||
<input type="email"
|
||||
class="form-control @error('email') is-invalid @enderror"
|
||||
id="email"
|
||||
name="email"
|
||||
value="{{ old('email') }}"
|
||||
placeholder="email@ejemplo.com"
|
||||
required>
|
||||
@error('email')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Contraseña *</label>
|
||||
<input type="password"
|
||||
class="form-control @error('password') is-invalid @enderror"
|
||||
id="password"
|
||||
name="password"
|
||||
placeholder="••••••••"
|
||||
required>
|
||||
<small class="text-muted">Mínimo 8 caracteres</small>
|
||||
@error('password')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password_confirmation" class="form-label">Confirmar Contraseña *</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
id="password_confirmation"
|
||||
name="password_confirmation"
|
||||
placeholder="••••••••"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="rol" class="form-label">Rol *</label>
|
||||
<select class="form-select @error('rol') is-invalid @enderror"
|
||||
id="rol"
|
||||
name="rol"
|
||||
required>
|
||||
<option value="">Selecciona el rol</option>
|
||||
<option value="admin" {{ old('rol') === 'admin' ? 'selected' : '' }}>Admin</option>
|
||||
<option value="super_admin" {{ old('rol') === 'super_admin' ? 'selected' : '' }}>Super Admin</option>
|
||||
</select>
|
||||
<small class="text-muted">El Super Admin tiene acceso completo a todas las funciones</small>
|
||||
@error('rol')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{{ route('admin.users.index') }}" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-arrow-left me-2"></i>Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary-admin">
|
||||
<i class="fas fa-save me-2"></i>Crear Usuario
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-info-circle me-2"></i>Información
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
Los usuarios administradores pueden acceder al panel de gestión del sitio web.
|
||||
</p>
|
||||
<ul class="text-muted">
|
||||
<li class="mb-2"><strong>Admin:</strong> Puede gestionar galería, productos y mensajes</li>
|
||||
<li class="mb-2"><strong>Super Admin:</strong> Acceso completo incluyendo gestión de usuarios</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
126
resources/views/admin/usuarios/edit.blade.php
Executable file
126
resources/views/admin/usuarios/edit.blade.php
Executable file
@@ -0,0 +1,126 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Editar Usuario - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Editar Usuario')
|
||||
|
||||
@section('content')
|
||||
<!-- Breadcrumb -->
|
||||
<nav aria-label="breadcrumb" class="mb-4">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.dashboard') }}">Dashboard</a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin.users.index') }}">Usuarios</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Editar Usuario</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-user-edit me-2"></i>Editar Usuario
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ route('admin.users.update', $usuario) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Nombre *</label>
|
||||
<input type="text"
|
||||
class="form-control @error('name') is-invalid @enderror"
|
||||
id="name"
|
||||
name="name"
|
||||
value="{{ old('name', $usuario->name) }}"
|
||||
placeholder="Nombre completo"
|
||||
required>
|
||||
@error('name')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email *</label>
|
||||
<input type="email"
|
||||
class="form-control @error('email') is-invalid @enderror"
|
||||
id="email"
|
||||
name="email"
|
||||
value="{{ old('email', $usuario->email) }}"
|
||||
placeholder="email@ejemplo.com"
|
||||
required>
|
||||
@error('email')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Nueva Contraseña</label>
|
||||
<input type="password"
|
||||
class="form-control @error('password') is-invalid @enderror"
|
||||
id="password"
|
||||
name="password"
|
||||
placeholder="••••••••">
|
||||
<small class="text-muted">Deja vacío para mantener la contraseña actual. Mínimo 8 caracteres</small>
|
||||
@error('password')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password_confirmation" class="form-label">Confirmar Contraseña</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
id="password_confirmation"
|
||||
name="password_confirmation"
|
||||
placeholder="••••••••">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="rol" class="form-label">Rol *</label>
|
||||
<select class="form-select @error('rol') is-invalid @enderror"
|
||||
id="rol"
|
||||
name="rol"
|
||||
required
|
||||
{{ $usuario->id === Auth::guard('admin')->user()->id ? 'disabled' : '' }}>
|
||||
<option value="admin" {{ $usuario->rol === 'admin' ? 'selected' : '' }}>Admin</option>
|
||||
<option value="super_admin" {{ $usuario->rol === 'super_admin' ? 'selected' : '' }}>Super Admin</option>
|
||||
</select>
|
||||
@if($usuario->id === Auth::guard('admin')->user()->id)
|
||||
<small class="text-warning">No puedes cambiar tu propio rol</small>
|
||||
@endif
|
||||
@error('rol')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between mt-4">
|
||||
<a href="{{ route('admin.users.index') }}" class="btn btn-secondary-admin">
|
||||
<i class="fas fa-arrow-left me-2"></i>Cancelar
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary-admin">
|
||||
<i class="fas fa-save me-2"></i>Actualizar Usuario
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-admin">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-info-circle me-2"></i>Información
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">
|
||||
Los usuarios administradores pueden acceder al panel de gestión del sitio web.
|
||||
</p>
|
||||
<ul class="text-muted">
|
||||
<li class="mb-2"><strong>Admin:</strong> Puede gestionar galería, productos y mensajes</li>
|
||||
<li class="mb-2"><strong>Super Admin:</strong> Acceso completo incluyendo gestión de usuarios</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
138
resources/views/admin/usuarios/index.blade.php
Executable file
138
resources/views/admin/usuarios/index.blade.php
Executable file
@@ -0,0 +1,138 @@
|
||||
@extends('admin.layouts.master')
|
||||
|
||||
@section('title', 'Usuarios Admin - Lash Vanshy')
|
||||
|
||||
@section('page-title', 'Gestión de Usuarios')
|
||||
|
||||
@section('content')
|
||||
<!-- Header Actions -->
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="mb-0">Usuarios Administradores</h2>
|
||||
<p class="text-muted mb-0">Administra los usuarios del panel</p>
|
||||
</div>
|
||||
<a href="{{ route('admin.users.create') }}" class="btn btn-primary-admin">
|
||||
<i class="fas fa-plus me-2"></i>Nuevo Usuario
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $usuarios->total() }}</h3>
|
||||
<p>Total Usuarios</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $usuarios->where('rol', 'super_admin')->count() }}</h3>
|
||||
<p>Super Admin</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="stat-card py-3">
|
||||
<div class="stat-info">
|
||||
<h3>{{ $usuarios->where('rol', 'admin')->count() }}</h3>
|
||||
<p>Admin</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Users Table -->
|
||||
<div class="card-admin">
|
||||
<div class="card-body">
|
||||
@if($usuarios->isNotEmpty())
|
||||
<div class="table-responsive">
|
||||
<table class="table table-admin">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Usuario</th>
|
||||
<th>Email</th>
|
||||
<th>Rol</th>
|
||||
<th>Fecha Alta</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($usuarios as $usuario)
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="user-avatar-sm">
|
||||
{{ strtoupper(substr($usuario->name, 0, 1)) }}
|
||||
</div>
|
||||
<strong>{{ $usuario->name }}</strong>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ $usuario->email }}</td>
|
||||
<td>
|
||||
<span class="badge-admin {{ $usuario->rol === 'super_admin' ? 'bg-danger' : 'bg-primary' }}">
|
||||
{{ $usuario->rol === 'super_admin' ? 'Super Admin' : 'Admin' }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ $usuario->created_at->format('d/m/Y') }}</td>
|
||||
<td>
|
||||
<div class="actions">
|
||||
<a href="{{ route('admin.users.edit', $usuario) }}"
|
||||
class="btn btn-sm btn-primary-admin"
|
||||
title="Editar">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
@if($usuario->id !== Auth::guard('admin')->user()->id)
|
||||
<form action="{{ route('admin.users.destroy', $usuario) }}"
|
||||
method="POST"
|
||||
class="d-inline"
|
||||
onsubmit="return confirm('¿Estás seguro de que deseas eliminar este usuario?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-sm btn-danger-admin" title="Eliminar">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="d-flex justify-content-center">
|
||||
{{ $usuarios->links() }}
|
||||
</div>
|
||||
@else
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-users"></i>
|
||||
<h4>No hay usuarios</h4>
|
||||
<p>Comienza agregando tu primer usuario administrador</p>
|
||||
<a href="{{ route('admin.users.create') }}" class="btn btn-primary-admin mt-3">
|
||||
<i class="fas fa-plus me-2"></i>Agregar Usuario
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.user-avatar-sm {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
281
resources/views/frontend/contacto/index.blade.php
Executable file
281
resources/views/frontend/contacto/index.blade.php
Executable file
@@ -0,0 +1,281 @@
|
||||
@extends('frontend.layouts.main')
|
||||
|
||||
@section('title', 'Contacto - Lash Vanshy')
|
||||
|
||||
@section('content')
|
||||
<!-- Page Header -->
|
||||
<section class="page-header">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1>Contáctanos</h1>
|
||||
<p>Estamos aquí para responder a todas tus preguntas</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Contact Section -->
|
||||
<section class="contact-section section-padding">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<!-- Contact Info -->
|
||||
<div class="col-lg-5 mb-4 mb-lg-0">
|
||||
<div class="contact-info h-100">
|
||||
<h3 class="mb-4">Información de Contacto</h3>
|
||||
|
||||
<div class="contact-info-item">
|
||||
<i class="fas fa-phone-alt"></i>
|
||||
<div>
|
||||
<strong>Teléfono</strong>
|
||||
@if(!empty($configuracion['telefono']))
|
||||
<a href="tel:{{ $configuracion['telefono'] }}">{{ $configuracion['telefono'] }}</a>
|
||||
@else
|
||||
<span>+34 000 000 000</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-info-item">
|
||||
<i class="fas fa-envelope"></i>
|
||||
<div>
|
||||
<strong>Email</strong>
|
||||
@if(!empty($configuracion['email']))
|
||||
<a href="mailto:{{ $configuracion['email'] }}">{{ $configuracion['email'] }}</a>
|
||||
@else
|
||||
<span>contacto@lashvanshy.com</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-info-item">
|
||||
<i class="fas fa-map-marker-alt"></i>
|
||||
<div>
|
||||
<strong>Dirección</strong>
|
||||
@if(!empty($configuracion['direccion']))
|
||||
<span>{{ $configuracion['direccion'] }}</span>
|
||||
@else
|
||||
<span>Tu dirección aquí</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-info-item">
|
||||
<i class="fas fa-clock"></i>
|
||||
<div>
|
||||
<strong>Horario</strong>
|
||||
@if(!empty($configuracion['horario']))
|
||||
<span>{!! nl2br(e($configuracion['horario'])) !!}</span>
|
||||
@else
|
||||
<span>Lunes - Viernes: 10:00 - 20:00</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<h5 class="mb-3">Síguenos en redes sociales</h5>
|
||||
<div class="social-links">
|
||||
@if(!empty($configuracion['facebook']))
|
||||
<a href="{{ $configuracion['facebook'] }}" target="_blank" class="social-link" title="Facebook">
|
||||
<i class="fab fa-facebook-f"></i>
|
||||
</a>
|
||||
@endif
|
||||
@if(!empty($configuracion['instagram']))
|
||||
<a href="{{ $configuracion['instagram'] }}" target="_blank" class="social-link" title="Instagram">
|
||||
<i class="fab fa-instagram"></i>
|
||||
</a>
|
||||
@endif
|
||||
@if(!empty($configuracion['whatsapp']))
|
||||
<a href="{{ $configuracion['whatsapp'] }}" target="_blank" class="social-link" title="WhatsApp">
|
||||
<i class="fab fa-whatsapp"></i>
|
||||
</a>
|
||||
@endif
|
||||
@if(!empty($configuracion['tiktok']))
|
||||
<a href="{{ $configuracion['tiktok'] }}" target="_blank" class="social-link" title="TikTok">
|
||||
<i class="fab fa-tiktok"></i>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contact Form -->
|
||||
<div class="col-lg-7">
|
||||
<div class="contact-card">
|
||||
<div class="contact-form">
|
||||
<h3 class="mb-4">Envíanos un Mensaje</h3>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">
|
||||
<i class="fas fa-check-circle me-2"></i>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
<i class="fas fa-exclamation-circle me-2"></i>
|
||||
<ul class="mb-0">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('contacto.send') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="nombre" class="form-label">Nombre completo *</label>
|
||||
<input type="text"
|
||||
class="form-control @error('nombre') is-invalid @enderror"
|
||||
id="nombre"
|
||||
name="nombre"
|
||||
value="{{ old('nombre') }}"
|
||||
placeholder="Tu nombre"
|
||||
required>
|
||||
@error('nombre')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="email" class="form-label">Email *</label>
|
||||
<input type="email"
|
||||
class="form-control @error('email') is-invalid @enderror"
|
||||
id="email"
|
||||
name="email"
|
||||
value="{{ old('email') }}"
|
||||
placeholder="tu@email.com"
|
||||
required>
|
||||
@error('email')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="telefono" class="form-label">Teléfono (opcional)</label>
|
||||
<input type="tel"
|
||||
class="form-control @error('telefono') is-invalid @enderror"
|
||||
id="telefono"
|
||||
name="telefono"
|
||||
value="{{ old('telefono') }}"
|
||||
placeholder="Tu número de teléfono">
|
||||
@error('telefono')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="mensaje" class="form-label">Mensaje *</label>
|
||||
<textarea class="form-control @error('mensaje') is-invalid @enderror"
|
||||
id="mensaje"
|
||||
name="mensaje"
|
||||
rows="5"
|
||||
placeholder="¿En qué podemos ayudarte?"
|
||||
required>{{ old('mensaje') }}</textarea>
|
||||
@error('mensaje')
|
||||
<div class="invalid-feedback">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary-custom">
|
||||
<i class="fas fa-paper-plane me-2"></i>Enviar Mensaje
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.page-header {
|
||||
background: linear-gradient(135deg, var(--secondary) 0%, var(--primary) 100%);
|
||||
padding: 60px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 2.5rem;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
color: var(--text-light);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
background: linear-gradient(135deg, var(--secondary) 0%, var(--primary) 100%);
|
||||
padding: 3rem;
|
||||
border-radius: 20px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.contact-info h3 {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.contact-info-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.contact-info-item i {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
background: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--primary-dark);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.contact-info-item strong {
|
||||
display: block;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.contact-info-item a,
|
||||
.contact-info-item span {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.contact-form .form-label {
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.contact-form .form-control {
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 0.75rem 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.contact-form .form-control:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 4px rgba(248, 180, 196, 0.2);
|
||||
}
|
||||
|
||||
.contact-form .form-control.is-invalid {
|
||||
border-color: var(--danger);
|
||||
}
|
||||
|
||||
.contact-form .invalid-feedback {
|
||||
color: var(--danger);
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
167
resources/views/frontend/galeria/index.blade.php
Executable file
167
resources/views/frontend/galeria/index.blade.php
Executable file
@@ -0,0 +1,167 @@
|
||||
@extends('frontend.layouts.main')
|
||||
|
||||
@section('title', 'Galería - Lash Vanshy')
|
||||
|
||||
@section('content')
|
||||
<!-- Page Header -->
|
||||
<section class="page-header">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1>Nuestra Galería</h1>
|
||||
<p>Explora nuestros trabajos realizados y transforma tu mirada</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Gallery Section -->
|
||||
<section class="gallery-section section-padding">
|
||||
<div class="container">
|
||||
<!-- Filters -->
|
||||
<div class="gallery-filters">
|
||||
<button class="filter-btn {{ $tipo === 'todos' ? 'active' : '' }}"
|
||||
onclick="filterGallery('todos')">
|
||||
<i class="fas fa-th-large me-2"></i>Todos ({{ $stats['total'] }})
|
||||
</button>
|
||||
<button class="filter-btn {{ $tipo === 'imagen' ? 'active' : '' }}"
|
||||
onclick="filterGallery('imagen')">
|
||||
<i class="fas fa-image me-2"></i>Imágenes ({{ $stats['imagenes'] }})
|
||||
</button>
|
||||
<button class="filter-btn {{ $tipo === 'video' ? 'active' : '' }}"
|
||||
onclick="filterGallery('video')">
|
||||
<i class="fas fa-video me-2"></i>Videos ({{ $stats['videos'] }})
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Gallery Grid -->
|
||||
@if($galeria->isNotEmpty())
|
||||
<div class="gallery-grid">
|
||||
@foreach($galeria as $item)
|
||||
<div class="gallery-item" data-type="{{ $item->tipo }}">
|
||||
@if($item->tipo === 'video')
|
||||
@if($item->thumbnail)
|
||||
<img src="{{ asset('storage/' . $item->thumbnail) }}"
|
||||
alt="{{ $item->titulo }}"
|
||||
loading="lazy">
|
||||
@elseif($item->archivo)
|
||||
<video poster="{{ asset('storage/' . $item->archivo . '/preview.jpg') }}">
|
||||
<source src="{{ asset('storage/' . $item->archivo) }}" type="video/mp4">
|
||||
</video>
|
||||
@else
|
||||
<div class="placeholder-img w-100 h-100">
|
||||
<i class="fas fa-video fa-2x"></i>
|
||||
</div>
|
||||
@endif
|
||||
<div class="play-icon">
|
||||
<i class="fas fa-play"></i>
|
||||
</div>
|
||||
@else
|
||||
@if($item->archivo)
|
||||
<a href="{{ asset('storage/' . $item->archivo) }}"
|
||||
data-lightbox="gallery"
|
||||
data-title="{{ $item->titulo }}">
|
||||
<img src="{{ asset('storage/' . $item->archivo) }}"
|
||||
alt="{{ $item->titulo }}"
|
||||
loading="lazy">
|
||||
</a>
|
||||
@else
|
||||
<div class="placeholder-img w-100 h-100">
|
||||
<i class="fas fa-image fa-2x"></i>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<div class="overlay">
|
||||
<div class="overlay-content">
|
||||
<h4>{{ $item->titulo }}</h4>
|
||||
@if($item->descripcion)
|
||||
<p>{{ Str::limit($item->descripcion, 60) }}</p>
|
||||
@endif
|
||||
<span class="badge bg-light text-dark mt-2">
|
||||
<i class="fas {{ $item->tipo === 'video' ? 'fa-video' : 'fa-image' }} me-1"></i>
|
||||
{{ $item->tipo === 'video' ? 'Video' : 'Imagen' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="pagination-wrapper">
|
||||
{{ $galeria->appends(['tipo' => $tipo])->links() }}
|
||||
</div>
|
||||
@else
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-images"></i>
|
||||
<h4>No hay elementos en la galería</h4>
|
||||
<p>Próximamente subiremos más contenido. ¡Vuelve pronto!</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.page-header {
|
||||
background: linear-gradient(135deg, var(--secondary) 0%, var(--primary) 100%);
|
||||
padding: 60px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 2.5rem;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
color: var(--text-light);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.pagination-wrapper {
|
||||
margin-top: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pagination .page-link {
|
||||
color: var(--text);
|
||||
border: 2px solid var(--border);
|
||||
margin: 0 0.25rem;
|
||||
border-radius: 10px;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.pagination .page-link:hover {
|
||||
background: var(--primary);
|
||||
border-color: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pagination .page-item.active .page-link {
|
||||
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
|
||||
border-color: transparent;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function filterGallery(tipo) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('tipo', tipo);
|
||||
window.location.href = url.toString();
|
||||
}
|
||||
|
||||
// Configurar Lightbox
|
||||
lightbox.option({
|
||||
'resizeDuration': 200,
|
||||
'wrapAround': true,
|
||||
'albumLabel': 'Imagen %1 de %2',
|
||||
'fadeDuration': 300,
|
||||
'imageFadeDuration': 300
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
209
resources/views/frontend/home/index.blade.php
Executable file
209
resources/views/frontend/home/index.blade.php
Executable file
@@ -0,0 +1,209 @@
|
||||
@extends('frontend.layouts.main')
|
||||
|
||||
@section('title', 'Lash Vanshy - Extensiones de Pestañas Profesionales')
|
||||
|
||||
@section('content')
|
||||
<!-- Hero Section -->
|
||||
<section class="hero-section">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-6 order-2 order-lg-1">
|
||||
<div class="hero-content text-center text-lg-start">
|
||||
<h1 class="hero-title">
|
||||
<span>Lash Vanshy</span>
|
||||
</h1>
|
||||
<p class="hero-subtitle">
|
||||
Extensiones de pestañas profesionales con productos de la más alta calidad.
|
||||
Destaca tu belleza natural con nuestros servicios especializados y personalizados.
|
||||
</p>
|
||||
<a href="{{ route('productos') }}" class="hero-btn">
|
||||
Ver Servicios <i class="fas fa-arrow-right ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 order-1 order-lg-2 mb-4 mb-lg-0">
|
||||
<div class="hero-image-wrapper">
|
||||
<img src="https://images.unsplash.com/photo-1516975080664-ed2fc6a32937?w=600&h=500&fit=crop"
|
||||
alt="Extensiones de pestañas profesionales"
|
||||
class="hero-image img-fluid"
|
||||
onerror="this.src='data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 400 300%22%3E%3Crect fill=%22%23FFE4EC%22 width=%22400%22 height=%22300%22/%3E%3Ctext x=%22200%22 y=%22150%22 text-anchor=%22middle%22 fill=%22%23E89AAD%22 font-size=%2240%22%3E%E2%9C%A8%3C/text%3E%3Ctext x=%22200%22 y=%22190%22 text-anchor=%22middle%22 fill=%22%234A4A4A%22 font-size=%2216%22%3ELash Vanshy%3C/text%3E%3C/svg%3E'">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Servicios Destacados -->
|
||||
@if($productosDestacados->isNotEmpty())
|
||||
<section class="section-padding">
|
||||
<div class="container">
|
||||
<div class="section-title">
|
||||
<h2>Servicios <span>Destacados</span></h2>
|
||||
<p>Descubre nuestros servicios más populares y solicitados por nuestras clientas.</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
@foreach($productosDestacados as $producto)
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card-custom h-100">
|
||||
<div class="position-relative">
|
||||
@if($producto->imagen)
|
||||
<img src="{{ asset('storage/' . $producto->imagen) }}"
|
||||
alt="{{ $producto->nombre }}"
|
||||
class="card-img-top">
|
||||
@else
|
||||
<div class="card-img-top placeholder-img">
|
||||
<i class="fas fa-eye"></i>
|
||||
</div>
|
||||
@endif
|
||||
<span class="badge-destacado">⭐ Destacado</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<span class="text-primary small text-uppercase fw-bold">{{ $producto->categoria }}</span>
|
||||
<h5 class="card-title mt-2">{{ $producto->nombre }}</h5>
|
||||
<p class="card-text">{{ Str::limit($producto->descripcion, 100) }}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="price">{{ $producto->precio_formateado }}</span>
|
||||
<a href="{{ route('contacto') }}" class="btn btn-sm btn-outline-primary">
|
||||
Reservar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<a href="{{ route('productos') }}" class="hero-btn">
|
||||
Ver Todos los Servicios <i class="fas fa-arrow-right ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
<!-- Galería Preview -->
|
||||
@if($galeria->isNotEmpty())
|
||||
<section class="section-padding bg-secondary">
|
||||
<div class="container">
|
||||
<div class="section-title">
|
||||
<h2>Nuestro <span>Trabajo</span></h2>
|
||||
<p>Mira algunos de nuestros trabajos realizados. Cada cliente es único y especial para nosotros.</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-3">
|
||||
@foreach($galeria as $item)
|
||||
<div class="col-6 col-md-4 col-lg-3">
|
||||
<div class="gallery-item">
|
||||
@if($item->tipo === 'video')
|
||||
@if($item->thumbnail)
|
||||
<img src="{{ asset('storage/' . $item->thumbnail) }}" alt="{{ $item->titulo }}">
|
||||
@else
|
||||
<div class="placeholder-img w-100 h-100">
|
||||
<i class="fas fa-play"></i>
|
||||
</div>
|
||||
@endif
|
||||
<div class="play-icon">
|
||||
<i class="fas fa-play"></i>
|
||||
</div>
|
||||
@else
|
||||
@if($item->archivo)
|
||||
<img src="{{ asset('storage/' . $item->archivo) }}" alt="{{ $item->titulo }}">
|
||||
@else
|
||||
<div class="placeholder-img w-100 h-100">
|
||||
<i class="fas fa-image"></i>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
<div class="overlay">
|
||||
<div class="overlay-content">
|
||||
<h4>{{ $item->titulo }}</h4>
|
||||
@if($item->descripcion)
|
||||
<p>{{ Str::limit($item->descripcion, 50) }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<a href="{{ route('galeria') }}" class="hero-btn">
|
||||
Ver Galería Completa <i class="fas fa-arrow-right ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
|
||||
<!-- Por qué elegirnos -->
|
||||
<section class="section-padding">
|
||||
<div class="container">
|
||||
<div class="section-title">
|
||||
<h2>¿Por qué <span>Elegirnos?</span></h2>
|
||||
<p>Nos comprometemos a darte los mejores resultados.</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="text-center p-4">
|
||||
<div class="feature-icon mb-3">
|
||||
<i class="fas fa-gem"></i>
|
||||
</div>
|
||||
<h4>Productos de Calidad</h4>
|
||||
<p class="text-muted">Utilizamos solo productos premium y seguros para tus pestañas.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-center p-4">
|
||||
<div class="feature-icon mb-3">
|
||||
<i class="fas fa-user-tie"></i>
|
||||
</div>
|
||||
<h4>Profesionales Expertas</h4>
|
||||
<p class="text-muted">Nuestro equipo tiene años de experiencia en el sector.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="text-center p-4">
|
||||
<div class="feature-icon mb-3">
|
||||
<i class="fas fa-heart"></i>
|
||||
</div>
|
||||
<h4>Atención Personalizada</h4>
|
||||
<p class="text-muted">Cada clienta recibe un tratamiento adaptado a sus necesidades.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA Contacto -->
|
||||
<section class="section-padding bg-primary-custom">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8 text-center">
|
||||
<h2 class="mb-3">¿Lista para transformar tu mirada?</h2>
|
||||
<p class="mb-4">Contáctanos hoy mismo y agenda tu cita. Estaremos encantadas de atenderte.</p>
|
||||
<a href="{{ route('contacto') }}" class="hero-btn">
|
||||
Contactar Ahora <i class="fas fa-envelope ms-2"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.feature-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: linear-gradient(135deg, var(--secondary), var(--primary));
|
||||
border-radius: 50%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2rem;
|
||||
color: var(--primary-dark);
|
||||
}
|
||||
</style>
|
||||
@endsection
|
||||
144
resources/views/frontend/layouts/main.blade.php
Executable file
144
resources/views/frontend/layouts/main.blade.php
Executable file
@@ -0,0 +1,144 @@
|
||||
@php
|
||||
$configuracion = \App\Models\Configuracion::allAsArray();
|
||||
@endphp
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Lash Vanshy - Extensiones de pestañas profesionales. Servicios de extensiones de pestañas, lift de pestañas y más.">
|
||||
<title>@yield('title', 'Lash Vanshy - Extensiones de Pestañas Profesionales')</title>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<!-- Lightbox CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/lightbox2@2.11.4/dist/css/lightbox.min.css" rel="stylesheet">
|
||||
|
||||
<!-- Estilos personalizados -->
|
||||
<link rel="stylesheet" href="{{ asset('css/frontend.css') }}">
|
||||
|
||||
@stack('styles')
|
||||
</head>
|
||||
<body>
|
||||
<!-- Header -->
|
||||
<header class="header-main">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="{{ route('home') }}">
|
||||
<span class="brand-icon">✨</span>
|
||||
<span class="brand-text">Lash Vanshy</span>
|
||||
</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->routeIs('home') ? 'active' : '' }}" href="{{ route('home') }}">Inicio</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->is('galeria*') ? 'active' : '' }}" href="{{ route('galeria') }}">Galería</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->is('productos*') ? 'active' : '' }}" href="{{ route('productos') }}">Servicios</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {{ request()->is('contacto*') ? 'active' : '' }}" href="{{ route('contacto') }}">Contacto</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- Contenido principal -->
|
||||
<main class="main-content">
|
||||
@yield('content')
|
||||
</main>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="footer-main">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-4 mb-4 mb-lg-0">
|
||||
<h5 class="footer-title">Lash Vanshy</h5>
|
||||
<p class="footer-description">
|
||||
Extensions de pestañas profesionales con productos de la más alta calidad. Destaca tu belleza natural con我们的 servicios especializados.
|
||||
</p>
|
||||
<div class="social-links">
|
||||
<a href="{{ $configuracion['facebook'] ?? '#' }}" target="_blank" class="social-link" title="Facebook">
|
||||
<i class="fab fa-facebook-f"></i>
|
||||
</a>
|
||||
<a href="{{ $configuracion['instagram'] ?? '#' }}" target="_blank" class="social-link" title="Instagram">
|
||||
<i class="fab fa-instagram"></i>
|
||||
</a>
|
||||
<a href="{{ $configuracion['whatsapp'] ?? '#' }}" target="_blank" class="social-link" title="WhatsApp">
|
||||
<i class="fab fa-whatsapp"></i>
|
||||
</a>
|
||||
<a href="{{ $configuracion['tiktok'] ?? '#' }}" target="_blank" class="social-link" title="TikTok">
|
||||
<i class="fab fa-tiktok"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4 mb-4 mb-lg-0">
|
||||
<h5 class="footer-title">Contacto</h5>
|
||||
<ul class="footer-contact">
|
||||
@if(!empty($configuracion['telefono']))
|
||||
<li>
|
||||
<i class="fas fa-phone"></i>
|
||||
<a href="tel:{{ $configuracion['telefono'] }}">{{ $configuracion['telefono'] }}</a>
|
||||
</li>
|
||||
@endif
|
||||
@if(!empty($configuracion['email']))
|
||||
<li>
|
||||
<i class="fas fa-envelope"></i>
|
||||
<a href="mailto:{{ $configuracion['email'] }}">{{ $configuracion['email'] }}</a>
|
||||
</li>
|
||||
@endif
|
||||
@if(!empty($configuracion['direccion']))
|
||||
<li>
|
||||
<i class="fas fa-map-marker-alt"></i>
|
||||
<span>{{ $configuracion['direccion'] }}</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<h5 class="footer-title">Horario</h5>
|
||||
<ul class="footer-schedule">
|
||||
@if(!empty($configuracion['horario']))
|
||||
<li><span>{!! nl2br(e($configuracion['horario'])) !!}</span></li>
|
||||
@else
|
||||
<li><span>Lunes - Viernes: 10:00 - 20:00</span></li>
|
||||
<li><span>Sábado: 10:00 - 18:00</span></li>
|
||||
<li><span>Domingo: Cerrado</span></li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-bottom">
|
||||
<p>© {{ date('Y') }} Lash Vanshy. Todos los derechos reservados.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Lightbox JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/lightbox2@2.11.4/dist/js/lightbox.min.js"></script>
|
||||
|
||||
@stack('scripts')
|
||||
</body>
|
||||
</html>
|
||||
178
resources/views/frontend/productos/index.blade.php
Executable file
178
resources/views/frontend/productos/index.blade.php
Executable file
@@ -0,0 +1,178 @@
|
||||
@extends('frontend.layouts.main')
|
||||
|
||||
@section('title', 'Servicios - Lash Vanshy')
|
||||
|
||||
@section('content')
|
||||
<!-- Page Header -->
|
||||
<section class="page-header">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h1>Nuestros Servicios</h1>
|
||||
<p>Descubre todos los servicios que temos para ti</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Productos Section -->
|
||||
<section class="products-section section-padding">
|
||||
<div class="container">
|
||||
<!-- Category Filters -->
|
||||
<div class="category-filters">
|
||||
<button class="filter-btn {{ $categoria === 'todos' ? 'active' : '' }}"
|
||||
onclick="filterProducts('todos')">
|
||||
<i class="fas fa-th-large me-2"></i>Todos
|
||||
</button>
|
||||
@foreach($categorias as $cat)
|
||||
<button class="filter-btn {{ $categoria === $cat ? 'active' : '' }}"
|
||||
onclick="filterProducts('{{ $cat }}')">
|
||||
{{ $cat }}
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<!-- Featured Products -->
|
||||
@if($destacados->isNotEmpty() && $productos->currentPage() === 1)
|
||||
<div class="mb-5">
|
||||
<h3 class="mb-4">
|
||||
<i class="fas fa-star text-warning me-2"></i>Servicios Destacados
|
||||
</h3>
|
||||
<div class="products-grid">
|
||||
@foreach($destacados as $producto)
|
||||
<div class="product-card">
|
||||
@if($producto->imagen)
|
||||
<img src="{{ asset('storage/' . $producto->imagen) }}"
|
||||
alt="{{ $producto->nombre }}">
|
||||
@else
|
||||
<div class="placeholder-img" style="height: 250px;">
|
||||
<i class="fas fa-spa fa-2x"></i>
|
||||
</div>
|
||||
@endif
|
||||
<div class="product-info">
|
||||
<span class="product-category">{{ $producto->categoria }}</span>
|
||||
<h3 class="product-title">{{ $producto->nombre }}</h3>
|
||||
<p class="product-description">{{ Str::limit($producto->descripcion, 120) }}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="product-price">{{ $producto->precio_formateado }}</span>
|
||||
<a href="{{ route('contacto') }}" class="btn btn-sm btn-outline-primary rounded-pill">
|
||||
Reservar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- All Products -->
|
||||
<div>
|
||||
<h3 class="mb-4">Todos los Servicios</h3>
|
||||
@if($productos->isNotEmpty())
|
||||
<div class="products-grid">
|
||||
@foreach($productos as $producto)
|
||||
<div class="product-card">
|
||||
@if($producto->imagen)
|
||||
<img src="{{ asset('storage/' . $producto->imagen) }}"
|
||||
alt="{{ $producto->nombre }}">
|
||||
@else
|
||||
<div class="placeholder-img" style="height: 250px;">
|
||||
<i class="fas fa-spa fa-2x"></i>
|
||||
</div>
|
||||
@endif
|
||||
<div class="product-info">
|
||||
<span class="product-category">{{ $producto->categoria }}</span>
|
||||
<h3 class="product-title">{{ $producto->nombre }}</h3>
|
||||
<p class="product-description">{{ Str::limit($producto->descripcion, 120) }}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="product-price">{{ $producto->precio_formateado }}</span>
|
||||
<a href="{{ route('contacto') }}" class="btn btn-sm btn-outline-primary rounded-pill">
|
||||
Reservar
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div class="pagination-wrapper">
|
||||
{{ $productos->appends(['categoria' => $categoria])->links() }}
|
||||
</div>
|
||||
@else
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-spa"></i>
|
||||
<h4>No hay servicios disponibles</h4>
|
||||
<p>Próximamente tendremos más servicios. ¡Vuelve pronto!</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<style>
|
||||
.page-header {
|
||||
background: linear-gradient(135deg, var(--secondary) 0%, var(--primary) 100%);
|
||||
padding: 60px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 2.5rem;
|
||||
color: var(--text);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
color: var(--text-light);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.pagination-wrapper {
|
||||
margin-top: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pagination .page-link {
|
||||
color: var(--text);
|
||||
border: 2px solid var(--border);
|
||||
margin: 0 0.25rem;
|
||||
border-radius: 10px;
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.pagination .page-link:hover {
|
||||
background: var(--primary);
|
||||
border-color: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pagination .page-item.active .page-link {
|
||||
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
|
||||
border-color: transparent;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
border-color: var(--primary);
|
||||
color: var(--primary-dark);
|
||||
}
|
||||
|
||||
.btn-outline-primary:hover {
|
||||
background: var(--primary);
|
||||
border-color: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
function filterProducts(categoria) {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('categoria', categoria);
|
||||
window.location.href = url.toString();
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
225
resources/views/welcome.blade.php
Executable file
225
resources/views/welcome.blade.php
Executable file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user