# SPEC — Money privacy (eye button) **Produto:** Rently / RentCharge **Data:** 2026-09-19 **Status:** implemented (prod + test) **Versão:** 007.2026 **Operador:** Roger **Relacionados:** `SPEC-SYSTEM-DASHBOARD.md` · `SPEC-SESSION-2026-09-19.md` --- ## Purpose Operators often work in open offices. Euro amounts start **hidden** (`€ ••••`). One control (header + page toolbars) reveals or hides **all** sensitive amounts together. After **30 seconds** idle, amounts hide again. ## UI | Control | Location | |---------|----------| | Show € / Hide € | AppShell header (all authenticated pages) | | Same button | Page toolbars (Dashboard, Residents, Mapa, Receivables, …) | ## Technical contract 1. Module flag: `frontend/src/lib/moneyFormat.ts` — `euro()`, `privacyMoneyLabel()`, `setMoneyHiddenFlag()`. 2. Flag changes **notify** subscribers (`subscribeMoneyHidden`). 3. React: `useMoneyPrivacyRender()` / `useMoneyPrivacy()` use `useSyncExternalStore` so components re-render when the eye toggles. 4. **Why not only AppShell?** Next.js App Router does not re-render the page slot when a Client layout updates state. Every page that displays euros must subscribe (or use a child that does). ## Rules - Print / statement handouts use `euroVisible()` (never masked). - Counts and percentages are not masked. - Default on load: **hidden**. ## Acceptance 1. Dashboard: eye toggles amounts. 2. `/residents`, `/mapa`, `/receivables`, resident detail: same toggle updates amounts without navigation. 3. Idle ~30s → amounts hide again. 4. Prod (`:3000`) and test (`:3010`) behave the same.