310 lines
8.2 KiB
TypeScript
310 lines
8.2 KiB
TypeScript
// src/renderer/components/BonusShopItem.tsx
|
||
import React from 'react';
|
||
import {
|
||
Card,
|
||
CardContent,
|
||
Box,
|
||
Typography,
|
||
Button,
|
||
CardMedia,
|
||
Divider,
|
||
} 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: 220,
|
||
height: 440,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
|
||
background: 'rgba(20,20,20,0.9)',
|
||
borderRadius: '2.5vw',
|
||
border: '1px solid rgba(255,255,255,0.08)',
|
||
boxShadow: '0 10px 40px rgba(0,0,0,0.8)',
|
||
overflow: 'hidden',
|
||
|
||
transition: 'transform 0.35s ease, box-shadow 0.35s ease',
|
||
|
||
'&:hover': {
|
||
transform: 'scale(1.01)',
|
||
boxShadow: '0 10px 20px rgba(242,113,33,0.1)',
|
||
},
|
||
}}
|
||
>
|
||
{/* Градиентный свет сверху — как в ShopItem */}
|
||
<Box
|
||
sx={{
|
||
position: 'absolute',
|
||
inset: 0,
|
||
pointerEvents: 'none',
|
||
background:
|
||
'radial-gradient(circle at top, rgba(242,113,33,0.25), transparent 60%)',
|
||
}}
|
||
/>
|
||
|
||
{imageUrl && (
|
||
<Box sx={{ position: 'relative', p: 1.5, pb: 0 }}>
|
||
<Box
|
||
sx={{
|
||
borderRadius: '1.8vw',
|
||
overflow: 'hidden',
|
||
border: '1px solid rgba(255,255,255,0.12)',
|
||
background:
|
||
'linear-gradient(135deg, rgba(40,40,40,0.9), rgba(15,15,15,0.9))',
|
||
}}
|
||
>
|
||
<CardMedia
|
||
component="img"
|
||
image={imageUrl}
|
||
alt={name}
|
||
sx={{
|
||
width: '100%',
|
||
height: 160,
|
||
objectFit: 'cover',
|
||
}}
|
||
/>
|
||
</Box>
|
||
</Box>
|
||
)}
|
||
|
||
<CardContent
|
||
sx={{
|
||
flexGrow: 1,
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
justifyContent: 'space-between',
|
||
pt: 2,
|
||
pb: 2,
|
||
}}
|
||
>
|
||
<Box>
|
||
{/* Имя бонуса — градиентом как у ShopItem */}
|
||
<Typography
|
||
sx={{
|
||
fontFamily: 'Benzin-Bold',
|
||
fontSize: '1rem',
|
||
mb: 0.5,
|
||
backgroundImage:
|
||
'linear-gradient(136deg, rgb(242,113,33), rgb(233,64,87), rgb(138,35,135))',
|
||
WebkitBackgroundClip: 'text',
|
||
WebkitTextFillColor: 'transparent',
|
||
}}
|
||
>
|
||
{name}
|
||
</Typography>
|
||
|
||
<Typography
|
||
sx={{
|
||
color: 'rgba(255,255,255,0.7)',
|
||
fontSize: '0.7rem',
|
||
mb: 0.8,
|
||
}}
|
||
>
|
||
Уровень: {level}
|
||
{isPermanent && ' • Постоянный'}
|
||
</Typography>
|
||
|
||
{description && (
|
||
<Typography
|
||
sx={{
|
||
color: 'rgba(255,255,255,0.75)',
|
||
fontSize: '0.7rem',
|
||
mb: 1.2,
|
||
minHeight: 40,
|
||
}}
|
||
>
|
||
{description}
|
||
</Typography>
|
||
)}
|
||
|
||
<Box sx={{ mb: 1 }}>
|
||
<Typography
|
||
sx={{ color: 'rgba(255,255,255,0.8)', fontSize: '0.8rem' }}
|
||
>
|
||
Текущий эффект:{' '}
|
||
<Box component="b" sx={{ fontWeight: 600 }}>
|
||
{effectValue.toLocaleString('ru-RU')}
|
||
</Box>
|
||
</Typography>
|
||
|
||
{typeof nextEffectValue === 'number' &&
|
||
!isBuyMode &&
|
||
canUpgrade && (
|
||
<Typography
|
||
sx={{
|
||
color: 'rgba(255,255,255,0.8)',
|
||
fontSize: '0.8rem',
|
||
mt: 0.4,
|
||
}}
|
||
>
|
||
Следующий уровень:{' '}
|
||
<Box component="b" sx={{ fontWeight: 600 }}>
|
||
{nextEffectValue.toLocaleString('ru-RU')}
|
||
</Box>
|
||
</Typography>
|
||
)}
|
||
</Box>
|
||
|
||
<Typography
|
||
sx={{
|
||
fontSize: '0.78rem',
|
||
mb: 1,
|
||
color: isActive
|
||
? 'rgba(0, 200, 140, 0.9)'
|
||
: 'rgba(255, 180, 80, 0.9)',
|
||
}}
|
||
>
|
||
{isActive ? 'Бонус активен' : 'Бонус не активен'}
|
||
</Typography>
|
||
|
||
<Box
|
||
sx={{
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
mb: 1,
|
||
}}
|
||
>
|
||
<Typography
|
||
sx={{ color: 'rgba(255,255,255,0.8)', fontSize: '0.85rem' }}
|
||
>
|
||
{isBuyMode ? 'Цена покупки' : 'Цена улучшения'}
|
||
</Typography>
|
||
{displayedPrice !== undefined && (
|
||
<CoinsDisplay
|
||
value={displayedPrice}
|
||
size="small"
|
||
autoUpdate={false}
|
||
showTooltip={true}
|
||
/>
|
||
)}
|
||
</Box>
|
||
|
||
<Divider sx={{background: 'rgba(160, 160, 160, 0.3)', borderRadius: '2vw'}}/>
|
||
|
||
{!isBuyMode && onToggleActive && (
|
||
<Typography
|
||
onClick={onToggleActive}
|
||
sx={{
|
||
mt: '1vw',
|
||
fontFamily: 'Benzin-Bold',
|
||
fontSize: '1vw',
|
||
textTransform: 'uppercase',
|
||
letterSpacing: '0.08em',
|
||
cursor: 'pointer',
|
||
backgroundImage:
|
||
'linear-gradient(71deg, #F27121 0%, #E940CD 60%, #8A2387 100%)',
|
||
WebkitBackgroundClip: 'text',
|
||
WebkitTextFillColor: 'transparent',
|
||
textShadow: '0 0 15px rgba(0,0,0,0.9)',
|
||
'&:hover': {
|
||
opacity: 1,
|
||
},
|
||
}}
|
||
>
|
||
{isActive ? 'Выключить' : 'Включить'}
|
||
</Typography>
|
||
)}
|
||
</Box>
|
||
|
||
{/* Кнопка в стиле Registration / ShopItem */}
|
||
<Button
|
||
variant="contained"
|
||
fullWidth
|
||
disabled={buttonDisabled}
|
||
onClick={handlePrimaryClick}
|
||
sx={{
|
||
mt: 2,
|
||
transition: 'transform 0.3s ease, opacity 0.2s ease',
|
||
background: buttonDisabled
|
||
? 'linear-gradient(71deg, #555 0%, #666 70%, #444 100%)'
|
||
: 'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
|
||
fontFamily: 'Benzin-Bold',
|
||
borderRadius: '2.5vw',
|
||
fontSize: '0.85rem',
|
||
color: '#fff',
|
||
opacity: buttonDisabled ? 0.6 : 1,
|
||
'&:hover': {
|
||
transform: buttonDisabled ? 'none' : 'scale(1.05)',
|
||
},
|
||
}}
|
||
>
|
||
{buttonText}
|
||
</Button>
|
||
</CardContent>
|
||
</Card>
|
||
);
|
||
};
|
||
|
||
export default BonusShopItem;
|