Imported from arremata/leilao-br (
frontend/AGENTS.md). Install upstream withnpx skills add arremata/leilao-br --skill frontend. Copyright stays with the author.
Frontend — Arremate React SPA
Overview
React 19 SPA (Vite) for browsing and analyzing Brazilian real estate auctions. Demo-ready with 3 real auction properties, real photos, and real market comparables. Responsive PWA with watchlist and history. Deployed to Vercel.
Commands
npm run dev # dev server with HMR (localhost:5173)
npm run build # production build to dist/
npm run lint # eslint
npm run preview # preview production build
Architecture
Four-Screen SPA
| Screen | Component | Route Key | Description |
|---|---|---|---|
| Feed | Feed.jsx |
feed |
Property grid/list with filters |
| Detail | PropertyDetail.jsx |
detail |
Full analysis modal with tabs |
| Watchlist | Watchlist.jsx |
watchlist |
Locally saved properties |
| History | History.jsx |
history |
Locally viewed properties |
Navigation via go(screen, prop) in App.jsx. Screen and watchlist persist to localStorage.
Files
| File | Purpose |
|---|---|
src/App.jsx |
Root: screen state, API calls, TopBar with URL analyzer input |
src/main.jsx |
Entry point (React 19 + StrictMode) |
src/api.js |
fetchProperties() and analyzeUrl() — calls proxied via /api |
src/components/shared.jsx |
Reusable components (Countdown, Photo (supports photoUrl), Sparkline, RiskSummary, RiskDots, Specs, PropertyCard, PropertyRow) + fixtures + helpers (fmtBRL, getEndsAtMs) |
src/components/Feed.jsx |
Feed screen with grid/list toggle, filters |
src/components/PropertyDetail.jsx |
Property detail with tabs (Viabilidade, Mercado, Encargos, Juridico) |
src/styles.css |
Layout primitives, typography, CSS custom properties |
API Integration
GET /api/properties→ array ofAuctionPropertyResult(proxied to:8000/properties)POST /api/analyze→ run pipeline on URL, returnAuctionPropertyResult(proxied to:8000/analyze)- Vite proxy config in
vite.config.js:/api/*→http://localhost:8000/*(strips/apiprefix) - App loads properties from API on mount; falls back to fixture data if API is empty/unavailable
Data Model
Each property matches the AuctionPropertyResult shape from the backend (backend/graph/contracts.py):
{
id, score, // 0-100
photoLabel, title, address, type, neighborhood, city,
auctionType, auctioneer, court,
discount, // percentage (negative = above market)
minBid, market, // raw BRL numbers — format with fmtBRL()
roi, // projected ROI % (negative = loss)
area, beds, baths, parking, floor,
endsAt, // ISO 8601 string — convert with getEndsAtMs()
occupancy, // "desocupado" | "ocupado" | "disputado"
risk: { j, f, l, o }, // "good" | "warn" | "bad"
photoUrl, // "/photos/auction1.jpg" — real property photo
auctionUrl, // original auction listing URL
marketDetail: {
confidenceLevel, // "low" | "medium" | "high"; numeric score stays internal
indicators, comparables
},
matricula, // official property registration number, when published
editalUrl, // official auction notice PDF
matriculaUrl, // official property registration PDF
editalData // structured official auction/property/payment facts
}
Important: market is the estimated market value based on real comparable sales (not the official auction appraisal). discount = (market - minBid) / market × 100. Can be negative when bid exceeds market value.
Key Conventions
- ES module imports/exports — no
windowglobals - Named exports for all components
- Monetary values: raw BRL numbers from backend;
fmtBRL()formats at render time - Dates:
getEndsAtMs()convertsendsAt(ISO string or epoch ms) to epoch ms forCountdown - Colors: oklch with CSS custom properties —
--good/--warn/--badfor risk,--accentfor primary actions - Styling: mostly inline
styleobjects;styles.csshandles layout primitives, typography, reusable patterns - Language: Brazilian Portuguese (pt-BR) throughout the UI
- No router: screen state managed in
App.jsx, persisted tolocalStorage(arremate_screen)