24b0b68ed0
Plugin acidaos (novo): - rust-dev: desenvolvimento Core em Rust (Axum, crates, debug compiler) - spoke-dev: desenvolvimento Spokes em Next.js/TypeScript + Storybook - devops: pipelines Gitea Actions CI/CD (adaptado de GitHub para Gitea) - docs: rustdoc, TypeDoc, Outline e ADRs dev-tools: - prompt-refine: skill genérica de engenharia de prompts para agentes IA Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
240 lines
5.9 KiB
Markdown
240 lines
5.9 KiB
Markdown
---
|
|
name: spoke-dev
|
|
description: Desenvolvimento de Spokes do AcidaOS em Next.js/TypeScript — criar aplicações Spoke, componentes React com testes e Storybook. Usar quando "spoke", "dashboard acidaos", "next.js acidaos", "componente react acidaos", "ts-create-spoke", "acidaos ui", "acidaos frontend".
|
|
allowed-tools: Read, Write, Edit, Bash, mcp__memory-supabase__search_memories, mcp__gitea__get_file_content, mcp__gitea__create_file, mcp__gitea__update_file
|
|
---
|
|
|
|
# AcidaOS Spoke Dev
|
|
|
|
Skill para desenvolvimento dos **Spokes do AcidaOS** em Next.js/TypeScript.
|
|
|
|
## Contexto do Projecto
|
|
|
|
Os Spokes são as aplicações de interface que comunicam com o Core via API interna:
|
|
|
|
```
|
|
acidaos-dashboard (Next.js 15 + App Router)
|
|
├── src/
|
|
│ ├── app/ ← App Router pages
|
|
│ ├── components/ ← Componentes React
|
|
│ │ ├── ui/ ← Componentes base (shadcn/ui)
|
|
│ │ └── features/ ← Componentes de funcionalidade
|
|
│ ├── lib/
|
|
│ │ ├── api/ ← Client para AcidaOS Core API
|
|
│ │ └── hooks/ ← React hooks custom
|
|
│ └── types/ ← TypeScript types partilhados
|
|
├── __tests__/ ← Jest + Testing Library
|
|
└── .storybook/ ← Storybook
|
|
```
|
|
|
|
**Stack:** Next.js 15 | TypeScript 5 | React 19 | Tailwind CSS | shadcn/ui | Vercel AI SDK | Vitest | Playwright
|
|
|
|
## Protocolo Inicial
|
|
|
|
```
|
|
mcp__memory-supabase__search_memories "acidaos spoke dashboard [componente]"
|
|
mcp__gitea__get_file_content acidaos-dashboard/package.json
|
|
```
|
|
|
|
---
|
|
|
|
## Operações
|
|
|
|
### 1. Criar nova aplicação Spoke (`/ts-create-spoke-app`)
|
|
|
|
**Input:** Nome do Spoke e propósito
|
|
|
|
**Estrutura a criar:**
|
|
```bash
|
|
# No container dev (Regra #48)
|
|
cd /root/Dev
|
|
npx create-next-app@latest acidaos-<nome> \
|
|
--typescript \
|
|
--tailwind \
|
|
--app \
|
|
--src-dir \
|
|
--import-alias "@/*" \
|
|
--no-git
|
|
```
|
|
|
|
**Ficheiros obrigatórios após scaffold:**
|
|
|
|
`src/lib/api/core-client.ts` — cliente para o Core:
|
|
```typescript
|
|
/**
|
|
* AcidaOS Core API Client
|
|
*
|
|
* @author Descomplicar® Crescimento Digital
|
|
* @link https://descomplicar.pt
|
|
*/
|
|
|
|
const CORE_API_URL = process.env.ACIDAOS_CORE_URL ?? 'http://localhost:3001';
|
|
|
|
export async function coreRequest<T>(
|
|
endpoint: string,
|
|
options?: RequestInit
|
|
): Promise<T> {
|
|
const res = await fetch(`${CORE_API_URL}${endpoint}`, {
|
|
...options,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-AcidaOS-Version': '1',
|
|
...options?.headers,
|
|
},
|
|
});
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`Core API error: ${res.status} ${res.statusText}`);
|
|
}
|
|
|
|
return res.json() as Promise<T>;
|
|
}
|
|
```
|
|
|
|
`src/types/index.ts` — tipos base:
|
|
```typescript
|
|
export interface AgentTask {
|
|
id: string;
|
|
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
createdAt: string;
|
|
completedAt?: string;
|
|
}
|
|
|
|
export interface CoreHealth {
|
|
status: 'healthy' | 'degraded' | 'down';
|
|
version: string;
|
|
uptime: number;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Criar componente React (`/ts-create-react-component`)
|
|
|
|
**Input:** Descrição do componente (nome, props, comportamento)
|
|
|
|
**Template base — componente:**
|
|
|
|
`src/components/features/<Nome>/<Nome>.tsx`:
|
|
```typescript
|
|
/**
|
|
* <Nome> — <Descrição curta>
|
|
*
|
|
* @author Descomplicar® Crescimento Digital
|
|
*/
|
|
|
|
import { type FC } from 'react';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
interface <Nome>Props {
|
|
// props do componente
|
|
className?: string;
|
|
}
|
|
|
|
export const <Nome>: FC<<Nome>Props> = ({ className }) => {
|
|
return (
|
|
<div className={cn('', className)}>
|
|
{/* conteúdo */}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
<Nome>.displayName = '<Nome>';
|
|
```
|
|
|
|
**Template — teste (Vitest + Testing Library):**
|
|
|
|
`src/components/features/<Nome>/<Nome>.test.tsx`:
|
|
```typescript
|
|
import { render, screen } from '@testing-library/react';
|
|
import { describe, it, expect } from 'vitest';
|
|
import { <Nome> } from './<Nome>';
|
|
|
|
describe('<Nome>', () => {
|
|
it('renderiza sem erros', () => {
|
|
render(<<Nome> />);
|
|
// asserção específica ao componente
|
|
});
|
|
|
|
it('aceita className personalizada', () => {
|
|
const { container } = render(<<Nome> className="test-class" />);
|
|
expect(container.firstChild).toHaveClass('test-class');
|
|
});
|
|
});
|
|
```
|
|
|
|
**Template — Storybook:**
|
|
|
|
`src/components/features/<Nome>/<Nome>.stories.tsx`:
|
|
```typescript
|
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
import { <Nome> } from './<Nome>';
|
|
|
|
const meta: Meta<typeof <Nome>> = {
|
|
title: 'Features/<Nome>',
|
|
component: <Nome>,
|
|
parameters: {
|
|
layout: 'centered',
|
|
},
|
|
};
|
|
|
|
export default meta;
|
|
type Story = StoryObj<typeof <Nome>>;
|
|
|
|
export const Default: Story = {
|
|
args: {},
|
|
};
|
|
```
|
|
|
|
**Criar index de exportação:**
|
|
|
|
`src/components/features/<Nome>/index.ts`:
|
|
```typescript
|
|
export { <Nome> } from './<Nome>';
|
|
export type { <Nome>Props } from './<Nome>';
|
|
```
|
|
|
|
---
|
|
|
|
## Padrões Obrigatórios
|
|
|
|
### TypeScript
|
|
- `strict: true` em `tsconfig.json` — sem `any` implícito
|
|
- Props sempre tipadas com interface exportada
|
|
- Hooks custom em `src/lib/hooks/use<Nome>.ts`
|
|
- Server Components por defeito; `'use client'` apenas quando necessário
|
|
|
|
### Performance
|
|
- `next/image` para todas as imagens
|
|
- `loading="lazy"` em componentes pesados
|
|
- `Suspense` boundaries em torno de data-fetching
|
|
- Cache de API calls com `unstable_cache` ou React cache
|
|
|
|
### Segurança
|
|
- Nunca expor `ACIDAOS_CORE_URL` no cliente (apenas server-side)
|
|
- Sanitizar input do utilizador antes de enviar ao Core
|
|
- Usar Next.js Server Actions para mutações
|
|
|
|
---
|
|
|
|
## Quality Gate
|
|
|
|
```bash
|
|
pnpm build # zero erros TypeScript
|
|
pnpm test # todos os testes passam
|
|
pnpm lint # zero erros ESLint
|
|
```
|
|
|
|
## Checklist Entrega
|
|
|
|
- [ ] Tipos TypeScript sem `any`
|
|
- [ ] Testes criados (>80% cobertura)
|
|
- [ ] Story Storybook criada
|
|
- [ ] `displayName` definido no componente
|
|
- [ ] `pnpm build` sem erros
|
|
- [ ] Client/Server components correctamente marcados
|
|
- [ ] CHANGELOG.md actualizado
|
|
|
|
---
|
|
**Versão**: 1.0.0 | **Autor**: Descomplicar® | **Plugin**: acidaos
|