Claude Code Plugins

Community-maintained marketplace

Feedback
27
0

Componentes de UI para produtos seguindo o padrão de components do Easy Budget.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name product-ui-components
description Componentes de UI para produtos seguindo o padrão de components do Easy Budget.

Componentes de UI para Produtos

Esta skill define os componentes Blade específicos para a gestão de produtos no Easy Budget, seguindo o padrão de components estabelecido no sistema.

Estrutura de Components

resources/views/components/
├── product/
│   ├── product-card.blade.php        # Card resumido de produto
│   ├── product-details.blade.php     # Detalhes completos do produto
│   ├── product-form.blade.php        # Formulário de criação/edição
│   ├── product-status.blade.php      # Badge de status do produto
│   ├── product-inventory.blade.php   # Informações de estoque
│   ├── product-actions.blade.php     # Ações disponíveis para produto
│   ├── product-filters.blade.php     # Filtros específicos para produtos
│   ├── product-pricing.blade.php     # Informações de preço
│   ├── product-image.blade.php       # Exibição de imagem do produto
│   └── product-summary.blade.php     # Resumo do produto
└── ...

1. Product Card Component

Componente para exibição resumida de produtos em listas e dashboards.

Uso Básico

<x-product.product-card :product="$product" :showCategory="true" :showStock="true" />

Parâmetros

Parâmetro Tipo Descrição Padrão
product Product Modelo do produto Obrigatório
showCategory bool Exibir categoria do produto true
showStock bool Exibir quantidade em estoque true
variant string Estilo do card (primary, secondary, etc.) primary

Estrutura

@props([
    'product',
    'showCategory' => true,
    'showStock' => true,
    'variant' => 'primary'
])

<div class="card border-0 shadow-sm h-100">
    <div class="card-body">
        <div class="d-flex justify-content-between align-items-start mb-3">
            <div class="d-flex align-items-center gap-3">
                <div class="avatar-circle bg-{{ $variant }} bg-gradient">
                    @if($product->image)
                        <img src="{{ asset('storage/' . $product->image) }}" alt="{{ $product->name }}" class="w-100 h-100 object-fit-cover rounded">
                    @else
                        <i class="bi bi-box text-white"></i>
                    @endif
                </div>
                <div>
                    <h6 class="mb-1 fw-bold">{{ $product->name }}</h6>
                    @if($product->code)
                        <small class="text-muted">Código: {{ $product->code }}</small>
                    @endif
                </div>
            </div>
            <x-product.product-status :product="$product" />
        </div>

        @if($showCategory && $product->category)
            <div class="mb-3">
                <span class="badge bg-secondary">{{ $product->category->name }}</span>
            </div>
        @endif

        <div class="mb-3">
            <small class="text-muted">Preço de Venda</small>
            <div class="fw-bold text-{{ $variant }}">
                R$ {{ number_format($product->price, 2, ',', '.') }}
            </div>
        </div>

        @if($showStock)
            <x-product.product-inventory :product="$product" />
        @endif

        <div class="d-flex justify-content-between align-items-center mt-3">
            <div>
                <small class="text-muted">Última Atualização</small>
                <div class="fw-semibold">{{ $product->updated_at->format('d/m/Y H:i') }}</div>
            </div>

            <div class="btn-group btn-group-sm" role="group">
                <a href="{{ route('provider.products.show', $product) }}" class="btn btn-outline-primary">
                    <i class="bi bi-eye"></i> Ver
                </a>
                <a href="{{ route('provider.products.edit', $product) }}" class="btn btn-outline-secondary">
                    <i class="bi bi-pencil"></i> Editar
                </a>
            </div>
        </div>
    </div>
</div>

2. Product Details Component

Componente para exibição detalhada de informações do produto.

Uso Básico

<x-product.product-details :product="$product" :showInventory="true" :showPricing="true" />

Parâmetros

Parâmetro Tipo Descrição Padrão
product Product Modelo do produto Obrigatório
showInventory bool Exibir informações de estoque true
showPricing bool Exibir informações de preço true
collapsible bool Permitir colapsar seções false

Estrutura

@props([
    'product',
    'showInventory' => true,
    'showPricing' => true,
    'collapsible' => false
])

<div class="product-details">
    <!-- Informações Básicas -->
    <div class="row g-3 mb-3">
        <div class="col-md-4">
            <div class="info-item">
                <label class="text-muted small">Nome</label>
                <div class="fw-bold">{{ $product->name }}</div>
            </div>
        </div>
        <div class="col-md-4">
            <div class="info-item">
                <label class="text-muted small">Código</label>
                <div class="fw-bold">{{ $product->code ?? 'Não definido' }}</div>
            </div>
        </div>
        <div class="col-md-4">
            <div class="info-item">
                <label class="text-muted small">Status</label>
                <x-product.product-status :product="$product" />
            </div>
        </div>
    </div>

    <!-- Descrição -->
    @if($product->description)
        <div class="row mb-3">
            <div class="col-12">
                <div class="card border-0">
                    <div class="card-header bg-transparent border-0">
                        <h6 class="mb-0">Descrição</h6>
                    </div>
                    <div class="card-body">
                        <p class="mb-0">{{ $product->description }}</p>
                    </div>
                </div>
            </div>
        </div>
    @endif

    <!-- Categoria -->
    @if($product->category)
        <div class="row mb-3">
            <div class="col-12">
                <div class="card border-0">
                    <div class="card-header bg-transparent border-0">
                        <h6 class="mb-0">Categoria</h6>
                    </div>
                    <div class="card-body">
                        <div class="d-flex justify-content-between align-items-center">
                            <div>
                                <span class="badge bg-secondary">{{ $product->category->name }}</span>
                                @if($product->category->parent)
                                    <small class="text-muted ms-2">Subcategoria de {{ $product->category->parent->name }}</small>
                                @endif
                            </div>
                            <a href="{{ route('provider.categories.show', $product->category) }}" class="btn btn-outline-primary btn-sm">
                                <i class="bi bi-eye"></i> Ver Categoria
                            </a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    @endif

    <!-- Preço -->
    @if($showPricing)
        <x-product.product-pricing :product="$product" />
    @endif

    <!-- Estoque -->
    @if($showInventory)
        <x-product.product-inventory :product="$product" />
    @endif

    <!-- Imagem -->
    @if($product->image)
        <x-product.product-image :product="$product" />
    @endif

    <!-- Resumo -->
    <x-product.product-summary :product="$product" />
</div>

3. Product Status Component

Componente para exibição do status do produto com cores e ícones apropriados.

Uso Básico

<x-product.product-status :product="$product" :showIcon="true" />

Parâmetros

Parâmetro Tipo Descrição Padrão
product Product Modelo do produto Obrigatório
showIcon bool Exibir ícone ao lado do status true
size string Tamanho do badge (sm, md, lg) md

Estrutura

@props([
    'product',
    'showIcon' => true,
    'size' => 'md'
])

@php
    $status = $product->active ? 'active' : 'inactive';
    $color = $product->active ? '#198754' : '#6c757d';
    $icon = $product->active ? 'check-circle' : 'x-circle';
    $description = $product->active ? 'Ativo' : 'Inativo';

    $sizeClass = match($size) {
        'sm' => 'badge-sm',
        'lg' => 'badge-lg',
        default => 'badge-md'
    };
@endphp

<span class="badge modern-badge {{ $sizeClass }}"
      style="background-color: {{ $color }}20; color: {{ $color }}; border: 1px solid {{ $color }}40;">
    @if($showIcon)
        <i class="bi bi-{{ $icon }} me-1"></i>
    @endif
    {{ $description }}
</span>

4. Product Inventory Component

Componente para exibição de informações de estoque do produto.

Uso Básico

<x-product.product-inventory :product="$product" :showMovements="true" />

Parâmetros

Parâmetro Tipo Descrição Padrão
product Product Modelo do produto Obrigatório
showMovements bool Exibir movimentações recentes true
showAlerts bool Exibir alertas de estoque true

Estrutura

@props([
    'product',
    'showMovements' => true,
    'showAlerts' => true
])

<div class="product-inventory">
    <div class="card border-0 shadow-sm">
        <div class="card-header bg-transparent border-0">
            <h6 class="mb-0">Controle de Estoque</h6>
        </div>
        <div class="card-body">
            <div class="row">
                <div class="col-md-4">
                    <div class="mb-3">
                        <label class="text-muted small">Quantidade Atual</label>
                        <div class="fw-bold fs-4 text-primary">{{ $product->inventory->quantity ?? 0 }}</div>
                    </div>
                </div>
                <div class="col-md-4">
                    <div class="mb-3">
                        <label class="text-muted small">Estoque Mínimo</label>
                        <div class="fw-bold">{{ $product->inventory->min_quantity ?? 0 }}</div>
                    </div>
                </div>
                <div class="col-md-4">
                    <div class="mb-3">
                        <label class="text-muted small">Estoque Máximo</label>
                        <div class="fw-bold">{{ $product->inventory->max_quantity ?? 'Ilimitado' }}</div>
                    </div>
                </div>
            </div>

            @if($showAlerts)
                <!-- Alertas de Estoque -->
                @php
                    $currentStock = $product->inventory->quantity ?? 0;
                    $minStock = $product->inventory->min_quantity ?? 0;
                    $maxStock = $product->inventory->max_quantity;
                @endphp

                @if($currentStock <= $minStock)
                    <div class="alert alert-warning d-flex align-items-center" role="alert">
                        <i class="bi bi-exclamation-triangle-fill me-2"></i>
                        <div>
                            <strong>Estoque Baixo!</strong> Quantidade atual está abaixo do mínimo recomendado.
                        </div>
                    </div>
                @elseif($maxStock && $currentStock >= $maxStock)
                    <div class="alert alert-info d-flex align-items-center" role="alert">
                        <i class="bi bi-info-circle-fill me-2"></i>
                        <div>
                            <strong>Estoque Cheio!</strong> Quantidade atual está no limite máximo.
                        </div>
                    </div>
                @endif
            @endif

            @if($showMovements && $product->inventoryMovements->isNotEmpty())
                <div class="mt-3">
                    <h6 class="mb-2">Últimas Movimentações</h6>
                    <div class="table-responsive">
                        <table class="table table-sm">
                            <thead>
                                <tr>
                                    <th>Data</th>
                                    <th>Tipo</th>
                                    <th>Quantidade</th>
                                    <th>Motivo</th>
                                </tr>
                            </thead>
                            <tbody>
                                @foreach($product->inventoryMovements->take(5) as $movement)
                                    <tr>
                                        <td>{{ $movement->created_at->format('d/m/Y H:i') }}</td>
                                        <td>
                                            <span class="badge {{ $movement->type === 'in' ? 'bg-success' : 'bg-danger' }}">
                                                {{ $movement->type === 'in' ? 'Entrada' : 'Saída' }}
                                            </span>
                                        </td>
                                        <td class="{{ $movement->type === 'in' ? 'text-success' : 'text-danger' }}">
                                            {{ $movement->type === 'in' ? '+' : '-' }}{{ $movement->quantity }}
                                        </td>
                                        <td>{{ $movement->reason ?? 'Sem motivo' }}</td>
                                    </tr>
                                @endforeach
                            </tbody>
                        </table>
                    </div>
                </div>
            @endif
        </div>
    </div>
</div>

5. Product Pricing Component

Componente para exibição de informações de preço do produto.

Uso Básico

<x-product.product-pricing :product="$product" :showCost="true" />

Parâmetros

Parâmetro Tipo Descrição Padrão
product Product Modelo do produto Obrigatório
showCost bool Exibir preço de custo (se disponível) true
showProfit bool Exibir margem de lucro true

Estrutura

@props([
    'product',
    'showCost' => true,
    'showProfit' => true
])

<div class="product-pricing">
    <div class="card border-0 shadow-sm">
        <div class="card-header bg-transparent border-0">
            <h6 class="mb-0">Informações de Preço</h6>
        </div>
        <div class="card-body">
            <div class="row">
                <div class="col-md-6">
                    <div class="mb-3">
                        <label class="text-muted small">Preço de Venda</label>
                        <div class="fw-bold fs-3 text-primary">
                            R$ {{ number_format($product->price, 2, ',', '.') }}
                        </div>
                    </div>
                </div>

                @if($showCost)
                    <div class="col-md-6">
                        <div class="mb-3">
                            <label class="text-muted small">Preço de Custo</label>
                            <div class="fw-bold">
                                R$ {{ number_format($product->cost ?? 0, 2, ',', '.') }}
                            </div>
                        </div>
                    </div>
                @endif
            </div>

            @if($showProfit && $product->cost)
                <div class="row">
                    <div class="col-md-6">
                        <div class="mb-3">
                            <label class="text-muted small">Margem de Lucro</label>
                            @php
                                $cost = $product->cost ?? 0;
                                $price = $product->price;
                                $profit = $price - $cost;
                                $margin = $cost > 0 ? ($profit / $cost) * 100 : 0;
                            @endphp
                            <div class="fw-bold {{ $margin >= 20 ? 'text-success' : 'text-warning' }}">
                                {{ number_format($margin, 2, ',', '.') }}%
                            </div>
                        </div>
                    </div>
                    <div class="col-md-6">
                        <div class="mb-3">
                            <label class="text-muted small">Lucro por Unidade</label>
                            <div class="fw-bold text-success">
                                R$ {{ number_format($profit, 2, ',', '.') }}
                            </div>
                        </div>
                    </div>
                </div>
            @endif
        </div>
    </div>
</div>

6. Product Form Component

Componente para formulário de criação e edição de produtos.

Uso Básico

<x-product.product-form :product="$product ?? null" :categories="$categories" :units="$units" />

Parâmetros

Parâmetro Tipo Descrição Padrão
product `Product null` Modelo do produto (para edição)
categories Collection Categorias disponíveis Obrigatório
units Collection Unidades de medida disponíveis Obrigatório
showInventory bool Exibir campos de estoque true

Estrutura

@props([
    'product' => null,
    'categories' => [],
    'units' => [],
    'showInventory' => true
])

<div class="product-form">
    <form action="{{ isset($product) ? route('provider.products.update', $product) : route('provider.products.store') }}"
          method="POST"
          id="productForm"
          enctype="multipart/form-data">
        @csrf
        @if(isset($product)) @method('PUT') @endif

        <!-- Informações Básicas -->
        <div class="row g-3 mb-4">
            <div class="col-12">
                <div class="card border-0 shadow-sm">
                    <div class="card-header bg-transparent border-0">
                        <h6 class="mb-0">Informações Básicas</h6>
                    </div>
                    <div class="card-body">
                        <div class="row">
                            <div class="col-md-8">
                                <label class="form-label small fw-bold text-muted text-uppercase">Nome *</label>
                                <input type="text"
                                       name="name"
                                       class="form-control @error('name') is-invalid @enderror"
                                       value="{{ old('name', $product->name ?? '') }}" required>
                                @error('name')
                                    <div class="invalid-feedback">{{ $message }}</div>
                                @enderror
                            </div>
                            <div class="col-md-4">
                                <label class="form-label small fw-bold text-muted text-uppercase">Código</label>
                                <input type="text"
                                       name="code"
                                       class="form-control @error('code') is-invalid @enderror"
                                       value="{{ old('code', $product->code ?? '') }}">
                                @error('code')
                                    <div class="invalid-feedback">{{ $message }}</div>
                                @enderror
                            </div>
                            <div class="col-12">
                                <label class="form-label small fw-bold text-muted text-uppercase">Descrição</label>
                                <textarea name="description"
                                          class="form-control @error('description') is-invalid @enderror"
                                          rows="3"
                                          placeholder="Descrição do produto...">{{ old('description', $product->description ?? '') }}</textarea>
                                @error('description')
                                    <div class="invalid-feedback">{{ $message }}</div>
                                @enderror
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Categoria e Preço -->
        <div class="row g-3 mb-4">
            <div class="col-md-6">
                <div class="card border-0 shadow-sm">
                    <div class="card-header bg-transparent border-0">
                        <h6 class="mb-0">Categoria</h6>
                    </div>
                    <div class="card-body">
                        <label class="form-label small fw-bold text-muted text-uppercase">Categoria *</label>
                        <select name="category_id" class="form-select @error('category_id') is-invalid @enderror">
                            <option value="">Selecione uma categoria</option>
                            @foreach($categories as $category)
                                <option value="{{ $category->id }}"
                                        {{ (old('category_id', $product->category_id ?? '') == $category->id) ? 'selected' : '' }}>
                                    {{ $category->name }}
                                </option>
                            @endforeach
                        </select>
                        @error('category_id')
                            <div class="invalid-feedback">{{ $message }}</div>
                        @enderror
                    </div>
                </div>
            </div>

            <div class="col-md-6">
                <div class="card border-0 shadow-sm">
                    <div class="card-header bg-transparent border-0">
                        <h6 class="mb-0">Preço</h6>
                    </div>
                    <div class="card-body">
                        <div class="row">
                            <div class="col-12">
                                <label class="form-label small fw-bold text-muted text-uppercase">Preço de Venda *</label>
                                <input type="number"
                                       name="price"
                                       step="0.01"
                                       min="0"
                                       class="form-control @error('price') is-invalid @enderror"
                                       value="{{ old('price', $product->price ?? '') }}" required>
                                @error('price')
                                    <div class="invalid-feedback">{{ $message }}</div>
                                @enderror
                            </div>
                            <div class="col-12 mt-3">
                                <label class="form-label small fw-bold text-muted text-uppercase">Preço de Custo</label>
                                <input type="number"
                                       name="cost"
                                       step="0.01"
                                       min="0"
                                       class="form-control @error('cost') is-invalid @enderror"
                                       value="{{ old('cost', $product->cost ?? '') }}">
                                @error('cost')
                                    <div class="invalid-feedback">{{ $message }}</div>
                                @enderror
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Estoque -->
        @if($showInventory)
            <div class="row g-3 mb-4">
                <div class="col-12">
                    <div class="card border-0 shadow-sm">
                        <div class="card-header bg-transparent border-0">
                            <h6 class="mb-0">Controle de Estoque</h6>
                        </div>
                        <div class="card-body">
                            <div class="row">
                                <div class="col-md-4">
                                    <label class="form-label small fw-bold text-muted text-uppercase">Quantidade Inicial</label>
                                    <input type="number"
                                           name="initial_quantity"
                                           class="form-control @error('initial_quantity') is-invalid @enderror"
                                           value="{{ old('initial_quantity', 0) }}">
                                    @error('initial_quantity')
                                        <div class="invalid-feedback">{{ $message }}</div>
                                    @enderror
                                </div>
                                <div class="col-md-4">
                                    <label class="form-label small fw-bold text-muted text-uppercase">Estoque Mínimo</label>
                                    <input type="number"
                                           name="min_quantity"
                                           class="form-control @error('min_quantity') is-invalid @enderror"
                                           value="{{ old('min_quantity', $product->inventory->min_quantity ?? 0) }}">
                                    @error('min_quantity')
                                        <div class="invalid-feedback">{{ $message }}</div>
                                    @enderror
                                </div>
                                <div class="col-md-4">
                                    <label class="form-label small fw-bold text-muted text-uppercase">Estoque Máximo</label>
                                    <input type="number"
                                           name="max_quantity"
                                           class="form-control @error('max_quantity') is-invalid @enderror"
                                           value="{{ old('max_quantity', $product->inventory->max_quantity ?? '') }}">
                                    @error('max_quantity')
                                        <div class="invalid-feedback">{{ $message }}</div>
                                    @enderror
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        @endif

        <!-- Imagem -->
        <div class="row g-3 mb-4">
            <div class="col-12">
                <div class="card border-0 shadow-sm">
                    <div class="card-header bg-transparent border-0">
                        <h6 class="mb-0">Imagem do Produto</h6>
                    </div>
                    <div class="card-body">
                        <div class="row">
                            <div class="col-md-6">
                                <label class="form-label small fw-bold text-muted text-uppercase">Imagem</label>
                                <input type="file"
                                       name="image"
                                       class="form-control @error('image') is-invalid @enderror"
                                       accept="image/*">
                                @error('image')
                                    <div class="invalid-feedback">{{ $message }}</div>
                                @enderror
                                @if(isset($product) && $product->image)
                                    <div class="mt-2">
                                        <img src="{{ asset('storage/' . $product->image) }}"
                                             alt="{{ $product->name }}"
                                             class="img-thumbnail"
                                             style="max-width: 200px;">
                                        <div class="form-check mt-2">
                                            <input type="checkbox" name="remove_image" value="1" class="form-check-input">
                                            <label class="form-check-label">Remover imagem atual</label>
                                        </div>
                                    </div>
                                @endif
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Status -->
        <div class="row g-3 mb-4">
            <div class="col-12">
                <div class="card border-0 shadow-sm">
                    <div class="card-header bg-transparent border-0">
                        <h6 class="mb-0">Status</h6>
                    </div>
                    <div class="card-body">
                        <div class="form-check form-switch">
                            <input type="checkbox"
                                   name="active"
                                   class="form-check-input"
                                   id="productActive"
                                   {{ (old('active', $product->active ?? true)) ? 'checked' : '' }}>
                            <label class="form-check-label" for="productActive">
                                Produto Ativo
                            </label>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Ações -->
        <div class="d-flex justify-content-between">
            <a href="{{ route('provider.products.index') }}" class="btn btn-outline-secondary">
                <i class="bi bi-arrow-left me-2"></i>Cancelar
            </a>
            <button type="submit" class="btn btn-primary">
                <i class="bi bi-check-circle me-2"></i>{{ isset($product) ? 'Atualizar' : 'Criar' }} Produto
            </button>
        </div>
    </form>
</div>

@push('scripts')
<script>
document.addEventListener('DOMContentLoaded', function() {
    // Formatar preço
    const priceInput = document.querySelector('input[name="price"]');
    const costInput = document.querySelector('input[name="cost"]');

    function formatCurrency(input) {
        if (input) {
            input.addEventListener('input', function() {
                let value = this.value.replace(/\D/g, '');
                value = (parseInt(value) / 100).toFixed(2);
                this.value = value;
            });
        }
    }

    formatCurrency(priceInput);
    formatCurrency(costInput);
});
</script>
@endpush

7. Product Actions Component

Componente para exibição de ações disponíveis para um produto.

Uso Básico

<x-product.product-actions :product="$product" :showInventory="true" :showEdit="true" />

Parâmetros

Parâmetro Tipo Descrição Padrão
product Product Modelo do produto Obrigatório
showInventory bool Exibir botão de controle de estoque true
showEdit bool Exibir botão de edição true
showDelete bool Exibir botão de exclusão true

Estrutura

@props([
    'product',
    'showInventory' => true,
    'showEdit' => true,
    'showDelete' => true
])

<div class="product-actions btn-group" role="group">
    <!-- Visualizar -->
    <a href="{{ route('provider.products.show', $product) }}"
       class="btn btn-outline-primary"
       title="Visualizar Produto">
        <i class="bi bi-eye"></i> Visualizar
    </a>

    <!-- Editar -->
    @if($showEdit)
        <a href="{{ route('provider.products.edit', $product) }}"
           class="btn btn-outline-secondary"
           title="Editar Produto">
            <i class="bi bi-pencil"></i> Editar
        </a>
    @endif

    <!-- Controle de Estoque -->
    @if($showInventory)
        <button type="button"
                class="btn btn-outline-info"
                data-bs-toggle="modal"
                data-bs-target="#inventoryModal-{{ $product->id }}"
                title="Controle de Estoque">
            <i class="bi bi-box-seam"></i> Estoque
        </button>
    @endif

    <!-- Alternar Status -->
    <button type="button"
            class="btn btn-outline-{{ $product->active ? 'warning' : 'success' }}"
            data-bs-toggle="modal"
            data-bs-target="#statusModal-{{ $product->id }}"
            title="{{ $product->active ? 'Desativar' : 'Ativar' }} Produto">
        <i class="bi bi-{{ $product->active ? 'toggle-on' : 'toggle-off' }}"></i>
        {{ $product->active ? 'Desativar' : 'Ativar' }}
    </button>

    <!-- Excluir -->
    @if($showDelete)
        <button type="button"
                class="btn btn-outline-danger"
                data-bs-toggle="modal"
                data-bs-target="#deleteModal-{{ $product->id }}"
                title="Excluir Produto">
            <i class="bi bi-trash"></i> Excluir
        </button>
    @endif
</div>

8. Product Filters Component

Componente para filtros específicos de listagem de produtos.

Uso Básico

<x-product.product-filters :filters="$filters" :showCategory="true" :showStatus="true" />

Parâmetros

Parâmetro Tipo Descrição Padrão
filters array Filtros atuais []
showCategory bool Exibir filtro por categoria true
showStatus bool Exibir filtro por status true
showPriceRange bool Exibir filtro por faixa de preço true

Estrutura

@props([
    'filters' => [],
    'showCategory' => true,
    'showStatus' => true,
    'showPriceRange' => true
])

<div class="product-filters">
    <form action="{{ request()->url() }}" method="GET" class="row g-3">
        @if($showCategory)
            <div class="col-md-3">
                <label class="form-label small fw-bold text-muted text-uppercase">Categoria</label>
                <select name="category_id" class="form-select form-select-sm">
                    <option value="">Todas as categorias</option>
                    @foreach($categories as $category)
                        <option value="{{ $category->id }}" {{ ($filters['category_id'] ?? '') == $category->id ? 'selected' : '' }}>
                            {{ $category->name }}
                        </option>
                    @endforeach
                </select>
            </div>
        @endif

        @if($showStatus)
            <div class="col-md-3">
                <label class="form-label small fw-bold text-muted text-uppercase">Status</label>
                <select name="active" class="form-select form-select-sm">
                    <option value="">Todos os status</option>
                    <option value="1" {{ ($filters['active'] ?? '') == '1' ? 'selected' : '' }}>Ativos</option>
                    <option value="0" {{ ($filters['active'] ?? '') == '0' ? 'selected' : '' }}>Inativos</option>
                </select>
            </div>
        @endif

        @if($showPriceRange)
            <div class="col-md-3">
                <label class="form-label small fw-bold text-muted text-uppercase">Faixa de Preço</label>
                <div class="input-group input-group-sm">
                    <input type="number"
                           name="min_price"
                           step="0.01"
                           class="form-control"
                           value="{{ $filters['min_price'] ?? '' }}"
                           placeholder="Mínimo">
                    <span class="input-group-text">até</span>
                    <input type="number"
                           name="max_price"
                           step="0.01"
                           class="form-control"
                           value="{{ $filters['max_price'] ?? '' }}"
                           placeholder="Máximo">
                </div>
            </div>
        @endif

        <div class="col-md-3">
            <label class="form-label small fw-bold text-muted text-uppercase">Busca</label>
            <input type="text"
                   name="search"
                   class="form-control form-control-sm"
                   value="{{ $filters['search'] ?? '' }}"
                   placeholder="Buscar por nome...">
        </div>

        <div class="col-12">
            <div class="d-flex justify-content-between">
                <div class="btn-group" role="group">
                    <button type="submit" class="btn btn-primary btn-sm">
                        <i class="bi bi-search me-2"></i>Filtrar
                    </button>
                    <a href="{{ route('provider.products.index') }}" class="btn btn-outline-secondary btn-sm">
                        <i class="bi bi-x-circle me-2"></i>Limpar
                    </a>
                </div>

                <div class="btn-group" role="group">
                    <button type="button" class="btn btn-outline-success btn-sm" data-bs-toggle="modal" data-bs-target="#createModal">
                        <i class="bi bi-plus-circle me-2"></i>Novo Produto
                    </button>
                </div>
            </div>
        </div>
    </form>
</div>

9. Product Image Component

Componente para exibição de imagem do produto.

Uso Básico

<x-product.product-image :product="$product" :showThumbnail="true" />

Parâmetros

Parâmetro Tipo Descrição Padrão
product Product Modelo do produto Obrigatório
showThumbnail bool Exibir miniatura true
size string Tamanho da imagem (small, medium, large) medium

Estrutura

@props([
    'product',
    'showThumbnail' => true,
    'size' => 'medium'
])

<div class="product-image">
    @if($product->image)
        <div class="card border-0 shadow-sm">
            <div class="card-header bg-transparent border-0">
                <h6 class="mb-0">Imagem do Produto</h6>
            </div>
            <div class="card-body text-center">
                @php
                    $sizeClass = match($size) {
                        'small' => 'img-thumbnail',
                        'large' => 'img-fluid',
                        default => 'img-thumbnail'
                    };
                @endphp

                <img src="{{ asset('storage/' . $product->image) }}"
                     alt="{{ $product->name }}"
                     class="{{ $sizeClass }}"
                     style="max-width: 300px; height: auto;">

                <div class="mt-3">
                    <small class="text-muted">Última atualização: {{ $product->updated_at->format('d/m/Y H:i') }}</small>
                </div>
            </div>
        </div>
    @else
        <div class="card border-0 shadow-sm">
            <div class="card-header bg-transparent border-0">
                <h6 class="mb-0">Imagem do Produto</h6>
            </div>
            <div class="card-body text-center">
                <div class="avatar-circle bg-secondary bg-gradient mb-3 mx-auto" style="width: 100px; height: 100px;">
                    <i class="bi bi-box text-white" style="font-size: 2rem;"></i>
                </div>
                <p class="text-muted">Nenhuma imagem cadastrada</p>
            </div>
        </div>
    @endif
</div>

10. Product Summary Component

Componente para exibição de resumo do produto.

Uso Básico

<x-product.product-summary :product="$product" :showStats="true" />

Parâmetros

Parâmetro Tipo Descrição Padrão
product Product Modelo do produto Obrigatório
showStats bool Exibir estatísticas true
showCategory bool Exibir informações da categoria true

Estrutura

@props([
    'product',
    'showStats' => true,
    'showCategory' => true
])

<div class="product-summary">
    <div class="row g-3">
        @if($showStats)
            <!-- Estatísticas -->
            <div class="col-md-4">
                <div class="card border-0 shadow-sm">
                    <div class="card-body text-center">
                        <div class="avatar-circle bg-primary bg-gradient mb-2 mx-auto">
                            <i class="bi bi-box-seam text-white"></i>
                        </div>
                        <div class="text-muted small">Estoque Atual</div>
                        <div class="h4 mb-0 fw-bold">{{ $product->inventory->quantity ?? 0 }}</div>
                    </div>
                </div>
            </div>

            <div class="col-md-4">
                <div class="card border-0 shadow-sm">
                    <div class="card-body text-center">
                        <div class="avatar-circle bg-success bg-gradient mb-2 mx-auto">
                            <i class="bi bi-currency-dollar text-white"></i>
                        </div>
                        <div class="text-muted small">Preço de Venda</div>
                        <div class="h4 mb-0 fw-bold">
                            R$ {{ number_format($product->price, 2, ',', '.') }}
                        </div>
                    </div>
                </div>
            </div>

            <div class="col-md-4">
                <div class="card border-0 shadow-sm">
                    <div class="card-body text-center">
                        <div class="avatar-circle bg-info bg-gradient mb-2 mx-auto">
                            <i class="bi bi-graph-up text-white"></i>
                        </div>
                        <div class="text-muted small">Movimentações</div>
                        <div class="h4 mb-0 fw-bold">{{ $product->inventoryMovements->count() }}</div>
                    </div>
                </div>
            </div>
        @endif
    </div>

    @if($showCategory && $product->category)
        <div class="row mt-3">
            <div class="col-12">
                <div class="card border-0 shadow-sm">
                    <div class="card-header bg-transparent border-0">
                        <h6 class="mb-0">Categoria</h6>
                    </div>
                    <div class="card-body">
                        <div class="d-flex w-100 justify-content-between align-items-center">
                            <div>
                                <h6 class="mb-1">{{ $product->category->name }}</h6>
                                <p class="mb-0 text-muted small">
                                    @if($product->category->parent)
                                        Subcategoria de {{ $product->category->parent->name }}
                                    @else
                                        Categoria principal
                                    @endif
                                </p>
                            </div>
                            <div class="text-end">
                                <span class="badge bg-secondary">{{ $product->category->products->count() }} produtos</span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    @endif
</div>

11. Integração com Padrões Existentes

Uso em Views

{{-- Dashboard --}}
<x-product.product-card :product="$product" />

{{-- Listagem --}}
<x-product.product-details :product="$product" />

{{-- Formulários --}}
<x-product.product-form :product="$product" :categories="$categories" :units="$units" />

{{-- Ações --}}
<x-product.product-actions :product="$product" />

Estilos CSS

/* Product Components Styles */
.product-form .form-control:focus {
    border-color: #0d6efd;
    box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25);
}

.product-actions .btn {
    transition: all 0.2s ease;
}

.product-actions .btn:hover {
    transform: translateY(-1px);
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.product-summary .card {
    border-left: 4px solid #0d6efd;
}

.product-inventory .alert {
    border-left: 4px solid;
}

.product-inventory .alert-warning {
    border-left-color: #ffc107;
}

.product-inventory .alert-info {
    border-left-color: #0dcaf0;
}

12. JavaScript Interatividade

Formulário de Produto

// product-form.js
document.addEventListener('DOMContentLoaded', function() {
    // Formatar campos de preço
    const priceInputs = document.querySelectorAll('input[name="price"], input[name="cost"]');

    priceInputs.forEach(input => {
        if (input) {
            input.addEventListener('input', function() {
                let value = this.value.replace(/\D/g, '');
                if (value.length > 2) {
                    value = value.replace(/(\d{2})$/, '.$1');
                }
                this.value = value;
            });
        }
    });

    // Validar estoque
    const minQuantityInput = document.querySelector('input[name="min_quantity"]');
    const maxQuantityInput = document.querySelector('input[name="max_quantity"]');

    if (minQuantityInput && maxQuantityInput) {
        minQuantityInput.addEventListener('change', function() {
            if (parseInt(this.value) > parseInt(maxQuantityInput.value)) {
                maxQuantityInput.value = this.value;
            }
        });
    }
});

13. Validação e Segurança

Autorização

// ProductPolicy.php
public function view(User $user, Product $product)
{
    return $user->tenant_id === $product->tenant_id;
}

public function update(User $user, Product $product)
{
    return $user->tenant_id === $product->tenant_id;
}

public function delete(User $user, Product $product)
{
    return $user->tenant_id === $product->tenant_id &&
           $product->budgetItems->isEmpty() &&
           $product->serviceItems->isEmpty();
}

Validations

{{-- Product Card com validação de permissões --}}
@can('view', $product)
    <x-product.product-card :product="$product" />
@endcan

{{-- Product Actions com validação de status --}}
@can('update', $product)
    <x-product.product-actions :product="$product" />
@endcan

Este padrão de components para produtos garante consistência visual, reutilização de código e manutenibilidade, seguindo os mesmos princípios estabelecidos nos outros components do sistema.