axyenniu daily reward page

This commit is contained in:
aurinex
2025-12-13 16:18:47 +05:00
parent 226f5c1393
commit 712ae70e2a
13 changed files with 835 additions and 49 deletions

View File

@ -11,7 +11,7 @@ import {
Box,
Chip,
} from '@mui/material';
import CustomTooltip from './CustomTooltip';
import CustomTooltip from './Notifications/CustomTooltip';
// Тип для плаща с необязательными полями для обоих вариантов использования
export interface CapeCardProps {
cape: {

View File

@ -1,6 +1,6 @@
// CoinsDisplay.tsx
import { Box, Typography } from '@mui/material';
import CustomTooltip from './CustomTooltip';
import CustomTooltip from './Notifications/CustomTooltip';
import { useEffect, useState } from 'react';
import { fetchCoins } from '../api';
@ -207,14 +207,14 @@ export default function CoinsDisplay({
<CoinsDisplay value={1500} />
// Пример 2: Получение данных по username с автообновлением
<CoinsDisplay
username="player123"
<CoinsDisplay
username="player123"
autoUpdate={true}
updateInterval={30000} // обновлять каждые 30 секунд
/>
// Пример 3: Кастомная стилизация без иконки
<CoinsDisplay
<CoinsDisplay
value={9999}
size="small"
showIcon={false}
@ -224,7 +224,7 @@ export default function CoinsDisplay({
/>
// Пример 4: Большой отображение для профиля
<CoinsDisplay
<CoinsDisplay
username="player123"
size="large"
tooltipText="Ваш текущий баланс"

View File

@ -0,0 +1,180 @@
import * as React from 'react';
import Snackbar, { SnackbarCloseReason } from '@mui/material/Snackbar';
import Alert from '@mui/material/Alert';
export type NotificationVertical = 'top' | 'bottom';
export type NotificationHorizontal = 'left' | 'center' | 'right';
export type NotificationPosition = {
vertical: NotificationVertical;
horizontal: NotificationHorizontal;
};
export type NotificationSeverity = 'success' | 'info' | 'warning' | 'error';
export interface CustomNotificationProps {
open: boolean;
message: React.ReactNode;
onClose: () => void;
severity?: NotificationSeverity;
position?: NotificationPosition;
autoHideDuration?: number;
variant?: 'filled' | 'outlined' | 'standard';
}
const getAccent = (severity: NotificationSeverity) => {
switch (severity) {
case 'success':
return {
// glow: 'rgba(43, 255, 0, 0.45)',
// a1: 'rgba(43, 255, 0, 0.90)',
// a2: 'rgba(0, 255, 170, 0.55)',
// a3: 'rgba(0, 200, 120, 0.35)',
glow: 'rgba(138, 35, 135, 0.45)',
a1: 'rgba(242, 113, 33, 0.90)',
a2: 'rgba(138, 35, 135, 0.90)',
a3: 'rgba(233, 64, 205, 0.90)',
};
case 'warning':
return {
glow: 'rgba(255, 193, 7, 0.45)',
a1: 'rgba(255, 193, 7, 0.90)',
a2: 'rgba(255, 120, 0, 0.55)',
a3: 'rgba(255, 80, 0, 0.35)',
};
case 'error':
return {
glow: 'rgba(255, 77, 77, 0.50)',
a1: 'rgba(255, 77, 77, 0.90)',
a2: 'rgba(233, 64, 87, 0.65)',
a3: 'rgba(138, 35, 135, 0.45)',
};
case 'info':
default:
return {
glow: 'rgba(33, 150, 243, 0.45)',
a1: 'rgba(33, 150, 243, 0.90)',
a2: 'rgba(0, 255, 255, 0.45)',
a3: 'rgba(120, 60, 255, 0.35)',
};
}
};
export default function CustomNotification({
open,
message,
onClose,
severity = 'info',
position = { vertical: 'bottom', horizontal: 'center' },
autoHideDuration = 3000,
variant = 'filled',
}: CustomNotificationProps) {
const accent = getAccent(severity);
const handleClose = (
_event?: React.SyntheticEvent | Event,
reason?: SnackbarCloseReason
) => {
if (reason === 'clickaway') return;
onClose();
};
return (
<Snackbar
open={open}
onClose={handleClose}
autoHideDuration={autoHideDuration}
anchorOrigin={position}
sx={{
'& .MuiSnackbarContent-root': {
background: 'transparent',
boxShadow: 'none',
},
}}
>
<Alert
onClose={handleClose}
severity={severity}
variant={variant}
icon={false}
sx={{
width: '100%',
borderRadius: '1vw',
px: '2vw',
py: '1vw',
display: 'flex',
alignItems: 'center',
// базовый фон как в тултипе
backgroundColor: 'rgba(0, 0, 0, 0.88)',
color: '#fff',
fontFamily: 'Benzin-Bold, sans-serif',
// рамка + неоновая подсветка
border: `1px solid ${accent.a2}`,
boxShadow: `
0 0 1.6vw ${accent.glow},
0 0 0.6vw rgba(0, 0, 0, 0.35),
inset 0 0 0.6vw rgba(0, 0, 0, 0.45)
`,
position: 'relative',
overflow: 'hidden',
// внутренний градиентный бордер как у CustomTooltip
'&::before': {
content: '""',
position: 'absolute',
inset: 0,
borderRadius: '1vw',
padding: '2px',
background: `
linear-gradient(
135deg,
${accent.a1} 0%,
${accent.a2} 50%,
${accent.a3} 100%
)
`,
WebkitMask:
'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
WebkitMaskComposite: 'xor',
maskComposite: 'exclude',
pointerEvents: 'none',
zIndex: 0,
},
// контент поверх ::before
'& .MuiAlert-message': {
position: 'relative',
zIndex: 1,
padding: 0,
fontSize: '1.5vw',
lineHeight: 1.25,
},
// кнопка закрытия
'& .MuiAlert-action': {
position: 'relative',
zIndex: 1,
alignItems: 'center',
padding: 0,
marginLeft: '1vw',
},
'& .MuiIconButton-root': {
color: 'rgba(255,255,255,0.85)',
transition: 'all 0.2s ease',
'&:hover': {
color: accent.a1,
transform: 'scale(1.08)',
backgroundColor: 'rgba(255,255,255,0.06)',
},
},
}}
>
{message}
</Alert>
</Snackbar>
);
}

View File

@ -39,6 +39,13 @@ export default function PageHeader() {
};
}
if (path.startsWith('/daily')) {
return {
title: 'Ежедневные награды',
subtitle: 'Ежедневный вход на сервер приносит бонусы и полезные награды!',
};
}
if (path.startsWith('/profile')) {
return {
title: 'Профиль пользователя',

View File

@ -10,6 +10,7 @@ import {
} from '@mui/material';
import { claimDaily, fetchDailyStatus, DailyStatusResponse } from '../../api';
import CoinsDisplay from '../CoinsDisplay';
import { useNavigate } from 'react-router-dom';
function formatHHMMSS(totalSeconds: number) {
const s = Math.max(0, Math.floor(totalSeconds));
@ -37,6 +38,7 @@ type DailyStatusCompat = DailyStatusResponse & {
};
export default function DailyRewards({ onClaimed, onOpenGame }: Props) {
const navigate = useNavigate();
const [status, setStatus] = useState<DailyStatusCompat | null>(null);
const [loading, setLoading] = useState(false);
const [tick, setTick] = useState(0);
@ -129,11 +131,14 @@ export default function DailyRewards({ onClaimed, onOpenGame }: Props) {
return `До следующей награды: ${formatHHMMSS(clientSecondsLeft)}`;
}, [status, wasOnlineToday, canClaim, clientSecondsLeft]);
const navigateDaily = () => {
navigate('/daily');
};
return (
<Card
sx={{
width: '90%',
maxWidth: 520,
width: '100%',
background: 'rgba(20,20,20,0.9)',
borderRadius: '2vw',
border: '1px solid rgba(255,255,255,0.08)',
@ -227,6 +232,22 @@ export default function DailyRewards({ onClaimed, onOpenGame }: Props) {
>
{loading ? 'Забираем...' : 'Забрать награду'}
</Button>
<Button
variant="contained"
fullWidth
onClick={navigateDaily}
sx={{
mt: 1,
transition: 'transform 0.3s ease',
background:
'linear-gradient(71deg, #F27121 0%, #E940CD 70%, #8A2387 100%)',
fontFamily: 'Benzin-Bold',
borderRadius: '2.5vw',
'&:hover': { transform: 'scale(1.03)' },
}}
>
Ежедневные награды
</Button>
</>
)}
</CardContent>

View File

@ -5,7 +5,7 @@ import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded';
import { useEffect, useRef, useState } from 'react';
import { Tooltip } from '@mui/material';
import { fetchCoins } from '../api';
import CustomTooltip from './CustomTooltip';
import CustomTooltip from './Notifications/CustomTooltip';
import CoinsDisplay from './CoinsDisplay';
declare global {
interface Window {