Gerador de relatórios para Next.js

Editor de layout

O usuário final mostra, oculta, reordena e redimensiona colunas — e vê o PDF atualizar ao vivo — sem tocar em código nem esperar um deploy. Por baixo, tudo é uma ReportView salva por um ReportStorageAdapter.

Sistema, duplicar, editar

Todo relatório nasce com uma view de sistema — somente leitura, espelhando o defineReport. Para customizar, o usuário duplica: nasce uma cópia editável, livre para virar a nova padrão.

View de sistema (isSystem: true) — somente leitura. O editor abaixo desabilita os controles e mostra apenas o botão "Duplicar para editar".

Duplicar — cria uma view nova (isSystem: false), livre para editar e apagar, a partir da view selecionada.

Definir como padrão — marca a view como isDefault: true e zera o default das demais views do mesmo relatório.

Ver o contrato completo (Persistência e views)

A view editada persiste no seu navegador

Este demo usa createLocalStorageReportAdapter({ prefix: "cm-report-docs" }) — tudo que você mudar abaixo fica salvo no localStorage deste navegador. Recarregue a página: a view continua como você deixou. Para limpar, apague os dados do site nas configurações do navegador.

Editor ao vivo — relatório de Vendas

CmSelect troca a view, os botões duplicam e definem o padrão, e CmReportLayoutEditor edita colunas, cabeçalho e resumo com preview de PDF das 15 primeiras linhas de vendas.

Carregando views…

Como plugar no seu app

O essencial: um adapter, useReportViews para o estado das views e CmReportLayoutEditor controlado — o mesmo padrão do demo acima, com o relatório e os dados do seu app.

tsx
import { createLocalStorageReportAdapter } from "cosmemilton-report";
// ou, num backend com banco: import { createPrismaReportAdapter } from "@/lib/report-adapter";

import { CmReportLayoutEditor, useReportViews } from "cosmemilton-report/client";
import { meuRelatorio } from "@/lib/reports/meu-relatorio";

// Uma instância por adapter — reaproveite entre páginas, não recrie a cada render.
const adapter = createLocalStorageReportAdapter({ prefix: "meuapp:relatorios" });

function EditorDoMeuRelatorio() {
  const { selectedView, saveView, duplicateView, selectView } = useReportViews({
    slug: meuRelatorio.slug,
    definition: meuRelatorio,
    adapter,
  });

  if (!selectedView) return null;

  return (
    <CmReportLayoutEditor
      definition={meuRelatorio}
      view={selectedView}
      onViewChange={selectedView.isSystem ? () => {} : saveView}
      onDuplicate={
        selectedView.isSystem
          ? () => duplicateView(selectedView.id).then((v) => selectView(v.id))
          : undefined
      }
      previewRows={minhasLinhasDeAmostra}
    />
  );
}