- Implementada nueva funcion para crear productos destacados por etiquetas, mostrando carrusel con los mismos en la pagina welcome.
This commit is contained in:
Bryam105
2026-06-02 09:46:53 -03:00
parent 63f91bdd6d
commit 902b8433d9
17 changed files with 480 additions and 33 deletions
+11 -2
View File
@@ -15,9 +15,10 @@ class CatalogoController extends Controller
// 1. Atrapamos lo que el usuario escribió o seleccionó
$query = $request->input('search');
$type = $request->input('type');
$tagFilter = $request->input('tag');
// --- CONSULTA PARA LA GRILLA (PAGINADA) ---
$productsQuery = Product::query();
$productsQuery = Product::with('tags');
$productsQuery->when($query, function ($q) use ($query) {
return $q->where('name', 'like', "%{$query}%");
@@ -29,6 +30,12 @@ class CatalogoController extends Controller
$productsQuery->where('type', '!=', 'service');
}
if ($tagFilter) {
$productsQuery->whereHas('tags', function($q) use ($tagFilter) {
$q->where('name', $tagFilter);
});
}
//mostrar si tiene stock mayor a 0
$productsQuery->where('stock_quantity', '>', 0);
@@ -40,8 +47,10 @@ class CatalogoController extends Controller
->orderBy('name', 'asc')
->get();
$allTags = \App\Models\Tag::orderBy('name')->get();
// Devuelve la vista
return view('catalogo.index', compact('products', 'allProducts'));
return view('catalogo.index', compact('products', 'allProducts', 'allTags'));
}
public function show(Product $product)
+12 -11
View File
@@ -9,17 +9,18 @@ class HomeController extends Controller
{
public function __invoke(Request $request)
{
// Si tuvieras un campo 'sales_count', podrías usar ->orderByDesc('sales_count')
$destacados = Product::where('type', 'bike')
->latest() // Las más nuevas
->take(5)
->get();
// Traer todas las etiquetas con sus productos
$tagsConProductos = \App\Models\Tag::with('products')->get()
->filter(function ($tag) {
// Solo mantener etiquetas que tengan al menos 1 producto
return $tag->products->count() > 0;
})
->map(function ($tag) {
// Limitar a 7 productos como máximo para el carrousel
$tag->setRelation('products', $tag->products->take(7));
return $tag;
});
// Si no hay bicis, traemos cualquier cosa para que no se rompa
if ($destacados->isEmpty()) {
$destacados = Product::take(5)->get();
}
return view('welcome', compact('destacados'));
return view('welcome', compact('tagsConProductos'));
}
}
+16 -3
View File
@@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Models\Product;
use App\Models\Supplier;
use App\Models\Tag;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Storage;
@@ -55,7 +56,8 @@ class ProductosController extends Controller
public function create()
{
$suppliers = Supplier::orderBy('name')->get();
return view('productos.create', compact('suppliers'));
$tags = Tag::orderBy('name')->get();
return view('productos.create', compact('suppliers', 'tags'));
}
public function store(Request $request)
@@ -79,7 +81,11 @@ class ProductosController extends Controller
}
unset($validated['image']);
Product::create($validated);
$product = Product::create($validated);
if ($request->has('tags')) {
$product->tags()->sync($request->tags);
}
return redirect()->route('productos.index')
->with('success', 'Producto creado correctamente.');
@@ -93,7 +99,8 @@ class ProductosController extends Controller
public function edit(Product $product)
{
$suppliers = Supplier::orderBy('name')->get();
return view('productos.edit', compact('product', 'suppliers'));
$tags = Tag::orderBy('name')->get();
return view('productos.edit', compact('product', 'suppliers', 'tags'));
}
public function update(Request $request, Product $product)
@@ -123,6 +130,12 @@ class ProductosController extends Controller
$product->update($validated);
if ($request->has('tags')) {
$product->tags()->sync($request->tags);
} else {
$product->tags()->detach();
}
return redirect()->route('productos.index')
->with('success', 'Producto actualizado exitosamente.');
}
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Tag;
class TagController extends Controller
{
public function index()
{
$tags = Tag::orderBy('name')->get();
return view('tags.index', compact('tags'));
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:50|unique:tags,name'
]);
Tag::create($validated);
return redirect()->route('tags.index')->with('success', 'Etiqueta creada correctamente.');
}
public function update(Request $request, Tag $tag)
{
$validated = $request->validate([
'name' => 'required|string|max:50|unique:tags,name,' . $tag->id
]);
$tag->update($validated);
return redirect()->route('tags.index')->with('success', 'Etiqueta actualizada correctamente.');
}
public function destroy(Tag $tag)
{
$tag->delete();
return redirect()->route('tags.index')->with('success', 'Etiqueta eliminada.');
}
}
+5
View File
@@ -42,6 +42,11 @@ class Product extends Model
{
return $this->belongsToMany(Sale::class, 'sale_details');
}
public function tags()
{
return $this->belongsToMany(Tag::class);
}
}
/**$user->sales: Te da la lista de todas las compras de ese usuario.
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Tag extends Model
{
use HasFactory;
protected $fillable = ['name'];
public function products()
{
return $this->belongsToMany(Product::class);
}
}
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('tags', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('tags');
}
};
@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('product_tag', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id')->constrained()->onDelete('cascade');
$table->foreignId('tag_id')->constrained()->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('product_tag');
}
};
+29 -1
View File
@@ -82,7 +82,7 @@
<button type="submit" class="px-5 py-3 text-sm font-bold text-neutral-900 bg-neon-lime rounded-lg hover:bg-[#b3e600] transition-colors">
Filtrar
</button>
@if(request('search') || request('type'))
@if(request('search') || request('type') || request('tag'))
<a href="{{ route('catalogo.index') }}" title="Limpiar Filtros" class="flex items-center justify-center p-3 text-sm font-bold text-red-600 bg-red-100 dark:bg-red-900/30 rounded-lg hover:bg-red-200 transition-colors">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18 17.94 6M18 18 6.06 6"/>
@@ -90,8 +90,25 @@
</a>
@endif
</div>
<input type="hidden" name="tag" value="{{ request('tag') }}">
</form>
<!-- Filtro de Etiquetas -->
@if($allTags->count() > 0)
<div class="mb-8 flex flex-wrap gap-2">
@foreach($allTags as $t)
<a href="{{ route('catalogo.index', array_merge(request()->query(), ['tag' => $t->name])) }}"
class="px-3 py-1.5 text-xs font-bold uppercase rounded-full border transition-colors
{{ request('tag') == $t->name
? 'bg-neon-lime border-neon-lime text-black dark:text-black'
: 'bg-transparent border-neutral-400 dark:border-neutral-600 text-gray-700 dark:text-gray-300 hover:border-neon-lime hover:text-neon-lime dark:hover:border-neon-lime dark:hover:text-neon-lime' }}">
#{{ $t->name }}
</a>
@endforeach
</div>
@endif
<!-- GRILLA DE PRODUCTOS -->
<!-- GRILLA DE PRODUCTOS -->
@if ($products->count() > 0)
@@ -113,6 +130,17 @@
<!-- ACÁ ESTÁ EL SEGUNDO CAMBIO: Agregamos "flex-1" al título -->
<h2 class="text-lg font-semibold text-black dark:text-white flex-1">{{ $product->name }}</h2>
{{-- Etiquetas --}}
@if($product->tags->count() > 0)
<div class="flex flex-wrap gap-1 mt-2">
@foreach($product->tags as $t)
<span class="text-[10px] font-bold uppercase tracking-wider px-2 py-0.5 rounded-full border border-neon-lime text-neon-lime bg-neon-lime/10">
#{{ $t->name }}
</span>
@endforeach
</div>
@endif
{{-- Precio --}}
@if (!empty($product->price))
<p class="text-neutral-900 dark:text-white group-hover:text-lime-700 group-hover:dark:text-neon-lime font-bold mt-3 transition-colors">${{ number_format($product->price, 2, ',', '.') }}</p>
+11
View File
@@ -50,6 +50,17 @@
<p class="text-stone-500 dark:text-neon-lime text-sm font-bold uppercase tracking-widest mb-2">SKU: {{ $product->sku ?? 'N/A' }}</p>
<h1 class="text-4xl font-black text-black dark:text-white italic mb-2 leading-tight">{{ $product->name }}</h1>
{{-- Etiquetas --}}
@if($product->tags->count() > 0)
<div class="flex flex-wrap gap-2 mt-2 mb-4">
@foreach($product->tags as $t)
<span class="text-xs font-bold uppercase tracking-wider px-3 py-1 rounded-full border border-neon-lime text-neon-lime bg-neon-lime/10">
#{{ $t->name }}
</span>
@endforeach
</div>
@endif
<!-- Stock Status -->
<div class="flex items-center gap-2 mt-4">
@if($product->stock_quantity > 5)
@@ -41,6 +41,12 @@
Stock
</a>
</li>
<li>
<a href="{{ route('tags.index') }}"
class="block py-2 px-3 md:p-0 transition-colors {{ request()->is('tags*') ? 'text-lime-600 dark:text-neon-lime border-b-2 border-lime-600 dark:border-neon-lime' : 'dark:text-white hover:text-lime-600 hover:dark:text-neon-lime' }}">
Etiquetas
</a>
</li>
<li>
<a href="{{ route('taller.index') }}"
class="block py-2 px-3 md:p-0 transition-colors {{ request()->is('taller*') ? 'text-lime-600 dark:text-neon-lime border-b-2 border-lime-600 dark:border-neon-lime' : 'dark:text-white hover:text-lime-600 hover:dark:text-neon-lime' }}">
@@ -1,14 +1,18 @@
@props(['items'])
@props(['items', 'title' => 'Destacados'])
<div class="relative w-full overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-800 bg-slate-50 dark:bg-panel-bg shadow-xl group" id="lauck-carousel">
@php
$id = Str::random(8);
@endphp
<div class="relative w-full overflow-hidden rounded-xl border border-neutral-200 dark:border-neutral-800 bg-slate-50 dark:bg-panel-bg shadow-xl group carousel-container" id="carousel-{{ $id }}">
<!-- Título Flotante (Opcional) -->
<div class="absolute top-4 left-6 z-10 bg-neon-lime/50 dark:bg-black/50 backdrop-blur-sm px-3 py-1 rounded border border-black/30 dark:border-neon-lime/30">
<span class="text-xs font-bold text-black dark:text-neon-lime uppercase tracking-widest">Destacados</span>
<span class="text-xs font-bold text-black dark:text-neon-lime uppercase tracking-widest">{{ $title }}</span>
</div>
<!-- Contenedor de Slides (Track) -->
<div class="flex transition-transform duration-500 ease-in-out h-[400px]" id="carousel-track">
<div class="flex transition-transform duration-500 ease-in-out h-[400px] carousel-track">
@forelse($items as $item)
<div class="w-full flex-shrink-0 flex flex-col md:flex-row h-full relative">
@@ -61,11 +65,11 @@
</div>
<!-- Controles (Flechas) -->
<button id="prevBtn" class="absolute top-1/2 left-4 -translate-y-1/2 bg-black/30 hover:bg-neon-lime hover:text-neutral-900 text-white p-3 rounded-full backdrop-blur-sm transition-all border border-white/10 z-20">
<button class="prevBtn absolute top-1/2 left-4 -translate-y-1/2 bg-black/30 hover:bg-neon-lime hover:text-neutral-900 text-white p-3 rounded-full backdrop-blur-sm transition-all border border-white/10 z-20">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path></svg>
</button>
<button id="nextBtn" class="absolute top-1/2 right-4 -translate-y-1/2 bg-black/30 hover:bg-neon-lime hover:text-neutral-900 text-white p-3 rounded-full backdrop-blur-sm transition-all border border-white/10 z-20">
<button class="nextBtn absolute top-1/2 right-4 -translate-y-1/2 bg-black/30 hover:bg-neon-lime hover:text-neutral-900 text-white p-3 rounded-full backdrop-blur-sm transition-all border border-white/10 z-20">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
</button>
@@ -80,7 +84,8 @@
<script type="module">
$(document).ready(function() {
const $track = $('#carousel-track');
const $container = $('#carousel-{{ $id }}');
const $track = $container.find('.carousel-track');
const $slides = $track.children();
const slideCount = $slides.length;
let currentIndex = 0;
@@ -91,8 +96,8 @@
$track.css('transform', `translateX(${translateX}%)`);
// Actualizar puntos
$('.carousel-dot').removeClass('bg-lime-500 dark:bg-neon-lime scale-125').addClass('bg-black/20 dark:bg-white/20');
$(`.carousel-dot[data-index="${currentIndex}"]`).addClass('bg-lime-500 dark:bg-neon-lime scale-125').removeClass('bg-black/20 dark:bg-white/20');
$container.find('.carousel-dot').removeClass('bg-lime-500 dark:bg-neon-lime scale-125').addClass('bg-black/20 dark:bg-white/20');
$container.find(`.carousel-dot[data-index="${currentIndex}"]`).addClass('bg-lime-500 dark:bg-neon-lime scale-125').removeClass('bg-black/20 dark:bg-white/20');
}
function nextSlide() {
@@ -106,17 +111,17 @@
}
// Event Listeners
$('#nextBtn').click(function() {
$container.find('.nextBtn').click(function() {
nextSlide();
resetAutoPlay();
});
$('#prevBtn').click(function() {
$container.find('.prevBtn').click(function() {
prevSlide();
resetAutoPlay();
});
$('.carousel-dot').click(function() {
$container.find('.carousel-dot').click(function() {
currentIndex = $(this).data('index');
updateCarousel();
resetAutoPlay();
@@ -35,6 +35,17 @@
<option value="spare" {{ old('type') == 'spare' ? 'selected' : '' }}>Repuesto</option>
</select>
</div>
<!-- Etiquetas -->
<div class="md:col-span-4">
<x-forms.label for="tags" value="Etiquetas" />
<select id="tags" name="tags[]" multiple="multiple" class="select2-tags w-full">
@foreach($tags as $tag)
<option value="{{ $tag->id }}" {{ in_array($tag->id, old('tags', [])) ? 'selected' : '' }}>
{{ $tag->name }}
</option>
@endforeach
</select>
</div>
<!-- Precios -->
<div class="">
<x-forms.label for="price" value="Precio Venta ($)" />
@@ -72,4 +83,83 @@
</div>
</form>
</div>
@push('styles')
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<style>
/* Dark Theme & Neon Lime overrides for Select2 */
.select2-container--default .select2-selection--multiple {
background-color: rgb(38 38 38); /* neutral-800 */
border-color: rgb(64 64 64); /* neutral-700 */
border-radius: 0.5rem;
min-height: 42px;
}
.select2-container--default.select2-container--focus .select2-selection--multiple {
border-color: #b3e600; /* neon-lime */
outline: 0;
box-shadow: 0 0 0 1px #b3e600;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice {
background-color: #b3e600;
border-color: #99c200;
color: #171717; /* neutral-900 */
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
color: #171717;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
background-color: #99c200;
color: #171717;
}
.select2-dropdown {
background-color: rgb(38 38 38);
border-color: rgb(64 64 64);
color: white;
}
.select2-search--dropdown .select2-search__field {
background-color: rgb(23 23 23);
border-color: rgb(64 64 64);
color: white;
}
.select2-container--default .select2-results__option--selected {
background-color: rgb(64 64 64);
}
.select2-container--default .select2-results__option--highlighted.select2-results__option--selectable {
background-color: #b3e600;
color: #171717;
}
.select2-container--default .select2-selection--multiple .select2-search--inline .select2-search__field {
color: white;
}
html:not(.dark) .select2-container--default .select2-selection--multiple {
background-color: rgb(209 213 219); /* gray-300 */
border-color: rgb(156 163 175); /* neutral-400 */
}
html:not(.dark) .select2-dropdown {
background-color: rgb(209 213 219);
border-color: rgb(156 163 175);
color: black;
}
html:not(.dark) .select2-search--dropdown .select2-search__field {
background-color: white;
color: black;
}
html:not(.dark) .select2-container--default .select2-selection--multiple .select2-search--inline .select2-search__field {
color: black;
}
</style>
@endpush
@push('scripts')
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
$(document).ready(function() {
$('.select2-tags').select2({
placeholder: "Buscar y seleccionar etiquetas...",
allowClear: true
});
});
</script>
@endpush
</x-layout>
+90
View File
@@ -34,6 +34,17 @@
<option value="spare" {{ old('type', $product->type) == 'spare' ? 'selected' : '' }}>Repuesto</option>
</select>
</div>
<!-- Etiquetas -->
<div class="md:col-span-4">
<x-forms.label for="tags" value="Etiquetas" />
<select id="tags" name="tags[]" multiple="multiple" class="select2-tags w-full">
@foreach($tags as $tag)
<option value="{{ $tag->id }}" {{ in_array($tag->id, old('tags', $product->tags->pluck('id')->toArray())) ? 'selected' : '' }}>
{{ $tag->name }}
</option>
@endforeach
</select>
</div>
<!-- Precios -->
<div class="">
<x-forms.label for="price" value="Precio Venta ($)" />
@@ -68,4 +79,83 @@
</div>
</form>
</div>
@push('styles')
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<style>
/* Dark Theme & Neon Lime overrides for Select2 */
.select2-container--default .select2-selection--multiple {
background-color: rgb(38 38 38); /* neutral-800 */
border-color: rgb(64 64 64); /* neutral-700 */
border-radius: 0.5rem;
min-height: 42px;
}
.select2-container--default.select2-container--focus .select2-selection--multiple {
border-color: #b3e600; /* neon-lime */
outline: 0;
box-shadow: 0 0 0 1px #b3e600;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice {
background-color: #b3e600;
border-color: #99c200;
color: #171717; /* neutral-900 */
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
color: #171717;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
background-color: #99c200;
color: #171717;
}
.select2-dropdown {
background-color: rgb(38 38 38);
border-color: rgb(64 64 64);
color: white;
}
.select2-search--dropdown .select2-search__field {
background-color: rgb(23 23 23);
border-color: rgb(64 64 64);
color: white;
}
.select2-container--default .select2-results__option--selected {
background-color: rgb(64 64 64);
}
.select2-container--default .select2-results__option--highlighted.select2-results__option--selectable {
background-color: #b3e600;
color: #171717;
}
.select2-container--default .select2-selection--multiple .select2-search--inline .select2-search__field {
color: white;
}
html:not(.dark) .select2-container--default .select2-selection--multiple {
background-color: rgb(209 213 219); /* gray-300 */
border-color: rgb(156 163 175); /* neutral-400 */
}
html:not(.dark) .select2-dropdown {
background-color: rgb(209 213 219);
border-color: rgb(156 163 175);
color: black;
}
html:not(.dark) .select2-search--dropdown .select2-search__field {
background-color: white;
color: black;
}
html:not(.dark) .select2-container--default .select2-selection--multiple .select2-search--inline .select2-search__field {
color: black;
}
</style>
@endpush
@push('scripts')
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
$(document).ready(function() {
$('.select2-tags').select2({
placeholder: "Buscar y seleccionar etiquetas...",
allowClear: true
});
});
</script>
@endpush
</x-layout>
+61
View File
@@ -0,0 +1,61 @@
<x-layout title="Lauck - Etiquetas">
<x-section-header subtitle="Administración" title="Gestión de " highlight="Etiquetas" />
<div class="w-full max-w-4xl mx-auto space-y-6">
<!-- Create Form -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl p-6 shadow-lg">
<h3 class="text-lg font-bold text-gray-900 dark:text-white mb-4">Nueva Etiqueta</h3>
<form action="{{ route('tags.store') }}" method="POST" class="flex flex-col md:flex-row gap-4 items-end">
@csrf
<div class="w-full md:flex-1">
<x-forms.label for="name" value="Nombre de la Etiqueta" />
<x-forms.input id="name" name="name" type="text" required placeholder="Ej: Oferta, Temporada Verano" :error="$errors->first('name')" />
</div>
<button type="submit" class="w-full md:w-auto px-6 py-2.5 h-[42px] bg-neutral-900 dark:bg-neon-lime text-neon-lime dark:text-neutral-900 font-bold rounded-lg hover:bg-neutral-800 hover:dark:bg-[#b3e600] transition-colors shadow-lg">
Guardar
</button>
</form>
</div>
<!-- Tags List -->
<div class="bg-gray-200 dark:bg-panel-bg border border-neutral-400 dark:border-neutral-800 rounded-xl shadow-lg overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<thead class="text-xs text-gray-700 uppercase bg-gray-300 dark:bg-neutral-800/50 dark:text-gray-400">
<tr>
<th scope="col" class="px-6 py-3">Nombre</th>
<th scope="col" class="px-6 py-3 text-right">Acciones</th>
</tr>
</thead>
<tbody>
@forelse($tags as $tag)
<tr class="bg-gray-200 dark:bg-neutral-900/50 border-b border-gray-300 dark:border-neutral-800 hover:bg-gray-300 dark:hover:bg-neutral-800/80 transition-colors">
<td class="px-6 py-4 font-medium text-gray-900 dark:text-white">
<form action="{{ route('tags.update', $tag) }}" method="POST" class="flex gap-2 items-center" id="form-edit-{{ $tag->id }}">
@csrf
@method('PUT')
<input type="text" name="name" value="{{ $tag->name }}" class="bg-transparent border-b border-transparent focus:border-neon-lime focus:ring-0 text-sm p-1 rounded-sm text-gray-900 dark:text-white w-full max-w-xs transition-colors" required>
</form>
</td>
<td class="px-6 py-4 text-right space-x-2 whitespace-nowrap">
<button type="button" onclick="document.getElementById('form-edit-{{ $tag->id }}').submit()" class="font-medium text-blue-600 dark:text-blue-500 hover:underline">Guardar</button>
<form action="{{ route('tags.destroy', $tag) }}" method="POST" class="inline-block">
@csrf
@method('DELETE')
<button type="submit" class="font-medium text-red-600 dark:text-red-500 hover:underline" onclick="return confirm('¿Seguro que deseas eliminar esta etiqueta?')">Eliminar</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="2" class="px-6 py-8 text-center text-gray-500 dark:text-gray-400">
No hay etiquetas creadas.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</x-layout>
+11 -4
View File
@@ -1,10 +1,17 @@
<x-layout title="Home - Lauck">
<x-section-header subtitle="Inicio" title="Bicicleteria " highlight="Lauck"/>
<!-- CARROUSEL DESTACADO -->
<div class="w-full mb-12">
<!-- datos del controlador -->
<x-ui.carrousel :items="$destacados" />
<!-- CARROUSELES POR ETIQUETA -->
<div class="w-full mb-12 space-y-16">
@forelse($tagsConProductos as $tag)
<div>
<x-ui.carrousel :items="$tag->products" title="{{ $tag->name }}" />
</div>
@empty
<div class="text-center py-12 bg-slate-100 dark:bg-panel-bg rounded-xl border border-neutral-200 dark:border-neutral-800">
<p class="text-gray-500 dark:text-gray-400 font-medium">Aún no hay productos etiquetados para mostrar.</p>
</div>
@endforelse
</div>
</x-layout>
+2
View File
@@ -15,6 +15,7 @@ use App\Http\Controllers\AppointmentController;
use App\Http\Controllers\SupplierController;
use App\Http\Controllers\PasswordResetLinkController;
use App\Http\Controllers\NewPasswordController;
use App\Http\Controllers\TagController;
use App\Models\Product;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
@@ -61,6 +62,7 @@ Route::middleware(['auth'])->group(function () {
Route::resource('suppliers', SupplierController::class);
Route::resource('expenses', \App\Http\Controllers\ExpenseController::class);
Route::get('reports', [\App\Http\Controllers\ReportController::class, 'index'])->name('reports.index');
Route::resource('tags', TagController::class)->except(['create', 'show']);
Route::resource('productos', ProductosController::class)->parameters([
'productos' => 'product'
]);