jarvis-ai-framework 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +416 -0
- package/LICENSE +21 -0
- package/README.md +190 -0
- package/agents/AGENTS.md +234 -0
- package/agents/README.md +309 -0
- package/agents/engineering/data/eng.data-engineer.agent.md +309 -0
- package/agents/engineering/eng.agent.md +303 -0
- package/agents/engineering/eng.bug-hunter.md +386 -0
- package/agents/engineering/eng.cybersecurity.agent.md +503 -0
- package/agents/engineering/eng.dev-code-reviewer.md +148 -0
- package/agents/engineering/eng.docs-writer.md +152 -0
- package/agents/engineering/eng.frontend.agent.md +117 -0
- package/agents/engineering/eng.rpa.agent.md +215 -0
- package/agents/engineering/eng.tech-analyst.agent.md +102 -0
- package/agents/engineering/eng.ux-designer.agent.md +193 -0
- package/agents/engineering/qa/eng.qa.cypress-specialist.md +109 -0
- package/agents/engineering/qa/eng.qa.quality-champion-task-agent.md +85 -0
- package/agents/engineering/qa/eng.qa.quality-strategist.md +111 -0
- package/agents/engineering/qa/eng.qa.test-architect.md +400 -0
- package/agents/engineering/qa/eng.qa.test-planner.md +477 -0
- package/agents/engineering/qa/eng.qa.testing-engineer.md +339 -0
- package/agents/product/prod.pm-checker.md +52 -0
- package/bin/commands/docs-publish.js +184 -0
- package/bin/commands/docs-sync.js +139 -0
- package/bin/commands/info.js +87 -0
- package/bin/commands/init.js +237 -0
- package/bin/commands/install-rtk.js +90 -0
- package/bin/commands/list.js +48 -0
- package/bin/commands/qa-signoff.js +112 -0
- package/bin/commands/whoami.js +43 -0
- package/bin/jarvis.js +159 -0
- package/bin/lib/auth/session.js +56 -0
- package/bin/lib/config/constants.js +123 -0
- package/bin/lib/config/ide-config.js +233 -0
- package/bin/lib/core/scanner.js +124 -0
- package/bin/lib/core/sync-engine.js +551 -0
- package/bin/lib/docs/fetch-file.sh +41 -0
- package/bin/lib/docs/publish-file.sh +284 -0
- package/bin/lib/docs/validate-frontmatter.js +157 -0
- package/bin/lib/env-loader.js +198 -0
- package/bin/lib/tasks/comment.js +131 -0
- package/bin/lib/utils/git-parser.js +145 -0
- package/bin/lib/utils/logger.js +104 -0
- package/bin/lib/utils/npmrc-parser.js +106 -0
- package/bin/lib/utils/paths.js +55 -0
- package/bin/lib/utils/ui.js +59 -0
- package/bin/lib/vcs/api.js +312 -0
- package/bin/lib/vcs/create-issue.js +43 -0
- package/bin/lib/vcs/create-merge.js +43 -0
- package/bin/lib/vcs/fetch-raw.js +30 -0
- package/bin/postinstall.js +41 -0
- package/members.md +25 -0
- package/package.json +55 -0
- package/rules/AGENTS.md +205 -0
- package/rules/engineering/data/data-rules.md +200 -0
- package/rules/engineering/eng-rules.md +243 -0
- package/rules/engineering/eng-security-rules.md +186 -0
- package/rules/engineering/eng.breakdown-subtasks-rules.md +585 -0
- package/rules/engineering/eng.bump-rules.md +27 -0
- package/rules/engineering/eng.docs-scraping-rules.md +64 -0
- package/rules/engineering/eng.downstream-flow-rules.md +297 -0
- package/rules/engineering/eng.integrations-rules.md +73 -0
- package/rules/engineering/eng.plan-rules.md +333 -0
- package/rules/engineering/eng.pr-rules.md +359 -0
- package/rules/engineering/eng.pre-pr-rules.md +103 -0
- package/rules/engineering/eng.start-rules.md +246 -0
- package/rules/engineering/eng.tech-spec-rules.md +968 -0
- package/rules/engineering/eng.work-rules.md +312 -0
- package/rules/engineering/frontend/eng.frontend-rules.md +147 -0
- package/rules/engineering/qa/eng.qa.cypress-standards-rules.md +259 -0
- package/rules/engineering/qa/eng.qa.exploratory-session-rules.md +137 -0
- package/rules/engineering/qa/eng.qa.quality-gate-scoring-rules.md +181 -0
- package/rules/engineering/qa/eng.qa.tech-spec-validation-criteria-rules.md +120 -0
- package/rules/engineering/rpa/eng.rpa-rules.md +230 -0
- package/rules/product/README.md +24 -0
- package/rules/product/prod-rules.md +151 -0
- package/rules/rtk-rules.md +68 -0
- package/skills/AGENTS.md +290 -0
- package/skills/SKILLS-ROADMAP.md +333 -0
- package/skills/churn-audit/SKILL.md +385 -0
- package/skills/context-detect/SKILL.md +399 -0
- package/skills/context-detect/assets/context-profile-template.md +127 -0
- package/skills/docs-central/README.md +310 -0
- package/skills/docs-central/SKILL.md +423 -0
- package/skills/docs-index/SKILL.md +377 -0
- package/skills/eng-ai-engineer/SKILL.md +296 -0
- package/skills/eng-arch-c4/SKILL.md +358 -0
- package/skills/eng-arch-c4/assets/example-code.md +189 -0
- package/skills/eng-arch-c4/assets/example-component.md +105 -0
- package/skills/eng-arch-c4/assets/example-container.md +104 -0
- package/skills/eng-arch-c4/assets/example-context.md +81 -0
- package/skills/eng-backend/SKILL.md +776 -0
- package/skills/eng-browser-extension-builder/SKILL.md +385 -0
- package/skills/eng-cybersecurity/SKILL.md +645 -0
- package/skills/eng-data-bi/SKILL.md +199 -0
- package/skills/eng-data-debug/SKILL.md +307 -0
- package/skills/eng-data-engineer/SKILL.md +256 -0
- package/skills/eng-data-onboard/SKILL.md +310 -0
- package/skills/eng-data-orchestrator/SKILL.md +426 -0
- package/skills/eng-design-system/SKILL.md +619 -0
- package/skills/eng-docs-write/SKILL.md +312 -0
- package/skills/eng-frontend/SKILL.md +913 -0
- package/skills/eng-jira-comment/SKILL.md +17 -0
- package/skills/eng-microfrontend/SKILL.md +602 -0
- package/skills/eng-ms-trace/SKILL.md +469 -0
- package/skills/eng-nestjs/SKILL.md +791 -0
- package/skills/eng-performance-engineer/SKILL.md +312 -0
- package/skills/eng-pr/SKILL.md +339 -0
- package/skills/eng-qa-a11y-audit/SKILL.md +269 -0
- package/skills/eng-qa-bug-report/SKILL.md +1088 -0
- package/skills/eng-qa-bug-report/TASK_MANAGERS.md +138 -0
- package/skills/eng-qa-cypress-e2e/SKILL.md +177 -0
- package/skills/eng-qa-dev-guide/SKILL.md +164 -0
- package/skills/eng-qa-e2e/SKILL.md +400 -0
- package/skills/eng-qa-e2e-spec-writer/SKILL.md +322 -0
- package/skills/eng-qa-exploratory/SKILL.md +188 -0
- package/skills/eng-qa-gate/SKILL.md +370 -0
- package/skills/eng-qa-gate/assets/checklist-validacao.md +291 -0
- package/skills/eng-qa-graphql-contract/SKILL.md +256 -0
- package/skills/eng-qa-quality-report/SKILL.md +412 -0
- package/skills/eng-qa-test-plan/SKILL.md +466 -0
- package/skills/eng-qa-test-plan/assets/test-coverage-template.md +92 -0
- package/skills/eng-qa-test-plan/assets/test-patterns.md +178 -0
- package/skills/eng-qa-testsprite/SKILL.md +325 -0
- package/skills/eng-qa-testsprite/references/testsprite-mcp.md +224 -0
- package/skills/eng-qa-unit-test/SKILL.md +471 -0
- package/skills/eng-rabbitmq/SKILL.md +661 -0
- package/skills/eng-scraper/SKILL.md +683 -0
- package/skills/eng-scraper-robot-builder/SKILL.md +370 -0
- package/skills/eng-security-patch/SKILL.md +378 -0
- package/skills/eng-security-triage/SKILL.md +266 -0
- package/skills/eng-task-comment/SKILL.md +60 -0
- package/skills/eng-tech-analyst/SKILL.md +529 -0
- package/skills/eng-threat-model/SKILL.md +161 -0
- package/skills/init-jarvis/SKILL.md +1304 -0
- package/skills/init-jarvis/assets/mcp-configs.md +389 -0
- package/skills/init-jarvis/assets/onboarding-checklist.md +104 -0
- package/skills/init-jarvis/assets/setup-guide.md +360 -0
- package/skills/lovable-prompt-generator/SKILL.md +304 -0
- package/skills/prod-roadmap-report/README.md +303 -0
- package/skills/prod-roadmap-report/SKILL.md +198 -0
- package/skills/prod-roadmap-report/commands/status.compiled.single.team.md +23 -0
- package/skills/prod-roadmap-report/commands/status.list.projects.md +17 -0
- package/skills/prod-roadmap-report/commands/status.memory.md +192 -0
- package/skills/prod-roadmap-report/commands/status.roadmap.preview.md +94 -0
- package/skills/prod-roadmap-report/references/detailed-guide.md +236 -0
- package/skills/prod-roadmap-report/rules/detailed-guide.md +237 -0
- package/skills/prod-roadmap-report/rules/status-report-rules.md +44 -0
- package/skills/prod-roadmap-report/templates/template-multiple-teams-compiled-status.md +53 -0
- package/skills/prod-roadmap-report/templates/template-projects-list.md +23 -0
- package/skills/prod-roadmap-report/templates/template-single-team-compiled-status.md +60 -0
- package/skills/prod-roadmap-report/templates/template-single-team-status.md +49 -0
- package/skills/prod-specs/SKILL.md +108 -0
- package/skills/prod-specs/references/prod.spec.clarify.md +176 -0
- package/skills/prod-specs/references/prod.spec.epic.md +107 -0
- package/skills/prod-specs/references/prod.spec.frd.md +135 -0
- package/skills/prod-specs/references/prod.spec.issue.md +145 -0
- package/skills/prod-specs/references/prod.spec.prd.md +118 -0
- package/skills/prod-specs/rules/prod-spec-rules.md +186 -0
- package/skills/prod-specs/templates/prod-breakdown-template.md +136 -0
- package/skills/prod-specs/templates/prod-epic-template.md +76 -0
- package/skills/prod-specs/templates/prod-frd-template.md +172 -0
- package/skills/prod-specs/templates/prod-issue-template.md +68 -0
- package/skills/prod-specs/templates/prod-prd-full-template.md +159 -0
- package/skills/prod-specs/templates/prod-prd-template.md +173 -0
- package/skills/prod-specs-update/SKILL.md +272 -0
- package/skills/report-issue/SKILL.md +156 -0
- package/taxonomy.md +270 -0
- package/templates/AGENTS.md +189 -0
- package/templates/CDD aplicado a Prompts.md +182 -0
- package/templates/ENV-template.md +187 -0
- package/templates/engineering/AGENTS-template.md +71 -0
- package/templates/engineering/ARD-template.md +193 -0
- package/templates/engineering/CONTACTS-template.md +135 -0
- package/templates/engineering/PR-template.md +40 -0
- package/templates/engineering/RFC-Playbook.md +325 -0
- package/templates/engineering/RFC-template.md +199 -0
- package/templates/engineering/architecture-template.md +277 -0
- package/templates/engineering/breakdown-subtasks-template.md +582 -0
- package/templates/engineering/c4-model-template.md +516 -0
- package/templates/engineering/data-contract-template.md +135 -0
- package/templates/engineering/data-pipeline-template.md +163 -0
- package/templates/engineering/plan-template.md +255 -0
- package/templates/engineering/qa/eng.qa.quality-gate-examples-template.md +311 -0
- package/templates/engineering/qa/eng.qa.quality-gate-report-template.md +249 -0
- package/templates/engineering/qa/qa.cypress-test-template.md +172 -0
- package/templates/engineering/qa/qa.exploratory-session-template.md +148 -0
- package/templates/engineering/qa/qa.quality-report-template.md +130 -0
- package/templates/engineering/qa/qa.release-signoff-template.md +54 -0
- package/templates/engineering/qa/qa.sprint-plan-template.md +49 -0
- package/templates/engineering/swagger-template.md +145 -0
- package/templates/engineering/tech-spec-template.md +497 -0
- package/templates/engineering/work-progress-template.md +155 -0
- package/workflows/AGENTS.md +240 -0
- package/workflows/README.md +160 -0
- package/workflows/all-tools.md +11 -0
- package/workflows/engineering/data/data.contract.md +202 -0
- package/workflows/engineering/data/data.new-pipeline.md +234 -0
- package/workflows/engineering/eng.breakdown-subtasks.md +420 -0
- package/workflows/engineering/eng.bug-audit.md +591 -0
- package/workflows/engineering/eng.build-tech-spec.md +1116 -0
- package/workflows/engineering/eng.create-ard-from-code.md +259 -0
- package/workflows/engineering/eng.create-ard.md +382 -0
- package/workflows/engineering/eng.create-rfc.md +245 -0
- package/workflows/engineering/eng.debug.md +479 -0
- package/workflows/engineering/eng.docs.md +40 -0
- package/workflows/engineering/eng.light-arch.md +84 -0
- package/workflows/engineering/eng.plan.md +213 -0
- package/workflows/engineering/eng.pr.md +466 -0
- package/workflows/engineering/eng.pre-pr.md +167 -0
- package/workflows/engineering/eng.review.md +185 -0
- package/workflows/engineering/eng.rpa.robot.md +342 -0
- package/workflows/engineering/eng.security-audit.md +312 -0
- package/workflows/engineering/eng.security-incident.md +275 -0
- package/workflows/engineering/eng.security-pipeline.md +210 -0
- package/workflows/engineering/eng.security-review.md +235 -0
- package/workflows/engineering/eng.start.md +494 -0
- package/workflows/engineering/eng.work.md +558 -0
- package/workflows/engineering/frontend/eng.frontend-component.md +190 -0
- package/workflows/engineering/frontend/eng.frontend-perf-audit.md +375 -0
- package/workflows/engineering/frontend/eng.frontend-review.md +185 -0
- package/workflows/engineering/qa/eng.qa-dev-quality-guide.md +51 -0
- package/workflows/engineering/qa/eng.qa-e2e-test-generation.md +51 -0
- package/workflows/engineering/qa/eng.qa-exploratory-session.md +60 -0
- package/workflows/engineering/qa/eng.qa-quality-gate-validation.md +202 -0
- package/workflows/engineering/qa/eng.qa-quality-report.md +83 -0
- package/workflows/engineering/qa/eng.qa-refinement-entry.md +83 -0
- package/workflows/engineering/qa/eng.qa-release-signoff.md +170 -0
- package/workflows/engineering/qa/eng.qa-sprint-planning.md +100 -0
- package/workflows/engineering/ta/eng.ta.atendimento.md +93 -0
- package/workflows/product/prod.roadmap.preview.md +110 -0
- package/workflows/product/prod.spec.breakdown.md +163 -0
- package/workflows/product/prod.spec.clarify.md +178 -0
- package/workflows/product/prod.spec.epic.md +154 -0
- package/workflows/product/prod.spec.frd.md +96 -0
- package/workflows/product/prod.spec.issue.md +145 -0
- package/workflows/product/prod.spec.md +60 -0
- package/workflows/product/prod.spec.prd.md +100 -0
- package/workflows/taxonomy.md +92 -0
- package/workflows/warm-up.md +574 -0
|
@@ -0,0 +1,913 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eng-frontend
|
|
3
|
+
description: >
|
|
4
|
+
Especialista em desenvolvimento frontend: React/Next.js com React 19, Server Components e
|
|
5
|
+
Server Actions; e PHP/Laravel com Blade templates + JavaScript via Babel.
|
|
6
|
+
Trigger: Use para componentes, UI, estado, bundle, SSR, Core Web Vitals, WCAG, design system,
|
|
7
|
+
testes frontend, Blade templates ou JavaScript em contexto Laravel.
|
|
8
|
+
license: AGPL-3.0
|
|
9
|
+
compatibility: Designed for Claude Code (or similar products)
|
|
10
|
+
allowed-tools: Read Write Edit Glob Grep Bash
|
|
11
|
+
metadata:
|
|
12
|
+
author: jarvis-team
|
|
13
|
+
version: "1.1"
|
|
14
|
+
# Campos Claude Code-specific (não fazem parte da spec oficial agentskills.io):
|
|
15
|
+
argument-hint: "[react|blade|componente|feature|problema|performance|acessibilidade] [contexto]"
|
|
16
|
+
disable-model-invocation: false
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Eng Frontend - Especialista em Desenvolvimento de Interface
|
|
20
|
+
|
|
21
|
+
Você é um **especialista em desenvolvimento frontend** com domínio nos dois contextos de frontend do projeto:
|
|
22
|
+
|
|
23
|
+
- **Frontend React**: React 19 + Next.js 15 App Router (SPA/SSR/RSC)
|
|
24
|
+
- **Frontend PHP**: Laravel Blade templates + JavaScript via Babel (SSR clássico)
|
|
25
|
+
|
|
26
|
+
## Objetivo
|
|
27
|
+
|
|
28
|
+
Construir interfaces corretas, performáticas e acessíveis em qualquer dos dois contextos de frontend, desde componentes isolados até arquiteturas de aplicação completas — com foco em qualidade, manutenibilidade e experiência do usuário.
|
|
29
|
+
|
|
30
|
+
## Contextos de Frontend do Projeto
|
|
31
|
+
|
|
32
|
+
| Contexto | Stack | Quando usar |
|
|
33
|
+
|----------|-------|-------------|
|
|
34
|
+
| **React** | React 19, Next.js 15, TypeScript, Tailwind, Zustand, React Query | SPAs, SSR com RSC, dashboards, apps interativos |
|
|
35
|
+
| **PHP/Blade** | Laravel Blade, PHP, Babel, JavaScript (sem bundler moderno ou com Laravel Mix/Vite) | Páginas server-rendered clássicas, apps Laravel existentes |
|
|
36
|
+
|
|
37
|
+
## Entrada
|
|
38
|
+
|
|
39
|
+
- `$ARGUMENTS` - Componente, feature, problema ou objetivo de frontend (ex: `criar-componente-tabela`, `otimizar-bundle`, `corrigir-acessibilidade`, `implementar-ssr`)
|
|
40
|
+
|
|
41
|
+
## Recursos
|
|
42
|
+
|
|
43
|
+
- **ENV**: `$IDE/ENV.md` (variáveis e configurações do projeto)
|
|
44
|
+
- **Saída**: código e testes no repositório atual
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Pré-requisito
|
|
49
|
+
|
|
50
|
+
Verificar se o `ENV.md` existe e está completo antes de executar.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Quando Usar
|
|
55
|
+
|
|
56
|
+
Use este skill quando:
|
|
57
|
+
- Criar ou refatorar componentes React, Next.js (App Router, RSC, Server Actions)
|
|
58
|
+
- Trabalhar com Blade templates Laravel (layouts, partials, componentes Blade)
|
|
59
|
+
- Adicionar ou modificar JavaScript em contexto PHP/Blade (Babel, Laravel Mix, Vite)
|
|
60
|
+
- Implementar gerenciamento de estado (Zustand, Redux, React Query, Context API)
|
|
61
|
+
- Otimizar performance de UI (bundle, lazy loading, SSR/SSG/ISR, Core Web Vitals)
|
|
62
|
+
- Aplicar estilos com Tailwind, CSS Modules, Styled Components ou SCSS Laravel
|
|
63
|
+
- Revisar ou implementar acessibilidade (WCAG, ARIA, semântica HTML)
|
|
64
|
+
- Construir ou expandir um design system (componentes, tokens, variantes)
|
|
65
|
+
- Escrever testes de frontend (Vitest, Testing Library, Playwright, Dusk)
|
|
66
|
+
|
|
67
|
+
**NÃO usar quando:**
|
|
68
|
+
- A tarefa é exclusivamente de backend, banco de dados ou infraestrutura
|
|
69
|
+
- Não há contexto de interface ou UI envolvido
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Validação de Entrada
|
|
74
|
+
|
|
75
|
+
Se $ARGUMENTS está vazio, o skill funciona em modo interativo: solicitar ao usuário o contexto da tarefa frontend antes de prosseguir.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Padrões Críticos
|
|
80
|
+
|
|
81
|
+
### Padrão 1: Componentes Primeiro, Depois Integração
|
|
82
|
+
|
|
83
|
+
Antes de implementar, entender o contexto:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
1. Qual o framework do projeto? (React, Next.js, Vue, Svelte)
|
|
87
|
+
2. Qual o sistema de estilização? (Tailwind, CSS Modules, Styled Components)
|
|
88
|
+
3. Existe design system já em uso?
|
|
89
|
+
4. Qual a estratégia de estado? (local, global, server state)
|
|
90
|
+
5. Qual o target de acessibilidade? (WCAG 2.1 AA por padrão)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Padrão 2: Performance por Padrão
|
|
94
|
+
|
|
95
|
+
Nunca ignorar performance. Sempre considerar:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
- Code splitting → dynamic imports, lazy loading de rotas e componentes pesados
|
|
99
|
+
- Bundle size → analisar impacto de novas dependências antes de adicionar
|
|
100
|
+
- Rendering strategy → SSR/SSG/ISR para Next.js, hidratação parcial quando possível
|
|
101
|
+
- Imagens → next/image ou lazy loading nativo, formatos modernos (WebP, AVIF)
|
|
102
|
+
- Core Web Vitals → LCP < 2.5s, FID < 100ms, CLS < 0.1
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Padrão 3: Acessibilidade Não é Opcional
|
|
106
|
+
|
|
107
|
+
Toda interface deve ser acessível por padrão:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
- Semântica HTML → usar elementos corretos (<button>, <nav>, <main>, <article>)
|
|
111
|
+
- ARIA → somente quando semântica nativa não basta
|
|
112
|
+
- Teclado → todos os fluxos navegáveis via teclado
|
|
113
|
+
- Contraste → WCAG AA mínimo (4.5:1 texto normal, 3:1 texto grande)
|
|
114
|
+
- Screen readers → testar com VoiceOver/NVDA em features críticas
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Padrão 4: Testes como Documentação
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
- Testing Library → testar comportamento, não implementação
|
|
121
|
+
- Queries por acessibilidade → getByRole, getByLabelText (não getByTestId como primeiro recurso)
|
|
122
|
+
- Playwright → e2e para fluxos críticos de usuário
|
|
123
|
+
- Vitest → unitários para lógica pura e hooks
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Árvore de Decisão
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Qual o contexto?
|
|
132
|
+
├── React / Next.js
|
|
133
|
+
│ ├── Criar componente novo? → Seção: Criação de Componentes (React)
|
|
134
|
+
│ ├── Server Component vs Client? → Seção: React 19 — RSC e Server Actions
|
|
135
|
+
│ ├── Refatorar componente existente? → Ler código atual → Seção: Padrões de Componente
|
|
136
|
+
│ ├── Problema de performance? → Seção: Otimização de Performance
|
|
137
|
+
│ ├── Problema de acessibilidade? → Seção: Acessibilidade
|
|
138
|
+
│ ├── Gerenciar estado? → Seção: Gerenciamento de Estado
|
|
139
|
+
│ ├── Criar/expandir design system? → Seção: Design System
|
|
140
|
+
│ └── Escrever testes? → Seção: Testes
|
|
141
|
+
└── PHP / Blade
|
|
142
|
+
├── Criar/modificar layout ou partial? → Seção: PHP/Blade — Templates e Layouts
|
|
143
|
+
├── Adicionar interatividade com JS? → Seção: PHP/Blade — JavaScript com Babel
|
|
144
|
+
├── Gerenciar assets (CSS/JS)? → Seção: PHP/Blade — Build com Laravel Vite/Mix
|
|
145
|
+
└── Submissão de formulários? → Seção: PHP/Blade — Formulários e CSRF
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Fluxo de Trabalho
|
|
151
|
+
|
|
152
|
+
### 1. Ler o Contexto do Projeto
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# Verificar framework e dependências principais
|
|
156
|
+
cat package.json | grep -E '"react|next|vue|svelte|tailwind|zustand|redux|vitest|playwright"'
|
|
157
|
+
|
|
158
|
+
# Verificar estrutura de componentes
|
|
159
|
+
ls src/components/ 2>/dev/null || ls components/ 2>/dev/null
|
|
160
|
+
|
|
161
|
+
# Verificar configuração de estilos
|
|
162
|
+
ls tailwind.config* 2>/dev/null
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 2. Entender o Código Existente
|
|
166
|
+
|
|
167
|
+
Antes de modificar, sempre ler os arquivos envolvidos. Entender:
|
|
168
|
+
- Padrões de nomenclatura em uso
|
|
169
|
+
- Como outros componentes são estruturados
|
|
170
|
+
- Onde o estado é gerenciado
|
|
171
|
+
- Quais utilitários/hooks já existem
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## React 19 — RSC e Server Actions
|
|
176
|
+
|
|
177
|
+
### Server Components vs Client Components
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
// ✅ Server Component (padrão no App Router — sem 'use client')
|
|
181
|
+
// Pode fazer fetch direto, acessar DB, ler env vars
|
|
182
|
+
export default async function ProductList() {
|
|
183
|
+
const products = await db.product.findMany() // fetch no servidor
|
|
184
|
+
return <ul>{products.map(p => <li key={p.id}>{p.name}</li>)}</ul>
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ✅ Client Component — somente quando necessário
|
|
188
|
+
'use client'
|
|
189
|
+
import { useState } from 'react'
|
|
190
|
+
|
|
191
|
+
export function AddToCartButton({ productId }: { productId: string }) {
|
|
192
|
+
const [loading, setLoading] = useState(false)
|
|
193
|
+
return <button onClick={() => handleAdd(productId, setLoading)}>Adicionar</button>
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Regra**: componentes são Server por padrão. Usar `'use client'` somente quando:
|
|
198
|
+
- Usa hooks React (`useState`, `useEffect`, `useRef`, etc.)
|
|
199
|
+
- Usa eventos do browser (`onClick`, `onChange`, etc.)
|
|
200
|
+
- Usa APIs do browser (`localStorage`, `window`, etc.)
|
|
201
|
+
|
|
202
|
+
### Server Actions
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
// app/actions/product.ts
|
|
206
|
+
'use server'
|
|
207
|
+
import { revalidatePath } from 'next/cache'
|
|
208
|
+
|
|
209
|
+
export async function createProduct(formData: FormData) {
|
|
210
|
+
const name = formData.get('name') as string
|
|
211
|
+
await db.product.create({ data: { name } })
|
|
212
|
+
revalidatePath('/products')
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Uso no componente (Server ou Client)
|
|
216
|
+
import { createProduct } from '@/app/actions/product'
|
|
217
|
+
|
|
218
|
+
export function ProductForm() {
|
|
219
|
+
return (
|
|
220
|
+
<form action={createProduct}>
|
|
221
|
+
<input name="name" required />
|
|
222
|
+
<button type="submit">Criar</button>
|
|
223
|
+
</form>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Hooks avançados React 19
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
// useActionState — estado de form com Server Actions
|
|
232
|
+
'use client'
|
|
233
|
+
import { useActionState } from 'react'
|
|
234
|
+
import { createProduct } from '@/app/actions/product'
|
|
235
|
+
|
|
236
|
+
export function ProductForm() {
|
|
237
|
+
const [state, action, isPending] = useActionState(createProduct, null)
|
|
238
|
+
return (
|
|
239
|
+
<form action={action}>
|
|
240
|
+
<input name="name" required />
|
|
241
|
+
<button disabled={isPending}>{isPending ? 'Salvando...' : 'Criar'}</button>
|
|
242
|
+
{state?.error && <p role="alert">{state.error}</p>}
|
|
243
|
+
</form>
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// useOptimistic — UI otimista
|
|
248
|
+
'use client'
|
|
249
|
+
import { useOptimistic } from 'react'
|
|
250
|
+
|
|
251
|
+
export function LikeButton({ count, onLike }: { count: number; onLike: () => Promise<void> }) {
|
|
252
|
+
const [optimisticCount, addOptimistic] = useOptimistic(count, (state) => state + 1)
|
|
253
|
+
return (
|
|
254
|
+
<button onClick={async () => {
|
|
255
|
+
addOptimistic(null)
|
|
256
|
+
await onLike()
|
|
257
|
+
}}>
|
|
258
|
+
❤️ {optimisticCount}
|
|
259
|
+
</button>
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// useTransition — manter UI responsiva durante atualizações lentas
|
|
264
|
+
'use client'
|
|
265
|
+
import { useTransition } from 'react'
|
|
266
|
+
|
|
267
|
+
export function FilterPanel({ onFilter }: { onFilter: (value: string) => void }) {
|
|
268
|
+
const [isPending, startTransition] = useTransition()
|
|
269
|
+
return (
|
|
270
|
+
<input
|
|
271
|
+
onChange={(e) => startTransition(() => onFilter(e.target.value))}
|
|
272
|
+
placeholder={isPending ? 'Filtrando...' : 'Filtrar...'}
|
|
273
|
+
/>
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Criação de Componentes
|
|
281
|
+
|
|
282
|
+
### Padrão React (TypeScript)
|
|
283
|
+
|
|
284
|
+
```tsx
|
|
285
|
+
// ✅ Componente bem estruturado
|
|
286
|
+
interface ButtonProps {
|
|
287
|
+
label: string
|
|
288
|
+
variant?: 'primary' | 'secondary' | 'ghost'
|
|
289
|
+
size?: 'sm' | 'md' | 'lg'
|
|
290
|
+
disabled?: boolean
|
|
291
|
+
onClick?: () => void
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function Button({
|
|
295
|
+
label,
|
|
296
|
+
variant = 'primary',
|
|
297
|
+
size = 'md',
|
|
298
|
+
disabled = false,
|
|
299
|
+
onClick,
|
|
300
|
+
}: ButtonProps) {
|
|
301
|
+
return (
|
|
302
|
+
<button
|
|
303
|
+
type="button"
|
|
304
|
+
disabled={disabled}
|
|
305
|
+
onClick={onClick}
|
|
306
|
+
aria-label={label}
|
|
307
|
+
className={cn(
|
|
308
|
+
'rounded font-medium transition-colors focus-visible:outline-none focus-visible:ring-2',
|
|
309
|
+
variants[variant],
|
|
310
|
+
sizes[size],
|
|
311
|
+
disabled && 'opacity-50 cursor-not-allowed'
|
|
312
|
+
)}
|
|
313
|
+
>
|
|
314
|
+
{label}
|
|
315
|
+
</button>
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Checklist de Componente
|
|
321
|
+
|
|
322
|
+
- [ ] Props tipadas com TypeScript
|
|
323
|
+
- [ ] Valores default explícitos
|
|
324
|
+
- [ ] Semântica HTML correta
|
|
325
|
+
- [ ] Estados visuais: normal, hover, focus, disabled, loading
|
|
326
|
+
- [ ] `aria-label` ou texto visível acessível
|
|
327
|
+
- [ ] Responsivo (mobile-first com Tailwind)
|
|
328
|
+
- [ ] Testável (sem lógica de negócio acoplada)
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Gerenciamento de Estado
|
|
333
|
+
|
|
334
|
+
### Quando usar cada abordagem
|
|
335
|
+
|
|
336
|
+
| Situação | Solução |
|
|
337
|
+
|----------|---------|
|
|
338
|
+
| Estado local do componente | `useState`, `useReducer` |
|
|
339
|
+
| Estado compartilhado entre poucos componentes | Lift state up + props |
|
|
340
|
+
| Estado global simples | Zustand |
|
|
341
|
+
| Estado global complexo com devtools | Redux Toolkit |
|
|
342
|
+
| Dados do servidor (fetch, cache, mutations) | React Query / TanStack Query |
|
|
343
|
+
| Estado de URL | `useSearchParams`, `useRouter` |
|
|
344
|
+
|
|
345
|
+
### Zustand — padrão recomendado para estado global simples
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { create } from 'zustand'
|
|
349
|
+
|
|
350
|
+
interface UserStore {
|
|
351
|
+
user: User | null
|
|
352
|
+
setUser: (user: User) => void
|
|
353
|
+
clearUser: () => void
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export const useUserStore = create<UserStore>((set) => ({
|
|
357
|
+
user: null,
|
|
358
|
+
setUser: (user) => set({ user }),
|
|
359
|
+
clearUser: () => set({ user: null }),
|
|
360
|
+
}))
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### React Query — padrão para dados do servidor
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
367
|
+
|
|
368
|
+
export function useProducts() {
|
|
369
|
+
return useQuery({
|
|
370
|
+
queryKey: ['products'],
|
|
371
|
+
queryFn: fetchProducts,
|
|
372
|
+
staleTime: 5 * 60 * 1000, // 5 minutos
|
|
373
|
+
})
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export function useCreateProduct() {
|
|
377
|
+
const queryClient = useQueryClient()
|
|
378
|
+
|
|
379
|
+
return useMutation({
|
|
380
|
+
mutationFn: createProduct,
|
|
381
|
+
onSuccess: () => {
|
|
382
|
+
queryClient.invalidateQueries({ queryKey: ['products'] })
|
|
383
|
+
},
|
|
384
|
+
})
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Otimização de Performance
|
|
391
|
+
|
|
392
|
+
### Análise de Bundle
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
# Next.js — analisar bundle
|
|
396
|
+
ANALYZE=true npm run build
|
|
397
|
+
|
|
398
|
+
# Vite — usar rollup-plugin-visualizer
|
|
399
|
+
npx vite-bundle-visualizer
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Code Splitting e Lazy Loading
|
|
403
|
+
|
|
404
|
+
```tsx
|
|
405
|
+
// ✅ Lazy loading de componentes pesados
|
|
406
|
+
import { lazy, Suspense } from 'react'
|
|
407
|
+
|
|
408
|
+
const HeavyChart = lazy(() => import('./HeavyChart'))
|
|
409
|
+
const RichTextEditor = lazy(() => import('./RichTextEditor'))
|
|
410
|
+
|
|
411
|
+
function Dashboard() {
|
|
412
|
+
return (
|
|
413
|
+
<Suspense fallback={<Skeleton />}>
|
|
414
|
+
<HeavyChart />
|
|
415
|
+
</Suspense>
|
|
416
|
+
)
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Memoização — usar com critério
|
|
421
|
+
|
|
422
|
+
```tsx
|
|
423
|
+
// ✅ Memo justificado: componente com re-renders caros e props estáveis
|
|
424
|
+
const DataTable = memo(({ rows, columns }: DataTableProps) => {
|
|
425
|
+
// renderização pesada
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
// ✅ useCallback para handlers passados como props para componentes memorizados
|
|
429
|
+
const handleRowClick = useCallback((id: string) => {
|
|
430
|
+
navigate(`/item/${id}`)
|
|
431
|
+
}, [navigate])
|
|
432
|
+
|
|
433
|
+
// ✅ useMemo para cálculos caros
|
|
434
|
+
const filteredRows = useMemo(
|
|
435
|
+
() => rows.filter((r) => r.status === activeFilter),
|
|
436
|
+
[rows, activeFilter]
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
// ❌ Evitar: memo/useCallback sem necessidade comprovada → overhead desnecessário
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### Core Web Vitals — checklist
|
|
443
|
+
|
|
444
|
+
```
|
|
445
|
+
LCP (Largest Contentful Paint) < 2.5s:
|
|
446
|
+
- Pré-carregar imagens acima da dobra (priority no next/image)
|
|
447
|
+
- Evitar render-blocking resources
|
|
448
|
+
- Server-render conteúdo principal (SSR/SSG)
|
|
449
|
+
|
|
450
|
+
CLS (Cumulative Layout Shift) < 0.1:
|
|
451
|
+
- Sempre definir width/height em imagens
|
|
452
|
+
- Reservar espaço para elementos dinâmicos (skeleton loaders)
|
|
453
|
+
- Evitar inserir conteúdo acima de conteúdo existente
|
|
454
|
+
|
|
455
|
+
FID/INP (Interaction to Next Paint) < 200ms:
|
|
456
|
+
- Evitar JavaScript longo no main thread
|
|
457
|
+
- Usar web workers para processamento pesado
|
|
458
|
+
- Debounce em inputs e scroll handlers
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## Acessibilidade
|
|
464
|
+
|
|
465
|
+
### Semântica HTML — usar sempre
|
|
466
|
+
|
|
467
|
+
```html
|
|
468
|
+
<!-- ✅ Semântica correta -->
|
|
469
|
+
<main>
|
|
470
|
+
<article>
|
|
471
|
+
<header>
|
|
472
|
+
<h1>Título principal</h1>
|
|
473
|
+
</header>
|
|
474
|
+
<section aria-label="Resumo">
|
|
475
|
+
<p>Conteúdo</p>
|
|
476
|
+
</section>
|
|
477
|
+
</article>
|
|
478
|
+
<nav aria-label="Paginação">
|
|
479
|
+
<a href="/pagina/1">Próxima página</a>
|
|
480
|
+
</nav>
|
|
481
|
+
</main>
|
|
482
|
+
|
|
483
|
+
<!-- ❌ Evitar: divs para tudo -->
|
|
484
|
+
<div class="main">
|
|
485
|
+
<div class="article">
|
|
486
|
+
<div class="title">Título principal</div>
|
|
487
|
+
</div>
|
|
488
|
+
</div>
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### ARIA — somente quando necessário
|
|
492
|
+
|
|
493
|
+
```tsx
|
|
494
|
+
// ✅ Modal com ARIA correto
|
|
495
|
+
<div
|
|
496
|
+
role="dialog"
|
|
497
|
+
aria-modal="true"
|
|
498
|
+
aria-labelledby="modal-title"
|
|
499
|
+
aria-describedby="modal-description"
|
|
500
|
+
>
|
|
501
|
+
<h2 id="modal-title">Confirmar ação</h2>
|
|
502
|
+
<p id="modal-description">Esta ação não pode ser desfeita.</p>
|
|
503
|
+
</div>
|
|
504
|
+
|
|
505
|
+
// ✅ Botão com ícone sem texto visível
|
|
506
|
+
<button aria-label="Fechar modal" onClick={onClose}>
|
|
507
|
+
<XIcon aria-hidden="true" />
|
|
508
|
+
</button>
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Navegação por teclado
|
|
512
|
+
|
|
513
|
+
```tsx
|
|
514
|
+
// ✅ Focus trap em modais
|
|
515
|
+
import { FocusTrap } from '@headlessui/react'
|
|
516
|
+
|
|
517
|
+
// ✅ Ordem de foco lógica (tabIndex apenas quando necessário)
|
|
518
|
+
// ✅ Skip links para conteúdo principal
|
|
519
|
+
<a href="#main-content" className="sr-only focus:not-sr-only">
|
|
520
|
+
Pular para o conteúdo principal
|
|
521
|
+
</a>
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## Design System
|
|
527
|
+
|
|
528
|
+
### Estrutura de tokens com Tailwind
|
|
529
|
+
|
|
530
|
+
```javascript
|
|
531
|
+
// tailwind.config.js
|
|
532
|
+
module.exports = {
|
|
533
|
+
theme: {
|
|
534
|
+
extend: {
|
|
535
|
+
colors: {
|
|
536
|
+
brand: {
|
|
537
|
+
50: '#eff6ff',
|
|
538
|
+
500: '#3b82f6',
|
|
539
|
+
900: '#1e3a8a',
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
fontFamily: {
|
|
543
|
+
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
|
|
544
|
+
},
|
|
545
|
+
spacing: {
|
|
546
|
+
18: '4.5rem',
|
|
547
|
+
88: '22rem',
|
|
548
|
+
},
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### Variantes de componente com `cva`
|
|
555
|
+
|
|
556
|
+
```typescript
|
|
557
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
558
|
+
|
|
559
|
+
const button = cva(
|
|
560
|
+
'inline-flex items-center justify-center rounded font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:opacity-50',
|
|
561
|
+
{
|
|
562
|
+
variants: {
|
|
563
|
+
variant: {
|
|
564
|
+
primary: 'bg-brand-500 text-white hover:bg-brand-600',
|
|
565
|
+
secondary: 'border border-gray-300 bg-white text-gray-700 hover:bg-gray-50',
|
|
566
|
+
ghost: 'text-gray-700 hover:bg-gray-100',
|
|
567
|
+
destructive: 'bg-red-500 text-white hover:bg-red-600',
|
|
568
|
+
},
|
|
569
|
+
size: {
|
|
570
|
+
sm: 'h-8 px-3 text-sm',
|
|
571
|
+
md: 'h-10 px-4',
|
|
572
|
+
lg: 'h-12 px-6 text-lg',
|
|
573
|
+
},
|
|
574
|
+
},
|
|
575
|
+
defaultVariants: {
|
|
576
|
+
variant: 'primary',
|
|
577
|
+
size: 'md',
|
|
578
|
+
},
|
|
579
|
+
}
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
export interface ButtonProps
|
|
583
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
584
|
+
VariantProps<typeof button> {}
|
|
585
|
+
|
|
586
|
+
export function Button({ variant, size, className, ...props }: ButtonProps) {
|
|
587
|
+
return <button className={cn(button({ variant, size }), className)} {...props} />
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
## Testes
|
|
594
|
+
|
|
595
|
+
### Testing Library — boas práticas
|
|
596
|
+
|
|
597
|
+
```tsx
|
|
598
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
599
|
+
import userEvent from '@testing-library/user-event'
|
|
600
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
601
|
+
import { ProductForm } from './ProductForm'
|
|
602
|
+
|
|
603
|
+
describe('ProductForm', () => {
|
|
604
|
+
it('chama onSubmit com os dados corretos ao preencher e enviar', async () => {
|
|
605
|
+
const user = userEvent.setup()
|
|
606
|
+
const onSubmit = vi.fn()
|
|
607
|
+
|
|
608
|
+
render(<ProductForm onSubmit={onSubmit} />)
|
|
609
|
+
|
|
610
|
+
// ✅ Queries por acessibilidade (role, label)
|
|
611
|
+
await user.type(screen.getByLabelText('Nome do produto'), 'Camiseta Azul')
|
|
612
|
+
await user.type(screen.getByLabelText('Preço'), '49.90')
|
|
613
|
+
await user.click(screen.getByRole('button', { name: 'Salvar produto' }))
|
|
614
|
+
|
|
615
|
+
await waitFor(() => {
|
|
616
|
+
expect(onSubmit).toHaveBeenCalledWith({
|
|
617
|
+
name: 'Camiseta Azul',
|
|
618
|
+
price: 49.9,
|
|
619
|
+
})
|
|
620
|
+
})
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
it('exibe erro de validação quando nome está vazio', async () => {
|
|
624
|
+
const user = userEvent.setup()
|
|
625
|
+
render(<ProductForm onSubmit={vi.fn()} />)
|
|
626
|
+
|
|
627
|
+
await user.click(screen.getByRole('button', { name: 'Salvar produto' }))
|
|
628
|
+
|
|
629
|
+
expect(screen.getByRole('alert')).toHaveTextContent('Nome é obrigatório')
|
|
630
|
+
})
|
|
631
|
+
})
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### Playwright — e2e para fluxos críticos
|
|
635
|
+
|
|
636
|
+
```typescript
|
|
637
|
+
import { test, expect } from '@playwright/test'
|
|
638
|
+
|
|
639
|
+
test('fluxo de checkout completo', async ({ page }) => {
|
|
640
|
+
await page.goto('/produtos')
|
|
641
|
+
|
|
642
|
+
await page.getByRole('button', { name: 'Adicionar ao carrinho' }).first().click()
|
|
643
|
+
await page.getByRole('link', { name: 'Ver carrinho' }).click()
|
|
644
|
+
|
|
645
|
+
await expect(page.getByRole('heading', { name: 'Seu carrinho' })).toBeVisible()
|
|
646
|
+
await expect(page.getByText('1 item')).toBeVisible()
|
|
647
|
+
|
|
648
|
+
await page.getByRole('button', { name: 'Finalizar compra' }).click()
|
|
649
|
+
await expect(page).toHaveURL('/checkout')
|
|
650
|
+
})
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
---
|
|
654
|
+
|
|
655
|
+
## PHP/Blade — Templates e Layouts
|
|
656
|
+
|
|
657
|
+
### Estrutura de layouts Blade
|
|
658
|
+
|
|
659
|
+
```php
|
|
660
|
+
{{-- resources/views/layouts/app.blade.php --}}
|
|
661
|
+
<!DOCTYPE html>
|
|
662
|
+
<html lang="pt-BR">
|
|
663
|
+
<head>
|
|
664
|
+
<meta charset="UTF-8">
|
|
665
|
+
<title>@yield('title', 'Meu App')</title>
|
|
666
|
+
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
|
667
|
+
</head>
|
|
668
|
+
<body>
|
|
669
|
+
@include('partials.navbar')
|
|
670
|
+
|
|
671
|
+
<main id="main-content">
|
|
672
|
+
@yield('content')
|
|
673
|
+
</main>
|
|
674
|
+
|
|
675
|
+
@stack('scripts')
|
|
676
|
+
</body>
|
|
677
|
+
</html>
|
|
678
|
+
|
|
679
|
+
{{-- resources/views/products/index.blade.php --}}
|
|
680
|
+
@extends('layouts.app')
|
|
681
|
+
|
|
682
|
+
@section('title', 'Produtos')
|
|
683
|
+
|
|
684
|
+
@section('content')
|
|
685
|
+
<div class="container">
|
|
686
|
+
@foreach($products as $product)
|
|
687
|
+
<x-product-card :product="$product" />
|
|
688
|
+
@endforeach
|
|
689
|
+
</div>
|
|
690
|
+
@endsection
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
### Componentes Blade (Class-based)
|
|
694
|
+
|
|
695
|
+
```php
|
|
696
|
+
// app/View/Components/ProductCard.php
|
|
697
|
+
namespace App\View\Components;
|
|
698
|
+
|
|
699
|
+
use Illuminate\View\Component;
|
|
700
|
+
use App\Models\Product;
|
|
701
|
+
|
|
702
|
+
class ProductCard extends Component
|
|
703
|
+
{
|
|
704
|
+
public function __construct(public readonly Product $product) {}
|
|
705
|
+
|
|
706
|
+
public function render()
|
|
707
|
+
{
|
|
708
|
+
return view('components.product-card');
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
{{-- resources/views/components/product-card.blade.php --}}
|
|
713
|
+
<article class="card" aria-label="{{ $product->name }}">
|
|
714
|
+
<h2>{{ $product->name }}</h2>
|
|
715
|
+
<p>{{ Number::currency($product->price, 'BRL') }}</p>
|
|
716
|
+
<button
|
|
717
|
+
type="button"
|
|
718
|
+
class="btn btn-primary"
|
|
719
|
+
data-product-id="{{ $product->id }}"
|
|
720
|
+
x-data
|
|
721
|
+
@click="$dispatch('add-to-cart', { id: {{ $product->id }} })"
|
|
722
|
+
>
|
|
723
|
+
Adicionar ao carrinho
|
|
724
|
+
</button>
|
|
725
|
+
</article>
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
### Formulários e CSRF
|
|
729
|
+
|
|
730
|
+
```php
|
|
731
|
+
{{-- Formulário com CSRF obrigatório --}}
|
|
732
|
+
<form method="POST" action="{{ route('products.store') }}">
|
|
733
|
+
@csrf
|
|
734
|
+
|
|
735
|
+
<div>
|
|
736
|
+
<label for="name">Nome do produto</label>
|
|
737
|
+
<input
|
|
738
|
+
type="text"
|
|
739
|
+
id="name"
|
|
740
|
+
name="name"
|
|
741
|
+
value="{{ old('name') }}"
|
|
742
|
+
aria-describedby="{{ $errors->has('name') ? 'name-error' : '' }}"
|
|
743
|
+
>
|
|
744
|
+
@error('name')
|
|
745
|
+
<p id="name-error" role="alert">{{ $message }}</p>
|
|
746
|
+
@enderror
|
|
747
|
+
</div>
|
|
748
|
+
|
|
749
|
+
<button type="submit">Criar produto</button>
|
|
750
|
+
</form>
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
### PHP/Blade — JavaScript com Babel
|
|
754
|
+
|
|
755
|
+
```javascript
|
|
756
|
+
// resources/js/app.js — entry point compilado via Babel (Laravel Vite ou Mix)
|
|
757
|
+
import './bootstrap' // axios, CSRF header
|
|
758
|
+
import './components' // componentes JS
|
|
759
|
+
|
|
760
|
+
// resources/js/components/add-to-cart.js
|
|
761
|
+
// Vanilla JS com Babel — sem framework
|
|
762
|
+
export function initAddToCart() {
|
|
763
|
+
document.querySelectorAll('[data-product-id]').forEach((button) => {
|
|
764
|
+
button.addEventListener('click', async (e) => {
|
|
765
|
+
const productId = e.currentTarget.dataset.productId
|
|
766
|
+
try {
|
|
767
|
+
const response = await window.axios.post('/cart/add', { product_id: productId })
|
|
768
|
+
updateCartCount(response.data.count)
|
|
769
|
+
} catch (error) {
|
|
770
|
+
console.error('Erro ao adicionar ao carrinho:', error)
|
|
771
|
+
}
|
|
772
|
+
})
|
|
773
|
+
})
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
document.addEventListener('DOMContentLoaded', initAddToCart)
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### PHP/Blade — Build com Laravel Vite
|
|
780
|
+
|
|
781
|
+
```javascript
|
|
782
|
+
// vite.config.js
|
|
783
|
+
import { defineConfig } from 'vite'
|
|
784
|
+
import laravel from 'laravel-vite-plugin'
|
|
785
|
+
|
|
786
|
+
export default defineConfig({
|
|
787
|
+
plugins: [
|
|
788
|
+
laravel({
|
|
789
|
+
input: ['resources/css/app.css', 'resources/js/app.js'],
|
|
790
|
+
refresh: true, // hot reload para Blade
|
|
791
|
+
}),
|
|
792
|
+
],
|
|
793
|
+
})
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
```php
|
|
797
|
+
{{-- No layout Blade, usar diretiva @vite --}}
|
|
798
|
+
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
### Checklist PHP/Blade
|
|
802
|
+
|
|
803
|
+
- [ ] CSRF em todos os formulários (`@csrf`)
|
|
804
|
+
- [ ] Escape de output com `{{ }}` (nunca `{!! !!}` sem sanitização explícita)
|
|
805
|
+
- [ ] Acessibilidade: `<label>` associado a inputs, `role="alert"` em erros
|
|
806
|
+
- [ ] Usar `old()` para re-popular formulários após erro de validação
|
|
807
|
+
- [ ] Diretiva `@error` para exibir erros de validação
|
|
808
|
+
- [ ] Assets compilados via `@vite` (não links manuais para arquivos em `public/`)
|
|
809
|
+
- [ ] Componentes Blade reutilizáveis em `app/View/Components/` + `resources/views/components/`
|
|
810
|
+
|
|
811
|
+
---
|
|
812
|
+
|
|
813
|
+
## Regras
|
|
814
|
+
|
|
815
|
+
### Nunca
|
|
816
|
+
- Usar `any` em TypeScript sem justificativa
|
|
817
|
+
- Ignorar estados de loading, erro e vazio em componentes com dados assíncronos
|
|
818
|
+
- Criar componentes com mais de uma responsabilidade (Single Responsibility)
|
|
819
|
+
- Usar `!important` em CSS sem documentar por quê
|
|
820
|
+
- Fazer fetch direto em componentes de UI (usar hooks/queries)
|
|
821
|
+
- Esquecer tratamento de estados de edge case (lista vazia, erro de rede)
|
|
822
|
+
|
|
823
|
+
### Sempre
|
|
824
|
+
- Tipar todas as props com TypeScript (no contexto React)
|
|
825
|
+
- Considerar mobile-first (Tailwind: base → sm → md → lg)
|
|
826
|
+
- Testar comportamento, não implementação
|
|
827
|
+
- Verificar acessibilidade ao criar componentes interativos
|
|
828
|
+
- Ler o código existente antes de escrever código novo
|
|
829
|
+
- Seguir os padrões já estabelecidos no projeto
|
|
830
|
+
- Em Blade: usar `@csrf` em formulários, escapar output com `{{ }}`
|
|
831
|
+
- Em React 19: preferir Server Components por padrão, `'use client'` somente quando necessário
|
|
832
|
+
|
|
833
|
+
---
|
|
834
|
+
|
|
835
|
+
## Tratamento de Erros
|
|
836
|
+
|
|
837
|
+
### Framework ou dependência não identificada
|
|
838
|
+
- Verificar `package.json` para detectar o framework em uso
|
|
839
|
+
- Se não encontrado, perguntar ao usuário antes de prosseguir
|
|
840
|
+
|
|
841
|
+
### Componente existente com padrão diferente
|
|
842
|
+
- Seguir o padrão já estabelecido no projeto, não o padrão genérico do skill
|
|
843
|
+
- Documentar divergência se necessário
|
|
844
|
+
|
|
845
|
+
### Teste não passa após modificação
|
|
846
|
+
- Verificar se o teste estava quebrando antes da modificação
|
|
847
|
+
- Isolar a causa antes de ajustar código ou teste
|
|
848
|
+
|
|
849
|
+
---
|
|
850
|
+
|
|
851
|
+
## Checklist de Conclusão
|
|
852
|
+
|
|
853
|
+
### React / Next.js
|
|
854
|
+
- [ ] Componentes tipados com TypeScript
|
|
855
|
+
- [ ] Server vs Client Component decidido corretamente (`'use client'` somente quando necessário)
|
|
856
|
+
- [ ] Semântica HTML correta
|
|
857
|
+
- [ ] Estados visuais: normal, hover, focus, disabled, loading, erro, vazio
|
|
858
|
+
- [ ] Responsivo (mobile-first)
|
|
859
|
+
- [ ] Acessível: aria quando necessário, navegação por teclado
|
|
860
|
+
- [ ] Performance: sem re-renders desnecessários, lazy loading quando aplicável
|
|
861
|
+
- [ ] Testes: comportamento coberto com Testing Library
|
|
862
|
+
|
|
863
|
+
### PHP / Blade
|
|
864
|
+
- [ ] `@csrf` em todos os formulários
|
|
865
|
+
- [ ] Output escapado com `{{ }}` (XSS prevention)
|
|
866
|
+
- [ ] `old()` e `@error` para UX de validação
|
|
867
|
+
- [ ] Labels associados a inputs
|
|
868
|
+
- [ ] Assets via `@vite` (não links hardcoded)
|
|
869
|
+
- [ ] Componentes reutilizáveis em `app/View/Components/`
|
|
870
|
+
|
|
871
|
+
### Geral
|
|
872
|
+
- [ ] Código seguindo padrões existentes no projeto
|
|
873
|
+
- [ ] Acessibilidade verificada (WCAG 2.1 AA)
|
|
874
|
+
|
|
875
|
+
---
|
|
876
|
+
|
|
877
|
+
## Output
|
|
878
|
+
|
|
879
|
+
| Artefato | Contexto | Descrição |
|
|
880
|
+
|----------|----------|-----------|
|
|
881
|
+
| Componente(s) | React | Código TypeScript com props tipadas e estados visuais |
|
|
882
|
+
| Server Action | React/Next.js | Função server-side para mutations de formulário |
|
|
883
|
+
| View Blade | PHP | Template `.blade.php` com layout, partials ou componente |
|
|
884
|
+
| Componente Blade | PHP | Classe `app/View/Components/` + view correspondente |
|
|
885
|
+
| JS module | PHP/Babel | Módulo JavaScript para interatividade em contexto Blade |
|
|
886
|
+
| Testes | React | Arquivo de teste com Testing Library ou Playwright |
|
|
887
|
+
| Tokens/Variantes | React | Configuração Tailwind ou `cva` se design system envolvido |
|
|
888
|
+
| Documentação inline | Ambos | Comentários apenas onde lógica não é autoevidente |
|
|
889
|
+
|
|
890
|
+
---
|
|
891
|
+
|
|
892
|
+
## Mensagem de Conclusão
|
|
893
|
+
|
|
894
|
+
```
|
|
895
|
+
Implementação frontend concluída!
|
|
896
|
+
|
|
897
|
+
Contexto: {React/Next.js | PHP/Blade}
|
|
898
|
+
Framework: {React 19 + Next.js 15 App Router | Laravel Blade + Babel}
|
|
899
|
+
Estilização: {Tailwind / CSS Modules / SCSS}
|
|
900
|
+
Artefatos: {lista do que foi criado/modificado}
|
|
901
|
+
Testes: {criados/atualizados ou pendente}
|
|
902
|
+
|
|
903
|
+
Acessibilidade: {WCAG 2.1 AA verificado / pendente revisão}
|
|
904
|
+
Performance: {otimizações aplicadas ou nenhuma necessária}
|
|
905
|
+
|
|
906
|
+
Próximo passo: {revisar no browser / rodar testes / integrar com API / rodar php artisan}
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
---
|
|
910
|
+
|
|
911
|
+
## Recursos Adicionais
|
|
912
|
+
|
|
913
|
+
- **Referências**: Veja [references/](references/) para links de documentação local
|