feat: adiciona 12 plugins Descomplicar ao marketplace

Plugins: automacao, crm-ops, design-media, dev-tools, gestao,
infraestrutura, marketing, negocio, perfex-dev, project-manager,
wordpress + hello-plugin (existente).

Totais: 83 skills, 44 agents, 12 datasets.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-07 21:41:24 +00:00
parent bcce928beb
commit 2cb3210962
209 changed files with 50869 additions and 0 deletions

View File

@@ -0,0 +1,250 @@
---
name: db-design
description: Database schema design and optimization. Creates normalized schemas,
indexes, and migration plans. Use when user mentions "database design", "schema",
"modelo dados", "database optimization", "sql design".
author: Descomplicar® Crescimento Digital
version: 1.0.0
quality_score: 75
user_invocable: true
desk_task: 1469
---
# Database Design Specialist
Skill para design e optimização de bases de dados seguindo padrões Descomplicar®.
## Quando Usar
- Desenhar schema para novo sistema
- Optimizar queries lentas
- Criar migrations (Laravel, Doctrine, Raw SQL)
- Auditar e optimizar índices
- Planear partitioning/sharding
## Protocolo Obrigatório
### 1. Pesquisa Inicial
```
mcp__memory-supabase__search_memories "database [projecto]"
mcp__wikijs__search_pages "database schema [sistema]"
```
### 2. Verificar Schema Existente
Antes de propor alterações, SEMPRE verificar estrutura actual.
## Princípios de Design
### Normalização
- **OLTP**: Mínimo 3NF (Third Normal Form)
- **OLAP/Reporting**: Desnormalização justificada
- **Híbrido**: Tabelas normalizadas + views materializadas
### Naming Conventions
```sql
-- Tabelas: snake_case, plural
users, order_items, product_categories
-- Colunas: snake_case, singular
created_at, user_id, is_active
-- Primary keys
id (auto-increment ou UUID)
-- Foreign keys: {tabela_singular}_id
user_id, order_id, category_id
-- Índices: idx_{tabela}_{coluna(s)}
idx_orders_customer_id
idx_orders_created_at_status
```
## Performance
### Índices Estratégicos
```sql
-- WHERE frequente
CREATE INDEX idx_users_email ON users(email);
-- JOIN
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
-- ORDER BY
CREATE INDEX idx_products_created_at ON products(created_at DESC);
-- Composto para queries específicas
CREATE INDEX idx_orders_status_date ON orders(status, created_at);
```
### Análise de Queries
```sql
-- SEMPRE usar EXPLAIN antes de optimizar
EXPLAIN ANALYZE
SELECT c.name, COUNT(o.id) as total
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.created_at >= '2025-01-01'
GROUP BY c.id
ORDER BY total DESC
LIMIT 100;
```
### Evitar Anti-patterns
```sql
-- ❌ EVITAR
SELECT * FROM orders; -- Usar colunas específicas
SELECT ... WHERE YEAR(created_at) = 2025; -- Função impede uso de índice
-- ✅ PREFERIR
SELECT id, customer_id, total FROM orders;
SELECT ... WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01';
```
## Template Migration Laravel
```php
<?php
/**
* Migration: Create subscriptions table
*
* @author Descomplicar® Crescimento Digital
* @link https://descomplicar.pt
* @copyright 2025 Descomplicar®
*/
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('subscriptions', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->foreignId('plan_id')->constrained();
$table->enum('status', ['active', 'cancelled', 'expired', 'trial']);
$table->timestamp('trial_ends_at')->nullable();
$table->timestamp('current_period_start');
$table->timestamp('current_period_end');
$table->timestamp('cancelled_at')->nullable();
$table->timestamps();
// Índices
$table->index(['user_id', 'status']);
$table->index('current_period_end');
});
}
public function down(): void
{
Schema::dropIfExists('subscriptions');
}
};
```
## Segurança
- **Prepared statements**: SEMPRE para queries com input
- **Least privilege**: Utilizadores DB com permissões mínimas
- **Audit logging**: Para tabelas com dados sensíveis
- **Encriptação**: Colunas com PII devem ser encriptadas
## Checklist Schema
### Design
- [ ] Normalização adequada (sem redundância desnecessária)
- [ ] Foreign keys definidas
- [ ] Tipos de dados apropriados
- [ ] Constraints (NOT NULL, UNIQUE, CHECK)
- [ ] Soft deletes onde apropriado (deleted_at)
### Performance
- [ ] Índices em colunas de WHERE
- [ ] Índices em colunas de JOIN
- [ ] Índices compostos onde necessário
- [ ] Evitados índices redundantes
- [ ] EXPLAIN em queries críticas
### Segurança
- [ ] Dados sensíveis identificados
- [ ] Audit trail se necessário
- [ ] Prepared statements em todo código
## Entregáveis Standard
1. DDL completo (CREATE TABLE, INDEX)
2. Diagrama ER se schema complexo
3. Migrations se framework especificado
4. Queries exemplo para operações comuns
5. EXPLAIN de queries críticas
---
## Datasets Dify (Consulta Obrigatória)
Em caso de dúvidas ou para aprofundar conhecimento, consultar os seguintes datasets via MCP:
| Dataset | ID | Prioridade |
|---------|----|-----------:|
| **TI (Tecnologia da Informação)** | `7f63ec0c-6321-488c-b107-980140199850` | 1 |
| **Desenvolvimento de Software** | `e7c7decc-0ded-4351-ab14-b110b3c38ec9` | 1 |
| **AWS (Amazon Web Services)** | `cc7f000a-ad86-49b6-b59b-179e65f8a229` | 2 |
### Como Consultar
```javascript
// Pesquisar optimização MySQL
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "7f63ec0c-6321-488c-b107-980140199850",
query: "mysql index optimization slow query"
})
// Padrões de design de bases de dados
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "e7c7decc-0ded-4351-ab14-b110b3c38ec9",
query: "database schema design patterns"
})
// RDS e bases de dados AWS
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "cc7f000a-ad86-49b6-b59b-179e65f8a229",
query: "RDS aurora postgresql"
})
```
### Quando Consultar
- Antes de desenhar novo schema
- Ao optimizar queries lentas
- Para escolher tipo de índice adequado
- Decidir entre MySQL/PostgreSQL
- Configurar bases de dados em cloud
---
**Versão**: 1.0.0 | **Autor**: Descomplicar®
---
## Quando NÃO Usar
- Para tarefas fora do domínio de especialização desta skill
- Quando outra skill mais específica está disponível
- Para operações que requerem confirmação manual do utilizador
## Exemplos
### Exemplo 1: Uso Básico
```
Input: [descrição da tarefa]
Output: [resultado esperado]
```
### Exemplo 2: Uso Avançado
```
Input: [caso complexo]
Output: [resultado detalhado]
```

View File

@@ -0,0 +1 @@
/home/ealmeida/.claude/plugins/anthropic-claude-code/plugins/frontend-design/skills/frontend-design

View File

@@ -0,0 +1,722 @@
---
name: nextjs
description: Next.js development best practices and patterns. App router, server components,
API routes, and deployment. Use when user mentions "nextjs", "next.js", "react server",
"app router", "next deployment".
author: Descomplicar® Crescimento Digital
version: 2.0.0
quality_score: 75
user_invocable: true
allowed-tools: Glob
---
# /nextjs - Next.js Development
Desenvolvimento Next.js moderno (13+) com App Router e Server Components.
## Quando Usar
- Criar aplicações Next.js
- Migrar de Pages para App Router
- Implementar Server Components
- Configurar Server Actions
- Optimizar SEO e performance
## App Router Structure
```
app/
├── layout.tsx # Root layout (obrigatório)
├── page.tsx # Home page
├── loading.tsx # Loading UI (Suspense)
├── error.tsx # Error boundary
├── not-found.tsx # 404 page
├── globals.css # Global styles
├── (auth)/ # Route group (não afecta URL)
│ ├── login/page.tsx
│ └── register/page.tsx
├── dashboard/
│ ├── layout.tsx # Nested layout
│ ├── page.tsx
│ └── [id]/
│ └── page.tsx # Dynamic route
└── api/
└── route.ts # API route
```
## Server vs Client Components
### Server Component (default)
```tsx
// ✅ PADRÃO - Runs on server
async function ProductsPage() {
// Pode fazer fetch directo
const products = await db.product.findMany();
return (
<div>
{products.map(p => (
<ProductCard key={p.id} product={p} />
))}
</div>
);
}
export default ProductsPage;
```
**Vantagens:**
- Acesso directo a BD
- Menos JavaScript no cliente
- SEO melhor
- Dados sempre frescos
### Client Component
```tsx
'use client';
import { useState } from 'react';
// ✅ Para interactividade
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
);
}
```
**Quando usar:**
- useState, useEffect, useContext
- Event listeners (onClick, onChange)
- Browser APIs (localStorage, window)
- Custom hooks
## Data Fetching Patterns
### 1. Static (SSG)
```tsx
// Default behaviour - cache infinito
async function Page() {
const data = await fetch('https://api.example.com/data', {
cache: 'force-cache' // Explícito (mas é default)
});
return <div>{data.title}</div>;
}
```
### 2. Dynamic (SSR)
```tsx
// Sempre fetch fresco
async function Page() {
const data = await fetch('https://api.example.com/data', {
cache: 'no-store' // NUNCA cache
});
return <div>{data.title}</div>;
}
```
### 3. Revalidate (ISR)
```tsx
// Cache com revalidação automática
async function Page() {
const data = await fetch('https://api.example.com/data', {
next: { revalidate: 60 } // Revalida a cada 60s
});
return <div>{data.title}</div>;
}
```
## Server Actions
```tsx
// app/actions.ts
'use server';
import { revalidatePath } from 'next/cache';
import { db } from '@/lib/db';
export async function createPost(formData: FormData) {
const title = formData.get('title') as string;
const content = formData.get('content') as string;
await db.post.create({
data: { title, content }
});
revalidatePath('/posts');
}
// app/create-post/page.tsx
import { createPost } from '@/app/actions';
export default function CreatePost() {
return (
<form action={createPost}>
<input name="title" required />
<textarea name="content" required />
<button type="submit">Create</button>
</form>
);
}
```
## Layouts e Loading States
### Root Layout
```tsx
// app/layout.tsx
import './globals.css';
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
export const metadata = {
title: 'My App',
description: 'Description'
};
export default function RootLayout({
children
}: {
children: React.ReactNode
}) {
return (
<html lang="pt">
<body className={inter.className}>
<nav>...</nav>
{children}
<footer>...</footer>
</body>
</html>
);
}
```
### Loading UI
```tsx
// app/dashboard/loading.tsx
export default function Loading() {
return <div>Loading...</div>;
}
// Wrapper automático em <Suspense>
```
### Error Handling
```tsx
// app/error.tsx
'use client';
export default function Error({
error,
reset
}: {
error: Error;
reset: () => void;
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={reset}>Try again</button>
</div>
);
}
```
## Routing Avançado
### Parallel Routes
```tsx
// app/layout.tsx
export default function Layout({
children,
modal,
sidebar
}: {
children: React.ReactNode;
modal: React.ReactNode;
sidebar: React.ReactNode;
}) {
return (
<>
{sidebar}
{children}
{modal}
</>
);
}
// app/@sidebar/page.tsx
// app/@modal/page.tsx
```
### Intercepting Routes
```tsx
// app/feed/page.tsx - Lista de fotos
// app/photo/[id]/page.tsx - Página completa da foto
// app/@modal/(.)photo/[id]/page.tsx - Modal da foto (intercepta)
// Navegar via <Link> abre modal
// Refresh ou URL directo abre página completa
```
## Middleware
```tsx
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('token');
// Auth guard
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
// Custom header
const response = NextResponse.next();
response.headers.set('x-custom-header', 'value');
return response;
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*']
};
```
## Performance Rules (Vercel Engineering)
57 regras priorizadas por impacto. Fonte: Vercel Engineering react-best-practices.
### 1. Eliminating Waterfalls (CRITICAL)
```tsx
// ❌ ERRADO: Awaits sequenciais (waterfall)
const session = await getSession()
const config = await getConfig()
const user = await getUser(session.userId)
// ✅ CORRECTO: Paralelo com Promise.all()
const [session, config] = await Promise.all([
getSession(),
getConfig()
])
const user = await getUser(session.userId) // depende de session
// ✅ CORRECTO: Suspense boundaries estratégicos
async function Page() {
return (
<Layout>
<Suspense fallback={<HeaderSkeleton />}>
<Header />
</Suspense>
<Suspense fallback={<ContentSkeleton />}>
<SlowContent />
</Suspense>
</Layout>
)
}
// ✅ CORRECTO: Defer await até necessário
async function handler(req) {
const dataPromise = fetchData() // inicia imediatamente
if (req.method === 'HEAD') return new Response() // early return sem await
const data = await dataPromise // só espera quando precisa
return Response.json(data)
}
```
**Regras:**
- `async-defer-await`: Mover await para onde é realmente necessário
- `async-dependency-parallel`: Usar `better-all` para dependências parciais
- `async-prevent-waterfall`: Iniciar promises antes de awaitar
- `async-promise-all`: `Promise.all()` para operações independentes
- `async-suspense-boundaries`: Suspense para UI progressiva
### 2. Bundle Size (CRITICAL)
```tsx
// ❌ ERRADO: Barrel file imports
import { Button, Icon } from '@mui/material' // carrega tudo
// ✅ CORRECTO: Import directo
import Button from '@mui/material/Button'
// ✅ CORRECTO: optimizePackageImports (Next.js 13.5+)
// next.config.js
module.exports = {
experimental: {
optimizePackageImports: ['lucide-react', '@mui/material', 'lodash']
}
}
// ✅ CORRECTO: Dynamic imports para componentes pesados
const MonacoEditor = dynamic(() => import('./MonacoEditor'), {
ssr: false,
loading: () => <EditorSkeleton />
})
// ✅ CORRECTO: Preload em hover/focus
function FeatureButton() {
return (
<button
onMouseEnter={() => import('./HeavyFeature')}
onClick={openFeature}
>
Open Feature
</button>
)
}
```
**Regras:**
- `bundle-barrel-imports`: Evitar barrel files (index.js re-exports)
- `bundle-conditional-loading`: Carregar módulos só quando feature activa
- `bundle-defer-third-party`: `next/dynamic` com `ssr: false` para analytics
- `bundle-dynamic-imports`: Lazy-load componentes pesados (>50KB)
- `bundle-preload-intent`: Preload em hover para UX instantânea
### 3. Server-Side Performance (HIGH)
```tsx
// ✅ Server Actions: autenticar SEMPRE (são endpoints públicos!)
'use server'
async function updateProfile(formData: FormData) {
const session = await auth() // 1. Autenticar
if (!session) throw new Error('Unauthorized')
const data = schema.parse(formData) // 2. Validar input
await db.user.update({ where: { id: session.userId }, data }) // 3. Executar
revalidatePath('/profile')
}
// ✅ Minimizar serialização RSC - só campos necessários
async function UserCard({ userId }: { userId: string }) {
const user = await db.user.findUnique({ where: { id: userId } })
// Passa só o necessário, não o objecto inteiro com 50+ campos
return <ClientCard name={user.name} avatar={user.avatar} />
}
// ✅ React.cache() para deduplicação per-request
const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } })
})
// ✅ after() para operações não-bloqueantes
import { after } from 'next/server'
async function submitForm(data: FormData) {
const result = await processForm(data)
after(async () => {
await logAnalytics(result) // executa APÓS resposta enviada
})
return result
}
```
**Regras:**
- `server-auth-actions`: Autenticar Server Actions como API routes
- `server-avoid-duplicate-serialization`: Transformar dados no cliente
- `server-lru-cache`: LRUCache para dados cross-request
- `server-minimize-serialization`: Só campos necessários em RSC props
- `server-parallel-composition`: Composição paralela de Server Components
- `server-react-cache`: `React.cache()` para deduplicação per-request
- `server-after`: `after()` para logging/analytics não-bloqueante
### 4. Client-Side Data Fetching (MEDIUM-HIGH)
```tsx
// ✅ SWR para deduplicação automática
const { data, isLoading } = useSWR('/api/user', fetcher)
// ✅ Passive event listeners para scroll performance
element.addEventListener('scroll', handler, { passive: true })
// ✅ localStorage versionado com try-catch
const key = 'userConfig:v2'
try {
const data = JSON.parse(localStorage.getItem(key) ?? '{}')
} catch { /* incognito mode, quota exceeded */ }
```
### 5. Re-render Optimization (MEDIUM)
```tsx
// ✅ Derived state: calcular durante render, NÃO em useEffect
function FilteredList({ items, query }: Props) {
const filtered = items.filter(i => i.name.includes(query)) // derive!
return <List items={filtered} />
}
// ✅ Functional setState para evitar stale closures
setItems(curr => [...curr, ...newItems]) // ✅
// setItems([...items, ...newItems]) // ❌ stale closure
// ✅ Lazy state initialization
const [index] = useState(() => buildSearchIndex(items)) // computa 1x
// ✅ useTransition para updates não-urgentes
const [isPending, startTransition] = useTransition()
startTransition(() => setSearchResults(results))
// ✅ Extrair defaults não-primitivos para module scope
const EMPTY_ARRAY: string[] = [] // ✅ fora do componente
const NOOP = () => {} // ✅ fora do componente
function Component({ items = EMPTY_ARRAY, onClick = NOOP }) { ... }
// ✅ Dependências de effect: primitivos, não objectos
useEffect(() => { fetchUser(userId) }, [userId]) // ✅ primitivo
// useEffect(() => { ... }, [user]) // ❌ objecto = nova ref cada render
```
### 6. Rendering Performance (MEDIUM)
```tsx
// ✅ CSS content-visibility para listas longas
// .message { content-visibility: auto; contain-intrinsic-size: 0 80px; }
// ✅ Hoist static JSX fora do componente
const skeleton = <div className="skeleton h-4 w-full" /> // module scope
// ✅ Conditional rendering explícito (evitar && com falsy)
{count > 0 ? <Badge>{count}</Badge> : null} // ✅
// {count && <Badge>{count}</Badge>} // ❌ renderiza "0"
// ✅ useTransition em vez de useState manual para loading
const [isPending, startTransition] = useTransition()
// const [isLoading, setIsLoading] = useState(false) // ❌ manual
// ✅ Hydration: inline script para evitar flicker
// <script dangerouslySetInnerHTML={{ __html: `
// document.documentElement.classList.add(
// localStorage.getItem('theme') === 'dark' ? 'dark' : 'light'
// )
// `}} />
```
### 7. JavaScript Performance (LOW-MEDIUM)
```tsx
// ✅ Index Maps para lookups repetidos O(1) vs O(n)
const userMap = new Map(users.map(u => [u.id, u]))
orders.map(o => ({ ...o, user: userMap.get(o.userId) }))
// ✅ Set para membership checks
const allowedIds = new Set(['a', 'b', 'c'])
if (allowedIds.has(id)) { ... } // O(1) vs .includes() O(n)
// ✅ toSorted() para imutabilidade (React-safe)
const sorted = items.toSorted((a, b) => a.name.localeCompare(b.name))
// items.sort() // ❌ muta o array original
// ✅ Early return para evitar processamento desnecessário
function validate(items: Item[]) {
for (const item of items) {
if (!item.valid) return { error: item.id } // sai cedo
}
return { success: true }
}
// ✅ Combinar iterações de array
const { active, expired } = items.reduce((acc, item) => {
if (item.active) acc.active.push(item)
else acc.expired.push(item)
return acc
}, { active: [], expired: [] })
// Em vez de items.filter(active) + items.filter(expired) = 2 iterações
```
### 8. Advanced Patterns (LOW)
```tsx
// ✅ Init uma vez, não por mount (Strict Mode remonta)
let didInit = false
function App() {
useEffect(() => {
if (didInit) return
didInit = true
initializeAnalytics()
}, [])
}
// ✅ useEffectEvent para callbacks estáveis em effects
const onTick = useEffectEvent((value) => {
console.log(value) // sempre valor mais recente
})
useEffect(() => {
const id = setInterval(() => onTick(count), 1000)
return () => clearInterval(id)
}, []) // sem dependência de count!
```
### Resumo de Impacto
| Prioridade | Categoria | Regras | Ganho |
|---|---|---|---|
| CRITICAL | Waterfalls | 5 | 2-10x latência |
| CRITICAL | Bundle Size | 5 | 200-800ms load |
| HIGH | Server-Side | 7 | RSC payload, TTI |
| MEDIUM-HIGH | Client Fetch | 4 | Request dedup |
| MEDIUM | Re-renders | 12 | UI responsiveness |
| MEDIUM | Rendering | 9 | Paint performance |
| LOW-MEDIUM | JS Perf | 12 | CPU cycles |
| LOW | Advanced | 3 | Init correctness |
---
## Optimizações
### Images
```tsx
import Image from 'next/image';
<Image
src="/photo.jpg"
width={500}
height={300}
alt="Photo"
priority // LCP image
placeholder="blur"
blurDataURL="data:image/..."
/>
```
### Fonts
```tsx
import { Inter, Roboto_Mono } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap',
});
const robotoMono = Roboto_Mono({
subsets: ['latin'],
weight: ['400', '700'],
});
```
### Metadata (SEO)
```tsx
// app/page.tsx
export const metadata = {
title: 'Home',
description: 'Home page',
openGraph: {
title: 'Home',
description: 'Home page',
images: ['/og-image.jpg'],
},
twitter: {
card: 'summary_large_image',
},
};
```
## Deployment
### Vercel (recomendado)
```bash
npm install -g vercel
vercel
```
### Docker
```dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/public ./public
COPY --from=builder /app/package*.json ./
RUN npm ci --production
EXPOSE 3000
CMD ["npm", "start"]
```
### Static Export
```js
// next.config.js
module.exports = {
output: 'export',
trailingSlash: true,
};
```
```bash
npm run build
# Output: out/
```
## Datasets Dify
| Dataset | ID | Prioridade |
|---------|----|-----------:|
| **Desenvolvimento de Software** | `e7c7decc-0ded-4351-ab14-b110b3c38ec9` | 1 |
| **TI (Tecnologia da Informação)** | `7f63ec0c-6321-488c-b107-980140199850` | 1 |
---
**Versão**: 1.0.0 | **Autor**: Descomplicar®
---
## Quando NÃO Usar
- Para tarefas fora do domínio de especialização desta skill
- Quando outra skill mais específica está disponível
- Para operações que requerem confirmação manual do utilizador
## Protocolo
1. Analisar requisitos da tarefa
2. Verificar disponibilidade de ferramentas necessárias
3. Executar operações de forma incremental
4. Validar resultados antes de concluir
5. Reportar status e próximos passos
## Exemplos
### Exemplo 1: Uso Básico
```
Input: [descrição da tarefa]
Output: [resultado esperado]
```
### Exemplo 2: Uso Avançado
```
Input: [caso complexo]
Output: [resultado detalhado]
```

View File

@@ -0,0 +1,370 @@
---
name: php-dev
description: >
Modern PHP fullstack development with Laravel, Symfony, RESTful APIs and backend architecture. Creates APIs, implements authentication (JWT, OAuth, Sanctum), develops service classes and refactors legacy code following PSR standards.
Use when developing PHP applications, creating REST APIs, implementing authentication, refactoring legacy code, or when user mentions
"php", "laravel", "symfony", "rest api", "jwt", "oauth", "backend", "service layer", "repository pattern", "php 8".
author: Descomplicar® Crescimento Digital
version: 1.2.0
user_invocable: true
tags: [php, laravel, symfony, api, backend, jwt, oauth, rest]
desk_task: 1477
allowed-tools: Read, Write, Edit, Bash, mcp__memory-supabase__search_memories, mcp__context7__get-library-docs, mcp__dify-kb__dify_kb_retrieve_segments
category: dev
quality_score: 80
updated: "2026-02-04T18:00:00Z"
---
# PHP Fullstack Engineer
Skill para desenvolvimento PHP moderno seguindo padrões Descomplicar®.
## Quando Usar
- Criar APIs RESTful
- Implementar autenticação (JWT, OAuth, Sanctum)
- Desenvolver service classes
- Refactorizar código legacy
- Integrar com APIs externas
## Protocolo Obrigatório
### 1. Pesquisa Inicial
```
mcp__memory-supabase__search_memories "[keywords php/laravel]"
mcp__wikijs__search_pages "[framework] best practices"
mcp__context7__get-library-docs para documentação actualizada
```
### 2. Quality Gate 70+
- Funções < 50 linhas
- Classes < 500 linhas
- Cyclomatic complexity < 10
- PHPDoc em todas as funções públicas
- Type hints em parâmetros e retornos
### 3. Assinatura Obrigatória
```php
<?php
/**
* [Nome do Ficheiro/Classe]
*
* @author Descomplicar® Crescimento Digital
* @link https://descomplicar.pt
* @copyright 2025 Descomplicar®
*/
```
## Padrões de Código
### PHP 8.1+ Features
```php
// Typed properties
private readonly string $name;
// Constructor promotion
public function __construct(
private readonly ProductService $service
) {}
// Enums
enum Status: string {
case Active = 'active';
case Inactive = 'inactive';
}
// Match expression
$result = match($status) {
Status::Active => 'Activo',
Status::Inactive => 'Inactivo',
};
// Named arguments
$this->service->create(
name: $name,
price: $price
);
```
### PSR Standards
- **PSR-1**: Basic Coding Standard
- **PSR-4**: Autoloading
- **PSR-12**: Extended Coding Style
- **PSR-7**: HTTP Message Interface
- **PSR-15**: HTTP Handlers
## Segurança (OBRIGATÓRIO)
```php
// SQL - SEMPRE prepared statements
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
// Input validation
$email = filter_var($input, FILTER_VALIDATE_EMAIL);
// Output escaping
echo htmlspecialchars($output, ENT_QUOTES, 'UTF-8');
// Password hashing
$hash = password_hash($password, PASSWORD_ARGON2ID);
// CSRF em forms
<input type="hidden" name="_token" value="<?= csrf_token() ?>">
```
## Padrão Laravel
### Controller
```php
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreProductRequest;
use App\Http\Resources\ProductResource;
use App\Services\ProductService;
use Illuminate\Http\JsonResponse;
/**
* ProductController - API de Produtos
*
* @author Descomplicar® Crescimento Digital
* @link https://descomplicar.pt
* @copyright 2025 Descomplicar®
*/
class ProductController extends Controller
{
public function __construct(
private readonly ProductService $productService
) {}
public function index(): JsonResponse
{
$products = $this->productService->paginate();
return ProductResource::collection($products)->response();
}
public function store(StoreProductRequest $request): JsonResponse
{
$product = $this->productService->create($request->validated());
return (new ProductResource($product))
->response()
->setStatusCode(201);
}
}
```
### Service
```php
<?php
namespace App\Services;
use App\Models\Product;
use App\Repositories\ProductRepository;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* ProductService - Lógica de negócio Produtos
*
* @author Descomplicar® Crescimento Digital
* @link https://descomplicar.pt
* @copyright 2025 Descomplicar®
*/
class ProductService
{
public function __construct(
private readonly ProductRepository $repository
) {}
public function paginate(array $filters = []): LengthAwarePaginator
{
return $this->repository->paginate($filters);
}
public function create(array $data): Product
{
return $this->repository->create($data);
}
}
```
### Repository
```php
<?php
namespace App\Repositories;
use App\Models\Product;
use Illuminate\Pagination\LengthAwarePaginator;
/**
* ProductRepository - Acesso a dados Produtos
*
* @author Descomplicar® Crescimento Digital
* @link https://descomplicar.pt
* @copyright 2025 Descomplicar®
*/
class ProductRepository
{
public function paginate(array $filters = []): LengthAwarePaginator
{
return Product::query()
->when($filters['category'] ?? null, fn($q, $cat) => $q->where('category_id', $cat))
->when($filters['active'] ?? null, fn($q, $active) => $q->where('active', $active))
->orderBy('created_at', 'desc')
->paginate($filters['per_page'] ?? 15);
}
public function create(array $data): Product
{
return Product::create($data);
}
}
```
## Debugging PHP
### Xdebug
```php
// php.ini
zend_extension=xdebug.so
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=127.0.0.1
xdebug.client_port=9003
```
### Logging
```php
// Monolog exemplo
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = new Logger('app');
$log->pushHandler(new StreamHandler('logs/app.log', Logger::DEBUG));
$log->info('User logged in', ['user_id' => $userId]);
$log->error('Database error', ['exception' => $e->getMessage()]);
```
### Profiling
```bash
# Com Blackfire
blackfire run php script.php
# Com Xhprof
php -d extension=xhprof.so script.php
```
## Testes Unitários
### PHPUnit
```php
<?php
use PHPUnit\Framework\TestCase;
class ProductServiceTest extends TestCase
{
private ProductService $service;
protected function setUp(): void
{
$this->service = new ProductService(
new InMemoryProductRepository()
);
}
public function test_create_product(): void
{
$product = $this->service->create([
'name' => 'Test Product',
'price' => 99.99
]);
$this->assertInstanceOf(Product::class, $product);
$this->assertEquals('Test Product', $product->name);
}
}
```
### Pest (alternativa moderna)
```php
<?php
test('creates product', function () {
$product = app(ProductService::class)->create([
'name' => 'Test',
'price' => 99.99
]);
expect($product)
->toBeInstanceOf(Product::class)
->name->toBe('Test');
});
```
## Checklist Entrega
- [ ] Assinatura Descomplicar® em todos os ficheiros
- [ ] php -l sem erros
- [ ] PHPDoc em funções públicas
- [ ] Type hints completos
- [ ] PSR-12 compliant
- [ ] Segurança validada (injection, XSS)
- [ ] Se API: documentação endpoints
- [ ] Testes unitários (cobertura >70%)
---
## Datasets Dify (Consulta Obrigatória)
Em caso de dúvidas ou para aprofundar conhecimento, consultar os seguintes datasets via MCP:
| Dataset | ID | Prioridade |
|---------|----|-----------:|
| **TI (Tecnologia da Informação)** | `7f63ec0c-6321-488c-b107-980140199850` | 1 |
| **Desenvolvimento de Software** | `e7c7decc-0ded-4351-ab14-b110b3c38ec9` | 1 |
| **Wordpress** | `9da0b2b9-5051-4b99-b9f6-20bf35067092` | 2 |
| **PerfexCRM** | `43354eb6-f0b2-40cc-aa53-44e375ab347c` | 3 |
### Como Consultar
```javascript
// Pesquisar padrões Laravel
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "e7c7decc-0ded-4351-ab14-b110b3c38ec9",
query: "laravel service repository pattern"
})
// APIs e autenticação
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "7f63ec0c-6321-488c-b107-980140199850",
query: "api rest jwt sanctum"
})
// Desenvolvimento Perfex
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "43354eb6-f0b2-40cc-aa53-44e375ab347c",
query: "module development hooks"
})
```
### Quando Consultar
- Implementar padrões de arquitectura
- Desenvolver APIs RESTful
- Integrar sistemas de autenticação
- Criar módulos Perfex CRM
- Refactorizar código legacy
---
**Versão**: 1.0.0 | **Autor**: Descomplicar®

View File

@@ -0,0 +1,586 @@
---
name: react-patterns
description: Modern React patterns and best practices. Implements hooks, context,
composition, and performance optimization patterns. Use when user mentions "react
patterns", "hooks", "react best practices", "component design", "react optimization".
author: Descomplicar® Crescimento Digital
version: 2.0.0
quality_score: 80
user_invocable: true
desk_task: 1478
---
# React Patterns
Skill para desenvolvimento React seguindo padrões modernos (React 18+/19).
## Quando Usar
- Desenvolver componentes React
- Implementar state management
- Optimizar performance (memoization, lazy loading)
- Migrar de class components para hooks
- Usar Server Components (Next.js App Router)
## Regras Core
### Hooks Fundamentais
```jsx
// ✅ useState - estado simples
const [count, setCount] = useState(0)
const [user, setUser] = useState<User | null>(null)
// ✅ useEffect - side effects
useEffect(() => {
fetchData()
return () => cleanup() // cleanup function
}, [dependency]) // array dependências
// ✅ useCallback - memoizar funções
const handleClick = useCallback(() => {
doSomething(id)
}, [id])
// ✅ useMemo - memoizar valores computados
const expensiveValue = useMemo(() => {
return computeExpensive(data)
}, [data])
// ✅ useRef - referências mutáveis
const inputRef = useRef<HTMLInputElement>(null)
```
### Component Patterns
```jsx
// ✅ CORRECTO: Functional components
function UserCard({ user, onSelect }: Props) {
return (
<div onClick={() => onSelect(user.id)}>
{user.name}
</div>
)
}
// ✅ CORRECTO: Composition over props drilling
function Layout({ children }) {
return <main className="container">{children}</main>
}
// ❌ ERRADO: Class components (legacy)
class UserCard extends React.Component { ... }
```
### State Management
```jsx
// ✅ Local state: useState
// Para estado de um componente
// ✅ Shared state: Context + useReducer
const AppContext = createContext<AppState | null>(null)
function AppProvider({ children }) {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<AppContext.Provider value={{ state, dispatch }}>
{children}
</AppContext.Provider>
)
}
// ✅ Server state: React Query / SWR
const { data, isLoading } = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
})
// ❌ ERRADO: Redux para tudo
// ❌ ERRADO: Estado global para estado local
```
### Server Components (Next.js 13+)
```jsx
// ✅ Server Component (default) - async, data fetching
async function UserList() {
const users = await db.users.findMany() // Direct DB access
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}
// ✅ Client Component - interactividade
'use client'
function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
// ✅ Padrão: Server parent + Client children
// page.tsx (Server) → InteractiveSection.tsx ('use client')
```
### Performance
```jsx
// ✅ Lazy loading componentes
const HeavyComponent = lazy(() => import('./HeavyComponent'))
<Suspense fallback={<Loading />}>
<HeavyComponent />
</Suspense>
// ✅ React.memo para componentes puros
const MemoizedCard = memo(function Card({ data }) {
return <div>{data.title}</div>
})
// ✅ Virtualização para listas grandes
import { useVirtualizer } from '@tanstack/react-virtual'
```
## Anti-Patterns
| Anti-Pattern | Problema | Solução |
|--------------|----------|---------|
| useEffect para tudo | Complexidade | Server Components / React Query |
| Props drilling | Manutenção | Context ou Composition |
| Inline functions em JSX | Re-renders | useCallback |
| State em URL | Perda estado | URL state com hooks |
| Class components | Legacy | Functional + Hooks |
| Index como key | Bugs listas | ID único |
## Checklist Componente
- [ ] Functional component (não class)
- [ ] TypeScript types/interfaces
- [ ] Props destructuring com defaults
- [ ] Hooks no topo (não condicionais)
- [ ] Keys únicos em listas
- [ ] Error boundaries para erros
- [ ] Loading states considerados
## Estrutura Ficheiros
```
components/
├── ui/ # Componentes base (Button, Input, Card)
├── features/ # Componentes feature-specific
├── layouts/ # Layout components
└── providers/ # Context providers
hooks/
├── useAuth.ts
├── useLocalStorage.ts
└── useDebounce.ts
```
## Custom Hooks Úteis
### useDebounce
```tsx
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// Uso
const searchTerm = useDebounce(input, 500);
```
### useLocalStorage
```tsx
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
};
return [storedValue, setValue] as const;
}
```
### useFetch
```tsx
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
```
## Padrões Avançados
### Compound Components
```tsx
// Tab.tsx
const TabsContext = createContext<{
activeTab: string;
setActiveTab: (id: string) => void;
} | null>(null);
function Tabs({ children, defaultTab }: Props) {
const [activeTab, setActiveTab] = useState(defaultTab);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
{children}
</TabsContext.Provider>
);
}
Tabs.List = function TabList({ children }: Props) {
return <div className="tab-list">{children}</div>;
};
Tabs.Tab = function Tab({ id, children }: Props) {
const { activeTab, setActiveTab } = useContext(TabsContext)!;
return (
<button
className={activeTab === id ? 'active' : ''}
onClick={() => setActiveTab(id)}
>
{children}
</button>
);
};
Tabs.Panel = function TabPanel({ id, children }: Props) {
const { activeTab } = useContext(TabsContext)!;
return activeTab === id ? <div>{children}</div> : null;
};
// Uso
<Tabs defaultTab="home">
<Tabs.List>
<Tabs.Tab id="home">Home</Tabs.Tab>
<Tabs.Tab id="profile">Profile</Tabs.Tab>
</Tabs.List>
<Tabs.Panel id="home">Home content</Tabs.Panel>
<Tabs.Panel id="profile">Profile content</Tabs.Panel>
</Tabs>
```
### Render Props (legacy, preferir hooks)
```tsx
// Evitar - usar custom hook
<DataFetcher url="/api/users" render={data => <UserList users={data} />} />
// Preferir
const { data } = useFetch('/api/users');
return <UserList users={data} />;
```
## Composition Patterns (Vercel Engineering)
Padrões de composição para componentes React escaláveis. Fonte: Vercel composition-patterns.
### Regra #1: Evitar Boolean Props (CRITICAL)
```tsx
// ❌ ERRADO: Booleans duplicam estados possíveis
<Composer isThread isEditing={false} showAttachments showFormatting={false} />
// ✅ CORRECTO: Variantes explícitas
<ThreadComposer channelId="abc" />
<EditMessageComposer messageId="xyz" />
<ForwardMessageComposer messageId="123" />
```
Cada boolean duplica os estados possíveis. 5 booleans = 32 combinações, maioria impossível.
### Regra #2: Compound Components com Context (HIGH)
```tsx
// ✅ Compound Components: context partilhado, composição explícita
const ComposerContext = createContext<ComposerContextValue | null>(null)
function ComposerFrame({ children }: { children: React.ReactNode }) {
return <form>{children}</form>
}
function ComposerInput() {
const { state, actions: { update }, meta } = use(ComposerContext)
return (
<TextInput
ref={meta.inputRef}
value={state.input}
onChangeText={(text) => update(s => ({ ...s, input: text }))}
/>
)
}
// Exportar como compound
const Composer = {
Frame: ComposerFrame,
Input: ComposerInput,
Submit: ComposerSubmit,
Footer: ComposerFooter,
}
// Uso: composição clara do que cada variante renderiza
<Composer.Frame>
<Composer.Input />
<Composer.Footer>
<Composer.Submit />
</Composer.Footer>
</Composer.Frame>
```
### Regra #3: State em Providers (HIGH)
```tsx
// ❌ ERRADO: Estado preso dentro do componente
function ForwardComposer() {
const [state, setState] = useState(initialState)
return <Composer.Frame>...</Composer.Frame>
}
// Como ForwardButton acede ao state? useEffect sync? Refs? Prop drilling?
// ✅ CORRECTO: State lifted para Provider
function ForwardProvider({ children }: { children: React.ReactNode }) {
const [state, setState] = useState(initialState)
const submit = useForwardMessage()
const inputRef = useRef(null)
return (
<Composer.Provider
state={state}
actions={{ update: setState, submit }}
meta={{ inputRef }}
>
{children}
</Composer.Provider>
)
}
// Agora QUALQUER componente dentro do Provider acede ao state
function ForwardDialog() {
return (
<ForwardProvider>
<Dialog>
<ForwardComposer />
<MessagePreview /> {/* acede state via context */}
<ForwardButton /> {/* acede submit via context */}
</Dialog>
</ForwardProvider>
)
}
// Botão FORA do Composer.Frame mas DENTRO do Provider
function ForwardButton() {
const { actions } = use(ComposerContext)
return <Button onPress={actions.submit}>Forward</Button>
}
```
**Princípio chave:** O boundary do Provider é o que importa, não o nesting visual.
### Regra #4: Interface Genérica para Dependency Injection (HIGH)
```tsx
// ✅ Definir interface genérica: state + actions + meta
interface ComposerContextValue {
state: { input: string; attachments: Attachment[]; isSubmitting: boolean }
actions: { update: (fn: (s: State) => State) => void; submit: () => void }
meta: { inputRef: React.RefObject<TextInput> }
}
// ✅ Múltiplos providers implementam a MESMA interface
function ChannelProvider({ channelId, children }) {
const { state, update, submit } = useGlobalChannel(channelId)
return <Composer.Provider state={state} actions={{ update, submit }}>{children}</Composer.Provider>
}
function ForwardProvider({ children }) {
const [state, setState] = useState(initialState)
return <Composer.Provider state={state} actions={{ update: setState, submit: forward }}>{children}</Composer.Provider>
}
// O mesmo Composer.Input funciona com AMBOS os providers!
```
**Princípio:** UI = pedaços reutilizáveis compostos. State = injectado pelo provider. Trocar provider, manter UI.
### Regra #5: Children > Render Props (MEDIUM)
```tsx
// ❌ Render props (inflexível, difícil de ler)
<Composer
renderHeader={() => <Header />}
renderFooter={() => <><Formatting /><Emojis /></>}
/>
// ✅ Children (composição natural)
<Composer.Frame>
<Header />
<Composer.Input />
<Composer.Footer>
<Formatting />
<Emojis />
</Composer.Footer>
</Composer.Frame>
// Excepção: render props quando parent passa dados
<List data={items} renderItem={({ item }) => <Item item={item} />} />
```
### Regra #6: React 19 APIs (MEDIUM)
```tsx
// ✅ React 19: ref como prop normal (sem forwardRef)
function Input({ ref, ...props }: Props & { ref?: React.Ref<HTMLInputElement> }) {
return <input ref={ref} {...props} />
}
// ✅ React 19: use() em vez de useContext()
const value = use(MyContext) // pode ser chamado condicionalmente!
```
### Checklist Composição
- [ ] Zero boolean props para variantes (usar componentes explícitos)
- [ ] Compound components com context partilhado
- [ ] State em Provider (não dentro do componente UI)
- [ ] Interface genérica (state/actions/meta)
- [ ] Children para composição, render props só para data passthrough
- [ ] React 19: `use()` e ref como prop
---
## Datasets Dify (Consulta Obrigatória)
Em caso de dúvidas ou para aprofundar conhecimento, consultar os seguintes datasets via MCP:
| Dataset | ID | Prioridade |
|---------|----|-----------:|
| **TI (Tecnologia da Informação)** | `7f63ec0c-6321-488c-b107-980140199850` | 1 |
| **Desenvolvimento de Software** | `e7c7decc-0ded-4351-ab14-b110b3c38ec9` | 1 |
| **Desenvolvimento de WebSites** | `c8489151-de94-42b2-8cee-c0b961cfac6d` | 2 |
| **UX e Usabilidade** | `e14ab89e-8910-43b6-becf-d57c78afd62d` | 3 |
### Como Consultar
```javascript
// Pesquisar padrões React
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "e7c7decc-0ded-4351-ab14-b110b3c38ec9",
query: "react hooks state management"
})
// Server Components Next.js
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "7f63ec0c-6321-488c-b107-980140199850",
query: "nextjs server components app router"
})
// UX e performance
mcp__dify-kb__dify_kb_retrieve_segments({
dataset_id: "e14ab89e-8910-43b6-becf-d57c78afd62d",
query: "performance loading states"
})
```
### Quando Consultar
- Implementar hooks customizados
- Escolher state management
- Optimizar performance (memoization, lazy loading)
- Server Components vs Client Components
- Padrões de UX em React
---
**Versão**: 1.0.0 | **Autor**: Descomplicar®
---
## Quando NÃO Usar
- Para tarefas fora do domínio de especialização desta skill
- Quando outra skill mais específica está disponível
- Para operações que requerem aprovação manual obrigatória
- Quando os requisitos não estão claramente definidos
## Protocolo de Execução
1. **Análise Inicial**
- Verificar requisitos e contexto
- Identificar ferramentas necessárias
2. **Preparação**
- Validar acesso a recursos
- Preparar ambiente de trabalho
3. **Execução**
- Executar operações de forma incremental
- Validar cada passo antes de prosseguir
4. **Validação**
- Verificar resultados obtidos
- Confirmar sucesso da operação
5. **Conclusão**
- Documentar alterações realizadas
- Reportar status final e próximos passos
## Exemplos de Uso
### Exemplo 1: Caso Básico
```
User: [requisição simples relacionada com react-patterns]
Skill: [execução directa com validação]
Output: [resultado conciso e accionável]
```
### Exemplo 2: Caso Complexo
```
User: [requisição multi-passo ou complexa]
Skill:
1. Análise dos requisitos
2. Planeamento da abordagem
3. Execução faseada
4. Validação contínua
Output: [resultado detalhado com próximos passos]
```
### Exemplo 3: Caso com Dependências
```
User: [requisição que depende de outros sistemas]
Skill:
1. Verificar dependências disponíveis
2. Coordenar com skills/MCPs necessários
3. Executar workflow integrado
Output: [resultado completo com referências]
```