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

View File

@@ -0,0 +1,156 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\AdminUserRequest;
use App\Models\AdminUser;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
class AdminUserController extends Controller
{
/**
* Mostrar lista de usuarios admin
*/
public function index(): View
{
$users = AdminUser::orderBy('created_at', 'desc')->paginate(15);
return view('admin.usuarios.index', compact('users'));
}
/**
* Mostrar formulario de creación
*/
public function create(): View
{
return view('admin.usuarios.create');
}
/**
* Guardar nuevo usuario admin
*/
public function store(AdminUserRequest $request): RedirectResponse
{
// Verificar permisos - solo super_admin puede crear otros admins
$currentUser = Auth::guard('admin')->user();
if (! $currentUser || ! $currentUser->isSuperAdmin()) {
return back()->withErrors(['error' => 'No tienes permisos para crear administradores.']);
}
// Si no es super_admin, no puede crear super_admin
if ($request->rol === 'super_admin' && ! $currentUser->isSuperAdmin()) {
return back()->withErrors(['error' => 'No tienes permisos para crear super administradores.']);
}
$data = $request->validated();
// Manejar upload de avatar
if ($request->hasFile('avatar')) {
$data['avatar'] = $this->uploadFile($request->file('avatar'), 'avatars');
}
AdminUser::create($data);
return redirect()->route('admin.users.index')->with('success', 'Usuario administrativo creado correctamente.');
}
/**
* Mostrar formulario de edición
*/
public function edit(AdminUser $admin_user): \Illuminate\Contracts\View\View|RedirectResponse
{
$user = Auth::guard('admin')->user();
// Solo super_admin puede editar otros usuarios
// Un usuario puede editar su propio perfil
if (! $user->isSuperAdmin() && $user->id !== $admin_user->id) {
return back()->withErrors(['error' => 'No tienes permisos para editar este usuario.']);
}
return view('admin.usuarios.edit', compact('admin_user'));
}
/**
* Actualizar usuario admin
*/
public function update(AdminUserRequest $request, AdminUser $admin_user): RedirectResponse
{
// Verificar permisos
$currentUser = Auth::guard('admin')->user();
// Un usuario puede editar su propio perfil, pero no cambiar su rol
if ($currentUser->id === $admin_user->id) {
// No permitir cambiar rol de uno mismo
if ($request->has('rol') && $request->rol !== $admin_user->rol) {
return back()->withErrors(['error' => 'No puedes cambiar tu propio rol.']);
}
} elseif (! $currentUser->isSuperAdmin()) {
return back()->withErrors(['error' => 'No tienes permisos para editar este usuario.']);
}
// Si no es super_admin, no puede crear/asignar super_admin
if ($request->rol === 'super_admin' && ! $currentUser->isSuperAdmin()) {
return back()->withErrors(['error' => 'No tienes permisos para asignar rol de super administrador.']);
}
$data = $request->validated();
// Si no se proporciona password, eliminar del array
if (empty($data['password'])) {
unset($data['password']);
}
// Manejar upload de avatar
if ($request->hasFile('avatar')) {
// Eliminar avatar anterior
if ($admin_user->avatar) {
Storage::disk('public')->delete($admin_user->avatar);
}
$data['avatar'] = $this->uploadFile($request->file('avatar'), 'avatars');
}
$admin_user->update($data);
return redirect()->route('admin.users.index')->with('success', 'Usuario administrativo actualizado correctamente.');
}
/**
* Eliminar usuario admin
*/
public function destroy(AdminUser $admin_user): RedirectResponse
{
// Verificar permisos - solo super_admin puede eliminar
$currentUser = Auth::guard('admin')->user();
if (! $currentUser || ! $currentUser->isSuperAdmin()) {
return back()->withErrors(['error' => 'No tienes permisos para eliminar administradores.']);
}
// No permitir eliminarse a sí mismo
if ($currentUser->id === $admin_user->id) {
return back()->withErrors(['error' => 'No puedes eliminar tu propia cuenta.']);
}
// Eliminar avatar
if ($admin_user->avatar) {
Storage::disk('public')->delete($admin_user->avatar);
}
$admin_user->delete();
return redirect()->route('admin.users.index')->with('success', 'Usuario administrativo eliminado correctamente.');
}
/**
* Subir archivo a almacenamiento
*/
private function uploadFile($file, string $directory): string
{
return $file->store($directory, 'public');
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\LoginRequest;
use App\Models\AdminUser;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth;
class AuthController extends Controller
{
/**
* Mostrar formulario de login
*/
public function showLogin(): \Illuminate\Contracts\View\View|RedirectResponse
{
if (Auth::guard('admin')->check()) {
return redirect()->route('admin.dashboard');
}
return view('admin.auth.login');
}
/**
* Procesar login
*/
public function login(LoginRequest $request): RedirectResponse
{
$credentials = $request->only('email', 'password');
// Buscar usuario por email
$adminUser = AdminUser::where('email', $credentials['email'])->first();
if (! $adminUser) {
return back()->withErrors([
'email' => 'Las credenciales no son válidas.',
])->withInput($request->except('password'));
}
// Verificar password
if (! $adminUser->validatePassword($credentials['password'])) {
return back()->withErrors([
'email' => 'Las credenciales no son válidas.',
])->withInput($request->except('password'));
}
// Login manual
Auth::guard('admin')->login($adminUser);
$request->session()->regenerate();
return redirect()->intended(route('admin.dashboard'))->with('success', 'Bienvenido al panel de administración.');
}
/**
* Cerrar sesión
*/
public function logout(): RedirectResponse
{
Auth::guard('admin')->logout();
request()->session()->invalidate();
request()->session()->regenerateToken();
return redirect()->route('admin.login')->with('success', 'Sesión cerrada correctamente.');
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Configuracion;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class ConfiguracionController extends Controller
{
/**
* Mostrar configuración
*/
public function index(): View
{
$configuracion = Configuracion::allAsArray();
return view('admin.configuracion.index', compact('configuracion'));
}
/**
* Actualizar configuración
*/
public function update(): RedirectResponse
{
$fields = [
'nombre_sitio',
'telefono',
'email',
'direccion',
'horario',
'facebook',
'instagram',
'whatsapp',
'tiktok',
'youtube',
'seo_titulo',
'seo_descripcion',
];
foreach ($fields as $field) {
$value = request($field);
// Only save if the field has a value
if ($value !== null && $value !== '') {
Configuracion::set($field, $value);
} else {
// Optionally clear empty fields
Configuracion::remove($field);
}
}
return redirect()->route('admin.configuracion.index')->with('success', 'Configuración guardada correctamente.');
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Galeria;
use App\Models\Mensaje;
use App\Models\Producto;
use Illuminate\View\View;
class DashboardController extends Controller
{
/**
* Mostrar el dashboard principal
*/
public function index(): View
{
// Obtener estadísticas
$stats = [
'mensajes_no_leidos' => Mensaje::noLeidos()->count(),
'total_modelos' => Galeria::count(),
'total_productos' => Producto::count(),
'productos_destacados' => Producto::where('destacado', true)->count(),
'modelos_activos' => Galeria::where('activo', true)->count(),
];
// Mensajes recientes
$mensajes_recientes = Mensaje::orderBy('created_at', 'desc')->take(5)->get();
return view('admin.dashboard.index', compact('stats', 'mensajes_recientes'));
}
}

View File

@@ -0,0 +1,113 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\GaleriaRequest;
use App\Models\Galeria;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
class GaleriaController extends Controller
{
public function index(): View
{
$galerias = Galeria::ordenado()->paginate(15);
return view('admin.galeria.index', compact('galerias'));
}
public function create(): View
{
return view('admin.galeria.create');
}
public function store(GaleriaRequest $request): RedirectResponse
{
try {
$data = $request->validated();
Log::info('GaleriaRequest validated', $data);
if ($request->hasFile('archivo')) {
Log::info('Has archivo file', ['file' => $request->file('archivo')->getClientOriginalName()]);
$data['archivo'] = $this->uploadFile($request->file('archivo'), 'galeria');
} else {
Log::warning('No archivo file in request');
Log::info('All files:', ['files' => $request->allFiles()]);
}
if ($request->hasFile('thumbnail')) {
$data['thumbnail'] = $this->uploadFile($request->file('thumbnail'), 'galeria/thumbnails');
}
$data['activo'] = $request->has('activo');
$data['orden'] = $data['orden'] ?? 0;
Galeria::create($data);
return redirect()->route('admin.galeria.index')->with('success', 'Modelo registrado correctamente.');
} catch (\Exception $e) {
Log::error('Error creating Galeria: '.$e->getMessage());
return redirect()->back()->with('error', 'Error al guardar: '.$e->getMessage())->withInput();
}
}
public function edit(Galeria $galeria): View
{
return view('admin.galeria.edit', compact('galeria'));
}
public function update(GaleriaRequest $request, Galeria $galeria): RedirectResponse
{
try {
$data = $request->validated();
if ($request->hasFile('archivo')) {
if ($galeria->archivo) {
Storage::disk('public')->delete($galeria->archivo);
}
$data['archivo'] = $this->uploadFile($request->file('archivo'), 'galeria');
}
if ($request->hasFile('thumbnail')) {
if ($galeria->thumbnail) {
Storage::disk('public')->delete($galeria->thumbnail);
}
$data['thumbnail'] = $this->uploadFile($request->file('thumbnail'), 'galeria/thumbnails');
}
$data['activo'] = $request->has('activo');
$data['orden'] = $data['orden'] ?? $galeria->orden;
$galeria->update($data);
return redirect()->route('admin.galeria.index')->with('success', 'Modelo actualizado correctamente.');
} catch (\Exception $e) {
Log::error('Error updating Galeria: '.$e->getMessage());
return redirect()->back()->with('error', 'Error al actualizar: '.$e->getMessage())->withInput();
}
}
public function destroy(Galeria $galeria): RedirectResponse
{
if ($galeria->archivo) {
Storage::disk('public')->delete($galeria->archivo);
}
if ($galeria->thumbnail) {
Storage::disk('public')->delete($galeria->thumbnail);
}
$galeria->delete();
return redirect()->route('admin.galeria.index')->with('success', 'Modelo eliminado correctamente.');
}
private function uploadFile($file, string $directory): string
{
return $file->store($directory, 'public');
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\Mensaje;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class MensajeController extends Controller
{
/**
* Mostrar lista de mensajes
*/
public function index(): View
{
$mensajes = Mensaje::orderBy('created_at', 'desc')->paginate(15);
return view('admin.mensajes.index', compact('mensajes'));
}
/**
* Mostrar mensaje específico
*/
public function show(Mensaje $mensaje): View
{
// Marcar como leído al ver
if (! $mensaje->leido) {
$mensaje->marcarLeido();
}
return view('admin.mensajes.show', compact('mensaje'));
}
/**
* Alternar estado de leído/no leído
*/
public function markRead(Mensaje $mensaje): RedirectResponse
{
if ($mensaje->leido) {
$mensaje->marcarNoLeido();
$message = 'Mensaje marcado como no leído.';
} else {
$mensaje->marcarLeido();
$message = 'Mensaje marcado como leído.';
}
return back()->with('success', $message);
}
/**
* Eliminar mensaje
*/
public function destroy(Mensaje $mensaje): RedirectResponse
{
$mensaje->delete();
return redirect()->route('admin.mensajes.index')->with('success', 'Mensaje eliminado correctamente.');
}
/**
* Marcar todos los mensajes como leídos
*/
public function markAllRead(): RedirectResponse
{
Mensaje::where('leido', false)->update(['leido' => true]);
return back()->with('success', 'Todos los mensajes marcados como leídos.');
}
}

View File

@@ -0,0 +1,110 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\ProductoRequest;
use App\Models\Producto;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
class ProductoController extends Controller
{
/**
* Mostrar lista de productos
*/
public function index(): View
{
$productos = Producto::orderBy('orden', 'asc')->orderBy('created_at', 'desc')->paginate(15);
return view('admin.productos.index', compact('productos'));
}
/**
* Mostrar formulario de creación
*/
public function create(): View
{
return view('admin.productos.create');
}
/**
* Guardar nuevo registro
*/
public function store(ProductoRequest $request): RedirectResponse
{
$data = $request->validated();
// Manejar upload de imagen
if ($request->hasFile('imagen')) {
$data['imagen'] = $this->uploadFile($request->file('imagen'), 'productos');
}
// Valores por defecto
$data['destacado'] = $request->has('destacado');
$data['activo'] = $request->has('activo');
$data['orden'] = $data['orden'] ?? 0;
Producto::create($data);
return redirect()->route('admin.productos.index')->with('success', 'Producto registrado correctamente.');
}
/**
* Mostrar formulario de edición
*/
public function edit(Producto $producto): View
{
return view('admin.productos.edit', compact('producto'));
}
/**
* Actualizar registro
*/
public function update(ProductoRequest $request, Producto $producto): RedirectResponse
{
$data = $request->validated();
// Manejar upload de imagen si se proporciona una nueva
if ($request->hasFile('imagen')) {
// Eliminar imagen anterior
if ($producto->imagen) {
Storage::disk('public')->delete($producto->imagen);
}
$data['imagen'] = $this->uploadFile($request->file('imagen'), 'productos');
}
// Valores por defecto
$data['destacado'] = $request->has('destacado');
$data['activo'] = $request->has('activo');
$data['orden'] = $data['orden'] ?? $producto->orden;
$producto->update($data);
return redirect()->route('admin.productos.index')->with('success', 'Producto actualizado correctamente.');
}
/**
* Eliminar registro
*/
public function destroy(Producto $producto): RedirectResponse
{
// Eliminar imagen
if ($producto->imagen) {
Storage::disk('public')->delete($producto->imagen);
}
$producto->delete();
return redirect()->route('admin.productos.index')->with('success', 'Producto eliminado correctamente.');
}
/**
* Subir archivo a almacenamiento
*/
private function uploadFile($file, string $directory): string
{
return $file->store($directory, 'public');
}
}