minor redesign inventory + add functions
This commit is contained in:
@ -1,16 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import {
|
import { Box, Typography, Grid, Button, Paper } from '@mui/material';
|
||||||
Box,
|
|
||||||
Typography,
|
|
||||||
Grid,
|
|
||||||
Button,
|
|
||||||
FormControl,
|
|
||||||
Select,
|
|
||||||
MenuItem,
|
|
||||||
InputLabel,
|
|
||||||
Tooltip,
|
|
||||||
Paper,
|
|
||||||
} from '@mui/material';
|
|
||||||
|
|
||||||
import { FullScreenLoader } from '../components/FullScreenLoader';
|
import { FullScreenLoader } from '../components/FullScreenLoader';
|
||||||
import { translateServer } from '../utils/serverTranslator';
|
import { translateServer } from '../utils/serverTranslator';
|
||||||
@ -20,6 +9,8 @@ import {
|
|||||||
type InventoryRawItem,
|
type InventoryRawItem,
|
||||||
type InventoryItemsResponse,
|
type InventoryItemsResponse,
|
||||||
} from '../api';
|
} from '../api';
|
||||||
|
import CustomTooltip from '../components/Notifications/CustomTooltip';
|
||||||
|
import { getPlayerServer } from '../utils/playerOnlineCheck';
|
||||||
|
|
||||||
const KNOWN_SERVER_IPS = [
|
const KNOWN_SERVER_IPS = [
|
||||||
'minecraft.hub.popa-popa.ru',
|
'minecraft.hub.popa-popa.ru',
|
||||||
@ -32,22 +23,75 @@ function stripMinecraftColors(text?: string | null): string {
|
|||||||
return text.replace(/§[0-9A-FK-ORa-fk-or]/g, '');
|
return text.replace(/§[0-9A-FK-ORa-fk-or]/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
const Glass = ({ children }: { children: React.ReactNode }) => (
|
const CARD_BG =
|
||||||
<Paper
|
'radial-gradient(circle at 10% 10%, rgba(242,113,33,0.14), transparent 55%), radial-gradient(circle at 90% 20%, rgba(233,64,205,0.12), transparent 55%), rgba(10,10,20,0.86)';
|
||||||
elevation={0}
|
|
||||||
sx={{
|
const CardFacePaperSx = {
|
||||||
borderRadius: '1.2vw',
|
borderRadius: '1.2vw',
|
||||||
overflow: 'hidden',
|
background: CARD_BG,
|
||||||
background:
|
|
||||||
'radial-gradient(circle at 10% 10%, rgba(242,113,33,0.14), transparent 55%), radial-gradient(circle at 90% 20%, rgba(233,64,205,0.12), transparent 55%), rgba(10,10,20,0.86)',
|
|
||||||
border: '1px solid rgba(255,255,255,0.08)',
|
border: '1px solid rgba(255,255,255,0.08)',
|
||||||
boxShadow: '0 1.2vw 3.2vw rgba(0,0,0,0.55)',
|
boxShadow: '0 1.2vw 3.2vw rgba(0,0,0,0.55)',
|
||||||
color: 'white',
|
color: 'white',
|
||||||
}}
|
} as const;
|
||||||
>
|
|
||||||
<Box sx={{ p: '1.8vw' }}>{children}</Box>
|
function readLauncherConfig(): any {
|
||||||
</Paper>
|
try {
|
||||||
);
|
const raw = localStorage.getItem('launcher_config');
|
||||||
|
return raw ? JSON.parse(raw) : {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeLauncherConfig(next: any) {
|
||||||
|
localStorage.setItem('launcher_config', JSON.stringify(next));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLayout(username: string, serverIp: string): Record<string, number> {
|
||||||
|
const cfg = readLauncherConfig();
|
||||||
|
return cfg?.inventory_layout?.[username]?.[serverIp] ?? {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLayout(username: string, serverIp: string, layout: Record<string, number>) {
|
||||||
|
const cfg = readLauncherConfig();
|
||||||
|
cfg.inventory_layout ??= {};
|
||||||
|
cfg.inventory_layout[username] ??= {};
|
||||||
|
cfg.inventory_layout[username][serverIp] = layout;
|
||||||
|
writeLauncherConfig(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSlots(
|
||||||
|
items: InventoryRawItem[],
|
||||||
|
layout: Record<string, number>,
|
||||||
|
size = 28,
|
||||||
|
): (InventoryRawItem | null)[] {
|
||||||
|
const slots: (InventoryRawItem | null)[] = Array.from({ length: size }, () => null);
|
||||||
|
|
||||||
|
const byId = new Map(items.map((it) => [it.id, it]));
|
||||||
|
const used = new Set<string>();
|
||||||
|
|
||||||
|
// 1) ставим туда, куда сохранено
|
||||||
|
for (const [id, idx] of Object.entries(layout)) {
|
||||||
|
const i = Number(idx);
|
||||||
|
const it = byId.get(id);
|
||||||
|
if (!it) continue;
|
||||||
|
if (Number.isFinite(i) && i >= 0 && i < size && !slots[i]) {
|
||||||
|
slots[i] = it;
|
||||||
|
used.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) остальные — в первые пустые
|
||||||
|
for (const it of items) {
|
||||||
|
if (used.has(it.id)) continue;
|
||||||
|
const empty = slots.findIndex((x) => x === null);
|
||||||
|
if (empty === -1) break;
|
||||||
|
slots[empty] = it;
|
||||||
|
used.add(it.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return slots;
|
||||||
|
}
|
||||||
|
|
||||||
export default function Inventory() {
|
export default function Inventory() {
|
||||||
const [username, setUsername] = useState('');
|
const [username, setUsername] = useState('');
|
||||||
@ -60,7 +104,72 @@ export default function Inventory() {
|
|||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
const [pages, setPages] = useState(1);
|
const [pages, setPages] = useState(1);
|
||||||
const [withdrawingIds, setWithdrawingIds] = useState<string[]>([]);
|
const [withdrawingIds, setWithdrawingIds] = useState<string[]>([]);
|
||||||
const isServerSelectVisible = availableServers.length > 1;
|
const [hoveredId, setHoveredId] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const [isOnline, setIsOnline] = useState(false);
|
||||||
|
const [playerServer, setPlayerServer] = useState<string | null>(null);
|
||||||
|
const [checkingOnline, setCheckingOnline] = useState(false);
|
||||||
|
|
||||||
|
const [draggedItemId, setDraggedItemId] = useState<string | null>(null);
|
||||||
|
const [dragOverIndex, setDragOverIndex] = useState<number | null>(null);
|
||||||
|
const [dragPos, setDragPos] = useState<{ x: number; y: number } | null>(null);
|
||||||
|
|
||||||
|
type SlotItem = InventoryRawItem | null;
|
||||||
|
|
||||||
|
const [slots, setSlots] = useState<SlotItem[]>(() => Array.from({ length: 28 }, () => null));
|
||||||
|
const [draggedFromIndex, setDraggedFromIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMove = (e: MouseEvent) => {
|
||||||
|
if (!draggedItemId) return;
|
||||||
|
setDragPos({ x: e.clientX, y: e.clientY });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUp = () => {
|
||||||
|
if (
|
||||||
|
draggedItemId &&
|
||||||
|
draggedFromIndex !== null &&
|
||||||
|
dragOverIndex !== null &&
|
||||||
|
draggedFromIndex !== dragOverIndex
|
||||||
|
) {
|
||||||
|
setSlots((prev) => {
|
||||||
|
const next = [...prev];
|
||||||
|
const moving = next[draggedFromIndex];
|
||||||
|
if (!moving) return prev;
|
||||||
|
|
||||||
|
// ✅ swap или move в пустоту
|
||||||
|
const target = next[dragOverIndex];
|
||||||
|
next[dragOverIndex] = moving;
|
||||||
|
next[draggedFromIndex] = target ?? null;
|
||||||
|
|
||||||
|
// ✅ сохраняем layout (позиции предметов)
|
||||||
|
const layout: Record<string, number> = {};
|
||||||
|
next.forEach((it, idx) => {
|
||||||
|
if (it) layout[it.id] = idx;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (username && selectedServerIp) {
|
||||||
|
setLayout(username, selectedServerIp, layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setDraggedItemId(null);
|
||||||
|
setDraggedFromIndex(null);
|
||||||
|
setDragOverIndex(null);
|
||||||
|
setDragPos(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('mousemove', handleMove);
|
||||||
|
window.addEventListener('mouseup', handleUp);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('mousemove', handleMove);
|
||||||
|
window.removeEventListener('mouseup', handleUp);
|
||||||
|
};
|
||||||
|
}, [draggedItemId, dragOverIndex]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const savedConfig = localStorage.getItem('launcher_config');
|
const savedConfig = localStorage.getItem('launcher_config');
|
||||||
@ -82,19 +191,24 @@ export default function Inventory() {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const servers = checks
|
return checks
|
||||||
.filter((r): r is PromiseFulfilledResult<{ ip: string; has: boolean }> => r.status === 'fulfilled')
|
.filter(
|
||||||
|
(r): r is PromiseFulfilledResult<{ ip: string; has: boolean }> => r.status === 'fulfilled',
|
||||||
|
)
|
||||||
.filter((r) => r.value.has)
|
.filter((r) => r.value.has)
|
||||||
.map((r) => r.value.ip);
|
.map((r) => r.value.ip);
|
||||||
|
|
||||||
return servers;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadInventory = async (u: string, ip: string, p: number) => {
|
const loadInventory = async (u: string, ip: string, p: number) => {
|
||||||
const res: InventoryItemsResponse = await fetchInventoryItems(u, ip, p, limit);
|
const res: InventoryItemsResponse = await fetchInventoryItems(u, ip, p, limit);
|
||||||
|
const list = res.items || [];
|
||||||
|
|
||||||
setItems(res.items || []);
|
setItems(res.items || []);
|
||||||
setTotal(res.total ?? 0);
|
setTotal(res.total ?? 0);
|
||||||
setPages(Math.max(1, Math.ceil((res.total ?? 0) / (res.limit ?? limit))));
|
setPages(Math.max(1, Math.ceil((res.total ?? 0) / (res.limit ?? limit))));
|
||||||
|
|
||||||
|
const layout = getLayout(u, ip);
|
||||||
|
setSlots(buildSlots(list, layout, 28));
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -164,11 +278,21 @@ export default function Inventory() {
|
|||||||
};
|
};
|
||||||
}, [selectedServerIp]);
|
}, [selectedServerIp]);
|
||||||
|
|
||||||
|
const withWithdrawing = async (id: string, fn: () => Promise<void>) => {
|
||||||
|
setWithdrawingIds((prev) => [...prev, id]);
|
||||||
|
try {
|
||||||
|
await fn();
|
||||||
|
} finally {
|
||||||
|
setWithdrawingIds((prev) => prev.filter((x) => x !== id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleWithdraw = async (item: InventoryRawItem) => {
|
const handleWithdraw = async (item: InventoryRawItem) => {
|
||||||
if (!username || !selectedServerIp) return;
|
if (!username || !selectedServerIp) return;
|
||||||
|
|
||||||
|
// сервер в UI может не совпадать — оставим защиту
|
||||||
if (selectedServerIp !== item.server_ip) {
|
if (selectedServerIp !== item.server_ip) {
|
||||||
alert("Ошибка! Вы не на том сервере для выдачи этого предмета.");
|
alert('Ошибка! Вы не на том сервере для выдачи этого предмета.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,24 +305,48 @@ export default function Inventory() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setItems((prevItems) => prevItems.filter((prevItem) => prevItem.id !== item.id));
|
setItems((prevItems) => prevItems.filter((prevItem) => prevItem.id !== item.id));
|
||||||
|
|
||||||
|
setSlots((prev) => {
|
||||||
|
const next = prev.map((x) => (x?.id === item.id ? null : x));
|
||||||
|
|
||||||
|
const layout: Record<string, number> = {};
|
||||||
|
next.forEach((it, idx) => {
|
||||||
|
if (it) layout[it.id] = idx;
|
||||||
|
});
|
||||||
|
|
||||||
|
setLayout(username, selectedServerIp, layout);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Ошибка при выводе предмета:", e);
|
console.error('Ошибка при выводе предмета:', e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const withWithdrawing = async (id: string, fn: () => Promise<void>) => {
|
const checkPlayerStatus = async () => {
|
||||||
setWithdrawingIds((prev) => [...prev, id]);
|
if (!username) return;
|
||||||
|
|
||||||
|
setCheckingOnline(true);
|
||||||
try {
|
try {
|
||||||
await fn();
|
const res = await getPlayerServer(username);
|
||||||
|
setIsOnline(!!res?.online);
|
||||||
|
setPlayerServer(res?.server?.ip ?? null);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Ошибка проверки онлайна:', e);
|
||||||
|
setIsOnline(false);
|
||||||
|
setPlayerServer(null);
|
||||||
} finally {
|
} finally {
|
||||||
setWithdrawingIds((prev) => prev.filter((x) => x !== id));
|
setCheckingOnline(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const headerServerName = selectedServerIp
|
useEffect(() => {
|
||||||
? translateServer(`Server ${selectedServerIp}`)
|
if (!username) return;
|
||||||
: '';
|
checkPlayerStatus();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [username]);
|
||||||
|
|
||||||
|
const headerServerName = selectedServerIp ? translateServer(`Server ${selectedServerIp}`) : '';
|
||||||
|
|
||||||
if (!username) {
|
if (!username) {
|
||||||
return (
|
return (
|
||||||
@ -253,8 +401,17 @@ export default function Inventory() {
|
|||||||
>
|
>
|
||||||
{loading && <FullScreenLoader fullScreen={false} message="Загрузка инвентаря..." />}
|
{loading && <FullScreenLoader fullScreen={false} message="Загрузка инвентаря..." />}
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, flexWrap: 'wrap', justifyContent: 'space-evenly', flexDirection: 'row-reverse' }}>
|
{/* ШАПКА + ПАГИНАЦИЯ */}
|
||||||
{/* Пагинация */}
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 2,
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
justifyContent: 'space-evenly',
|
||||||
|
flexDirection: 'row-reverse',
|
||||||
|
}}
|
||||||
|
>
|
||||||
{!!selectedServerIp && (
|
{!!selectedServerIp && (
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<Button
|
<Button
|
||||||
@ -303,57 +460,284 @@ export default function Inventory() {
|
|||||||
>
|
>
|
||||||
Инвентарь {headerServerName ? `— ${headerServerName}` : ''}
|
Инвентарь {headerServerName ? `— ${headerServerName}` : ''}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* GRID */}
|
||||||
<Grid container spacing={2}>
|
<Grid container spacing={2}>
|
||||||
{Array.from({ length: 28 }).map((_, index) => {
|
{Array.from({ length: 28 }).map((_, index) => {
|
||||||
const item = items[index];
|
const item = slots[index];
|
||||||
|
|
||||||
|
// ПУСТАЯ ЯЧЕЙКА
|
||||||
|
if (!item) {
|
||||||
return (
|
return (
|
||||||
<Grid item xs={3} key={index}>
|
<Grid
|
||||||
<Glass>
|
item
|
||||||
|
xs={3}
|
||||||
|
key={index}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
if (draggedItemId) setDragOverIndex(index);
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
outline:
|
||||||
|
draggedItemId && dragOverIndex === index
|
||||||
|
? '2px dashed rgba(255,255,255,0.4)'
|
||||||
|
: 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Paper
|
||||||
|
elevation={0}
|
||||||
|
sx={{
|
||||||
|
...CardFacePaperSx,
|
||||||
|
overflow: 'hidden',
|
||||||
|
width: '12vw',
|
||||||
|
height: '12vw',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
color: 'rgba(255,255,255,0.5)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography>Пусто</Typography>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayNameRaw =
|
||||||
|
item.item_data?.meta?.display_name ?? item.item_data?.material ?? 'Предмет';
|
||||||
|
const displayName = stripMinecraftColors(displayNameRaw);
|
||||||
|
|
||||||
|
const amount =
|
||||||
|
(item as any)?.amount ??
|
||||||
|
(item as any)?.item_data?.amount ??
|
||||||
|
(item as any)?.item_data?.meta?.amount ??
|
||||||
|
1;
|
||||||
|
|
||||||
|
const isHovered = hoveredId === item.id;
|
||||||
|
const isWithdrawing = withdrawingIds.includes(item.id);
|
||||||
|
|
||||||
|
// ✅ проверка: игрок реально онлайн на нужном сервере
|
||||||
|
const isOnRightServer = isOnline && playerServer === item.server_ip;
|
||||||
|
const canWithdraw = isOnRightServer && !loading && !checkingOnline && !isWithdrawing;
|
||||||
|
|
||||||
|
const texture = item.item_data?.material
|
||||||
|
? `https://cdn.minecraft.popa-popa.ru/textures/${item.item_data.material.toLowerCase()}.png`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid
|
||||||
|
item
|
||||||
|
xs={3}
|
||||||
|
key={item.id}
|
||||||
|
onMouseEnter={() => {
|
||||||
|
if (draggedItemId) setDragOverIndex(index);
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
outline:
|
||||||
|
draggedItemId && dragOverIndex === index
|
||||||
|
? '2px dashed rgba(255,255,255,0.4)'
|
||||||
|
: 'none',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: '8vw',
|
width: '100%',
|
||||||
height: '8vw',
|
perspective: '1200px',
|
||||||
display: 'flex',
|
cursor: draggedItemId === item.id ? 'grabbing' : 'grab',
|
||||||
justifyContent: 'center',
|
opacity: draggedItemId === item.id ? 0.4 : 1,
|
||||||
alignItems: 'center',
|
}}
|
||||||
position: 'relative',
|
onMouseEnter={() => setHoveredId(item.id)}
|
||||||
borderRadius: '5px',
|
onMouseLeave={() => setHoveredId(null)}
|
||||||
boxShadow: item ? '0 10px 40px rgba(0,0,0,0.3)' : 'none',
|
onMouseDown={(e) => {
|
||||||
'&:hover': {
|
const target = e.target as HTMLElement;
|
||||||
transform: 'scale(1.1)',
|
if (target.closest('button')) return;
|
||||||
cursor: item ? 'pointer' : 'default',
|
|
||||||
},
|
e.preventDefault();
|
||||||
transition: 'all 0.25s ease',
|
setDraggedItemId(item.id);
|
||||||
|
setDraggedFromIndex(index);
|
||||||
|
setDragPos({ x: e.clientX, y: e.clientY });
|
||||||
}}
|
}}
|
||||||
onClick={() => item && handleWithdraw(item)}
|
|
||||||
>
|
>
|
||||||
{item ? (
|
<Box
|
||||||
<Tooltip
|
sx={{
|
||||||
title={`${item.item_data?.meta?.display_name}\n${item.item_data?.material}`}
|
position: 'relative',
|
||||||
placement="top"
|
width: '12vw',
|
||||||
|
height: '12vw', // фиксированная высота = Grid не дергается
|
||||||
|
transformStyle: 'preserve-3d',
|
||||||
|
transition: 'transform 0.5s cubic-bezier(0.4, 0.2, 0.2, 1)',
|
||||||
|
transform: isHovered ? 'rotateY(180deg)' : 'rotateY(0deg)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* FRONT */}
|
||||||
|
<Paper
|
||||||
|
elevation={0}
|
||||||
|
sx={{
|
||||||
|
...CardFacePaperSx,
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
backfaceVisibility: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
component="img"
|
component="img"
|
||||||
src={`https://cdn.minecraft.popa-popa.ru/textures/${item.item_data?.material.toLowerCase()}.png`}
|
src={texture}
|
||||||
sx={{
|
sx={{
|
||||||
width: '60%',
|
width: '5vw',
|
||||||
height: '60%',
|
height: '5vw',
|
||||||
objectFit: 'contain',
|
objectFit: 'contain',
|
||||||
imageRendering: 'pixelated',
|
imageRendering: 'pixelated',
|
||||||
|
userSelect: 'none',
|
||||||
|
transition: 'transform 0.25s ease',
|
||||||
|
transform: isHovered ? 'scale(1.05)' : 'scale(1)',
|
||||||
}}
|
}}
|
||||||
|
draggable={false}
|
||||||
|
alt={displayName}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Paper>
|
||||||
|
|
||||||
|
{/* BACK */}
|
||||||
|
<Paper
|
||||||
|
elevation={0}
|
||||||
|
sx={{
|
||||||
|
...CardFacePaperSx,
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
backfaceVisibility: 'hidden',
|
||||||
|
transform: 'rotateY(180deg)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
justifyContent: 'center',
|
||||||
|
gap: '0.8vw',
|
||||||
|
px: '1.1vw',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontFamily: 'Benzin-Bold',
|
||||||
|
fontSize: '0.8vw',
|
||||||
|
lineHeight: 1.2,
|
||||||
|
textAlign: 'center',
|
||||||
|
wordBreak: 'break-word',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{displayName}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: '0.7vw',
|
||||||
|
textAlign: 'center',
|
||||||
|
color: 'rgba(255,255,255,0.7)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Кол-во: {amount}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{!isOnRightServer ? (
|
||||||
|
<CustomTooltip
|
||||||
|
essential
|
||||||
|
title={
|
||||||
|
!isOnline
|
||||||
|
? 'Вы должны быть онлайн на сервере'
|
||||||
|
: `Перейдите на сервер ${item.server_ip}`
|
||||||
|
}
|
||||||
|
placement="top"
|
||||||
|
arrow
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<Button
|
||||||
|
disabled
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
sx={{
|
||||||
|
fontSize: '0.8vw',
|
||||||
|
borderRadius: '2vw',
|
||||||
|
fontFamily: 'Benzin-Bold',
|
||||||
|
border: '1px solid rgba(255,255,255,0.15)',
|
||||||
|
color: 'rgba(255,255,255,0.5)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Выдать
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</CustomTooltip>
|
||||||
) : (
|
) : (
|
||||||
<Typography sx={{ color: 'rgba(255,255,255,0.5)' }}>Пусто</Typography>
|
<Button
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
disabled={!canWithdraw}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
handleWithdraw(item);
|
||||||
|
}}
|
||||||
|
sx={{
|
||||||
|
fontSize: '0.8vw',
|
||||||
|
borderRadius: '2vw',
|
||||||
|
fontFamily: 'Benzin-Bold',
|
||||||
|
border: '1px solid rgba(255,255,255,0.15)',
|
||||||
|
color: 'white',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isWithdrawing ? 'Выдача...' : 'Выдать'}
|
||||||
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</Glass>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Grid>
|
</Grid>
|
||||||
|
{draggedItemId && dragPos && (() => {
|
||||||
|
const draggedItem = items.find(i => i.id === draggedItemId);
|
||||||
|
if (!draggedItem) return null;
|
||||||
|
|
||||||
|
const texture = `https://cdn.minecraft.popa-popa.ru/textures/${draggedItem.item_data.material.toLowerCase()}.png`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
position: 'fixed',
|
||||||
|
left: dragPos.x,
|
||||||
|
top: dragPos.y,
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
//width: '12vw',
|
||||||
|
//height: '12vw',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
zIndex: 9999,
|
||||||
|
opacity: 0.9,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Paper
|
||||||
|
elevation={0}
|
||||||
|
sx={{
|
||||||
|
...CardFacePaperSx,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: '12vw',
|
||||||
|
height: '12vw',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
component="img"
|
||||||
|
src={texture}
|
||||||
|
sx={{
|
||||||
|
width: '5vw',
|
||||||
|
height: '5vw',
|
||||||
|
imageRendering: 'pixelated',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
</Box>
|
</Box>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user