beetask/CLAUDE.md
Gabriel Santana de Menezes 064251f6ab docs: update CLAUDE.md to require branch and PR workflow
Clarify that commits should never go directly to main. All work must go through
feature branches and pull requests for review and traceability.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-18 11:03:26 -04:00

131 lines
7.1 KiB
Markdown

# CLAUDE.md
Guidance for Claude Code when working in this repository. Keep edits aligned with the conventions below.
## Project
**bee-task-v3 / TimeTrack** — task & time-tracking SaaS. Monorepo with React (Vite) frontend and Quarkus backend, PostgreSQL + Keycloak via Docker. Backlog and user stories live in `backlog.md`.
## Layout
```
backend/ Quarkus 3.34, Java 17, Hibernate ORM + Panache, Flyway, JWT (smallrye)
frontend/ Vite + React 19 + TypeScript + Tailwind v4
docker/ Postgres init scripts
docker-compose.yml Postgres + Keycloak
specification/ Domain specs
backlog.md User stories (US-001…)
```
Java root package: `net.beetechgroup.beetask`.
## Commands
Infra: `docker-compose up` (Postgres :5432, Keycloak :8081).
Backend (`cd backend`):
- Dev: `./mvnw quarkus:dev` (port 8080, uses H2 in dev profile, swagger at `/swagger`, OpenAPI at `/openapi`)
- Test: `./mvnw test`
- Build: `./mvnw package`
Frontend (`cd frontend`):
- Dev: `npm run dev` (port 5173, calls backend directly at `http://localhost:8080` via `VITE_API_URL`)
- Build: `npm run build` (runs `tsc -b` then `vite build`)
- Lint: `npm run lint`
## Backend Architecture (Clean Architecture, adapted)
Four layers, dependencies point inward. **Never** import outward.
| Package | Role | May depend on |
|---|---|---|
| `entities/` | Pure domain POJOs and enums (`Task`, `Project`, `User`, `Organization`, `TaskStatus`, …). Plain Java, no annotations. | nothing |
| `usecase/<feature>/<action>/` | Business logic. Plain classes with constructor DI. Records for `XxxInput`/`XxxOutput`. Static `XxxMapper` for domain→Output. | `entities`, `usecase.repository` |
| `usecase/repository/` | Repository **ports** (interfaces) returning domain types. | `entities` |
| `interfaceadapters/controllers/<feature>/` | JAX-RS controllers, `XxxRequest`/`XxxResponse` records, `XxxControllerMapper`. Translates HTTP ↔ use case I/O. | `usecase`, `entities` |
| `frameworks/persistence/` | Panache `XxxEntity`, `XxxRepositoryImpl` (implements port + `PanacheRepository<XxxEntity>`), `XxxEntityMapper` (entity ↔ domain). | everything |
| `frameworks/config/` | `@ApplicationScoped` classes with `@Produces` methods that build use cases from injected repository ports. | everything |
### Use case anatomy
A new use case `usecase/<feature>/<action>/` contains exactly:
- `XxxInput.java` — `record` with primitives/IDs/email (no domain entities in the signature unless intentional)
- `XxxOutput.java` — `record` with the response shape
- `XxxUseCase.java` — plain class, constructor-injected repository ports, single `execute(XxxInput)` method
- `XxxMapper.java` — static `toXxxOutput(domain)` only
Then **wire it** via `@Produces` in the matching `frameworks/config/<Feature>UseCaseConfig.java`. Use cases are NOT annotated with CDI scopes themselves — the producer is what makes them injectable.
### Query parameter DTOs
When a controller endpoint receives **3 or more query/request parameters**, create a dedicated DTO class in the controller package and a mapping method in the `XxxControllerMapper`:
- For **query parameters** (GET): use a plain Java class with public fields annotated with `@QueryParam`, and receive it via `@BeanParam XxxRequest` in the controller method.
- For **request body** parameters (POST/PUT): use a `record XxxRequest(...)` as usual.
- Add `toXxxInput(XxxRequest request, ...)` to `XxxControllerMapper` — never inline the mapping in the controller.
### Controller anatomy
`interfaceadapters/controllers/<feature>/`:
- `XxxController.java` — `@Path`, JAX-RS verbs, `@Inject` use cases + `SecurityIdentity`, OpenAPI annotations (`@Tag`, `@Operation`, `@APIResponse`)
- `XxxRequest.java` / `XxxResponse.java` — records mirroring the use case Input/Output (don't reuse use case records in HTTP layer)
- `XxxControllerMapper.java` — static `toXxxInput(request, email)` / `toXxxResponse(output)`
The authenticated email comes from `securityIdentity.getPrincipal().getName()` and is passed into the Input.
### Repository pattern
- Port: `usecase/repository/XxxRepository.java`, methods return **domain** types.
- Impl: `frameworks/persistence/repository/XxxRepositoryImpl.java`:
- `@ApplicationScoped`
- `implements XxxRepository, PanacheRepository<XxxEntity>`
- `@Transactional` on writes
- Uses `XxxEntityMapper.toEntity` / `toDomain` at the boundary
### DB / migrations
- Postgres in prod, **H2 in dev** (`%dev` profile, file at `backend/data/beetask`).
- `quarkus.hibernate-orm.database.generation=validate` (prod) / `update` (dev).
- Flyway migrations live at `backend/src/main/resources/db/migration/V{N}__{snake_name}.sql`.
- `quarkus.flyway.migrate-at-start=false` — migrations are not auto-applied; run them deliberately.
### Auth
- JWT signed with `privateKey.pem`, verified with `publicKey.pem`. Issuer `https://beetech.net`.
- Keycloak is provisioned in docker-compose but **not yet integrated** into the app — JWTs are minted by `usecase/auth/login`.
## Frontend Conventions
- **React 19**, **Tailwind v4** (`@tailwindcss/vite`, no Tailwind config file required), **react-router-dom v7**.
- Folder layout: `pages/`, `components/`, `services/`, `contexts/`, `types/`, `lib/`.
- HTTP through `apiFetch` in `src/lib/api.ts` — it auto-injects the JWT from `localStorage.user.jwt` and prefixes `VITE_API_URL` (defaults to `http://localhost:8080` em dev; configurar para a URL real do backend em prod).
- Each backend feature gets a service in `services/<feature>Service.ts` exporting an object of methods.
- Class merging uses `cn(...inputs)` from `src/lib/utils.ts` (clsx + tailwind-merge).
- Icons: `lucide-react`. Animations: `framer-motion`. DnD: `@hello-pangea/dnd`.
- Auth state: `AuthContext` (`src/contexts/AuthContext.tsx`) — `user`, `activeOrg`, `login`, `logout`, `refreshUser`. Persisted in `localStorage` (`user`, `activeOrgId`).
- Authenticated routes nest under `<AppShell />` in `src/router.tsx`. Role-restricted routes wrap with `<RoleGate allowedRoles={["OWNER", "ADMIN"]}>`.
- UI strings are in pt-BR. Keep that consistent.
## Conventions & Constraints
- Don't put business logic in controllers — they only translate HTTP ↔ use case I/O.
- **Never return a use case Output record directly from a controller.** Controllers must define their own `XxxResponse` records and use `XxxControllerMapper.toResponse(output)` to convert. Use case I/O types must not leak into the HTTP layer.
- Don't import `jakarta.*`, `io.quarkus.*`, or Panache from `entities/` or `usecase/` (except `usecase.repository` ports which are pure interfaces).
- Don't reach for a UI library on the frontend — Tailwind only.
- Don't create files outside the conventional folders.
- Vertical slices first; refactor when a pattern repeats. The backlog is in `backlog.md`.
## Git Workflow
**Never commit directly to `main`.** Always:
1. Create a feature branch: `git checkout -b feature/short-description`
2. Make commits to the branch
3. Create a pull request (GitHub) for review
4. Merge via PR after approval
This ensures code review, clear history, and traceability.
## Skills
Skills do projeto em `.claude/skills/`:
- `new-task` — workflow completo: ler issue → branch → código → commit → PR.