import {Dispatch, SetStateAction, useMemo, useState} from 'react'; import styled from 'styled-components'; import { deleteProduct, replaceProduct } from '../../api/promoApi'; import { useCartPolling } from '../../hooks/useCartPolling'; import { declension } from '../../utils/declension'; import { formatCurrency } from '../../utils/formatCurrency'; import type { Product } from '../../types/cart'; import { CartItem } from './CartItem'; import { CartFooter } from './CartFooter'; import { ProductModal } from './ProductModal'; import {useGlobalZustandState} from "../../hooks/useGlobalZustandState"; interface CartProps { products: Product[]; days: number; isComplete: boolean; isLoading: boolean; onProductsChange: Dispatch>; onComplete: () => void; onLoadingChange: (isLoading: boolean) => void; } export function Cart({ products, days, isComplete, isLoading, onProductsChange, onComplete, onLoadingChange, }: CartProps) { const [visibleCount, setVisibleCount] = useState(5); const [displayText, setDisplayText] = useState(''); const [selectedProduct, setSelectedProduct] = useState(null); const [processingProductName, setProcessingProductName] = useState(null); const {showFullRefreshButton, excess} = useGlobalZustandState((state) => state); const visibleProducts = products.slice(0, visibleCount); const remaining = products.length - visibleProducts.length; const totalPrice = useMemo( () => products.reduce((sum, product) => sum + product.total_price, 0), [products], ); const formattedTotalPrice = formatCurrency(totalPrice); const stockDays = Math.round(days * excess + days); useCartPolling({ enabled: !isComplete, onProductsChange, onComplete, onLoadingChange, setDisplayText, }); async function handleReplace(product: Product) { setProcessingProductName(product.name); try { await replaceProduct(product.name); onProductsChange(products.filter(item => item.name !== product.name)); } finally { setProcessingProductName(null); } } async function handleDelete(product: Product) { setProcessingProductName(product.name); try { await deleteProduct(product.name); onProductsChange(products.filter(item => item.name !== product.name)); } finally { setProcessingProductName(null); } } return ( <>
Ваша корзина на {days} {declension(days, ['день', 'дня', 'дней'])} {showFullRefreshButton && Начать заново} {products.length > 0 && ( <> {products.length} {declension(products.length, ['товар', 'товара', 'товаров'])} {formattedTotalPrice} с запасами на {stockDays} {declension(stockDays, ['день', 'дня', 'дней'])} )} {displayText}
{visibleProducts.map(product => ( setSelectedProduct(product)} onReplace={() => handleReplace(product)} onDelete={() => handleDelete(product)} /> ))} {products.length === 0 && ( )} {remaining > 0 && ( setVisibleCount(products.length)}> Показать ещё {remaining} товаров )} setSelectedProduct(null)} onReplace={(product) => { handleReplace(product); setSelectedProduct(null); }} onDelete={(product) => { handleDelete(product); setSelectedProduct(null); }} /> ); } const AbsoluteRefreshButton = styled.button` background-color: ${({ theme }) => theme.colors.green}; height: 32px; color: #ffffff; border: none; padding: 14px; border-radius: 12px; font-size: 16px; font-weight: 370; line-height: 20px; cursor: pointer; transition: background 0.2s; display: flex; align-items: center; position: absolute; right: 0; top: 0; &:hover, &:focus { background-color: ${({ theme }) => theme.colors.greenHover}; } &:active { background-color: #4A8D19; } ` const Summary = styled.div` display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; flex-shrink: 0; width: 100%; position: relative; `; const SummaryTitle = styled.div` font-size: 20px; line-height: 24px; font-weight: 600; color: ${({ theme }) => theme.colors.darkGreen}; margin-bottom: 8px; `; const Green = styled.span` color: ${({ theme }) => theme.colors.green}; `; const SummaryDetails = styled.div<{ $hidden: boolean }>` min-height: 24px; font-size: 14px; color: ${({ theme }) => theme.colors.gray}; display: flex; justify-content: start; align-items: center; gap: 8px; visibility: ${({ $hidden }) => ($hidden ? 'hidden' : 'visible')}; span { font-weight: 350; } b { font-family: initial; font-size: 20px; line-height: 8px; } `; const SummaryExcess = styled.div` background-color: ${({ theme }) => theme.colors.paleGreen}; padding: 3px 6px 5px; color: #6D8982; border-radius: 4px; font-size: 14px; font-weight: 350; `; const Loader = styled.div<{ $hidden: boolean, "data-display-text": string }>` font-size: 16px; line-height: 19px; display: inline-block; color: transparent; background-clip: text; position: absolute; top:32px; visibility: ${({ $hidden }) => ($hidden ? 'hidden' : 'visible')}; &:before, &:after { content: '${({"data-display-text": displayText}) => displayText}'; display: block; position: absolute; inset: 0; } &:before { background: linear-gradient(79deg, #FFBA00 0%, #FFEC47 31%, #C4E30F 63%, #5EA12D 98%) 100%; background-clip: text; animation: opacityAnim 1s ease-in-out 0s infinite alternate; } &:after { background: linear-gradient(79deg, #5EA12D 2%, #C4E30F 38%, #FFEC47 69%, #FFBA00 100%) 100%; background-clip: text; animation: opacityAnim 1s ease-in-out -1s infinite alternate; } `; const ScrollWrapper = styled.div` flex: 1; overflow-y: auto; overflow-x: hidden; padding-right: 4px; margin-bottom: 10px; position: relative; display: flex; &::-webkit-scrollbar { width: 6px; } &::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 4px; } &::-webkit-scrollbar-thumb { background: #d1d5db; border-radius: 4px; } `; const CartList = styled.ul` width: 100%; list-style: none; `; const EmptyLoader = styled.img` animation: spin 1.4s cubic-bezier(0.4, 0.2, 0.35, 0.7) infinite; position: absolute; left: 50%; margin-left: -30px; align-self: center; `; const ShowMore = styled.button` display: flex; align-items: center; justify-content: center; padding: 20px 0 10px; color: ${({ theme }) => theme.colors.green}; font-size: 14px; cursor: pointer; font-weight: 500; transition: 0.2s; flex-shrink: 0; gap: 4px; border: 0; background: transparent; &:hover { opacity: 0.8; } svg { width: 24px; height: 24px; stroke: ${({ theme }) => theme.colors.green}; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; margin-left: 6px; } `;