Initial commit: Lash Vanshy - Complete project with admin panel, gallery, products, and contact

This commit is contained in:
2026-04-08 00:23:16 -06:00
commit e07e065791
111 changed files with 17939 additions and 0 deletions

11
resources/css/app.css Executable file
View 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
View File

@@ -0,0 +1 @@
import './bootstrap';

4
resources/js/bootstrap.js vendored Executable file
View File

@@ -0,0 +1,4 @@
import axios from 'axios';
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

View 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>

View 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&#10;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

View 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

View 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

View 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

View 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

View 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>

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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>&copy; {{ 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>

View 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

File diff suppressed because one or more lines are too long