add bonuses in shop

This commit is contained in:
2025-12-07 20:11:41 +05:00
parent 3e03c1024d
commit 833444df2e
3 changed files with 705 additions and 15 deletions

View File

@ -0,0 +1,310 @@
import React from 'react';
import {
Card,
CardContent,
Box,
Typography,
Button,
CardMedia,
} from '@mui/material';
import CoinsDisplay from './CoinsDisplay';
export interface BonusShopItemProps {
id: string;
name: string;
description?: string;
level: number;
effectValue: number;
nextEffectValue?: number;
// цена покупки и улучшения
price?: number;
upgradePrice: number;
canUpgrade: boolean;
mode?: 'buy' | 'upgrade';
isActive?: boolean;
isPermanent?: boolean;
imageUrl?: string;
disabled?: boolean;
onBuy?: () => void;
onUpgrade?: () => void;
onToggleActive?: () => void;
}
export const BonusShopItem: React.FC<BonusShopItemProps> = ({
name,
description,
level,
effectValue,
nextEffectValue,
price,
upgradePrice,
canUpgrade,
mode,
isActive = true,
isPermanent = false,
imageUrl,
disabled,
onBuy,
onUpgrade,
onToggleActive,
}) => {
const isBuyMode = mode === 'buy' || level === 0;
const buttonText = isBuyMode
? 'Купить'
: canUpgrade
? 'Улучшить'
: 'Макс. уровень';
const displayedPrice = isBuyMode ? (price ?? upgradePrice) : upgradePrice;
const buttonDisabled =
disabled ||
(isBuyMode
? !onBuy || displayedPrice === undefined
: !canUpgrade || !onUpgrade);
const handlePrimaryClick = () => {
if (buttonDisabled) return;
if (isBuyMode && onBuy) onBuy();
else if (!isBuyMode && onUpgrade) onUpgrade();
};
return (
<Card
sx={{
position: 'relative',
width: '100%',
maxWidth: 280,
height: 420,
display: 'flex',
flexDirection: 'column',
bgcolor: 'rgba(5, 5, 15, 0.96)',
borderRadius: '20px',
border: '1px solid rgba(255, 255, 255, 0.08)',
boxShadow: '0 18px 45px rgba(0, 0, 0, 0.8)',
overflow: 'hidden',
transition:
'transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease',
'&:hover': {
transform: 'translateY(-6px)',
boxShadow: '0 26px 60px rgba(0, 0, 0, 0.95)',
borderColor: 'rgba(255, 255, 255, 0.18)',
},
}}
>
{/* верхний “свет” */}
<Box
sx={{
position: 'absolute',
inset: 0,
pointerEvents: 'none',
background:
'radial-gradient(circle at top, rgba(255,255,255,0.13), transparent 55%)',
}}
/>
{imageUrl && (
<Box
sx={{
position: 'relative',
p: '0.9vw',
pb: 0,
overflow: 'hidden',
}}
>
<Box
sx={{
borderRadius: '16px',
overflow: 'hidden',
border: '1px solid rgba(255, 255, 255, 0.12)',
background:
'linear-gradient(135deg, rgba(40,40,80,0.9), rgba(15,15,35,0.9))',
}}
>
<CardMedia
component="img"
image={imageUrl}
alt={name}
sx={{
width: '100%',
height: '11vw',
minHeight: '140px',
objectFit: 'cover',
filter: 'saturate(1.1)',
}}
/>
</Box>
</Box>
)}
<CardContent
sx={{
position: 'relative',
zIndex: 1,
pt: imageUrl ? '0.9vw' : '1.4vw',
pb: '1.3vw',
flexGrow: 1,
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
}}
>
<Box>
<Typography
variant="h6"
color="white"
sx={{
fontFamily: 'Benzin-Bold',
fontSize: '1.05rem',
mb: 0.5,
}}
>
{name}
</Typography>
<Typography
variant="body2"
color="white"
sx={{
opacity: 0.7,
fontSize: '0.8rem',
mb: 1,
}}
>
Уровень: {level}
</Typography>
{description && (
<Typography
variant="body2"
color="white"
sx={{
opacity: 0.75,
fontSize: '0.85rem',
mb: 1.4,
minHeight: 40,
maxHeight: 40,
overflow: 'hidden',
textOverflow: 'ellipsis',
}}
>
{description}
</Typography>
)}
<Box sx={{ mb: 1.2 }}>
<Typography
variant="body2"
color="white"
sx={{ opacity: 0.8, fontSize: '0.8rem' }}
>
Текущий эффект: <b>{effectValue.toLocaleString('ru-RU')}</b>
</Typography>
{typeof nextEffectValue === 'number' &&
!isBuyMode &&
canUpgrade && (
<Typography
variant="body2"
color="white"
sx={{ opacity: 0.8, fontSize: '0.8rem', mt: 0.4 }}
>
Следующий уровень:{' '}
<b>{nextEffectValue.toLocaleString('ru-RU')}</b>
</Typography>
)}
</Box>
<Box sx={{ mb: 1 }}>
<Typography
variant="body2"
color={isActive ? 'success.main' : 'warning.main'}
sx={{ fontSize: '0.78rem' }}
>
{isActive ? 'Бонус активен' : 'Бонус не активен'}
</Typography>
</Box>
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
mb: 1,
}}
>
<Typography
variant="body2"
color="white"
sx={{ opacity: 0.8, fontSize: '0.85rem' }}
>
{isBuyMode ? 'Цена покупки' : 'Цена улучшения'}
</Typography>
{displayedPrice !== undefined && (
<CoinsDisplay
value={displayedPrice}
size="small"
autoUpdate={false}
showTooltip={true}
/>
)}
</Box>
{!isBuyMode && onToggleActive && (
<Button
variant="outlined"
size="small"
sx={{
mb: 1,
borderRadius: '999px',
textTransform: 'none',
fontSize: '0.75rem',
}}
onClick={onToggleActive}
disabled={disabled}
>
{isActive ? 'Выключить' : 'Включить'}
</Button>
)}
</Box>
<Button
variant="contained"
fullWidth
sx={{
mt: 0.5,
borderRadius: '999px',
py: '0.45vw',
color: 'white',
background: !buttonDisabled
? 'linear-gradient(135deg, rgb(0, 160, 90), rgb(0, 200, 140))'
: 'linear-gradient(135deg, rgba(120,120,120,0.9), rgba(80,80,80,0.9))',
'&:hover': {
background: !buttonDisabled
? 'linear-gradient(135deg, rgba(0, 160, 90, 0.85), rgba(0, 200, 140, 0.9))'
: 'linear-gradient(135deg, rgba(120,120,120,0.9), rgba(80,80,80,0.9))',
},
fontFamily: 'Benzin-Bold',
fontSize: '0.9rem',
textTransform: 'uppercase',
letterSpacing: 0.8,
}}
disabled={buttonDisabled}
onClick={handlePrimaryClick}
>
{buttonText}
</Button>
</CardContent>
</Card>
);
};
export default BonusShopItem;