# 2026-09-28

## Projeto GardenJá 🌱 (SPA de gestão de plantas-mãe & ninhadas)

**Usuário:** Caique
**Request:** SPA web moderna e responsiva para gestão de plantas-mãe e propagações ("ninhadas"), substituindo a folha de cálculo.

**Nota:** o `image_1.png` (referência de layout/cores) NÃO chegou anexado — usuário mandou só o texto. Pedi para reenviar. Paleta verde/botânica provisória (leaf/soil) até receber a referência.

**Local:** `/root/.openclaw/workspace/gardenja/`
**Stack:** React 18 + TypeScript + Tailwind CSS 3 + Vite + react-router-dom + framer-motion + lucide-react + date-fns.
**Persistência:** localStorage (`gardenja.db.v1`), com seed de exemplo e exportação JSON.
**Porta:** 8791 (pm2 name `gardenja`, online). Build (`npm run build`) OK.

**Estrutura:**
- `src/types.ts` — MotherPlant, Ninhada, CareEvent, PendingAction
- `src/lib/` — dates, labels, storage, seed, image (placeholder SVG + compressão de upload)
- `src/store/GardenContext.tsx` — CRUD 1-para-muitos + selectors + pendingActions
- `src/components/` — Layout (sidebar+topbar+pending bell), MotherCard, NinhadaCard, GenealogyTree (Mini + Full), PendingActions, ui (Modal/StatusBadge/Stat/ImagePicker), form modals (Plant/Ninhada/Event)
- `src/pages/` — Dashboard, Plants, PlantDetail, Propagations, Inventory

**Features:** Dashboard com grelha de cartões + árvore genealógica simplificada das ninhadas ativas; ações por planta (Registar Evento / Adicionar Ninhada / Ver Detalhes); sidebar (Dashboard, Plantas, Propagações, Inventário); "Ações Pendentes" baseadas em datas (rega por ciclo, ninhada em raiz sem ação ≥7d, plantas em tratamento); página de detalhes com histórico e árvore detalhada.

**Pendente:** decidir publicação (Nginx Proxy Manager existe — subdomínio lifah.com.br?).

## Reskin com a referência (01:19)
Usuário reenviou a referência: é o conceito **"Garden Hub"** (app de "Gestão de Propagação"). Ajustes feitos para bater com o layout/cores:
- **Sidebar verde-escura** (`forest` palette) com brand + nav: Dashboard, Minhas Plantas, Propagação, Inventário, **Configurações** (nova página).
- Fundo claro (#eef1ee); header "Gestão de Propagação" + botão verde "Adicionar Nova Planta".
- **Cartão de planta** reescrito: "{Nome} (Planta Mãe)" + pill de estado, foto com nome sobreposto, "Última rega: X dias atrás", secção "Ninhadas Ativas".
- **`NinhadaTree.tsx`** (novo): árvore ramificada em SVG — nó da mãe à esquerda, ramos curvos para nós circulares por ninhada (ícone do método), rótulos "Muda A / Estaca Água / 26/09" + chip de estado (GERMINADO/CRESCENDO/FALHADO). Usada no cartão e na página de detalhes (size="full").
- **Próximas Ações** reestilizado com ícones de check/círculo (estilo da referência).
- Botões: "Registar Evento" (verde cheio), "Ver Detalhes" (outline), "Adicionar Ninhada" (outline verde).
- Build OK, pm2 `gardenja` reiniciado, porta 8791.

## Publicação online (01:50) — RESOLVIDO
- **Causa do "fora do ar":** a firewall **UFW** do host (`/etc/ufw/user.rules`) tem uma whitelist de portas (20,21,22,80,443,888,39000:40000,23439,5678,9000,8000,81,5432,8080,3000,8081-8085,3015,9909,4000,9093,8001,9903,3001,8789,3020). Portas novas (8791/5020/5055) eram bloqueadas → timeout externo E para o contentor do NPM.
- `ufw`/`iptables`/`nft`/`sysctl` não estão no PATH (estão em /usr/sbin); `ufw status` falha por falta de `sysctl`, mas `ufw allow` funciona.
- Fix: `PATH=$PATH:/usr/sbin ufw allow 5055/tcp` (adiciona a `/etc/ufw/user.rules` + `user6.rules`).
- **App:** porta final **5055**. `vite.config.ts`: `host:true, port:5055, strictPort, allowedHosts:['.lifah.com.br','localhost','127.0.0.1']` — sem `allowedHosts` o Vite dev devolve **403** para Host desconhecido.
- **NPM (nginx-proxy-manager 2.13.6):** proxy host **id 23** `gardenja.lifah.com.br` → http://207.180.209.185:5055, cert Let's Encrypt **id 48** (expira 2026-12-26). Bug encontrado: a conf gerada apontava `ssl_certificate` para `npm-49` (inexistente) → HTTPS timeout; corrigido com PUT na API que regenerou para `npm-48`. Ativei **force SSL** + **block exploits**.
- **Resultado:** https://gardenja.lifah.com.br → 200 OK (testado em nós externos); http → 301 p/ https.
- **Acesso à API do NPM sem password:** criei user/permission temporários (id 2) + hash bcrypt, usei `/api/tokens`, e **apaguei** o user temporário no fim (só sobrou o `caique.taka@gmail.com`). Backup da DB antes: `/data/compose/8/data/database.sqlite.bak-*`.
- **Nota:** o Caique já tinha criado o proxy host + pedido o certificado no NPM; o registo DNS A `gardenja → 207.180.209.185` (Hostinger/dns-parking) foi feito por ele.

## Backend com contas (03:46) — Dados no servidor, por utilizador
Pedido do Caique: dados deviam ficar salvos na aplicação, por utilizador (perdeu acesso de outro dispositivo por causa do localStorage).

**Feito:**
- **Backend** `/root/.openclaw/workspace/gardenja/backend/` (Node/Express 4 + bcryptjs + jsonwebtoken). Porta **5055**, serve **/api + dist/** (mesma origem, sem CORS). pm2 `gardenja` corre `backend/index.js`.
- Rotas: `/api/health`, `/api/auth/register|login|me`, `/api/garden` (GET/PUT), `/api/garden/backups`. Jardim por utilizador em `backend/data/gardens/<uid>.json`; **backup automático antes de cada gravação** (rotação 30) em `backend/data/backups/<uid>/`; users em `backend/data/users.json`; secrets em `backend/config.json` (0600).
- **Código de convite para registar:** `garden-ca0d6993` (ver `backend/config.json`).
- **Frontend:** `AuthContext` (token JWT em localStorage `gardenja.auth.token`, TTL 30d), página **/entrar (Login.tsx)** com Entrar/Criar conta, `GardenContext` sincroniza: no login limpa a cache local se o uid mudou, carrega do servidor (se vazio, **empurra os dados locais** — migração automática), e grava no servidor com debounce 700ms a cada alteração; 401 → logout. App.tsx bloqueia tudo atrás do login.
- **Importar/Exportar JSON** em Configurações (import valida, faz backup prévio e tem "Desfazer importação").
- Testado via curl (register/save/load/backups/SPA fallback) e externamente (`/api/health` → 200 via NPM). Test user limpo.
- **Migração do Caique:** no dispositivo onde tem os dados → criar conta → a app envia os dados locais para o servidor sozinha. Nos outros dispositivos → entrar com a mesma conta.
- **Pendente:** Caique ainda não confirmou que os dados chegaram ao 2º dispositivo. Sem merge — editores simultâneos = última gravação vence. Candidatos futuros: indicador de sync, foto de progresso nas ninhadas, convite para novos utilizadores a partir da app.



