add bonuses in shop
This commit is contained in:
310
src/renderer/components/BonusShopItem.tsx
Normal file
310
src/renderer/components/BonusShopItem.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user