sass-imobiliaria/specs/032-performance-homepage/tasks.md
MatheusAlves96 cf5603243c
Some checks failed
CI/CD → Deploy via SSH / Build & Push Docker Images (push) Successful in 1m0s
CI/CD → Deploy via SSH / Deploy via SSH (push) Successful in 4m35s
CI/CD → Deploy via SSH / Validate HTTPS & Endpoints (push) Failing after 46s
feat: features 025-032 - favoritos, contatos, trabalhe-conosco, area-cliente, navbar, hero-light-dark, performance-homepage
- feat(025): favoritos locais com FavoritesContext, HeartButton, PublicFavoritesPage
- feat(026): central de contatos admin (leads/contatos unificados)
- feat(027): configuração da página de contato via admin
- feat(028): trabalhe conosco - candidaturas com upload e admin
- feat(029): UX área do cliente - visitas, comparação, perfil
- feat(030): navbar UX - menu mobile, ThemeToggle, useFavorites
- feat(031): hero light/dark - imagens separadas por tema, upload, preview, seed
- feat(032): performance homepage - Promise.all parallel fetches, sessionStorage cache,
  preload hero image, loading=lazy nos cards, useInView hook, will-change carrossel,
  keyframes em index.css, AgentsCarousel e HomeScrollScene via props
- fix: light mode HomeScrollScene - gradiente, cores de texto, scroll hint

migrations: g1h2i3j4k5l6 (source em leads), h1i2j3k4l5m6 (contact_config),
            i1j2k3l4m5n6 (job_applications), j2k3l4m5n6o7 (hero theme images)
2026-04-22 22:35:17 -03:00

8 KiB

Tasks — 032: Performance Homepage

Ordem de execução respeita dependências. Cada task é atômica e pode ser validada individualmente.


FASE 1 — Fundação (sem quebra de interface)

TASK-01: Criar hook useInView

  • Arquivo: frontend/src/hooks/useInView.ts (criar)
  • Ação:
    import { useEffect, useRef, useState } from 'react'
    
    export function useInView(options?: IntersectionObserverInit) {
        const ref = useRef<HTMLDivElement>(null)
        const [inView, setInView] = useState(false)
    
        useEffect(() => {
            const el = ref.current
            if (!el) return
            const observer = new IntersectionObserver(([entry]) => {
                if (entry.isIntersecting) {
                    setInView(true)
                    observer.disconnect()
                }
            }, options)
            observer.observe(el)
            return () => observer.disconnect()
        // eslint-disable-next-line react-hooks/exhaustive-deps
        }, [])
    
        return { ref, inView }
    }
    
  • Validação: get_errors sem erros

TASK-02: Mover @keyframes fadeDown para index.css

  • Arquivo: frontend/src/index.css (editar — adicionar ao final)
  • Conteúdo a adicionar:
    @keyframes fadeDown {
        0%, 100% { opacity: 0; transform: translateY(-4px); }
        50%       { opacity: 1; transform: translateY(4px); }
    }
    
  • Arquivo: frontend/src/components/HomeScrollScene.tsx (editar — remover o bloco <style> inline)
  • Validação: Build passa, setas do scroll hint continuam animadas

TASK-03: loading="lazy" + decoding="async" em SlideImage

  • Arquivo: frontend/src/components/PropertyRowCard.tsx
  • Alvo: função SlideImage, tag <img>
  • Adicionar atributos: loading="lazy" e decoding="async"
  • Cuidado: Não adicionar loading="lazy" na imagem hero de HomeScrollScene (ela deve ser eager)
  • Validação: get_errors sem erros

TASK-04: will-change: transform no track do AgentsCarousel

  • Arquivo: frontend/src/components/AgentsCarousel.tsx
  • Alvo: elemento div com ref={trackRef} que recebe transform no estilo inline
  • Ação: Adicionar willChange: 'transform' no objeto de style do track
  • Validação: get_errors sem erros

FASE 2 — Refatoração de AgentsCarousel para receber props

TASK-05: Adicionar props de dados em AgentsCarousel

  • Arquivo: frontend/src/components/AgentsCarousel.tsx
  • Ação:
    1. Definir interface AgentsCarouselProps { agents: Agent[]; loading: boolean }
    2. Receber { agents, loading } como props do componente
    3. Remover useEffect que chama getAgents() e os estados agents / loading
    4. Remover import de getAgents
    5. Manter toda a lógica de carrossel (autoplay, prev/next, etc.) inalterada
  • Validação: get_errors sem erros (vai reportar erro de prop em HomePage.tsx — resolver na TASK-08)

FASE 3 — Refatoração de HomeScrollScene para receber props

TASK-06: Adicionar props de dados em HomeScrollScene

  • Arquivo: frontend/src/components/HomeScrollScene.tsx
  • Ação:
    1. Adicionar ao HomeScrollSceneProps: properties: Property[] e loadingProperties: boolean
    2. Remover const [properties, setProperties] = useState<Property[]>([])
    3. Remover const [loading, setLoading] = useState(true)
    4. Remover useEffect(() => { getFeaturedProperties()... })
    5. Remover import de getFeaturedProperties
    6. No JSX, substituir uso de loading por loadingProperties
  • Validação: get_errors (vai ter erro em uso — resolver na TASK-08)

TASK-07: Refatorar RiseCard para usar useInView

  • Arquivo: frontend/src/components/HomeScrollScene.tsx
  • Ação:
    1. Importar useInView de '../hooks/useInView'
    2. Substituir o corpo de RiseCard: remover useRef, useState(false), useEffect com IntersectionObserver
    3. Usar const { ref, inView } = useInView({ threshold: 0.05 })
    4. Manter a div com ref={ref} e classes condicionais em inView
  • Validação: get_errors sem erros

FASE 4 — HomePage como orchestrator

TASK-08: Paralelizar fetches + cache + preload em HomePage

  • Arquivo: frontend/src/pages/HomePage.tsx
  • Ação:
    1. Adicionar imports: getFeaturedProperties de '../services/properties', getAgents de '../services/agents', Agent de '../types/agent', Property de '../types/property'
    2. Adicionar estados: featuredProperties: Property[], agents: Agent[], loadingProperties: boolean, loadingAgents: boolean
    3. Criar helpers de cache:
      const CFG_CACHE_KEY = 'homepage_config_v1'
      const CFG_CACHE_TTL = 5 * 60 * 1000
      
      function getCachedConfig(): HomepageConfig | null { ... }
      function setCachedConfig(data: HomepageConfig): void { ... }
      
    4. Substituir useEffect de getHomepageConfig por Promise.all:
      useEffect(() => {
          const cached = getCachedConfig()
          const configFetch = cached
              ? Promise.resolve(cached)
              : getHomepageConfig().then(d => { setCachedConfig(d); return d })
      
          Promise.all([configFetch, getFeaturedProperties(), getAgents()])
              .then(([cfg, props, agts]) => {
                  setConfig(cfg)
                  setFeaturedProperties(props)
                  setAgents(agts)
              })
              .catch(() => {})
              .finally(() => {
                  setIsLoading(false)
                  setLoadingProperties(false)
                  setLoadingAgents(false)
              })
      }, [])
      
    5. Adicionar useEffect de preload:
      useEffect(() => {
          if (!themedBackgroundImage) return
          const link = document.createElement('link')
          link.rel = 'preload'
          link.as = 'image'
          link.href = themedBackgroundImage
          document.head.appendChild(link)
          return () => { document.head.removeChild(link) }
      }, [themedBackgroundImage])
      
    6. Passar properties={featuredProperties} e loadingProperties={loadingProperties} para <HomeScrollScene>
    7. Passar agents={agents} e loading={loadingAgents} para <AgentsCarousel>
  • Validação: get_errors sem erros; npm run build passa

TASK-09: fetchPriority="high" + loading="eager" na hero image

  • Arquivo: frontend/src/components/HomeScrollScene.tsx
  • Alvo: tag <img> do backgroundImage (dentro do bloco {backgroundImage ? ()
  • Adicionar: fetchPriority="high" e loading="eager" e decoding="async"
  • Nota: fetchPriority é atributo HTML5 — TypeScript pode reclamar; usar {...{ fetchpriority: 'high' } as React.ImgHTMLAttributes<HTMLImageElement>} se necessário, ou verificar suporte em @types/react
  • Validação: get_errors sem erros

FASE 5 — Validação Final

TASK-10: Build e checklist final

  • Ação:
    1. Executar npm run build no diretório frontend/
    2. Verificar zero erros TypeScript
    3. Testar manualmente:
      • Home carrega no tema dark sem erro visual
      • Home carrega no tema light sem erro visual
      • Cards de destaque aparecem com animação rise
      • Carousel de corretores funciona (autoplay, prev/next)
      • Troca de tema reage corretamente no gradiente hero
      • DevTools Network: 3 requests paralelos no load inicial
      • Segunda visita (< 5 min): /homepage-config não é chamado

Checklist de Qualidade

  • TASK-01 concluída — hooks/useInView.ts criado
  • TASK-02 concluída — @keyframes em index.css, sem <style> inline
  • TASK-03 concluída — loading="lazy" em SlideImage
  • TASK-04 concluída — will-change: transform no carrossel
  • TASK-05 concluída — AgentsCarousel recebe props
  • TASK-06 concluída — HomeScrollScene recebe props
  • TASK-07 concluída — RiseCard usa useInView
  • TASK-08 concluída — HomePage paraleliza fetches + cache + preload
  • TASK-09 concluída — hero image com prioridade correta
  • TASK-10 concluída — build verde, smoke test manual OK