Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Repository Guidelines

## Project Structure & Module Organization
- `src/app/`: Next.js App Router. Routes: `blog/`, `articles/`, `projects/`, `about/`, `services/`, `thanks/`.
- `src/app/components/`: Reusable UI (e.g., `Navbar/`, `Footer/`, `ArticleCard/`, `ContactForm/`). Use one component per folder with `index.tsx`.
- `src/data/`, `src/utils/`: Static data and helpers (e.g., `content.ts`).
- `public/`: Static assets, `robots.txt`, `sitemap.xml`. Served at site root.
- `out/`: Static export output for deployment (don’t edit manually).
- Config: `next.config.ts`, `tailwind.config.ts`, `eslint.config.mjs`, `.htaccess`.

## Build, Test, and Development Commands
- `npm run dev`: Dev server with Turbopack at `http://localhost:3000`.
- `npm run build`: Production build. If using static hosting, enable `output: 'export'` in `next.config.ts` to generate `out/`.
- `npm start`: Run the production server locally.
- `npm run lint`: ESLint (Next core-web-vitals + TS). Auto-fix: `npm run lint -- --fix`.
- Preview static build: `npx http-server ./out -p 8000`.

## Coding Style & Naming Conventions
- TypeScript; 2-space indent. Components in PascalCase; route segments lowercase (e.g., `src/app/blog/page.tsx`).
- Tailwind CSS in JSX; shared styles in `src/app/globals.css`.
- Keep pages self-contained with `metadata.ts` per route when relevant.

## Testing Guidelines
- No tests configured. Validate via `npm run dev`. If adding tests: Jest + Testing Library for unit/UI; Playwright for e2e. Place under `src/**/__tests__` or as `*.test.ts(x)`.

## Commit & Pull Request Guidelines
- Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`, `refactor:`). Example: `feat(services): add static contact form`.
- PRs: clear description, linked issues, screenshots for UI, and confirm `npm run lint`/`build` pass.

## SEO & Content
- Global SEO in `src/app/layout.tsx` (Open Graph, Twitter, JSON-LD). Per-page metadata in `src/app/**/metadata.ts`.
- Keep a single H1 por página; use descrições objetivas e CTAs claros.
- Atualize `sitemap.xml`/`robots.txt` ao criar rotas novas (ou migre para `src/app/sitemap.ts` e `robots.ts`).
- Prefira imagens OG 1200x630 em `public/images/` e referencie em metadata.

## Contato (Formulário Estático)
- `ContactForm` usa FormSubmit (sem backend). Para trocar e-mail, edite `action` no componente.
- Redireciona para `/thanks`. Honeypot ativado; sem captcha por padrão.
32 changes: 32 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
SHELL := /bin/bash

# Config
PORT ?= 8000
HOST ?= 127.0.0.1

.PHONY: help install dev build serve build-serve

## help: Show available targets
help:
@grep -E '^##' Makefile | sed -E 's/## //' | column -t -s ':'

## install: Install project dependencies (npm ci fallback to npm install)
install:
npm ci || npm install

## dev: Run Next.js in development mode at http://localhost:3000
dev:
npm run dev

## build: Build production (static export expected into ./out)
build:
npm run build

## serve: Serve the static ./out folder on $(HOST):$(PORT)
serve:
npx http-server ./out -a $(HOST) -p $(PORT) -c-1

## build-serve: Build then serve the static ./out folder
build-serve:
npm run build && npx http-server ./out -a $(HOST) -p $(PORT) -c-1

6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ O HelpDev é um portal dedicado a compartilhar conhecimento sobre desenvolviment

1. Clone o repositório:
```bash
git clone https://github.com/seu-usuario/helpdev.git
cd helpdev
git clone https://github.com/gbzarelli/helpdev.com.br.git
cd helpdev.com.br
```

2. Instale as dependências:
Expand Down Expand Up @@ -89,7 +89,7 @@ O projeto inclui um arquivo `.htaccess` com as seguintes configurações:
## 📝 Estrutura do Projeto

```
helpdev/
helpdev.com.br/
├── src/
│ ├── app/
│ │ ├── components/
Expand Down
2 changes: 2 additions & 0 deletions next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const withBundleAnalyzer = require('@next/bundle-analyzer')({
});

const nextConfig: NextConfig = {
output: 'export',
experimental: {
// Enable Turbopack optimizations
serverComponentsHmrCache: true, // Cache Server Components across HMR
Expand All @@ -21,6 +22,7 @@ const nextConfig: NextConfig = {

// Image optimization configuration
images: {
unoptimized: true,
remotePatterns: [
{
protocol: 'https',
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"analyze": "ANALYZE=true npm run build"
"analyze": "ANALYZE=true npm run build",
"serve:static": "npx http-server ./out -p 8000",
"build:serve": "npm run build && npx http-server ./out -p 8000"
},
"dependencies": {
"next": "15.3.0",
Expand Down
118 changes: 118 additions & 0 deletions src/app/components/ContactForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React from 'react';

type Props = {
redirectPath?: string;
subject?: string;
};

export function ContactForm({
redirectPath = '/thanks',
subject = 'Novo contato - Proposta | HelpDev',
}: Props) {
const actionUrl = 'https://formsubmit.co/gbzarelli@helpdev.com.br';

return (
<form
action={actionUrl}
method="POST"
className="bg-white text-gray-900 p-6 rounded-xl shadow-md max-w-2xl mx-auto text-left"
>
{/* FormSubmit configuration */}
<input type="hidden" name="_next" value={redirectPath} />
<input type="hidden" name="_subject" value={subject} />
<input type="hidden" name="_captcha" value="false" />
<input type="text" name="_honey" className="hidden" aria-hidden="true" />

<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-800">Nome</label>
<input
id="name"
name="name"
type="text"
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 placeholder-gray-400"
placeholder="Seu nome"
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-800">E-mail</label>
<input
id="email"
name="email"
type="email"
required
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 placeholder-gray-400"
placeholder="seu@email.com"
/>
</div>
<div>
<label htmlFor="company" className="block text-sm font-medium text-gray-800">Empresa (opcional)</label>
<input
id="company"
name="company"
type="text"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 placeholder-gray-400"
placeholder="Nome da empresa"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label htmlFor="budget" className="block text-sm font-medium text-gray-800">Orçamento</label>
<select
id="budget"
name="budget"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 bg-white"
defaultValue=""
>
<option value="" disabled>Selecione…</option>
<option value="<10k">Até R$10k</option>
<option value="10-50k">R$10k–R$50k</option>
<option value=">50k">Acima de R$50k</option>
<option value="avaliar">A avaliar</option>
</select>
</div>
<div>
<label htmlFor="timeline" className="block text-sm font-medium text-gray-800">Prazo</label>
<select
id="timeline"
name="timeline"
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 bg-white"
defaultValue=""
>
<option value="" disabled>Selecione…</option>
<option value="urgente">Urgente</option>
<option value="1-3m">1–3 meses</option>
<option value=">3m">Mais de 3 meses</option>
<option value="indefinido">Indefinido</option>
</select>
</div>
</div>
<div className="md:col-span-2">
<label htmlFor="message" className="block text-sm font-medium text-gray-800">Mensagem</label>
<textarea
id="message"
name="message"
required
rows={5}
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 placeholder-gray-400"
placeholder="Conte um pouco sobre o projeto, objetivos e restrições."
/>
</div>
<div className="md:col-span-2 flex items-start">
<input id="consent" name="consent" type="checkbox" required className="mt-1 mr-2" />
<label htmlFor="consent" className="text-sm text-gray-800">Aceito ser contatado por e-mail para tratativas sobre este projeto.</label>
</div>
</div>
<div className="mt-6 flex items-center gap-3">
<button
type="submit"
className="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Enviar proposta
</button>
<a href="mailto:gbzarelli@helpdev.com.br" className="text-blue-600 hover:text-blue-800">ou envie um e-mail</a>
</div>
</form>
);
}
5 changes: 3 additions & 2 deletions src/app/components/Footer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ export const Footer: React.FC = () => {
<div className="flex flex-col md:flex-row justify-between items-center">
<div className="mb-4 md:mb-0">
<h2 className="text-2xl font-bold">HelpDev</h2>
<p className="text-gray-400 mt-2">Compartilhando conhecimento em desenvolvimento de software desde 2013</p>
<p className="text-gray-400 mt-2">Desenvolvimento de Software e Consultoria Técnica</p>
</div>

<div className="flex flex-col md:flex-row gap-8">
<div>
<h3 className="text-lg font-semibold mb-2">Links</h3>
<ul className="space-y-1">
<li><Link href="/" className="text-gray-400 hover:text-white transition-colors">Home</Link></li>
<li><Link href="/services" className="text-gray-400 hover:text-white transition-colors">Serviços</Link></li>
<li><Link href="/about" className="text-gray-400 hover:text-white transition-colors">Sobre</Link></li>
<li><Link href="/articles" className="text-gray-400 hover:text-white transition-colors">Artigos</Link></li>
<li><Link href="/projects" className="text-gray-400 hover:text-white transition-colors">Projetos</Link></li>
<li><Link href="/blog" className="text-gray-400 hover:text-white transition-colors">Blog</Link></li>
<li><Link href="/#contato" className="text-gray-400 hover:text-white transition-colors">Contato</Link></li>
</ul>
</div>

Expand All @@ -47,4 +49,3 @@ export const Footer: React.FC = () => {
};

export default Footer;

34 changes: 34 additions & 0 deletions src/app/components/Navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ export const Navbar = React.memo(() => {
>
Artigos
</Link>
<Link
href="/services"
className={`inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium ${
isActive('/services')
? 'border-blue-500 text-gray-900'
: 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'
}`}
>
Serviços
</Link>
<Link
href="/projects"
className={`inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium ${
Expand Down Expand Up @@ -78,6 +88,12 @@ export const Navbar = React.memo(() => {
>
Sobre
</Link>
<Link
href="/#contato"
className={`inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700`}
>
Contato
</Link>
</div>
</div>

Expand Down Expand Up @@ -137,6 +153,17 @@ export const Navbar = React.memo(() => {
>
Artigos
</Link>
<Link
href="/services"
className={`block pl-3 pr-4 py-2 border-l-4 text-base font-medium ${
isActive('/services')
? 'border-blue-500 text-blue-700 bg-blue-50'
: 'border-transparent text-gray-500 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-700'
}`}
onClick={closeMenu}
>
Serviços
</Link>
<Link
href="/projects"
className={`block pl-3 pr-4 py-2 border-l-4 text-base font-medium ${
Expand Down Expand Up @@ -170,6 +197,13 @@ export const Navbar = React.memo(() => {
>
Sobre
</Link>
<Link
href="/#contato"
className={`block pl-3 pr-4 py-2 border-l-4 text-base font-medium border-transparent text-gray-500 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-700`}
onClick={closeMenu}
>
Contato
</Link>
</div>
</div>
</nav>
Expand Down
Loading