import HeaderProfile from "@/components/Headers/HeaderProfile"; import { OfferCard, OfferType } from "@/components/OfferCard"; import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; import { ChevronRight } from "lucide-react"; import { useState, useRef, useEffect, useCallback } from "react"; import HomeCardDetails from "./HomeCardDetails"; import CardBusinessGrade from "./CardBusinessGrade"; import ScoreCard from "./ScoreCard"; import HomeSkeleton from "./HomeSkeleton"; import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; import CardCompleteProfile from "./CardCompleteProfile"; import CardEmptyOffersState from "./CardEmptyOffersState"; import CardApplicationRejectionCard from "./Cards/CardApplicationRejectionCard"; import HomeCardNoFundingYet from "./HomeCardNoFundingYet"; import CardReapplyNow from "./CardReapplyNow"; import { useGetMatchmakingDashboard } from "@/hook/queries/matchmaking"; import { useGetUserMe } from "@/hook/queries/user"; import { formatCurrency, formatTerm, formatAPR, formatExpirationText } from "@/utils/formatters"; import HeaderDesktopProfile from "@/components/Headers/HeaderDesktopProfile"; import { useAuthStore } from "@/store/useAuthStore"; import CardNeedMoreCapital from "./CardNeedMoreCapital"; import CardReferBusiness from "./CardReferBusiness"; import InviteAndEarnModal from "@/components/Modals/InviteAndEarnModal"; import { getLogoForProduct } from "@/utils/get-logo-for-product"; import { FundingOffer } from "@/@types/matchmaking"; import { LazyImage } from "@/components/OptimizedImage"; interface Offer { id: number; expiresIn: string; offerAmount: string; title: OfferType; term: string; apr: string; logoSrc?: string; logoType?: string; logoColor?: string; } export default function HomePage() { const navigate = useNavigate(); const { user } = useAuthStore(); const { data: userMe } = useGetUserMe(); const { data: MatchmakingDashboard, isLoading: isLoadingMatchmaking } = useGetMatchmakingDashboard(userMe?.business_id); const [activeIndex, setActiveIndex] = useState(0); const [activeOfferIndex, setActiveOfferIndex] = useState(0); const scrollAreaRef = useRef(null); const offerScrollAreaRef = useRef(null); const cardRefs = useRef<(HTMLDivElement | null)[]>([]); const offerCardRefs = useRef<(HTMLDivElement | null)[]>([]); // Drag-to-scroll state for financing ScrollArea const [isFinancingDragging, setIsFinancingDragging] = useState(false); const [financingStartX, setFinancingStartX] = useState(0); const [financingScrollLeft, setFinancingScrollLeft] = useState(0); // Drag-to-scroll state for offers ScrollArea const [isOffersDragging, setIsOffersDragging] = useState(false); const [offersStartX, setOffersStartX] = useState(0); const [offersScrollLeft, setOffersScrollLeft] = useState(0); // Helper functions to determine current state const canRunMatchmaking = MatchmakingDashboard?.data?.matchmaking_status?.can_run_now; // Estados condicionais baseados no matchmaking dashboard const qualifiedCount = MatchmakingDashboard?.data?.products?.qualified_count || 0; const withFundingOffers = MatchmakingDashboard?.data?.products?.with_funding_offers || 0; const OffersActiveteds = MatchmakingDashboard?.data.products.funding_offers.filter(({ status }) => status === "active") const acceptedCount = MatchmakingDashboard?.data?.products?.accepted_count || 0; const completionPercentage = MatchmakingDashboard?.data?.requirements?.completion_percentage || 0; const daysUntilNext = MatchmakingDashboard?.data?.matchmaking_status?.days_until_next || 0; const scoreGrade = MatchmakingDashboard?.data?.user?.score const scoreLastCheck = MatchmakingDashboard?.data?.user?.last_check_score // Get real data arrays - DECLARE BEFORE USING const realFundingOffers = MatchmakingDashboard?.data?.products?.funding_offers || []; // Variáveis booleanas para renderização condicional const shouldShowCompleteProfile = qualifiedCount > 0 && completionPercentage !== 100; // const shouldShowNoOffers = withFundingOffers === 0; const shouldShowOffers = withFundingOffers > 0 && realFundingOffers.length > 0; const shouldShowRejectionCard = qualifiedCount === 0 && daysUntilNext > 0; const shouldShowReapplyNow = qualifiedCount === 0 && daysUntilNext === 0; // const shouldShowCarousel = withFundingOffers > 0; const shouldShowYourFinancing = acceptedCount > 0 || (OffersActiveteds ?? []).length > 0; const shouldShowNoFundingYet = qualifiedCount > 0 && withFundingOffers === 0; // const shouldShowCongratulations = acceptedCount > 0; // Use real funding offers when available, filtered for pending and non-expired (limit to 3) const offersDataArray: Offer[] = realFundingOffers.length > 0 ? realFundingOffers .filter((offer: FundingOffer) => { // Only show offers that are not expired and have pending client response status const isNotExpired = offer.is_expired === false; const isPendingResponse = offer.client_response_status === 'pending'; return isNotExpired && isPendingResponse; }) .slice(0, 3) .map((offer: FundingOffer, index: number) => { // Use new API structure for funding offers from matchmaking const fundingAmountStr = offer.financial_info?.funding_amount || (offer as any).funding_amount || "0"; const termStr = offer.financial_info?.term || (offer as any).term || "Unknown"; const aprStr = offer.financial_info?.apr || (offer as any).apr || "0"; const productName = offer.product_info?.name || (offer as any).product_name || ""; // Use APR from API directly const aprValue = parseFloat(aprStr.replace('%', '')) || 0; // Calculate days until expiration for new structure const expiresAt = offer.response_info?.expires_at || (offer as any).expires_at; let daysUntilExpiration = (offer as any).days_until_expiration || 30; if (expiresAt && !daysUntilExpiration) { const expireDate = new Date(expiresAt); const now = new Date(); const timeDiff = expireDate.getTime() - now.getTime(); daysUntilExpiration = Math.ceil(timeDiff / (1000 * 3600 * 24)); } return { id: offer.id || (offer as any).offer_id || index + 1, expiresIn: formatExpirationText(daysUntilExpiration), offerAmount: formatCurrency(fundingAmountStr), title: productName as OfferType, term: formatTerm(termStr), apr: formatAPR(aprValue), logoSrc: getLogoForProduct(productName), logoType: "credit-card", }; }) : []; // Função para lidar com o clique no botão "Start New Matchmaking" const handleMatchmakingClick = () => { if (canRunMatchmaking) { // Lógica para iniciar novo matchmaking console.log("Starting new matchmaking..."); // TODO: Implementar lógica de matchmaking } else { const message = daysUntilNext > 0 ? `You can run matchmaking again in ${daysUntilNext} days. We update opportunities every 30-45 days.` : "Matchmaking is currently not available. Please try again later."; toast.info(message); } }; useEffect(() => { offerCardRefs.current = offerCardRefs.current.slice(0, offersDataArray.length); }, [offersDataArray.length]); // Function to scroll to specific card const scrollToCard = useCallback((index: number) => { const cardElement = cardRefs.current[index]; if (cardElement && scrollAreaRef.current) { const scrollContainer = scrollAreaRef.current.querySelector('[data-slot="scroll-area-viewport"]'); if (scrollContainer) { const cardRect = cardElement.getBoundingClientRect(); const containerRect = scrollContainer.getBoundingClientRect(); const scrollLeft = cardElement.offsetLeft - (containerRect.width - cardRect.width) / 2; scrollContainer.scrollTo({ left: scrollLeft, behavior: 'smooth' }); setActiveIndex(index); } } }, []); // Function to scroll to specific offer card const scrollToOfferCard = useCallback((index: number) => { const cardElement = offerCardRefs.current[index]; if (cardElement && offerScrollAreaRef.current) { const scrollContainer = offerScrollAreaRef.current.querySelector('[data-slot="scroll-area-viewport"]'); if (scrollContainer) { const cardRect = cardElement.getBoundingClientRect(); const containerRect = scrollContainer.getBoundingClientRect(); const scrollLeft = cardElement.offsetLeft - (containerRect.width - cardRect.width) / 2; scrollContainer.scrollTo({ left: scrollLeft, behavior: 'smooth' }); setActiveOfferIndex(index); } } }, []); // Intersection Observer for auto-detection of active offer card useEffect(() => { const observerOptions = { root: offerScrollAreaRef.current?.querySelector('[data-slot="scroll-area-viewport"]'), rootMargin: '0px', threshold: 0.7 }; const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { const index = offerCardRefs.current.indexOf(entry.target as HTMLDivElement); if (index !== -1) { setActiveOfferIndex(index); } } }); }, observerOptions); offerCardRefs.current.forEach((card) => { if (card) observer.observe(card); }); return () => { offerCardRefs.current.forEach((card) => { if (card) observer.unobserve(card); }); }; }, [offersDataArray.length]); // Drag-to-scroll handlers for financing ScrollArea const handleFinancingMouseDown = useCallback((e: React.MouseEvent) => { const scrollContainer = scrollAreaRef.current?.querySelector('[data-slot="scroll-area-viewport"]'); if (!scrollContainer) return; setIsFinancingDragging(true); setFinancingStartX(e.pageX - scrollContainer.getBoundingClientRect().left); setFinancingScrollLeft(scrollContainer.scrollLeft); // Prevent text selection e.preventDefault(); }, []); const handleFinancingMouseMove = useCallback((e: React.MouseEvent) => { if (!isFinancingDragging) return; const scrollContainer = scrollAreaRef.current?.querySelector('[data-slot="scroll-area-viewport"]'); if (!scrollContainer) return; e.preventDefault(); const x = e.pageX - scrollContainer.getBoundingClientRect().left; const walk = (x - financingStartX) * 2; // Multiply by 2 for faster scrolling scrollContainer.scrollLeft = financingScrollLeft - walk; }, [isFinancingDragging, financingStartX, financingScrollLeft]); const handleFinancingMouseUp = useCallback(() => { setIsFinancingDragging(false); }, []); const handleFinancingMouseLeave = useCallback(() => { setIsFinancingDragging(false); }, []); // Drag-to-scroll handlers for offers ScrollArea const handleOffersMouseDown = useCallback((e: React.MouseEvent) => { const scrollContainer = offerScrollAreaRef.current?.querySelector('[data-slot="scroll-area-viewport"]'); if (!scrollContainer) return; setIsOffersDragging(true); setOffersStartX(e.pageX - scrollContainer.getBoundingClientRect().left); setOffersScrollLeft(scrollContainer.scrollLeft); // Prevent text selection e.preventDefault(); }, []); const handleOffersMouseMove = useCallback((e: React.MouseEvent) => { if (!isOffersDragging) return; const scrollContainer = offerScrollAreaRef.current?.querySelector('[data-slot="scroll-area-viewport"]'); if (!scrollContainer) return; e.preventDefault(); const x = e.pageX - scrollContainer.getBoundingClientRect().left; const walk = (x - offersStartX) * 2; // Multiply by 2 for faster scrolling scrollContainer.scrollLeft = offersScrollLeft - walk; }, [isOffersDragging, offersStartX, offersScrollLeft]); const handleOffersMouseUp = useCallback(() => { setIsOffersDragging(false); }, []); const handleOffersMouseLeave = useCallback(() => { setIsOffersDragging(false); }, []); const renderLogo = (offer: Offer) => { if (offer.logoSrc) { return ( ); } }; if (isLoadingMatchmaking) { return ; } return ( <>
👋 Hi {user?.first_name}, Welcome back! {/* Success notification - only show when user has accepted products */} {/* {shouldShowCongratulations && ( )} */} {/* Renderização condicional: Complete Profile */} {shouldShowCompleteProfile && ( )} {/* Renderização condicional: Ready to Reapply */} {shouldShowReapplyNow && ( )} {shouldShowNoFundingYet && ( )} {/* Renderização condicional: Your financing section */} {shouldShowYourFinancing && (

Your financing

navigate("/financing")} > View all my financing
navigate("/financing")} />
{(OffersActiveteds && OffersActiveteds.length > 0) ? ( <>
{OffersActiveteds.map(({apr, payback_amount, lender_name, status, term}, index) => (
{ cardRefs.current[index] = el; }} className="flex-none w-full lg:w-98" >
))}
{OffersActiveteds.map((_, index) => (
) : ( )}
)}
{/* */}
{/* Renderização condicional: Try Again in X Days */} {shouldShowRejectionCard && ( )} {/* Available offers section - only show if NOT showing rejection card or reapply card */} {!shouldShowRejectionCard && !shouldShowReapplyNow && (

Available offers

navigate("/offers")} > See all offers
navigate("/offers")} />
{/* Renderização condicional: Ofertas disponíveis */} {shouldShowOffers ? ( <>
{offersDataArray.map((offer, index) => (
{ offerCardRefs.current[index] = el; }} className="flex-none w-[calc(100vw-48px)] lg:w-98" > navigate(`/card/${offer.id}`)} onDetails={() => navigate(`/card/${offer.id}`)} />
))}
{offersDataArray.map((_, index) => (
) : ( /* Renderização condicional: Sem ofertas */ )}
)}
) }