add info for item
This commit is contained in:
@ -174,6 +174,7 @@ export interface MarketplaceResponse {
|
|||||||
export interface MarketplaceItemResponse {
|
export interface MarketplaceItemResponse {
|
||||||
_id: string;
|
_id: string;
|
||||||
id: string;
|
id: string;
|
||||||
|
description: string;
|
||||||
material: string;
|
material: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
price: number;
|
price: number;
|
||||||
@ -186,6 +187,10 @@ export interface MarketplaceItemResponse {
|
|||||||
slot: number;
|
slot: number;
|
||||||
material: string;
|
material: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
|
meta?: {
|
||||||
|
enchants?: Record<string, number>;
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
created_at: string;
|
created_at: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,6 +50,8 @@ import CloseIcon from '@mui/icons-material/Close';
|
|||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import type { SxProps, Theme } from '@mui/material/styles';
|
import type { SxProps, Theme } from '@mui/material/styles';
|
||||||
import { getWsBaseUrl } from '../realtime/wsBase';
|
import { getWsBaseUrl } from '../realtime/wsBase';
|
||||||
|
import { formatEnchants } from '../utils/itemTranslator';
|
||||||
|
import { translateMetaKey, formatMetaValue } from '../utils/itemTranslator';
|
||||||
|
|
||||||
interface TabPanelProps {
|
interface TabPanelProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@ -133,15 +135,31 @@ export default function Marketplace() {
|
|||||||
const [editPriceOpen, setEditPriceOpen] = useState(false);
|
const [editPriceOpen, setEditPriceOpen] = useState(false);
|
||||||
const [editItem, setEditItem] = useState<MarketplaceItemResponse | null>(null);
|
const [editItem, setEditItem] = useState<MarketplaceItemResponse | null>(null);
|
||||||
const [editPriceValue, setEditPriceValue] = useState<string>('');
|
const [editPriceValue, setEditPriceValue] = useState<string>('');
|
||||||
|
const [description, setDescription] = useState('');
|
||||||
|
|
||||||
const [removeOpen, setRemoveOpen] = useState(false);
|
const [removeOpen, setRemoveOpen] = useState(false);
|
||||||
const [removeItem, setRemoveItem] = useState<MarketplaceItemResponse | null>(null);
|
const [removeItem, setRemoveItem] = useState<MarketplaceItemResponse | null>(null);
|
||||||
|
|
||||||
const inventoryRef = useRef<PlayerInventoryHandle | null>(null);
|
const inventoryRef = useRef<PlayerInventoryHandle | null>(null);
|
||||||
|
|
||||||
|
const [metaDialogOpen, setMetaDialogOpen] = useState(false);
|
||||||
|
const [metaItem, setMetaItem] = useState<MarketplaceItemResponse | null>(null);
|
||||||
|
const [metaSearch, setMetaSearch] = useState('');
|
||||||
|
|
||||||
|
const openMetaDialog = (item: MarketplaceItemResponse) => {
|
||||||
|
setMetaItem(item);
|
||||||
|
setMetaDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeMetaDialog = () => {
|
||||||
|
setMetaDialogOpen(false);
|
||||||
|
setMetaItem(null);
|
||||||
|
};
|
||||||
|
|
||||||
const openEditPrice = (item: MarketplaceItemResponse) => {
|
const openEditPrice = (item: MarketplaceItemResponse) => {
|
||||||
setEditItem(item);
|
setEditItem(item);
|
||||||
setEditPriceValue(String(item.price ?? ''));
|
setEditPriceValue(String(item.price ?? ''));
|
||||||
|
setDescription(item.description ?? '');
|
||||||
setEditPriceOpen(true);
|
setEditPriceOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -319,14 +337,26 @@ export default function Marketplace() {
|
|||||||
const handleSavePrice = async () => {
|
const handleSavePrice = async () => {
|
||||||
if (!username || !selectedServer || !editItem) return;
|
if (!username || !selectedServer || !editItem) return;
|
||||||
|
|
||||||
const parsed = Number(editPriceValue);
|
const price = Number(editPriceValue);
|
||||||
if (!Number.isFinite(parsed) || parsed <= 0) {
|
if (!price || price <= 0) {
|
||||||
showNotification('Цена должна быть числом больше 0', 'warning');
|
showNotification('Цена должна быть числом больше 0', 'warning');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ДОДЕЛАТЬ - ИЗМЕНЕНИЕ ОПИСАНИЯ
|
||||||
|
// const payload: any = {
|
||||||
|
// itemId: editItem.id,
|
||||||
|
// price,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// if (description.trim() !== '') {
|
||||||
|
// payload.description = description.trim();
|
||||||
|
// } else {
|
||||||
|
// payload.description = null;
|
||||||
|
// }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateMarketplaceItemPrice(username, editItem.id, parsed);
|
await updateMarketplaceItemPrice(username, editItem.id, price);
|
||||||
|
|
||||||
showNotification('Цена обновлена', 'success');
|
showNotification('Цена обновлена', 'success');
|
||||||
|
|
||||||
@ -413,6 +443,20 @@ export default function Marketplace() {
|
|||||||
setTabValue(newValue);
|
setTabValue(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const metaKeyMatchesSearch = (key: string, search: string) => {
|
||||||
|
if (!search) return true;
|
||||||
|
|
||||||
|
const s = search.toLowerCase();
|
||||||
|
|
||||||
|
const original = key.toLowerCase();
|
||||||
|
const translated = translateMetaKey(key).toLowerCase();
|
||||||
|
|
||||||
|
return (
|
||||||
|
original.includes(s) ||
|
||||||
|
translated.includes(s)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const handleBuyItem = async (itemId: string) => {
|
const handleBuyItem = async (itemId: string) => {
|
||||||
try {
|
try {
|
||||||
if (!username) return;
|
if (!username) return;
|
||||||
@ -502,10 +546,10 @@ export default function Marketplace() {
|
|||||||
color: 'rgba(255,255,255,0.95)',
|
color: 'rgba(255,255,255,0.95)',
|
||||||
},
|
},
|
||||||
|
|
||||||
transition: 'transform 0.18s ease, filter 0.18s ease, border-color 0.18s ease',
|
transition: 'transform 0.18s ease, filter 0.18s ease, border-color 0.25s ease, box-shadow 0.25s ease',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
transform: 'scale(1.01)',
|
borderColor: 'rgba(200, 33, 242, 0.35)',
|
||||||
borderColor: 'rgba(255,255,255,0.14)',
|
boxShadow: '0 1.2vw 3.2vw rgba(53, 3, 66, 0.45)',
|
||||||
},
|
},
|
||||||
'&.Mui-focused': {
|
'&.Mui-focused': {
|
||||||
borderColor: 'rgba(255,255,255,0.18)',
|
borderColor: 'rgba(255,255,255,0.18)',
|
||||||
@ -579,6 +623,26 @@ export default function Marketplace() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getItemMeta = (item: MarketplaceItemResponse) => {
|
||||||
|
return item.item_data?.meta ?? null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractEnchants = (meta: Record<string, any>) => {
|
||||||
|
return meta?.enchants && typeof meta.enchants === 'object'
|
||||||
|
? meta.enchants
|
||||||
|
: null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasItemMeta = (item: MarketplaceItemResponse) => {
|
||||||
|
const meta = item.item_data;
|
||||||
|
if (!meta) return false;
|
||||||
|
|
||||||
|
// базовые поля, которые НЕ считаем метой
|
||||||
|
const baseKeys = ['slot', 'material', 'amount'];
|
||||||
|
|
||||||
|
return Object.keys(meta).some((key) => !baseKeys.includes(key));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const statusChip = useMemo(() => {
|
const statusChip = useMemo(() => {
|
||||||
if (statusLoading) {
|
if (statusLoading) {
|
||||||
@ -676,7 +740,6 @@ export default function Marketplace() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
autoFocus
|
|
||||||
fullWidth
|
fullWidth
|
||||||
label="Новая цена"
|
label="Новая цена"
|
||||||
value={editPriceValue}
|
value={editPriceValue}
|
||||||
@ -685,6 +748,15 @@ export default function Marketplace() {
|
|||||||
sx={PRICE_FIELD_SX}
|
sx={PRICE_FIELD_SX}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* <TextField
|
||||||
|
fullWidth
|
||||||
|
label="Описание (необязательно)"
|
||||||
|
value={description}
|
||||||
|
onChange={(e) => setDescription(e.target.value)}
|
||||||
|
inputProps={{ min: 1 }}
|
||||||
|
sx={PRICE_FIELD_SX}
|
||||||
|
/> */}
|
||||||
|
|
||||||
{editItem && (
|
{editItem && (
|
||||||
<Box sx={{ mt: 1.2, color: 'rgba(255,255,255,0.62)', fontWeight: 700 }}>
|
<Box sx={{ mt: 1.2, color: 'rgba(255,255,255,0.62)', fontWeight: 700 }}>
|
||||||
Текущая цена: <b style={{ color: 'rgba(255,255,255,0.9)' }}>{editItem.price}</b>
|
Текущая цена: <b style={{ color: 'rgba(255,255,255,0.9)' }}>{editItem.price}</b>
|
||||||
@ -802,6 +874,180 @@ export default function Marketplace() {
|
|||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
{/* FULL INFO ITEM */}
|
||||||
|
<Dialog
|
||||||
|
open={metaDialogOpen}
|
||||||
|
onClose={closeMetaDialog}
|
||||||
|
fullWidth
|
||||||
|
maxWidth="sm"
|
||||||
|
PaperProps={{ sx: GLASS_PAPER_SX }}
|
||||||
|
>
|
||||||
|
<DialogTitle sx={DIALOG_TITLE_SX}>
|
||||||
|
Информация о предмете
|
||||||
|
<IconButton onClick={closeMetaDialog} sx={CLOSE_BTN_SX}>
|
||||||
|
<CloseIcon fontSize="small" />
|
||||||
|
</IconButton>
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
|
<DialogContent dividers sx={DIALOG_DIVIDERS_SX}>
|
||||||
|
{metaItem && (() => {
|
||||||
|
const meta = getItemMeta(metaItem);
|
||||||
|
if (!meta) return null;
|
||||||
|
|
||||||
|
const enchants = formatEnchants(meta.enchants);
|
||||||
|
|
||||||
|
const filteredMeta = Object.entries(meta).filter(([key]) =>
|
||||||
|
key !== 'enchants' &&
|
||||||
|
metaKeyMatchesSearch(key, metaSearch)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '1vw' }}>
|
||||||
|
|
||||||
|
{/* IMAGE + TITLE */}
|
||||||
|
<Box sx={{ display: 'flex', gap: '1vw', alignItems: 'center' }}>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
borderRadius: '1vw',
|
||||||
|
border: '1px solid rgba(255,255,255,0.12)',
|
||||||
|
background: 'rgba(0,0,0,0.35)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={`https://cdn.minecraft.popa-popa.ru/textures/${metaItem.material.toLowerCase()}.png`}
|
||||||
|
alt={metaItem.material}
|
||||||
|
style={{ width: '70%', imageRendering: 'pixelated' }}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Typography sx={{ fontFamily: 'Benzin-Bold', fontSize: '1.2rem' }}>
|
||||||
|
{metaItem.display_name || metaItem.material}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Typography sx={{ color: 'rgba(255,255,255,0.7)', fontSize: '0.9rem' }}>
|
||||||
|
Количество: {metaItem.amount} · Цена: {metaItem.price}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Divider sx={{mt: '0.7vw'}} />
|
||||||
|
|
||||||
|
{/* DESCRIPTION (optional) */}
|
||||||
|
{metaItem.description && (
|
||||||
|
<Typography>
|
||||||
|
Описание
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
mt: 0.6,
|
||||||
|
p: '0.8vw',
|
||||||
|
borderRadius: '0.8vw',
|
||||||
|
background: 'rgba(255,255,255,0.06)',
|
||||||
|
border: '1px solid rgba(255,255,255,0.10)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography sx={{ color: 'rgba(255,255,255,0.85)', fontSize: '0.9rem' }}>
|
||||||
|
{metaItem.description}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Divider sx={{mt: '2vw'}} />
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* SEARCH */}
|
||||||
|
<TextField
|
||||||
|
placeholder="Поиск по метаданным..."
|
||||||
|
value={metaSearch}
|
||||||
|
onChange={(e) => setMetaSearch(e.target.value)}
|
||||||
|
fullWidth
|
||||||
|
sx={PRICE_FIELD_SX}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Divider sx={{mt: '0.7vw'}} />
|
||||||
|
|
||||||
|
{/* ENCHANTS */}
|
||||||
|
{enchants && (
|
||||||
|
<>
|
||||||
|
{enchants.length > 0 &&
|
||||||
|
<Typography sx={{ fontFamily: 'Benzin-Bold', fontSize: '1rem' }}>
|
||||||
|
Зачарования
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: '0.6vw' }}>
|
||||||
|
{enchants.map((e) => (
|
||||||
|
<Chip
|
||||||
|
key={e.label}
|
||||||
|
label={`${e.label} ${e.level}`}
|
||||||
|
sx={{
|
||||||
|
fontFamily: 'Benzin-Bold',
|
||||||
|
background: 'rgba(156,255,198,0.15)',
|
||||||
|
border: '1px solid rgba(156,255,198,0.25)',
|
||||||
|
color: 'rgba(156,255,198,0.95)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* OTHER META */}
|
||||||
|
{filteredMeta.length > 0 && (
|
||||||
|
<>
|
||||||
|
<Typography sx={{ fontFamily: 'Benzin-Bold', fontSize: '1rem' }}>
|
||||||
|
Свойства
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: '0.4vw' }}>
|
||||||
|
{filteredMeta.map(([key, value]) => (
|
||||||
|
<Box
|
||||||
|
key={key}
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
gap: '1vw',
|
||||||
|
fontSize: '0.85rem',
|
||||||
|
color: 'rgba(255,255,255,0.85)',
|
||||||
|
background: 'rgba(255,255,255,0.04)',
|
||||||
|
borderRadius: '0.6vw',
|
||||||
|
px: '0.8vw',
|
||||||
|
py: '0.4vw',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ opacity: 0.7 }}>{translateMetaKey(key)}</span>
|
||||||
|
<span style={{ fontWeight: 600 }}>{formatMetaValue(value)}</span>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
<DialogActions sx={{ p: '1.2vw' }}>
|
||||||
|
<Button
|
||||||
|
onClick={closeMetaDialog}
|
||||||
|
sx={{
|
||||||
|
fontFamily: 'Benzin-Bold',
|
||||||
|
color: '#fff',
|
||||||
|
borderRadius: '999px',
|
||||||
|
px: '1.6vw',
|
||||||
|
py: '0.6vw',
|
||||||
|
background: GRADIENT,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Закрыть
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
{/* HEADER (glass) */}
|
{/* HEADER (glass) */}
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
@ -1114,6 +1360,7 @@ export default function Marketplace() {
|
|||||||
<Box sx={{ position: 'relative', p: '1.0vw', pb: 0 }}>
|
<Box sx={{ position: 'relative', p: '1.0vw', pb: 0 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
position: 'relative', // 👈 важно
|
||||||
borderRadius: '1.4vw',
|
borderRadius: '1.4vw',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
border: '1px solid rgba(255,255,255,0.12)',
|
border: '1px solid rgba(255,255,255,0.12)',
|
||||||
@ -1124,6 +1371,36 @@ export default function Marketplace() {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{/* INFO BUTTON */}
|
||||||
|
{hasItemMeta(item) && (
|
||||||
|
<IconButton
|
||||||
|
onClick={() => openMetaDialog(item)}
|
||||||
|
size="small"
|
||||||
|
sx={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '0.5vw',
|
||||||
|
right: '0.5vw',
|
||||||
|
width: '1.8vw',
|
||||||
|
height: '1.8vw',
|
||||||
|
borderRadius: '50%',
|
||||||
|
zIndex: 2,
|
||||||
|
|
||||||
|
color: 'rgba(255,255,255,0.85)',
|
||||||
|
background: 'rgba(0,0,0,0.45)',
|
||||||
|
border: '1px solid rgba(255,255,255,0.18)',
|
||||||
|
backdropFilter: 'blur(8px)',
|
||||||
|
|
||||||
|
'&:hover': {
|
||||||
|
background: 'rgba(255,255,255,0.15)',
|
||||||
|
transform: 'scale(1.05)',
|
||||||
|
},
|
||||||
|
transition: 'all 0.2s ease',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
i
|
||||||
|
</IconButton>
|
||||||
|
)}
|
||||||
|
|
||||||
<CardMedia
|
<CardMedia
|
||||||
component="img"
|
component="img"
|
||||||
sx={{
|
sx={{
|
||||||
@ -1252,11 +1529,11 @@ export default function Marketplace() {
|
|||||||
p: '1.6vw',
|
p: '1.6vw',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{playerServer && username ? (
|
{ username ? (
|
||||||
<PlayerInventory
|
<PlayerInventory
|
||||||
ref={inventoryRef}
|
ref={inventoryRef}
|
||||||
username={username}
|
username={'Yana'}
|
||||||
serverIp={playerServer.ip}
|
serverIp={'minecraft.survival.popa-popa.ru'}
|
||||||
onSellSuccess={() => {
|
onSellSuccess={() => {
|
||||||
if (selectedServer) loadMarketItems(selectedServer.ip, 1);
|
if (selectedServer) loadMarketItems(selectedServer.ip, 1);
|
||||||
showNotification('Предмет успешно выставлен на продажу!', 'success');
|
showNotification('Предмет успешно выставлен на продажу!', 'success');
|
||||||
@ -1270,7 +1547,6 @@ export default function Marketplace() {
|
|||||||
</Box>
|
</Box>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
{/* "Мои товары" — пока заглушка (как было, логики у тебя в файле нет) */}
|
|
||||||
<TabPanel value={tabValue} index={2}>
|
<TabPanel value={tabValue} index={2}>
|
||||||
{myLoading ? (
|
{myLoading ? (
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', mt: '6vh' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'center', mt: '6vh' }}>
|
||||||
@ -1491,3 +1767,41 @@ export default function Marketplace() {
|
|||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ItemMetaTooltip({ meta }: { meta: Record<string, any> }) {
|
||||||
|
return (
|
||||||
|
<Box sx={{ minWidth: 220 }}>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontFamily: 'Benzin-Bold',
|
||||||
|
fontSize: '0.85rem',
|
||||||
|
mb: 0.6,
|
||||||
|
color: '#fff',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Информация о предмете
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{Object.entries(meta).map(([key, value]) => (
|
||||||
|
<Box
|
||||||
|
key={key}
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
gap: '0.8vw',
|
||||||
|
fontSize: '0.75rem',
|
||||||
|
color: 'rgba(255,255,255,0.75)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{key}</span>
|
||||||
|
<span style={{ opacity: 0.9 }}>
|
||||||
|
{typeof value === 'object'
|
||||||
|
? JSON.stringify(value)
|
||||||
|
: String(value)}
|
||||||
|
</span>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
106
src/renderer/utils/itemTranslator.ts
Normal file
106
src/renderer/utils/itemTranslator.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// utils/itemTranslator.ts
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
/* ENCHANT TRANSLATIONS */
|
||||||
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
export const ENCHANT_TRANSLATIONS: Record<string, string> = {
|
||||||
|
sharpness: 'Острота',
|
||||||
|
smite: 'Небесная кара',
|
||||||
|
bane_of_arthropods: 'Бич членистоногих',
|
||||||
|
efficiency: 'Эффективность',
|
||||||
|
unbreaking: 'Прочность',
|
||||||
|
fortune: 'Удача',
|
||||||
|
silk_touch: 'Шёлковое касание',
|
||||||
|
power: 'Сила',
|
||||||
|
punch: 'Отдача',
|
||||||
|
flame: 'Огонь',
|
||||||
|
infinity: 'Бесконечность',
|
||||||
|
protection: 'Защита',
|
||||||
|
fire_protection: 'Огнестойкость',
|
||||||
|
blast_protection: 'Взрывоустойчивость',
|
||||||
|
projectile_protection: 'Защита от снарядов',
|
||||||
|
feather_falling: 'Невесомость',
|
||||||
|
respiration: 'Подводное дыхание',
|
||||||
|
aqua_affinity: 'Подводник',
|
||||||
|
thorns: 'Шипы',
|
||||||
|
depth_strider: 'Подводная ходьба',
|
||||||
|
frost_walker: 'Ледоход',
|
||||||
|
mending: 'Починка',
|
||||||
|
binding_curse: 'Проклятие несъёмности',
|
||||||
|
vanishing_curse: 'Проклятие утраты',
|
||||||
|
looting: 'Добыча',
|
||||||
|
sweeping: 'Разящий клинок',
|
||||||
|
fire_aspect: 'Заговор огня',
|
||||||
|
knockback: 'Отдача',
|
||||||
|
luck_of_the_sea: 'Морская удача',
|
||||||
|
lure: 'Приманка',
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
/* GENERIC META TRANSLATIONS */
|
||||||
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
export const META_TRANSLATIONS: Record<string, string> = {
|
||||||
|
durability: 'Прочность',
|
||||||
|
max_durability: 'Максимальная прочность',
|
||||||
|
custom_model_data: 'Кастомная модель',
|
||||||
|
unbreakable: 'Неразрушимый',
|
||||||
|
repair_cost: 'Стоимость починки',
|
||||||
|
hide_flags: 'Скрытые флаги',
|
||||||
|
rarity: 'Редкость',
|
||||||
|
damage: 'Урон',
|
||||||
|
attack_speed: 'Скорость атаки',
|
||||||
|
armor: 'Броня',
|
||||||
|
armor_toughness: 'Твёрдость брони',
|
||||||
|
knockback_resistance: 'Сопротивление отталкиванию',
|
||||||
|
glowing: 'Подсветка',
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
/* FORMATTERS */
|
||||||
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
export function translateEnchant(key: string): string {
|
||||||
|
return ENCHANT_TRANSLATIONS[key.toLowerCase()] ?? beautifyKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function translateMetaKey(key: string): string {
|
||||||
|
return META_TRANSLATIONS[key.toLowerCase()] ?? beautifyKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function beautifyKey(key: string): string {
|
||||||
|
return key
|
||||||
|
.replace(/_/g, ' ')
|
||||||
|
.replace(/\b\w/g, (l) => l.toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
/* VALUE FORMATTERS */
|
||||||
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
export function formatMetaValue(value: any): string {
|
||||||
|
if (typeof value === 'boolean') return value ? 'Да' : 'Нет';
|
||||||
|
if (typeof value === 'number') return String(value);
|
||||||
|
if (typeof value === 'string') return value;
|
||||||
|
|
||||||
|
if (Array.isArray(value)) return value.join(', ');
|
||||||
|
if (typeof value === 'object') return 'Сложное значение';
|
||||||
|
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------- */
|
||||||
|
/* ENCHANT LIST HELPER */
|
||||||
|
/* ----------------------------- */
|
||||||
|
|
||||||
|
export function formatEnchants(
|
||||||
|
enchants?: Record<string, number>,
|
||||||
|
): { label: string; level: number }[] {
|
||||||
|
if (!enchants || typeof enchants !== 'object') return [];
|
||||||
|
|
||||||
|
return Object.entries(enchants).map(([key, level]) => ({
|
||||||
|
label: translateEnchant(key),
|
||||||
|
level,
|
||||||
|
}));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user