Use when asked to review, audit, or check an Angular feature module/branch in the DemarcoDS / Bancodoc.Interface project — both (A) for design-system consistency in the code (toasts incl. "salvo" form vs "inserido/editado" inline, modals incl. danger removal variant, button/action naming, form validation & duplicate-name messages, tables, breadcrumbs, form layout, clickable-count→modal links, tooltip-vs-alert), and (B) verifying the change in the running app (stale bundles, change-detection zones, mock-API signals). Reports what is out of standard with the correct pattern and the fix. Triggers on "está no padrão?", "fora do padrão", "padroniza", "revisa o módulo", "review em funcionamento/componentes".
Resources
1Install
npx skillscat add paulovictordsz/review-skill Install via the SkillsCat registry.
DemarcoDS Review (review-skill)
Overview
One skill, two complementary jobs on the same feature module/branch:
- Part A — Audit DS compliance (code): check a feature module against the project's Design System and reference screens, reporting every deviation as
fora do padrão→o padrão é→a correção(withfile:lineand severity). - Part B — Verify in the running app: confirm a UI change actually landed and behaves correctly in a running hot-reload dev server (often with a mock API like MSW), seeing past stale bundles, change-detection traps, and misleading mock-API signals.
Use Part A to find/fix deviations; use Part B to prove the fix is real in the browser. This skill is DemarcoDS / Bancodoc.Interface (Angular/Nx) specific.
Core principle (A): The standard is whatever libs/ui (the DS) and the mature reference apps already do — apps/empresas (clientes) and apps/servicos (documentos). The audited module must match them, not reinvent its own components.
Core principle (B): Confirm the build, drive with trusted events, and read framework state — not just the DOM.
Part A — Auditing Design-System Compliance
How to run the audit
- Anchor the standard: read the relevant DS component in
libs/uiand howempresas/servicosuse it. Never infer the standard from the module under review. - Scan each dimension below (grep the anti-patterns), across the module's pages, forms, shared dialogs.
- Report a deviation table:
arquivo:linha · o que faz · o padrão · a correção · severidade. Group by dimension. Confirm what IS compliant too. - Optionally verify fixes in the running app — see Part B below.
The standards (what to check)
1. Toasts
- Sucesso: título
"[Entidade] [ação no particípio]!", sem subtítulo (a versão reduzida do toast é só título — omitirmessage). - Erro: título
"Não foi possível [ação] o [item]"+ subtítulo com o erro real. - Info/bloqueio:
"Sem permissão"/ título curto + subtítulo com o motivo. - 🚩 Anti-padrões: títulos genéricos
"Sucesso"/"Erro"; mensagens"...com sucesso"; feedback de campo obrigatório em toast (deve ser helper text no campo). - Grep:
title: 'Sucesso',title: 'Erro',com sucesso.
Vocabulário do toast de sucesso por comportamento
O particípio segue a ação e concorda em gênero com a entidade. Sempre termina com "!".
Distinção crítica do toast de "Salvar" — depende de criar vs. editar:
- Salvar EDIÇÃO de uma entidade já cadastrada (tela dedicada de editar/gerenciar, cadastro já concluído; botão "Salvar"/"Salvar alterações" no modo gerenciamento) → SEMPRE
Alterações salvas!(texto fixo, genérico — NÃO a entidade). Este é o padrão para salvar edições.- Criar uma entidade nova (cadastro/wizard concluído com sucesso) →
[Entidade] cadastrado!(ex.:Fornecedor cadastrado!,Cliente cadastrado!).- Form unificado create+edit numa única tela (a mesma tela faz os dois, sem separação clara) →
[Entidade] salvo!(ex.:Grupo salvo!,Perfil salvo!,Usuário salvo!). Referência:usuarios.- CRUD inline em grid/lista (adicionar/editar/renomear um item dentro de uma tabela, sem sair da tela) →
inserido!/editado!/renomeado!. Referência:servicos(categorias, fundamentos).
🚩 Numa tela de gerenciar/editar registro existente, NÃO use[Entidade] salvo!nemeditado!— o correto éAlterações salvas!.
| Comportamento | Toast | Exemplos reais |
|---|---|---|
| Salvar edição (tela gerenciar/editar) | Alterações salvas! (fixo) |
"Alterações salvas!" (gerenciar fornecedor/cliente) |
| Criar entidade (cadastro novo) | [Entidade] cadastrado/a! |
"Fornecedor cadastrado!", "Cliente cadastrado!" |
| Salvar entidade (form unificado create+edit) | salvo / salva |
"Grupo salvo!", "Perfil salvo!", "Usuário salvo!" |
| Inserção inline (grid) | inserido / inserida |
"Tipo de documento inserido!", "Orientação geral inserida!", "Categoria de dívida inserida!", "Cargo para análise inserido!" |
| Edição | editado / editada |
"Tipo de critério editado!", "Categoria de motivo editada!", "Fundamento de identificação editado!" |
| Exclusão | excluído / excluída |
"Tipo de uso excluído!", "Justificativa de motivo excluída!", "Cargo para análise excluído!" |
| Renomeação | renomeado / renomeada |
"Tipo de orientação renomeado!", "Categoria de procedimento renomeada!" |
| Vinculação | vinculado / vinculada (+ plural) |
"Pesquisa vinculada!", "Pesquisas vinculadas!", "Regra vinculada!", "Regras vinculadas!" |
| Exportação | (entidade fixa) | "Arquivo exportado!" |
Regras de redação:
- Concordância de gênero com o substantivo da entidade: Categoria… →
inserida/editada/excluída/renomeada; Tipo…/Fundamento…/Momento…/Cargo… →inserido/editado/excluído/renomeado. - Plural quando a ação atinge vários itens:
Pesquisas vinculadas!,Regras vinculadas!. - 🚩 Anti-padrões adicionais: usar
cadastrado/salvogenérico quando o comportamento é inserir/editar/renomear; faltar o!final; gênero trocado ("Categoria … inserido!"); plural errado.
Como auditar toasts de sucesso (passo a passo)
Não basta olhar o texto isolado — o particípio correto depende da ação que o handler executa. Para cada toast:
- Encontre todos os toasts de sucesso. Grep:
toastService.success((e variações.success({). Audite cada ocorrência, não só as que "parecem erradas". - Classifique o comportamento pela ação do handler (leia o método que chama o toast — não adivinhe pelo texto atual):
- salvar EDIÇÃO numa tela de gerenciar/editar registro existente (rota
/:id, modo edição, PATCH/update) →Alterações salvas!(fixo) - criar entidade nova (cadastro/wizard, POST/create) →
[Entidade] cadastrado! - save de form unificado create+edit (uma tela só faz os dois) →
[Entidade] salvo! - cria registro novo inline em grid (
inserir…numa tabela, sem sair da tela) → inserção →inserido/inserida - atualiza registro existente (PUT/PATCH /
update…/editar…) → edição →editado/editada - altera só o nome (rename inline /
renomear…) → renomeação →renomeado/renomeada - associa itens entre entidades (vincular /
vincular…/link…) → vinculação →vinculado/vinculada - remove registro (DELETE /
excluir…/remover…) → exclusão →excluído/excluída - gera/baixa arquivo (export / download) → exportação →
Arquivo exportado!
- salvar EDIÇÃO numa tela de gerenciar/editar registro existente (rota
- Determine o gênero do substantivo-núcleo da entidade (o primeiro substantivo do nome, ignorando complementos): Categoria de dívida → feminino (
a); Tipo de documento → masculino (o); Orientação geral → feminino; Fundamento de identificação → masculino. Concorde o particípio com esse núcleo. - Decida singular vs. plural pela quantidade que a ação atinge: uma → singular; várias → plural (
Pesquisas vinculadas!). - Monte o título
"[Entidade] [particípio]!"e compare com o atual. Reporte como desvio se: verbo errado para o comportamento (ex.:salvo/cadastradonuma edição), gênero/plural trocado, ou falta o!.
⚠️ Conflito comum: numa tela de gerenciar/editar registro existente, o save é
Alterações salvas!(não[Entidade] salvo!nemeditado!).[Entidade] cadastrado!é só para criação;inserido!/editado!/renomeado!só para CRUD inline em grid (ver distinção acima).
2. Modais — usar o componente certo do DS, não rolar o próprio
- Confirmar exclusão (sem vínculo): título "Atenção!" vermelho + ícone
trash(bg-error-100/text-error-500) + texto de ação irreversível +[Cancelar] [Confirmar]. - Remover vínculo / excluir registro (variante
danger): mesmo visual "Atenção!" vermelho +trash+bg-error-100+[Cancelar] [Confirmar]. Texto no padrão do DS:Ao clicar em "Confirmar", você concorda que [o item] vinculado será removido [do contexto].(concordância de gênero/número). Num dialog genérico parametrizável, exponha uma variantedangerem vez de criar outro componente. - Mudança de status (desativar/ativar): confirmação neutra (ícone
warning,dm-dialog-alert-template), nãodanger— vermelho/trash é só para remoção/exclusão. - Bloqueio por vínculo: "Atenção!" +
dm-data-tablepaginada com os itens vinculados + só[Voltar]. - Sair sem salvar: usar
ExitConfirmationDialogComponentdo DS (@bancodoc/ui) — primária = "Continuar editando". 🚩 Atenção: ao remover o botão "Cancelar" do footer, garanta que o gatilho do modal continue ligado (ex.:(backClick)dodm-page-headerchamandohandleCancel()), senão o modal fica órfão e a saída navega direto sem confirmar.- Detecte "alterações não salvas" por VALOR, não por
.dirty: compare o estado atual do form com um baseline capturado no carregamento (JSON.stringifydos valores persistíveis, ignorando controles só-de-UI). Efeitos internos (sync de listas/vigência,enable()/disable(),patchValue) podem sujar o.dirtysem edição real do usuário → o modal aparece falso-positivo. Aplique a checagem só em modo edição, e re-capture o baseline após salvar.
- Detecte "alterações não salvas" por VALOR, não por
- Confirmação genérica:
dm-dialog-alert-template, dialogsize="md", botõessize="sm". - Tamanho do modal de confirmação/exclusão:
size="md"(igualexcluir-cliente-dialog/grupo-delete-dialog). 🚩size="sm"deixa o modal pequeno demais — fora do padrão. - Botão Confirmar do modal (exclusão/confirmação): primary preenchido (sem
color) — ocolor="danger"doDmButtoné ghost (texto vermelho) e NÃO deve ser usado no confirmar. O vermelho vem só do íconetrash+ título "Atenção!". (color="danger"é válido em botões de ação/action-bar como "Remover selecionados", não no confirmar do modal.) - Vinculação:
dm-data-table+ busca + alertanotify/bell("É necessário selecionar pelo menos um X para salvar a vinculação.") + botão "Salvar" sempre ativo. - 🚩 Anti-padrões:
<table>HTML cru dentro do dialog; modal próprio em vez do componente do DS; botão de vinculação[disabled]/ rotulado "Vincular X"; alerta removido;size="sm"no modal de confirmação;color="danger"(ghost) no botão Confirmar.
3. Botões, ações e nomenclatura
- Gerenciar um registro = ação única "Gerenciar" (ícone
gear) — não "Visualizar"/"Editar" separados nem rota/editar. Somente-leitura é definido por permissão. - Botão de confirmar em modal de vinculação = "Salvar".
- Botão de salvar de formulário = sempre "Salvar" (não "Cadastrar"/"Enviar"/"Salvar alterações"), tanto em criação quanto em edição — inclusive no modo gerenciar/editar. (⚠️ o toast disso é "Alterações salvas!", mas o botão é só "Salvar".)
- Footer do formulário:
border-t border-neutral-200 bg-neutral-50 rounded-b-lg, alinhado à direita, só o botão primário "Salvar" no modo edição/criação (sem "Cancelar"/"Voltar" — voltar é pelo header). Referência:servicos/programas. - Link clicável (contagem → modal, "ver detalhes", etc.) = diretiva
dmLinkButtondo DS (cortext-secondary-700) — nãotext-primary-500, nem cor hardcodedtext-[#0075FF], nemhover:underlinecustom. - 🚩 Anti-padrões: ações "Visualizar"+"Editar"; ícone
eye; botão "Vincular X"; botão "Cadastrar"/"Enviar" em form; "Cancelar" no footer.
4. Validação de formulário
- Required =
DmValidators.required(trata espaço em branco como vazio) — nãoValidators.requirednativo. - Ao submeter inválido: util
markAllAsToucheddo DS (emitestatusChanges→ renderiza erro sob OnPush + scroll-to-invalid) — não oform.markAllAsTouched()nativo. - Mensagem padrão no campo: "Campo obrigatório.".
- Tabela obrigatória: erro "Campo obrigatório." ABAIXO da tabela (não acima, não em toast).
- Erro de servidor (duplicado 409) →
setErrors({ duplicate: true })no campo (helper text viacustomErrors), nunca toast. Mensagem de nome duplicado: "Já existe um item com este nome cadastrado." (genérico "item", não a entidade). E-mail inválido: "Insira um e-mail válido"; e-mail duplicado: "Já existe um usuário com este e-mail cadastrado.". - Validadores "só-criação" em telas de edição: um validador que só faz sentido ao criar (ex.:
data ≥ hoje, "início no futuro") NÃO deve ser aplicado a dados pré-existentes carregados no modo editar/gerenciar — datas antigas de registros já criados invalidam o form, e aí o Salvar vira no-op silencioso (nem sucesso nem erro). Torne o validador condicional ao contexto (registro novo vs. existente). Sintoma clássico: "cliquei em Salvar e não aconteceu nada / nenhum toast". - Grep:
Validators.required,this.form.markAllAsTouched(.
5. Tabelas
- Título da listagem: "[Item]s cadastrados" (ex.: "Grupos cadastrados").
pageSizepadrão 10 (10, 20, 30…).- Empty state padrão do DS: "Ops, não existem registros cadastrados!".
- Usar
dm-data-tabledo DS. - Grep:
signal(50),signal(20)em pageSize.
6. Navegação / textos
- Breadcrumb começa pelo módulo (ex.: "Empresa"), com chaves i18n quando o resto do app é traduzível.
- Textos em PT com acentuação correta (inclusive seeds da fake-api).
7. Layout dos formulários (página de cadastro/edição)
- Cabeçalho (
dm-page-headercom botão voltar + título) aparece em criação E edição —Novo Xao criar,Gerenciar Xao editar. 🚩 Anti-padrão: header dentro de@if (isCreateMode)→ some na edição (botão voltar + título desaparecem). - Espaçamento: grids de campos e seções usam
gap-8/space-y-8(32px), padrão do cadastro de programas. 🚩gap-4/space-y-4(16px) está fora do padrão. (Não confundir comspace-y-1de label/hint.) - Footer: ver dimensão 3 (
bg-neutral-50, só "Salvar"). - Grep:
@if (isCreateMode)em volta dedm-page-header;grid gap-4,space-y-4nos forms.
8. Contagem clicável → modal com os itens
- Em listagens/tabelas (exceto tabelas dentro de modais), toda célula que mostra um número de itens vinculados (Unidades, Usuários, Grupos, Perfis…) deve ser um link clicável que abre um modal listando os itens.
- Link =
dmLinkButton(cortext-secondary-700) + íconearrow-line-up-right(source="ph" [size]="16"); renderiza só quando> 0(senão "0" puro). - Modal =
dm-data-tablepaginada (título + subtítulo = nome do registro) +[Cancelar]. Um único dialog genérico reutilizável (ex.:RelatedItemsDialogComponent) atende todas as colunas. Referência:programas(consulta → "Unidades vinculadas"). - Dados: resolva ids→nomes pelas listas-mestre do store quando a linha já traz os ids; quando a linha só tem a contagem (sem ids), crie endpoint/fetch para buscar os itens.
- 🚩 Anti-padrões: número como texto puro não-clicável; link com cor própria (
text-primary-500,text-[#0075FF]) em vez dedmLinkButton/secondary-700.
9. Info contextual: tooltip no título, não alert
- Informação de apoio não-obrigatória ao lado de um título de seção → tooltip no padrão DS:
<div [dmTooltip]="…" tooltipPosition="right"><dm-icon name="info" source="ph" [size]="16"></dm-icon></div>— não umdm-alertocupando espaço fixo. Referência:programas("Conteúdo do programa ⓘ"). dm-alert(notify/bell) permanece para ação requerida (ex.: alerta dentro do modal de vinculação: "É necessário selecionar pelo menos um X…").
Output format (Part A)
🔴/🟡/🟢 [Dimensão] —
arquivo:linha
Faz:<o que está fora>· Padrão:<o correto, com a referência no libs/ui ou empresas/servicos>· Correção:<a mudança>
Sempre listar também o que já está conforme, para dar confiança.
Common mistakes when auditing
- Inferir o "padrão" a partir do próprio módulo auditado → sempre ancorar em
libs/ui+empresas/servicos. - Marcar como desvio algo que é decisão de produto (ex.: grupo permite "Excluir mesmo assim") → separar inconsistência de DS de regra de negócio.
- Esquecer de varrer os dialogs compartilhados (
shared/ui/components), não só as páginas. - Auditar só QUAL componente do DS foi usado e esquecer os PROPS de detalhe — usar o componente certo com prop errado ainda é desvio. Para cada componente do DS confira: dialog
size(confirmação =md, nãosm), botãocolor/size(Confirmar de modal = primary preenchido, nãodangerghost),dm-iconsource/name/[size], variante dodm-alert(notify/bellp/ ação vsinfop/ contexto), colunas/larguras dadm-data-table. Grep sugerido:size="sm"emdm-dialogde confirmação;color="danger"no botão de confirmar.
Part B — Verifying UI Changes in a Running App
When you verify a UI change in a running app, two layers can lie to you: the dev server (may serve a stale bundle) and your automation harness (may not drive the framework's reactivity). Don't trust "it looks broken" or "it looks fixed" until you've ruled both out.
When to use Part B
- You changed template/form/component code and "it doesn't appear" in the running app.
- Your Puppeteer/Playwright check gives a result that contradicts the code.
- App uses a watch/HMR dev server and/or a mock API (MSW, mirage, etc.).
The traps (the meat)
| Trap | Symptom | Fix |
|---|---|---|
| Stale bundle from a compile error | App returns 200 and "looks up", but your change isn't there; tests reflect old behavior | A TS/template error makes the watch server keep serving the last good bundle. Run the real build (nx build <proj>, nx run <proj>:typecheck, tsc --noEmit) to surface the error. "Server responds 200" is NOT proof your code compiled. Fix, rebuild, then hard-reload. |
evaluate runs OUTSIDE the framework's change-detection / zone |
Synthetic el.value=…; dispatchEvent(new Event('input')) or el.click() change the DOM but nothing re-renders |
Synthetic events in page.evaluate don't tick CD or update the form model. Drive with trusted events: real page.click() / page.type() / fill. Use evaluate only to read state or fetch. |
| Asserting on the DOM only | Control is invalid but no error shows; value "didn't change" | Read framework state: in Angular dev mode window.ng.getComponent(el) → control.value/valid/errors/touched, signals. The DOM can lag a CD tick. |
| Mock-API signals mislead | fetch(endpoint) → 200, so "it works" |
200 only proves the worker intercepts — not that your scenario passes. The in-memory DB usually resets on full reload (records created via API vanish). Use a deterministic seed (faker.seed(n)) so data is stable across reloads. |
Angular OnPush form-error gotchas
Validators.requiredtreats whitespace' 'as valid → use a trimming required validator (DSDmValidators.required) for true "required".- A form-field renders errors off the control's
statusChanges.markAllAsTouched()does not emitstatusChanges, so under OnPush the error won't render on submit. Use the DSmarkAllAsTouchedutil that also callsupdateValueAndValidity()(emits the event).setErrors()does emit — use it for server/duplicate errors so the field renders. - A conditionally-projected hint (
ContentChild) may not render under OnPush → render it as a plain sibling element instead.
Red flags — stop and check
- "The dev server is up (200)" → did you actually build?
- "My puppeteer click did nothing" → trusted event vs
evaluate. - "Control is invalid but no error shows" →
statusChanges/markAllAsTouched/ build. - "Created a record but it's gone after reload" → mock DB reset; seed it.
Reliable verification loop
- Build the affected project — catch compile errors that would freeze the bundle.
- Wait for the dev server to recompile; hard-reload the page.
- Drive the UI with real clicks/typing (trusted events).
- Assert on framework state (
window.ng) and the rendered DOM/screenshot. - For mock APIs, rely on seeded, deterministic data — not on freshly created records surviving a reload.
DemarcoDS run notes
- Fake API:
npm run serve-fake -- dm(MSW viaDM_FAKE_API=true); app emhttp://localhost:4200/plataforma/. O MSW intercepta no browser —curldireto a/api/*retorna 404 (esperado, não é erro). - Compile gate antes de confiar no que está na tela:
nx run <proj>:typecheck.