Roteiro completo para gravar 31 vídeo-aulas de 10–20 min construindo um sistema de Ordem de Serviço (manutenção em campo) inteiramente pela plataforma MadBuilder. O mesmo material é publicado na Ajuda do Studio (public/help/curso-ordem-de-servico.html) para qualquer usuário seguir sozinho.
31 aulas · ~9 h14 tipos de páginaSem programaçãoDados fictícios
00
Antes de começar
O curso é uma sequência: cada aula usa o que a anterior deixou pronto. A tabela abaixo é o mapa — duração, o que a aula ensina e de que aula ela depende. Os módulos aparecem logo em seguida, um por seção.
Módulos do app: Cadastros · Operação · Relatórios. Grupos de permissão: Atendimento · Técnico · Financeiro. Perfis: Atendente · Técnico · Gerente · Diretoria (só aprova orçamentos acima de R$ 5.000 — aula 22).
Tudo aqui é fictício. Empresa, clientes, técnicos, CNPJ, CPF, e-mails, telefones e valores foram inventados para o curso. Nunca use nome, documento ou dado de cliente real nas suas telas ou gravações — e use exatamente os mesmos nomes e números desta lista: assim os seus prints batem com os do curso e as contas dos relatórios dão o mesmo resultado.
Modelo de dados — os scripts SQL do curso
Os scripts ficam à vista, na ordem em que o curso os usa. Em cada um, Copiar no canto do bloco leva o texto inteiro para a área de transferência, e Baixar salva o .sql como arquivo (o modal de importação também aceita Upload arquivo). O script da aula 03 reaparece dentro da própria aula, no passo em que você o cola.
Aula 02 — as três primeiras tabelas, à mão · sql/a02-manual.sql
Referência do que você digita no diagrama. Não é para importar: a aula 02 cria cliente, especialidade e tecnico campo por campo.
-- Aula 02 — referência do que o aluno DIGITA no diagrama (não é importado).
-- Índice em tecnico.gerente_id é criado pela UI (o importador ignora CREATE INDEX).
CREATE TABLE cliente (
id SERIAL PRIMARY KEY,
tipo_pessoa CHAR(2) DEFAULT 'PJ',
nome_razao_social VARCHAR(200) NOT NULL,
cpf_cnpj VARCHAR(20),
email VARCHAR(150),
telefone VARCHAR(30),
cep VARCHAR(10),
logradouro VARCHAR(200),
numero VARCHAR(20),
complemento VARCHAR(100),
bairro VARCHAR(100),
cidade VARCHAR(100),
uf CHAR(2),
observacoes TEXT,
ativo BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP,
updated_at TIMESTAMP,
deleted_at TIMESTAMP
);
CREATE TABLE especialidade (
id SERIAL PRIMARY KEY,
nome VARCHAR(100) NOT NULL UNIQUE,
descricao TEXT,
cor VARCHAR(20),
ativo BOOLEAN NOT NULL DEFAULT true
);
CREATE TABLE tecnico (
id SERIAL PRIMARY KEY,
nome VARCHAR(150) NOT NULL,
matricula VARCHAR(30) UNIQUE,
cargo VARCHAR(60),
foto VARCHAR(500),
email VARCHAR(150),
telefone VARCHAR(30),
especialidade_id INTEGER REFERENCES especialidade(id),
custo_hora NUMERIC(12,2) DEFAULT 0,
valor_hora_cliente NUMERIC(12,2) DEFAULT 0,
capacidade_hora_dia NUMERIC(5,2) DEFAULT 8,
gerente_id INTEGER, -- gerente (mesma tabela): auto-FK não é suportada, fica como inteiro + índice
ativo BOOLEAN NOT NULL DEFAULT true
);
-- índice (criar pela UI): idx_tecnico_gerente ON tecnico(gerente_id)
Aula 03 — o modelo completo, para importar · sql/a03-importar.sql
Este é o script da importação: Modelos de dados → Importar schema SQL → Colar SQL. São 14 tabelas; cliente, especialidade e tecnico aparecem como "já existe — pulada", de propósito.
-- Aula 03 — cole este script em Modelos de dados → Importar schema SQL → Colar SQL.
-- Só CREATE TABLE com REFERENCES inline (é o que o importador lê).
-- cliente, especialidade e tecnico já existem (aula 02): aparecem como "já existe" e são puladas.
CREATE TABLE cliente (
id SERIAL PRIMARY KEY,
nome_razao_social VARCHAR(200) NOT NULL
);
CREATE TABLE especialidade (
id SERIAL PRIMARY KEY,
nome VARCHAR(100) NOT NULL
);
CREATE TABLE tecnico (
id SERIAL PRIMARY KEY,
nome VARCHAR(150) NOT NULL
);
CREATE TABLE status_os (
id SERIAL PRIMARY KEY,
codigo VARCHAR(30) NOT NULL UNIQUE,
nome VARCHAR(60) NOT NULL,
cor VARCHAR(20),
ordem INTEGER DEFAULT 0,
e_final BOOLEAN DEFAULT false
);
CREATE TABLE tecnico_especialidade (
id SERIAL PRIMARY KEY,
tecnico_id INTEGER NOT NULL REFERENCES tecnico(id),
especialidade_id INTEGER NOT NULL REFERENCES especialidade(id),
nivel VARCHAR(20),
dt_certificacao DATE
);
CREATE TABLE equipamento (
id SERIAL PRIMARY KEY,
cliente_id INTEGER NOT NULL REFERENCES cliente(id),
codigo_patrimonio VARCHAR(50),
nome VARCHAR(200) NOT NULL,
tipo VARCHAR(100),
marca VARCHAR(100),
modelo VARCHAR(100),
numero_serie VARCHAR(100),
dt_aquisicao DATE,
dt_garantia_fim DATE,
localizacao_descricao VARCHAR(250),
ativo BOOLEAN DEFAULT true
);
CREATE TABLE servico (
id SERIAL PRIMARY KEY,
codigo VARCHAR(30) UNIQUE,
nome VARCHAR(200) NOT NULL,
descricao TEXT,
especialidade_id INTEGER REFERENCES especialidade(id),
preco NUMERIC(12,2) DEFAULT 0,
duracao_estimada_min INTEGER,
ativo BOOLEAN DEFAULT true
);
CREATE TABLE peca (
id SERIAL PRIMARY KEY,
sku VARCHAR(50) UNIQUE,
ean VARCHAR(20),
nome VARCHAR(200) NOT NULL,
descricao TEXT,
unidade_medida VARCHAR(10) DEFAULT 'un',
preco_custo NUMERIC(12,2) DEFAULT 0,
preco_venda NUMERIC(12,2) DEFAULT 0,
estoque_atual NUMERIC(12,3) DEFAULT 0,
estoque_minimo NUMERIC(12,3) DEFAULT 0,
ativo BOOLEAN DEFAULT true
);
CREATE TABLE ordem_servico (
id SERIAL PRIMARY KEY,
numero VARCHAR(30) NOT NULL UNIQUE,
titulo VARCHAR(150) NOT NULL,
cliente_id INTEGER NOT NULL REFERENCES cliente(id),
equipamento_id INTEGER REFERENCES equipamento(id),
tecnico_id INTEGER REFERENCES tecnico(id),
status_os_id INTEGER NOT NULL REFERENCES status_os(id),
tipo VARCHAR(30) DEFAULT 'corretiva',
prioridade VARCHAR(20) DEFAULT 'media',
descricao_problema TEXT NOT NULL,
descricao_solucao TEXT,
dt_abertura TIMESTAMP NOT NULL,
dt_agendada TIMESTAMP,
dt_prevista TIMESTAMP,
dt_inicio DATE,
dt_fim DATE,
progresso INTEGER DEFAULT 0,
os_pai_id INTEGER, -- OS pai (hierarquia do Gantt); auto-FK não é suportada no modelo, fica como inteiro
dt_conclusao TIMESTAMP,
horas_trabalhadas NUMERIC(10,2) DEFAULT 0,
valor_servicos NUMERIC(12,2) DEFAULT 0,
valor_pecas NUMERIC(12,2) DEFAULT 0,
valor_deslocamento NUMERIC(12,2) DEFAULT 0,
valor_total NUMERIC(12,2) DEFAULT 0,
status_aprovacao VARCHAR(20) DEFAULT 'rascunho',
observacoes TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
deleted_at TIMESTAMP
);
CREATE TABLE os_servico_item (
id SERIAL PRIMARY KEY,
ordem_servico_id INTEGER NOT NULL REFERENCES ordem_servico(id),
servico_id INTEGER NOT NULL REFERENCES servico(id),
quantidade NUMERIC(10,2) DEFAULT 1,
preco_unitario NUMERIC(12,2) DEFAULT 0,
desconto_pct NUMERIC(5,2) DEFAULT 0,
subtotal NUMERIC(12,2) DEFAULT 0,
observacao VARCHAR(250)
);
CREATE TABLE os_peca_item (
id SERIAL PRIMARY KEY,
ordem_servico_id INTEGER NOT NULL REFERENCES ordem_servico(id),
peca_id INTEGER NOT NULL REFERENCES peca(id),
quantidade NUMERIC(10,3) NOT NULL DEFAULT 1,
preco_unitario NUMERIC(12,2) DEFAULT 0,
subtotal NUMERIC(12,2) DEFAULT 0,
numero_serie VARCHAR(100)
);
CREATE TABLE os_anexo (
id SERIAL PRIMARY KEY,
ordem_servico_id INTEGER NOT NULL REFERENCES ordem_servico(id),
tipo VARCHAR(20),
nome_arquivo VARCHAR(200),
arquivo VARCHAR(500),
imagem VARCHAR(500),
assinatura_base64 TEXT,
nome_signatario VARCHAR(150),
codigo_lido VARCHAR(100),
descricao VARCHAR(250),
created_at TIMESTAMP
);
CREATE TABLE os_historico (
id BIGSERIAL PRIMARY KEY,
ordem_servico_id INTEGER NOT NULL REFERENCES ordem_servico(id),
titulo VARCHAR(120) NOT NULL,
observacao TEXT,
dt_evento TIMESTAMP NOT NULL,
icone VARCHAR(40),
cor VARCHAR(20),
status_anterior VARCHAR(60),
status_novo VARCHAR(60),
usuario_nome VARCHAR(120)
);
CREATE TABLE os_apontamento (
id BIGSERIAL PRIMARY KEY,
ordem_servico_id INTEGER NOT NULL REFERENCES ordem_servico(id),
tecnico_id INTEGER NOT NULL REFERENCES tecnico(id),
dt_referencia DATE NOT NULL,
tipo VARCHAR(20) DEFAULT 'execucao',
horas NUMERIC(6,2) NOT NULL DEFAULT 0,
valor_hora NUMERIC(12,2) DEFAULT 0,
descricao_atividade VARCHAR(250)
);
Aula 21 — as três tabelas do PDV (plano B) · sql/a21-vendas.sql
Use só se o Criar pra mim (recomendado) do assistente de PDV falhar ao criar venda, venda_item e venda_pagamento. Com as tabelas no modelo, escolha Mapear existentes no assistente.
-- Aula 21 — tabelas do PDV (venda, venda_item, venda_pagamento).
--
-- ⚠️ Use este script SÓ enquanto o **Criar pra mim (recomendado)** do
-- assistente de PDV estiver falhando com
-- `Falha ao criar as tabelas de venda: venda_item: FK produto_id → Peca.id:
-- foreign_key.references_table 'Peca' not found in diagram`
-- (o assistente manda o NOME DO MODEL onde o importador espera o NOME DA
-- TABELA). Com as três tabelas já no modelo, escolha **Mapear existentes** no
-- passo **Venda, itens e pagamentos (3 tabelas)**.
--
-- Cole em: Modelos de dados → Ordem de Serviço → Importar schema SQL → Colar SQL.
-- Depois marque **AI** na coluna `id` das três (o importador não lê SERIAL).
CREATE TABLE venda (
id SERIAL PRIMARY KEY,
data_hora TIMESTAMP,
cliente_id INTEGER REFERENCES cliente(id),
operador_id INTEGER,
status VARCHAR(20),
subtotal DOUBLE PRECISION DEFAULT 0,
desconto DOUBLE PRECISION DEFAULT 0,
total DOUBLE PRECISION NOT NULL DEFAULT 0,
documento VARCHAR(20),
troco DOUBLE PRECISION DEFAULT 0,
client_uuid CHAR(36) UNIQUE,
observacao TEXT
);
CREATE TABLE venda_item (
id SERIAL PRIMARY KEY,
venda_id INTEGER NOT NULL REFERENCES venda(id),
produto_id INTEGER NOT NULL REFERENCES peca(id),
quantidade DOUBLE PRECISION NOT NULL DEFAULT 0,
preco_unitario DOUBLE PRECISION NOT NULL DEFAULT 0,
desconto DOUBLE PRECISION DEFAULT 0,
total DOUBLE PRECISION NOT NULL DEFAULT 0
);
CREATE TABLE venda_pagamento (
id SERIAL PRIMARY KEY,
venda_id INTEGER NOT NULL REFERENCES venda(id),
forma VARCHAR(30) NOT NULL,
valor DOUBLE PRECISION NOT NULL DEFAULT 0,
valor_recebido DOUBLE PRECISION
);
Criar a conta, o projeto Assistec Manutenção Exemplo e conhecer o Mad Studio, para que as 30 aulas seguintes tenham onde acontecer.
O que você terá no fim
Conta ativa no manager, com login funcionando.
Projeto Assistec Manutenção Exemplo criado e aberto no Mad Studio.
Interface em português e no tema claro.
Noção do que cada grupo do rail faz e em qual aula do curso ele é usado.
Dois caminhos para abrir qualquer painel: o rail e a busca rápida (Ctrl+P).
Clareza sobre o que será construído nas 30 aulas seguintes.
Manager › Meus projetos
Tela Meus projetos no manager, lista de cards e o card Criar projeto
Manager › Meus projetos › Novo projeto
Tela Novo projeto na aba Em branco, com o campo Nome do projeto preenchido: Assistec Manutenção Exemplo
Studio › Início
Mad Studio recém-aberto: rail recolhido à esquerda, rail global na borda, área central vazia
Studio › Rail expandido
Rail expandido com os grupos Principal, Geração & ferramentas e Módulos & acesso, a seta de Mais itens abaixo e o bloco fixo do rodapé (Baixar projeto, Estatísticas e atividades, Compartilhar projeto, Propriedades do projeto)
Passo a passo
Abra manager.madbuilder.dev. A tela Entrar na sua conta aparece.
Se ainda não tem conta: clique em Criar conta grátis, preencha Nome, E-mail e Senha e confirme o e-mail que chega na caixa de entrada.
Preencha E-mail e Senha, marque Manter-me conectado por 30 dias e clique em Entrar.
Se a conta tiver verificação em duas etapas, digite o Código de seis dígitos do aplicativo autenticador e confirme.
Você cai em Meus projetos. Clique no card Criar projeto (o primeiro da lista; a barra lateral também tem o link Novo projeto).
Fique na aba Em branco. As outras duas abas — Templates e Gerar com IA — não são usadas neste curso.
Em Nome do projeto, digite Assistec Manutenção Exemplo.
Ícone e Logo são opcionais (PNG ou JPG, até 4 MB). Pode deixar em branco — dá para definir depois em Propriedades do projeto.
Clique em Criar projeto e aguarde. A criação monta a estrutura inicial do projeto e leva alguns segundos.
No card do projeto, clique em Abrir — ele leva ao detalhe do projeto. É lá (e na visão em lista) que fica o Abrir Mad Studio. Clique nele: o editor abre com o rail à esquerda e a área central vazia.
No rail, clique em Expandir menu para ver os nomes dos painéis. O botão vira Recolher menu.
Reconheça o grupo Principal, o do dia a dia: Arquivos (as telas e os códigos do projeto), Buscar, Documentação (os guias escritos), Modelos de dados (o desenho do banco — aulas 02 e 03), Banco de dados (o console SQL — aula 27), Importar 4.0 e Transformers (aula 23).
Desça para Geração & ferramentas. É o grupo mais cheio, e quase todo o curso passa por ele: Gerar CRUDs em lote (aula 04), Rotas da API e MCP Server (aula 26), Editor de menus (aula 05), MadTrace · observabilidade (aula 27), Agendador · schedule.php (aula 23), Fluxos de Aprovação (aula 22), Tenancy & Banco (aula 25), Acesso de Agente (MCP) (aula 26) e Teste online (o sistema rodando de verdade — aula 06).
Em seguida vem Módulos & acesso: Módulos, Grupos de permissão e Perfis de acesso. Os três são a aula 05 inteira.
O rail rola: a seta Mais itens abaixo indica que há mais coisa. Continue até Customização — Traduções e Temas (aula 24) e Arquivos customizados (aula 26).
Por último, o grupo Deploy: Servidores de deploy, Deploy SSH, Git Deploy, Hospedagem · MadCloud e Pacotes Composer. É o módulo 7 do curso, nas aulas 28 e 29. Abaixo dele, já fora de qualquer grupo, fica o bloco fixo do rodapé: Baixar projeto (aula 29), Estatísticas e atividades, Compartilhar projeto (as duas na aula 30) e Propriedades do projeto (aula 25).
No rail global (a borda mais externa), reconheça Projetos, Loja, Ajuda, Novidades, Reportar bug e Conta.
Abra Conta e confira que o tema está em Tema claro. Se estiver escuro, clique em Tema claro.
Ainda em Conta, note que é dali que saem Minha conta, Preferências e Sair. O painel volta na aula 30.
Pressione Ctrl+P (ou ⌘P no Mac) para abrir Ir para / Buscar. O campo diz "Buscar arquivos, telas, modelos, ações…" — ou seja, a busca cobre as quatro coisas.
Digite modelos e pressione Enter. O painel Modelos de dados abre.
Feche a aba pelo X e reabra pelo rail, para confirmar que os dois caminhos levam ao mesmo lugar.
Abra o painel Documentação (grupo Principal) e localize o card deste curso. É a versão escrita de tudo o que você vai ver em vídeo, com os mesmos prints.
Deixe o Studio aberto — a aula 02 continua exatamente daqui.
Sobre os dados do curso.Empresa, clientes, técnicos, equipamentos, serviços e peças são fictícios e estão listados no início do material. Use exatamente os mesmos nomes e valores: assim os seus prints batem com os do curso e as contas dos relatórios dão o mesmo número.
Erros comuns
O login volta para a tela inicial sem mensagem
o navegador está bloqueando cookies ou você está numa janela anônima com bloqueio agressivo → use uma janela normal do Chrome e entre de novo.
Aparece "Beta fechado" ou "Acesso ainda não liberado" depois do login
a plataforma está em beta e este e-mail ainda não foi convidado → o login em si funcionou; aguarde o e-mail de liberação antes de seguir o curso.
O botão Criar projeto fica desabilitado
o campo Nome do projeto está vazio ou tem menos de dois caracteres → digite o nome completo Assistec Manutenção Exemplo.
O Studio abre em inglês
o idioma da conta está em outro locale → abra Conta → Minha conta no manager, troque o Idioma para Português e recarregue o Studio.
Ctrl+K abre uma janela de conversa em vez da busca
Ctrl+K é o atalho do Agente de IA; a busca rápida é Ctrl+P → use Ctrl+P. O Agente entra só na aula 31.
A busca rápida diz "Carregando índice…" e não acha nada
o índice de busca do projeto ainda está sendo montado logo depois da criação → espere alguns segundos e digite de novo.
Um item do rail aparece apagado e não abre
aquele painel exige um plano superior ao da sua conta → cada aula avisa o plano mínimo no início; o restante do curso segue normalmente.
A aba do Studio abre em branco depois de alguns minutos parada
a sessão do editor expirou → recarregue a página; se persistir, volte ao manager e clique de novo em Abrir Mad Studio.
Criei o projeto duas vezes por engano
o segundo clique em Criar projeto entrou antes da tela responder → abra o menu Mais opções do card duplicado em Meus projetos e use Deletar; o projeto vai para a Lixeira e não atrapalha o curso.
Checklist de encerramento
Consigo entrar no manager com e-mail e senha.
O projeto Assistec Manutenção Exemplo aparece em Meus projetos.
Abrir Mad Studio carrega o editor sem erro.
O rail expandido mostra os cinco grupos (Principal, Geração & ferramentas, Módulos & acesso, Customização e Deploy) — os dois últimos só depois de rolar a lista.
Sei dizer em qual grupo ficam Modelos de dados, Gerar CRUDs em lote e Teste online.
Encontrei o card do curso em Documentação.
Anotei a lista de dados fictícios que o curso usa do começo ao fim.
Ctrl+P abre a busca e encontra Modelos de dados.
A interface está em português e no tema claro.
Sei abrir o mesmo painel de duas formas: pelo rail e pela busca rápida.
Desenhar à mão as três primeiras tabelas do sistema — cliente, especialidade e tecnico — para você entender coluna por coluna o que o MadBuilder faz com cada tipo, cada flag e cada relacionamento.
O que você terá no fim
Um modelo de dados chamado Ordem de Serviço no projeto.
Três tabelas desenhadas: cliente (18 colunas), especialidade (8) e tecnico (16).
Colunas padrão (created_at, updated_at, deleted_at) nascendo sozinhas em toda tabela nova.
Uma coluna descritiva por tabela — o texto que aparece no lugar do id em toda ligação (e a marca automática da plataforma desfeita onde ela errou o alvo).
Uma ligação 1:n entre tecnico e especialidade, criada pela toolbar.
A coluna gerente_id com IDX (índice), pronta para o organograma da aula 19.
Clareza sobre por que a plataforma ainda não cria chave estrangeira de uma tabela para ela mesma.
Studio › Modelos de dados › Novo modelo de dados
Modal Novo modelo de dados com Nome, Database name e Tipo de banco preenchidos
Studio › Modelos de dados › Ordem de Serviço
Canvas com o aviso 'Clique no canvas onde a TABELA deve aparecer' no topo e o botão de tabela aceso na toolbar (o modo de posicionamento só existe depois que o modelo tem a primeira tabela)
Studio › Modelos de dados › Ordem de Serviço › cliente
Card da tabela cliente com as 18 colunas e o popover Editar coluna aberto em nome_razao_social, com os selos NN e Desc marcados
Studio › Modelos de dados › Ordem de Serviço › Nova foreign key
Modal Nova foreign key com origem e destino em tecnico e a mensagem de erro 'Origem e destino devem ser tabelas diferentes'
Studio › Modelos de dados › Ordem de Serviço › tecnico › gerente_id
Popover Editar coluna de gerente_id, tipo Int (Inteiro), com o selo IDX marcado
Studio › Modelos de dados › Ordem de Serviço
Recorte do canvas com os cards cliente (18 colunas), especialidade (8) e tecnico (16), a ligação 1:n fk_tecnico_1 entre tecnico e especialidade e o rodapé FK 1 · IDX 1 no card tecnico (print capturado depois da aula 03, por isso há linhas de outras tabelas nas bordas)
Passo a passo
No rail, abra Modelos de dados.
Clique em Criar modelo de dados (se o projeto já tiver algum modelo, o botão é Novo modelo).
No modal Novo modelo de dados, preencha:
Nome: Ordem de Serviço
Database name: assistec
Tipo de banco: deixe no padrão (PostgreSQL)
Clique em Criar modelo. A área de desenho abre com a mensagem Diagrama vazio e o botão Adicionar tabela no meio — enquanto o modelo não tem nenhuma tabela, não existe canvas para clicar.
Na toolbar, clique em Propriedades do diagrama (o primeiro botão, ícone de engrenagem).
Em Colunas padrões ao criar tabela, use Adicionar coluna padrão três vezes e preencha a grade (Nome, Tipo, Tamanho, Valor padrão, Rótulo, Not null, Esconder):
Nome
Tipo
Not null
created_at
Timestamp (Timestamp)
Não
updated_at
Timestamp (Timestamp)
Não
deleted_at
Timestamp (Timestamp)
Não
Em Colunas de inserção automáticas, aponte Horário de criação do registro para created_at, Horário de atualização do registro para updated_at e Horário de deleção do registro para deleted_at. É o que faz o sistema gerado preencher essas datas sozinho.
Clique em Salvar. A regra vale para as tabelas criadas a partir de agora.
Crie a tabela cliente. Com o modelo vazio, o caminho é o botão Adicionar tabela da tela Diagrama vazio: ele abre o modal Nova entidade, onde você escolhe o tipo (Tabela, já selecionado), digita cliente em Nome e clica em Criar.
Da segunda tabela em diante o caminho é outro, e é o que o vídeo mostra: clique no botão Nova tabela da toolbar (o + com o ícone de banco) e depois no canvas, no ponto onde ela deve nascer. Enquanto o modo está armado, uma faixa no topo avisa Clique no canvas onde a TABELA deve aparecer (e Cancelar desarma). A tabela nasce com o nome já em edição: digite por cima e tecle Enter. Se você clicar fora antes de digitar, ela fica como tabela_1 — aí sim use o duplo clique no título (Duplo clique para renomear).
Passe o mouse sobre o card de cliente: os botões de ação aparecem em volta dele. Clique em Adicionar coluna (o +). A coluna nasce e o editor Editar coluna abre sozinho, com o nome selecionado. Preencha Nome, Tipo, Tamanho (quando o tipo pedir), Valor padrão e os selos de marcação; não existe botão Salvar — o editor grava quando você o fecha (Esc, o X ou um clique fora). Os selos são:
selo
o que é
NN
Not null — campo obrigatório
UQ
Único — não aceita valor repetido
AI
Auto-increment (só na chave primária)
UUID
chave primária em UUID (só na chave primária)
Desc
Descritiva — o texto que aparece no lugar do id nas ligações
IDX
Índice — busca mais rápida por esta coluna
Cadastre a lista abaixo:
Coluna
Tipo (na UI)
Tamanho
NN
Valor padrão
Outros selos
tipo_pessoa
Char (Caractere fixo)
2
não
PJ
—
nome_razao_social
Varchar (Texto curto)
200
sim
—
Desc
cpf_cnpj
Varchar (Texto curto)
20
não
—
—
email
Varchar (Texto curto)
150
não
—
—
telefone
Varchar (Texto curto)
30
não
—
—
cep
Varchar (Texto curto)
10
não
—
—
logradouro
Varchar (Texto curto)
200
não
—
—
numero
Varchar (Texto curto)
20
não
—
—
complemento
Varchar (Texto curto)
100
não
—
—
bairro
Varchar (Texto curto)
100
não
—
—
cidade
Varchar (Texto curto)
100
não
—
—
uf
Char (Caractere fixo)
2
não
—
—
observacoes
Text (Texto longo)
—
não
—
—
ativo
Boolean (Booleano)
—
sim
true
—
Confira a coluna descritiva antes de seguir. Quando a tabela ainda não tem nenhuma coluna marcada como Desc, a plataforma marca uma sozinha — e em cliente isso acontece na primeira coluna que você digita, tipo_pessoa. Marcar nome_razao_social depois não desmarca a primeira: as duas ficam com o olho azul. Abra tipo_pessoa e desmarque Desc.
O card nasce com 220 × 180 e rola por dentro: com 18 colunas você só vê as primeiras. Clique no card e arraste a alça do canto inferior direito até ver a tabela inteira.
Crie a segunda tabela pelo caminho da toolbar (Nova tabela → clique no canvas → digite o nome → Enter) e chame de especialidade:
Coluna
Tipo (na UI)
Tamanho
NN
Valor padrão
Outros selos
nome
Varchar (Texto curto)
100
sim
—
UQ, Desc
descricao
Text (Texto longo)
—
não
—
—
cor
Varchar (Texto curto)
20
não
—
—
ativo
Boolean (Booleano)
—
sim
true
—
Crie a terceira tabela, tecnico, do mesmo jeito:
Coluna
Tipo (na UI)
Tamanho
NN
Valor padrão
Outros selos
nome
Varchar (Texto curto)
150
sim
—
Desc
matricula
Varchar (Texto curto)
30
não
—
UQ
cargo
Varchar (Texto curto)
60
não
—
—
foto
Varchar (Texto curto)
500
não
—
—
email
Varchar (Texto curto)
150
não
—
—
telefone
Varchar (Texto curto)
30
não
—
—
custo_hora
Double (Decimal)
—
não
0
—
valor_hora_cliente
Double (Decimal)
—
não
0
—
capacidade_hora_dia
Double (Decimal)
—
não
8
—
ativo
Boolean (Booleano)
—
sim
true
—
Ligue tecnico a especialidade: na toolbar, clique no botão 1:n, depois clique no card tecnico (a origem) e por último no card especialidade (o destino). A plataforma cria sozinha a coluna especialidade_id em tecnico, nomeia a chave fk_tecnico_1 e desenha a linha entre os dois cards.
Crie a coluna gerente_id em tecnico com tipo Int (Inteiro), sem NN. É essa coluna que o organograma da aula 19 usa como pai.
Agora veja por que ela é um inteiro comum e não uma chave: no card tecnico, abra a seção Foreign keys e clique no +. No modal Nova foreign key, a origem já vem travada em tecnico; escolha Coluna FK = gerente_id e Tabela alvo = tecnico. Ao clicar em Criar FK a mensagem é literal: "Origem e destino devem ser tabelas diferentes (auto-FK não suportado nesta versão)." Clique em Cancelar.
Pela ferramenta 1:n da toolbar o aviso não aparece: clicar duas vezes na mesma tabela simplesmente cancela o modo, sem mensagem nenhuma. Quem explica o motivo é o modal.
Volte na coluna gerente_id (duplo clique na linha) e marque o selo IDX. Diferente dos outros selos, ele grava na hora — o índice é outro registro, não um campo da coluna.
Confira o card tecnico: 16 colunas (id, as três datas, as dez que você digitou, especialidade_id e gerente_id). O rodapé do card mostra FK 1 e IDX 1.
Confirme no canto direito da toolbar o texto Salvo agora. O diagrama grava a cada alteração — não existe botão Salvar.
Use Ajustar à tela para enquadrar as três tabelas e deixe o canvas pronto para o print de fechamento.
Por que tanto cuidado com a coluna Descritiva?Ela é uma por tabela e é o texto que aparece no lugar do id em toda tela que apontar para essa tabela. Sem ela, a listagem de ordens de serviço mostraria "Cliente 47" em vez de "Padaria Pão Dourado Exemplo".
Erros comuns
Não acho o botão de Salvar do editor de coluna
ele não existe → o Editar coluna grava sozinho quando você fecha (Esc, X ou clique fora). Se o editor não fecha, é porque há erro: ele aparece em vermelho no topo do próprio editor (nome repetido, tipo sem tamanho…).
Duas colunas com o olho azul (descritiva)
a plataforma marca uma coluna como Desc sozinha enquanto a tabela não tem nenhuma; a primeira que você digita herda a marca → abra a coluna errada e desmarque Desc. Descritiva é uma por tabela: com duas, a listagem pode mostrar PJ no lugar do nome do cliente.
Liguei tecnico a tecnico com a ferramenta 1:n e não aconteceu nada
o modo de ligação cancela em silêncio quando origem e destino são a mesma tabela → use o + da seção Foreign keys do card para ver a mensagem que explica o motivo.
A tabela nasceu com o nome tabela_1
você clicou fora antes de digitar; ela nasce com o nome em edição → duplo clique no título do card (Duplo clique para renomear) e digite o nome certo.
O card mostra só as primeiras colunas
ele nasce 220 × 180 e rola por dentro → clique no card e arraste a alça do canto para esticá-lo.
As três datas não apareceram na tabela nova
você criou a tabela antes de salvar as Colunas padrões ao criar tabela → a regra só vale para tabelas criadas depois; adicione created_at, updated_at e deleted_at na mão nessa tabela, ou exclua e crie de novo.
O campo Tamanho fica travado
o tipo escolhido não usa tamanho (Inteiro, Decimal, Booleano, Data, Timestamp) → só Varchar (Texto curto) e Char (Caractere fixo) pedem tamanho; se for texto longo sem limite, use Text (Texto longo).
"Origem e destino devem ser tabelas diferentes"
ao ligar tecnico a tecnico → a versão atual não cria chave estrangeira de uma tabela para ela mesma → crie a coluna gerente_id como Int (Inteiro) com o selo IDX; o organograma e o Gantt funcionam com a coluna, não dependem da constraint.
A tabela alvo aparece como "Tabela alvo sem PK"
você clicou numa tabela que ainda não tem chave primária (acontece em view ou estrutura) → ligue só tabelas; toda tabela criada pelo botão Nova tabela já nasce com id.
Nome de coluna recusado
você usou maiúscula, acento, espaço ou uma palavra reservada do banco (order, group, index, key…) → use letras minúsculas, números e sublinhado, começando por letra: dt_abertura, nome_razao_social.
Marquei UQ numa coluna que já tem dados repetidos
o índice único não pode ser criado com duplicatas → limpe as duplicatas antes de publicar, ou deixe a coluna sem UQ.
Checklist de encerramento
O modelo Ordem de Serviço aparece em Modelos de dados.
O canvas mostra as três tabelas: cliente, especialidade e tecnico.
Toda tabela tem id, created_at, updated_at e deleted_at.
cliente tem 18 colunas e sónome_razao_social está marcada como Desc (confira que tipo_pessoa ficou sem a marca).
especialidade.nome está NN, UQ e Desc.
tecnico.matricula está UQ e tecnico.nome está Desc.
tecnico tem 16 colunas e o rodapé do card mostra FK 1 e IDX 1.
Existe a linha 1:n entre tecnico e especialidade, com a coluna especialidade_id.
tecnico.gerente_id existe, é Int (Inteiro) e está com IDX marcado.
A toolbar mostra Salvo agora (nenhuma alteração pendente).
Sei explicar, em uma frase, para que serve a coluna descritiva.
Trazer as outras onze tabelas do sistema de uma vez com o Importar schema SQL, cadastrar os status da ordem de serviço, configurar dados fake para o Teste Online e marcar um checkpoint do modelo.
O que você terá no fim
14 tabelas no modelo Ordem de Serviço (186 colunas), com os relacionamentos desenhados.
As três tabelas da aula 02 preservadas — elas aparecem como Já existe — pulada.
Os seis status da OS cadastrados como dados fixos em status_os.
Seeds de dev configurados para as tabelas principais.
Um checkpoint manual chamado modelo completo.
O id de todas as tabelas com numeração automática (AI) — o importador lê o SERIAL do script e já marca.
Noção do que o importador lê do script e do que ele ignora.
Studio › Modelos de dados › Importar Schema SQL
Modal Importar Schema SQL com a aba Colar SQL ativa e o script colado na área de texto
Studio › Modelos de dados › Importar Schema SQL
Mesmo modal depois de Analisar, recortado acima da lista de tabelas: Banco de dados PostgreSQL, Origem Colar SQL, o script na área de texto, a linha 14 tabela(s) · 121 coluna(s) · 16 FK(s) ao lado do botão Analisar e o bloco Antes de importar com o aviso de colunas obrigatórias
Studio › Modelos de dados › Importar Schema SQL
Bloco 'Antes de importar — o que este dump não traz' com o aviso ℹ️ de 28 colunas NOT NULL sem DEFAULT
Studio › Modelos de dados › Ordem de Serviço
Canvas com as 14 tabelas e as linhas de relacionamento, enquadrado com Ajustar à tela
Studio › Modelos de dados › status_os › Inserir dados de teste
Modal Inserir dados — status_os na aba Dados fixos, com as seis linhas de status preenchidas (inclusive a coluna id, que é obrigatória)
Studio › Modelos de dados › Seeds de dev (Faker)
Passo 2 de 2 do assistente Seeds de dev (Faker), mostrando Registros e a coluna Provider Faker de uma tabela
Studio › Modelos de dados › Checkpoints do modelo de dados
Painel Checkpoints do modelo de dados com o checkpoint manual v1 'modelo completo' selecionado (14 tab · 186 col) e a comparação De/Até com o estado atual à direita
O script da aula
Cole exatamente este conteúdo (ele também está em sql/a03-importar.sql). As três primeiras tabelas estão ali de propósito, para você ver o comportamento de "já existe":
-- Aula 03 — cole este script em Modelos de dados → Importar schema SQL → Colar SQL.
-- Só CREATE TABLE com REFERENCES inline (é o que o importador lê).
-- cliente, especialidade e tecnico já existem (aula 02): aparecem como "já existe" e são puladas.
CREATE TABLE cliente (
id SERIAL PRIMARY KEY,
nome_razao_social VARCHAR(200) NOT NULL
);
CREATE TABLE especialidade (
id SERIAL PRIMARY KEY,
nome VARCHAR(100) NOT NULL
);
CREATE TABLE tecnico (
id SERIAL PRIMARY KEY,
nome VARCHAR(150) NOT NULL
);
CREATE TABLE status_os (
id SERIAL PRIMARY KEY,
codigo VARCHAR(30) NOT NULL UNIQUE,
nome VARCHAR(60) NOT NULL,
cor VARCHAR(20),
ordem INTEGER DEFAULT 0,
e_final BOOLEAN DEFAULT false
);
CREATE TABLE tecnico_especialidade (
id SERIAL PRIMARY KEY,
tecnico_id INTEGER NOT NULL REFERENCES tecnico(id),
especialidade_id INTEGER NOT NULL REFERENCES especialidade(id),
nivel VARCHAR(20),
dt_certificacao DATE
);
CREATE TABLE equipamento (
id SERIAL PRIMARY KEY,
cliente_id INTEGER NOT NULL REFERENCES cliente(id),
codigo_patrimonio VARCHAR(50),
nome VARCHAR(200) NOT NULL,
tipo VARCHAR(100),
marca VARCHAR(100),
modelo VARCHAR(100),
numero_serie VARCHAR(100),
dt_aquisicao DATE,
dt_garantia_fim DATE,
localizacao_descricao VARCHAR(250),
ativo BOOLEAN DEFAULT true
);
CREATE TABLE servico (
id SERIAL PRIMARY KEY,
codigo VARCHAR(30) UNIQUE,
nome VARCHAR(200) NOT NULL,
descricao TEXT,
especialidade_id INTEGER REFERENCES especialidade(id),
preco NUMERIC(12,2) DEFAULT 0,
duracao_estimada_min INTEGER,
ativo BOOLEAN DEFAULT true
);
CREATE TABLE peca (
id SERIAL PRIMARY KEY,
sku VARCHAR(50) UNIQUE,
ean VARCHAR(20),
nome VARCHAR(200) NOT NULL,
descricao TEXT,
unidade_medida VARCHAR(10) DEFAULT 'un',
preco_custo NUMERIC(12,2) DEFAULT 0,
preco_venda NUMERIC(12,2) DEFAULT 0,
estoque_atual NUMERIC(12,3) DEFAULT 0,
estoque_minimo NUMERIC(12,3) DEFAULT 0,
ativo BOOLEAN DEFAULT true
);
CREATE TABLE ordem_servico (
id SERIAL PRIMARY KEY,
numero VARCHAR(30) NOT NULL UNIQUE,
titulo VARCHAR(150) NOT NULL,
cliente_id INTEGER NOT NULL REFERENCES cliente(id),
equipamento_id INTEGER REFERENCES equipamento(id),
tecnico_id INTEGER REFERENCES tecnico(id),
status_os_id INTEGER NOT NULL REFERENCES status_os(id),
tipo VARCHAR(30) DEFAULT 'corretiva',
prioridade VARCHAR(20) DEFAULT 'media',
descricao_problema TEXT NOT NULL,
descricao_solucao TEXT,
dt_abertura TIMESTAMP NOT NULL,
dt_agendada TIMESTAMP,
dt_prevista TIMESTAMP,
dt_inicio DATE,
dt_fim DATE,
progresso INTEGER DEFAULT 0,
os_pai_id INTEGER, -- OS pai (hierarquia do Gantt); auto-FK não é suportada no modelo, fica como inteiro
dt_conclusao TIMESTAMP,
horas_trabalhadas NUMERIC(10,2) DEFAULT 0,
valor_servicos NUMERIC(12,2) DEFAULT 0,
valor_pecas NUMERIC(12,2) DEFAULT 0,
valor_deslocamento NUMERIC(12,2) DEFAULT 0,
valor_total NUMERIC(12,2) DEFAULT 0,
status_aprovacao VARCHAR(20) DEFAULT 'rascunho',
observacoes TEXT,
created_at TIMESTAMP,
updated_at TIMESTAMP,
deleted_at TIMESTAMP
);
CREATE TABLE os_servico_item (
id SERIAL PRIMARY KEY,
ordem_servico_id INTEGER NOT NULL REFERENCES ordem_servico(id),
servico_id INTEGER NOT NULL REFERENCES servico(id),
quantidade NUMERIC(10,2) DEFAULT 1,
preco_unitario NUMERIC(12,2) DEFAULT 0,
desconto_pct NUMERIC(5,2) DEFAULT 0,
subtotal NUMERIC(12,2) DEFAULT 0,
observacao VARCHAR(250)
);
CREATE TABLE os_peca_item (
id SERIAL PRIMARY KEY,
ordem_servico_id INTEGER NOT NULL REFERENCES ordem_servico(id),
peca_id INTEGER NOT NULL REFERENCES peca(id),
quantidade NUMERIC(10,3) NOT NULL DEFAULT 1,
preco_unitario NUMERIC(12,2) DEFAULT 0,
subtotal NUMERIC(12,2) DEFAULT 0,
numero_serie VARCHAR(100)
);
CREATE TABLE os_anexo (
id SERIAL PRIMARY KEY,
ordem_servico_id INTEGER NOT NULL REFERENCES ordem_servico(id),
tipo VARCHAR(20),
nome_arquivo VARCHAR(200),
arquivo VARCHAR(500),
imagem VARCHAR(500),
assinatura_base64 TEXT,
nome_signatario VARCHAR(150),
codigo_lido VARCHAR(100),
descricao VARCHAR(250),
created_at TIMESTAMP
);
CREATE TABLE os_historico (
id BIGSERIAL PRIMARY KEY,
ordem_servico_id INTEGER NOT NULL REFERENCES ordem_servico(id),
titulo VARCHAR(120) NOT NULL,
observacao TEXT,
dt_evento TIMESTAMP NOT NULL,
icone VARCHAR(40),
cor VARCHAR(20),
status_anterior VARCHAR(60),
status_novo VARCHAR(60),
usuario_nome VARCHAR(120)
);
CREATE TABLE os_apontamento (
id BIGSERIAL PRIMARY KEY,
ordem_servico_id INTEGER NOT NULL REFERENCES ordem_servico(id),
tecnico_id INTEGER NOT NULL REFERENCES tecnico(id),
dt_referencia DATE NOT NULL,
tipo VARCHAR(20) DEFAULT 'execucao',
horas NUMERIC(6,2) NOT NULL DEFAULT 0,
valor_hora NUMERIC(12,2) DEFAULT 0,
descricao_atividade VARCHAR(250)
);
Abra Modelos de dados e entre no modelo Ordem de Serviço.
Na toolbar, clique em Importar schema SQL. Abre o modal Importar Schema SQL.
Em Banco de dados, escolha PostgreSQL. Esse campo é o dialeto do script que você vai colar (MySQL / MariaDB, PostgreSQL, SQLite, Oracle, SQL Server, Firebird), não o banco do projeto — ele muda como o importador lê tipos como character varying e TINYINT(1).
Em Origem, deixe Colar SQL selecionado. A outra opção, Upload arquivo, aceita .sql ou .txt.
Cole o script inteiro na área de texto.
Clique em Analisar. Nada é criado ainda — é leitura.
Confira a linha de totais: 14 tabela(s) · 121 coluna(s) · 16 FK(s).
Leia o bloco Antes de importar — o que este dump não traz. Ele lista o que o script não resolve e o que você vai precisar ajustar depois. Com este script aparece um aviso:
ℹ️ 28 coluna(s) NOT NULL sem DEFAULT — o formulário gerado vai exigir o preenchimento. Se o dado antigo não tem valor, ajuste a coluna ou defina um padrão.
O que não aparece também informa: não há aviso de chave primária sem sequence, porque o importador lê o SERIAL/BIGSERIAL do script e já marca o selo AI na chave primária.
Se você usa uma versão anterior da plataformae vir o aviso ⚠️ *N tabela(s) com chave primária inteira SEM sequence*, o id das tabelas importadas entrou como inteiro comum: abra o id de cada uma com duplo clique, marque o selo AI e feche com Esc. Sem isso, o CRUD da aula 04 pede o número do registro em toda gravação.
O bloco Criar N relação(ões) sugerida(s) pelos nomes das colunasnão aparece com este script: ele só existe quando o dump não traz nenhuma relação declarada, e o nosso declara todas com REFERENCES. Se um dia aparecer, deixe-o desmarcado até conferir cada palpite.
Na lista de pré-visualização, confirme que cliente, especialidade e tecnico estão marcadas como Já existe — pulada, e que as outras onze estão como Pronta para importar.
Clique em Importar 11 tabela(s) e aguarde. O resultado mostra os totais Tabelas, Colunas e FKs criados, a lista Puladas (3) (cliente, especialidade e tecnico — *já existe no diagrama*) e a mensagem Importação concluída sem erros. Clique em Fechar.
As tabelas novas entram empilhadas a partir do centro da tela, muitas vezes por cima das que já existiam. Arraste os cards para organizar (o diagrama grava a posição sozinho) e use Ajustar à tela na toolbar para ver o modelo inteiro. Com tudo à vista, repare no card ordem_servico: o os_pai_id é um inteiro comum, sem linha de ligação — é a mesma limitação de auto-relacionamento da aula 02, e o Gantt da aula 18 trabalha com a coluna.
Confira uma das importadas: dê um duplo clique na linha id de equipamento. O selo AI já está marcado — veio do SERIAL. Feche com Esc.
Atalho para achar cada tabela: use a lista da esquerda (Buscar tabela…) e o botão Ver … no diagrama, que centraliza o card no canvas.
Opcional, só para conhecer: o botão Template de tabelas traz modelos pré-prontos (CRUDs, kanban, calendário, gantt). Não usamos no curso porque queremos o modelo do zero.
Cadastre os status da OS. Passe o mouse sobre o card status_os (ou clique nele): os botões de ação aparecem em volta do card. Clique no de Inserir dados de teste. Na aba Dados fixos, use Adicionar linha e preencha:
id
codigo
nome
cor
ordem
e_final
1
ABERTA
Aberta
#8b96ad
1
false
2
AGENDADA
Agendada
#1E55E8
2
false
3
EM_ATENDIMENTO
Em atendimento
#F26B1F
3
false
4
AGUARDANDO_PECA
Aguardando peça
#d98e00
4
false
5
CONCLUIDA
Concluída
#3DA86F
5
true
6
CANCELADA
Cancelada
#D93A2B
6
true
O id é obrigatório: a grade exige valor em toda coluna Not null, e a chave primária é uma delas. Se deixar em branco, o Salvar não fecha e aparece *Todos os campos de "id" são obrigatórios.* As colunas created_at, updated_at e deleted_at também estão na grade (elas vieram das colunas padrão do modelo) e podem ficar vazias.
A última coluna da grade (StatusOs::CONST) é opcional: o nome que você digitar ali vira uma constante no código gerado. Deixe em branco se não for usar.
Clique em Salvar.
Na toolbar, clique em Seeds de dev (Faker). Abre o assistente Seeds de dev (Faker) — configuração em lote.
No Passo 1 de 2, marque as tabelas que devem receber dados fake: cliente, tecnico, equipamento, servico, peca, ordem_servico, os_servico_item, os_peca_item, os_historico e os_apontamento. Cada tabela marcada já mostra o campo Registros ali mesmo (sugestão: 20 para cliente e equipamento, 8 para tecnico, 40 para ordem_servico, 10 para as demais). Clique em Avançar.
No Passo 2 de 2, confira a coluna Provider Faker de cada coluna (e ajuste os Registros, se preferir). O provider Auto (inferido) já acerta a maioria: nome, e-mail, telefone, CNPJ, cidade, preço. A chave primária aparece como ID sequencial (auto) e as FKs como FK → tabela (sorteia do alvo).
Clique em Salvar tudo. Os dados não são gerados agora: eles nascem no Teste Online, quando você clicar em Popular dados de teste (aula 06) — é o que diz o rodapé do assistente.
Na toolbar, clique em Checkpoints do modelo. Abre o painel Checkpoints do modelo de dados.
Clique em Criar checkpoint manual, preencha Nome do checkpoint com modelo completo e clique em Salvar. Ele passa a aparecer na lista como v1, com a marca MANUAL e o resumo 14 tab · 186 col.
Selecione o checkpoint na lista e veja a área de comparação (De / Até) e o Preview da migration. Logo depois de criado, comparado com Estado atual do modelo, o painel mostra 0 mudança — é assim que você compara o modelo de hoje com o de qualquer marco. Se você mexer em algo depois, a diferença aparece ali: no print da aula, os dados fixos de status_os gravados depois do checkpoint aparecem em Dados de seed alterados (informativo — seeds não re-rodam em instalações existentes).
Ainda na toolbar, abra Migrations do projeto para ver a tela Migrations & seeds do projeto — a lista dos arquivos que a próxima publicação vai emitir (migrations/ com o *create tables* e o *add foreign keys*, seeders/ProjectDevSeeder.php e os .sql de inserts). É só pré-visualização; nada é gravado. Feche.
Por que status virou tabela?Se status fosse um texto dentro de ordem_servico, o kanban da aula 13 não teria colunas para montar, e mudar "Em atendimento" para "Em execução" exigiria mexer em todos os registros. Como tabela, o status tem cor, ordem e a marca de etapa final.
Erros comuns
O formulário gerado pede o número do registro (id) em toda gravação
a chave primária da tabela está sem o selo AI. Em versão atual da plataforma o importador lê o SERIAL/BIGSERIAL e marca sozinho; se a análise tiver avisado N tabela(s) com chave primária inteira SEM sequence (versões anteriores), abra o id de cada tabela importada e marque o AI na mão.
O Salvar de Inserir dados não fecha o modal, com *Todos os campos de "id" são obrigatórios.
* → a grade exige valor em toda coluna Not null, e a chave primária é uma delas → preencha o id de cada linha (1 a 6 no caso dos status).
"Nenhum CREATE TABLE encontrado no script."
você colou só uma parte, ou colou um dump com INSERT/ALTER sem os CREATE TABLE → cole o arquivo inteiro a03-importar.sql.
As três tabelas da aula 02 foram importadas de novo (duplicadas)
o nome no diagrama está diferente do nome no script (clientes × cliente, por exemplo) → o importador compara por nome exato; exclua a tabela duplicada e renomeie para bater com o script.
Nenhuma linha de relacionamento apareceu
você colou uma versão do script sem os REFERENCES → reimporte com o script desta aula, ou ligue as tabelas na mão com a ferramenta de cardinalidade (aula 02).
ordem_servico não tem linha para ela mesma
é esperado: os_pai_id é um inteiro comum, porque a plataforma não cria chave estrangeira de uma tabela para ela mesma (aula 02) → o Gantt da aula 18 usa a coluna, não a constraint.
O importador criou colunas que não estão no script
são as colunas padrão do modelo (created_at, updated_at, deleted_at, da aula 02): o import também as aplica nas tabelas novas, pulando as que o script já declara.
O índice que criei na aula 02 sumiu do script gerado
o importador ignora CREATE INDEX e UNIQUE composto, e essas duas coisas se criam pela interface → índice fica no popover da coluna (selo IDX) ou na seção Índices da tabela.
Cliquei em Salvar tudo nos seeds e nenhum dado apareceu
seeds de dev não geram linhas na hora; eles rodam no Teste Online → clique em Popular dados de teste no painel do Teste Online, na aula 06.
Uma coluna entrou como "Customizado"
o tipo do script não é reconhecido pelo importador → abra a coluna e escreva o tipo SQL em Tipo customizado, senão ela é publicada como varchar(255).
Checklist de encerramento
O canvas mostra 14 tabelas (186 colunas no total).
O id das 11 tabelas importadas já veio com AI marcado (conferi em pelo menos uma).
cliente, especialidade e tecnico continuam com as colunas da aula 02 (não foram sobrescritas).
ordem_servico tem as ligações para cliente, equipamento, tecnico e status_os (o os_pai_id fica como inteiro, sem ligação).
status_os tem as seis linhas de status, com id de 1 a 6, cor e ordem.
Os seeds de dev estão salvos para pelo menos cliente, tecnico, equipamento e ordem_servico.
O checkpoint manual modelo completo aparece na lista.
Abri Migrations do projeto e vi a lista de arquivos da próxima publicação.
Transformar as tabelas do modelo em telas de verdade — formulário e listagem por tabela, mais um formulário mestre-detalhe para a ordem de serviço — numa única execução.
O que você terá no fim
Formulário e listagem gerados para cliente, especialidade, tecnico, servico, peca, equipamento e status_os.
Um formulário mestre-detalhe de ordem_servico com serviços, peças e anexos.
Os módulos Cadastros e Operação criados direto no gerador, com as telas já distribuídas entre eles.
Entendimento do que o chip JÁ EXISTE protege.
Noção do que dá para mudar depois (tudo) e do que é melhor decidir agora (o módulo).
Studio › Geração de CRUDs em lote
Coluna esquerda Padrões para todas, com Duas colunas selecionado e Página inteira, Listagem com busca no cabeçalho, Toast e Módulo padrão Cadastros preenchidos
Studio › Geração de CRUDs em lote
Lista central com o contador 7 / 14 selecionadas no topo e os primeiros cards marcados (cliente, equipamento, especialidade) mostrando as pílulas Módulo/Nomes/Rótulos/Rota
Studio › Geração de CRUDs em lote › Personalizar — OrdemServico
Modal Personalizar — OrdemServico com a seção Master-detail aberta, Gerar como Master-Detail marcado e as pílulas FieldList/DetailForm por tabela detalhe
Studio › Geração de CRUDs em lote
Coluna direita Resumo da geração, com 8 Tabelas, 16 Páginas e 32 Arquivos e a lista Arquivos a gerar agrupada por módulo
Studio › Geração de CRUDs em lote
Topo do gerador com a faixa Concluído: 8 criados, 0 já existiam, 0 falharam
Passo a passo
No rail, abra Gerar CRUDs em lote. A aba Geração de CRUDs em lote ocupa a tela em três colunas (o gerador não é um modal: é uma aba do workspace, e o botão Gerar fica no topo).
Na coluna esquerda, Padrões para todas, configure:
Layout do formulário: Duas colunas
Apresentação do formulário: Página inteira
Tipo de listagem: Listagem com busca no cabeçalho
Mensagem após salvar: Toast
Em Módulo padrão, clique no + à direita do combo (dica: Novo módulo). O combo vira um campo com o texto de apoio Nome do novo módulo: digite Cadastros e confirme no botão ✓ (dica: Criar módulo). O módulo é criado na hora e já fica selecionado.
Na coluna do meio, confira o nome do banco (base: assistec) e use o campo Filtrar tabelas… se precisar. A lista vem em ordem alfabética, não na ordem do diagrama.
Marque estas sete tabelas:
Tabela
O que vira
cliente
formulário + listagem
equipamento
formulário + listagem
especialidade
formulário + listagem
peca
formulário + listagem
servico
formulário + listagem
status_os
formulário + listagem
tecnico
formulário + listagem
O contador no topo da lista passa a mostrar 7 / 14 selecionadas. Cada card marcado abre quatro pílulas de ajuste rápido — Módulo, Nomes, Rótulos e Rota — já preenchidas a partir dos padrões (Módulo: Cadastros, Nomes: ClienteForm, ClienteList, Rótulos: Clientes, Rota: /clientes).
Não marque tecnico_especialidade, os_servico_item, os_peca_item, os_anexo, os_historico nem os_apontamento. Elas são tabelas filhas: aparecem dentro de outras telas.
Marque também ordem_servico e clique na engrenagem do card (dica: Personalizar). Abre o modal Personalizar — OrdemServico: o título usa o nome amigável da tabela e o nome físico (ordem_servico) fica no subtítulo.
Desça até a seção Master-detail. O subtítulo diz 5 tabela(s) detalhe detectada(s) via FK.
Marque Gerar como Master-Detail e ajuste a lista:
Tabela detalhe
FK
Marcar?
Estilo
os_servico_item
ordem_servico_id
sim
FieldList
os_peca_item
ordem_servico_id
sim
FieldList
os_anexo
ordem_servico_id
sim
DetailForm
os_historico
ordem_servico_id
não
—
os_apontamento
ordem_servico_id
não
—
Cada linha nasce desmarcada e com DetailForm aceso; as pílulas só ficam clicáveis depois de marcar a linha.
Ainda no modal, na seção Módulo / Submódulo / Sub-submódulo (subtítulo Padrão: Cadastros), crie e escolha o módulo Operação pelo +, do mesmo jeito do passo 3. Submódulo e Sub-submódulo ficam desabilitados enquanto o módulo não tiver filhos (— sem submódulos — / — n/a —). Feche em OK.
Confira a coluna direita, Resumo da geração: 8 Tabelas, 16 Páginas, 32 Arquivos (.php + .blade.php), a lista Arquivos a gerar agrupada por módulo e a estimativa (~9s para gerar · Você pode editar tudo depois).
Clique em Gerar 8 CRUDs, no topo. A faixa mostra Gerando X/8… com a porcentagem e o botão de cancelar vira Cancelar execução enquanto roda.
No fim aparece a faixa Concluído: 8 criados, 0 já existiam, 0 falharam. A seleção é zerada no sucesso; se algo falhar ou você cancelar, ela é preservada e a faixa avisa *Seleção mantida — clique em Gerar para tentar de novo*.
Abra Arquivos no rail e confirme as 16 telas geradas: ClienteForm e ClienteList, EquipamentoForm/EquipamentoList, EspecialidadeForm/EspecialidadeList, PecaForm/PecaList, ServicoForm/ServicoList, StatusOsForm/StatusOsList, TecnicoForm/TecnicoList e OrdemServicoForm/OrdemServicoList.
Abra uma delas com um duplo clique só para ver o editor visual. Não mexa em nada ainda — a edição começa na aula 07.
Por que decidir o módulo agora?O módulo é o lugar da tela no menu do sistema gerado (e é o que a aula 05 organiza). Os controllers em si nascem em app/control/ com o nome da classe (app/control/ClienteForm.php) — o módulo não muda esse caminho. Os outros padrões — layout, apresentação, tipo de listagem, mensagem — você troca tela a tela, quando quiser.
Erros comuns
A tabela aparece com o chip JÁ EXISTE e não é gerada
já existe uma página no projeto com o mesmo controller → é proteção: seu trabalho anterior não é sobrescrito. O card fica esmaecido e o clique não marca nada. Renomeie a página antiga ou troque o nome na pílula Nomes do card.
"Nenhuma tabela detalhe detectada (sem FKs apontando para esta)"
a tabela filha não tem chave estrangeira apontando para a tabela mestre → confira o modelo na aula 03; sem a FK o gerador não tem como saber quem é filho de quem.
A própria ordem_servico apareceu na lista de tabelas detalhe
só acontece se você tiver criado uma FK de os_pai_id para a própria tabela. No modelo do curso os_pai_id é um inteiro simples (a auto-FK é bloqueada pela plataforma), então a lista traz cinco detalhes e nenhum deles é a própria OS. Se aparecer, desmarque — senão o formulário nasce com uma grade de OS dentro da OS.
O botão Gerar está desabilitado
nenhuma tabela marcada → marque pelo menos uma; o contador no topo da lista mostra quantas estão selecionadas.
Gerei sem escolher módulo e as telas ficaram "sem módulo"
o Módulo padrão estava vazio → dá para arrumar pelos Módulos e pelo Editor de menus (aula 05); a tela continua funcionando, só não tem lugar no menu.
Faltou uma tela que eu queria
a tabela não estava marcada, ou o filtro escondeu ela → limpe o campo Filtrar tabelas…, marque a tabela e gere de novo. O gerador pula o que já existe.
Checklist de encerramento
O módulo Cadastros existe e é o Módulo padrão do gerador.
O módulo Operação existe e é o módulo de ordem_servico.
Em Arquivos aparecem formulário e listagem das sete tabelas de cadastro (14 telas).
Existe OrdemServicoForm gerado como mestre-detalhe e OrdemServicoList.
O formulário da OS tem serviços e peças como FieldList e anexos como DetailForm.
Nenhuma tela foi gerada para os_historico, os_apontamento ou tecnico_especialidade.
A faixa de conclusão mostrou 8 criados, 0 já existiam, 0 falharam.
Organizar as telas geradas em módulos, arrumar o menu que o usuário vai ver e definir quem enxerga o quê, com grupos de permissão e perfis de acesso.
O que você terá no fim
Três módulos: Cadastros, Operação e Relatórios, com ícone, cor e posição de menu.
Dois submódulos dentro de Cadastros (Pessoas e Catálogo).
O Menu lateral com os grupos na ordem certa e salvo.
Três grupos de permissão: Atendimento, Técnico e Financeiro.
Quatro perfis de acesso: Atendente, Técnico, Gerente e Diretoria.
Entendimento da diferença entre módulo (organização), grupo (pacote de telas) e perfil (quem recebe o pacote).
Studio › Módulos › Módulo: Cadastros
Painel Módulos (coluna de 360px à esquerda) com Operação, Cadastros e Relatórios, e a aba Módulo: Cadastros aberta à direita com Páginas 14 / Tabelas 7 / Submódulos 2, os cards Catálogo e Pessoas e o bloco Detalhes do módulo
Studio › Editor de menus › Menu lateral
Editor de menus na aba Menu lateral, modo Apenas visual, com Operação em cima (1 item), Cadastros aberto (9 itens) e Relatórios vazio
Studio › Grupos de permissão
Grupos de permissão com Atendimento selecionado, a seção Páginas 6 de 16 aberta e as ações Visualizar/Incluir/Editar marcadas em cada tela
Studio › Perfis de acesso
Perfis de acesso com o perfil Gerente selecionado e a seção Grupos 3 de 6 mostrando Atendimento, Financeiro e Técnico marcados
Passo a passo
No rail, no grupo Módulos & acesso, abra Módulos. Ele abre um painel à esquerda (não é aba): o clique no item do rail é um interruptor — clicar de novo fecha o painel. Os módulos Cadastros e Operação já estão lá, criados pelo gerador na aula 04, sem cor e sem ícone.
Clique no + do cabeçalho do painel (dica: Novo módulo (raiz)) e preencha:
Tipo: Módulo — *Domínio de negócio (raiz)* (os outros cartões são Submódulo e Sub-submódulo)
Nome: Relatórios
Código: deixe o valor sugerido (relato, curto e em snake_case)
O bloco Caminho: mostra app/Modules/Relatórios enquanto você digita
Posição do menu: Lateral esquerdo (as outras são Superior e Dropdown)
Controller / Método: deixe vazios (só para módulo que abre uma tela ao ser clicado)
Aparência: escolha uma cor da paleta e clique em Escolher ícone — o seletor busca entre os ícones Lucide pelo nome (ex.: FileText)
Descrição (opcional): Relatórios operacionais e financeiros
Clique em Criar módulo.
Passe o mouse na linha Cadastros da árvore: aparece um + no fim da linha (dica: Adicionar submódulo). Crie Pessoas (para cliente e tecnico) e repita com Catálogo (para servico, peca e equipamento). No modal de submódulo o botão final é Criar submódulo.
Clique num módulo: abre a aba Módulo: <nome> no workspace, com Páginas · Tabelas · Submódulos · Última edição no topo, os cards dos submódulos, o bloco Detalhes do módulo (Identificador, Caminho, Permissões, Cor, Ícone, Visível no menu) e a Zona de perigo com Excluir módulo. É aqui, no detalhe, que se ajusta a aparência dos módulos que vieram do gerador: escolha uma Cor na paleta e clique em Escolher no campo Ícone (em módulo que já tem um, o botão é Trocar). Faça isso em Cadastros e em Operação.
Abra Editor de menus no rail — ele fica no grupo Geração & ferramentas, não junto dos outros três painéis desta aula. As abas em cima são os quatro menus do sistema: Menu lateral, Menu superior, Menu público e Dropdown da navbar. Fique em Menu lateral. À direita fica o modo: Apenas visual (padrão) ou Edição completa.
Cada item tem uma alça de arrasto (⠿) à esquerda. Antes de reordenar, recolha os grupos pelas setinhas: com os grupos abertos você solta o item dentro da lista de filhos sem querer — e isso não é só cosmético (veja o aviso abaixo). Ordem sugerida: Operação, Cadastros, Relatórios.
O botão Item (criar item de menu do zero) só existe no modo Edição completa. No modo Apenas visual os itens vêm das telas e dos módulos, e você só reordena, renomeia, troca ícone/cor e exclui.
Confira o resultado antes de gravar, na barra do editor: A–Z reordena o menu inteiro em ordem alfabética, Código abre Código gerado (menu.xml) e Diff abre Diferenças do menu (à esquerda o menu atual, à direita o resultado das suas alterações). No modo Apenas visual a barra tem só esses três botões mais o Salvar.
Clique em Salvar. O aviso alterações não salvas some e aparece Menu salvo.
Abra Grupos de permissão no rail (aqui é aba, não painel) e clique em Novo grupo.
Em Identificação, preencha Nome do grupo com Atendimento. O campo Code pode ficar vazio — é gerado automaticamente.
Na seção Páginas (contador N de 16), marque as telas — elas aparecem pelo nome do controller, com o selo MAD. Ao marcar, abre embaixo a linha AÇÕES com Visualizar · Incluir · Editar · Excluir · Exportar. Use este desenho:
A seção Códigos lista controllers e classes do projeto. Aqui ela está em 0 de 0 — entra na aula 26, quando existir código customizado.
Clique em Salvar grupo (o botão largo no fim do formulário; o Salvar do cabeçalho faz a mesma coisa) e repita os passos 11 a 14 para Técnico e Financeiro.
Abra Perfis de acesso no rail e clique em Novo perfil.
Em Dados do perfil, o Code é gerado do nome (Diretoria → diretoria) — e é por ele que os Fluxos de Aprovação da aula 22 encontram o aprovador; só preencha à mão se quiser um código diferente do nome. Preencha Nome e Descrição:
Perfil
Descrição
Grupos marcados
Atendente
Abre e acompanha ordens de serviço
Atendimento
Técnico
Executa e atualiza as ordens em campo
Técnico
Gerente
Acompanha a operação inteira
Atendimento, Técnico, Financeiro
Diretoria
Aprova orçamentos acima de R$ 5.000
Financeiro
Na seção Grupos, marque os grupos do perfil. A lista traz seis: os três que você criou mais um por módulo (Operação, Cadastros, Relatórios), que a plataforma mantém sozinha.
A seção Programas extras serve para liberar uma tela solta, com ações próprias, sem precisar criar um grupo só para ela. Deixe vazia por enquanto.
Clique em Salvar perfil e repita para os outros três.
A diferença em uma frase.Módulo é onde a tela aparece no menu. Grupo de permissão é um pacote de telas com as ações permitidas. Perfil é a função da pessoa, que recebe um ou mais grupos. Quem cria usuário e escolhe o perfil é o administrador, já dentro do sistema gerado.
⚠️ Arrastar entre grupos MOVE a tela de módulo. No modo Apenas visual, o Salvar do editor de menus grava a *posição* de cada item de volta na tela correspondente. Soltar Ordem de Serviços dentro de Cadastros não muda só o menu: a página passa a pertencer ao módulo Cadastros. Por isso a regra do passo 7 — recolha os grupos antes de reordenar, e se errar, arraste de volta e salve outra vez.
Erros comuns
Criei o módulo e ele não aparece no menu do sistema
o interruptor Visível no menu está desligado, ou a Posição do menu não é Lateral esquerdo → ajuste na aba de detalhe do módulo e republique o Teste Online.
Criei o módulo mas ele não aparece no menu do app
módulo sem nenhuma tela dentro não é desenhado no menu do sistema gerado. Relatórios só aparece a partir da aula 15/16, quando ganhar a primeira página.
Não consigo excluir um módulo
ele tem filhos diretos → a própria tela avisa: "Filhos diretos precisam ser removidos antes". Exclua os submódulos primeiro.
Arrastei um item e ele entrou dentro do grupo errado
a lista de filhos do grupo aberto ocupa quase toda a altura dele e "engole" o drop → recolha os grupos antes de reordenar. Se já salvou, arraste de volta para o grupo certo e salve de novo (isso devolve a página ao módulo).
Salvei o menu e ele voltou ao que era
o menu estava em Apenas visual e a plataforma reconciliou o arquivo ao gerar páginas novas → se você quer ser dono do arquivo, use Assumir controle (Edição completa); aí o MadBuilder para de mexer nele, e itens de telas novas passam a ser sua responsabilidade.
Perdi as alterações do menu ao fechar a aba
o editor guarda um rascunho no navegador (rascunho salvo), mas ele não substitui o Salvar → salve antes de sair; se o servidor mudou no meio, a plataforma pergunta se você quer Manter rascunho ou Descartar.
"Selecione ao menos uma página ou código"
ao salvar o grupo → o grupo está vazio → marque pelo menos uma tela na seção Páginas.
Criei os perfis mas todo mundo continua vendo tudo
permissão só passa a valer depois de publicar → na aula 06 você publica; o menu do sistema muda conforme o perfil do usuário logado.
Checklist de encerramento
Existem os módulos Cadastros, Operação e Relatórios.
Cadastros tem os submódulos Pessoas e Catálogo.
Todos os módulos têm ícone, cor e Visível no menu ligado.
O Menu lateral está na ordem Operação · Cadastros · Relatórios e foi salvo (Menu salvo).
Ordem de Serviços continua dentro de Operação no menu.
Existem os grupos Atendimento, Técnico e Financeiro, cada um com telas e ações.
Existem os perfis Atendente, Técnico, Gerente e Diretoria.
O perfil Gerente acumula os três grupos.
Sei explicar a diferença entre módulo, grupo de permissão e perfil.
Colocar o sistema no ar num ambiente descartável, entrar nele com usuário e senha e ver, pela primeira vez, as telas, o menu e o banco funcionando de verdade.
O que você terá no fim
Um ambiente de Teste Online publicado, com URL própria.
A senha do admin definida por você.
O banco criado, as tabelas migradas e os seis status já cadastrados.
O sistema aberto no navegador, com o menu da aula 05.
Clareza sobre a diferença entre publicar de novo, popular dados e recriar o ambiente.
Studio › Teste online
Painel Teste Online (coluna de 300px à esquerda do workspace) com Sem ambiente ativo, Nenhum deploy realizado ainda, Credenciais do admin, Servidor/Banco/Conector e o botão laranja Testar agora
Studio › Teste online › Credenciais do admin
Card Credenciais do admin com Login admin, o campo Senha preenchido e mascarado e o botão Salvar aparecendo
Studio › Teste online
Bloco Deploy em andamento com os cinco passos: metadados, arquivos e container concluídos, Subindo banco riscado (pulado, banco SQLite) e Publicando código em andamento
Aplicação › Início
Aplicação gerada aberta e autenticada, com a tira de ícones de módulo à esquerda e o módulo Cadastros aberto listando Clientes, Especialidades, Tecnicos, Status de Os, Equipamentos, Serviços e Pecas
Aplicação › Cadastros › Clientes
Listagem de clientes da aplicação gerada já com os dados do popular: busca no cabeçalho, linhas do Faker, paginação 1–15 de 19 e o botão Novo. ATENÇÃO — a captura foi feita no projeto de referência, onde a listagem JÁ passou pela aula 08 (colunas escolhidas, rótulos em português, filtro por coluna). Na sua gravação, aqui a listagem ainda mostra TODAS as colunas da tabela, com os nomes crus
Studio › Teste online › Sync estrutural
Bloco Sync estrutural aberto, com as entradas Estrutura sincronizada · baseline via deploy e o horário de cada publicação
Passo a passo
No rail, abra Teste online. O painel abre à esquerda do workspace (é painel, não aba — e o clique no rail é interruptor: clicar de novo fecha). Se ainda não houve publicação, o estado é Sem ambiente ativo e a mensagem Nenhum deploy realizado ainda.
No card Credenciais do admin, confira o Login (admin) — a Senha já vem sorteada e mascarada. Digite uma senha sua, com no mínimo seis caracteres: o botão Salvar só aparece depois que você mexe no campo. Clique em Salvar.
Se o ambiente ainda não existe, a confirmação é Senha salva — vale a partir do próximo publish.
Com o ambiente no ar, ela vira Senha aplicada no ambiente ativo.
Use uma senha exclusiva deste ambiente de teste. Não reaproveite senha de e-mail ou banco.
O olho ao lado do campo revela a senha; o ícone de cópia copia sem revelar.
Confira Servidor (PHP 8.4), Banco de dados (SQLite) e Conector (Banco do teste online). Deixe nos valores padrão.
Clique em Testar agora. O painel mostra Deploy em andamento e percorre os cinco passos: Preparando metadados → Gerando arquivos → Criando container → Subindo banco → Publicando código.
Com SQLite, Subindo banco aparece riscado: não existe servidor de banco separado para subir, o arquivo vive dentro do próprio container.
No fim abre o aviso Teste Online publicado 🎉, com a URL e as credenciais do admin em claro. Feche (ou use Abrir aplicação) — não deixe esse aviso na tela se estiver gravando. O topo do painel passa a mostrar ONLINE, a URL ativa, o botão Abrir aplicação e a linha há Nm · PHP 8.4 · SQLite · 18.9s.
Clique em Popular dados de teste. Ele gera, no ambiente que está rodando, os registros configurados nos seeds de dev da aula 03. O resultado sai em uma linha, no formato cliente: 19 · especialidade: 10 · tecnico: 8 · ….
⚠️ Semeie primeiro, cadastre depois. O resultado do popular nem sempre convive com os registros que você cadastrou à mão: depois de um Testar agora a contagem já voltou ao número do seed e a massa cadastrada teve de ser refeita. Rode o popular no começo e só então cadastre os dados do curso.
Clique em Abrir aplicação. O sistema gerado abre numa aba nova.
Entre com o usuário admin e a senha que você definiu no passo 2 (campo Usuário, campo Senha, botão ENTRAR).
Confira no sistema:
a tira de ícones à esquerda, um ícone por módulo — clicar num deles abre a lista de telas daquele módulo. Relatórios ainda não aparece: módulo sem tela não é desenhado no menu;
em Cadastros: Clientes, Especialidades, Tecnicos, Status de Os, Equipamentos, Serviços e Pecas — os rótulos saem automaticamente do nome da tabela, sem acento (ajustáveis pela pílula Rótulos do gerador ou no menu);
em Operação: Ordem de Serviços — abra um registro e repare nas grades de serviços e peças que o gerador já montou dentro do formulário;
a listagem de clientes, com busca no cabeçalho, ordenação e filtro por coluna;
o formulário de cliente em duas colunas, com o toast ao salvar;
o combo de status com os seis valores cadastrados na aula 03.
Volte ao Studio e abra o bloco Sync estrutural no painel. Ele lista as mudanças de estrutura (tabelas, CRUDs, menu e permissões) aplicadas ao ambiente. O status saudável é Estrutura sincronizada; logo após um publish a linha traz baseline via deploy.
Conheça os três botões e a diferença entre eles:
Botão
O que faz
Quando usar
Testar agora
Publica o código e preserva o banco; migrations novas rodam sozinhas
Sempre que mexer em tela, menu ou permissão
Popular dados de teste
Gera as linhas dos seeds de dev no ambiente que está no ar
Quando o sistema estiver vazio demais para testar — antes de cadastrar à mão
Recriar ambiente (reseta o banco de dados)
Apaga o banco e semeia de novo
Só para zerar os dados ou trocar de engine/versão
Abra Histórico para ver as publicações anteriores, com data, modo e resultado.
Deixe o ambiente no ar. Da aula 07 em diante, o fim de cada aula é sempre o mesmo: Testar agora e conferir o resultado na aplicação.
Sobre os perfis da aula 05.Os grupos e perfis são semeados na publicação. Para ver o menu mudar por perfil, crie no próprio sistema gerado um usuário com o perfil Técnico e entre com ele: a barra lateral passa a mostrar só o que aquele perfil enxerga.
Erros comuns
"O ambiente subiu, mas o aplicativo respondeu HTTP 500 na primeira página"
algum arquivo gerado não compila, ou uma tabela esperada não existe → o painel mostra Causa provável e Detalhes técnicos; copie os detalhes, corrija e clique em Testar agora de novo.
"Ambiente publicado com falha de migration"
nem todas as tabelas foram criadas, em geral por causa de uma coluna obrigatória sem valor padrão ou de um índice único com duplicatas → leia Parou em, corrija a coluna no modelo de dados e publique de novo.
O login não funciona com senha nenhuma
o relatório de migration avisa "Nenhum usuário foi criado no ambiente" → a migration que cria os usuários falhou; corrija o modelo e publique de novo.
Cliquei em Popular dados de teste e não veio nada
nenhuma tabela tem seeds de dev configurados, ou as tabelas relacionadas ainda estão vazias → volte ao diagrama, aba Seeds de dev (Faker), configure e tente outra vez.
Popular disse que gerou as linhas, mas a listagem continua vazia
quase sempre é a própria tela cortando: uma Regra de carregamento ou um filtro ativo → limpe a busca e os filtros e recarregue. Se ainda assim não vier nada, clique em Testar agora (o ambiente pode ter sido encerrado entre o popular e a abertura) e confira de novo.
"O Teste Online não está no ar" ao popular
o ambiente foi encerrado por inatividade → clique em Testar agora e depois em Popular dados de teste.
Publiquei e o menu continua igual
o menu não foi salvo no Editor de menus, ou o módulo está com Visível no menu desligado, ou o módulo está vazio → salve o menu, confira o módulo e publique de novo.
Mudei a senha e o login antigo continua valendo
a senha nova só vale depois de aplicada; confira se a mensagem é Senha aplicada no ambiente ativo e não vale a partir do próximo publish.
Checklist de encerramento
O painel mostra ONLINE e uma URL ativa.
A senha do admin foi definida por mim e está salva.
O deploy terminou com o aviso Teste Online publicado e sem erro de migration.
Popular dados de teste rodou e listou as tabelas semeadas.
Consigo entrar na aplicação com admin e a minha senha.
A tira de módulos mostra Operação e Cadastros (e Relatórios quando tiver tela).
A listagem de clientes abre com a busca no cabeçalho e o botão Novo.
O formulário da ordem de serviço abre com as grades de serviços e peças.
O combo de status mostra os seis status da aula 03.
Sei a diferença entre Testar agora, Popular dados de teste e Recriar ambiente (reseta o banco de dados).
Transformar o ClienteForm gerado na aula 04 num cadastro que preenche o endereço sozinho pelo CEP, aceita CPF e CNPJ no mesmo campo, mascara o telefone e devolve a pessoa à listagem depois de salvar.
O que você terá no fim
ClienteForm com o campo cep do tipo CEP e mapeamento para logradouro, bairro, cidade e uf.
telefone com Máscara de digitação = Telefone (dinâmico).
cpf_cnpj com Máscara de digitação = CPF/CNPJ (dinâmico) — a carteira da Assistec tem empresa e pessoa física.
O campo CNPJ conhecido: o que ele traz da Receita e por que ele não serve quando a mesma tabela guarda os dois documentos.
Após salvar configurado para atualizar a linha em ClienteList e mostrar uma mensagem.
O cadastro funcionando no Teste Online com os dados fictícios do curso.
Studio › Início › ClienteForm › Visual
ClienteForm aberto no Studio: painel Componentes à esquerda, canvas no centro com a seção Cadastro de cliente, painel Propriedades à direita mostrando Selecione um componente
Studio › ClienteForm › Propriedades › CEP
Painel Propriedades do campo cep: combo de tipo em CEP (mad-cep-field), seções Identificação/Validação/Comportamento/Apresentação e o cartão MAPEAMENTO com Configurar mapeamento e Busca automática no blur
Studio › ClienteForm › CEP › Configurar mapeamento
Modal Mapeamento de auto-fill — CEP com as quatro linhas do Mapear automaticamente (logradouro, bairro, cidade, uf) e os botões Adicionar linha / Mapear automaticamente / Cancelar / Salvar
Studio › ClienteForm › Propriedades › Apresentação
Campo telefone selecionado com a seção Apresentação aberta e Máscara de digitação em Telefone (dinâmico)
Studio › ClienteForm › Propriedades › Apresentação
Campo cpf_cnpj de volta ao tipo Texto, com a seção Apresentação aberta e Máscara de digitação em CPF/CNPJ (dinâmico)
Studio › ClienteForm › Propriedades da página › Após salvar
Seção Após salvar das Propriedades da página: Ação em Atualizar a linha na listagem, Listagem em ClienteList ★ e Mensagem de sucesso preenchida
App › Cadastros › Clientes › Novo
App no Teste Online: cadastro de cliente preenchido, endereço completo vindo do CEP e telefone já mascarado
Passo a passo
Abra ClienteForm. Dois caminhos: na aba Início, digite ClienteForm em Buscar telas, códigos, controllers… e clique na linha ClienteForm.blade.php; ou, no rail, abra Arquivos e procure o arquivo na árvore. A página abre numa aba própria, na visão Visual — ao lado ficam PHP e Blade, que mostram o mesmo arquivo em código.
A tela do editor tem três colunas: Componentes (a paleta, com Estrutura do Formulário, Entrada de Dados, Seleção, Busca, Seleção (DB), Arquivos, Interface), o canvas no meio e Propriedades à direita. Enquanto nada está selecionado, o painel diz Selecione um componente.
Ao lado de Componentes existe a aba Árvore: a lista de tudo que a tela tem, na ordem, com o nome do campo entre parênteses (input-field (cep)). Clicar numa linha seleciona o componente — é o caminho mais rápido num formulário cheio.
Clique no campo cep (no canvas ou na Árvore). O painel mostra, de cima para baixo: o caminho (form › form-section › form-grid › input-field "cep"), o combo de tipo, e as abas EVENTOS e PROPRIEDADES. Dentro de PROPRIEDADES ficam as seções Identificação, Validação, Comportamento e Apresentação — o número ao lado do título é quantas propriedades daquela seção estão preenchidas.
Abra o combo de tipo (ele mostra Texto / mad-input-field). Ele tem um campo Buscar… e agrupa as opções como a paleta. Escolha CEP (grupo Entrada de Dados). Se a troca precisar descartar alguma propriedade, o editor pergunta antes em Trocar componente? — de Texto para CEP com só nome e rótulo preenchidos, não pergunta.
Com o cep selecionado, role o painel até o cartão MAPEAMENTO. Ele fica abaixo das seções, não dentro de Comportamento. Enquanto está vazio, mostra Nenhum mapeamento configurado. Clique em Configurar mapeamento.
No modal Mapeamento de auto-fill — CEP, clique em Mapear automaticamente. Ele cria estas quatro linhas:
Campo da API
Campo do formulário
logradouro (sem tipo)
logradouro — Logradouro
bairro
bairro — Bairro
cidade (texto)
cidade — Cidade
uf
uf — Uf
Use Adicionar linha para o que faltar e a lixeira para o que sobrar. Confirme em Salvar.
Olhe a lista de Campo da API: além de cep, bairro, uf e companhia, ela tem rua (tipo + logradouro), estado (nome completo), cidade_cod_ibge, estado_cod_ibge, cidade_id (resolvido) e estado_id (resolvido). Os dois últimos existem para quando cidade e uf são chaves estrangeiras para tabelas próprias — aí o auto-fill grava o id em vez do texto, e o modal ganha a seção Resolução no banco (cidade/estado). No nosso modelo as duas colunas são texto, então essa seção não aparece.
Ainda no cep: Busca automática no blur já vem ligada (é a chave embaixo do mapeamento). Em Comportamento existe Remover máscara ao salvar — deixe desligada: queremos 01001-000 gravado com o traço.
Selecione cpf_cnpj e troque o tipo para CNPJ, só para conhecer o campo. Ele traz dois cartões próprios:
TIPO DE CONSULTA, com Básico (Razão social, telefone, endereço. Resposta rápida.) e Completo (+ situação cadastral, CNAEs, QSA, capital social. Mais lento.);
Mapeamento de auto-fill, igual ao do CEP, com Configurar mapeamento e Busca automática no blur (aqui, 14 dígitos).
Abra Configurar mapeamento e clique em Mapear automaticamente. Ele acerta o endereço e o telefone (ddd_telefone_1 (principal) → telefone — Telefone), mas não cria a linha da razão social: a coluna da nossa tabela se chama nome_razao_social, e o automático só casa nomes iguais. Use Adicionar linha e monte razao_social → nome_razao_social — Nome Razao Social.
Agora a decisão de projeto: a Assistec atende quatro empresas e uma pessoa física (Marina Costa). O campo CNPJ valida 14 dígitos e não tem Máscara de digitação — ele é o caminho certo quando a carteira é só de pessoa jurídica. Como a nossa é mista, volte o tipo de cpf_cnpj para Texto e resolva pela máscara, no passo 14.
Selecione o campo telefone e abra Apresentação. Além de Largura, Ícone e Forçar digitação, existe Máscara de digitação: uma lista que começa em — Alias pronto — e traz CPF — 999.999.999-99, CNPJ — 99.999.999/9999-99, CPF/CNPJ (dinâmico), CEP — 99999-999, Telefone (dinâmico), Data — 99/99/9999, Hora — 99:99, Data + Hora — 99/99/9999 99:99, Placa — AAA-9999 e RG — 99.999.999-9. Escolha Telefone (dinâmico): o campo passa a formatar (11) 90000-0000 enquanto a pessoa digita, e aceita fixo de oito dígitos também.
Repita em cpf_cnpj com CPF/CNPJ (dinâmico). Essa máscara alterna o formato pelo tamanho do que foi digitado — é o que permite guardar CPF e CNPJ na mesma coluna. Embaixo da lista existe ou pattern custom, para máscara própria (9 = dígito, A = letra, * = alfanumérico). As duas são mutuamente exclusivas.
Ainda em cpf_cnpj, olhe as outras seções: Validação tem Obrigatório; Comportamento tem Placeholder, Valor padrão, Dica e Somente leitura. É tudo o que decide o estado de um campo pelo painel.
Na tira do editor, clique na engrenagem Propriedades da página. O painel da direita troca de conteúdo e mostra Título, Tipo de exibição, Tamanho, depois Após salvar e Menu e módulos.
Em Após salvar → Ação, escolha Atualizar a linha na listagem (as outras são Fechar e avisar, Ir para outra página e Continuar no formulário). Em Listagem, escolha ClienteList ★ — a estrela marca as listagens da mesma tabela.
Em Mensagem de sucesso, escreva Cliente salvo. Vazio significa salvar em silêncio. O campo só grava quando você sai dele (ou aperta Enter).
Clique em Salvar (Cmd+S). No rail, abra Teste online e clique em Testar agora.
No app, entre em Cadastros → Clientes → Novo e cadastre: Padaria Pão Dourado Exemplo, CEP 01001-000, número 100, telefone (11) 90000-0000, e-mail contato@paodourado.exemplo.com.br.
Confira: ao sair do campo de CEP, logradouro, bairro, cidade e uf se preenchem sozinhos; o telefone sai mascarado; ao salvar, a mensagem aparece e a linha entra na listagem. Cadastre do mesmo jeito os outros quatro clientes do curso — Clínica Vida Exemplo, Condomínio Jardim Exemplo, Mercado Bom Preço Exemplo e Marina Costa (pessoa física, CPF 000.000.000-00) —, variando só o telefone ((11) 91000-0000, (11) 93000-0000, (11) 94000-0000, (11) 92000-0000). São os cinco que as aulas seguintes usam.
Sobre o CNPJ da gravação.O CNPJ fictício do curso (00.000.000/0000-00) não existe na Receita: a busca responde que não encontrou — e vale mostrar isso, porque é o que a pessoa vai ver quando digitar errado. Para gravar a busca dando certo, use o CNPJ da sua própria empresa. Nunca use o CNPJ de um cliente real.
Sobre o CEP 01001-000.É o CEP público da Praça da Sé, em São Paulo. O endereço que volta é de logradouro público, não de nenhuma pessoa.
Erros comuns
Digito o CEP, saio do campo e nada acontece
ou Busca automática no blur está desligada (aí é preciso clicar na lupa), ou o CEP não tem oito dígitos → ligue a chave no cartão MAPEAMENTO e confira o número.
A busca do CEP responde, mas os campos continuam vazios
o Mapeamento (form → API) está vazio ou aponta para campos que não existem na tela → abra Configurar mapeamento e use Mapear automaticamente; linhas com (não encontrado) ao lado do nome precisam ser corrigidas.
Procurei o mapeamento dentro de Comportamento e não achei
ele não fica lá: o cartão MAPEAMENTO nasce com a troca de tipo e fica abaixo das seções, no fim do painel.
O CNPJ preenche o endereço mas apaga a razão social que eu tinha digitado
é o comportamento do auto-fill: cada linha do mapeamento sobrescreve o campo de destino → o mapeamento de CEP/CNPJ não tem opção de "só se estiver vazio"; se a razão social não pode ser sobrescrita, remova a linha de razao_social do mapeamento. (A opção Só preencher se o campo estiver vazio existe no Preenchimento de campos do DB Combo — é assunto da aula 10.)
Cadastrei uma pessoa física e o campo de documento não aceita o CPF
o campo está como CNPJ, que só valida 14 dígitos → volte o tipo para Texto e use a máscara CPF/CNPJ (dinâmico).
Quero o campo CNPJ com máscara dinâmica
não dá: Máscara de digitação só existe no campo de Texto. CEP e CNPJ já trazem a máscara deles embutida. Ou você usa o campo especializado (com busca automática), ou usa Texto com máscara — não os dois.
Salvei a tela e o app continua igual
o Teste Online serve a última publicação → Rail → Teste online → Testar agora.
Quero esconder o campo de CNPJ quando o cliente é pessoa física
o painel do formulário não tem regra de "mostrar este campo quando aquele valor for X". Isso existe em coluna de listagem (Condição de exibição (PHP)) e em coluna de linhas filhas (Visível quando), não em campo de formulário → use um único campo cpf_cnpj com a máscara dinâmica, que é o caminho do curso.
Checklist de encerramento
O campo cep aparece no painel como CEP, com lupa ao lado no canvas.
Configurar mapeamento do CEP tem as quatro linhas (logradouro, bairro, cidade, uf).
telefone tem Máscara de digitação = Telefone (dinâmico).
cpf_cnpj está como Texto com CPF/CNPJ (dinâmico).
Após salvar está em Atualizar a linha na listagem, apontando ClienteList ★.
Mensagem de sucesso preenchida.
Página salva (Salvar (Cmd+S)) e Teste Online republicado (Testar agora).
No app, o CEP 01001-000 preenche o endereço sozinho e o telefone sai mascarado.
Os cinco clientes do curso (Padaria Pão Dourado Exemplo, Clínica Vida Exemplo, Condomínio Jardim Exemplo, Mercado Bom Preço Exemplo e Marina Costa) estão cadastrados.
Deixar o ClienteList gerado na aula 04 pronto para o dia a dia: as colunas certas, filtro no cabeçalho, barra de filtros, botões em cada linha e exportação em PDF com cabeçalho da empresa.
O que você terá no fim
ClienteList com seis colunas úteis, rótulos legíveis e ordenação padrão.
Filtro no cabeçalho da coluna uf, no tipo Opções fixas.
A barra Filtros da Listagem conhecida (estilos Toolbar, Chips, Drawer…).
Os dois botões de linha entendidos: um que navega e um que executa um método.
Exportação em PDF com cabeçalho e rodapé próprios, a partir de um modelo pronto.
Studio › ClienteList › Propriedades › Colunas
Painel da grade com a seção COLUNAS (6) aberta: as seis colunas com rótulo e meta (campo · sort · filter) e o botão + Add
Studio › ClienteList › Coluna uf › Filtro da coluna
Drill-in Filtro da coluna (UF): Filtrar por esta coluna marcado, Tipo de filtro em Opções fixas, Comparação em Igual a (padrão) e a Prévia embaixo
Studio › ClienteList › Ações › Editar › Navegação
Painel da ação Editar com a seção Navegação aberta: modo Escolher página, Página em ClienteForm e o método da linha
Studio › ClienteList › Propriedades da página › Exportação PDF
Modal Cabeçalho e rodapé do PDF com o modelo Corporativo clássico aplicado: Cabeçalho do PDF e Rodapé do PDF em Personalizado, e a folha com o logo, o título Clientes e Página 1 de 12
App › Cadastros › Clientes
App no Teste Online: listagem de clientes com as seis colunas e os botões de ação na linha, com a busca do topo preenchida com Exemplo e o rodapé em 1–5 de 5 (os cinco clientes do curso)
Passo a passo
Abra ClienteList. No painel do meio, use a aba Árvore e clique em grid — é o jeito mais direto de selecionar a grade inteira.
O painel da direita mostra, nesta ordem: Regras de carregamento, Filtros, Propriedades (com Agrupamento, Ordenação, Paginação, Carregamento inicial e Layout), COLUNAS (N) e AÇÕES (N). No topo há ainda um atalho Configurar relatório, que reúne agrupamento e totais — assunto da aula 16.
Abra Layout e ligue Buscável, Exportável e Header Fixo. Ali também ficam Lado das ações, Config inline e Atualizável (que vale em telas que ficam abertas o dia inteiro).
⚠️ Clique na chavinha, não na linha. Nessas chaves o clique só conta em cima do interruptor; clicar no meio do rótulo não alterna nada e não avisa. O contador ao lado do nome da seção (Layout 4) é a conferência rápida: ele conta as propriedades ligadas.
Em Paginação, Itens por página vem em 15. Acima de 50 a tela começa a pesar.
Em Ordenação → Ordenação padrão, escolha Coluna = nome_razao_social e Direção = Crescente (A→Z). Coluna é um campo de busca (começa em *— escolha a coluna —*) e Direção é uma lista fechada que só destrava depois de a coluna ser escolhida — se a direção estiver cinza, é porque falta a coluna.
Abra COLUNAS. Cada linha mostra o rótulo em cima e, embaixo, o meta da coluna (nome_razao_social · sort · filter:text). As setas reordenam, o + Add cria e a lixeira (aparece ao passar o mouse) remove — com um Remover? / Sim antes. Remova id, tipo_pessoa, email, cep, logradouro, numero, complemento, bairro e observacoes. Devem sobrar seis:
Campo
Label
Alinhamento
nome_razao_social
Cliente
esquerda
cpf_cnpj
CPF / CNPJ
esquerda
telefone
Telefone
esquerda
cidade
Cidade
esquerda
uf
UF
centro
ativo
Situação
centro
Clique numa coluna para abrir o painel dela: Identificação (Campo, Label, Largura, Alinhamento), Ordenação (Ordenável), Formato, Cálculo, Visibilidade, e os cartões FILTRO DA COLUNA e EDIÇÃO INLINE.
Label não é uma caixa de texto comum: é um botão que mostra o texto atual e o selo text. Clicando nele abre a janela Literal / i18n key — digite o novo rótulo e clique em Salvar texto. (Salvar e traduzir cria uma chave de tradução; é assunto da aula 24.)
Selecione a coluna uf e, no cartão FILTRO DA COLUNA, clique em Configurar filtro. O painel inteiro troca pelo filtro — repare no caminho no topo: … › col › Filtro. O botão ← volta.
Marque Filtrar por esta coluna e escolha o Tipo de filtro. São dez: Texto, Opções fixas, Combo de tabela, Busca no banco, Múltipla escolha, Data, Período, Número, Faixa numérica e Sim/Não. Para uf, Opções fixas. Embaixo ainda há Coluna filtrada (deixe vazio para filtrar pela própria coluna), Comparação (Igual a (padrão) aqui), Texto de ajuda no campo, a Prévia e o Blade gerado.
⚠️ Hoje o botão Adicionar opção não cria a linha. A opção nasce vazia e é descartada na hora de gravar, então a lista volta vazia. Enquanto isso não muda, a lista de opções precisa ser escrita na aba Blade da página (:filter-opts="['SP' => 'SP', 'RJ' => 'RJ', 'MG' => 'MG']" na <mad-col> do uf) — ou use Combo de tabela, quando a coluna aponta uma tabela.
Volte com o ← e confira cidade: ela já veio da geração em lote com Texto (busca por trecho), que é o que queremos. As seis colunas ficam então com o meta nome_razao_social · sort · filter:text, cpf_cnpj · sort · filter:text, telefone · sort · filter:text, cidade · sort · filter:text, uf · sort · filter:select e ativo · sort.
Dica (opcional, fora do que a aula grava).ativo também aceita filtro, no tipo Sim/Não — ele pede Valor de "Sim" no banco e Valor de "Não" no banco. Deixamos a coluna sem filtro para manter a tela enxuta.
Na tira do editor, clique em Configurar Filtros da Listagem (o ícone azul de controles, que só aparece em listagem e relatório). No modal Filtros da Listagem:
ESTILO tem sete opções — Toolbar (Filtros inline + popovers), Chips, Formulário, Drawer, Modal, Sidebar ← e Sidebar →. Escolha Toolbar;
Título (opcional) aceita Filtros; Nome (ID, opcional) é o id do componente;
em Colunas da tabela, marque cidade e ativo (há atalhos Todas s/ PK e Nenhuma). Cada coluna marcada cria um filtro em Filtros declarados;
REGRAS DE FILTRO é onde se diz qual coluna cada filtro corta; enquanto estiver Nenhuma regra declarada., use + Adicionar Regra de Filtro.
Feche em Fechar.
Abra AÇÕES (2). Os dois botões já existem — a geração em lote da aula 04 criou Editar e Excluir. Clique em Editar: em Navegação, o modo Escolher página tem Página = ClienteForm e Método, e Argumentos manda o id da linha. O modo Manual aceita ClienteForm::onEdit({id}) digitado à mão.
Clique em Excluir. A diferença está em qual grupo é preenchido:
Navegação → Navigate abre outra tela (é o botão de abrir);
Eventos → On Click chama um método PHP do controller da própria listagem.
Ele vem com On Click = onDelete, confirmação Tem certeza que quer remover esse registro? e Variante vermelha.
Em Visibilidade, Exibir quando… esconde o botão nas linhas em que ele não faz sentido. Deixe para depois — o botão de excluir vale em toda linha.
Na tira do editor, clique na engrenagem Propriedades da página. A seção Exportação PDF só aparece em página de listagem ou de relatório. Ela mostra o selo Padrão do projeto enquanto nada foi personalizado. Clique em Configurar cabeçalho e rodapé….
O modal Cabeçalho e rodapé do PDF é um editor de arrastar e soltar, não um formulário:
à esquerda, o card Modelos prontos, a caixa ELEMENTOS (Texto, Logo / imagem, Nº de página, Data, Campo dinâmico), os chips de CAMPOS DISPONÍVEIS ({TITLE}, {SUBTITLE}, {DATE}, {PERIOD}, {FILTERS}, {TOTAL_REGISTER}, {APP_NAME}, {TENANT_NAME}, {UNIT_NAME}, {USER_NAME}), CAMPOS DO PROJETO e os ATALHOS do teclado;
no meio, Cabeçalho do PDF e Rodapé do PDF, cada um com Padrão do projeto / Personalizado / Sem cabeçalho, e a folha A4 com as faixas CABEÇALHO e RODAPÉ (cada uma com altura e respiro em mm);
à direita, as propriedades do elemento selecionado.
Clique no cartão Modelos prontos: abre uma segunda janela com os modelos, cada um com um botão Usar este modelo. Escolha Corporativo clássico (Logo, título centralizado e paginação.). Os dois seletores viram Personalizado e a folha passa a mostrar o logo, o título e Página 1 de 12. Feche em Fechar.
De volta ao painel da grade, em Agrupamento, Título da exportação alimenta o {TITLE} das bandas do PDF e Subtítulo da exportação o {SUBTITLE}. Preencha o título com Clientes.
Clique em Salvar (Cmd+S). No rail, abra Teste online e clique em Testar agora.
No app, entre em Cadastros → Clientes e confira: as seis colunas, o filtro no cabeçalho de UF e os botões da linha. Digite Exemplo na busca do topo — a lista fecha nos cinco clientes do curso (1–5 de 5), separando-os das linhas do Faker.
Regra de carregamento não é filtro.Regras de carregamento é o corte que o sistema aplica sempre e que a pessoa não vê nem desliga; Filtros é a barra que a pessoa preenche e limpa. Confundir os dois é o erro mais comum desta tela — e o motivo mais frequente de "sumiram registros".
Erros comuns
A lista abre em ordem diferente a cada vez
falta Ordenação padrão no painel da grade → escolha nome_razao_social e a direção.
Cliquei em Adicionar opção no filtro e não apareceu linha nenhuma
é uma limitação conhecida da versão atual → escreva a lista na aba Blade (:filter-opts="['SP' => 'SP', …]") ou use outro Tipo de filtro.
Procurei o filtro da coluna e só achei um botão
é isso mesmo: Configurar filtro troca o painel inteiro pelo filtro (caminho … › col › Filtro); o ← volta.
Cliquei no botão da linha e nada aconteceu
a ação está sem destino: Navegação vazia e Eventos → On Click vazio → preencha um dos dois (eles são mutuamente exclusivos).
O botão abre a tela certa, mas ela vem em branco
faltam os Argumentos: a tela de destino não recebeu o id da linha e abriu como cadastro novo.
O filtro do cabeçalho busca por trecho quando eu queria lista
Tipo de filtro está em Texto → troque para Opções fixas ou Combo de tabela; o painel avisa quando o filtro está no (formato antigo) — foi assim que a geração em lote deixou as colunas.
A listagem abriu vazia depois que mexi nos filtros
uma Regra de carregamento está cortando tudo → abra a janela de regras e leia o Preview do PHP gerado.
O PDF sai sem o logo
o projeto ainda não tem logo em Propriedades do projeto, ou o elemento de logo foi removido da faixa.
Checklist de encerramento
Em Layout, Buscável, Exportável e Header Fixo estão ligados.
Ordenação padrão aponta nome_razao_social em ordem crescente.
A listagem mostra seis colunas, com UF centralizada.
Os rótulos estão legíveis (Cliente, CPF / CNPJ, Situação).
uf está com Filtrar por esta coluna e Opções fixas.
Os dois botões de linha existem e o de excluir pede confirmação.
O cabeçalho do PDF está Personalizado, com um modelo aplicado.
Página salva (Salvar (Cmd+S)) e Teste Online republicado (Testar agora).
Deixar a ordem de serviço com tudo que ela precisa numa tela só: os serviços executados, as peças aplicadas e os anexos — cada linha filha calculando o seu subtotal e somando no rodapé.
O que você terá no fim
OrdemServicoForm com dois Field List (os_servico_item, os_peca_item) e um Detail Form (os_anexo), com rótulos legíveis.
Coluna subtotal calculada por fórmula, só leitura e somando no rodapé, nas duas listas.
Anexos em gaveta, com o campo de arquivo gravando o caminho numa pasta do projeto.
Após salvar em Continuar no formulário, porque quem abre uma OS volta a ela.
No app: uma OS de exemplo fechando 600,00 de serviços e 240,00 de peças.
Studio › + Criar novo › Nova página › Formulário › Cadastro com Detalhe
Assistente Criar nova página Mad (tela cheia) no Passo 3 de 3 · Configuração, variação Cadastro com Detalhe e tabela ordem_servico, com a lista Tabelas detalhe: os_servico_item, os_peca_item, os_anexo, os_historico e os_apontamento, cada uma com o seletor field-list / detail-form
Studio › OrdemServicoForm › Field List flOsServicoItem
Field List de serviços selecionado: Identificação com Nome flOsServicoItem e Label Botão Adicionar serviço, Fonte de dados com a Tabela detalhe os_servico_item bloqueada, Editar 7 colunas, Coluna FK e Model, e a seção COLUNAS (7) começando
Studio › OrdemServicoForm › Coluna Subtotal › Cálculo
Modal Editar fórmula de cálculo: CAMPOS DISPONÍVEIS com os rótulos das colunas irmãs, os chips de FUNÇÕES (round, max, min, abs, floor, ceil, if, discount) e a FÓRMULA do subtotal escrita
Studio › OrdemServicoForm › Coluna Subtotal › Totalizador
Painel da coluna Subtotal com Cálculo aberto mostrando a fórmula e, embaixo, o cartão TOTALIZADOR com Somar valores ligado e Contar linhas bloqueado
Studio › OrdemServicoForm › dfOsAnexo › campo arquivo
Campo arquivo de dentro do Detail Form de anexos, do tipo Upload (mad-file-field), com Armazenamento de dados aberto: Tipos aceitos, Tamanho máximo, Tipo de gravação em Armazenar caminho do arquivo na coluna, Pasta de upload os_anexos e Modo de renomeação
App › Operação › Ordem de Serviços › Editar
App no Teste Online: a OS OS-2026-001 aberta e rolada até as listas filhas — dois serviços somando 600,00 no rodapé, uma peça somando 240,00 e o botão Adicionar anexo
Passo a passo
Só para mostrar o caminho: na tira do Studio, clique no + (Criar novo) e escolha Nova página. O assistente ocupa a tela inteira (não é uma janelinha) e tem três passos: Tipo, Variação e Configuração.
Passo 1: Formulário (Cadastros e edições — CRUD completo).
Passo 2: Cadastro com Detalhe (Master-detail com tabelas filhas).
Passo 3: Base de dadosassistec e Tabelaordem_servico. Assim que a tabela é escolhida nasce a lista Tabelas detalhe:, com todas as tabelas que apontam para ordem_servico — os_servico_item, os_peca_item, os_anexo, os_historico e os_apontamento —, cada uma com o (via ordem_servico_id) e um seletor field-list / detail-form. Marcar a caixa inclui aquela tabela na tela.
Saia em Cancelar (canto superior direito). Não crie a página: ela já existe desde a aula 04, e criar de novo deixaria duas telas para a mesma tabela.
Abra OrdemServicoForm. No painel do meio, use a aba Árvore: a tela já tem field-list (flOsServicoItem), field-list (flOsPecaItem) e detail-form (dfOsAnexo) — a geração em lote da aula 04 montou os três, cada um numa seção própria.
Selecione flOsServicoItem. Em Identificação:
Nome (flOsServicoItem) é o identificador usado nos hooks do formulário (onSaveDetail/onLoadDetail);
Label Botão: troque Adicionar item por Adicionar serviço.
Em Comportamento, Dica aceita um texto de apoio (Um serviço por linha) que sai abaixo da lista, e ali também ficam Addable, Removable e Sortable.
Em Fonte de dados, confira Tabela detalhe = os_servico_item com FK: ordem_servico_id. Os dois aparecem bloqueados — o aviso diz: *"Tabela detalhe bloqueada. Para trocar, remova este Field List e adicione um novo."* Embaixo estão Editar N colunas, Coluna FK e Model.
Abra COLUNAS. Clique em cada coluna e ajuste o Label em Identificação:
Campo
Label
servico_id
Serviço
quantidade
Qtd
preco_unitario
Preço
desconto_pct
Desc. %
subtotal
Subtotal
observacao
Observação
A coluna id fica como está: ela é hidden e não aparece para a pessoa.
O painel de uma coluna tem dois blocos: o cartão COLUNA (Identificação com Campo, Label e Largura, mais Outras) e o cartão CAMPO, cujo topo é um seletor de tipo (text, number, numeric, money, date, discount, combo_input, select, dbcombo, hidden, email, tel, checkbox, radio, textarea, file). Dentro dele: Validação, Comportamento, Formato e Cálculo.
Selecione a coluna Serviço. Ela já nasce dbcombo e com a Fonte de dados preenchida: Databaseassistec, Tabela (Model)servico, Chaveid, Display{nome} e Ordenar pornomeASC. É isso que faz o combo mostrar o nome do serviço em vez do código. Ali embaixo ainda há Combo dependente e Regras de carregamento — assunto da aula 10.
Selecione a coluna Subtotal, abra Cálculo e clique em Adicionar fórmula. No modal Editar fórmula de cálculo, escreva:
CAMPOS DISPONÍVEIS insere a coluna já entre chaves (a lista mostra o rótulo, mas o que entra na fórmula é o nome da coluna); FUNÇÕES traz round, max, min, abs, floor, ceil, if e discount. Se você citar uma coluna que não existe, aparece o aviso ⚠ Validação embaixo da caixa. Clique em Salvar.
Ao gravar a primeira fórmula, a plataforma liga o Read-only da coluna sozinha — é o comportamento certo: campo calculado que aceita digitação é campo que vai ser sobrescrito na frente do usuário. Confira em Comportamento.
Ainda em Subtotal, vá até o fim do painel: o cartão TOTALIZADOR explica que a linha de totais mostra *soma ou contagem — nunca os dois juntos*. Ligue Somar valores; Contar linhas fica bloqueado na mesma hora.
Repita em flOsPecaItem: Label BotãoAdicionar peça, rótulos Peça, Qtd, Preço, Subtotal e Nº de série, e a fórmula mais simples (não há desconto aqui):
round({quantidade} * {preco_unitario}, 2)
Ligue Somar valores no Subtotal dela também.
Selecione dfOsAnexo. Em Identificação: NomedfOsAnexo, Modo com as três opções inline / modal / drawer (deixe drawer), Label BotãoAdicionar anexo e Título do FormAnexo da OS. Em Fonte de dados, a Tabela detalheos_anexo também vem travada, com o botão Escolher colunas e campos.
Escolher colunas e campos abre um assistente de três passos — Escolher tabela detalhe, Selecionar colunas de os_anexo e Dispor campos no formulário (com COLUNAS DO FORMULÁRIO em 1, 2, 3 ou 4 e os cards arrastáveis). Ele já vem resolvido: oito campos, duas colunas. Feche em Esc se for só olhar.
Abra o detail-form na Árvore até chegar nos campos. Repare no que o gerador escolheu sozinho, pelo tipo e pelo nome da coluna:
arquivo e nome_arquivo → Upload (mad-file-field);
imagem → Imagem (mad-image-field);
assinatura_base64 → Assinatura (mad-signature-field) — assunto da aula 12.
Selecione o campo arquivo e abra Armazenamento de dados:
Tipos aceitos é uma fileira de chips (Imagens (todas), PNG, JPEG, WebP, GIF, SVG, PDF, Word, Excel, CSV, ZIP/RAR, Áudio, Vídeo) mais Outros (CSV de MIME/extensão). Sem nenhum marcado, o campo aceita qualquer arquivo;
Tamanho máximo aceita 10MB;
Tipo de gravação já vem em Armazenar caminho do arquivo na coluna — é o que faz o arquivo ser guardado de verdade;
Pasta de upload vem preenchida com os_anexos;
Modo de renomeação evita que dois arquivos com o mesmo nome se atropelem.
Na tira do editor, abra a engrenagem Propriedades da página → Após salvar. A Ação tem quatro opções: Atualizar a linha na listagem, Fechar e avisar, Ir para outra página e Continuar no formulário. Escolha Continuar no formulário e preencha Mensagem de sucesso com OS salva.
Clique em Salvar (Cmd+S). No rail, abra Teste online e clique em Testar agora.
No app, em Operação → Ordem de Serviços, abra (ou crie) a OS OS-2026-001 da Padaria Pão Dourado Exemplo e monte: Visita técnica diagnóstica (1 × 150,00) e Limpeza de câmara fria (1 × 450,00); em peças, Gás R-410A kg (2 × 120,00). O rodapé dos serviços fecha em 600,00 e o das peças em 240,00.
A soma do rodapé não grava nada.Ela é um cálculo de tela. Os campos valor_servicos, valor_pecas e valor_total da OS são gravados por código, no evento da lista. Se o rodapé mostra a soma certa e a OS grava zero, é esse evento que está vazio — e não a fórmula.
Erros comuns
Adiciono as linhas, salvo, e nada é gravado
a lista está ligada pela metade: falta Tabela detalhe ou Coluna FK → confira as duas em Fonte de dados; sem uma delas, o salvamento das linhas é pulado sem aviso.
Quero trocar a tabela da lista e o campo está travado
é assim mesmo: o aviso diz *"Tabela detalhe bloqueada. Para trocar, remova este Field List e adicione um novo."*
O subtotal fica sempre zero
a fórmula cita uma coluna que não está na lista, ou o nome digitado é o rótulo e não o campo ({Qtd} não existe; {quantidade} sim) → leia o aviso ⚠ Validação dentro do modal. Célula vazia conta como zero.
Dá para digitar por cima do subtotal
o Read-only foi desligado depois → ligue de novo em Comportamento; toda coluna com fórmula deve ficar assim.
O rodapé não mostra soma nenhuma
Somar valores não está ligado, ou a coluna é de texto → só coluna de número ou dinheiro soma.
Liguei Somar valores e o Contar linhas ficou cinza
é a regra do cartão: cada célula mostra soma ou contagem, nunca as duas.
O anexo entra na gaveta, salvo, e a coluna do arquivo fica vazia
o campo está com Tipo de gravação em Manual (sem persistência automática) → troque para Armazenar caminho do arquivo na coluna e confira a Pasta de upload.
Salvei a OS e a tela voltou para a listagem
Após salvar está em Atualizar a linha na listagem → troque para Continuar no formulário.
O formulário não salva e só diz "Corrija os erros antes de continuar"
uma coluna obrigatória da tabela filha ficou de fora da lista → adicione a coluna, ou tire a obrigatoriedade no modelo de dados, e republique.
Checklist de encerramento
OrdemServicoForm tem dois Field List e um Detail Form.
Os dois Field List têm Tabela detalhe e Coluna FK preenchidos.
Os botões dizem Adicionar serviço e Adicionar peça.
Serviço e Peça são combos de banco, mostrando o nome e não o código.
Subtotal tem fórmula, está Read-only e soma no rodapé — nas duas listas.
O Detail Form de anexos abre em gaveta com o título Anexo da OS.
O campo arquivo está em Armazenar caminho do arquivo na coluna, com Pasta de upload preenchida.
Após salvar está em Continuar no formulário.
No app, a OS de exemplo fecha em 600,00 de serviços e 240,00 de peças.
Página salva (Salvar (Cmd+S)) e Teste Online republicado (Testar agora).
Fazer os combos do cabeçalho da OS trabalharem juntos: o equipamento só mostra o que é do cliente escolhido, preenche o título da OS sozinho e deixa cadastrar um equipamento novo sem sair da tela.
O que você terá no fim
cliente_id, equipamento_id e tecnico_id conferidos como DB Combo com Display legível.
equipamento_id em cascata: só os equipamentos do cliente escolhido.
Regras de carregamento no mesmo equipamento_id, cortando equipamento inativo sem a pessoa ver: o combo mostra só o que é do cliente *e* está ativo.
Preenchimento de campos: escolher o equipamento preenche o titulo da OS.
Sem resultados com os dois modos ativos: abrir o cadastro completo e cadastrar na hora.
Studio › OrdemServicoForm › cliente_id › Fonte de dados
Campo cliente_id (DB Combo) do OrdemServicoForm com a seção Fonte de dados aberta: Database assistec, Tabela (Model) cliente, Chave id, Display {nome_razao_social} e Ordenar por nome_razao_social ASC
Studio › OrdemServicoForm › equipamento_id › Combo dependente
Campo equipamento_id com a seção Combo dependente aberta, Depende de em cliente_id e Coluna FK em cliente_id; no alto do painel, REGRAS DE CARREGAMENTO com o selo 1 (a regra ativo = true mora no mesmo campo)
Studio › OrdemServicoForm › equipamento_id › Preenchimento de campos
Modal Preenchimento de campos com uma linha preenchida: Campo de destino titulo — Titulo, Origem do valor nome, Transformação vazia e Só vazio marcado
Studio › OrdemServicoForm › Sem resultados › Cadastro rápido inline
Janela do Cadastro rápido inline com Formulário de cadastro EquipamentoForm, Método (gerado) onQuickSaveEquipamento e a lista CAMPOS com #1 nome (Texto) e #2 cliente_id (DBCombo (FK)) selecionado
Studio › OrdemServicoForm › equipamento_id › Sem resultados
Seção Sem resultados do equipamento_id com a Mensagem (cabeçalho) preenchida e os dois cards ATIVO: Abrir formulário completo (EquipamentoForm::show) e Cadastro rápido inline (2 CAMPOS)
App › Operação › Ordem de Serviços › Novo
App no Teste Online: OS nova com Cliente Padaria Pão Dourado Exemplo, Equipamento Câmara fria 01 (único do cliente) e o Titulo já preenchido sozinho com Câmara fria 01
Passo a passo
Abra OrdemServicoForm e selecione cliente_id na Árvore. Ele já é um DB Combo (mad-dbcombo-field) — a geração em lote da aula 04 cria assim toda coluna que é chave estrangeira.
Abra Fonte de dados e confira o que veio pronto:
Database: assistec (o banco do projeto — não mexa);
Tabela (Model): cliente;
Chave: id — o valor que vai ser gravado;
Display: {nome_razao_social} — o texto que a pessoa lê;
Ordenar por: nome_razao_social, ASC.
O Display aceita juntar colunas: {nome_razao_social} - {cidade} ajuda quando há nomes parecidos. O mesmo vale para tecnico_id (tecnico, {nome}) e equipamento_id (equipamento, {nome}).
Selecione equipamento_id e abra Combo dependente:
Depende de = cliente_id — o campo pai, que a pessoa preenche antes;
Coluna FK = cliente_id — a coluna da tabela equipamento que aponta para o pai.
Os dois são obrigatórios. Preencher só o Depende de faz o sistema filtrar por uma coluna que não existe, e a lista volta sempre vazia.
Os dois controles são diferentes de propósito: Depende de é um campo de busca (ele lista os outros combos do formulário) e Coluna FK é uma lista fechada, com as chaves estrangeiras da tabela do combo. Num combo cuja tabela não tem FK nenhuma, ele aparece com o aviso Tabela sem FKs.
Continue no equipamento_id e abra Regras de carregamento → Configurar filtros. O construtor abre com o Grupo raiz vazio (AND / OR):
clique em Regra;
Coluna = ativo (boolean), Operador = =, Valor = Valor literaltrue;
o Preview do PHP gerado mostra, na hora, :filters="[['ativo', '=', true]]";
clique em Aplicar.
Cascata e regra somam: o combo passa a listar o que é do cliente escolhido *e* está ativo. No painel, REGRAS DE CARREGAMENTO ganha o selo 1.
Por que o preview muda de forma.Num combo sem cascata a regra vira uma consulta pronta (:query="$filter_<campo>", com a declaração acima da tag). Num combo com cascata ela vira a lista de condições :filters — é a forma que o framework aceita ao lado do depends-on. Você não escolhe: a plataforma decide pela presença do Depende de. Confira pela aba Blade. Se a sua versão ainda gravar :query num campo com Depende de, a tela inteira deixa de abrir com a prop :query não suporta depends-on (cascata). Nesse caso mova a regra para o combo pai (cliente_id, sem cascata) até atualizar a plataforma. O botão Grupo permite aninhar condições (um OR dentro do AND, por exemplo).
Abra a seção Preenchimento de campos e clique em Configurar. O modal Preenchimento de campos já abre com uma linha em branco — não clique em Adicionar campo antes de preencher essa, ou você fica com uma linha vazia sobrando. Na linha:
CAMPO DE DESTINO = titulo — Titulo (uma lista com os campos do formulário);
ORIGEM DO VALOR = nome (as colunas da tabela do combo, com busca);
TRANSFORMAÇÃO (OPCIONAL) — deixe vazia (há MAIÚSCULAS, minúsculas, Título, Aparar, Moeda, Data (formato), Número (casas), Máscara e método PHP próprio);
marque SÓ VAZIO.
Clique em Salvar.
Preenchimento de camposexiste no DB Combo, no DB Select e no Unique Search — sempre dentro de um <mad-form>. Nas colunas de um Field List o equivalente é o evento On Change da coluna, que chama um método PHP.
Abra a seção Sem resultados. Em MENSAGEM (CABEÇALHO), escreva Não encontrado. Cadastre agora:. Os dois modos começam INATIVO; clicar no card abre a configuração dele.
Modo 1 — card Abrir formulário completo:
Formulário = EquipamentoForm;
Método (abre o form) fica por conta da plataforma (o resumo mostra EquipamentoForm::show);
em APARÊNCIA DO BOTÃO dá para trocar Label, Ícone e as classes CSS.
Clique em Salvar. O card passa a mostrar ATIVO.
Modo 2 — card Cadastro rápido inline:
Formulário de cadastro = EquipamentoForm. O Método (gerado) aparece ao lado, em cinza: onQuickSaveEquipamento. Ele é gerado no formulário escolhido — deixe o campo vazio; só preencha se quiser um método próprio (Avançado (Classe::método));
em CAMPOS, clique em Adicionar primeiro campo. O campo nasce como campo_1; o Nome é escolhido no painel da direita, numa lista com as colunas da tabela (o atalho Digitar o nome manualmente libera digitar). Crie nome, tipo Texto;
clique em adicionar e crie o segundo campo, cliente_id, tipo DBCombo (FK).
Clique em Salvar.
A tabela equipamento tem cliente_id obrigatório. Sem esse campo no mini-formulário, o cadastro rápido falha na hora de salvar — é a pegadinha mais comum do recurso. Quando a tabela exige muita coisa, use só o modo 1.
De volta ao painel, a seção Sem resultados mostra os dois cards em ATIVO, com o método de cada um (EquipamentoForm::show e EquipamentoForm::onQuickSaveEquipamento) e a contagem 2 CAMPOS.
Clique em Salvar (Cmd+S). No rail, abra Teste online e clique em Testar agora.
No app, abra Operação → Ordem de Serviços → Novo e confira, nesta ordem:
o combo Cliente abre com uma caixa de busca — é um combo próprio do sistema gerado, não o <select> cinza do navegador;
Equipamento começa vazio e só se enche depois do cliente;
escolhendo Padaria Pão Dourado Exemplo, o equipamento mostra só Câmara fria 01;
escolhendo a câmara fria, o Titulo da OS se preenche sozinho;
trocando o cliente, o equipamento limpa e recarrega;
digitando um equipamento que não existe, aparecem a mensagem e os dois botões.
Erros comuns
O combo abre vazio
confira nesta ordem: a tabela tem registros no ambiente que você está testando (o Teste Online tem banco próprio); uma regra em Regras de carregamento está cortando tudo (leia o Preview do PHP gerado); ou o campo tem Depende de preenchido — aí ele fica vazio de propósito até o pai ser escolhido.
Escolho o cliente e o equipamento continua vazio
quase sempre é a Coluna FK em branco na seção Combo dependente → preencha com cliente_id.
Liguei a regra ativo = true e sumiu tudo
os registros foram cadastrados com o campo Ativo desmarcado → marque nos registros, ou tire a regra. (No Teste Online, os registros criados pelo Popular dados de teste nem sempre nascem ativos.)
A tela inteira parou de abrir depois que criei a regra
plataforma desatualizada: ela gravou :query num combo que tem Depende de, e o app mostra a prop :query não suporta depends-on (cascata) sem nenhum campo → atualize, ou mova a regra para o combo pai. Na versão atual o combo com cascata grava :filters e os dois convivem.
Tirei a regra e a tela continua quebrada
em versões anteriores, apagar a última regra apagava o bloco de PHP mas deixava a citação :query="$filter_…" na tag, apontando para o que não existe mais → abra a aba Blade e remova a linha :query="$filter_<campo>" à mão. Hoje a limpeza leva as duas coisas juntas.
O combo mostra o código em vez do nome
o Display está apontando para a coluna errada, provavelmente o id → aponte {nome}.
Salvei o preenchimento e não aconteceu nada
a linha ficou pela metade: sem CAMPO DE DESTINO ou sem ORIGEM DO VALOR ela é descartada ao gravar.
O preenchimento apaga o título que eu tinha digitado
falta marcar SÓ VAZIO na linha.
O cadastro rápido dá erro ao salvar
a tabela pede uma coluna que o mini-formulário não tem → adicione o campo em CAMPOS ou use Abrir formulário completo.
O campo do cadastro rápido ficou "sem nome — edite à direita"
o Nome é escolhido no painel da direita, não na lista da esquerda.
O botão de cadastrar abre a tela e não devolve o registro selecionado
o formulário escolhido está configurado para abrir como página inteira, e aí ele substitui a tela da combo → em Propriedades da página do EquipamentoForm, use gaveta ou modal.
A lista demora a abrir
passou de algumas centenas de registros → troque o campo por Unique Search, que busca conforme a pessoa digita.
Checklist de encerramento
cliente_id, equipamento_id e tecnico_id são DB Combo com Display legível.
equipamento_id tem Depende de e Coluna FK preenchidos.
equipamento_id tem uma regra em Regras de carregamento cortando ativo = true, e a aba Blade mostra :filters="[['ativo', '=', true]]" na tag — sem nenhum :query.
O Preenchimento de campos tem a linha titulo ← nome, com SÓ VAZIO marcado.
Sem resultados tem a mensagem e os dois cards em ATIVO.
O Cadastro rápido inline tem os campos nome e cliente_id.
No app, escolher o cliente reduz o combo de equipamento e o Titulo se preenche.
Página salva (Salvar (Cmd+S)) e Teste Online republicado (Testar agora).
Criar uma tela de abertura de OS em etapas, com validação a cada passo, para quem atende o telefone não ter que encarar um formulário de vinte campos de uma vez.
O que você terá no fim
Página Abertura de OS (tipo Formulário, variação Wizard) no módulo Operação.
Três etapas nomeadas: Cliente e equipamento, Problema e Serviços previstos — mais a etapa Resumo, que a plataforma acrescenta sozinha.
A terceira etapa gravando numa tabela própria (os_servico_item), pela chave estrangeira.
GravaçãoNo final: nada entra no banco enquanto a última etapa não for confirmada.
Validação por etapa funcionando no Teste Online.
Studio › + Criar novo › Nova página › Formulário › Variação
Assistente Criar nova página Mad no Passo 2 de 3 · Variação, com as cinco variações de Formulário — Cadastro, Cadastro com Detalhe, Wizard (Formulário em etapas com validação por passo), Consulta e Livre
Studio › Nova página › Wizard › Configuração
Passo 3 de 3 · Configuração com a tabela ordem_servico, a lista Etapas com três linhas — o título de cada etapa é a caixa estreita à esquerda da linha, e a terceira aponta os_servico_item · FK ordem_servico_id —, Gravação em No final, e o bloco de menu/classe/rota/módulo preenchido com Abertura de OS, AberturaOsWizard, /abertura-os e Operação
Studio › Abertura de OS › Visual
Canvas do wizard com o aviso runtime mostra 1 etapa por vez, o indicador das quatro etapas e a etapa Cliente e equipamento aberta; à direita o painel Etapa do Wizard com Chave, Título, Ícone e Descrição
App › Operação › Abertura de OS
App no Teste Online: wizard de abertura de OS depois de tentar avançar sem preencher — o aviso Corrija os erros antes de continuar e os campos obrigatórios marcados em vermelho
Passo a passo
Na tira do Studio, clique no + (Criar novo) → Nova página. No passo Tipo, clique em Formulário.
No passo Variação, as cinco opções aparecem com a descrição de cada uma. Clique em Wizard (Formulário em etapas com validação por passo) — o card escolhido fica com um ✓ — e depois em Continuar.
No passo Configuração, escolha Base de dadosassistec e Tabela = ordem_servico.
A seção Etapas nasce com duas linhas já preenchidas (Dados gerais e Complemento). Cada linha tem o campo de título e, ao lado, o seletor Tabela desta etapa. Renomeie as duas e clique em + Etapa para criar a terceira:
#
Título da etapa
Tabela desta etapa
1
Cliente e equipamento
Tabela principal
2
Problema
Tabela principal
3
Serviços previstos
os_servico_item · FK ordem_servico_id
A dica ao lado explica o que muda: *Etapa com tabela grava numa tabela própria (FK pro master)*.
⚠️ O campo de título fica bem estreito quando alguma linha tem uma tabela de nome longo (os_servico_item · FK ordem_servico_id): o seletor empurra o campo de texto. O texto continua lá e é gravado normalmente — clique dentro e use as setas para conferir.
Em Gravação, escolha No final. A dica ao lado muda conforme a escolha:
No final — Transação única no Salvar final (tudo ou nada);
A cada etapa — Avançar grava a etapa — processo retomável, pode deixar registro parcial.
Preencha Nome do item de menu com Abertura de OS, Classe de controle com AberturaOsWizard, Route com /abertura-os e Módulo com Operação. Deixe Exibir no menu em Sim e clique em Criar página.
O painel SERÁ GERADO, à direita, mostra o que vai sair: app/Controllers/AberturaOsWizard.php, app/Views/operacao/AberturaOsWizard.mad e a rota /abertura-os. O arquivo no Explorer, porém, aparece com o nome derivado da rota (abertura-os-wizard.blade.php) — e o item na lista de telas leva o nome do menu, Abertura de OS.
No canvas, as etapas aparecem empilhadas, cada uma com o seu selo e a sua área de soltar. O aviso no topo lembra: runtime mostra 1 etapa por vez — aqui todas ficam visíveis pra editar. Repare no contador: Wizard · 4 etapas — a plataforma acrescenta uma etapa Resumo no fim, para a pessoa conferir antes de gravar.
O gerador põe TODOS os campos da tabela na primeira etapa. A etapa 2 nasce vazia. O trabalho da aula é distribuir: arraste da etapa 1 para a etapa 2 o que é sobre o problema (tipo, prioridade, descricao_problema, dt_agendada) e apague da etapa 1 o que só faz sentido depois do atendimento (descricao_solucao, dt_fim, progresso, horas_trabalhadas, os campos de valor, status_aprovacao) — esses continuam no OrdemServicoForm da aula 09.
As colunas numero, titulo, cliente_id, status_os_id, descricao_problema e dt_abertura são obrigatórias no banco. Toda coluna obrigatória precisa continuar em alguma etapa — nem que seja com valor padrão. Se ficar de fora, o wizard chega ao fim e o salvamento falha sem ter onde mostrar o erro.
Clique no selo de uma etapa para abrir o painel dela: Chave, Título, Ícone e Descrição. A Chave é o identificador estável (cliente_e_equipamento), gerado a partir do título — é ela que a validação por etapa usa. Mude o Título à vontade; a Chave, só com motivo.
Selecione o bloco Wizard (o selo de fora) para ver Indicador — numbers, dots, arrows ou progress —, Indicador clicável e a mesma Gravação escolhida no assistente. Deixe Indicador clicável ligado: a pessoa volta para conferir o que já preencheu.
A etapa 3 já nasceu ligada a os_servico_item: ela é uma seção de detalhe com os campos da tabela filha prefixados (os_servico_item__servico_id, os_servico_item__quantidade…). Deixe só o que a abertura precisa — serviço, quantidade e preço — e apague o resto.
Clique em Salvar (Cmd+S). No rail, abra Teste online e clique em Testar agora.
No app, entre em Operação → Abertura de OS e confira:
o indicador das etapas aparece no topo, com a etapa 1 em destaque;
clicar em Avançar sem preencher mostra o aviso Corrija os erros antes de continuar e marca cada campo obrigatório em vermelho, com o texto O campo <nome> é obrigatório.;
preenchendo tudo, a etapa 2 abre e o indicador marca a 1 como concluída;
na etapa 3, adicionar um serviço soma no rodapé;
Salvar grava a OS e os itens de uma vez — e não antes.
Abra Operação → Ordem de Serviços e confirme que a OS aberta pelo wizard está lá, com os itens.
Wizard não substitui o formulário completo.O wizard é para abrir; o OrdemServicoForm da aula 09 continua sendo a tela de trabalhar a OS, com peças, anexos e valores. As duas gravam na mesma tabela.
Erros comuns
Escolhi a variação errada e já estou na configuração
use Voltar, no topo do assistente: o passo Variação reabre com o card certo à disposição.
O botão Criar página fica cinza
falta um campo obrigatório (marcado com *): em geral é o Módulo, que começa em Selecione....
Não acho o arquivo da página no Explorer
ele leva o nome derivado da Route (abertura-os-wizard.blade.php), não o da classe; na busca de telas, procure pelo nome do menu (Abertura de OS).
A etapa 2 está vazia
é o estado inicial: o gerador coloca todos os campos na etapa 1 → arraste o que for da etapa 2 para lá.
Avancei e o wizard deixou passar campo obrigatório vazio
esse campo está numa etapa posterior, ou foi apagado da tela; a validação por etapa só cobre o que está na etapa.
Salvei no fim e nada foi gravado
com Gravação No final a transação é única: um erro em qualquer etapa desfaz tudo → leia a mensagem e corrija a etapa apontada.
Quero retomar um cadastro pela metade
aí a escolha certa é A cada etapa, ciente de que ela deixa registro parcial no banco.
O indicador não deixa voltar
Indicador clicável está desligado no bloco do wizard.
Checklist de encerramento
A página Abertura de OS existe, no módulo Operação e visível no menu.
As três etapas estão nomeadas, e a terceira aponta os_servico_item.
Gravação está em No final.
Os campos foram distribuídos entre as etapas 1 e 2 — nenhuma coluna obrigatória ficou de fora.
Cada etapa tem uma Chave estável.
No app, avançar sem preencher mostra o erro e não muda de etapa.
A OS aberta pelo wizard aparece na listagem, com os itens.
Página salva (Salvar (Cmd+S)) e Teste Online republicado (Testar agora).
Montar uma tela de consulta da OS — cabeçalho em leitura e as listagens das tabelas filhas — e conhecer os campos que só existem no celular: assinatura na tela, leitura de código de barras e foto com recorte.
O que você terá no fim
Página Consulta de OS (tipo Formulário, variação Consulta) no módulo Operação, com três Listagens detalhe: os_servico_item, os_peca_item e os_historico.
O campo assinatura_base64 dos anexos como Assinatura, com altura, modos e câmera.
O campo codigo_lido como Scanner, no formato Os dois.
O campo imagem como Imagem, com recorte e limite de tamanho.
Clareza sobre quando usar Upload Múltiplo em vez de um formulário por linha.
Studio › + Criar novo › Nova página › Formulário › Variação
Grade de variações do passo Variação de formulário, com o card Consulta (Visualização com listagens detalhe) em destaque ao lado de Cadastro, Cadastro com Detalhe, Wizard e Livre
Studio › Nova página › Consulta › Configuração
Passo 3 de 3 · Configuração da Consulta sobre ordem_servico, com Listagens detalhe marcando os_servico_item, os_peca_item e os_historico, e o bloco de menu/classe/rota/módulo em Consulta de OS, ConsultaOsForm, /consulta-os e Operação
Studio › OrdemServicoForm › dfOsAnexo › assinatura_base64
Campo assinatura_base64 do Detail Form de anexos, do tipo Assinatura (mad-signature-field), com Aparência (Altura 200px, Largura 100%) e Comportamento (Modos disponíveis draw,type,upload e Permitir câmera ligado) abertos
Studio › OrdemServicoForm › dfOsAnexo › codigo_lido
Campo codigo_lido convertido para Scanner (mad-scanner-field), com a seção Formato aberta mostrando a escolha Os dois (as outras são Só QR e Só código de barras)
App › Operação › Consulta de OS
App no Teste Online: a Consulta de OS aberta sobre a OS OS-2026-001 — cabeçalho só de leitura (ID, NUMERO, TITULO, CLIENTE, STATUS OS, DESCRICAO PROBLEMA…, com — no que está vazio) e, no rodapé do quadro, a primeira listagem detalhe com as colunas SERVICO, QUANTIDADE, PRECO UNITARIO, DESCONTO PCT, SUBTOTAL e OBSERVACAO
Passo a passo
Na tira do Studio, clique no + (Criar novo) → Nova página → Formulário. No passo Variação, clique em Consulta (Visualização com listagens detalhe) e depois em Continuar.
No passo Configuração, escolha Base de dadosassistec e Tabela = ordem_servico. A seção Listagens detalhe: aparece com todas as tabelas que apontam para ela, cada uma com o (via ordem_servico_id) e uma caixa de marcação. Marque os_servico_item, os_peca_item e os_historico.
Repare na diferença para a aula 09: no Cadastro com Detalhe a seção se chama Tabelas detalhe e cada linha tem um seletor field-list / detail-form. Na Consulta não há seletor — tudo vira listagem, porque a tela é de leitura.
Preencha Nome do item de menu com Consulta de OS, Classe de controle com ConsultaOsForm, Route com /consulta-os e Módulo com Operação. Deixe Exibir no menu em Sim e clique em Criar página.
No canvas, a tela nasce com o cabeçalho da OS e as três listagens. Selecione um campo do cabeçalho (numero, cliente_id, titulo) e, em Comportamento, confirme Somente leitura. Dois caminhos valem aqui:
Somente leitura no próprio campo — mantém a aparência de campo, sem digitação;
trocar o tipo para Display — vira texto puro, sem caixa.
Use Display no que é só informação (numero, dt_abertura) e Somente leitura no que a pessoa reconhece como campo.
Abra OrdemServicoForm — é lá que a tabela os_anexo é editada, pelo Detail FormdfOsAnexo que você montou na aula 09. Na Árvore, abra detail-form (dfOsAnexo) → detail-fields → form-grid para chegar nos campos.
Repare no que o gerador já escolheu pelo nome e pelo tipo da coluna: assinatura_base64 nasceu Assinatura (mad-signature-field), imagem nasceu Imagem (mad-image-field) e arquivo/nome_arquivo nasceram Upload (mad-file-field). Só codigo_lido nasce como Texto.
Selecione assinatura_base64. Confira o tipo Assinatura no combo do topo e configure:
Aparência → Altura200px, Largura100%;
Comportamento → Modos disponíveisdraw,type,upload (desenhar, digitar o nome ou enviar uma imagem) e Permitir câmera ligado;
Armazenamento de dados — deixe como está: o desenho é gravado direto na coluna, que é texto longo.
Selecione codigo_lido e troque o tipo para Scanner (o combo do topo do painel; busque por "Scanner"). Abra a seção Formato e escolha Os dois — as alternativas são Só QR e Só código de barras. O campo continua aceitando digitação, para quem não tem câmera.
Selecione imagem (tipo Imagem) e abra Armazenamento de dados: Tipo de gravação em Armazenar caminho do arquivo na coluna, Pasta de upload, os Tipos aceitos só em imagens e Tamanho máximo5MB. No app, esse campo vira a área *"Arraste uma imagem ou clique para selecionar"*, já anunciando IMAGE/PNG, IMAGE/JPEG, IMAGE/GIF · Máx 5MB.
Só para conhecer o caminho alternativo: um Upload Múltiplo tem, em Armazenamento de dados, o Modo de armazenamento com duas opções — comma (os caminhos em CSV numa única coluna) e table (uma linha por arquivo, numa tabela-pivô). É a escolha certa quando a pessoa manda muitos arquivos de uma vez e não precisa descrever cada um. Aqui, quem manda em os_anexo é o Detail Form — não adicione um Upload Múltiplo apontando a mesma tabela: dois componentes gravando a mesma tabela brigam entre si.
Clique em Salvar (Cmd+S). No rail, abra Teste online e clique em Testar agora.
No app, abra Operação → Ordem de Serviços, edite a OS de exemplo e clique em Adicionar anexo. A gaveta Anexo da OS mostra, lado a lado:
Tipo e Nome Signatario, campos de texto;
dois campos de upload (*Clique para enviar ou arraste aqui*);
a área de Imagem, com o limite anunciado;
a Assinatura, com o botão Desenhar, a tela de desenho e a paleta de cores;
o Scanner, que abre a câmera no celular e aceita digitação no computador.
Abra a Consulta de OS a partir de uma OS da listagem — a tela é de um registro e precisa do id. O cabeçalho vem em leitura, cada coluna como rótulo e valor (com — no que está vazio), e embaixo vêm as listagens dos serviços, das peças e do histórico daquela OS.
Consulta não é somente leitura por decreto.A variação Consulta monta a tela com as listagens e o cabeçalho, mas o que impede a edição é o Somente leitura de cada campo. Quem tiver permissão de editar a OS continua editando pelo OrdemServicoForm — é lá que a regra de acesso vale, e ela é assunto da aula 05.
Erros comuns
A Consulta abre dizendo que não encontrou o registro
a tela é de UM registro: ela precisa ser aberta a partir de uma listagem, com o id na URL.
Não acho o campo de assinatura na Árvore
ele está dentro do Detail Form, e a Árvore começa com esse nó fechado → abra detail-form → detail-fields → form-grid.
Troquei o tipo para Scanner e a seção Formato não apareceu
o painel é remontado na troca; role até o fim: Formato entra entre Comportamento e Apresentação.
A assinatura não grava
o campo precisa apontar uma coluna de texto longo (assinatura_base64 é text); numa coluna curta o desenho não cabe.
A foto sai gigante
faltou Tamanho máximo e Permitir recorte no campo de imagem.
O scanner não abre a câmera
o navegador só libera câmera em HTTPS e com permissão; no computador, use a digitação.
Coloquei um Upload Múltiplo e os anexos começaram a se perder
dois componentes gravando os_anexo ao mesmo tempo → escolha um.
Checklist de encerramento
A página Consulta de OS existe, no módulo Operação e visível no menu.
Ela tem as três Listagens detalhe: serviços, peças e histórico.
Os campos do cabeçalho estão em Somente leitura (ou como Display).
assinatura_base64 é Assinatura, com Modos disponíveis e Permitir câmera.
codigo_lido é Scanner, no formato Os dois.
imagem grava o caminho, com pasta, tipos aceitos e tamanho máximo.
No app, Adicionar anexo abre a gaveta com upload, imagem e assinatura.
No app, a Consulta de OS abre a partir de uma OS, com o cabeçalho em leitura e as listagens detalhe embaixo.
Página salva (Salvar (Cmd+S)) e Teste Online republicado (Testar agora).
Transformar a tabela de ordens de serviço num quadro visual em que cada coluna é um status e arrastar o cartão muda o status no banco.
O que você terá no fim
Página Quadro de OS (tipo Kanban) no módulo Operação, visível no menu do app.
Seis colunas geradas a partir de status_os, na ordem e nas cores cadastradas na aula 03.
Cartão mostrando número da OS, cliente, técnico e valor em moeda.
Clique no cartão abrindo a OS no formulário da aula 09, e uma barra de filtros por técnico e por cliente no topo do quadro.
Arrastar um cartão gravando ordem_servico.status_os_id — conferido no Teste Online.
Studio › + › Nova página › Kanban
Assistente de página nova no passo Configuração (Passo 3 de 3), com o combo Tabela de etapas aberto e status_os na lista
Studio › + › Nova página › Kanban
Mesmo passo com o combo Tabela de itens aberto mostrando ordem_servico (FK: status_os_id)
Studio › Quadro de OS › Visual › Card do Kanban › Editor visual com preview
Modal Configurar meta do card com as três metas na lista e o cartão montado no preview ao vivo
Studio › Quadro de OS › Configurar filtros do Kanban
Modal Filtros do Kanban com Toolbar marcado, os dois filtros DB Combo e a regra WHERE tecnico_id = :tecnico_id
App › Operação › Quadro de OS
App no Teste Online com as seis colunas de status lado a lado e os cartões mostrando número, cliente, valor e técnico
Passo a passo
Na tira do Studio, clique em + → Nova página e escolha Kanban (Quadro com colunas de status) na lista de tipos.
O Kanban não tem variações: o clique no tipo já leva ao passo Configuração (o trilho da esquerda mostra Variação · Não aplicável, e o rodapé, Passo 3 de 3). Não há Continuar para clicar.
Deixe Base de dados no banco do projeto (assistec) e escolha em Tabela de etapas a tabela status_os. Cada linha dela vira uma coluna do quadro.
Em Tabela de itens, escolha ordem_servico (FK: status_os_id). O combo fica desabilitado até você escolher as etapas (dica selecione a tabela de etapas) e só lista tabelas que apontam para elas — sem FK, a dica vira nenhuma tabela com FK para etapas.
Preencha Nome do item de menu com Quadro de OS, Classe de controle com QuadroOsMad e Route com /quadro-os (os dois vêm sugeridos a partir da tabela de itens — OrdemServicoKanban e /ordemservicokanban). Escolha Módulo = Operação, deixe Exibir no menu em Sim e clique em Criar página.
⚠️ O título da tela e o rótulo do menu não usam o nome que você digitou. Os dois nascem Kanban de status de os, derivados da tabela de etapas. Arrume nos dois lugares em que eles moram:
barra do editor → Propriedades da página → Título = Quadro de OS e, mais abaixo, Rótulo no menu = Quadro de OS; Fechar;
painel do meio → aba Árvore → page-header → Título = Quadro de OS.
Clique em Salvar.
Selecione o nó kanban na Árvore. Em Fonte de dados, Database, Model (cards), Model (stages) e FK do stage aparecem travados, com a nota Definido na criação da página — não editável aqui. Para trocar, crie outra página.
Abra Coluna (stage). ⚠️ Atributo com o título, Atributo com a cor e Coluna de ordenação aparecem desabilitados, com o texto Configure model + database — ver *Erros comuns*. Você não precisa deles nesta aula: sem nada preenchido o quadro assume nome, cor e ordem na tabela de etapas, que é como a status_os foi montada na aula 03. Deixe Ordem em Ascendente.
Em Comportamento, confirme Drag & drop ligado e use Click no card → Escolher página → OrdemServicoForm (OrdemServicoForm) para o cartão abrir a OS da aula 09.
Selecione o Card do Kanban (nó kanban-card). Em Título, escolha numero — é um combo de atributo: ele lista as colunas de ordem_servico e as de tabelas relacionadas.
Ainda em Conteúdo, clique em Editor visual com preview logo abaixo de Meta do card. O modal Configurar meta do card abre com o cartão montado à direita, e tudo que você muda aparece nele na hora. Adicione:
Bloco com Path / templatecliente->nome_razao_social;
Inline (chip) com Path / templatetecnico->nome;
Bloco com Path / templatevalor_total e, em Formatação, Tipo = Moeda — só então aparecem Prefixo (R$ ) e Casas decimais (2).
Cada linha da lista tem Remover e Duplicar; arrastar reordena. Feche em Fechar.
Logo abaixo, Badges do card tem o mesmo par: os atalhos #ID, Estado e Texto na *edição rápida* e o Editor visual com preview para o modal Configurar badges do card. O cartão já nasce com #ID e Estado; declarar qualquer badge próprio desliga os dois automáticos — o próprio modal avisa isso.
Em Comportamento, Ações do card oferece Inline (rodapé), Menu (três pontinhos) e Dropdown (botão único). Cada ação guarda o seu próprio *placement*.
Na barra do editor, clique no ícone Configurar filtros do Kanban (só aparece em página do tipo Kanban). No modal Filtros do Kanban, escolha Estilo = Toolbar e preencha Título (opcional) com Filtros.
Em Filtros declarados, clique em Adicionar filtro duas vezes. Em cada linha, escolha o tipo DB Combo e dê o nome do *bind*: tecnico_id e cliente_id. O rodapé do modal explica o que cada um vira no controller: public string $X = '' no bloco @mad-block:kanban-filter-props.
Em Regras de filtro, clique em + Adicionar Regra de Filtro. Abre o construtor Regras de filtro — OrdemServico (é ali, e não no modal de filtros, que mora a chave Auto-vincular com filtros declarados). Clique em Regra, escolha Coluna = tecnico_id e Valor = tecnico_id; o Preview do PHP gerado mostra o when(...) que vai para o controller. Clique em Aplicar. A lista passa a mostrar OrdemServico · 1 regra — WHERE tecnico_id = :tecnico_id.
Fechar, Salvar e, no rail, Teste online → Testar agora para o app receber a tela nova.
No app, entre em Operação → Quadro de OS. As seis colunas aparecem na ordem de status_os.ordem, com o ponto colorido de status_os.cor e a contagem ao lado do nome. Arraste um cartão de Aberta para Agendada e reabra a OS no formulário para confirmar que o status mudou.
Erros comuns
O combo Tabela de itens fica vazio, com o aviso nenhuma tabela com FK para etapas
nenhuma tabela do banco aponta para status_os → volte a Modelos de dados e crie a chave estrangeira ordem_servico.status_os_id antes de criar a página.
O menu e o título da tela dizem Kanban de status de os
é assim que a página nasce: o assistente usa a tabela de etapas para o rótulo, e não o Nome do item de menu que você digitou. Corrija em Propriedades da página (Título e Rótulo no menu) e no nó page-header da Árvore (passo 6).
Configure model + database nos combos de coluna do <mad-kanban>
o Kanban nasce sem a propriedade database, e esses combos exigem *model* + *database* no mesmo nó; como o campo Database é travado ("Definido na criação da página"), não há como preenchê-lo pelo editor visual. Na prática isso só impede o Campo numérico (o somatório por coluna); título, cor e ordem das colunas já têm padrão (nome, cor, ordem) e o quadro sai correto sem eles.
O quadro abre sem nenhuma coluna
a tabela status_os está vazia → insira os seis status da aula 03 (ABERTA, AGENDADA, EM_ATENDIMENTO, AGUARDANDO_PECA, CONCLUIDA, CANCELADA) e recarregue.
As colunas saem com o código em vez do nome, ou fora de ordem
a tabela de etapas não tem as colunas nome, cor e ordem com esses nomes → ou renomeie as colunas no modelo, ou preencha Coluna (stage) pelo editor de Blade (ver o item anterior).
O cartão só mostra o número, e o painel avisa Nenhuma meta configurada. Use os botões acima.
o card ainda não tem meta → abra Editor visual com preview em Meta do card e adicione pelo menos um Bloco.
Arrastei o cartão no app e o status voltou ao que era
ou Drag & drop está desligado no painel, ou a página não foi republicada → ligue a opção, clique em Salvar e use Testar agora no Teste online.
Arrastar dentro da MESMA coluna não guarda a ordem
reordenar cartões exige Campo de ordem em Fonte de dados, que está bloqueado pelo mesmo motivo do Configure model + database. Mudar de coluna (que é o que a aula ensina) funciona normalmente.
Checklist de encerramento
A página Quadro de OS existe no módulo Operação e abre pelo menu do app.
O menu e o cabeçalho dizem Quadro de OS (não Kanban de status de os).
O quadro mostra seis colunas, na ordem de status_os.ordem e com a cor de status_os.cor.
O cartão mostra número, cliente, valor_total em moeda e o técnico como chip.
A barra de filtros aparece no topo com Tecnico_id e Cliente_id.
Regras de filtro mostra OrdemServico · 1 regra.
Arrastar um cartão de Aberta para Agendada muda o status ao reabrir a OS.
Página salva (Salvar) e Teste Online republicado (Testar agora).
Transformar as datas de agendamento da OS numa agenda visual, em que a equipe vê a semana inteira, abre o chamado com um clique e remarca a visita arrastando o evento.
O que você terá no fim
Página Agenda de visitas (tipo Calendário) no módulo Operação, visível no menu do app.
Eventos lidos de ordem_servico, começando em dt_agendada e terminando em dt_prevista.
Título do evento com número da OS e cliente, e a cor do evento vinda do status.
Jornada de 08:00 às 18:00, sem fim de semana, com slot de 1 hora.
Clique no evento abrindo o formulário de OS e arraste gravando a nova data — conferido no Teste Online.
Studio › + › Nova página › Calendário
Assistente no passo Configuração (Passo 3 de 3) com dt_agendada (timestamp) e dt_prevista (timestamp) escolhidas e o botão Criar página aceso
Studio › Agenda de visitas › Visualização › Configurar visualização
Janela Configurar visualização com Horário 08:00–18:00, Duração do slot em 1 hora e Sem fim de semana ligado (os dias da semana esmaecidos)
App › Operação › Agenda de visitas
App no Teste Online com a agenda de segunda a sexta, das 08h às 18h, três visitas marcadas com número e cliente no título e a barra de filtros (Tecnico_id, Cliente_id) no topo
Passo a passo
Na tira do Studio, clique em + → Nova página e escolha Calendário (Agenda visual de eventos).
O Calendário não tem variações: o clique no tipo já leva ao passo Configuração (Passo 3 de 3; o trilho mostra Variação · Não aplicável).
Deixe Base de dados no banco do projeto (assistec) e escolha ordem_servico em Tabela.
Em Coluna com a data/hora inicial do agendamento, escolha dt_agendada (timestamp). O combo só oferece colunas de data, data/hora ou timestamp — a dica ao lado conta quantas existem (9 coluna(s)); se a tabela não tiver nenhuma, a dica vira nenhuma coluna date/datetime/timestamp.
Em Coluna com a data/hora final do agendamento, escolha dt_prevista (timestamp). As duas são obrigatórias — o botão Criar página só acende com as duas preenchidas.
Preencha Nome do item de menu com Agenda de visitas, Classe de controle com AgendaVisitasMad, Route com /agenda-visitas e Módulo com Operação. Deixe Exibir no menu em Sim e clique em Criar página.
⚠️ Como no Kanban, o rótulo do menu e o título da tela nascem derivados da tabela (Calendário de ordem de serviços). Arrume nos dois lugares: Propriedades da página → Título e Rótulo no menu = Agenda de visitas; e, na Árvore, page-header → Título. Salvar.
Selecione o nó calendar na Árvore. Em Fonte de dados, Database, Model, Data/hora inicial e Data/hora final aparecem travados, com a nota Definido na criação da página — não editável aqui. Para trocar, crie outra página.
Campo título aceita um atributo só — ou uma máscara com vários. A máscara não sai da lista: clique no botãozinho Múltiplos atributos / máscara ao lado do combo, escreva {numero} — {cliente->nome_razao_social} na caixa Máscara e clique em Aplicar. (Clicar nos atributos da lista de baixo também anexa cada um à máscara.)
Em Campo cor, escolha {statusOs->cor} — a mesma coluna cor que a aula 03 cadastrou em status_os. ⚠️ Repare no nome: a lista mostra a relação do model (statusOs), não a tabela (status_os). Tabela de nome composto vira relação em camelCase, e é assim que ela aparece aqui. Cada evento passa a sair na cor do status em que a OS está.
Campo ID aparece desabilitado com Configure model + database (mesma limitação do Kanban) e não precisa de você: o padrão é id. Deixe Campo all-day vazio — as visitas têm hora marcada.
Abra Visualização. View padrão já vem agendaWeek (a semana com as horas do dia); month mostra o mês inteiro e listWeek, a semana em lista.
Clique em Configurar visualização. Em Horário, coloque Início08:00 e Fim18:00 — a jornada comercial da assistência. O que você escolher aqui vira time-range="08:00-18:00" no Blade da página.
Ainda na janela, em Duração do slot clique em 1 hora (são quatro atalhos — 15 min, 30 min, 1 hora, 2 horas — mais um campo de hora livre ao lado). Em Dias da semana, ligue Sem fim de semana: os sete botões de dia ficam esmaecidos, porque o atalho manda neles. Confirme Locale = Português (Brasil) e Fechar.
Abra a aba EVENTOS do painel (ao lado de PROPRIEDADES). Em Clique no evento, deixe Escolher página marcado e selecione OrdemServicoForm (OrdemServicoForm). Em Modo de abertura, escolha drawer (gaveta lateral) ou modal. Se quiser uma pergunta antes de gravar o arraste, preencha Texto confirm() com Remarcar esta visita?.
Ainda em EVENTOS, Clique em dia vazio aponta o formulário de OS: quem clica num horário livre abre uma OS nova já com aquela data, que chega como {date}.
Volte a PROPRIEDADES, abra Edição e ligue Drag & resize. Auto-update no drag já vem ligado — é ele que grava a data nova ao soltar o evento.
Abra Popover e preencha Popover título (máscara, igual ao passo 9) com {numero} — {cliente->nome_razao_social} e Popover conteúdo com descricao_problema. Em Gatilho, hover mostra a espiada ao passar o mouse; click exige um clique.
Em Apresentação, deixe Altura (px) vazio ou ligue Ocupa altura total para a agenda ocupar a tela.
Na barra do editor, clique em Configurar filtros do Calendário. Escolha Estilo = Toolbar, Título (opcional) = Filtros e, em Filtros declarados, Adicionar filtro duas vezes, com tipo DB Combo e os nomes tecnico_id e cliente_id. Em Regras de filtro, + Adicionar Regra de Filtro abre o construtor (é lá que mora Auto-vincular com filtros declarados).
Clique em Salvar. No rail, abra Teste online e clique em Testar agora.
No app, entre em Operação → Agenda de visitas. A semana aparece de segunda a sexta, das 08h às 18h, com os eventos escritos número — cliente. Clique num evento: o formulário da OS abre. Arraste outro para o dia seguinte e reabra a OS para confirmar que dt_agendada mudou.
Dica de dados.O gerador de dados de teste espalha dt_agendada por vários meses, então a semana que a agenda abre pode aparecer vazia. Agende quatro ou cinco OS para esta semana (formulário da OS → Dt Agendada e Dt Prevista) antes de gravar a aula.
Erros comuns
A agenda abre vazia, sem nenhum evento
ou as OS estão com dt_agendada em branco, ou as que têm data caem em outra semana → agende algumas OS para a semana corrente e recarregue; Hoje, Mês e as setas ajudam a achar onde os eventos estão.
A agenda mostra as 24 horas do dia
a página não foi republicada depois de você mexer em Configurar visualização, ou Início/Fim ficaram em branco → confira a jornada, Salvar e Testar agora. (Até setembro/2026 a faixa 08:00–18:00 era a única que a janela não gravava, por ser o valor que ela exibe como padrão; está corrigido.)
Os eventos aparecem todos na mesma cor, mesmo com Campo cor preenchido
falta republicar. (Até setembro/2026 relação de nome composto não pintava o evento; hoje a própria lista já oferece a chain pelo nome da relação, {statusOs->cor}, e o app resolve. Nomes simples, como {cliente->…}, sempre funcionaram.)
Cada evento mostra só um número
Campo título está vazio e o calendário caiu no padrão → escreva a máscara pelo botão Múltiplos atributos / máscara.
Arrastei o evento e a data voltou ao que era
Drag & resize está desligado, Auto-update no drag foi desmarcado, ou a página não foi republicada → ligue os dois em Edição, Salvar e Testar agora.
O sábado e o domingo continuam na tela
a chave Sem fim de semana não chegou a virar: ela é um interruptor à direita do texto, e clicar no rótulo não faz nada. Clique na chavinha — os sete botões de dia ficam esmaecidos quando ela liga.
O botão Criar página fica apagado no passo Configuração
falta a coluna de fim → o calendário exige as duas datas; escolha dt_prevista em Coluna com a data/hora final do agendamento.
Os eventos aparecem como faixas de dia inteiro, sem hora
Campo all-day aponta uma coluna verdadeira em todas as linhas → deixe o campo vazio.
Checklist de encerramento
A página Agenda de visitas existe no módulo Operação e abre pelo menu do app.
O menu e o cabeçalho dizem Agenda de visitas.
Os eventos vêm de ordem_servico, começando em dt_agendada e terminando em dt_prevista.
Cada evento mostra número da OS e cliente no título.
A agenda abre na semana, das 08:00 às 18:00, sem sábado e domingo.
Clicar num evento abre o formulário da OS, e clicar num horário livre abre uma OS nova.
Passar o mouse num evento mostra o popover com o problema.
Arrastar um evento para outro dia grava a nova dt_agendada.
A barra de filtros aparece no topo com Tecnico_id e Cliente_id.
Página salva (Salvar) e Teste Online republicado (Testar agora).
Montar a tela de abertura do sistema: quatro números-resumo, gráficos por status, por mês e por técnico, e uma barra de filtros no topo que recalcula tudo de uma vez.
O que você terá no fim
Página Painel da operação (tipo Dashboard) no módulo Relatórios, visível no menu do app.
Grade desenhada no designer de layout: uma linha de quatro cartões e três linhas de blocos.
Quatro KPI Card — OS abertas (com regra de carregamento cortando as concluídas e as canceladas), horas trabalhadas, ticket médio e faturamento.
Um Bar Chart por status, um Line Chart por mês, um Donut Chart por técnico, uma Pivot Table e uma Grid (listagem).
Barra de filtros com período e cliente, e cada bloco dizendo a quais filtros obedece.
Clique numa barra do gráfico de status filtrando o painel inteiro.
Studio › Painel da operação › Configurar layout
Janela Layout do Dashboard com as quatro linhas montadas (4 KPI Cards; Bar+Line; Donut+Pivot em 60/40; a Linha 4 de uma coluna aparecendo no rodapé) e o botão Aplicar layout visível
Studio › Painel da operação › Visual › KPI Card
Painel do KPI Card os_abertas: REGRAS DE CARREGAMENTO com 1 filtro · 1 regra (AND) e o botão Editar filtros, o grupo Filtros do dashboard em Todos, e Identificação + Fonte de dados abertas (Database assistec, Tabela (Model) ordem_servico, Agregação Contar)
Studio › Painel da operação › Visual › Bar Chart
Canvas com o Bar Chart selecionado ao lado do painel mostrando Tabela (Model), Agrupar por e o preview dos gráficos
Studio › Painel da operação › Configurar filtros do dashboard
Janela Filtros do Dashboard com Toolbar marcado e os dois filtros declarados (Período mês/ano e DB Combo cliente_id)
Studio › Painel da operação › Visual › Bar Chart › Drill-down
Seção Drill-down do Bar Chart com Habilitar filtro ao clicar ligado, Modo em Direto (FK simples) e Propriedade alvo status_os_id
App › Painel da operação
App no Teste Online com os quatro cartões no topo (29 em OS abertas — já sem as concluídas e canceladas —, 19.744, R$ 5.265,76 e R$ 210.630,31), o Bar com os nomes dos status, o Line por mês, o Donut por técnico, a Pivot em Sem dados para exibir e a listagem de apoio embaixo; barra de filtros com o mês escolhido
Passo a passo
Na tira do Studio, clique em + → Nova página e escolha Dashboard (KPIs, gráficos e indicadores). Como os outros tipos sem variação, o clique já leva ao passo Configuração.
Não há combo de tabela — o dashboard não pede nenhuma. Preencha Nome do item de menu com Painel da operação, Classe de controle com PainelOperacaoDashboard, Route com /painel-operacao e Módulo com Relatórios. Clique em Criar página. (Aqui o rótulo do menu e o título saem certos — o desvio do Kanban e do Calendário não acontece porque não há tabela de onde derivar nome.)
A tela nasce com o bloco de filtros no topo e a grade vazia. Na barra do editor, clique em Configurar layout (linhas + colunas de widgets).
Na janela Layout do Dashboard, clique em Adicionar linha e monte:
Linha 1 — Cols4, ModoUniforme; as quatro células em KPI Card;
Linha 2 — Cols2, ModoUniforme; Bar Chart e Line Chart;
Linha 3 — Cols2, ModoCustomizado, Larguras (%)60 e 40 (há presets prontos: 50 / 50, 70 / 30, 20 / 60 / 20…); Donut Chart e Pivot Table;
Linha 4 — Cols1, ModoUniforme; Grid (listagem) — a lista de apoio embaixo do painel. Ela precisa de dois ajustes que os outros widgets não pedem (passo 14).
Clique em Aplicar layout. Célula que mantém o tipo mantém a configuração — aparece o aviso props preservadas nela.
Selecione o primeiro KPI Card pela Árvore (db-metric-card). Em Fonte de dados: Database já vem no banco do projeto; escolha Tabela (Model) = ordem_servico e Agregação = Contar (contar não pede campo numérico).
Em Estilo, preencha Label com OS abertas e Cor do cartão com Informação. Em Identificação, Nome (ID) = os_abertas — ele precisa ser único na página. ⚠️ Preencha o Nome (ID) por último: digitar nele redesenha o painel e o próximo combo que você clicar não abre.
Corte o cartão para o que o rótulo promete. Do jeito que está, ele conta todas as OS: o número é 40, o mesmo total da tabela, e o rótulo "OS abertas" passa a mentir. Abra Regras de carregamento (a seção fica acima de PROPRIEDADES) → Configurar filtros → Regra:
Coluna = status_os_id;
Operador = not in (a lista tem =, !=, <>, >, <, >=, <=, like, ilike, not like, not ilike, in, not in, is, is not);
Valor = 5,6 — os dois status que a aula 03 cadastrou com e_final = true: Concluída (5) e Cancelada (6).
Clique em Aplicar. O cartão passa a mostrar 29 no app — e o rótulo volta a ser verdade. A seção fica com o selo 1 filtro · 1 regra (AND).
Regra de carregamento × barra de filtros.A regra é do cartão e a pessoa não desliga — é a definição de "aberta". A barra de filtros do passo 15 é do painel inteiro e recorta o período. O grupo Filtros do dashboard (logo abaixo, com Todos / Somente / Exceto / Nenhum) decide quais filtros da barra este cartão obedece; deixe em Todos.
Repita nos outros três cartões (sem regra de carregamento — eles medem o período inteiro):
horas_mes — AgregaçãoSomar, Campo numéricohoras_trabalhadas, LabelHoras trabalhadas;
ticket_medio — AgregaçãoMédia, Campo numéricovalor_total, LabelTicket médio;
faturamento — AgregaçãoSomar, Campo numéricovalor_total, LabelFaturamento.
Nos dois últimos, clique em Formato do campo numérico, busque R$ e escolha Moeda — R$ (R$ 1.234,50).
Selecione o Bar Chart. Em Fonte de dados, Tabela (Model) = ordem_servico, Agrupar por = {statusOs->nome}, Agregação = Contar. Em Identificação, Título = OS por status e Nome (ID) = os_por_status. ⚠️ Agrupar pela FK (status_os_id) também funciona, mas aí o eixo do gráfico sai com os números dos status. A lista mostra a chain pelo nome da relação do model — {statusOs->nome}, camelCase — e não pelo nome da tabela (status_os).
Selecione o Line Chart: Agrupar por = dt_abertura, Título = OS por mês, Nome (ID) = os_por_mes. Para o eixo mostrar o mês e não o instante, clique na chave ao lado do combo (Aplicar máscara SQL (ex: agrupar por mês)), escolha o banco (SQLite é o que o Teste Online usa) e o preset Mês (AAAA-MM); o rodapé mostra o SQL FINAL (strftime('%Y-%m', dt_abertura)). Aplicar.
Selecione o Donut Chart: Agrupar por = {tecnico->nome}, Título = OS por técnico, Nome (ID) = os_por_tecnico. Em Apresentação, Mostrar legenda já vem ligado.
Selecione a Pivot Table. A fonte de dados dela é própria e usa outro tipo de combo (abre um painel flutuante com busca): escolha Base de dados = assistec e Tabela = ordem_servico, e dê o TítuloServiços por técnico e mês. ⚠️ Não pule este passo: sem tabela o bloco sai :query="$that->baseQuery()" no código e a página inteira para de abrir (*Erros comuns*). Em Configurar Campos e Visões você monta linhas, colunas e medidas da tabela dinâmica.
Grid (listagem) — selecione o nó grid na Árvore. Ele não tem *Agrupar por*: em Fonte de dados ele tem o campo Tabela, com Database e Model (escolha assistec e ordem_servico (OrdemServico)). Num dashboard não existe "tabela da página" — sem isso a página inteira para de abrir, igual à Pivot. Depois abra Colunas: ela começa em COLUNAS (0) / *Nenhum colunas configurado*. Clique + Add uma vez por coluna e preencha Campo e Label: numero (Nº), titulo (Título), dt_abertura (Abertura) e valor_total (Valor). ⚠️ Os filtros de período do dashboard não chegam a esta listagem (o próprio campo avisa) — use a Ordenação padrão e os filtros de coluna.
Na barra do editor, clique em Configurar filtros do dashboard. Escolha Estilo = Toolbar, Título (opcional) = Filtros e, em Filtros declarados, Adicionar filtro duas vezes:
a primeira linha em Período mês/ano — ela não tem campo de nome, porque reserva os binds $mes e $ano;
a segunda em DB Combo, com o nome cliente_id.
Em Critério base ficam as regras fixas que valem para todos os blocos (Nenhuma regra declarada. enquanto não houver).
De volta ao canvas, cada bloco tem no topo do painel a faixa Filtros do dashboard: Todos obedece a tudo, Somente é lista branca, Exceto é lista negra e Nenhum ignora a barra. Deixe tudo em Todos.
Selecione o Bar Chart e abra a seção Drill-down (ela fica acima de PROPRIEDADES). Clique em Habilitar filtro ao clicar — o texto ao lado da chavinha também liga.
Em Modo, escolha Direto (FK simples): o clique manda o próprio valor da categoria para uma propriedade pública. Em Propriedade alvo, escreva status_os_id; o painel mostra que ela é Auto-declarada como public string $status_os_id = ''; no controller. Ligue Re-click na mesma categoria limpa o filtro.
Os outros modos: Lookup (label → ID) quando o gráfico agrupa pelo NOME e o filtro precisa do código (pede Modelo — dois combos, Database + Model — e Campo de match); Período (mes/ano) quando o clique deve mexer no período do painel.
Clique em Salvar. No rail, abra Teste online e Testar agora.
No app, abra Painel da operação. Escolha o mês na barra, clique em Atualizar e veja os blocos recalcularem; clique numa barra do gráfico de status para filtrar o painel inteiro.
Erros comuns
A página inteira estoura com Too few arguments to function …::baseQuery(), 0 passed
algum bloco ficou sem tabela. Pivot Table e Grid (listagem) nascem :query="$that->baseQuery()" — sem model — e o baseQuery() do controller exige um. Configure a Fonte de dados da Pivot (passo 13) e, no Grid (listagem), o campo Tabela (passo 14).
A listagem do painel aparece como uma faixa vazia, só com o paginador (1–15 de 40)
ela está com a tabela certa e nenhuma coluna. O widget nasce com COLUNAS (0); acrescente as colunas no + Add da seção Colunas (passo 14).
O gráfico vem vermelho com ambiguous column name: deleted_at
a página foi criada antes de setembro/2026 e o baseQuery() dela é o antigo → abra a página no editor e Salvar: o bloco é regravado com a coluna qualificada, e agrupar por chain ({statusOs->nome}, {tecnico->nome}) volta a funcionar.
Em Agrupar por, procuro o status pelo nome da tabela e a lista não tem
a chain aparece com o nome da relação (em camelCase), não o da tabela: procure por {statusOs->nome}. Vale para toda tabela de nome composto.
O combo não abre depois que preenchi o Nome (ID)
escrever no campo de id redesenha o painel; clique de novo no combo, ou preencha a Fonte de dados antes do Nome (ID) (passo 7).
Nome (ID) em vermelho
dois blocos com o mesmo id, ou id vazio. Cada bloco precisa de um identificador único na página.
O cartão mostra count(Model) em vez de um número
é o preview do editor, que não executa a consulta. O número só aparece no app.
"OS abertas" mostra o total da tabela (40)
falta a regra de carregamento do passo 8 → status_os_idnot in5,6. Com ela o número cai para 29. Confira o Preview do PHP gerado: ele tem que terminar em ->whereNotIn('status_os_id', [5, 6]).
A Pivot Table diz Sem dados para exibir
ela tem tabela, mas os campos (linhas/colunas/medidas) ainda não foram montados em Configurar Campos e Visões.
O painel ignora o filtro de período
o bloco está em Nenhum ou Exceto na faixa Filtros do dashboard, ou o filtro Período mês/ano não foi declarado.
Cliquei na barra e nada aconteceu
Drill-down desligado, Propriedade alvo vazia, ou a página não foi republicada.
Aparece uma tira ⚙ SQL DEBUG — METRIC_… embaixo de cada bloco no app
não é erro da sua página: era o app gerado nascendo com general.debug='1' fixo no config/mad.php do esqueleto antigo. A partir do framework 5.83.2 o esqueleto usa env('MAD_DEBUG', false), então um app novo (ou um existente depois de uma publicação completa — Recriar ambiente ou Testar agora → Tudo) não mostra mais a tira. Se ainda aparecer depois de uma publicação completa, é um esqueleto anterior à 5.83.2 — vale reportar.
Checklist de encerramento
A página Painel da operação existe no módulo Relatórios e abre no app.
A grade tem quatro linhas: 4 cartões, 2 gráficos, donut + pivot em 60/40 e a listagem de apoio.
Os quatro cartões mostram número, horas, ticket médio e faturamento — os dois últimos em moeda.
O cartão OS abertas tem 1 filtro · 1 regra (AND) e mostra 29 no app (não 40): a regra status_os_id not in 5,6 está gravada.
O Bar Chart mostra a contagem por status (com o nome no eixo, não o código) e o Donut, por técnico.
A listagem de apoio mostra as colunas Nº, Título, Abertura e Valor.
A barra de filtros tem o período (mês/ano) e o combo de cliente.
Clicar numa barra filtra o painel; clicar de novo limpa.
Nenhum bloco aparece em vermelho no app.
Página salva (Salvar) e Teste Online republicado (Testar agora).
Montar o relatório que o financeiro leva para a reunião: faturamento quebrado por cliente e por mês, com sub-totais, e exportado em PDF com o timbre da empresa.
O que você terá no fim
Página Faturamento por cliente (tipo Relatório) no módulo Relatórios.
Duas quebras: cliente no primeiro nível, mês de abertura no segundo.
Filtro de período por dt_abertura e filtros de técnico e de status.
Três colunas somadas — serviços, peças e total — com sub-total por grupo e total geral.
Uma segunda linha descritiva abaixo de cada OS, com número e problema.
Cabeçalho e rodapé do PDF montados no editor de bandas, com título, subtítulo, período, filtros e paginação.
Studio › + › Nova página › Relatório
Assistente no passo Configuração com Agrupar por (nível 1) em cliente_id, Coluna de cliente em nome_razao_social, nível 2 em dt_abertura com Granularidade Mês e Campo de período em dt_abertura
Studio › + › Nova página › Relatório
Mesmo passo rolado até Colunas totalizadas com valor_servicos, valor_pecas e valor_total marcados, Função de cada total em Soma e a Linha descritiva com {numero} — {descricao_problema}
Studio › Faturamento por cliente › Visual › Grid
Canvas do relatório ao lado do painel com a seção Agrupamento aberta — os dois níveis, Banda do grupo em Alinhada às colunas e o Rótulo do sub-total
Studio › Faturamento por cliente › Propriedades da página › Exportação PDF
Janela Cabeçalho e rodapé do PDF com o modelo Relatório financeiro já aplicado (logo, título, subtítulo, período, filtros e paginação na folha), os dois seletores em Personalizado e a coluna CAMPOS DO PROJETO ainda em Nenhum campo próprio no projeto
App › Faturamento por cliente › Exportar › PDF
1ª página do PDF de verdade gerado por Exportar → PDF, com Status Os em Concluída e só sete colunas (Numero, Cliente, Tecnico, Dt Conclusao e as três de valor): as quebras Cliente e Mês, a linha descritiva abaixo de cada OS e as faixas SUB-TOTAL DO CLIENTE com os valores alinhados sob as colunas, cabeçalho com o título do relatório e rodapé com nome da empresa, número de página e data
Passo a passo
Na tira do Studio, clique em + → Nova página e escolha Relatório (Agrupamentos, subtotais e export PDF/Excel). Como os outros tipos sem variação, o clique já leva ao passo Configuração.
No passo Configuração, escolha Base de dados e, em Tabela, ordem_servico.
Em Agrupar por (nível 1), escolha cliente_id. Como é uma chave estrangeira, o assistente abre um segundo combo, Coluna de cliente, com o hint agrupa pelo valor da tabela relacionada. Escolha nome_razao_social. Deixar em Usar o código (ID) faria o relatório quebrar pelo número interno do cliente.
Em Agrupar por (nível 2), escolha dt_abertura. Como é uma data, aparece Granularidade da data, com Dia já escolhido. Troque para Mês — é isso que junta todas as OS do mesmo mês num grupo só.
Deixe Agrupar por (nível 3) em Sem agrupamento. Dois níveis já dão a leitura que o financeiro precisa.
Em Campo de período, escolha dt_abertura. É o campo que vira o filtro de datas na tela e alimenta o {PERIOD} do cabeçalho do PDF. Sem ele, o hint fica Sem filtro de período.
Em Filtros de dimensão (hint FKs que viram combos de filtro), marque tecnico_id e status_os_id.
⚠️ Colunas totalizadas já vem marcada: o assistente pré-seleciona TODA coluna decimal da tabela (horas_trabalhadas, valor_servicos, valor_pecas, valor_deslocamento, valor_total). Desmarque o que não entra no faturamento e deixe só valor_servicos, valor_pecas e valor_total. Em Função de cada total, confirme Soma — é o padrão.
Deixe Saldo acumulado — crédito em Sem saldo acumulado. O saldo corrente serve a relatórios de caixa; este é de faturamento.
Em Linha descritiva (hint opcional — segunda linha abaixo de cada registro), clique no botãozinho Múltiplos atributos / máscara ao lado do combo, escreva {numero} — {descricao_problema} na caixa Máscara e clique em Aplicar (clicar nos atributos da lista de baixo também anexa cada um).
Preencha Nome do item de menu com Faturamento por cliente, Classe de controle com FaturamentoClienteMad, Route com /faturamento-cliente e Módulo com Relatórios. Clique em Criar página. ⚠️ Como nas aulas 13 e 14, o título da tela e o rótulo do menu nascem derivados da tabela (Relatório de ordem de serviços) — e aqui há um terceiro lugar: a migalha (Relatórios > Relatório de ordem de serviços). Ajuste em Propriedades da página (Título e Rótulo no menu) e, na Árvore, no nó page-header (Título e Breadcrumb).
Selecione a grade pela Árvore (nó grid). Abra a seção Agrupamento do painel.
Em Níveis de agrupamento, confira Nível 1 e Nível 2. Cada nível tem Agrupar por, Texto do cabeçalho (opcional) — a máscara da faixa do grupo — e, quando o campo é data, Separar datas por. Use Subir e Descer para trocar a ordem e Adicionar nível para incluir mais uma quebra.
Confirme Subtotais por grupo ligado. Em Banda do grupo, troque Linha única (padrão) por Alinhada às colunas: os valores do sub-total passam a sair embaixo da coluna correspondente, como num relatório impresso.
Em Rótulo do sub-total, escreva Sub-total do cliente. O campo aceita {group} para repetir o nome do grupo; vazio usa o padrão do tema.
Ainda em Agrupamento, confira Linha descritiva e preencha Título da exportação com Faturamento por cliente e Subtítulo da exportação com {PERIOD}. São eles que alimentam o {TITLE} e o {SUBTITLE} das bandas do PDF.
Na seção Paginação, coloque Itens por página em 0. Relatório agrupado não pagina: um grupo partido entre páginas não fecha o sub-total.
Abra a engrenagem Propriedades da página e desça até a seção Exportação PDF (ela só existe em listagem e relatorio); clique em Configurar cabeçalho e rodapé…. A janela Cabeçalho e rodapé do PDF abre com a folha ao centro, o CABEÇALHO em cima e o RODAPÉ embaixo, e dois seletores no topo: Cabeçalho do PDF e Rodapé do PDF, ambos em Padrão do projeto enquanto a página herdar.
Clique no cartão Modelos prontos (canto superior esquerdo, Comece com um layout pronto): abre a galeria com sete layouts — Corporativo clássico, Minimalista, Timbrado completo, Faixa azul, Navy executivo, Relatório financeiro e Confidencial, cada um com miniatura e o botão Usar este modelo. Escolha Relatório financeiro: ele traz título, subtítulo, período e filtros no cabeçalho e o total de registros no rodapé, e os dois seletores passam a Personalizado.
Ajuste o que faltar arrastando de ELEMENTOS: Texto, Logo / imagem, Nº de página, Data e Campo dinâmico. Em CAMPOS DISPONÍVEIS ficam os chips — {TITLE}, {SUBTITLE}, {DATE}, {PERIOD}, {FILTERS}, {TOTAL_REGISTER}, {APP_NAME}, {TENANT_NAME}, {UNIT_NAME} e {USER_NAME}.
Use Ímã e Grade para alinhar, e os campos altura, respiro e Fundo colorido para ajustar cada banda. As setas do teclado movem o elemento selecionado em passos finos.
Enquanto não houver campo próprio, a coluna CAMPOS DO PROJETO da janela de bandas fica em *Nenhum campo próprio no projeto* — é o estado do print desta aula. Para imprimir o CNPJ da empresa sem escrever código, abra Propriedades do projeto → Exportação de PDF → Campos próprios. Preencha Nome do campo com CNPJ e Valor com 00.000.000/0000-00 e clique em Adicionar. O campo vira um chip no editor de bandas. Repita com RAZAO_SOCIAL = Assistec Manutenção Exemplo Ltda.
Volte ao editor de bandas, arraste os chips {RAZAO_SOCIAL} e {CNPJ} para o cabeçalho e feche em Fechar. Precisa de um valor calculado para todas as telas? O botão Criar classe de campos, na mesma seção, gera a classe já com exemplos.
Clique em Salvar. No rail, abra Teste online e clique em Testar agora.
No app, abra Faturamento por cliente e deixe a leitura apresentável antes de mostrar (ou exportar):
em Status Os, escolha Concluída e clique em Atualizar — faturamento é o que fechou; o chip passa a marcar 1 filtro;
clique em Escolher colunas (o ícone de duas colunas, ao lado do de exportar) e deixe só Numero, Cliente, Tecnico, Dt Conclusao, Valor Servicos, Valor Pecas e Valor Total.
⚠️ Sem esse segundo passo o relatório parece quebrado. A grade do relatório é <mad-grid self>: ela nasce com todas as 25 colunas de ordem_servico, a tabela fica três vezes mais larga que a tela e as três colunas de valor caem para fora — as faixas SUB-TOTAL DO CLIENTE aparecem vazias mesmo com os números calculados corretamente.
Agora confira as quebras Cliente: … e Mês: …, a linha descritiva abaixo de cada OS e os sub-totais alinhados sob as colunas de valor. Exportar é o ícone de download no canto superior direito da tabela (o rótulo só aparece no tooltip), com as opções Excel e PDF.
Os dois guias da Ajuda aprofundam esta aula: Relatório analítico com saldo acumulado (quando o relatório precisa de saldo corrente) e Cabeçalho e rodapé do PDF (o editor de bandas por inteiro). A própria janela do PDF tem o botão Ajuda.
Erros comuns
O mesmo cliente aparece várias vezes, cada vez com o seu sub-total
a ordenação padrão da grade não começa pelos campos de grupo → em Ordenação, ordene primeiro por cliente e depois por data; sem isso o agrupamento se fragmenta.
Cada OS virou um grupo com o próprio sub-total
o nível de data ficou em Dia e a coluna guarda data e hora → abra o Nível 2 em Níveis de agrupamento e ponha Separar datas por = Mês.
O relatório quebra grupos no meio da página do PDF
Itens por página está diferente de 0 → coloque 0 na seção Paginação.
{PERIOD} e {FILTERS} saem em branco no PDF
ou a página não tem Campo de período, ou o app não foi republicado depois da mudança → preencha dt_abertura e use Testar agora no Teste online.
O {CNPJ} saiu literal no PDF
o campo próprio não foi cadastrado → Propriedades do projeto → Exportação de PDF → Campos próprios, e republique.
A linha descritiva não veio no Excel
é o comportamento correto: ela sai na tela e no PDF, e é omitida no Excel e no CSV para não quebrar tabelas dinâmicas.
As faixas de sub-total aparecem VAZIAS no app
as colunas de valor estão fora da tela. O relatório é <mad-grid self> e mostra as 25 colunas da tabela → use Escolher colunas e deixe só o que interessa (os números sempre estiveram lá; role a tabela para a direita e você os vê).
Escondi colunas e os números do cabeçalho do grupo (Cliente: …) ficaram fora da tabela
limitação conhecida: as faixas de total do grupo ficam ancoradas nas posições originais das colunas. As faixas de SUB-TOTAL DO CLIENTE alinham certo. Recarregar não resolve; para o PDF, exporte com todas as colunas.
Checklist de encerramento
A página Faturamento por cliente existe no módulo Relatórios e abre pelo menu do app.
O relatório quebra por cliente (pelo nome, não pelo código) e, dentro dele, por mês.
O filtro de período por dt_abertura aparece na tela, junto dos combos de técnico e status.
Serviços, peças e total somam por grupo e no total geral.
Cada OS mostra a segunda linha com número e descrição do problema.
Banda do grupo está em Alinhada às colunas e o sub-total tem rótulo próprio.
Itens por página está em 0.
A janela Cabeçalho e rodapé do PDF está com o modelo aplicado e os dois seletores em Personalizado.
Exportar oferece Excel e PDF. (O arquivo em si é a única coisa da aula que não aparece em print: abra o PDF e confira o timbre à mão antes de gravar.)
Página salva (Salvar) e Teste Online republicado (Testar agora).
Criar a tela em que o técnico lança várias horas de uma vez — digitando na grade ou colando direto do Excel — e o sistema grava tudo numa única operação.
O que você terá no fim
Página Apontamento de horas (tipo Planilha de lançamento) sobre os_apontamento.
Sete colunas configuradas, com tipo, obrigatoriedade e largura definidos por coluna.
Quinze linhas em branco ao abrir e rodapé de totais somando as horas.
Um lote colado do Excel gravado no app, com a validação reprovando o lote inteiro quando alguma linha está incompleta.
Studio › + › Nova página › Planilha de lançamento
Assistente no passo Configuração com os sete chips de Colunas da planilha marcados e Linhas iniciais em 15
Studio › Apontamento de horas › Colunas da planilha
Janela Colunas da planilha com as colunas na ordem da grade — OS e Técnico Obrigatórias em Combo, Horas em Número com Σ Soma e Valor/hora em Dinheiro (a sétima fica abaixo da dobra)
App › Operação › Apontamento de horas
Planilha no app logo depois do Ctrl+V, com as três linhas coladas, o contador 3 linha(s) preenchida(s) e o Σ somando as horas
App › Operação › Apontamento de horas
Tentativa de salvar sem preencher OS e Técnico: aviso 6 erro(s) de validação, as células obrigatórias destacadas em vermelho e nada gravado
Passo a passo
Na tira do Studio, clique em + → Nova página e escolha Planilha de lançamento. Como o Kanban e o Calendário, ela não tem variações: o clique no tipo já leva ao passo Configuração (o trilho da esquerda mostra Variação · Não aplicável, e o rodapé, Passo 3 de 3).
No passo Configuração, escolha Base de dados e, em Tabela, os_apontamento.
Em Colunas da planilha (hint clique para incluir/remover), deixe marcadas as sete colunas que a pessoa digita: ordem_servico_id, tecnico_id, dt_referencia, tipo, horas, valor_hora e descricao_atividade. A chave e as colunas de controle não são oferecidas — e aqui, ao contrário das Colunas totalizadas do relatório, a lista já vem toda marcada, então é só desmarcar o que sobra.
Troque Linhas iniciais (hint padrão 10) para 15.
Preencha Nome do item de menu com Apontamento de horas, Classe de controle com ApontamentoHorasMad, Route com /apontamento-horas e Módulo com Operação. Clique em Criar página.
No canvas, selecione a planilha. Em Fonte de dados, Database e Model aparecem travados, com a nota Definido na criação da página — não editável aqui. Para trocar, crie outra página.
Clique em Colunas da planilha. A janela abre com o aviso Ordem da lista = ordem visual da grade. Tipo, obrigatoriedade, totais e combos por coluna.
⚠️ Mexa nos Tipo ANTES dos rótulos. Trocar o tipo para Número ou Dinheiro faz nascer o campo Decimais na linha, e tudo que vem depois anda uma casa — quem preenche na ordem "linha a linha" acaba escrevendo o rótulo de uma linha dentro da Largura da anterior. Depois use Mover para cima e Mover para baixo (as setas ▲▼ à esquerda do número da linha) para deixar nesta ordem: ordem_servico_id, tecnico_id, dt_referencia, tipo, horas, valor_hora, descricao_atividade.
Tipo: Combo nas duas primeiras, Data em dt_referencia, Texto em tipo e descricao_atividade, Número em horas, Dinheiro em valor_hora.
Obrigatória (o botão alterna entre Opcional e Obrigatória) em ordem_servico_id, tecnico_id e dt_referencia — são as três que a validação vai cobrar. As demais ficam em Opcional.
Largura maior em descricao_atividade; Decimais2 em horas e valor_hora.
No campo Σ, escolha Soma para horas e para valor_hora.
Nas colunas de combo, clique em Abrir propriedades da coluna (o ícone de controles à direita da linha). Na seção Dados, configure o Datasource (tabela e coluna exibida) e, se a lista for grande, ligue Busca no servidor. Sem isso a coluna avisa configure a fonte do combo em Propriedades.
Feche a janela. Com a planilha selecionada, abra a seção Grade e confirme Linhas iniciais = 15, Máx. linhas por lote no padrão e Linha de totais (Σ) ligado.
Clique em Salvar. No rail, abra Teste online e clique em Testar agora.
No app, entre em Operação → Apontamento de horas. A grade abre com as 15 linhas em branco, um contador de linhas preenchidas no meio da barra e os botões +10 linhas, Desfazer, Validar e Salvar.
Copie este bloco de uma planilha (uma coluna por tabulação) e cole com Ctrl+V com o cursor na coluna Data da primeira linha — a colagem começa na célula onde o cursor está, não na linha de baixo:
05/09/2026 execucao 3,5 90,00 Diagnóstico da câmara fria
05/09/2026 execucao 2,0 90,00 Troca do termostato
06/09/2026 execucao 4,0 90,00 Recarga de gás e teste de estanqueidade
Confira o rodapé: o Σ passa a mostrar 9,50 em Horas e 270,00 em Valor/hora, e o contador vira 3 linha(s) preenchida(s). As três linhas coladas trouxeram data, tipo, horas, valor e atividade de uma vez; OS e Técnico continuam vazias.
Clique em Salvar antes de completar as linhas: nada é gravado e o app avisa 6 erro(s) de validação — corrija as células destacadas, com as células obrigatórias em vermelho (três linhas × duas colunas). É a gravação em lote se protegendo — ou entra tudo, ou não entra nada. Preencha OS e Técnico nas três linhas e salve de novo; agora as três entram juntas. (Validar faz a mesma conferência sem gravar.)
Erros comuns
A tela não aparece no menu e a URL responde 404 Not Found
a página não foi republicada depois de criada → Salvar e, no rail, Teste online → Testar agora. (Até setembro/2026 havia aqui um problema de plataforma: a Planilha de lançamento — como o Gantt, o Formulário de Gantt e o Organograma — nascia sem rota nenhuma, e o contorno era escrever um método público qualquer no bloco @mad-block:user:functions da aba PHP. Está corrigido: a página nasce com URL e no menu, sem contorno.)
A janela avisa Defina o model do <mad-sheet> antes de configurar as colunas.
a página foi criada sem tabela → crie outra página de Planilha de lançamento escolhendo os_apontamento em Tabela; a tabela não é editável depois.
A grade abre sem colunas, com Nenhuma coluna ainda — use "Adicionar coluna" abaixo.
nenhum chip foi marcado na criação → abra Colunas da planilha e use Adicionar coluna.
A coluna de combo abre vazia e mostra configure a fonte do combo em Propriedades
falta o Datasource → clique em Abrir propriedades da coluna, seção Dados, e aponte a tabela de origem.
Salvei e o sistema recusou o lote inteiro por causa de uma linha
é o comportamento correto: a gravação é tudo ou nada, para não deixar meio lançamento no banco → corrija a linha apontada e salve de novo.
O rodapé com a soma não aparece
Linha de totais (Σ) está desligado na seção Grade, ou nenhuma coluna tem Soma no campo Σ → ligue os dois.
Checklist de encerramento
A página Apontamento de horas existe no módulo Operação e abre pelo menu do app.
A grade abre com 15 linhas em branco e as sete colunas na ordem definida.
As colunas de OS e técnico abrem a lista de registros (combo configurado).
O rodapé Σ soma as horas enquanto a pessoa digita.
Um bloco colado do Excel entra de uma vez na grade.
Salvar com uma linha incompleta não grava nada e aponta a linha.
Página salva (Salvar) e Teste Online republicado (Testar agora).
Montar o cronograma das ordens de serviço — cada OS vira uma barra no tempo, com subtarefas e percentual concluído — e criar o formulário que abre quando você clica numa barra.
O que você terá no fim
Página Cronograma de OS (tipo Gantt, classe CronogramaOsMad) no módulo Operação, visível no menu do app.
Barras montadas a partir de dt_inicio e dt_fim, com hierarquia por os_pai_id.
Sidebar do cronograma com as colunas Tarefa, Início, Fim e Dias.
Página Tarefa do cronograma (tipo Formulário de Gantt, classe CronogramaOsMadForm) vinculada ao Gantt e fora do menu.
No app: clicar numa barra — ou no botão Nova — abre o formulário em gaveta.
Studio › + › Nova página › Gantt
Assistente do tipo Gantt no passo Configuração (Passo 3 de 3, trilho com Variação · Não aplicável), com titulo, dt_inicio, dt_fim e os_pai_id escolhidos, Coluna de progresso ainda em — Sem progresso — e o preview wireframe à direita
Studio › + › Nova página › Formulário de Gantt
Mesmo passo no tipo Formulário de Gantt — sem combo de tabela, só Gantt vinculado apontando Cronograma de OS e a classe CronogramaOsMadForm já sugerida
App › Operação › Cronograma de OS
Cronograma no app no zoom Ano: sidebar com Tarefa/Início/Fim/Dias, as barras das OS na linha do tempo e os botões Nova e Caminho crítico no cabeçalho
App › Tarefa do cronograma
Gaveta Tarefa do cronograma aberta em branco no app, com Identificação (Nome da tarefa), Cronograma (Início e Fim já preenchidos) e Hierarquia (Tarefa pai)
Passo a passo
Parte 1 — a página Gantt
No Explorer, clique em Nova página. No passo Tipo, grupo Operacional, escolha Gantt (descrição Cronograma de projeto — tarefas, fases e dependências).
O Gantt não tem variações: o clique no tipo já leva ao passo Configuração (o trilho da esquerda mostra Variação · Não aplicável, e o rodapé, Passo 3 de 3). Não há Continuar para clicar.
No passo Configuração, deixe Base de dados no banco do projeto e escolha Tabela = ordem_servico.
Preencha o mapeamento das colunas:
Coluna do nome da tarefa = titulo — é o texto escrito dentro da barra.
Coluna da data de início = dt_inicio — define onde a barra começa.
Coluna da data de fim = dt_fim — define a largura da barra. Enquanto o início estiver vazio, o campo fica com o aviso primeiro escolha o início.
Coluna pai (hierarquia) = os_pai_id. O hint explica: FK self-referente → tarefas/subtarefas. Opcional. Deixar em — Lista plana — dá um cronograma sem árvore.
Coluna de progresso = progresso. O hint é Numérica 0–100 (ou 0–1). Opcional.; a opção vazia é — Sem progresso —. ⚠️ Hoje esse combo não guarda a escolha: ele volta para — Sem progresso — por conta própria e o cronograma nasce sem a barra de preenchimento. O Gantt funciona sem ela (é opcional); para ter o preenchimento, aponte o campo Progresso depois, no modal Campos & colunas do canvas.
Preencha Nome do item de menu com Cronograma de OS, Classe de controle com CronogramaOsMad, Route com /cronograma-os e Módulo com Operação. Deixe Exibir no menu em Sim e clique em Criar página.
⚠️ Guarde o nome da classe. O cronograma gerado abre a gaveta de edição por um nome fixo, <classe do Gantt>Form — no nosso caso CronogramaOsMadForm. É esse nome que o formulário do passo 15 tem de ter.
No canvas, clique no cronograma para selecionar o <mad-gantt>. Na seção Fonte de dados, Database e Model aparecem travados, com a nota Definido na criação da página — não editável aqui. Para trocar, crie outra página.
Clique em Campos & colunas para abrir o modal Campos & colunas do Gantt. Cada linha é um campo que o Gantt entende:
Nome → texto da barra; Início e Fim → posição e largura;
Duração tem Origemcalculada de início/fim — não existe coluna no banco;
Progresso e Pai (hierarquia) ficam em Só mapeia (alimentam o desenho, mas não viram coluna da lateral). É aqui que você aponta Progresso para a coluna progresso, já que o assistente não guardou a escolha do passo 4 — opcional, o cronograma funciona sem a barra de preenchimento;
Responsável, Código, Marco (milestone), Fase, Cor e Tipo ficam desligados neste curso — ligue com Incluir quando o modelo tiver a coluna.
Para cada campo visível você ajusta Rótulo (cabeçalho) e Largura (px). A largura da lateral é a soma das colunas visíveis.
Clique em Dependências para abrir Dependências entre tarefas. Há três modos: Nenhuma (sem setas), Coluna FK (uma coluna da própria tarefa aponta o predecessor, sempre Finish→Start) e Tabela (uma tabela separada de arestas, com tipo e folga por linha). Nosso modelo tem só um auto-relacionamento, os_pai_id, e ele já é a hierarquia — então deixe em Nenhuma e feche. Setas entram quando você criar uma coluna os_predecessora_id ou uma tabela de dependências.
No grupo Tempo, escolha Zoom = Semana e deixe Locale em pt-br. Passo ao arrastar (min) em 1440 faz o arraste andar de dia em dia. ℹ️ Nota para quem grava: o print do app foi tirado no zoom Ano porque os dados de teste espalham dt_inicio/dt_fim por vários anos e, em Semana, quase nenhuma barra cai na janela visível. Com datas de verdade, Semana é o zoom certo.
No grupo Calendário, marque em Dias úteis de Seg a Sex e ajuste Horário de trabalho para o expediente da equipe. Fora dos dias úteis o cronograma não conta prazo.
No grupo Colunas & Visual, confira Mostrar fins de semana, Minimapa e Busca (⌘K) ligados, e escolha a Densidade.
Clique em Salvar.
Parte 2 — o Formulário de Gantt
Ainda no Explorer, clique em Nova página e escolha Formulário de Gantt (descrição CRUD de tarefa vinculado a um Gantt existente). Repare que este tipo não pede tabela: ele usa a tabela do Gantt.
No passo Configuração, abra Gantt vinculado e escolha Cronograma de OS. O hint conta quantos existem (1 Gantt disponível). Se você ainda não tivesse criado o Gantt, o combo viria vazio com o aviso nenhum Gantt no projeto — crie um Gantt primeiro — é por isso que a ordem desta aula é Gantt primeiro, formulário depois.
Preencha Nome do item de menu com Tarefa do cronograma. Em Classe de controle, não mude o que o assistente sugeriu: ao escolher o Gantt vinculado ele já escreveu CronogramaOsMadForm ali. Este nome importa — o Gantt do passo 5 abre a gaveta por <classe do Gantt>Form, e qualquer outro nome (por exemplo o OrdemServicoGanttForm derivado da tabela) deixa o clique na barra e o botão Nova abrindo uma classe que não existe. Deixe Exibir no menu em Não — o formulário é aberto pelo cronograma, não pelo menu. Clique em Criar página.
O formulário nasce com os campos que o Gantt usa, em três seções: Identificação (Nome da tarefa, titulo), Cronograma (Início, dt_inicio, e Fim, dt_fim — que já abrem preenchidos com hoje e hoje + 3 dias) e Hierarquia (Tarefa pai, os_pai_id, com a própria tarefa excluída da lista para não virar ciclo). Ajuste rótulos e larguras no canvas como em qualquer formulário e clique em Salvar.
No rail, abra Teste online e clique em Testar agora.
No app, entre em Operação → Cronograma de OS. Teste os três gestos:
arrastar uma barra muda dt_inicio/dt_fim;
clicar numa barra abre o formulário em gaveta;
o botão Nova, no cabeçalho do cronograma, abre o mesmo formulário em branco.
Erros comuns
O cronograma abre vazio, sem nenhuma barra
as OS têm dt_inicio ou dt_fim em branco → preencha as duas datas em pelo menos algumas OS (ou use os dados de teste da aula 03). Barra sem data de início não tem onde ser desenhada.
O combo Coluna da data de início não lista nada, com o aviso nenhuma coluna date/datetime
a tabela escolhida não tem coluna de data → confira se você selecionou ordem_servico.
Clico na barra (ou em Nova) e não abre nada
o formulário tem outro nome de classe. O Gantt chama <classe do Gantt>Form — com a classe CronogramaOsMad, ele abre CronogramaOsMadForm. Se você batizou a página de outro jeito, ou renomeie a Classe de controle do formulário, ou troque o nome nos métodos onTaskClick e onAddTask, na aba PHP do Gantt.
O tipo Formulário de Gantt aparece com crie um Gantt primeiro e não deixa continuar
você está criando o formulário antes do Gantt → crie a página Gantt, salve e volte.
A hierarquia não aparece: tudo sai em lista plana
Coluna pai (hierarquia) ficou em — Lista plana —, ou nenhuma OS tem os_pai_id preenchido → aponte os_pai_id e preencha o pai em pelo menos uma OS filha.
Arrasto a barra e a data volta ao lugar
a página não foi republicada → Salvar e depois Testar agora no Teste online.
Checklist de encerramento
A página Cronograma de OS existe no módulo Operação e abre pelo menu do app.
As barras respeitam dt_inicio e dt_fim.
A lateral mostra Tarefa, Início, Fim e Dias (esta última calculada, sem coluna no banco).
OS com os_pai_id preenchido aparece recuada sob a OS pai.
A página Tarefa do cronograma existe com a classe CronogramaOsMadForm, está vinculada ao Gantt e não aparece no menu.
Clicar numa barra abre o formulário; o botão Nova abre o formulário em branco.
Arrastar uma barra grava as novas datas (confirmado ao reabrir a OS).
Páginas salvas (Salvar) e Teste Online republicado (Testar agora).
Duas telas que mostram relação em vez de lista: o organograma desenha quem responde a quem na equipe técnica, e a timeline conta a história de uma OS na ordem em que aconteceu.
O que você terá no fim
Página Organograma da equipe (tipo Organograma) lendo tecnico pela FK gerente_id.
Cards com nome, cargo, foto (ou iniciais) e a capacidade diária como badge.
No app: navegar com pan e zoom, buscar uma pessoa e arrastar um card para trocar de gerente.
Página Histórico da OS (tipo Timeline) com o componente Timeline montado à mão.
Linha do tempo lendo os_historico, com um marcador por evento na ordem configurada.
Studio › + › Nova página › Organograma
Assistente do Organograma no passo Configuração (Passo 3 de 3, trilho com Variação · Não aplicável), com gerente_id, nome, cargo, foto e capacidade_hora_dia escolhidos
App › Cadastros › Organograma da equipe
Organograma no app: card do topo com o selo 3 e os três subordinados pendurados, os demais técnicos como raízes soltas ao lado, e a barra com busca, zoom e Resetar
Studio › Histórico da OS › Componentes
Paleta de componentes filtrada por Timeline, mostrando o grupo Interface com o único resultado, e a página ainda vazia ao lado
Studio › Histórico da OS › Visual › Timeline
Canvas com o mad-timeline dentro da página e a seção Fonte de dados do painel: Database assistec, Tabela (Model) os_historico, Chave, Ordenar por e os cinco campos da linha do tempo preenchidos — Campo Título titulo, Campo Corpo observacao, Campo Data dt_evento, Campo Ícone icone, Campo Cor cor
App › Operação › Histórico da OS
App no Teste Online com a linha do tempo do histórico da OS: dez eventos, um marcador por evento, cada um com título, corpo (observacao) e data formatada (dt_evento); o círculo do marcador mostra um ícone de chave inglesa de verdade (Provider Valor fixo nos Seeds de dev — ver Erros comuns), igual em todos os eventos
Passo a passo
Parte 1 — Organograma da equipe
No Explorer, clique em Nova página. No passo Tipo, grupo Visualização, escolha Organograma (descrição Árvore hierárquica visual — cards com pan/zoom, busca, lazy e re-parent por drag).
O Organograma não tem variações: o clique no tipo já leva ao passo Configuração (o trilho da esquerda mostra Variação · Não aplicável, e o rodapé, Passo 3 de 3). Não há Continuar para clicar.
No passo Configuração, escolha Tabela = tecnico e preencha:
FK do pai (auto-referência) = gerente_id — hint coluna que aponta pra esta mesma tabela (ex.: parent_id). Quem tem essa coluna vazia é raiz da árvore.
Campo do título do card = nome.
Subtítulo (opcional) = cargo — hint cargo/código exibido sob o título.
Campo da foto (opcional) = foto — hint URL/path da imagem — vazio usa iniciais.
Métrica (opcional) = capacidade_hora_dia — hint numérico exibido como badge no card. A opção vazia é — nenhum —.
Preencha Nome do item de menu com Organograma da equipe, Classe de controle com TecnicoOrgChart, Route com /organograma-equipe e Módulo com Cadastros. Deixe Exibir no menu em Sim e clique em Criar página.
No canvas, selecione o organograma. No painel, grupo Card, preencha Rótulo da métrica com h/dia — é o texto que acompanha o número no badge.
Ainda no painel, grupo Comportamento:
Click no card aponta o formulário de técnico da aula 04 (TecnicoForm::onEdit({id}));
ligue Drag re-parent — arrastar um card sobre outro troca o gerente. O servidor recusa movimentos que criariam ciclo (alguém virar chefe do próprio chefe);
Níveis no load inicial em 0 traz a árvore inteira; num quadro grande, 2 carrega dois níveis e busca o resto conforme você expande.
Clique em Salvar e, no rail, Teste online → Testar agora.
Defina quem está no topo — a árvore não se monta sozinha. O organograma lê gerente_id: raiz é quem está com o gerente VAZIO, e todo o resto pendura debaixo de alguém. Antes de abrir a tela, no cadastro de técnicos:
abra o coordenador (o primeiro da lista) e deixe Gerente em branco;
abra outros três técnicos e ponha, em Gerente, o id do coordenador.
⚠️ Gerente é uma caixinha de número, não um combo.tecnico.gerente_id é Intsem chave estrangeira no modelo, e o gerador escolhe o campo pelo tipo da coluna: sai um campo numérico, onde você digita o id (1), não o nome. Se preferir escolher pelo nome, volte ao Modelos de dados e transforme gerente_id em FK de tecnico para tecnico — aí o CRUD nasce com um DB Combo mostrando o nome.
Os dados de teste do Faker não preenchem gerente_id — sem esse passo você vê os técnicos lado a lado, numa fileira, sem nenhuma ligação.
No app, entre em Cadastros → Organograma da equipe. Carlos Andrade aparece no topo, com os três técnicos abaixo. Arraste a tela para navegar, use o zoom, busque por nome e arraste um card para trocar o gerente.
Parte 2 — Timeline da OS
No Explorer, clique em Nova página e escolha Timeline (grupo Visualização, descrição Linha do tempo de eventos). Repare: este tipo não pede tabela.
Preencha Nome do item de menu com Histórico da OS, Classe de controle com OsHistoricoTimeline, Route com /historico-os e Módulo com Operação. Clique em Criar página.
A página abre vazia, com apenas o cabeçalho. É assim mesmo: a Timeline é um componente, não um gerador — quem monta a tela é você.
Na busca da paleta de componentes (Buscar componente...), escreva Timeline: o grupo Interface fica com um único resultado. Arraste-o para dentro do conteúdo da página. Os grupos da paleta nascem recolhidos — por isso a busca é o caminho curto.
Com o componente selecionado, na seção Fonte de dados do painel (não é modal: os campos ficam ali mesmo):
Database = o banco do projeto (assistec) e Tabela (Model) = os_historico;
Chave = id;
Ordenar por = dt_evento, direção DESC — é ele que decide a ordem da linha do tempo;
Campo Título = titulo; Campo Corpo = observacao; Campo Data = dt_evento; Campo Ícone = icone; Campo Cor = cor;
Itens por página = 10. Limite total em branco (ou 0) traz tudo.
Os cinco Campo … ficam gravados como nome de coluna puro (o componente lê $registro->{$campo}) e chegam assim ao app publicado: título, corpo e data aparecem corretamente. (O painel não mostra mais um "Campo de exibição" separado aqui — ele não tinha efeito nenhum na Timeline e foi retirado da seção.)
Ícone e cor também chegam ao app pelo nome puro da coluna, mas só aparecem de verdade se o dado dessas colunas for um valor reconhecível: icone precisa do nome de um ícone do conjunto lucide (ex.: wrench, truck, check) e cor de uma cor CSS (ex.: blue, green, red) ou um hex. Os Seeds de dev (Faker) da aula 03 não sabem disso por padrão — sem escolher um Provider de lista fixa para essas duas colunas, icone/cor saem como texto aleatório (às vezes até uma URL de imagem) e o marcador aparece cinza, sem ícone (ver Erros comuns).
Escolha o nome da coluna de título com carinho: é ele que a pessoa vai ler. Se os dados de teste da aula 03 preencheram titulo com nomes de pessoa (o Faker infere "nome" para um varchar chamado assim), troque o provider dessa coluna em Seeds de dev (Faker) antes de gravar — senão a linha do tempo vira uma lista de nomes.
No grupo Comportamento, ligue Itens como cards, Agrupar por data e Carregar mais. Dois lados (zigzag) distribui os itens à esquerda e à direita do trilho — bom para históricos longos, opcional aqui.
No grupo Formato, escolha Formato data/hora = d/m/Y H:i.
Clique em Salvar e depois Testar agora no Teste online. No app, Operação → Histórico da OS mostra a linha do tempo com um marcador por evento.
Extra (opcional) — a mesma peça dentro de outra tela. Abra a consulta de OS da aula 12, arraste outra Timeline para uma aba Histórico e repita a configuração do passo
Depois abra Regras de carregamento: no modal, clique em Regra e monte
Colunaordem_servico_id · Operador= · Valor{id}. Clique em Aplicar. Assim a linha do tempo mostra só o histórico da OS aberta na tela.
Erros comuns
O organograma abre com todo mundo solto, lado a lado, sem ligação
FK do pai (auto-referência) ficou vazia, ou nenhum técnico tem gerente_id preenchido → aponte gerente_id e preencha o gerente de pelo menos três técnicos.
A tela abre e não aparece card nenhum
o contrário do anterior: ninguém está com o gerente vazio, então não há raiz por onde começar a árvore (é o que acontece quando um lote de dados de teste grava o mesmo gerente em todo mundo) → deixe o Gerente em branco em quem está no topo.
A tela do organograma some / fica em branco ao arrastar um card
o arraste foi recusado porque criaria ciclo (mover o chefe para dentro do próprio subordinado) → recarregue e mova o card para um nó que não esteja abaixo dele.
A timeline não aparece na página recém-criada
a página de Timeline nasce vazia de propósito → arraste o componente Timeline do grupo Interface da paleta.
A timeline aparece, mas sem nenhum item
faltou o Datasource, ou os_historico está vazia → configure a tabela e insira eventos (os dados de teste da aula 03 já trazem alguns).
Os itens saem fora de ordem
Ordenar por ficou no padrão (titulo) → escolha dt_evento e a direção DESC.
Os títulos são nomes de pessoa
o Faker inferiu "nome" para a coluna titulo nos seeds de dev → troque o provider dessa coluna em Seeds de dev (Faker) (aula 03) e rode Popular dados de teste de novo.
Marcadores cinza / ícone vazio
a coluna icone/cor dos dados de teste não tem nome de ícone/cor válido (o Faker às vezes grava até uma URL de imagem em icone) → em Inserir dados da tabela (clique no ícone de banco no card os_historico) → aba Seeds de dev (Faker), troque o Provider de icone e cor para Valor fixo e preencha um valor real em Parâmetros (ex.: wrench para icone, blue para cor). ⚠️ Hoje não existe Provider de LISTA (um valor sorteado por linha) — Valor fixo grava o MESMO valor em todas as linhas, então todos os marcadores saem iguais; é o jeito real de provar que o componente lê ícone/cor, não de variar por evento. Depois de salvar, é preciso reseedar a tabela — "Popular dados de teste" é aditivo e não regrava linhas que já existem; Recriar ambiente (reseta o banco de dados) (ou apagar as linhas antigas) e rodar Popular dados de teste de novo.
A timeline dentro da consulta mostra o histórico de TODAS as OS
faltou a regra de carregamento → abra Regras de carregamento e crie ordem_servico_id = {id}.
Checklist de encerramento
A página Organograma da equipe existe no módulo Cadastros e abre pelo menu do app.
A árvore mostra Carlos Andrade no topo e os três subordinados abaixo.
O card mostra cargo como subtítulo e a capacidade diária como badge.
Arrastar um card sobre outro troca o gerente (confirmado ao reabrir o cadastro).
A página Histórico da OS existe no módulo Operação.
A timeline está na página com Tabela (Model) = os_historico e os campos da linha do tempo preenchidos.
No app, a linha do tempo mostra um marcador por evento de os_historico.
Páginas salvas (Salvar) e Teste Online republicado (Testar agora).
Desenhar a ordem de serviço impressa — a folha que o técnico entrega e o cliente assina — e gerar o PDF direto do registro, sem escrever uma linha de código.
O que você terá no fim
Página Impressão da OS (tipo Documento) ligada a ordem_servico.
Folha A4 com cabeçalho e rodapé que se repetem em toda página.
Dados da OS escritos como texto comum, com chips de variável no meio.
Tabela de serviços vinda de os_servico_item, com total somado.
QR Code e linha de assinatura no rodapé.
PDF conferido pelo botão Testar PDF.
Studio › + › Nova página › Documento
Assistente do tipo Documento no passo Configuração (Passo 3 de 3, trilho com Variação · Não aplicável), com Tabela ordem_servico, os campos de menu preenchidos e Exibir no menu em Não
Studio › Impressão da OS › Visual
Editor de Documento recém-aberto: paleta Blocos do Documento à esquerda, folha A4 vazia no meio e Configurações da página à direita
Studio › Impressão da OS › Visual
Parágrafo em edição com o chip {numero} já no texto e o menu do $ aberto listando os atributos de ordem_servico
Studio › Impressão da OS › Visual › Tabela
Folha do documento com o bloco de Tabela de dados ainda sem detalhe (faixa TABELA · SEM DETALHE · 1 COL), a paleta Blocos do Documento à esquerda e o painel do bloco selecionado à direita
Studio › Impressão da OS › Testar PDF
O PDF de verdade (1ª página), gerado pelo botão Testar PDF com um registro de exemplo: título Ordem de Serviço, o parágrafo com o chip {numero} resolvido (OS Nº + valor de exemplo) e a tabela de dados ligada a os_servico_item com as colunas Serviço, Qtd, Preço e Subtotal já nomeadas e a faixa de totais no rodapé
Passo a passo
No Explorer, clique em Nova página. No passo Tipo, grupo Operacional, escolha Documento (descrição Ordem de serviço, NF, contrato).
O Documento não tem variações: o clique no tipo já leva ao passo Configuração (o trilho da esquerda mostra Variação · Não aplicável, e o rodapé, Passo 3 de 3). Não há Continuar para clicar.
No passo Configuração, escolha Tabela = ordem_servico. Preencha Nome do item de menu com Impressão da OS, Classe de controle com OrdemServicoDocument, Route com /impressao-os e Módulo com Operação. Deixe Exibir no menu em Não — o documento é chamado a partir da OS, não pelo menu. Clique em Criar página.
A página abre na aba Visual com o Editor de Documento: paleta Blocos do Documento à esquerda (grupos Conteúdo, Dados e Layout), a folha no meio e o painel à direita. A folha mostra três faixas: CABEÇALHO, o corpo e RODAPÉ, com a nota cabeçalho e rodapé se repetem no PDF.
A paleta diz Arraste ou clique para adicionar: além de arrastar, clicar no bloco já o insere na folha. Onde esta aula disser "arraste", o clique também serve.
Sem nenhum bloco selecionado, o painel da direita é Configurações da página. Ajuste: Tamanho do papelA4, OrientaçãoRetrato, Margens (mm) (Topo, Direita, Base, Esquerda), Fonte e Tamanho (pt). Em Cabeçalho defina Altura do cabeçalho (mm) e em Rodapé, Altura do rodapé (mm).
Opcional: clique em Templates para abrir a Galeria de templates (Escolha um modelo — você pode customizar tudo depois.). Aplicar um modelo pede confirmação (Substituir o documento atual?) porque troca todos os blocos — o Ctrl+Z desfaz.
Cabeçalho. Arraste Imagem (grupo Conteúdo) para a banda CABEÇALHO e preencha URL / data URI com a logo; ajuste Largura (mm) e Alinhamento. Ao lado, arraste um Título e escreva Assistec Manutenção Exemplo Ltda.
Identificação da OS. No corpo, arraste um Parágrafo. Escreva o texto fixo e, onde entra um dado da OS, digite $: um menu abre com os atributos de ordem_servico. Escolha e o valor vira um chip dentro do texto. Monte, por exemplo:
OS {numero} · Aberta em {dt_abertura}
Cliente: {cliente->nome_razao_social}
Equipamento: {equipamento->nome}
Técnico: {tecnico->nome}
O que está entre chaves não é digitado: é o chip escolhido na lista do $. A lista abre com as colunas de ordem_servico (id, numero, titulo, cliente_id, equipamento_id, tecnico_id, tipo, prioridade, descricao_problema…) e rola para o resto. ℹ️ Nota para quem grava: confirme na sua lista se as colunas das tabelas relacionadas (cliente->nome_razao_social, tecnico->nome) aparecem; se não aparecerem, imprima o que existe na própria OS e deixe a chain fora da folha.
Formato dos valores. Clique num chip: o painel mostra Formatador. Escolha o formato de data para dt_abertura e o de moeda para valores. O Formatador não é um <select>: é uma lista agrupada com busca — Texto, Moeda, Número, Data e Hora, Documento BR e Personalizado. A aula 23 cria dois formatadores próprios e eles aparecem em Personalizado, no fim da lista.
Problema relatado. Arraste outro Parágrafo e insira o chip {descricao_problema}. Texto longo quebra sozinho na folha.
Tabela de serviços. Arraste Tabela de dados (grupo Dados). Ela entra na folha ainda vazia, com a faixa TABELA · SEM DETALHE · 1 COL — é o estado normal de quem acabou de inserir. Com o bloco selecionado, no painel:
aponte Tabela detalhe = os_servico_item (só entram tabelas que apontam para ordem_servico, marcadas com via FK);
escolha as colunas servico_id, quantidade, preco_unitario e subtotal. Alinhamento e formato já vêm sugeridos por tipo de coluna.
O botão Assistente de colunas faz os dois passos de uma vez (escolhe a tabela detalhe — se ainda não escolhida — e as colunas), com os atalhos Todas (N), Nenhuma, Não-PK e Obrigatórias na seleção. ⚠️ Prefira o assistente para LIGAR o conteúdo da coluna; o editor livre de conteúdo (o ícone ao lado do campo, que abre uma caixa de texto com {atributo}) é ótimo para trocar só o rótulo depois, mas usá-lo para configurar o conteúdo do zero de uma tabela de dados pode deixar a célula em branco no PDF gerado por Testar PDF — vale conferir o PDF depois de montar a tabela por esse caminho.
Ainda no painel da tabela, ajuste por coluna: Rótulo da coluna, Largura, Alinhamento e Formato. Na coluna subtotal, ligue Totalizador = Soma. Em Estilo, ligue Zebra e ajuste Fonte (pt) e Cor do cabeçalho.
Peças. Repita o passo 11 com outra Tabela de dados apontando os_peca_item.
Totais. Arraste um Parágrafo alinhado à direita e insira os chips {valor_servicos}, {valor_pecas} e {valor_total}, cada um com Formatador de moeda.
Rodapé. Na banda RODAPÉ:
arraste QR Code e preencha Dados (URL ou texto) com um chip do número da OS; ajuste Tamanho (mm) e Correção de erro;
arraste Assinatura e preencha Nome e Cargo / papel (ex.: Cliente), ajustando Largura da linha (mm);
arraste Número de página (grupo Layout) — o formato padrão é Página {page} de {total}.
Clique em Salvar. ⚠️ Salve antes de trocar de aba: bloco inserido e não salvo some — o editor não guarda rascunho.
Clique em Testar PDF. O documento é gerado com um registro real da tabela e abre numa aba nova do navegador, no visualizador de PDF. O botão Preview, ao lado, mostra a renderização dentro do próprio editor.
Extra (opcional). Para chamar o documento a partir da OS, volte ao formulário da aula 09 e aponte um botão para esta página. Na aula 23 esse caminho é feito pelo Após salvar, que manda a OS recém-gravada direto para Impressão da OS · show.
Erros comuns
Vincule o documento a uma tabela primeiro.
a página foi criada sem tabela → o tipo Documento exige tabela; crie outra página apontando ordem_servico.
O $ não abre nada no parágrafo
o cursor não está num bloco de texto rico (Título ou Parágrafo), ou a página ainda não terminou de carregar → clique dentro do texto e tente de novo.
O chip aparece com chaves ({numero}) no PDF, não com o valor
Testar PDF SEMPRE usa um registro de EXEMPLO gerado (não é um dado real do seu banco); se ele mostra o rótulo cru em vez do valor, é sinal de que o preview não achou nenhum registro de exemplo — normalmente passa sozinho. Não adianta procurar esse número no app: o valor que aparece é fictício.
A tabela sai com a faixa SEM DETALHE · 1 COL e uma coluna vazia
é como o bloco nasce → aponte a Tabela detalhe e escolha as colunas no painel do bloco.
A tabela de serviços sai com as colunas certas, mas todas as células em branco no PDF
a coluna foi ligada pelo editor livre de conteúdo (ícone ao lado do campo) em vez do Assistente de colunas — desfaça e escolha a coluna de novo pelo assistente. Linhas de preview só muda o que o editor mostra, não o PDF.
Os blocos que acabei de inserir sumiram
a página foi trocada de aba sem salvar → o editor de documento não guarda rascunho; clique em Salvar antes de sair.
O total não aparece embaixo da tabela
faltou ligar Totalizador na coluna → abra a coluna subtotal e escolha Soma.
Salve antes de testar o PDF.
você clicou em Testar PDF com alterações pendentes → clique em Salvar e repita.
Cabeçalho e rodapé somem no PDF
Altura do cabeçalho (mm) ou Altura do rodapé (mm) está em zero → defina uma altura em Configurações da página.
Checklist de encerramento
A página Impressão da OS existe, é do tipo Documento e está ligada a ordem_servico.
O papel está em A4, com margens e alturas de cabeçalho/rodapé definidas.
O cabeçalho traz logo e nome da empresa e se repete em todas as páginas do PDF.
Os dados da OS aparecem como chips escolhidos pelo $, não como texto fixo.
Datas e valores têm Formatador aplicado.
A tabela de serviços aponta Tabela detalhe = os_servico_item e soma subtotal.
O rodapé tem QR Code, linha de assinatura e número de página.
Documento salvo (Salvar) e Testar PDF abrindo o arquivo numa aba nova, sem erro.
Montar o balcão da assistência: o atendente bipa a peça, o carrinho soma, o cliente paga em mais de uma forma e a venda fica gravada em tabelas do seu próprio projeto.
O que você terá no fim
Página Balcão de peças (tipo PDV / Frente de caixa) no módulo Operação.
Catálogo lendo peca: bip por EAN, busca por SKU e por nome, preço e saldo.
Três tabelas novas criadas pelo assistente — venda, venda_item e venda_pagamento — fechando o modelo em 17 tabelas.
Formas de pagamento com tecla de atalho e troco só no dinheiro.
Teto de desconto, pergunta de CPF/CNPJ e cupom não-fiscal pelo navegador.
Studio › Explorer › Nova página › PDV / Frente de caixa
Assistente de página nova no tipo PDV, seção Produto preenchida com peca/nome/ean/sku e a seção Preço logo abaixo
Studio › Explorer › Nova página › PDV / Frente de caixa
Mesmo assistente na seção Venda, itens e pagamentos (3 tabelas), com Criar pra mim (recomendado) selecionado
Studio › Explorer › Nova página › PDV / Frente de caixa
Aviso Serão criadas no seu banco: com venda, venda_item e venda_pagamento listadas
Studio › Modelos de dados › Ordem de Serviço
Diagrama do modelo com as três tabelas novas (venda, venda_item, venda_pagamento): venda e venda_pagamento com FK 1, venda_item com FK 2 — venda_id para venda e produto_id para peca
App › Operação › Balcão de peças
App no Teste Online com o caixa aberto: barra de bipe no topo, carrinho com as três peças (a primeira com o aviso Estoque insuficiente) e o Total R$ 135,00 em destaque
App › Operação › Balcão de peças › Pagamento
Coluna de pagamento do caixa: Total R$ 83,00, Pix R$ 40,00 lançado antes do Dinheiro R$ 43,00, Pago R$ 83,00 e Troco R$ 7,00 em verde, com Finalizar (F10) abaixo
Passo a passo
No Explorer, clique em Nova página. No passo Tipo, grupo Dados, escolha PDV / Frente de caixa (descrição Caixa completo: bipe produtos, carrinho, multi-pagamento com troco e venda gravada nas suas tabelas).
O PDV não tem passo Variação.Clicar o card salta direto para Configurar a página (Passo 3 de 3); o trilho da esquerda mostra Variação · Não aplicável. É o mesmo comportamento do Kanban.
Seção Produto. Em Tabela de PRODUTOS (catálogo) escolha peca — o hint lembra O que o caixa bipa. As tabelas de venda/itens/pagamentos são escolhidas mais abaixo. Depois:
Coluna do nome = nome (Nome exibido do produto);
Coluna do EAN = ean (Código de barras — bip exato. Informe EAN e/ou código);
Coluna do código = sku (Código interno/SKU (2ª prioridade do bip)).
Seção Preço. Deixe Onde mora o preço em Na tabela do produto e Coluna do preço = preco_venda. A opção Em outra tabela existe para ERPs com tabela de preço por vigência ou por lista — não é o nosso caso.
Seção Estoque.Onde mora o estoque tem três opções — Sem controle, Na tabela do produto e Em outra tabela. Escolha Na tabela do produto, Coluna do estoque = estoque_atual e Comportamento do estoque = Avisar (permite negativo).
Comportamento do estoquesó aparece quando existe estoque, e só oferece Avisar (permite negativo) e Bloquear além do saldo (aí o servidor recusa a venda). "Não controlar" não mora aqui: é a opção Sem controle de Onde mora o estoque — escolhida ela, o combo de comportamento some da tela.
Seção Venda, itens e pagamentos (3 tabelas). O campo se chama Tabelas de venda e o hint explica: O PDV grava venda, itens e pagamentos em 3 tabelas do SEU projeto. Escolha Criar pra mim (recomendado) — logo abaixo aparece a tira Serão criadas no seu banco: com os três chips venda, venda_item e venda_pagamento. Se o seu banco já tiver essas tabelas, o assistente recusa com Já existem tabelas ... neste banco — use "Mapear existentes" ou renomeie-as; a opção Mapear existentes é para quem já tem a estrutura pronta e aponta Tabela da venda, Tabela dos itens e Tabela dos pagamentos na mão.
Em versões anterioreso Criar pra mim criava as três tabelas e parava na chave estrangeira do item (FK produto_id → Peca.id … 'Peca' not found in diagram). Se você vir essa mensagem, as tabelas já existem: volte ao mesmo campo, troque para Mapear existentes, aponte as três e ligue venda_item.produto_id em peca.id à mão no Modelos de dados. Hoje o assistente termina sozinho, com a FK incluída.
Seção Cliente (opcional). Escolha Tabela de clientes (opcional) = cliente e deixe Cliente na venda em Opcional. Deixar a tabela vazia dá um PDV sem cliente.
Seção Pagamentos & comportamento.
Formas de pagamento: deixe as quatro padrão (dinheiro, débito, crédito e Pix); o hint avisa Reordene/adicione depois no painel da página.
Perguntar CPF/CNPJ na venda = Sim.
Teto de desconto (%) = 10 (Vazio = sem teto. Enforçado no servidor).
Desconto — forma de digitar = Ambos (toggle R$/%).
Preencha Nome do item de menu com Balcão de peças, Classe de controle com PecaPdv, Route com /balcao-pecas e Módulo com Operação. Deixe Exibir no menu em Sim e clique em Criar página.
Abra Modelos de dados no rail. As três tabelas novas estão no diagrama:
venda — data/hora, cliente, operador, status, subtotal, desconto, total, documento, troco e uma coluna client_uuid única que impede a mesma venda de ser gravada duas vezes;
venda_item — duas FKs (FK 2 no rodapé do card): uma para venda e outra de produto_id para peca. Além delas, quantidade, preço unitário, desconto e total;
venda_pagamento — FK para venda, forma, valor e valor recebido.
O modelo do curso fecha em 17 tabelas.
Volte à página e selecione o <mad-pdv> no canvas. No painel, grupo Pagamentos, clique em Formas de pagamento para abrir Formas de pagamento do PDV. Para cada forma há Código, Rótulo, Troco (Dá troco / Sem troco) e Tecla. Deixe Dá troco só no dinheiro. A ordem da lista é a ordem dos botões no caixa.
Ainda no painel, grupo Carrinho, clique em Colunas do carrinho se quiser uma coluna extra — por exemplo, o número de série da peça, digitado item a item. Sem nenhuma, o carrinho usa o layout padrão.
Grupo Cupom: Impressão = Navegador (cupom não-fiscal), Largura (mm) = 80, Cabeçalho = Assistec Manutenção Exemplo Ltda, Rodapé = Obrigado pela preferência!. Ligue Imprimir ao finalizar se o balcão tiver impressora.
Grupo Comportamento: confira Desconto, Teto de desconto (%) e Perguntar CPF/CNPJ. Segurar/retomar venda (tecla F8) deixa o atendente pausar uma venda para atender outro cliente; Máx. em espera limita quantas ficam paradas. Quantidade fracionada só é necessária para peça vendida por metro — o nosso PC-0005 Cabo flexível 2,5 mm (m) é exatamente esse caso, então ligue.
Grupo Aparência: Título = Balcão de peças, Símbolo da moeda = R$, Fotos dos produtos ligado.
Clique em Salvar e, no rail, Teste online → Testar agora.
No app, entre em Operação → Balcão de peças e monte uma venda:
digite o EAN de PC-0001 Filtro de ar split 9.000 BTU — o item entra direto no carrinho;
digite PC-0003 (o SKU) — o código interno é a segunda prioridade da busca;
digite capacitor — busca por nome, com escolha na lista;
aplique um desconto acima de 10 % e confirme que o servidor recusa;
pague em duas formas, lançando o dinheiro por último: PixR$ 40,00 e depois DinheiroR$ 43,00 sobre um total de R$ 83,00 — a linha do dinheiro mostra Troco R$ 7,00 e o rodapé fecha com Pago e Troco;
clique em Finalizar (F10): o caixa pergunta o CPF/CNPJ (Informar CPF/CNPJ? (Enter pula)), você confirma em Confirmar venda e a tela responde Venda concluída.
Abra o cadastro de peças e confirme que estoque_atual baixou.
Erros comuns
Falha ao criar as tabelas de venda: venda_item: FK produto_id → Peca.id … not found
plataforma desatualizada: o Criar pra mim mandava o nome do model onde o modelo espera o nome da tabela. As três tabelas já foram criadas → troque para Mapear existentes, aponte venda, venda_item e venda_pagamento, crie a página e ligue venda_item.produto_id em peca.id no Modelos de dados. Na versão atual o assistente termina sozinho, com a FK.
O assistente recusa com Já existem tabelas venda, venda_item, venda_pagamento neste banco
o assistente não sobrescreve tabela existente → use Mapear existentes e aponte as três, ou renomeie as tabelas antigas.
Bipo o código, o texto some e o carrinho continua vazio
a peça está com o Ativo desmarcado. O PDV nasce com a coluna ativo em Coluna de ativo, e produto inativo não é achado nem pelo EAN, nem pelo SKU, nem pelo nome — e a barra não mostra aviso nenhum → abra o cadastro de peças e marque Ativo.
A linha entra no carrinho com o aviso Estoque insuficiente em amarelo
é o Comportamento do estoque em Avisar (permite negativo) funcionando: a venda passa, o saldo fica negativo. Para barrar, use Bloquear além do saldo.
Bipar o código de barras não acha nada
Coluna do EAN ficou vazia, ou o EAN cadastrado tem espaço/hífen que o leitor não envia → preencha ean e cadastre o código exatamente como o leitor manda.
O caixa vende peça sem saldo
Comportamento do estoque está em Avisar (permite negativo) → escolha Bloquear além do saldo se o balcão não pode vender a descoberto (são as duas únicas opções).
O desconto passa do teto
Teto de desconto (%) ficou vazio → preencha; a regra é aplicada no servidor, não só na tela.
Nenhuma forma de pagamento dá troco
Troco está em Sem troco em todas → abra Formas de pagamento do PDV e ligue Dá troco no dinheiro.
Paguei em dinheiro a mais e não apareceu troco
o troco só nasce quando a forma que dá troco é a ÚLTIMA lançada e o valor digitado passa do que ainda falta. Lançar o dinheiro primeiro e o Pix depois fecha a conta sem troco nenhum: comece pelas formas sem troco e deixe o dinheiro por último.
A venda finaliza mas não aparece nas tabelas
a página não foi republicada depois de criar as tabelas → Salvar e Testar agora no Teste online; se as tabelas nasceram depois do último deploy, use também o sincronismo de estrutura da aula 06.
Checklist de encerramento
A página Balcão de peças existe no módulo Operação e abre pelo menu do app.
venda, venda_item e venda_pagamento aparecem no diagrama (o modelo fecha em 17 tabelas), venda.cliente_id ligada a cliente, venda_item.venda_id ligada a venda e venda_item.produto_id ligada a peca — o card de venda_item mostra FK 2.
As peças que o caixa vai bipar estão com Ativo marcado.
Bipar o EAN, digitar o SKU e buscar pelo nome acham a peça.
O desconto acima de 10 % é recusado.
Dá para pagar em duas formas na mesma venda, com troco só no dinheiro (lançado por último).
Finalizar (F10) fecha a venda com Venda concluída e o estoque_atual da peça baixou.
O grupo Cupom está configurado com Navegador (cupom não-fiscal), 80 mm e o cabeçalho da empresa.
Página salva (Salvar) e Teste Online republicado (Testar agora).
Fazer com que uma OS acima de um certo valor pare e espere aprovação: o gerente decide, orçamentos altos sobem para a diretoria, e cada pessoa vê o que falta decidir numa fila própria.
O que você terá no fim
O perfil Diretoria conferido em Perfis de acesso (ele foi criado na aula 05), com o Code que a plataforma gera do nome.
Página Aprovações (tipo Fila de Aprovação) no módulo Operação.
Fluxo Aprovação de orçamento de OS sobre ordem_servico, gravando em status_aprovacao, publicado v1.
Nível 1 com o perfil Gerente; nível 2 com Diretoria e a condição valor_total > 5000.
Simulação rodada antes de publicar, com a trilha dos dois níveis.
O botão Enviar para aprovação no formulário da OS, colocado pelo Aplicar aos formulários do próprio painel — sem escrever uma linha.
No app: as pendências na fila, com Abrir e Delegar.
Studio › Explorer › Nova página › Fila de Aprovação
Assistente Criar nova página Mad no tipo Fila de Aprovação, passo Configuração (Passo 3 de 3, trilho com Variação · Não aplicável) sem campo de tabela, com menu, classe, rota e módulo preenchidos e o RESUMO mostrando Tabela —
Studio › Fluxos de Aprovação › Aprovação de orçamento de OS
Canvas do fluxo com Início → Aprovação do gerente → Aprovação da diretoria (o funil marca a condição) → Aprovado, a paleta NÓS à esquerda e o selo Sem problemas
Studio › Fluxos de Aprovação › Simular
Painel Simulação com a Trilha dos dois níveis percorridos e o selo Fluxo encerrado: approved, o canvas atrás com os nós acesos
Studio › Fluxos de Aprovação
Painel em modo Lista depois de Publicar: o cabeçalho mostra publicado v1, o nível 1 com Gerente e o botão Condição, e o nível 2 com Diretoria e Condição ativa. O print foi feito antes do diálogo Aplicar aos formulários existir, então ele não aparece aqui — na sua tela o publish pergunta primeiro, e o botão Aplicar aos formulários fica no cabeçalho, ao lado de Publicar
App › Operação › Aprovações
App no Teste Online na página Aprovações, com as pendências listadas (Cod., Registro, Etapa, Fluxo, Prazo vazio e Criada em) e os ícones Abrir e Delegar na primeira coluna. A pendência deste print veio de um botão colado à mão no Blade — hoje quem coloca o botão é o Aplicar aos formulários, e a fila fica igual
Passo a passo
Parte 1 — perfis e fila (antes do fluxo)
No rail, abra Perfis de acesso e confira que existem os quatro perfis da aula 05 — Atendente, Técnico, Gerente e Diretoria. O nível 2 do fluxo precisa do Diretoria; se ele não estiver aí, crie agora.
Confira o campo Code de Gerente e Diretoria (gerente e diretoria). Ele fica ao lado de Nome do perfil e é gerado do nome ao salvar — é por esse código que a pendência procura a pessoa, e é o mesmo código que o fluxo grava ao publicar. Só mexa nele se quiser um código diferente do nome; nesse caso, o perfil e o fluxo têm que usar o mesmo.
No Explorer, clique em Nova página. No passo Tipo, escolha Fila de Aprovação (descrição Pendências de fluxos — aprovar, devolver, rejeitar). A Fila de Aprovação não tem variações: o clique no tipo já leva ao passo Configuração (o trilho da esquerda mostra Variação · Não aplicável, e o rodapé, Passo 3 de 3).
No passo Configuraçãonão há campo de tabela — a fila lista pendências de qualquer fluxo, e o RESUMO da lateral mostra Tabela —. Preencha Nome do item de menu com Aprovações, Classe de controle com FilaAprovacao, Route com /aprovacoes e Módulo com Operação. Deixe Exibir no menu em Sim e clique em Criar página.
Por que a fila vem primeiro. Publicar o fluxo sem a fila publica verde e trava o registro em aprovação: ninguém tem onde aprovar. O próprio painel avisa isso ao publicar, com Fluxo publicado — mas ainda falta tela. Crie UMA por projeto — ela lista os fluxos todos.
Parte 2 — montar o fluxo (modo Lista)
No rail, abra Fluxos de Aprovação e clique em + Novo (o botão fica no topo da coluna Fluxos). Dê o nome Aprovação de orçamento de OS (o campo sugere Ex: Aprovação de Compra).
Logo abaixo do nome há um combo sem rótulo, cujo texto é Tabela alvo — escolha ordem_servico. Sem ele o botão Adicionar fica apagado. O cabeçalho do painel passa a mostrar Tabela alvo: ordem_servico · nunca publicado.
Escolha Coluna de status = status_aprovacao (hint *recebe aprovado/rejeitado/devolvido no registro*). A opção Não gravar status existe para fluxos que não marcam o registro — não é o nosso caso.
O painel tem dois modos: Lista e Canvas, e os dois montam o mesmo fluxo. Em Lista, cada nível é uma linha com três campos: Nome do nível, Perfil aprovador e o botão Condição. Clique + Nível e preencha:
nível 1 — Aprovação do gerente, perfil Gerente, sem condição;
nível 2 — Aprovação da diretoria, perfil Diretoria, com condição (próximo passo).
As setas ↑ ↓ reordenam e a lixeira remove. O rodapé resume a regra: *Aprovado avança pro próximo nível; rejeitado/devolvido encerram o fluxo. Nível com condição não satisfeita é pulado.*
No nível 2, clique em Condição. Abre Condição do nível "Aprovação da diretoria" — quando este nível se aplica?, o mesmo construtor de filtros do resto da plataforma. Clique Regra e preencha Coluna = valor_total (double), Operador = >, Valor = 5000. Confira o Preview do PHP gerado: tem que sair fn($q) => $q->where('valor_total', '>', 5000). Clique Aplicar — o botão passa a mostrar Condição ativa (com um ✕ para limpar). ⚠️ Logo abaixo de Valor existe Aplicar função na coluna (use {$c} para a coluna). Número digitado ali vira whereRaw('5000 = ?', ['']) — regra silenciosamente errada.
Clique Salvar. Agora abra o Canvas: como o fluxo ainda não tem desenho próprio, ele é gerado a partir dos níveis — Início → Aprovação do gerente → Aprovação da diretoria → Aprovado, com as saídas aprova, devolve e rejeita em cada passo e o funil da condição no nível 2. Organizar arruma as posições; o contador mostra Sem problemas. A paleta NÓS (Passo humano · Condição (gateway) · Ação automática · Paralelo · Junção · Notificação · Fim do fluxo) e o Salvar desenho são para quando você quiser um desenho próprio, com responsáveis múltiplos, botões de saída customizados, lente de campos e prazo (SLA) — recursos do nó Passo humano, que o modo Lista não expõe. Gerar com IA desenha um rascunho a partir de uma descrição em português, e Abrir no Studio leva o mesmo canvas para uma aba própria.
Parte 3 — simular, publicar
Clique em Simular. O painel SIMULAÇÃO abre à direita com Registro de exemplo — uma linha por coluna da tabela alvo (*As condições avaliam estes valores de verdade*). Preencha valor_total com 7200 e clique Iniciar simulação.
A simulação para em cada nível e mostra os botões reais: Aprovar, Rejeitar, Devolver. Clique Aprovar no gerente — o nó da diretoria acende, porque 7200 > 5000 — e Aprovar de novo. A Trilha fica assim: ✓ Aprovação do gerente: Aprovar · ✓ Aprovação da diretoria: Aprovar · ◉ Aprovado (approved), e no topo aparece Fluxo encerrado: approved. Reiniciar volta ao formulário; repita com valor_total = 1200 e a diretoria sai como pulado por condição. Condição que depende de sessão ou subconsulta abre uma pergunta (Sim / Não) em vez de adivinhar.
Clique Salvar e depois Publicar. O cabeçalho passa de nunca publicado para publicado v1. Publicar valida o desenho, compila e congela a versão: instâncias que já estão andando continuam na versão delas.
Parte 4 — o botão no formulário da OS
O publish já resolve isso. O botão Enviar para aprovação é escrito no formulário na hora em que ele é criado, e só quando já existe fluxo publicado sobre a tabela — o formulário da aula 09 é mais velho que o fluxo, então nasceu sem o botão. Logo depois do Publicar aparece a pergunta Fluxo publicado — os formulários antigos não têm o botão, com a lista das páginas nessa situação (aqui, OrdemServicoForm). Clique em Aplicar aos formulários. Agora não fecha sem mexer em nada.
O resultado vem numa confirmação: Botão aplicado aos formulários, com uma linha por página e o que entrou nela — • OrdemServicoForm (método + botão). Se aparecer Nenhum formulário foi alterado / *Os formulários já estavam em dia*, é porque o botão já existe: rodar de novo não duplica nada. O que a plataforma acrescenta é exatamente o que o gerador escreveria: a linha <mad-btn mad:click="onSubmitApproval" variant="outline" icon="send">Enviar para aprovação</mad-btn> dentro de <mad-form-actions> e o método onSubmitApproval() num bloco próprio (@mad-block:workflow:submit), que chama WorkflowEngine::start('aprovacao_de_orcamento_de_os', …). O resto do seu código não é tocado.
Fechou a pergunta sem querer? O mesmo conserto está no cabeçalho do painel: com o fluxo publicado, aparece o botão Aplicar aos formulários ao lado de Publicar (a dica diz *"Pode rodar de novo à vontade: não duplica nada e não mexe no resto do seu código"*).
Duas situações em que uma página é pulada — a própria lista diz qual e por quê:
*Em edição por …* — alguém (ou você, em outra aba) está com a página aberta e travada. Feche e rode de novo;
*O Blade não tem <mad-form-actions>* — o método entra, mas o botão não tem onde ficar. Nesse caso, abra a aba Blade View e cole a linha do botão onde ficam os botões da tela.
Se você ainda vai criar o formulário da tabela, publique o fluxo ANTES: aí o botão, o método, o modo tarefa (?wf_task=N) e o modal Acompanhar aprovação vêm prontos, sem precisar aplicar nada.
Se a sua versão não tem o diálogo.Em projetos publicados antes de setembro de 2026 o publish avisava e parava ali — não havia Aplicar aos formulários. O caminho manual era: abrir OrdemServicoForm → Blade View → colar <mad-btn mad:click="onSubmitApproval" variant="outline" icon="send">Enviar para aprovação</mad-btn> dentro de <mad-form-actions> → Salvar (o editor cria sozinho o stub do método a partir do mad:click) → e só então escrever o corpo do método no PHP. Inserir o método antes de salvar o Blade não funcionava: o save do Blade regenerava o PHP e descartava.
Parte 5 — testar no app
No rail, abra Teste online e clique em Testar agora.
No app, abra Admin › Usuários, edite Administrator e marque os perfis Gerente e Diretoria (clique na linha do perfil — o checkbox sozinho não alterna). Salve. A fila mostra as pendências dos seus perfis; sem perfil, ela abre vazia.
Abra uma OS que tenha cliente preenchido, deixe valor_total acima de 5000, Salve e clique em Enviar para aprovação — o botão que o Aplicar aos formulários colocou. O toast confirma *Enviado para aprovação!*. Clicar de novo no mesmo registro responde *Registro já está em aprovação no fluxo …* — é o guarda do motor, não um erro seu.
Entre em Operação → Aprovações. A fila lista Cod., Registro, Etapa, Fluxo, Prazo e Criada em, com dois ícones na primeira coluna: Abrir e Delegar (são só ícones — o rótulo aparece ao passar o mouse). Três detalhes que a tela mostra assim mesmo: Fluxo traz o *slug* (aprovacao_de_orcamento_de_os) em vez do nome, Criada em sai em formato ISO e Prazo fica vazio, porque nível montado em Lista não tem prazo.
Clique em Abrir. Como o nível foi montado em Lista (sem formulário vinculado), abre o modal Decidir pendência com o nome do passo, o registro, um campo Comentário e os botões Aprovar / Rejeitar / Devolver. Aprove: a linha some e, se o valor passar de R$ 5.000, uma nova pendência aparece em Aprovação da diretoria.
Reabra a OS e confira status_aprovacao. O campo só recebe o valor final quando o fluxo chega a um Fim do fluxo.
Erros comuns
A fila abre vazia mesmo com OS enviada
o usuário logado não tem o perfil do nível → Admin › Usuários no app, marque Gerente / Diretoria e entre de novo.
A fila abre vazia num projeto antigo (publicado antes de set/2026)
o perfil foi gravado sem Code naquela época: o campo prometia *"gerado automaticamente se vazio"* e o app publicado ficava com o código em branco, enquanto o fluxo guardava gerente/diretoria. Nenhum usuário casava e a fila ficava vazia sem dizer por quê. Hoje o código é gerado do nome nas duas pontas; num projeto daquela época, abra Perfis de acesso, salve cada perfil (o código aparece), use Sincronizar permissões e republique.
O botão Enviar para aprovação não aparece no formulário
o formulário foi criado antes de o fluxo existir → clique em Aplicar aos formulários, no cabeçalho do painel de fluxos (parte 4), e republique. Se a página aparecer como pulada, a lista diz o motivo (página travada em edição, ou Blade sem <mad-form-actions>).
Fluxo publicado — mas ainda falta tela
não existe página Fila de Aprovação no projeto, ou a tabela alvo não tem formulário → crie a fila (parte 1) e publique de novo.
Salve antes de publicar
há alterações não gravadas → clique em Salvar e repita.
A condição não filtra nada / toda OS sobe para a diretoria
o valor foi digitado em Aplicar função na coluna em vez de Valor. Reabra Condição ativa e confira o Preview do PHP gerado: o certo é $q->where('valor_total', '>', 5000); o errado é $q->whereRaw('5000 = ?', ['']).
A simulação não anda quando clico em Aprovar
mesma condição quebrada acima: a avaliação estoura no meio do clique e o painel congela sem mensagem. Corrija a regra.
Registro já está em aprovação no fluxo …
esse registro já tem instância aberta. Use outra OS ou decida a pendência existente.
O contador do canvas insiste em N problema(s)
algum nó está sem ligação de saída → o próprio painel aponta o nó; ligue-o a um Fim do fluxo.
Checklist de encerramento
O perfil Diretoria existe em Perfis de acesso, com Codediretoria.
A página Aprovações existe no módulo Operação e abre pelo menu do app.
O fluxo Aprovação de orçamento de OS aponta ordem_servico e status_aprovacao.
O nível 1 tem o perfil Gerente; o nível 2, Diretoria com Condição ativa.
O preview da condição é $q->where('valor_total', '>', 5000).
A simulação percorre os dois caminhos (7200 passa pela diretoria, 1200 a pula).
O cabeçalho mostra publicado v1.
Rodei Aplicar aos formulários e o OrdemServicoForm ganhou o botão Enviar para aprovação e o método onSubmitApproval.
O usuário do app tem os perfis Gerente e Diretoria.
A fila lista a pendência e Abrir abre o modal Decidir pendência.
Aprovar no gerente cria a pendência de Aprovação da diretoria.
Três automações que não têm tela própria: uma rotina que roda sozinha todo dia, formatadores reaproveitáveis para o PDF e a decisão do que acontece depois que o usuário clica em salvar.
O que você terá no fim
Um agendamento diário no Agendador, publicado em schedule.php.
Dois transformers — MoedaBR e StatusBadge — no contexto Documento (PDF).
O MoedaBR aplicado no chip do documento da aula 20, escolhido em Formatador › PERSONALIZADO (onde o StatusBadge também já aparece, pronto para uso).
O formulário da OS com Após salvar em Ir para outra página, indo para a impressão.
Studio › Agendador › Novo agendamento
Modal Novo agendamento com o Alvo OrdemServicoList escolhido, Modo Direto, Método chamado gerarPreventivas, Frequência Diária às 06:00 e a linha Todo dia às 06:00 · próxima: à direita
Studio › Agendador
Painel Agendador com o card Gerar OS preventivas (selos PÁGINA e Direto, linha Todo dia às 06:00 0 6 * * *), o rodapé schedule.php v1.0.2 e o botão Publicar no topo
Studio › Transformers
Painel Transformers com moeda_br aberto: Identificação (Contexto Documento (PDF), Escopo Somente este projeto) e a Implementação mostrando a classe DocumentTransformer com o corpo do método
Studio › Impressão da OS › Visual
Editor de Documento com o popover do chip aberto e a lista do Formatador rolada até o grupo PERSONALIZADO, com MoedaBR marcado e StatusBadge abaixo
Studio › OrdemServicoForm › Propriedades
Canvas do OrdemServicoForm com o painel direito na seção Após salvar: Ação Ir para outra página, Destino Impressão da OS · show e Mensagem de sucesso preenchida
Passo a passo
Parte 1 — Agendador
No rail, abra Agendador · schedule.php. O painel mostra Tarefas agendadas (schedule.php) · Assistec Manutenção Exemplo. Vazio, ele traz Nenhum agendamento ainda com o texto Crie a primeira tarefa cron sem escrever PHP. Ela vira uma entrada no schedule.php. e o botão Criar agendamento.
Clique em Novo agendamento e preencha:
Nome = Gerar OS preventivas (o campo sugere Ex.: Alerta de estoque baixo);
Descrição (opcional) = Abre as OS de manutenção preventiva do dia;
Alvo: a lista mistura páginas e códigos do projeto, cada um com o selo PÁGINA ou CÓDIGO e o módulo. ⚠️ Ela usa o nome da página (OrdemServicoList), não o rótulo do menu ("Ordem de Serviços") — procurando pelo rótulo o painel responde Nenhum alvo encontrado.. Busque OrdemServicoList em Buscar página ou código… e clique no item. Escolher o alvo já preenche o Nome se ele estiver vazio.
Modo de execução: Em fila (assíncrono) manda a tarefa para a fila de jobs; Direto (síncrono) executa na hora. Escolha Direto — aí aparece Método chamado, que já vem preenchido com render; troque por gerarPreventivas. Em Em fila, o campo que aparece é Classe do job, com o hint opcional, padrão = <classe do alvo>.
Payload é opcional: pares de chave e valor (texto ou número) entregues à rotina. Use Adicionar campo se precisar.
Frequência: escolha o preset Diária e a hora 06:00. Os presets são A cada N min, De hora em hora, Diária, Dias úteis, Semanal, Mensal e Cron avançado (aí sim você digita a Expressão, e o painel recusa com Expressão cron inválida se estiver errada). Abaixo do campo, a linha próxima: mostra quando a regra dispara pela próxima vez — é a conferência mais rápida.
Deixe Sem sobreposição ligado (se a execução anterior ainda estiver rodando, a nova não começa) e Ativo ligado. Clique em Salvar.
No topo do painel, clique em Publicar. Abre a confirmação Publicado — schedule.php v<N> publicado e gravado no projeto. — com os botões OK e Fechar. Só a publicação escreve o arquivo no projeto; Criar agendamento apenas guarda a configuração. O rodapé do painel passa a mostrar conectado · <projeto> · schedule.php v<N> e 1 ativo, e o card ganha os selos PÁGINA (tipo do alvo) e Direto (modo), com a linha Todo dia às 06:00 0 6 * * *.
Clique em Ver código para conferir a entrada gerada: uma linha Schedule::call(...) com ->dailyAt('06:00'), ->name(...) e ->withoutOverlapping(). Copiar schedule.php copia o arquivo inteiro.
Cada card tem, à direita, os ícones de Pausar / Ativar, editar e remover (são só ícones — o rótulo aparece ao passar o mouse). Pausado, o agendamento continua no arquivo, mas sai comentado — some do cron sem sumir do projeto. Remover pede confirmação (Remover agendamento?).
Atenção ao alvo. O agendamento roda o método que você nomeou; se esse método ainda não existir no controller, a tarefa dispara e não faz nada. A aula 26 cria um código dedicado (mad_code) para rotinas assim — volte aqui depois e troque o Alvo para ele.
Parte 2 — Transformers
No rail, abra Transformers. O painel explica: Formatadores customizados que ficam disponíveis nos blocos de documento.
Clique em Novo transformer. Contexto e Escopo são listas normais; os três campos de texto (Nome técnico (slug), Rótulo e Descrição) têm exemplos no próprio campo (status_color, Status com cor, Ex.: badge colorida lida da coluna estado_pedido_cor). Em Identificação:
Nome técnico (slug) = moeda_br. A regra é Use letras minúsculas, números e sublinhado; comece com letra.;
Rótulo = MoedaBR — é o texto que aparece no dropdown;
Descrição = Valor em reais com separador de milhar.
Contexto = Documento (PDF). O hint avisa: Define a assinatura PHP e os parâmetros disponíveis. As outras opções são Listagem (Grid), Preenchimento (Fill) e Qualquer contexto — esta última cria o método nas três classes ao mesmo tempo.
Escopo = Somente este projeto. O outro escopo (Developer) deixa o transformer disponível em qualquer projeto seu — é o que o texto embaixo do campo explica.
Em Implementação (*Corpo PHP do método. Recebe $value, retorne string formatada.*) o editor mostra a classe inteira já montada — namespace App\Transformer; class DocumentTransformer { public static function moedaBr(mixed $value, array $opts = []): string { … } } — e só a linha do corpo é editável (ela começa em return (string) $value;). À direita ficam os Parâmetros disponíveis: $value, $opts['row'], $opts['record'], $opts['column'] e $opts['page_type']. Troque o corpo por:
Alterne para a aba Código fonte: ela mostra a Classe final gerada como será materializada no projeto, com os dois métodos. Só os corpos são editáveis — assinatura e cabeçalhos são gerados.
Depois de criado, abrir o transformer na lista troca o botão para Salvar alterações e libera Excluir e Duplicar. O botão Deploy abre Deploy dos transformers, que envia só os três arquivos de transformer para o Teste Online, para o Git ou para um Servidor de deploy, sem republicar o projeto inteiro.
Parte 3 — aplicar no documento e configurar o Após salvar
Abra a página Impressão da OS (aula 20). ⚠️ Clique no chip, não no parágrafo: clicar no texto seleciona o BLOCO (o painel da direita mostra "Parágrafo"); clicar no chip abre um balão próprio, com o nome da variável, uma lixeira e o campo Formatador.
Formatador não é uma lista comum: é um seletor com busca (Buscar formatador…) e grupos — TEXTO, MOEDA, NÚMERO, DATA E HORA, DOCUMENTO BR e, no fim da lista, PERSONALIZADO. Role até lá: os dois transformers que você acabou de criar estão ali. Escolha MoedaBR; o chip passa a mostrar {numero} · custom:moeda_br. Clique em Salvar. *Dica (fora do roteiro):* o StatusBadge se aplica do mesmo jeito, no chip de um campo de status — basta ter esse chip no documento.
Abra o formulário de OS da aula 09 e, na barra do editor, abra Propriedades da página. Role até a seção Após salvar: ela define o que o formulário faz depois de gravar — e é a última coisa que acontece no salvamento.
Em Ação, escolha Ir para outra página. As outras opções são Atualizar a linha na listagem (insere ou atualiza a linha sem recarregar), Fechar e avisar e Continuar no formulário.
Em Destino, escolha Impressão da OS · show. ⚠️ A lista não traz páginas, e sim página · método (Abertura de OS · onEdit, Agenda de visitas · onShow, Impressão da OS · show…) — procurar só pelo nome da página não acha nada. Há também a opção Outra URL ou rota…, que libera um campo livre (https://… ou /minha-rota).
Em Mensagem de sucesso, escreva OS salva. Abrindo a impressão…. Em branco, nenhum aviso aparece.
Se alguém já tiver editado esse trecho no código, o painel mostra o badge Personalizado (editado à mão) com o aviso O bloco after-save foi alterado no código. Mexer nas opções acima sobrescreve o que está lá. e o botão Substituir pela configuração. Há ainda dois avisos possíveis: Código preservado (o código faz algo que as opções não representam — nada é sobrescrito) e Nada foi gerado no código (o salvamento não termina de um jeito que o painel consiga gerenciar).
Clique em Salvar e, no rail, Teste online → Testar agora. No app, salve uma OS: a mensagem aparece e a tela vai para a impressão.
Erros comuns
Nenhum alvo encontrado. ao buscar a página no agendamento
a lista usa o nome da página (OrdemServicoList, OrdemServicoForm), não o rótulo do menu → busque pelo nome.
O corpo do transformer volta para return (string) $value;
você selecionou o editor inteiro e colou por cima. Só a linha do corpo é editável; a assinatura e o cabeçalho da classe são gerados e o editor recusa a edição em silêncio → apague só a linha do return e escreva a sua.
Cliquei no chip e apareceu o painel "Parágrafo"
o clique caiu no texto, não no chip → clique exatamente em cima do {campo}; o balão do chip é o único lugar com Formatador.
O Destino do Após salvar fica em "Abertura de OS · onEdit"
a lista é página · método e o item procurado não foi encontrado pelo nome da página → procure por Impressão da OS · show.
Criei o agendamento e nada roda
faltou Publicar → salvar só guarda a configuração; publicar escreve o schedule.php do projeto.
Falta o alvo
ao salvar o agendamento → nenhuma página ou código foi escolhido em Alvo → escolha um item da lista antes de salvar.
O agendamento roda mas não acontece nada
o Método chamado não existe no controller do alvo → confira o nome do método, ou aponte o Alvo para um código que tenha essa rotina.
Nome inválido. Use letras minúsculas, números e sublinhado; comece com letra.
o Nome técnico (slug) do transformer tem maiúscula, espaço ou acento → use moeda_br, e deixe o nome bonito no Rótulo.
O transformer não aparece no combo Formatador do documento
ele foi criado em outro Contexto → transformer de Listagem (Grid) não aparece no documento; troque para Documento (PDF) ou Qualquer contexto.
Mudei as opções do Após salvar e o comportamento não mudou
o bloco está com o badge Código preservado → o painel não sobrescreve código à mão; use Substituir pela configuração se quiser mesmo trocar.
Depois de salvar, a tela vai para a impressão mas o PDF sai vazio
o documento espera o registro recém-salvo → confira que o Destino é a página de documento correta e que a OS tem itens lançados.
Checklist de encerramento
O agendamento Gerar OS preventivas aparece no painel com Todo dia às 06:00 0 6 * * *.
Ver código mostra a entrada com ->dailyAt('06:00') e ->withoutOverlapping().
O painel confirmou schedule.php v<N> publicado e gravado no projeto.
Os transformers MoedaBR e StatusBadge existem no contexto Documento (PDF).
A aba Código fonte mostra os dois métodos na classe gerada.
No documento, o chip {numero} mostra custom:moeda_br e o grupo PERSONALIZADO do Formatador lista MoedaBR e StatusBadge.
O formulário da OS tem Após salvar em Ir para outra página, destino Impressão da OS, com mensagem de sucesso.
No app, salvar uma OS mostra a mensagem e abre a impressão.
Páginas salvas (Salvar) e Teste Online republicado (Testar agora).
Deixar o sistema falando mais de um idioma e com a identidade visual da Assistec, sem tocar em CSS nem em arquivo de tradução.
O que você terá no fim
Português (BR) e Espanhol habilitados no projeto, com o português como idioma padrão.
Um grupo de traduções próprio (assistec) com as frases do sistema de OS.
As frases traduzidas em lote pela IA, com o que foi revisado à mão preservado.
A validação (Validar) rodada no grupo, sem key faltando nem placeholder quebrado.
Um tema assistec salvo, marcado como Default do projeto e Liberado no projeto.
O app de teste publicado já com as cores e os cantos do tema.
Noção clara de onde o consumo de IA aparece e quem paga por ele.
Studio › Traduções › Traduzir IA
Painel Traduções com o grupo assistec selecionado à esquerda, a tabela de keys ao centro (colunas PT-BR ★ e ES) e o modal Tradução IA aberto mostrando Modelo IA, Locales target, Keys (4/4) e o botão Traduzir 4 strings
Studio › Temas › Assistec
Painel Temas do projeto com o tema Assistec em Temas salvos (1), aba Layout aberta, preview ao vivo ao centro e o bloco Identificação à direita com Liberado no projeto e Default do projeto marcados
Teste Online › Operação › Ordem de Serviços
Teste Online com o app já tematizado: faixa do logo na cor da marca e a listagem Ordem de Serviços usando os cantos e a tipografia do tema
Passo a passo
Rail → grupo CUSTOMIZAÇÃO → Traduções. O painel abre com os grupos à esquerda, a tabela de keys ao centro e o detalhe da key à direita.
Um projeto novo começa com 0/0 idiomas: o painel mostra Configure pelo menos 1 idioma e Nova key não funciona enquanto isso. Clique em Idiomas (o botão traz a contagem: Idiomas (2) depois que os dois existirem).
Em Idiomas do projeto, use Buscar idioma… e habilite nesta ordem:
🇧🇷 Português (BR) — o primeiro idioma habilitado vira default + fallback;
🇪🇸 Español.
Se você habilitar o espanhol primeiro, ele vira o padrão — e a linha de criação de key passa a gravar o texto português dentro do espanhol. Conserto: no rodapé do modal, os combos Default: e Fallback: trocam o idioma padrão a qualquer momento.
O idioma padrão traz uma estrela e não pode ser desabilitado (o card fica com a dica *Idioma padrão: defina outro como padrão antes de desativar este*).
Feche o modal. Em Adicionar grupo, digite assistec e clique em Criar. O nome aceita minúsculas, dígitos, - e _, até 80 caracteres. Grupos com o selo CORE são somente leitura.
Com o grupo assistec selecionado, clique em Nova key. A linha de criação abre com três controles: o nome da key, o Valor em Português (BR) e um checkbox IA. Desmarque o IA: ligado (é o padrão), ele dispara uma tradução por LLM a cada key criada. Nesta aula a tradução é feita uma vez só, em lote.
Crie estas quatro, preenchendo o valor e clicando em Criar (ou Enter):
key
valor em pt-BR
os.aberta
Ordem de serviço aberta com sucesso
os.concluida
Ordem de serviço concluída
os.sem_tecnico
Nenhum técnico disponível para esta especialidade
os.aguardando_peca
Aguardando peça para retomar o atendimento
A linha continua aberta depois de criar, para emendar a próxima. Clicar em Nova key de novo fecha a linha (o botão é um liga/desliga).
Clique no filtro Missing para ver o que falta em espanhol. Deve listar as quatro (a coluna ES mostra *missing*).
Clique em Traduzir IA. No modal Tradução IA:
o cabeçalho conta o serviço: 4 keys × 1 targets = 4 chamadas;
Modelo IA — vem Claude Haiku 4.5 (*Rápido + barato (recomendado)*); a lista traz também GPT-4o Mini, Gemini 2.5 Flash, Claude Sonnet, GPT-4o e a opção + Outro modelo (slug custom);
Locales target — es já vem marcado (todos os idiomas que não são o padrão);
Keys (4/4) — todas marcadas; Todas / Nenhum ajustam a seleção;
Sobrescrever user_value existente — deixe desmarcado. Assim a IA preenche só o que está vazio.
Clique em Traduzir 4 strings. Ao terminar, o bloco Resultado mostra Traduzidos: 4 e Pulados: 0. Feche o modal.
Clique em Validar, no topo do painel (entre Documentação e Idiomas). Ele varre os idiomas atrás de key sem valor e de placeholder perdido. Se apontar alguma, abra a key indicada: ou falta valor num idioma, ou um placeholder (:name, :count) se perdeu na tradução.
Clicar numa linha da tabela abre, à direita, o detalhe da key: os valores por idioma, os problemas e os Exemplos de uso (PHP, Blade e MadResponse) prontos para copiar.
Rail → Propriedades do projeto → seção Traduções:
Habilitar traduções? → Sim (é um par de botões Sim/Não, não um checkbox);
Idioma principal: Português;
Idiomas habilitados: acrescente Espanhol (a caixa de chips abre um seletor ao ser clicada). Se o chip não entrar, siga em frente: o app usa os idiomas habilitados em Traduções, e o Resumo do projeto, à direita, já mostra Disponíveis: 2;
Timezone: America/Sao_Paulo.
Clique em Salvar alterações (o botão fica cinza enquanto não há nada pendente). O rodapé confirma Tudo salvo.
Rail → grupo CUSTOMIZAÇÃO → Temas. O painel abre em Temas do projeto com TEMAS SALVOS (0). No topo: Gerar com IA · Presets · Importar · Exportar · Novo, mais o seletor Tema base (Notch) — *Os presets seguem o tema base selecionado.*
Clique em Novo. O tema nasce como Novo tema (não salvo).
Abra Presets (Temas pré-prontos) e aplique Bootstrap 5 como ponto de partida. Aplicar um preset cria um tema novo — ele só entra no projeto quando você salvar.
No bloco Identificação (coluna da direita, aba Simples), preencha:
Label: Assistec — o Slug se preenche sozinho (assistec, que vira themes/assistec.css);
Descrição: Tema institucional da Assistec;
Escopo: Só este projeto.
Opcional: abra Paleta do logo, envie o logo da empresa e clique na cor que deve virar a principal; depois, Aplicar paleta ao tema. Se aparecer um aviso de Contraste abaixo de 4.5:1, escolha um tom mais escuro ou mais claro — o aviso é sobre legibilidade, não sobre gosto.
Aba Layout: escolha Modo (Light/Dark), Layout (Default, Float, Stripe, Dock, Glass, Border, Inset), Menu (Default, Flyout, Glass) e Paleta. O preview ao centro responde a cada mudança.
Aba Componentes gerais: no modo Simples você mexe no essencial. Em Avançado cada variável de CSS aparece agrupada — botões, campos, tabelas, modal, além das medidas finas como larguras, alturas e cantos — e a aba CSS aceita regras soltas.
Aba Login: escolha a variante (por exemplo Split 50/50), preencha Título do formulário (Acessar conta), Texto subtítulo e Texto do botão (Entrar), e envie a Logo grande. As Cores do login começam vazias — *Vazio = usa default da variant*. Enquanto ficarem assim, a tela de login sai praticamente igual à padrão, mesmo com o tema aplicado.
Marque Default do projeto e confira Liberado no projeto (ele já vem marcado).
Clique em Salvar e aplicar. O tema aparece em TEMAS SALVOS (1) com a estrela de padrão.
Opcional: Duplicar tema cria a variação (por exemplo uma versão escura) sem refazer nada. Exportar e Importar movem o tema como .json entre projetos.
Opcional: Gerar com IA abre Gerar tema com IA — você descreve a marca em uma frase e recebe um rascunho para revisar antes de salvar. Nada é salvo automaticamente.
Rail → Teste online → Testar agora para publicar traduções e tema no ambiente de teste.
Abra a aplicação e navegue até uma listagem: a faixa do logo assume a cor da marca e os cantos, a tipografia e os botões seguem o tema.
Sobre o consumo de IA.Traduzir IA e Gerar com IA chamam um modelo de linguagem e gastam tokens da sua conta — pelo Mad Coding Plan ou pela sua própria chave (BYOK), conforme o seu plano. O contador de tokens na barra de status do Studio abre o Histórico de uso da LLM, com modelo, tokens de entrada e saída e o horário de cada chamada. O modelo usado em cada área do produto é escolhido em Propriedades do projeto → Modelos de IA. Restaurar padrão volta ao modelo sugerido pela plataforma.
Erros comuns
O botão Nova key não faz nada e aparece "Configure pelo menos 1 idioma"
o projeto ainda não tem idioma nenhum → clique em Idiomas e habilite ao menos um; o primeiro vira padrão e fallback.
O texto em português foi parar na coluna do espanhol
o espanhol foi habilitado primeiro e virou o idioma padrão → no rodapé do modal Idiomas do projeto, troque Default: e Fallback: para pt-BR e refaça as keys erradas (ou apague o grupo e recrie).
Criei a chave e a IA já cobrou uma tradução
o checkbox IA da linha de criação vem ligado ("Após criar, traduzir IA pros demais locales") → desmarque antes de criar e use o lote em Traduzir IA.
Cliquei em Nova key e a linha sumiu
o botão é um liga/desliga, e a linha já estava aberta → clique de novo para reabrir; para criar em sequência, use Criar (ou Enter) sem tocar no Nova key.
O botão Traduzir IA está apagado
o filtro atual não devolveu nenhuma key (com tudo traduzido, Missing fica vazio) → volte para Todos.
Criei as quatro keys e o card do grupo continua dizendo 0 keys
a contagem do card vem do carregamento do painel e não acompanha a criação; a tabela ao centro está certa → recarregue o Studio para os dois números baterem.
Traduzi em lote e nada mudou nas keys já preenchidas
Sobrescrever user_value existente estava desmarcado, que é o comportamento recomendado → para refazer uma tradução específica, apague o valor daquela key e rode de novo, ou marque Sobrescrever sabendo que ele passa por cima de revisão humana.
A validação aponta "placeholders mismatch"
a tradução perdeu ou renomeou um :name/:count → edite o valor do idioma indicado e devolva o placeholder exatamente como está no idioma padrão.
Salvei o tema e o app continua igual
faltou marcar Default do projeto, ou faltou publicar → marque, clique em Salvar e aplicar e depois Testar agora no Teste online.
O tema aplicou no sistema, mas a tela de login continua igual
as Cores da aba Login estão vazias e a variante usa os defaults dela → preencha as cores do login (ou troque a variante) para a porta de entrada mudar também.
Não consigo editar uma frase que veio do framework
ela está num grupo CORE, que é somente leitura → crie a mesma key no seu grupo: o valor do projeto sobrescreve o do core.
Checklist de encerramento
O botão do topo mostra Idiomas (2), o rodapé do modal traz Default: pt-BR e Fallback: pt-BR, e a coluna PT-BR da tabela tem a estrela.
O grupo assistec existe e a tabela mostra 4 keys com valor em pt-BR.
As quatro keys têm valor em espanhol.
O filtro Missing não retorna nada no grupo assistec.
Validar não aponta key faltando nem placeholder quebrado no grupo assistec.
Propriedades do projeto → Traduções está com Habilitar traduções? em Sim, idioma principal e timezone corretos.
O tema Assistec aparece em TEMAS SALVOS (1), com Default do projeto e Liberado no projeto marcados.
O app publicado mostra a cor da marca e os cantos do tema.
Sei onde ver o consumo de IA (Histórico de uso da LLM) e onde trocar o modelo (Modelos de IA).
Duração 16 minPré-requisitosAula 06 · Aula 24requer plano Avançado
Objetivo
Entender como o sistema decide onde os dados de cada empresa ficam e revisar, numa passada só, as configurações que valem para o projeto inteiro.
O que você terá no fim
Clareza sobre as três estratégias de tenancy e quando cada uma vale a pena.
O preview do que a configuração de tenancy gera (.env e comandos), sem alterar o projeto do curso.
A configuração de banco do projeto conferida e entendida.
Propriedades do projeto percorrida seção por seção, sabendo o que cada uma controla.
O auto-cadastro de cidade/estado conferido (é o que faz os campos de CEP e CNPJ das aulas 07 e 10 criarem a cidade e o estado quando eles não existem).
A lista do que ainda dá para configurar nessas telas, para você aplicar no seu projeto quando fizer sentido.
Plano.O painel Tenancy & Banco exige plano Advanced ou superior. Se o item aparecer apagado no rail, a dica diz "Disponível a partir do Advanced" — o restante da aula (Propriedades do projeto) funciona em qualquer plano.
Studio › Tenancy & Banco
Painel Tenancy & Banco com os blocos ① Banco, ② Tenancy (cliente), ②b Unidade (filial) e ③ IA · acesso, a estratégia Cliente único selecionada e o cartão O que isto gera à direita com o .env
Studio › Propriedades do projeto
Propriedades do projeto com as quinze seções à esquerda, a seção Layout aberta ao centro e o Resumo do projeto à direita (Layout, Acesso, Logs, Idiomas & TZ, Atalhos)
Passo a passo
Parte 1 — Tenancy & Banco
Rail → grupo GERAÇÃO & FERRAMENTAS → Tenancy & Banco. O painel abre com quatro blocos numerados (① Banco, ② Tenancy (cliente), ②b Unidade (filial) e ③ IA · acesso) e o cartão O que isto gera à direita.
Esta tela grava sozinha.Não há botão Salvar: cada clique vira um PATCH depois de meio segundo, e o topo pisca Salvando… → Salvo. Trocar a estratégia "só para ver" já muda o projeto.
① Banco — "Onde as tabelas vivem fisicamente". São quatro cartões: SQLite (arquivo) (com o selo padrão, e é o que o Teste online usa), MySQL, MariaDB e PostgreSQL. ⚠️ Não clique para conferir: escolher um engine já grava.
Com um engine de servidor, aparecem Host, Porta, Banco, Usuário e Senha (deixar a senha em branco mantém a atual) e a Conexão segura (SSL/TLS), que aceita o Certificado da autoridade (CA) entregue pelo provedor — para banco gerenciado, só o CA costuma bastar. Com SQLite selecionado, esses campos nem existem na tela.
Armazenamento por domínio define um nome de banco por conexão no mesmo servidor. Vazio = tudo no mesmo banco.
② Tenancy (cliente) — "Como os clientes (empresas) são isolados". O painel oferece três cartões:
estratégia
o que faz
quando usar
Cliente único *(padrão)*
1 cliente, sem isolamento
app interno, um cliente só — é o caso do curso
Pool
muitos clientes num banco, separados por tenant_id
SaaS com muitos clientes pequenos
Dedicado (Bridge)
1 banco por cliente
contrato que exige isolamento físico
Abaixo deles, Control-plane (travado) com os selos iam e log: identidade e auditoria nunca são roteadas por cliente.
Não troque a estratégia para "ver o que acontece". A tela grava sozinha — o clique vira um PATCH e o topo passa por Salvando… → Salvo. Escolher Pool liga Isolar por linha (tenant_id) e acrescenta a coluna de cliente em todas as tabelas do app gerado; as telas já criadas passam a precisar preencher essa coluna.
O cartão O que isto gera, à direita, é o que dá para explorar sem risco: ele mostra a Versão, o bloco .ENV (com um ícone de copiar no canto), os Comandos e os Avisos da configuração atual. No projeto do curso ele traz DB_MAD_DRIVER=sqlite, MAD_TENANT_ROW_SCOPE_ENABLED=false, MAD_MULTIUNIT=0, MAD_MULTI_DATABASE=0 e MAD_LICENSING=0, com Nenhum comando e Sem avisos.
②b Unidade (filial) — camada dentro do cliente, não outro cliente. Multi-unidade liga o seletor de unidade no login; Multi-banco por unidade permite um banco por filial.
③ IA · acesso limita o que o agente lê e escreve, por usuário ou unidade. A configuração completa fica no MCP Server — botão Abrir MCP Server, assunto da aula 26.
Com a estratégia Dedicado (Bridge), aparece a seção Conexões de tenant (allowlist), que lista as conexões permitidas e mostra em Provisionar (rode no app gerado): os comandos para criar cada banco. Em Cliente único, que é o caso do curso, ela não aparece.
Parte 2 — Propriedades do projeto
Rail → Propriedades do projeto (último item, abaixo do bloco DEPLOY). A coluna da esquerda lista as quinze seções — além das que a aula percorre, há Header & Footer Tags e um atalho para Tenancy & Banco, que é o painel da Parte 1. A direita mostra o Resumo do projeto, e o rodapé, o estado (Tudo salvo. / Alterações pendentes.).
Layout — a seção que muda a cara do app:
Título do app aparece na aba do navegador, na tela de login, no manifest (PWA) e nos e-mails. Em branco, o app usa o nome do projeto — por isso o campo aparece vazio, só com o texto de exemplo;
Tipo de Menu é múltiplo: dá para ter Menu lateral e Menu superior ao mesmo tempo;
Tipo de abertura das páginas: Uma página por vez é o padrão; Uma página por vez em abas deixa o app com abas como o Studio;
Tipo da caixa de diálogo: Bootstrap (padrão) ou Sweet Alert.
Armazenamento de arquivos: Servidor local (padrão) é o que o curso usa. S3 / compatível pede Bucket, Região, Chave de acesso e Chave secreta, e tem Testar conexão. Trocar o destino não move os arquivos que já existem.
Imagens do projeto: Ícone (48 × 48), Logo grande (600 × 200), Logo pequeno (256 × 50) e Favicon (16 × 16). O tema da aula 24 usa essas imagens quando não tem uma própria.
Exportação de PDF: cabeçalho e rodapé padrão dos PDFs exportados. Já foi configurado na aula 16 — aqui só confirme que continua como você deixou.
Contas e permissões reúne quatro decisões de política: Usuário pode se auto cadastrar (num sistema de OS quem cadastra usuário costuma ser o gerente), Usuário pode resetar a sua senha, Sessão única por usuário e Sem permissão para realizar uma ação, os botões devem estar: — Ocultos deixa a tela mais limpa, Desabilitados ensina o usuário que aquela ação existe.
Segurança: mostra a Rest key e o Token de instalação da base. Use Mostrar só quando precisar, e Gerar nova apenas se a chave vazar. Nunca deixe esses valores visíveis num print ou numa gravação. O mesmo vale para Senha do admin no Teste Online, em Contas e permissões, e para o bloco Credenciais do admin da coluna do Teste online — feche esse painel antes de gravar.
Logs de Requisições: Web registra o que o navegador chama; PHP CLI e API (rest) ficam a seu critério. É a fonte que o MadTrace da aula 27 lê.
Traduções: confira o que foi feito na aula 24 — Habilitar traduções?, Idioma principal, Idiomas habilitados e Timezone.
Modelos de IA: cada área do produto tem seu modelo — Agente de IA, Tradução, MCP Server, Gerador de temas, Gerador de fluxos, Planejador de tarefas e Descrição de imagens. Restaurar padrão volta ao modelo sugerido pela plataforma.
Agente IA: abas Regras, Skills e Memórias. Só reconheça a seção — ela é a aula 31.
Endereço / CEP: confira Auto-cadastrar cidades e estados por padrão. É o que faz os campos de CEP e CNPJ das aulas 07 e 10 criarem a cidade e o estado quando eles ainda não existem — se estiver desligado, ligue.
Se você mexeu em algo, clique em Salvar alterações: o rodapé passa de Alterações pendentes. para Tudo salvo. Depois recarregue o Studio — algumas configurações de layout só aparecem depois de salvar e recarregar.
Se o projeto já tiver hospedagem contratada, pode aparecer o aviso de que o app hospedado ainda não recebeu essa configuração, com o botão Ver na Hospedagem. Isso é resolvido na aula 28, sincronizando as variáveis.
O que mais dá para configurar aqui
Estas são as opções que a aula mostra e não altera — o projeto do curso funciona sem elas, e cada uma faz sentido num momento diferente do seu projeto. Nada aqui é passo da aula; é o mapa de volta quando você precisar.
Título do app (Layout) — preencha quando o app tiver nome próprio, diferente do nome do projeto.
Sem permissão, os botões devem estar: Ocultos (Contas e permissões) — decida junto com o desenho dos perfis da aula 05.
Usuário pode resetar a sua senha e Usuário pode se auto cadastrar (Contas e permissões) — dependem de quem vai administrar os usuários.
As quatro imagens do projeto (Imagens do projeto) — entram quando você tiver a identidade visual pronta; o tema da aula 24 as reaproveita.
Cache das agregações de charts + TTL (segundos) (Performance) — vale a pena quando o dashboard da aula 15 começar a ficar lento com volume real.
Destinos para atualização automática → Teste Online (Atalhos) — economiza um clique por salvamento: qualquer save (editor, Ctrl+S, agente de IA) sincroniza o arquivo no ambiente de teste sem passar por Testar agora.
Erros comuns
O item Tenancy & Banco aparece apagado e não abre
o plano da conta não cobre multi-tenancy → a dica mostra "Disponível a partir do Advanced"; siga a aula pela parte de Propriedades do projeto, que não tem gate.
Liguei o Pool e as telas pararam de salvar
o filtro por linha acrescentou uma coluna obrigatória de cliente que os formulários existentes não preenchem → volte para Cliente único, ou regere as telas depois de ligar o Pool; no curso, mantenha Cliente único.
Cliquei numa opção do Tenancy sem querer e não achei o botão Cancelar
essa tela não tem botão nenhum: ela grava sozinha meio segundo depois do clique → volte a opção anterior no próprio painel e confira o selo Salvo e a Versão no topo.
Troquei o engine para MySQL e o Teste Online parou
o ambiente de teste usa a configuração do projeto e agora aponta para um servidor que ele não alcança → volte para SQLite (arquivo) e recrie o ambiente de teste.
Salvei as propriedades e o app continua igual
as mudanças entram em vigor depois de salvar e recarregar; no ambiente de teste, ainda é preciso publicar → recarregue o Studio e clique em Testar agora no Teste online.
O toggle de auto-cadastro de cidade não mudou nada nos campos já existentes
ele pré-liga a opção em campos novos e cobre campos que já declaram a cidade e o estado sem mapa explícito → nos campos antigos, revise a configuração do CEP na própria página.
Checklist de encerramento
Sei explicar, em uma frase cada, Cliente único, Pool e Dedicado (Bridge).
O projeto do curso continua em Cliente único e o painel mostra Salvo.
Li o cartão O que isto gera e sei que ele reflete a configuração já gravada (a tela não tem botão Salvar).
Percorri as quinze seções de Propriedades do projeto e sei o que cada uma controla.
Auto-cadastrar cidades e estados por padrão está ligado.
Sei onde ficam Título do app, as imagens do projeto, o Cache das agregações de charts e os Atalhos — e por que cada um entra só quando eu precisar.
O rodapé de Propriedades do projeto mostra Tudo salvo.
Nenhuma chave da seção Segurança ficou visível na gravação.
Código customizado, Rotas da API, MCP e Acesso de agente
Duração 18 minPré-requisitosAula 09 · Aula 25Tabelasordem_servicorequer plano Pro IA
Objetivo
Sair do editor visual quando ele não basta: escrever uma classe PHP própria, expor uma rota de API e abrir o projeto, com controle, para agentes de inteligência artificial.
O que você terá no fim
Um HelperOsNumero em app/Helpers/OsNumero.php, criado pelo assistente de código.
Noção de onde é seguro editar o PHP de uma página gerada (os blocos) e o que o editor visual sobrescreve.
O painel Arquivos customizados mostrando o que já foi editado à mão, com diff e restauração.
Uma API REST de ordem de serviço publicada em /api/os e /api/os/{id}.
O MCP Server do projeto configurado com contexto, a entidade ordem_servico exposta e uma query salva.
Um token de Acesso de Agente (MCP) criado só com ferramentas de leitura, e o Registro de atividade onde cada chamada dele aparece.
Plano.MCP Server e Acesso de Agente (MCP) exigem o plano Pro IA. Se os dois itens aparecerem apagados no rail, a dica diz "Disponível a partir do Pro IA" — a primeira metade da aula (código customizado e Rotas da API) funciona em qualquer plano.
Studio › Explorer › Novo código
Assistente Criar novo código no Passo 2 de 2 (Configurar o arquivo), tipo Helper no resumo, nome OsNumero com o selo ✓ disponível e o preview à direita com SERÁ GERADO app/Helpers/OsNumero.php e o snippet inicial
Studio › Rotas da API
Painel Construtor de Rotas com o grupo /api expandido, o selo do ApiOrdemServicoController e as duas rotas GET /os → index e GET /os/{id} → show (a aba Código Gerado fica no canto superior direito)
Studio › MCP Server › Entidades & Tools
MCP Server na tela Entidades & Tools com ordem_servico exposta (verbos LIST e READ ativos, selo sem escopo, status incompleta) e o aviso 1 entidade exposta sem descrição semântica no topo
Studio › Acesso de Agente (MCP) › Criar token
Modal Criar token de acesso com o nome Demo curso, o seletor Expira, o contador 0/67 de Tools permitidas e o grupo PÁGINAS com os selos só leitura — antes de criar, sem nenhum segredo na tela
Passo a passo
Parte 1 — Um código seu
Na tira do Studio, clique no + (Criar novo) e escolha Novo código — o mesmo item existe no menu de contexto do Explorer. O assistente Criar novo código abre no passo Tipo.
Em Que tipo de código? os tipos vêm agrupados em PHP, Laravel, Web / Frontend e Dados. Escolha Helper ("Classe utilitária de apoio (app/Helpers)") e clique em Continuar.
No passo Configurar o arquivo, digite OsNumero em Nome do arquivo (PascalCase, sem extensão). O preview à direita mostra SERÁ GERADOapp/Helpers/OsNumero.php e o SNIPPET INICIAL; abaixo do campo aparece ✓ disponível (ou Nome já em uso.).
Deixe Incluir snippet inicial marcado e Módulo vazio. Clique em Criar código.
O arquivo abre no editor. Substitua o corpo da classe por este método:
/** Devolve o próximo número de OS do ano, no formato OS-2026-0001. */
public static function proximo(): string
{
$ano = date('Y');
$doAno = \App\Models\OrdemServico::whereYear('dt_abertura', $ano)->count();
return sprintf('OS-%s-%04d', $ano, $doAno + 1);
}
Confira o nome do model no Explorer, em app/Models — é a classe gerada a partir da tabela ordem_servico.
Salve com Ctrl+S.
Abra a página OrdemServicoForm e vá na aba PHP (ao lado de Visual e Blade).
Repare na divisão: as áreas marcadas Bloqueado — gerado pela UI são reescritas a cada salvamento do editor visual; as marcadas Editável são suas. O botão Ir para bloco lista os blocos do arquivo.
Dentro do bloco editável correspondente, chame o helper para preencher o número quando a OS for nova:
$this->numero = \App\Helpers\OsNumero::proximo();
Salve. Modo avançado libera o arquivo inteiro para edição, mas o próprio painel avisa: mudanças no Visual podem sobrescrever esse PHP. Não ligue sem necessidade.
Rail → grupo Customização → Arquivos customizados. O painel Verificar Arquivos Customizados lista os arquivos que nasceram de um template do framework e foram modificados neste projeto.
Clique em Comparar numa linha: o diff abre com TEMPLATE ORIGINAL (somente leitura) de um lado e SUA CUSTOMIZAÇÃO (editável) do outro. Salvar customização grava dali mesmo.
Restaurar devolve o arquivo ao conteúdo do template; a sua edição é preservada como entrada no histórico de versões.
Parte 2 — Rotas da API
Rail → grupo GERAÇÃO & FERRAMENTAS → Rotas da API. O painel Construtor de Rotas abre com a árvore de grupos e, no canto superior direito, o par de abas Construtor de Rotas / Código Gerado.
Clique em Grupo de Rota para criar um grupo raiz e, no campo do grupo, escreva o prefixo /api. O grupo nasce com Middleware: 0 e Sem rotas neste grupo.
Clique em API Controller. Abre a barra lateral API Controller — Configure propriedades do controlador.
Em Informações do Controlador:
Banco de Dados: assistec;
Tabela: ordem_servico — ao escolher a tabela, o painel preenche sozinho o Nome do Controlador (ApiOrdemServicoController) e o Prefixo (/api/ordem-servico);
Chave Primária: id (também vem preenchida sozinha);
Por Página: 50.
Em Configurações de Rota, troque o Prefixo para /api/os — é ele que forma GET /api/os.
Em Métodos Disponíveis os cinco verbos vêm marcados. Uma API de consulta desmarca Criar um novo registro, Atualizar um registro existente e Excluir um registro, deixando só os dois GET.
Em Campos de Índice, escolha o que volta na listagem: numero, titulo, dt_abertura, status_os_id. Em Campos de Exibição, acrescente descricao_problema, valor_total e tecnico_id.
Em Campos Filtráveis inclua status_os_id e tecnico_id; em Campos Ordenáveis, dt_abertura.
Clique em Criar. As rotas aparecem na árvore, dentro do grupo, com o selo do controlador — e o painel Código Gerado mostra o Router::group real, com botão de copiar.
As rotas só nascem no Criar.O botão Atualizar (quando você reabre um controlador existente) grava o arquivo e não cria rota nenhuma. E criar de novo um controlador com um nome que já existe devolve A controller named '…' already exists — nada é gravado.
Confira em Exemplos de Rotas: GET /api/os e GET /api/os/{id}.
O ícone ao lado de API Controller abre Controllers Sem Rota — a lista de controladores que existem como arquivo mas não estão em nenhuma rota. Clicar num deles reabre a barra lateral.
Se um controlador ficou órfão (existe, mas sem rota), use + Adicionar Rota dentro do grupo e monte a linha à mão: verbo (GET), caminho (/os — o prefixo /api do grupo já entra na frente), o controlador no combo e a ação (index). Repita com /os/{id} e show. Cada linha é gravada na hora.
Parte 3 — MCP Server
Rail → grupo GERAÇÃO & FERRAMENTAS → MCP Server. A navegação tem nove telas: Visão geral, Contexto, Entidades & Tools, Campos, Glossário, Queries salvas, Permissões, Exemplos · few-shot e Playground (BETA).
Cuidado ao gravar a Visão geral.Ela mostra o endpoint do MCP e a linha token: mcp_…, mascarada só da metade em diante. Pule essa tela na captura ou cubra a linha do token.
Contexto — o texto que entra no prompt do agente em toda conversa:
Nome do sistema: Assistec Manutenção Exemplo;
Descrição em linguagem natural: descreva em 60 a 80 palavras o que o sistema faz, para quem e em que contexto (atendimento, técnicos em campo, ordens de serviço com serviços e peças, faturamento);
Domínio de negócio: ERP; Idioma principal: Português (Brasil); Tom de resposta: Técnico;
Instruções gerais para o agente (uma por linha): Sempre confirme antes de cancelar uma ordem de serviço. e Nunca exponha CPF/CNPJ completo em respostas.
Entidades & Tools — a tabela lista as 17 entidades do projeto com os filtros Todas, Expostas, Pendentes e Ocultas. Marque ordem_servico: os verbos LIST e READ acendem, a linha ganha o selo sem escopo e o status incompleta, e o topo passa a avisar 1 entidade exposta sem descrição semântica. Complete a entidade:
preencha a Descrição semântica (o botão Gerar sugestões do aviso propõe um texto que você revisa — ele consome créditos de IA);
abra Escopo & acesso e defina a Coluna de dono (usuário) ou a Coluna de unidade. Sem escopo definido, o acesso àquela entidade é negado — é proposital.
Campos — revise as descrições por coluna da entidade exposta e marque como PII o que não pode sair inteiro nas respostas (numa entidade de cliente, por exemplo, cpf_cnpj, email e telefone).
Queries salvas → Nova query salva: Nome técnico (slug)os_abertas_por_tecnico, Entidade baseordem_servico, um filtro por status_os_id, colunas de retorno e Ordenação por dt_abertura. Use Executar amostra antes de Salvar query.
Permissões — a matriz perfil × ferramenta. Deixe herda onde a permissão do sistema já resolve, e restrinja explicitamente o que for sensível.
Playground — faça uma pergunta como um usuário faria ("quantas ordens de serviço estão abertas?") e acompanhe no Inspector · tools chamadas quais ferramentas o agente usou. O agente só enxerga o que você expôs: perguntar por nome de técnico ou de cliente não devolve nada enquanto essas entidades estiverem ocultas.
Clique em Publicar mudanças, no canto superior direito do painel (ele fica visível em qualquer uma das nove telas, ao lado de Exportar manifest). O selo do rodapé passa de Rascunho para ativo.
Parte 4 — Acesso de Agente (MCP)
Rail → grupo GERAÇÃO & FERRAMENTAS → Acesso de Agente (MCP).
Clique em Criar token. O modal Criar token de acesso abre com:
Nome: digite Demo curso;
Expira: o campo abre em Nunca expira — troque por um prazo (30 dias serve para uma demonstração);
Tools permitidas: o contador começa em 0/67 e as ferramentas vêm agrupadas por domínio (PÁGINAS é o primeiro), cada grupo com um Selecionar tudo. Marque só as que trazem o selo só leitura — em PÁGINAS, Listar páginas (page_list) e page_files. Deixe desmarcada qualquer ferramenta que escreve (Criar página, Editar página, Excluir página, Gerar CRUD…).
Clique em Criar token. A tela Token criado — copie agora mostra o segredo uma única vez e o comando pronto em Adicionar no Claude Code. Nunca mostre esse valor em gravação, print ou log. Copie, guarde no seu gerenciador de senhas e clique em Concluir.
Na lista, use Ver atividade para abrir o Registro de atividade: ferramenta chamada, status, argumentos, duração e horário.
Revogar interrompe o token imediatamente e não pode ser desfeito. Crie um novo quando precisar.
Erros comuns
Cliquei em Criar e apareceu A controller named '…' already exists
já existe um arquivo de controlador com esse nome (uma tentativa anterior, por exemplo) → troque o Nome do Controlador, ou monte as rotas à mão com + Adicionar Rota apontando para o controlador que já existe. O Excluir do Explorer vem desabilitado para arquivo de API controller.
Criei o controlador mas o grupo continua dizendo "Sem rotas neste grupo"
as rotas só são geradas no Criar; reabrir e clicar em Atualizar não cria rota → use + Adicionar Rota.
A entidade exposta no MCP aparece como "incompleta" e com o selo "sem escopo"
falta a Descrição semântica e o Escopo & acesso daquela entidade → preencha os dois; sem escopo definido, o acesso à entidade é negado de propósito.
O nome do arquivo fica em vermelho com "Nome inválido pro tipo escolhido"
tipo PHP exige PascalCase sem extensão → escreva OsNumero, não os_numero.php.
Editei o PHP da página e o editor visual apagou a minha mudança no salvamento seguinte
a linha estava numa área Bloqueado — gerado pela UI → mova o código para dentro de um bloco editável, ou coloque a regra num Helper e chame do bloco.
A rota da API responde 404 no app
o grupo ou o controlador foram criados, mas o projeto não foi publicado → clique em Testar agora no Teste online (ou publique) e teste de novo.
O Playground diz que o motor MCP não está publicado
o manifest ainda está como rascunho → volte à Visão geral e clique em Publicar mudanças.
O agente externo conecta mas não enxerga nenhuma tabela
a entidade está exposta sem Escopo & acesso definido, e a regra é negar por padrão → defina a coluna de dono ou de unidade, ou marque a entidade como Tabela de referência (exempt) se ela for mesmo um lookup público.
Checklist de encerramento
app/Helpers/OsNumero.php existe no Explorer e tem o método proximo().
A aba PHP da página de OS chama o helper dentro de um bloco editável.
Sei diferenciar área Editável de área Bloqueado — gerado pela UI.
O painel Arquivos customizados abre e o botão Comparar mostra o diff.
O grupo /api existe em Rotas da API e leva o selo do ApiOrdemServicoController.
As rotas GET /os → index e GET /os/{id} → show aparecem dentro do grupo (isto é, GET /api/os e GET /api/os/{id}) e no Código Gerado.
Controllers Sem Rota não lista nenhum controlador do projeto.
O MCP Server tem Contexto preenchido e está publicado (selo ativo).
ordem_servico é a única entidade exposta, com LIST e READ, descrição semântica preenchida e escopo definido (a linha deixou de dizer incompleta).
A query salva os_abertas_por_tecnico devolve registros em Executar amostra.
Existe um token em Acesso de Agente (MCP) com escopo só de leitura.
Nenhum token, chave ou segredo apareceu na gravação.
Duração 16 minPré-requisitosAula 06 · Aula 15Tabelasordem_servicoclienteos_apontamentorequer plano Pro
Objetivo
Ver o sistema por dentro depois que ele já roda: descobrir o que quebrou, o que está lento e consultar o banco sem sair da plataforma.
O que você terá no fim
O MadTrace ligado no ambiente de teste, com ingestão ativa no rodapé do painel.
Um erro real capturado, com rastreamento e contexto da requisição.
A leitura dos cinco indicadores de desempenho: Throughput, Resposta p95, Tempo em banco, Taxa de erro e Apdex.
O formulário Nova regra de alerta montado (GATILHO, CANAL e DESTINO) — sem criar a regra, que dispara e-mail de verdade a cada evento.
O console SQL conectado ao banco do Teste Online, com uma consulta escrita em português pela IA.
A consulta OS cadastradas salva na biblioteca e o plano de execução analisado.
Plano.MadTrace · observabilidade exige plano Pro ou superior; Banco de dados exige Advanced ou superior. Os dois itens aparecem apagados no rail quando o plano não cobre, com a dica "Disponível a partir do …".
Studio › MadTrace · observabilidade › Visão geral
MadTrace na tela Visão geral · desempenho com a janela de 1 hora selecionada, os cinco indicadores no topo (Throughput, Resposta p95, Tempo em banco, Taxa de erro, Apdex) todos em zero e os blocos Rotas mais lentas e Queries mais custosas vazios — projeto com ingestão ativa e nenhum evento na janela
Studio › MadTrace · observabilidade › Alertas
Tela Alertas com o formulário Nova regra de alerta aberto: GATILHO (New Issue, Regression, Spike, Fatal), CANAL (E-mail / Webhook) e DESTINO preenchido com alertas@exemplo.com.br, sobre o estado Nenhuma regra ainda e a seção Gatilhos disponíveis
Studio › Banco de dados › Studio
Painel Banco de dados: a conexão Teste Online à esquerda, o explorador listando só as tabelas de framework (cache, jobs, migrations, sessions…), o editor com SELECT count(*) FROM ordem_servico; e a aba Resultados com a linha devolvida (1 linhas · 0 ms no canto)
Passo a passo
Parte 1 — MadTrace
Rail → grupo Geração & ferramentas → MadTrace · observabilidade. No topo ficam o seletor de projeto (Assistec Manutenção Exemplo) e o de ambiente; no rodapé, o estado da ingestão, o plano e a quota (eventos 0 / 5k · retenção 7d · rate 60/min).
Se o rodapé ainda não disser ingestão ativa, abra a tela Instalar SDK:
DSN do projeto — copie o valor. A chave pública vai no cabeçalho da chamada; o host vira o endereço de envio.
Habilitar no .env — copie o bloco de variáveis. No app do Teste online, MADTRACE_ENABLED=true e o DSN bastam; o MadTrace já vem no esqueleto do projeto.
Modo de envio — para o ambiente de teste use shutdown (envia no fim de cada requisição, sem agendador). Em produção, async (lote, sem impacto na latência) — e aí o agendador do Laravel precisa estar rodando.
Publique no ambiente de teste (Teste online → Testar agora) e navegue algumas telas do app. O rodapé do MadTrace passa a ingestão ativa.
Provoque um erro de propósito para ter o que analisar: abra uma tela do app com um filtro inválido, ou acesse uma OS com um id que não existe.
Tela Issues: a falha aparece agrupada por assinatura. Abra a issue e leia:
Ocorrências · últimos 14 dias, com o pico por dia;
o rastreamento com arquivo e linha;
o contexto da requisição e as consultas executadas naquela chamada.
Marque a issue como resolvida depois de corrigir. Se ela voltar a acontecer, o gatilho Regression existe exatamente para isso.
Tela Visão geral · desempenho. O seletor do canto oferece 15 min, 1 hora, 24 horas e 7 dias, e a tela abre em 1 hora — janela sem tráfego mostra tudo em zero, que é o estado do print. Leia os indicadores:
indicador
o que responde
Throughput
requisições por minuto — quanto o sistema está sendo usado
Resposta p95
o tempo que a maioria dos usuários sente, não a média
Tempo em banco
quanto da resposta foi gasto em consulta; se domina, o gargalo é o banco
Taxa de erro
percentual de respostas com erro de servidor — acima de 2% fica vermelho
Apdex
satisfação pelo tempo de resposta, de 0 a 1 — 0,90 ou mais é saudável
Nos blocos Rotas mais lentas e Queries mais custosas, clique em ver requisições para ir direto às chamadas que compõem o número. Sem tráfego na janela os dois ficam em *Nenhuma rota amostrada nesta janela* / *Nenhuma query custosa nesta janela* — não é erro.
Tela Requisições: escolha uma chamada. O painel da direita mostra o trace completo, a divisão do tempo e cada SQL executado. Use só lentas para filtrar.
Repare nas marcações das consultas: N+1, lenta, repetida e duplicada. A marca N+1 é a listagem que dispara uma consulta por linha.
Tela Consultas SQL: a mesma informação organizada pela consulta, com quantas vezes ela roda por requisição.
Tela Filas & jobs: throughput, falhas recentes e Reprocessar. Os agendamentos da aula 23 aparecem aqui como jobs.
Tela Alertas → Nova regra. O banner do topo avisa: *As regras rodam na fila a cada evento ingerido. O envio por e-mail está ativo* — e *O canal webhook chega em breve*. No formulário Nova regra de alerta:
GATILHO: New Issue (primeiro evento de um *fingerprint* novo). Os outros três são Regression (issue resolvida que voltou a receber eventos), Spike (events_count cresce além do limite na janela) e Fatal (qualquer evento com nível fatal, imediato). A seção Gatilhos disponíveis, abaixo do formulário, repete essas descrições;
CANAL: E-mail;
DESTINO: o e-mail que vai receber — use alertas@exemplo.com.br na gravação.
⚠️ Não crie a regra na gravação. Ela passa a disparar e-mail de verdade a cada evento ingerido, e DESTINO é obrigatório. Clique em Cancelar: o painel volta a Nenhuma regra ainda. Quando quiser a regra valendo, é Criar regra — e aí o card ganha o botão de teste que o banner cita, para validar a entrega.
Tela Uso & quotas: Eventos no mês, Rate-limit por chave, Retenção e o Plano. Ao estourar a quota, novos eventos são descartados sem quebrar o app.
Parte 2 — Banco de dados
Rail → grupo Principal → Banco de dados (é outro grupo: o MadTrace fica em Geração & ferramentas, o console SQL não).
Na coluna da esquerda estão as conexões, com o contador no topo (1 CONEXÕES). O Teste online já criou a conexão dele automaticamente — o card mostra Teste Online, SQLite, o selo REST e um botão Testar.
Antes de usar, abra Nova conexão só para conhecer os três modos e feche em seguida:
modo
quando usar
Conexão direta
banco acessível por IP/host/porta — rede interna, homologação
Túnel SSH
banco atrás de firewall: a conexão passa por um servidor intermediário
Driver REST
quando nenhuma porta pode ser aberta — o servidor do cliente roda um comando e você pareia por chave e segredo
Em qualquer um deles, Somente leitura (bloqueia INSERT/UPDATE/DELETE) é a trava recomendada para banco de produção.
Clique na conexão Teste Online. O explorador do meio lista as tabelas do arquivo de banco selecionado — na conexão do Teste Online ele abre com as oito tabelas de framework (cache, jobs, migrations, sessions…), não com as do seu modelo. Use Filtrar tabelas/colunas para navegar nelas.
Como ordem_servico não está nessa lista, escreva a consulta à mão no editor:
SELECT count(*) FROM ordem_servico;
A consulta vale para o banco da conexão, mesmo com a tabela fora do explorador.
Clique em Executar (ou ⌘↵). O canto da barra mostra a contagem de linhas e o tempo (1 linhas · 0 ms), e a aba Resultados traz a linha devolvida.
Abra as três abas do resultado:
Resultados — a grade de linhas;
Mensagens — o que o banco devolveu;
Plano — Gerar plano roda o EXPLAIN real e Analisar com IA lê o plano e aponta onde falta índice ou onde há varredura completa de tabela.
Clique em Salvar, dê o nome OS cadastradas e, se quiser, uma pasta. A consulta passa a aparecer em Consultas salvas (botão Salvas, no topo).
Agora peça em português. Na barra de IA no topo (*Descreva a consulta em português…*), digite:
total faturado por cliente nos últimos 90 dias, do maior para o menor
e clique em → ask. O SQL aparece no editor — leia antes de executar. Ajuste o que estiver errado e rode.
Abra Histórico: toda execução fica registrada, inclusive as que o agente de IA rodou.
Erros comuns
Criei a regra de alerta só para ver e agora chega e-mail
as regras rodam na fila a cada evento ingerido e o canal E-mail já está ativo → apague a regra em Alertas (o card tem Excluir regra); para só demonstrar, preencha o formulário e clique em Cancelar.
O formulário de alerta não deixa criar
DESTINO é obrigatório → preencha um e-mail antes de Criar regra.
Salvei a consulta e o console sumiu
salvar abre a gaveta Consultas salvas por cima do console → feche a gaveta no X; a consulta continua lá, em Salvas.
O explorador de tabelas não mostra as minhas tabelas
o explorador segue o arquivo de banco selecionado, e na conexão do Teste Online ele abre nas tabelas de framework → troque o arquivo no seletor BANCO do topo; a consulta escrita à mão continua valendo para o banco da conexão.
Os indicadores ficam todos em zero e nenhuma rota é amostrada
ou o app não está enviando, ou não houve tráfego na janela escolhida → confirme MADTRACE_ENABLED=true e o DSN no ambiente do app, publique de novo, navegue algumas telas e abra a janela de 24 horas.
A regra de alerta foi criada mas o e-mail não chega
destino errado ou caixa de spam → use o botão de teste no card da regra: ele diz para qual endereço saiu.
Nenhuma conexão aparece em Banco de dados
o ambiente de teste nunca subiu, então a conexão automática não existe → abra Teste online, clique em Testar agora e volte.
O editor SQL recusa um UPDATE
a conexão está marcada como somente leitura → é o comportamento desejado em produção; para o ambiente de teste, desligue a trava na própria conexão, com consciência do que está fazendo.
A IA gerou um SQL que não roda
ela escreve a partir do catálogo de tabelas, e pode errar um nome de coluna → corrija no editor; o SQL fica sempre visível antes de executar.
Checklist de encerramento
O rodapé do MadTrace mostra ingestão ativa para o projeto do curso.
Existe pelo menos uma issue capturada, com rastreamento legível.
Sei dizer o que significam Throughput, Resposta p95, Tempo em banco, Taxa de erro e Apdex.
Sei ler Rotas mais lentas e Queries mais custosas — e que os dois ficam vazios quando não houve tráfego na janela.
Abri uma requisição e vi as consultas SQL dela com o tempo de cada uma.
Montei o formulário Nova regra de alerta (New Issue · E-mail · destino) e saí em Cancelar — o painel continua em Nenhuma regra ainda.
A conexão Teste Online aparece em Banco de dados.
Rodei uma consulta escrita pela IA e conferi o SQL antes de executar.
A consulta OS cadastradas aparece em Consultas salvas.
Duração 18 minPré-requisitosAula 06 · Aula 25requer plano pago
Objetivo
Tirar o sistema do ambiente de teste e colocá-lo no ar de verdade, num endereço público, com banco, HTTPS e backup — sem configurar servidor.
O que você terá no fim
Sem gastar nada:
O assistente Hospede seu app na MadCloud percorrido até o RESUMO DA CONTRATAÇÃO — tipo, plano, região e ciclo escolhidos.
Clareza sobre o que a hospedagem entrega (endereço com HTTPS, banco, backup, métricas) e sobre quanto custa por mês.
Clareza sobre como cancelar e o que acontece com os dados depois.
Após contratar — o resto da aula, que só existe com uma assinatura ativa:
Uma assinatura de hospedagem ativa, contratada de verdade.
O app da Assistec publicado e respondendo num endereço com HTTPS.
As credenciais do administrador do app publicado.
O histórico de publicações, com reversão disponível.
Backup automático confirmado e um backup manual feito à mão.
As variáveis de ambiente entendidas e em sincronia com o projeto.
Custo real.A partir do passo Contratar e pagar, esta aula contrata e paga um plano de hospedagem. A gravação usa o 1 vCPU · 2 GB (o MAIS ESCOLHIDO, R$ 67,00/mês na data da captura) no ciclo Mensal, e cancela ao terminar (a última seção mostra como) — se você só quer ver a tela funcionando, o 1 vCPU · 1 GB sai mais barato. O painel Hospedagem · MadCloud é exclusivo do dono do projeto: convidados veem o item apagado com a dica de que só o proprietário acessa.
Studio › Hospedagem · MadCloud › Visão geral
Hospedagem antes da contratação: o assistente Hospede seu app na MadCloud no passo 1 Tipo, com os dois cartões lado a lado — Hospedagem compartilhada e Servidor privado. A Visão geral com Endereço do app, Uso do período e cartões de recursos só existe depois de contratar
Studio › Hospedagem · MadCloud › Contratação
Passo 3 Pagamento do assistente, com o cartão RESUMO DA CONTRATAÇÃO — Plano 1 vCPU · 2 GB, Região Atlanta (EUA), Cobrança Mensal, Valor R$ 67,00/mês — e os botões Voltar e Contratar e pagar, sem nenhum dado de cartão na tela. É o plano que a aula escolhe; os valores são os da tabela de preços na data do print
Studio › Hospedagem · MadCloud › Publicações
Print em breve — Studio › Hospedagem · MadCloud › Publicações
PENDENTE (só depois de contratar a hospedagem) — Tela Publicações com o histórico de versões e o painel de progresso de uma publicação em andamento, mostrando as etapas e o log ao vivo
Studio › Hospedagem · MadCloud › Domínios
Print em breve — Studio › Hospedagem · MadCloud › Domínios
PENDENTE (só depois de contratar a hospedagem) — Tela Domínios com o subdomínio incluído no topo e o assistente Adicionar domínio próprio mostrando os registros TXT e CNAME
Studio › Hospedagem · MadCloud › Métricas
Print em breve — Studio › Hospedagem · MadCloud › Métricas
PENDENTE (só depois de contratar a hospedagem) — Tela Métricas na janela de 24 horas, com os gráficos de Memória usada, Uso de CPU e Requisições por minuto
Passo a passo
Contratar
Rail → grupo Deploy → Hospedagem · MadCloud. Se o item estiver apagado, você não é o dono deste projeto. Sem hospedagem contratada o painel abre direto no assistente Hospede seu app na MadCloud, com os passos 1 Tipo · 2 Plano · 3 Pagamento — a Visão geral só existe depois de contratar.
Passo Tipo — escolha Hospedagem compartilhada (*ambiente gerenciado e otimizado, ideal pra começar; publicação em minutos, com banco, backups e SSL inclusos*). Servidor privado é máquina dedicada, para app em produção com carga alta.
Passo Plano — são três planos compartilhados: 1 vCPU · 1 GB · R$ 47,00/mês, 1 vCPU · 2 GB · R$ 67,00/mês (marcado MAIS ESCOLHIDO) e 2 vCPU · 2 GB · R$ 97,00/mês, cada um com Memória, CPU, Disco, Banco de dados e Retenção de backup. A aula usa o 1 vCPU · 2 GB, que é o do print; o 1 vCPU · 1 GB serve se você quer só ver a tela pelo menor preço. ⚠️ Não existe botão "Escolher" no cartão: quem seleciona é o clique no cartão inteiro, e o Continuar do rodapé só habilita depois disso. A Região do servidor vem definida como Atlanta (EUA). Em Cobrança, escolha Mensal.
Passo Pagamento — confira o RESUMO DA CONTRATAÇÃO: Plano (1 vCPU · 2 GB), Região (Atlanta (EUA)), Cobrança (Mensal) e Valor (R$ 67,00/mês na data do print). Nenhum dado de cartão aparece nesta tela. Até aqui você não gastou nada — dá para percorrer o assistente inteiro e sair. O passo seguinte é o que cobra.
Clique em Contratar e pagar: o checkout abre numa aba nova. Pare a gravação aqui e retome depois do pagamento — nenhum dado de cartão deve aparecer no vídeo.
Daqui em diante, tudo é "após contratar".As três seções seguintes do passo a passo — e os prints 28-publicacoes, 28-dominios e 28-metricas — só existem com uma assinatura ativa. Sem ela, o painel continua abrindo no assistente, e é normal: nada está errado no seu projeto.
Publicar (após contratar)
De volta ao painel, a tela mostra Aguardando confirmação do pagamento…. Se demorar, clique em Já paguei, verificar.
Confirmado o pagamento, começa Preparando sua hospedagem, com as etapas: configurando endereço, criando banco, criando a máquina, instalando o sistema, aplicando segurança.
Ao terminar, aparece Ambiente pronto — falta a primeira publicação. Clique em Fazer primeira publicação. Essa publicação cria o banco e aplica as migrations, coloca o endereço no ar com HTTPS e gera o login e a senha do administrador.
Acompanhe as etapas (Na fila → Backup do banco → Gerando o pacote → Enviando → Publicando → No ar) e o Log da publicação.
Na Visão geral, copie o Endereço do app e abra Acesso ao sistema para ver Login e Senha. A senha exibida é a inicial: se você trocá-la dentro do sistema, o painel continua mostrando a original.
Clique em Abrir app em nova aba, faça login e navegue pelo menu que você montou na aula 05.
Volte ao painel. A Visão geral resume rede, recursos, cluster do app, banco e backups.
Faça uma alteração qualquer no editor (um rótulo numa tela) e publique de novo: tela Publicações → Publicar.
Publicar atualização envia só o que mudou desde a última publicação;
Publicação completa regenera e envia o projeto inteiro;
Publicar em staging só aparece quando existe ambiente de staging.
Leia Revisar publicação antes de confirmar: arquivos alterados, migrations pendentes e ações destrutivas. Se a publicação remover tabela ou coluna, é preciso marcar Estou ciente de que dados podem ser removidos. — um backup automático é feito antes.
Clique em Publicar agora. Se falhar, a versão anterior continua no ar e o motivo aparece no log.
No histórico, Ver detalhes abre a publicação e Reverter para esta versão volta o código. Atenção: as migrations aplicadas depois não são desfeitas.
Endereço, banco e backups (após contratar)
Tela Domínios. O Subdomínio incluído já responde, sem ajuste de DNS.
Para usar o domínio da empresa: Adicionar domínio → digite os.suaempresa.com.br → Adicionar e gerar instruções. Crie no seu provedor os dois registros mostrados — Registro TXT (prova de posse) e Registro CNAME (apontamento) — e clique em Verificar agora. O certificado é emitido quando o DNS propaga; a verificação continua sozinha.
Tela Banco de dados: nome, tamanho, conexões simultâneas e Credenciais de acesso. O botão Abrir no Database Manager conecta o console SQL da aula 27 a este banco, em modo somente leitura. Acesso externo cria um endereço e um usuário próprios para ferramentas como DBeaver, e nada fica acessível até você autorizar um IP.
Tela Backups: o cabeçalho diz o horário do backup automático e a retenção do plano. Clique em Fazer backup agora → Tudo (banco + arquivos).
Restaurar… substitui os dados atuais pelo backup escolhido e pede o nome do app digitado como confirmação final. Não conclua durante a aula.
Variáveis, métricas e staging (após contratar)
Tela Variáveis de ambiente. São três grupos:
Plataforma — geradas pelo MadBuilder a partir das Propriedades do projeto. Se aparecer desatualizada, clique em Sincronizar agora;
Suas variáveis — integrações e chaves do seu app. Adicionar variável, depois Salvar e aplicar;
Infraestrutura MadCloud — banco e serviços internos, somente leitura.
Aplicar sobe um container novo ao lado do atual e troca o tráfego quando ele responde saudável: sem indisponibilidade.
Tela Métricas: Memória usada, Uso de CPU e Requisições por minuto, nas janelas 24 horas e 7 dias.
Tela Agendador: os agendamentos criados na aula 23 ficam gravados no projeto e entram em vigor na próxima publicação.
Tela Serviços: Versão do PHP, runtime e os processos do container, com Reiniciar por serviço. Mudanças de runtime aplicam na próxima publicação.
Tela Staging → Criar staging. Escolha Banco novo (vazio) ou Cópia da produção. Com ele no ar, o fluxo vira: Publicar em staging → testar → Promover para produção (reaproveita o mesmo código já testado).
Cobrança e cancelamento (após contratar)
Tela Cobrança: Plano atual, Gerenciar pagamento (portal do meio de pagamento), Trocar de plano e Faturas.
Cancelar plano mostra uma linha do tempo com três datas: hoje nada muda; na data do fim do período o app sai do ar e servidor, banco e arquivos são destruídos (com backup final); e alguns dias depois os backups finais são apagados.
Confirme em Cancelar no fim do período. Até a data do desligamento, Retomar plano desfaz o cancelamento.
Se o plano chegar a encerrar, a tela Plano encerrado permite Baixar os backups finais e Reativar plano.
Erros comuns
O item Hospedagem · MadCloud aparece apagado
o painel é exclusivo do dono do projeto → peça ao proprietário, ou trabalhe num projeto seu.
Paguei e a tela continua em "Pagamento pendente"
a confirmação ainda não chegou → clique em Já paguei, verificar; se o pagamento não foi aprovado, o painel diz e permite tentar de novo com outro cartão.
A publicação falha em "construindo a imagem"
erro de build no servidor → abra o Log da publicação, corrija o que ele apontar no editor e publique de novo; o app continua no ar na versão anterior.
A publicação é recusada com aviso de mudanças destrutivas
ela remove tabela ou coluna do banco → revise o modelo; se a remoção for intencional, marque Estou ciente de que dados podem ser removidos.
Mudei uma configuração no projeto e o app hospedado não mudou
as variáveis geradas pela plataforma ficaram desatualizadas → em Variáveis de ambiente, clique em Sincronizar agora.
Checklist de encerramento
Até o RESUMO DA CONTRATAÇÃO, sem gastar nada — é o que a aula cobra de você:
O painel abriu no assistente Hospede seu app na MadCloud, nos passos 1 Tipo · 2 Plano · 3 Pagamento.
Escolhi Hospedagem compartilhada, o plano 1 vCPU · 2 GB e o ciclo Mensal.
O RESUMO DA CONTRATAÇÃO mostra Plano, Região, Cobrança e Valor conferindo com o que escolhi.
Sei o que acontece ao Cancelar plano e em que datas — mesmo sem ter contratado.
Após contratar — não confira nada aqui se você não contratou; nenhum item desta lista é pré-requisito para a aula 29:
A assinatura aparece como ativa em Cobrança.
O Endereço do app responde e abre a tela de login do sistema.
Consegui entrar no app publicado com as credenciais de Acesso ao sistema.
O histórico em Publicações tem pelo menos duas versões e a última está no ar.
Li a tela Revisar publicação e sei o que ela mostra.
O Subdomínio incluído aparece em Domínios.
Backups lista o backup manual que eu fiz.
Variáveis de ambiente mostra as três seções e nenhuma pendência de sincronização.
Métricas desenha os três gráficos na janela de 24 horas.
O plano foi cancelado ao fim da gravação (se este era um projeto de curso).
Duração 20 minPré-requisitosAula 28requer plano Starter
Objetivo
Mostrar os três caminhos de saída do projeto além da MadCloud: publicar num servidor seu por SSH, enviar o código para um repositório Git e baixar o projeto Laravel inteiro — mais como declarar pacotes extras.
O que você terá no fim
O formulário de Servidores de deploy preenchido e a sequência do Teste de conexão entendida.
O passo a passo de preparo do servidor (Como preparar o servidor) aberto e entendido.
O resultado de Verificar requisitos, que é o ponto em que esta aula para.
O caminho claro para concluir a instalação: o guia Instalação em servidor próprio.
O Git Deploy configurado: provedor, GIT URL (SSH), branch, pastas excluídas e chave de deploy.
O projeto Laravel completo baixado em zip.
Um pacote extra declarado em Pacotes Composer, com a versão fixada.
Plano.Baixar projeto, Deploy SSH e Git Deploy exigem plano Starter ou superior. Deploy SSH e Git Deploy também exigem a permissão Deploy no projeto (aula 30). Só Linux: Ubuntu 22.04 / 24.04 / 26.04 ou Debian 12. Windows e macOS não são suportados.
Studio › Servidores de deploy
Painel Servidores de deploy com o formulário de um servidor novo: Identificação, Conexão SSH (host de exemplo 203.0.113.10, porta 22, usuário deploy, método Chave SSH e o aviso da chave pública do builder) e Destino do deploy, com o cartão Teste de conexão · nunca testado à direita
Studio › Deploy SSH › Verificar requisitos
Painel Deploy SSH sem servidor cadastrado: a coluna 0 SERVIDOR, o estado Nenhum servidor selecionado com o botão Gerenciar servidores e o Log do deploy em Aguardando
Studio › Git Deploy
Painel Git Deploy com os provedores (GitHub · Bitbucket EM BREVE · GitLab EM BREVE · SSH/Outro ATUAL), o campo GIT URL (SSH) vazio, Branch main, as pastas excluídas, o bloco SSH DEPLOY KEY sem chave gerada, o cartão O QUE VAI SUBIR em Full Project com 32 arquivos no projeto (31 Pages · 1 Codes) e o Commit & Deploy desabilitado
Studio › Baixar projeto
Rail durante a geração: o item Baixar projeto vira Gerando projeto… com o spinner, enquanto o Studio segue na tela de Início
Studio › Pacotes Composer
Painel Pacotes Composer com Onde o Composer roda, Pacotes padrões, Minhas configurações e Meus pacotes — este último com a busca do Packagist aberta em barryvdh/laravel-dompdf e o rodapé em Alterações pendentes
Passo a passo
Parte 1 — Servidores de deploy
Rail → grupo Deploy → Servidores de deploy → Novo servidor. ⚠️ Os campos não têm rótulo clicável: o que identifica cada um é o *placeholder* (ex: Produção AWS, Homologação, Teste local, ex: deploy.acme.com.br ou 10.0.4.18, 22, deploy, /var/www/erp, 775, www-data).
Identificação:
Nome do servidor: Produção Assistec;
Servidor global: ligue apenas se quiser o mesmo servidor disponível em todos os projetos da conta.
Conexão SSH:
Host ou IP: o endereço do servidor — na gravação use o IP de documentação 203.0.113.10. Dica: colar deploy@203.0.113.10:22 preenche host, usuário e porta de uma vez;
Porta: 22;
Usuário SSH: deploy;
Método de autenticação: Chave SSH (recomendado). A tela mostra a chave pública do builder (ssh-ed25519 …) e o Fingerprint — essa linha precisa estar no ~/.ssh/authorized_keys do usuário no servidor. Ela é pública: pode aparecer no vídeo. Senha também funciona e fica guardada criptografada.
Destino do deploy:
Caminho de instalação: /var/www/erp;
Chmod: 775 — as permissões dos arquivos enviados;
Grupo (chown): www-data.
Clique em Salvar servidor e depois em Executar teste. O Teste de conexão roda, em ordem: Resolução DNS, Abertura porta TCP, Identidade do servidor (host key), Autenticação SSH, Acesso ao diretório e Teste de escrita. A aba Log do servidor mostra os comandos executados.
Em Host key registrado, a identidade do servidor é gravada na primeira conexão. Se ela mudar depois, a plataforma bloqueia e pede Aceitar novo host key — só aceite se você reinstalou o servidor ou trocou as chaves dele.
Parte 2 — Deploy SSH (até a verificação)
Rail → Deploy SSH. Sem servidor salvo, o painel abre com a coluna 0 SERVIDOR, o estado Nenhum servidor selecionado + Gerenciar servidores, e o Log do deploy em Aguardando — é o estado do print. Com um servidor vinculado, o cartão do topo resume host, autenticação, caminho e permissões.
No bloco Requisitos do servidor você vê o que a máquina precisa ter. O preparo em si não é gerado nessa tela: o passo a passo fica em Como preparar o servidor, o link do formulário do servidor (Servidores de deploy).
Siga o guia como root no servidor: ele cobre PHP na versão certa, Apache, extensões e ferramentas, o usuário de deploy, o diretório com as permissões certas e a instalação da chave pública do builder. Com domínio dá para emitir HTTPS válido; sem domínio, o certificado é autoassinado e o navegador avisa uma vez.
De volta ao painel, clique em Verificar requisitos. A verificação roda pela própria conexão SSH e cobre: Sistema operacional (Linux), PHP ≥ 8.4.1 e ferramentas, Permissões do diretório e Raiz web (public/).
O resultado é um de quatro: Servidor pronto, Pronto, com avisos, Faltam requisitos ou Não é Linux.
Esta aula para aqui. O deploy real e o assistente de instalação no navegador estão no guia da Ajuda: clique em Ver guia completo (Ajuda · Instalação em servidor próprio). Ele cobre provisionamento, cadastro, verificação, deploy, conclusão no navegador, fila, agendamentos, HTTPS, backups e solução de problemas.
Só para reconhecer a tela, sem executar: em O que vai subir você escolhe Projeto completo ou Arquivos específicos, escreve uma descrição da release e clica em Fazer deploy. Um backup da instalação atual é criado antes de sobrescrever, o log sai ao vivo e cada release entra no Histórico de deploys.
Parte 3 — Git Deploy
Rail → Git Deploy. São quatro provedores na tira do topo: GitHub, Bitbucket (*EM BREVE*), GitLab (*EM BREVE*) e SSH/Outro (*ATUAL*). O cartão O QUE VAI SUBIR já conta o que existe no projeto antes de qualquer configuração — no projeto do curso, 32 arquivos no projeto · 31 Pages · 1 Codes. É o jeito rápido de ver se o painel está olhando para o projeto certo.
Caminho recomendado: Conectar GitHub. A plataforma instala um aplicativo no GitHub com acesso somente aos repositórios que você escolher — sem chave SSH, sem copiar e colar. Depois use Escolha um repositório e Usar este repo.
SSH DEPLOY KEY: nasce em *Sem chave gerada*; clique em Gerar SSH key e cole a chave pública nas chaves de deploy do repositório, com permissão de escrita;
PASTAS EXCLUÍDAS: já vem com vendor e app/config; acrescente o que não deve subir;
Salvar config.
Em O QUE VAI SUBIR, escolha Full Project (ou Select Files, com o buscador de arquivos).
Escreva a mensagem em COMMIT & DEPLOY — ela é obrigatória e tem limite de 500 caracteres. Exemplo: feat(os): cadastro, kanban e relatório de faturamento.
Clique em Fazer Deploy — ele fica desabilitado enquanto faltar a GIT URL (SSH). O aviso Sobrescrever branch remota? é literal: mudanças feitas no remoto fora do MadBuilder são perdidas. Confirme e acompanhe o log.
Parte 4 — Baixar projeto
No rodapé do rail, clique em Baixar projeto. O item do rail troca de rótulo inteiro para Gerando projeto…, com o *spinner*, e o zip começa a baixar. O aviso vem primeiro, o download depois.
O pacote é o projeto Laravel inteiro: esqueleto, models por tabela, migrations, páginas e códigos, menu, seeds de permissão e perfil, rotas, agendamentos, temas e o .env.
Se a geração incluir remoção de tabela ou coluna, o download é bloqueado e uma tela lista exatamente o que seria apagado. Revise o modelo antes de liberar.
O download é retomável: se a conexão cair no meio, ele continua de onde parou.
Parte 5 — Pacotes Composer
Rail → Pacotes Composer. Leia Onde o Composer roda: esta tela salva a lista de pacotes; a instalação acontece ao publicar (deploy, hospedagem, Teste Online).
Pacotes padrões lista o que todo projeto MadBuilder já traz. É somente leitura.
Meus pacotes → Adicionar pacote. ⚠️ O campo de busca só aparece depois desse clique. Digite barryvdh/laravel-dompdf: o autocomplete busca no Packagist real. Escolha a versão em Escolher versão. Sempre fixe a versão — sem ela, o Composer instala a mais recente compatível, e isso muda com o tempo.
Minhas configurações → Adicionar configuração. Os presets cobrem repositórios (Repositório VCS (Git/SVN)), autenticação (Token GitHub (OAuth)), plataforma (Travar versão do PHP), autoloader (Otimizar autoloader) e mais. Cada preset mostra a Linha gerada antes de você confirmar.
Com o pacote na lista, o rodapé passa a Alterações pendentes. Clique em Salvar e gerar vendor — o Log do composer aparece na tela e a geração pode levar alguns minutos. Descartar desfaz a pendência e devolve a lista ao que era, que é a saída para quem só estava demonstrando.
Erros comuns
Cadastrei o servidor e o Testar conexão falha
falta colar a chave pública do builder no ~/.ssh/authorized_keys do usuário SSH do servidor → o próprio formulário mostra a linha e o botão copiar; sem isso a autenticação por chave falha.
O Deploy SSH diz "Nenhum servidor selecionado"
não há servidor cadastrado ainda → clique em Gerenciar servidores (ou no item Servidores de deploy do rail) e cadastre um antes.
O botão Fazer Deploy do Git está apagado
falta a GIT URL (SSH) → preencha a URL, gere a SSH DEPLOY KEY e cole a chave pública nos *Deploy keys* do repositório com permissão write.
Declarei o pacote e ele não apareceu no projeto
esta tela salva a lista, não instala: o composer roda ao publicar (deploy/hospedagem) ou no Teste Online → publique depois de Salvar.
O teste de conexão falha em "Autenticação SSH"
a chave pública do builder não está no authorized_keys do usuário, ou o usuário não existe → siga Como preparar o servidor (o preparo instala a chave) ou cole a linha à mão.
Falha em "Identidade do servidor (host key)"
com aviso de mudança → o servidor apresentou outra chave → confira no servidor com ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub; só aceite se você mesmo reinstalou a máquina.
Verificar requisitos reprova o PHP
a versão instalada é menor que 8.4.1, ou é build 32-bit → refaça o preparo por Como preparar o servidor; ele fixa a versão certa como PHP padrão do sistema.
O download do projeto não começa e aparece um aviso de mudanças destrutivas
a geração removeria tabela ou coluna → revise o modelo de dados; a tela lista nome por nome o que seria apagado.
Adicionei um pacote e o app publicado continua sem ele
a lista foi salva, mas a instalação acontece na publicação → publique de novo, ou use Salvar e gerar vendor.
Checklist de encerramento
O servidor Produção Assistec aparece em Servidores de deploy.
Sei a sequência do Teste de conexão: DNS, porta, host key, autenticação, diretório e escrita.
Abri Como preparar o servidor e sei o que o preparo instala na máquina.
Verificar requisitos devolveu Servidor pronto (ou sei exatamente o que falta).
Abri o guia Instalação em servidor próprio e sei onde retomar a instalação real.
O Git Deploy está configurado: provedor, GIT URL (SSH), BRANCH, PASTAS EXCLUÍDAS e SSH DEPLOY KEY.
O zip do projeto foi baixado e abre com a estrutura Laravel completa.
Em Pacotes Composer, o pacote extra aparece em Meus pacotes com versão fixada.
Sei que Pacotes Composer salva a lista e que quem instala é a publicação.
Trazer outras pessoas para o projeto com o poder exato que cada uma precisa, acompanhar o que foi feito e conhecer as telas de conta, planos e suporte.
O que você terá no fim
O convite de Compartilhar projeto preenchido, com o preset escolhido e as permissões ajustadas à mão.
Entendimento dos nove interruptores de permissão, um a um.
A leitura do painel Estatísticas e atividades: quem acessou, quem editou o quê e quando.
As preferências de IA da conta configuradas (chave própria ou plano).
Noção de onde ficam licenças, Coding Plan e complementos na Loja.
O caminho oficial para reportar um bug, com contexto técnico.
Quem acessa o quê.Compartilhar projeto e Hospedagem · MadCloud são exclusivos do dono do projeto. Estatísticas e atividades abre para todos os membros. Preferências da Conta e Loja são da sua conta, não do projeto.
Studio › Compartilhar projeto
Modal Compartilhar projeto com o e-mail de exemplo digitado, o seletor Convidar como em Dev, as nove colunas de permissão (Download, Código, Deploy, Snapshot, Clonar, Editar, Agente, Schema, Migrations) e o estado Ninguém compartilha esse projeto ainda
Studio › Estatísticas e atividades
Estatísticas e atividades na aba Dashboard, com os quatro indicadores da semana (Acessos, Edições, Arquivos editados, Deploys), o gráfico Atividade na semana e a rosca Recursos do projeto
Studio › Preferências da Conta
Aba Preferências da Conta na seção Provedores de IA: os provedores do Mad Coding Plan com Chave da plataforma inclusa e o consumo da franquia, e os provedores BYOK com o selo sem chave e o botão Adicionar chave — nenhuma chave visível
Manager › Loja
Loja do manager na aba Todos, com as categorias à esquerda (Licenças, Aplicativos, App Generator, Suporte) e a grade de licenças com preço e botão Adicionar
Passo a passo
Parte 1 — Compartilhar projeto
No rodapé do rail, clique em Compartilhar projeto. Se o item estiver apagado, você não é o dono deste projeto.
Em Email da pessoa, digite o e-mail do convidado (nos prints do curso, colega@exemplo.com.br). Em Convidar como, escolha o preset — ele abre em Dev, com a explicação *Edita projeto, clona, usa agente. Sem deploy nem schema.* logo abaixo:
papel
o que libera
Visitante
só baixar o projeto — sem edição, sem deploy
Dev
editar o projeto, clonar e usar o agente — sem deploy e sem schema
Editor
tudo de Dev, mais alterar o schema — sem deploy e sem migrations
Admin
acesso total, incluindo deploy e aplicar migrations em produção
Clique em Adicionar: a pessoa entra na tabela com Membro e Role. Enquanto ninguém foi convidado, a tabela mostra *Ninguém compartilha esse projeto ainda* — foi assim que o print desta aula foi tirado. ⚠️ O convite sai na hora: só clique em Adicionar se for convidar alguém de verdade.
Ajuste os nove interruptores de permissão na linha dela. Mexer em qualquer um transforma o papel em Custom:
permissão
o que a pessoa passa a poder fazer
Download do projeto
baixar o projeto inteiro como pacote
Download / edição do código de páginas
baixar e editar os arquivos de código das páginas
Deploy
publicar o projeto em servidores
Snapshot
criar e restaurar snapshots do projeto
Clonar
duplicar o projeto para outro workspace
Editar página visual e arquivo de código
editar o conteúdo pelo editor visual
Usar agente IA
disparar o agente de IA
Editar schema do banco
criar, alterar ou remover tabelas e colunas no modelo de dados
Aplicar migrations
aplicar migrations em produção — mudanças irreversíveis
Para o técnico da equipe, deixe ligados Editar página visual e arquivo de código, Usar agente IA e Download do projeto; deixe desligados Deploy, Editar schema do banco e Aplicar migrations.
Clique em Salvar alterações — ele fica desabilitado enquanto não houver nada pendente. Aplicar preset volta a linha para um dos quatro papéis prontos.
Remover acesso pede um segundo clique para confirmar.
Quem entra sem a permissão Usar agente IA vê a tela do agente bloqueada, com a explicação de pedir liberação em Compartilhar projeto.
Parte 2 — Estatísticas e atividades
No rodapé do rail, clique em Estatísticas e atividades.
Aba Dashboard — os quatro indicadores da semana: Acessos, Edições, Arquivos editados e Deploys.
Gráficos:
Atividade na semana — acessos, edições e deploys por dia;
Recursos do projeto — a rosca da composição atual: Tabelas, Páginas, Códigos e Modelos. O cartão Visão geral, ao lado, repete os números e acrescenta Customizados;
Ações na semana — Downloads, Deploys, Git deploys e Test online deploys.
Use Anterior e Próxima para andar entre semanas e comparar.
Aba Atividades do projeto — acessos por desenvolvedor. Aba Atividades de edição — Total de arquivos únicos editados, Total de edições e Últimos arquivos alterados.
Clique num desenvolvedor: a gaveta Arquivos editados abre com Arquivo, Tipo, Ação e Quando.
Parte 3 — Conta, preferências e planos
No rail global (a borda mais externa), clique no avatar. O menu Minha conta tem Conta, Preferências, o tema e Sair.
Conta abre o manager: dados pessoais, senha e verificação em duas etapas.
Preferências abre a aba Preferências da Conta, dentro do Studio. Ela vale para todos os seus projetos.
Em Provedores de IA, cada provedor tem:
Chave de API — Adicionar chave, Trocar ou Remover. A chave fica na sua conta (BYOK);
Modelo padrão — a lista carrega depois que a chave é salva. Digitar outro modelo aceita um id manual.
Quem não quer gerenciar chave usa o Mad Coding Plan: assinatura mensal com franquia de tokens e chave da plataforma inclusa. O painel mostra a janela semanal e o ritmo das últimas horas.
Lembre da diferença: aqui é a conta; o modelo de cada área do projeto fica em Propriedades do projeto → Modelos de IA (aula 25).
Rail global → Loja. Ela abre o marketplace da conta, no manager: Licenças, Aplicativos, App Generator e Suporte na coluna da esquerda, e os filtros do topo (Todos, Licenças, Coding Plan, Promoções…) sobre os cards com preço e Adicionar. Todo item apagado no rail do Studio aponta para cá.
Rail global → Novidades: o que mudou na plataforma e no Mad Framework, em duas abas.
Parte 4 — Reportar bug
Rail global → Reportar bug. O painel tem Novo report e Meus reports.
Em Onde acontece, escolha Editor / Studio ou App gerado (framework).
Preencha Título (uma frase) e Descrição (o que você fez, o que esperava e o que aconteceu). Cole um print com Ctrl+V direto na descrição — a imagem pode ser anotada com retângulo, texto e seta.
Deixe Enviar contexto técnico junto ligado. Vão só metadados: Projeto, Página, Mad Framework, Sessão do agente, Navegador e Endereço. Nada do que você escreveu no canvas, no código ou no chat é enviado.
Clique em Enviar report. O painel devolve Abrir o tópico no fórum. ⚠️ O report abre um tópico de verdade no fórum da comunidade — envie quando tiver um bug real para relatar, não só para ver a tela.
Acompanhe pela aba Meus reports, com os estados Aberto, Respondido e Fechado.
Erros comuns
Digitei o e-mail e o convidado não apareceu na lista
preencher o campo não convida ninguém; é preciso clicar em Adicionar (e depois Salvar alterações) → confira a lista de membros abaixo do formulário.
A Loja não mostra "planos"
o item Loja do rail global abre o marketplace (licenças, aplicativos, App Generator, suporte) → os planos do builder ficam nos filtros Licenças e Coding Plan, no topo da grade.
Compartilhar projeto aparece apagado
o painel é exclusivo do dono → peça ao proprietário do projeto.
Convidei alguém e a pessoa diz que não consegue editar
o preset Visitante só permite baixar → troque para Dev ou ligue Editar página visual e arquivo de código.
O convidado abre o agente e vê "Agente IA desabilitado pra você neste projeto"
falta a permissão Usar agente IA → ligue o interruptor e salve.
Salvei a chave de API e a lista de modelos continua vazia
a chave foi recusada pelo provedor, ou o provedor está fora do ar → confira a chave e clique em Tentar de novo.
Um item continua bloqueado depois de trocar de plano
o plano da conta é lido em cache → recarregue o Studio.
Checklist de encerramento
Sei convidar alguém em Compartilhar projeto e o que o botão Adicionar dispara.
Sei o que cada um dos nove interruptores libera.
Sei quais interruptores deixar desligados para um técnico da equipe: Deploy, Editar schema do banco e Aplicar migrations.
Estatísticas e atividades mostra os quatro indicadores da semana.
Consegui abrir a gaveta de arquivos editados de um desenvolvedor.
Preferências da Conta abre e eu sei onde ficam a chave e o modelo padrão.
Sei a diferença entre o modelo da conta e o modelo por área do projeto.
Abri a Loja e localizei os filtros Licenças e Coding Plan.
Sei montar um report com contexto técnico e onde acompanhar a resposta (Meus reports).
Duração 20 minTiposformulario/livrePré-requisitosAula 13 · Aula 17 · Aula 25 · Aula 30Tabelasordem_servicoos_apontamentotecnicorequer plano Pro IA
Objetivo
Usar o agente de inteligência artificial para construir uma tela nova e ajustar uma existente, e delegar trabalho a ele pelo quadro de tarefas — sempre com você aprovando o que entra.
O que você terá no fim
Uma página Painel do técnico criada pelo agente a partir de um pedido em português — e o critério para dizer se a entrega fechou ou não.
O hábito de revisar o Plan, testar no app e pedir o ajuste: o agente pode entregar outra coisa (na gravação ele fez um formulário com gaveta em vez de uma página livre) e quem valida é você.
Um turno em modo Plan, aprovado por você antes de o agente aplicar qualquer coisa.
Um ajuste no formulário de OS pedido por prompt e aplicado depois de ler o diff.
Uma regra e uma skill cadastradas, e as memórias revisadas — o que molda o agente neste projeto.
Duas tarefas no backlog, uma do agente e uma de gente, com revisão humana antes de concluir.
Clareza sobre quanto isso custa e onde o custo aparece.
Plano.O Agente de IA exige plano Pro IA. Dependendo do plano, ele usa a franquia do Mad Coding Plan ou a sua própria chave de API, configurada em Preferências da Conta (aula 30). Convidado sem a permissão Usar agente IA (aula 30) vê a tela bloqueada.
Studio › Agente
Aba Agente com o prompt do Painel do técnico escrito no composer, o seletor de modelo (MAD MiniMax M3), o Esforço: padrão e os modos Normal · Plan · Roadmap · Auto — antes de enviar
Studio › Agente
Sessão do agente em modo Plan: o chat com as respostas e o contador de ferramentas do turno, a aba Plano (N) no painel direito e o preview LIVE da página em construção. O nome do arquivo diz roadmap por histórico — o modo que a tela chama de Plan é este, e Roadmap é o modo vizinho, que a aula não usa
Studio › Agente › Preview
Painel direito na aba Preview com o selo LIVE e o aviso Agente está aplicando alterações…, mostrando a página aberta no preview (Quadro de OS) enquanto o chat relata o passo atual e o contador de ferramentas do turno
Studio › Agente › Diff
Painel direito na aba Diff no estado vazio — Nenhum diff ainda. Envie um pedido de mudança e o agente devolve um diff aqui —, com a sessão retomável no chat
Studio › Tarefas
Quadro Tarefas com a barra Frota do agente (0 executando · 0 na fila), as colunas Backlog · Em andamento · Em revisão · Concluído e duas tarefas novas no Backlog — uma de Agente e uma de Humano, nenhuma iniciada
Passo a passo
Parte 1 — O agente cria a página livre
Pressione Ctrl+K (ou abra a aba Agente).
No rodapé do composer, ajuste três controles:
Modelo — quem vai responder (na gravação, MAD MiniMax M3);
modo — a fileira Normal · Plan · Roadmap · Auto. Escolha Plan: ele propõe as ações e espera você aprovar, enquanto o Normal executa direto, sem etapa de aprovação (Roadmap planeja uma feature inteira em tarefas e Auto decide o modo sozinho — não são o assunto desta aula). Com Plan ligado, a barra de status passa a mostrar *plan-mode ativo · revisar tudo*;
Esforço de raciocínio — o botão mostra Esforço: padrão. Um esforço menor responde rápido e barato; um maior pensa mais antes de agir, custa mais e demora mais.
Escreva o pedido no composer:
Crie uma página livre chamada Painel do técnico com as OS do dia do
técnico logado, as horas apontadas na semana e um atalho para o Kanban de OS.
Digite @ para mencionar ordem_servico, os_apontamento e a página do Kanban — a menção dá contexto exato e reduz idas e vindas.
Clique em Enviar. O agente lê o projeto antes de escrever: as ferramentas chamadas aparecem na conversa, uma a uma, com argumento e resultado.
Se aparecer O agente precisa de uma decisão, responda. Pular faz o agente seguir com a premissa mais conservadora, sem esperar.
Em Plan, o agente lista o que pretende fazer e espera: clique em Aprovar e executar (N) para liberar. Leia a lista antes de aprovar — é aqui que você vê se ele entendeu o pedido (tipo de página, tabela, onde a tela vai aparecer). Se a lista não for a que você quer, use Parar no cabeçalho da sessão e reescreva o pedido: sai mais barato do que deixar executar e desfazer depois.
Enquanto ele trabalha, use o painel da direita:
aba
o que mostra
Preview
a página aberta, atualizada em tempo real, com o selo LIVE e o aviso *Agente está aplicando alterações…*
Teste Online
o app publicado, para conferir o efeito de verdade
Arquivos
o que foi criado, editado, movido ou apagado nesta sessão
Plano
os passos, em ordem, com o que já concluiu
Diff
as alterações propostas, linha a linha
Modelo
as mudanças no modelo de dados desta sessão
Arquivos e Plano trazem o número de itens no próprio nome da aba. No corpo do chat, cada turno mostra o contador de ferramentas (16 ferramentas ✓12 ⊘4) e a faixa Snapshot capturado, cujo Reverter *restaura IR, PHP, seleção e aba ativa do início do run* — e vale enquanto você não salvar.
Quando o agente for executar algo sensível, ele para em Aprovação necessária com a lista de ações. Marque o que autoriza e clique em Aprovar e executar. Auto-aprovar similares dispensa a próxima aprovação do mesmo tipo — use com critério.
Ao terminar o turno, confira o que realmente ficou de pé na aba Preview, no app (Teste online) e no chat: as OS do dia filtradas pelo técnico logado, o total de horas apontadas na semana e o botão para o Kanban. ⚠️ O agente pode não validar a própria entrega — e essa checagem é sua. Na sessão gravada ele respondeu *"A execução terminou com falha e a entrega não foi validada. A página ainda está como stub CRUD no servidor"*, e o que ficou no projeto foi um formulário com gaveta (tipo formulario, wrapper drawer de 600 px) com um <mad-form> vazio — não a página livre de tela cheia que o pedido descrevia. Nada na tela acusa isso: quem descobre é quem abre e testa. O ciclo é sempre o mesmo: revise o Plan → teste no app → peça o ajuste, agora fatiado (uma coisa por vez: primeiro a lista de OS do dia, depois o total de horas, depois o atalho), conferindo o preview a cada passo.
Clique em Abrir no editor visual. A página é uma página comum, editável como qualquer outra do curso — se o agente parou no meio, ou entregou o tipo errado, é aqui que você conserta à mão (o wrapper drawer, por exemplo, sai nas Propriedades da página).
Parte 2 — O agente ajusta o que já existe
Volte ao chat e peça a mudança, mencionando a página com @:
No @OrdemServicoForm, deixe o campo de descrição do problema obrigatório
e mostre o total de horas apontadas ao lado do valor total.
O agente devolve a alteração na aba Diff: linhas adicionadas em verde, removidas em vermelho. Enquanto não há pedido de mudança em arquivo existente, ela fica no estado vazio — *Nenhum diff ainda. Envie um pedido de mudança e o agente devolve um diff aqui.*
Leia o diff antes de aplicar. Clique em Aplicar se estiver correto, ou Recusar e explique no chat o que ficou errado — ele corrige na mesma sessão, com o contexto inteiro.
Abra a página no editor visual e confira. Publique com Testar agora no Teste online.
Parte 3 — Regras, Skills e Memórias
No menu do agente (Mais opções), clique em Regras, skills e memórias. Também dá para chegar por Propriedades do projeto → Agente IA.
Aba Regras → Nova regra. Regras ativas entram em todas as conversas do agente neste projeto. Use para o que nunca muda:
Nome: Padrões da Assistec;
Conteúdo (markdown): por exemplo, "Toda listagem nova deve ter filtro por status e por técnico." e "Valores monetários sempre com duas casas decimais.";
deixe Ativa marcada.
Aba Skills → Nova skill. Skills são instruções sob demanda: o agente ativa quando o contexto combina com a descrição.
Nome (slug): relatorio-os;
Descrição (gatilho de ativação): "Quando o pedido envolver relatório ou exportação de ordens de serviço";
Conteúdo (markdown): o passo a passo que você quer que ele siga;
Escopo: Projeto.
Aba Memórias: o que o agente memorizou sobre este projeto. Corrija ou apague o que estiver errado — ele continua usando enquanto estiver ali. EscopoGlobal só para conhecimento que vale em todos os seus projetos.
Parte 4 — Tarefas e frota de agentes
Abra a aba Tarefas. O quadro tem quatro colunas — Backlog, Em andamento, Em revisão e Concluído — e os filtros Todos · Humanos · Agente no topo.
Clique em Nova tarefa e crie a primeira, que é a continuação natural do que o agente deixou pela metade:
título (*O que precisa ser feito?*): Painel do técnico — revisar layout e permissões;
Quem executa? → Agente (*IA executa autonomamente*);
Prioridade: P2;
Módulo e Vence em, se quiser situar a tarefa no tempo e no lugar.
Clique em Salvar no backlog (*Cria a tarefa em Backlog*). O outro botão, Criar e iniciar agente, dispara o agente na hora — e consome franquia na hora; deixe-o para quando o pedido estiver redondo.
Crie a segunda, agora para gente: Validar com a equipe de campo o que falta no painel, com Quem executa? → Humano (*Pessoa do time*). O quadro serve para os dois.
As duas ficam em Backlog. A barra Frota do agente mostra 0 executando · 0 na fila, e Executar backlog (1) conta só a tarefa de agente — a de humano não entra na fila.
Quando uma tarefa de agente roda e termina, ela vai para Em revisão — o agente nunca marca como concluída sozinho: quem lê o que mudou, testa no Teste online e conclui é você.
Planejar com IA faz o caminho inverso: você descreve a feature, a IA decompõe em tarefas e você revisa antes de criar no backlog.
Sobre o custo.Tudo nesta aula consome tokens — a franquia do Mad Coding Plan ou a sua chave própria (BYOK). O rodapé do composer mostra o consumo da janela semanal; o contador na barra de status abre o Histórico de uso da LLM, com modelo, tokens de entrada e saída e a duração de cada chamada. Três coisas reduzem a conta: esforço menor quando a tarefa é simples, menção com @ em vez de explicação longa, e um pedido específico em vez de dez idas e vindas no chat.
Erros comuns
Recarreguei a aba e o agente parou no meio
a sessão fica com *O agente foi interrompido antes de terminar* e um botão Continuar → clique em Continuar: ele retoma de onde parou, sem refazer o que já aplicou. Reenviar o prompt recomeça tudo (e cobra de novo).
O composer não aceita digitação
enquanto o agente trabalha, o campo fica indisponível → espere o turno terminar (ou clique em Parar) antes de escrever o próximo pedido.
Não apareceu nenhum plano para aprovar
em modo Normal o agente executa direto → ligue Plan no composer antes de enviar e ele passa a propor as ações com Aprovar e executar (N).
A aba Diff está vazia
ela só enche quando o agente devolve mudança em arquivo existente; página nova aparece em Arquivos e no Preview.
O agente diz que a entrega não foi validada e a página está vazia
a reescrita dele foi rejeitada na validação e o estado real continua o anterior → refaça o pedido fatiado (uma coisa por vez), conferindo o preview a cada passo, ou termine a página à mão no editor visual.
A página existe, mas não é o que eu pedi (abriu como gaveta, ou é um formulário CRUD)
o agente escolheu outro tipo de página e nada avisa isso → abra a página no editor visual, ajuste o tipo e o wrapper nas Propriedades da página, ou peça a correção nomeando o que está errado ("a página tem que ser de tela cheia, sem gaveta"). Ler o Plan antes de aprovar evita esse retrabalho.
A aba do agente abre bloqueada
o plano não cobre o Agente de IA, ou falta a permissão Usar agente IA neste projeto → confira na Loja e em Compartilhar projeto.
Aparece um aviso pedindo para configurar a chave de API
o plano usa chave própria → cadastre em Preferências da Conta (aula 30) e tente de novo.
O agente diz que a franquia semanal acabou
a cota de tokens do plano zerou até a virada da semana → espere a renovação, troque de tier ou use a sua própria chave.
O agente parou no meio dizendo que uma ação foi bloqueada
uma proteção cortou uma repetição sem progresso → leia o motivo no aviso e responda com uma orientação nova, em vez de mandar "continua".
Apliquei o diff e a página quebrou
o diff foi aplicado sem leitura → use o Histórico de versões da página para voltar, e peça a correção descrevendo o sintoma.
Checklist de encerramento
A página Painel do técnico existe e abre no editor visual.
Conferi no preview e no app o que o agente realmente entregou — inclusive se o tipo de página é o que eu pedi — e sei o que falta terminar à mão.
Li o Plan e aprovei o turno antes de o agente aplicar qualquer coisa.
Sei o ciclo quando a entrega não fecha: revisar o Plan, testar no app, pedir o ajuste fatiado.
Sei ler um diff antes de aplicar e onde ficam Aplicar e Recusar.
Existe uma Regra ativa no projeto.
Existe uma Skill com descrição de gatilho.
Revisei as Memórias e apaguei o que estava errado.
Duas tarefas estão no Backlog — uma de Agente, uma de Humano — e a Frota do agente mostra 0 executando · 0 na fila.
Sei onde ver o consumo de tokens e o que aumenta a conta.
O sistema de Ordem de Serviço está completo e publicado.