Report Pattern
by JoseApRJunior
Garante padrões consistentes para relatórios no Easy Budget (filtros, exportação, visualização).
Skill Details
name: report-pattern description: Garante padrões consistentes para relatórios no Easy Budget (filtros, exportação, visualização).
Padrão de Relatórios do Easy Budget
Esta skill define os padrões obrigatórios para criação de relatórios no sistema Easy Budget, incluindo filtros de data, exportação e visualização.
Estrutura de Relatório
resources/views/pages/[module]/
└── report.blade.php # Página de relatório com filtros
1. Report Pattern - Estrutura Base
Cabeçalho do Relatório
<x-page-header
title="Relatório de [Módulo]"
icon="clipboard-data"
:breadcrumb-items="[
'[Módulo]' => route('provider.[modulo].index'),
'Relatório' => '#'
]">
<p class="text-muted mb-0">Resumo e detalhes conforme filtros aplicados</p>
</x-page-header>
Card de Filtros (Estado Inicial)
O relatório deve iniciar vazio (isInitial).
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<form action="{{ route('provider.[modulo].report') }}" method="GET" id="reportForm">
<div class="row g-3">
<!-- Filtro de Tipo -->
<div class="col-md-4">
<label for="type" class="form-label small fw-bold text-muted text-uppercase">Tipo</label>
<select name="type" id="type" class="form-select tom-select">
<option value="">Todos</option>
<option value="type_a">Tipo A</option>
<option value="type_b">Tipo B</option>
</select>
</div>
<!-- Data Inicial -->
<div class="col-md-4">
<label for="start_date" class="form-label small fw-bold text-muted text-uppercase">
{{ $isFinancialReport ? 'Período Inicial' : 'Cadastro Inicial' }}
</label>
<input type="text" id="start_date" name="start_date"
class="form-control"
value="{{ old('start_date') }}"
inputmode="numeric" placeholder="DD/MM/AAAA" required>
</div>
<!-- Data Final -->
<div class="col-md-4">
<label for="end_date" class="form-label small fw-bold text-muted text-uppercase">
{{ $isFinancialReport ? 'Período Final' : 'Cadastro Final' }}
</label>
<input type="text" id="end_date" name="end_date"
class="form-control"
value="{{ old('end_date') }}"
inputmode="numeric" placeholder="DD/MM/AAAA" required>
</div>
</div>
<div class="d-flex gap-2 mt-3">
<x-button type="submit" variant="primary" icon="search" label="Filtrar" class="flex-grow-1" id="btnFilter" />
<x-button type="link" :href="route('provider.[modulo].report')" variant="outline-secondary" icon="x" label="Limpar" />
</div>
</form>
</div>
</div>
Estado Inicial (Aguardando Filtros)
@if($isInitial)
<div class="card border-0 shadow-sm">
<div class="card-body text-center py-5">
<i class="bi bi-funnel text-muted" style="font-size: 3rem;"></i>
<h5 class="mt-3 text-muted">Aguardando Filtros</h5>
<p class="text-muted">Selecione os parâmetros acima e clique em Filtrar para gerar o relatório.</p>
</div>
</div>
@else
<!-- Resultados do Relatório -->
<div class="card border-0 shadow-sm">
<!-- ... conteúdo dos resultados ... -->
</div>
@endif
2. Campos de Data - Padrão Obrigatório
Labels por Contexto
| Tipo de Relatório | Label Data Inicial | Label Data Final |
|---|---|---|
| Registros (Produtos, Categorias) | "Cadastro Inicial" | "Cadastro Final" |
| Movimentação/Financeiro | "Período Inicial" | "Período Final" |
Implementação JavaScript de Validação
const parseDate = (str) => {
if (!str) return null;
const parts = str.split('/');
if (parts.length === 3) {
const d = new Date(parts[2], parts[1] - 1, parts[0]);
return isNaN(d.getTime()) ? null : d;
}
return null;
};
const validateDates = () => {
const startDate = document.getElementById('start_date');
const endDate = document.getElementById('end_date');
if (!startDate || !endDate || !startDate.value || !endDate.value) return true;
const start = parseDate(startDate.value);
const end = parseDate(endDate.value);
if (start && end && start > end) {
const message = 'A data inicial não pode ser maior que a data final.';
if (window.easyAlert) {
window.easyAlert.warning(message);
} else {
alert(message);
}
return false;
}
return true;
};
// No submit do formulário
document.getElementById('reportForm').addEventListener('submit', function(e) {
if (!validateDates()) {
e.preventDefault();
return;
}
const startDate = document.getElementById('start_date').value;
const endDate = document.getElementById('end_date').value;
if (startDate && !endDate) {
e.preventDefault();
const message = 'Para filtrar por período, informe as datas inicial e final.';
if (window.easyAlert) window.easyAlert.error(message);
document.getElementById('end_date').focus();
} else if (!startDate && endDate) {
e.preventDefault();
const message = 'Para filtrar por período, informe as datas inicial e final.';
if (window.easyAlert) window.easyAlert.error(message);
document.getElementById('start_date').focus();
}
});
3. Tabela de Resultados
Desktop (modern-table)
<div class="desktop-view">
<div class="table-responsive">
<table class="modern-table table mb-0">
<thead>
<tr>
<th>Coluna 1</th>
<th>Coluna 2</th>
<th class="text-end">Valor</th>
<th class="text-center">Ações</th>
</tr>
</thead>
<tbody>
@foreach($reportData as $item)
<tr>
<td>{{ $item->field1 }}</td>
<td>{{ $item->field2 }}</td>
<td class="text-end">{{ formatCurrency($item->value) }}</td>
<td class="text-center">
<a href="#" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-eye"></i>
</a>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
Mobile (list-group)
<div class="mobile-view">
<div class="list-group list-group-flush">
@foreach($reportData as $item)
<div class="list-group-item">
<div class="d-flex justify-content-between align-items-start">
<div>
<div class="fw-semibold">{{ $item->field1 }}</div>
<div class="text-muted small">{{ $item->field2 }}</div>
</div>
<div class="text-end">
<div class="fw-semibold">{{ formatCurrency($item->value) }}</div>
</div>
</div>
</div>
@endforeach
</div>
</div>
4. Exportação
Botões de Exportação
<div class="d-flex gap-2 mb-3">
<x-button variant="outline-success" icon="file-earmark-excel"
:href="route('provider.[modulo].export', array_merge(request()->all(), ['format' => 'xlsx']))"
label="Excel" />
<x-button variant="outline-danger" icon="file-earmark-pdf"
:href="route('provider.[modulo].export', array_merge(request()->all(), ['format' => 'pdf']))"
label="PDF" />
</div>
Regras de Exportação
- Formato: Sempre disponibilizar PDF e Excel
- CSV: Remover opção CSV (conforme padrão)
- Filtros: Exportação deve respeitar filtros aplicados
- Dados: Exportar apenas resultados filtrados (não estado inicial)
5. Integração com Backend
Service - Processamento de Relatório
public function getReportData(array $filters = []): ServiceResult
{
return $this->safeExecute(function () use ($filters) {
// 1. Definir parâmetros
$startDate = $filters['start_date'] ?? null;
$endDate = $filters['end_date'] ?? null;
$type = $filters['type'] ?? null;
// 2. Verificar Estado Inicial
$isInitial = empty($startDate) && empty($endDate) && empty($type);
if ($isInitial) {
return [
'reportData' => new \Illuminate\Pagination\LengthAwarePaginator([], 0, 10),
'isInitial' => true,
'summary' => null,
'filters' => $filters,
];
}
// 3. Validar e processar
$query = $this->buildReportQuery($filters);
$data = $query->paginate(20);
$summary = $this->calculateSummary($filters);
return [
'reportData' => $data,
'isInitial' => false,
'summary' => $summary,
'filters' => $filters,
];
});
}
6. Checklist de Implementação
- Page header com icon e breadcrumb
- Card de filtros com estado inicial vazio
- Labels corretos (Cadastro/Período)
- Inputs de data com máscara DD/MM/AAAA
- Validação JavaScript de datas
- Botões Filtrar/Limpar padronizados
- Tabela desktop com modern-table
- Lista mobile com list-group
- Exportação PDF e Excel
- Service com processamento de filtros
Related Skills
Attack Tree Construction
Build comprehensive attack trees to visualize threat paths. Use when mapping attack scenarios, identifying defense gaps, or communicating security risks to stakeholders.
Grafana Dashboards
Create and manage production Grafana dashboards for real-time visualization of system and application metrics. Use when building monitoring dashboards, visualizing metrics, or creating operational observability interfaces.
Matplotlib
Foundational plotting library. Create line plots, scatter, bar, histograms, heatmaps, 3D, subplots, export PNG/PDF/SVG, for scientific visualization and publication figures.
Scientific Visualization
Create publication figures with matplotlib/seaborn/plotly. Multi-panel layouts, error bars, significance markers, colorblind-safe, export PDF/EPS/TIFF, for journal-ready scientific plots.
Seaborn
Statistical visualization. Scatter, box, violin, heatmaps, pair plots, regression, correlation matrices, KDE, faceted plots, for exploratory analysis and publication figures.
Shap
Model interpretability and explainability using SHAP (SHapley Additive exPlanations). Use this skill when explaining machine learning model predictions, computing feature importance, generating SHAP plots (waterfall, beeswarm, bar, scatter, force, heatmap), debugging models, analyzing model bias or fairness, comparing models, or implementing explainable AI. Works with tree-based models (XGBoost, LightGBM, Random Forest), deep learning (TensorFlow, PyTorch), linear models, and any black-box model
Pydeseq2
Differential gene expression analysis (Python DESeq2). Identify DE genes from bulk RNA-seq counts, Wald tests, FDR correction, volcano/MA plots, for RNA-seq analysis.
Query Writing
For writing and executing SQL queries - from simple single-table queries to complex multi-table JOINs and aggregations
Pydeseq2
Differential gene expression analysis (Python DESeq2). Identify DE genes from bulk RNA-seq counts, Wald tests, FDR correction, volcano/MA plots, for RNA-seq analysis.
Scientific Visualization
Meta-skill for publication-ready figures. Use when creating journal submission figures requiring multi-panel layouts, significance annotations, error bars, colorblind-safe palettes, and specific journal formatting (Nature, Science, Cell). Orchestrates matplotlib/seaborn/plotly with publication styles. For quick exploration use seaborn or plotly directly.
