Awesome-omni-skill budget-ui-components
Componentes de UI para orçamentos seguindo o padrão de components do Easy Budget.
git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/design/budget-ui-components" ~/.claude/skills/diegosouzapw-awesome-omni-skill-budget-ui-components && rm -rf "$T"
skills/design/budget-ui-components/SKILL.mdComponentes de UI para Orçamentos
Esta skill define os componentes Blade específicos para a gestão de orçamentos no Easy Budget, seguindo o padrão de components estabelecido no sistema.
Estrutura de Components
resources/views/components/ ├── budget/ │ ├── budget-card.blade.php # Card resumido de orçamento │ ├── budget-details.blade.php # Detalhes completos do orçamento │ ├── budget-actions.blade.php # Ações disponíveis para orçamento │ ├── budget-status.blade.php # Badge de status do orçamento │ ├── budget-totals.blade.php # Totais e valores do orçamento │ ├── budget-items.blade.php # Lista de itens/serviços do orçamento │ ├── budget-filters.blade.php # Filtros específicos para orçamentos │ └── budget-quick-stats.blade.php # Estatísticas rápidas de orçamentos └── ...
1. Budget Card Component
Componente para exibição resumida de orçamentos em listas e dashboards.
Uso Básico
<x-budget.budget-card :budget="$budget" :showCustomer="true" :showActions="true" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Modelo do orçamento | Obrigatório |
| | Exibir informações do cliente | |
| | Exibir botões de ação | |
| | Estilo do card (primary, secondary, etc.) | |
Estrutura
@props([ 'budget', 'showCustomer' => true, 'showActions' => 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"> <i class="bi bi-file-earmark-text text-white"></i> </div> <div> <h6 class="mb-1 fw-bold">{{ $budget->code }}</h6> <small class="text-muted">{{ $budget->created_at->format('d/m/Y') }}</small> </div> </div> <x-budget.budget-status :budget="$budget" /> </div> @if($showCustomer && $budget->customer) <div class="mb-3"> <small class="text-muted">Cliente:</small> <div class="fw-semibold">{{ $budget->customer->display_name }}</div> </div> @endif <div class="d-flex justify-content-between align-items-center"> <div> <small class="text-muted">Valor Total</small> <div class="h6 mb-0 fw-bold text-{{ $variant }}"> R$ {{ number_format($budget->total, 2, ',', '.') }} </div> </div> @if($showActions) <div class="btn-group btn-group-sm" role="group"> <a href="{{ route('provider.budgets.show', $budget) }}" class="btn btn-outline-primary"> <i class="bi bi-eye"></i> Ver </a> <a href="{{ route('provider.budgets.edit', $budget) }}" class="btn btn-outline-secondary"> <i class="bi bi-pencil"></i> Editar </a> </div> @endif </div> </div> </div>
2. Budget Details Component
Componente para exibição detalhada de informações do orçamento.
Uso Básico
<x-budget.budget-details :budget="$budget" :showServices="true" :showTotals="true" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Modelo do orçamento | Obrigatório |
| | Exibir serviços vinculados | |
| | Exibir totais detalhados | |
| | Permitir colapsar seções | |
Estrutura
@props([ 'budget', 'showServices' => true, 'showTotals' => true, 'collapsible' => false ]) <div class="budget-details"> <!-- Informações Básicas --> <div class="row g-3 mb-3"> <div class="col-md-3"> <div class="info-item"> <label class="text-muted small">Código</label> <div class="fw-bold">{{ $budget->code }}</div> </div> </div> <div class="col-md-3"> <div class="info-item"> <label class="text-muted small">Data</label> <div class="fw-bold">{{ $budget->created_at->format('d/m/Y H:i') }}</div> </div> </div> <div class="col-md-3"> <div class="info-item"> <label class="text-muted small">Validade</label> <div class="fw-bold">{{ $budget->due_date?->format('d/m/Y') ?? 'Indeterminado' }}</div> </div> </div> <div class="col-md-3"> <div class="info-item"> <label class="text-muted small">Status</label> <x-budget.budget-status :budget="$budget" /> </div> </div> </div> <!-- Descrição --> @if($budget->description) <div class="row mb-3"> <div class="col-12"> <div class="card border-0"> <div class="card-body"> <label class="text-muted small">Descrição</label> <p class="mb-0">{{ $budget->description }}</p> </div> </div> </div> </div> @endif <!-- Serviços --> @if($showServices && $budget->services->isNotEmpty()) <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">Serviços Vinculados</h6> </div> <div class="card-body p-0"> <div class="table-responsive"> <table class="table table-hover mb-0"> <thead> <tr> <th>Serviço</th> <th>Categoria</th> <th class="text-end">Valor</th> </tr> </thead> <tbody> @foreach($budget->services as $service) <tr> <td>{{ $service->description ?? $service->category->name }}</td> <td>{{ $service->category->name }}</td> <td class="text-end fw-bold"> R$ {{ number_format($service->total, 2, ',', '.') }} </td> </tr> @endforeach </tbody> </table> </div> </div> </div> </div> </div> @endif <!-- Totais --> @if($showTotals) <x-budget.budget-totals :budget="$budget" /> @endif </div>
3. Budget Status Component
Componente para exibição do status do orçamento com cores e ícones apropriados.
Uso Básico
<x-budget.budget-status :budget="$budget" :showIcon="true" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Modelo do orçamento | Obrigatório |
| | Exibir ícone ao lado do status | |
| | Tamanho do badge (sm, md, lg) | |
Estrutura
@props([ 'budget', 'showIcon' => true, 'size' => 'md' ]) @php $status = $budget->status; $metadata = $status->getMetadata(); $color = $metadata['color'] ?? '#6c757d'; $icon = $metadata['icon'] ?? 'circle'; $description = $metadata['description'] ?? $status->value; $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 {{ $icon }} me-1"></i> @endif {{ $description }} </span>
4. Budget Totals Component
Componente para exibição detalhada dos totais e valores do orçamento.
Uso Básico
<x-budget.budget-totals :budget="$budget" :showBreakdown="true" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Modelo do orçamento | Obrigatório |
| | Exibir detalhamento dos valores | |
| | Estilo visual (primary, success, etc.) | |
Estrutura
@props([ 'budget', 'showBreakdown' => true, 'variant' => 'primary' ]) <div class="budget-totals"> @if($showBreakdown) <div class="row g-3 mb-3"> <div class="col-md-4"> <div class="card border-0"> <div class="card-body text-center"> <div class="text-muted small">Subtotal</div> <div class="h5 mb-0 fw-bold"> R$ {{ number_format($budget->services->sum('total'), 2, ',', '.') }} </div> </div> </div> </div> <div class="col-md-4"> <div class="card border-0"> <div class="card-body text-center"> <div class="text-muted small">Desconto</div> <div class="h5 mb-0 fw-bold text-danger"> - R$ {{ number_format($budget->discount, 2, ',', '.') }} </div> </div> </div> </div> <div class="col-md-4"> <div class="card border-0 bg-{{ $variant }} bg-gradient text-white"> <div class="card-body text-center"> <div class="small">Valor Total</div> <div class="h4 mb-0 fw-bold"> R$ {{ number_format($budget->total, 2, ',', '.') }} </div> </div> </div> </div> </div> @else <div class="card border-0 bg-{{ $variant }} bg-gradient text-white"> <div class="card-body"> <div class="row align-items-center"> <div class="col-md-8"> <div class="small">Valor Total do Orçamento</div> <div class="h3 mb-0 fw-bold"> R$ {{ number_format($budget->total, 2, ',', '.') }} </div> </div> <div class="col-md-4 text-end"> <x-budget.budget-status :budget="$budget" /> </div> </div> </div> </div> @endif </div>
5. Budget Actions Component
Componente para exibição de ações disponíveis para um orçamento.
Uso Básico
<x-budget.budget-actions :budget="$budget" :showShare="true" :showDelete="false" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Modelo do orçamento | Obrigatório |
| | Exibir botão de compartilhamento | |
| | Exibir botão de exclusão | |
| | Exibir botão de impressão | |
Estrutura
@props([ 'budget', 'showShare' => true, 'showDelete' => false, 'showPrint' => true ]) <div class="budget-actions btn-group" role="group"> <!-- Visualizar --> <a href="{{ route('provider.budgets.show', $budget) }}" class="btn btn-outline-primary" title="Visualizar Orçamento"> <i class="bi bi-eye"></i> Visualizar </a> <!-- Editar --> @if($budget->status->isEditable()) <a href="{{ route('provider.budgets.edit', $budget) }}" class="btn btn-outline-secondary" title="Editar Orçamento"> <i class="bi bi-pencil"></i> Editar </a> @endif <!-- Compartilhar --> @if($showShare) <button type="button" class="btn btn-outline-info" data-bs-toggle="modal" data-bs-target="#shareModal-{{ $budget->id }}" title="Compartilhar Orçamento"> <i class="bi bi-share"></i> Compartilhar </button> @endif <!-- Imprimir --> @if($showPrint) <a href="{{ route('provider.budgets.pdf', $budget) }}" class="btn btn-outline-warning" target="_blank" title="Imprimir Orçamento"> <i class="bi bi-printer"></i> Imprimir </a> @endif <!-- Enviar por E-mail --> <button type="button" class="btn btn-outline-success" data-bs-toggle="modal" data-bs-target="#emailModal-{{ $budget->id }}" title="Enviar por E-mail"> <i class="bi bi-envelope"></i> Enviar </button> <!-- Excluir --> @if($showDelete && $budget->canDelete()) <button type="button" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#deleteModal-{{ $budget->id }}" title="Excluir Orçamento"> <i class="bi bi-trash"></i> Excluir </button> @endif </div>
6. Budget Filters Component
Componente para filtros específicos de listagem de orçamentos.
Uso Básico
<x-budget.budget-filters :filters="$filters" :showDateRange="true" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Filtros atuais | |
| | Exibir filtro de período | |
| | Exibir filtro por status | |
| | Exibir filtro por cliente | |
Estrutura
@props([ 'filters' => [], 'showDateRange' => true, 'showStatus' => true, 'showCustomer' => true ]) <div class="budget-filters"> <form action="{{ request()->url() }}" method="GET" class="row g-3"> <div class="col-md-3"> <label class="form-label small fw-bold text-muted text-uppercase">Código</label> <input type="text" name="code" class="form-control form-control-sm" value="{{ $filters['code'] ?? '' }}" placeholder="Código do orçamento"> </div> @if($showCustomer) <div class="col-md-3"> <label class="form-label small fw-bold text-muted text-uppercase">Cliente</label> <select name="customer_id" class="form-select form-select-sm"> <option value="">Todos os clientes</option> @foreach($customers as $customer) <option value="{{ $customer->id }}" {{ ($filters['customer_id'] ?? '') == $customer->id ? 'selected' : '' }}> {{ $customer->display_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="status" class="form-select form-select-sm"> <option value="">Todos os status</option> @foreach(\App\Enums\BudgetStatus::cases() as $status) @php $metadata = $status->getMetadata(); @endphp <option value="{{ $status->value }}" {{ ($filters['status'] ?? '') == $status->value ? 'selected' : '' }}> {{ $metadata['description'] }} </option> @endforeach </select> </div> @endif @if($showDateRange) <div class="col-md-3"> <label class="form-label small fw-bold text-muted text-uppercase">Período</label> <div class="input-group input-group-sm"> <input type="date" name="start_date" class="form-control" value="{{ $filters['start_date'] ?? '' }}"> <span class="input-group-text">até</span> <input type="date" name="end_date" class="form-control" value="{{ $filters['end_date'] ?? '' }}"> </div> </div> @endif <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.budgets.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 Orçamento </button> </div> </div> </div> </form> </div>
7. Budget Quick Stats Component
Componente para exibição de estatísticas rápidas de orçamentos.
Uso Básico
<x-budget.budget-quick-stats :stats="$stats" :showChart="true" />
Parâmetros
| Parâmetro | Tipo | Descrição | Padrão |
|---|---|---|---|
| | Dados estatísticos | Obrigatório |
| | Exibir gráfico rápido | |
| | Período analisado | |
Estrutura
@props([ 'stats', 'showChart' => true, 'timeRange' => 'month' ]) <div class="budget-quick-stats"> <div class="row g-3"> <!-- Total de Orçamentos --> <div class="col-md-3"> <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-file-earmark-text text-white"></i> </div> <div class="text-muted small">Total de Orçamentos</div> <div class="h4 mb-0 fw-bold">{{ $stats['total'] ?? 0 }}</div> </div> </div> </div> <!-- Orçamentos Aprovados --> <div class="col-md-3"> <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-check-circle text-white"></i> </div> <div class="text-muted small">Aprovados</div> <div class="h4 mb-0 fw-bold">{{ $stats['approved'] ?? 0 }}</div> </div> </div> </div> <!-- Orçamentos Pendentes --> <div class="col-md-3"> <div class="card border-0 shadow-sm"> <div class="card-body text-center"> <div class="avatar-circle bg-warning bg-gradient mb-2 mx-auto"> <i class="bi bi-clock text-white"></i> </div> <div class="text-muted small">Pendentes</div> <div class="h4 mb-0 fw-bold">{{ $stats['pending'] ?? 0 }}</div> </div> </div> </div> <!-- Valor Total --> <div class="col-md-3"> <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-currency-dollar text-white"></i> </div> <div class="text-muted small">Valor Total</div> <div class="h4 mb-0 fw-bold"> R$ {{ number_format($stats['total_value'] ?? 0, 2, ',', '.') }} </div> </div> </div> </div> </div> <!-- Gráfico --> @if($showChart && isset($stats['chart_data'])) <div class="row mt-3"> <div class="col-12"> <div class="card border-0 shadow-sm"> <div class="card-body"> <h6 class="card-title">Evolução de Orçamentos</h6> <canvas id="budgetChart" width="400" height="100"></canvas> </div> </div> </div> </div> @push('scripts') <script> document.addEventListener('DOMContentLoaded', function() { const ctx = document.getElementById('budgetChart').getContext('2d'); new Chart(ctx, { type: 'line', data: @json($stats['chart_data']), options: { responsive: true, plugins: { legend: { display: false } } } }); }); </script> @endpush @endif </div>
8. Integração com Padrões Existentes
Uso em Views
{{-- Dashboard --}} <x-budget.budget-quick-stats :stats="$budgetStats" /> {{-- Listagem --}} <x-budget.budget-card :budget="$budget" /> {{-- Detalhes --}} <x-budget.budget-details :budget="$budget" /> {{-- Formulários --}} <x-budget.budget-actions :budget="$budget" />
Estilos CSS
/* Budget Components Styles */ .modern-badge { font-size: 0.75rem; font-weight: 600; padding: 4px 8px; border-radius: 6px; border: 1px solid transparent; } .avatar-circle { width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-right: 10px; } .info-item { background: #f8f9fa; padding: 12px; border-radius: 8px; border: 1px solid #e9ecef; } .budget-actions .btn { transition: all 0.2s ease; } .budget-actions .btn:hover { transform: translateY(-1px); box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
9. JavaScript Interatividade
Compartilhamento de Orçamentos
// budget-actions.js document.addEventListener('DOMContentLoaded', function() { // Modal de compartilhamento const shareModals = document.querySelectorAll('[data-bs-target^="#shareModal"]'); shareModals.forEach(modal => { modal.addEventListener('click', function() { const budgetId = this.getAttribute('data-budget-id'); generateShareLink(budgetId); }); }); // Geração de link público function generateShareLink(budgetId) { fetch(`/api/budgets/${budgetId}/share`, { method: 'POST', headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content } }) .then(response => response.json()) .then(data => { const shareUrl = data.share_url; // Atualizar modal com URL gerada updateShareModal(shareUrl); }); } });
10. Validação e Segurança
Autorização
// BudgetPolicy.php public function view(User $user, Budget $budget) { return $user->tenant_id === $budget->tenant_id; } public function update(User $user, Budget $budget) { return $user->tenant_id === $budget->tenant_id && $budget->status->isEditable(); } public function delete(User $user, Budget $budget) { return $user->tenant_id === $budget->tenant_id && $budget->status->isDeletable(); }
Validations
{{-- Budget Card com validação de permissões --}} @can('view', $budget) <x-budget.budget-card :budget="$budget" /> @endcan {{-- Budget Actions com validação de status --}} @can('update', $budget) @if($budget->status->isEditable()) <x-budget.budget-actions :budget="$budget" /> @endif @endcan
Este padrão de components para orçamentos garante consistência visual, reutilização de código e manutenibilidade, seguindo os mesmos princípios estabelecidos nos outros components do sistema.