Merge branch 'main' of https://github.com/Korvarix/Galaxy-Strike-Online
This commit is contained in:
commit
e3213dc045
@ -58,90 +58,92 @@ const DatapackTab = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tab-content active datapack-tab-wrapper">
|
<div className="tab-content active datapack-tab-wrapper">
|
||||||
<div className="datapack-controls">
|
<MeteorRegion>
|
||||||
<div className="section-selector">
|
<div className="datapack-controls">
|
||||||
{sections.map((s) => (
|
<div className="section-selector">
|
||||||
<button
|
{sections.map((s) => (
|
||||||
key={s.id}
|
<button
|
||||||
className={`section-btn ${activeSection === s.id ? "active" : ""}`}
|
key={s.id}
|
||||||
onClick={() => setActiveSection(s.id)}
|
className={`section-btn ${activeSection === s.id ? "active" : ""}`}
|
||||||
>
|
onClick={() => setActiveSection(s.id)}
|
||||||
<i className={`fas ${s.icon}`}></i>
|
>
|
||||||
<span>{s.label}</span>
|
<i className={`fas ${s.icon}`}></i>
|
||||||
</button>
|
<span>{s.label}</span>
|
||||||
))}
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="search-bar">
|
||||||
|
<i className="fas fa-search"></i>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search ID or Name..."
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="search-bar">
|
<div className="datapack-content">
|
||||||
<i className="fas fa-search"></i>
|
<div className="datapack-grid">
|
||||||
<input
|
{displayList.map((item) => (
|
||||||
type="text"
|
<div
|
||||||
placeholder="Search ID or Name..."
|
key={item.id}
|
||||||
value={searchQuery}
|
className={`datapack-card ${activeSection === "hostiles" ? "hostile-card" : ""}`}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onClick={() =>
|
||||||
/>
|
setSelectedItem({ ...item, sectionType: activeSection })
|
||||||
</div>
|
}
|
||||||
</div>
|
>
|
||||||
|
<div className="card-icon">
|
||||||
<MeteorRegion className="datapack-content">
|
{item.texture ? (
|
||||||
<div className="datapack-grid">
|
<img
|
||||||
{displayList.map((item) => (
|
src={`${config.serverUrl}/static/${item.texture}`}
|
||||||
<div
|
alt=""
|
||||||
key={item.id}
|
/>
|
||||||
className={`datapack-card ${activeSection === "hostiles" ? "hostile-card" : ""}`}
|
) : (
|
||||||
onClick={() =>
|
<div className="fallback-icon">
|
||||||
setSelectedItem({ ...item, sectionType: activeSection })
|
{item.displayName?.[0] || "?"}
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="card-icon">
|
|
||||||
{item.texture ? (
|
|
||||||
<img
|
|
||||||
src={`${config.serverUrl}/static/${item.texture}`}
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div className="fallback-icon">
|
|
||||||
{item.displayName?.[0] || "?"}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="card-info">
|
|
||||||
<span className="card-name">{item.displayName}</span>
|
|
||||||
<span className="card-id">{item.id}</span>
|
|
||||||
|
|
||||||
{/* Секція луту без картинок, лише текст або іконка коробки */}
|
|
||||||
{activeSection === "hostiles" &&
|
|
||||||
item.loot &&
|
|
||||||
item.loot.length > 0 && (
|
|
||||||
<div className="card-loot-preview">
|
|
||||||
{item.loot.map((lootEntry, idx) => {
|
|
||||||
const lootData = GameDataManager.getItem(lootEntry.id);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={`${item.id}-loot-${idx}`}
|
|
||||||
className="loot-mini-slot-text"
|
|
||||||
title={`${lootData?.displayName || lootEntry.id} (${(lootEntry.chance * 100).toFixed(0)}%)`}
|
|
||||||
>
|
|
||||||
<i
|
|
||||||
className="fas fa-box-open"
|
|
||||||
style={{ fontSize: "10px", marginRight: "4px" }}
|
|
||||||
></i>
|
|
||||||
<span>
|
|
||||||
{lootData?.displayName ||
|
|
||||||
lootEntry.id.split(":").pop()}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card-info">
|
||||||
|
<span className="card-name">{item.displayName}</span>
|
||||||
|
<span className="card-id">{item.id}</span>
|
||||||
|
|
||||||
|
{activeSection === "hostiles" &&
|
||||||
|
item.loot &&
|
||||||
|
item.loot.length > 0 && (
|
||||||
|
<div className="card-loot-preview">
|
||||||
|
{item.loot.map((lootEntry, idx) => {
|
||||||
|
const lootData = GameDataManager.getItem(
|
||||||
|
lootEntry.id,
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`${item.id}-loot-${idx}`}
|
||||||
|
className="loot-mini-slot-text"
|
||||||
|
title={`${lootData?.displayName || lootEntry.id} (${(lootEntry.chance * 100).toFixed(0)}%)`}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fas fa-box-open"
|
||||||
|
style={{ fontSize: "10px", marginRight: "4px" }}
|
||||||
|
></i>
|
||||||
|
<span>
|
||||||
|
{lootData?.displayName ||
|
||||||
|
lootEntry.id.split(":").pop()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MeteorRegion>
|
</MeteorRegion>
|
||||||
|
|
||||||
{selectedItem && (
|
{selectedItem && (
|
||||||
<DatapackDetailsModal
|
<DatapackDetailsModal
|
||||||
data={selectedItem}
|
data={selectedItem}
|
||||||
|
|||||||
@ -18,24 +18,23 @@
|
|||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
max-width: 450px;
|
max-width: 450px;
|
||||||
padding: 24px;
|
|
||||||
box-shadow: 0 0 30px rgba(0, 204, 255, 0.15);
|
box-shadow: 0 0 30px rgba(0, 204, 255, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-headerr {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
padding-bottom: 15px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header h3 {
|
.modal-header {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: #00ccff;
|
color: #00ccff;
|
||||||
font-family: "Orbitron", sans-serif;
|
font-family: "Orbitron", sans-serif;
|
||||||
font-size: 1.1rem;
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-x {
|
.close-x {
|
||||||
@ -125,7 +124,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--primary-color);
|
background: var(--primary-color);
|
||||||
box-shadow: 0 0 10px var(--primary-color);
|
box-shadow: 0 0 10px var(--primary-color);
|
||||||
transition: width 1s linear; /* Плавне заповнення */
|
transition: width 1s linear;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
@ -140,7 +139,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Стани інгредієнтів */
|
|
||||||
.res-item {
|
.res-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -161,7 +159,6 @@
|
|||||||
background: rgba(255, 68, 68, 0.1);
|
background: rgba(255, 68, 68, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Кольори значень */
|
|
||||||
.val-red {
|
.val-red {
|
||||||
color: #ff4444;
|
color: #ff4444;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@ -181,7 +178,6 @@
|
|||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Кнопка */
|
|
||||||
.btn-start-craft {
|
.btn-start-craft {
|
||||||
background: #28a745;
|
background: #28a745;
|
||||||
color: white;
|
color: white;
|
||||||
@ -202,3 +198,60 @@
|
|||||||
background: #218838;
|
background: #218838;
|
||||||
box-shadow: 0 0 10px rgba(40, 167, 69, 0.4);
|
box-shadow: 0 0 10px rgba(40, 167, 69, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.item-preview-header {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 15px;
|
||||||
|
background: rgba(0, 212, 255, 0.05);
|
||||||
|
border: 1px solid rgba(0, 212, 255, 0.1);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-icon-container {
|
||||||
|
position: relative;
|
||||||
|
width: 90px;
|
||||||
|
height: 90px;
|
||||||
|
background: #000;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-display-icon {
|
||||||
|
max-width: 80%;
|
||||||
|
max-height: 80%;
|
||||||
|
object-fit: contain;
|
||||||
|
filter: drop-shadow(0 0 5px var(--primary-color));
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-qty-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: -8px;
|
||||||
|
right: -8px;
|
||||||
|
background: var(--primary-color);
|
||||||
|
color: #000;
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-type-tag {
|
||||||
|
display: block;
|
||||||
|
font-size: 10px;
|
||||||
|
color: var(--primary-color);
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-description {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #ccc;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import "./CraftModal.css";
|
import "./CraftModal.css";
|
||||||
|
import { getServerUrl } from "../../../../config/api";
|
||||||
|
|
||||||
const CraftModal = ({
|
const CraftModal = ({
|
||||||
recipe,
|
recipe,
|
||||||
@ -10,6 +11,15 @@ const CraftModal = ({
|
|||||||
}) => {
|
}) => {
|
||||||
if (!recipe) return null;
|
if (!recipe) return null;
|
||||||
|
|
||||||
|
const CONNECT_URL = getServerUrl();
|
||||||
|
const ASSET_BASE_URL = `${CONNECT_URL}/static/`;
|
||||||
|
|
||||||
|
const getFullTextureUrl = (path) => {
|
||||||
|
if (!path) return "/assets/no-image.png";
|
||||||
|
if (path.startsWith("http")) return path;
|
||||||
|
return `${ASSET_BASE_URL}${path}`;
|
||||||
|
};
|
||||||
|
|
||||||
const isBusy = !!activeCraft;
|
const isBusy = !!activeCraft;
|
||||||
const outputQty = Object.values(recipe.output || {})[0] || 1;
|
const outputQty = Object.values(recipe.output || {})[0] || 1;
|
||||||
const canAfford = recipe.ingredients?.every(
|
const canAfford = recipe.ingredients?.every(
|
||||||
@ -19,7 +29,7 @@ const CraftModal = ({
|
|||||||
return (
|
return (
|
||||||
<div className="craft-modal-overlay" onClick={onClose}>
|
<div className="craft-modal-overlay" onClick={onClose}>
|
||||||
<div className="craft-modal" onClick={(e) => e.stopPropagation()}>
|
<div className="craft-modal" onClick={(e) => e.stopPropagation()}>
|
||||||
<div className="modal-header">
|
<div className="modal-headerr">
|
||||||
<h3>
|
<h3>
|
||||||
<i className="fas fa-tools"></i> Construction: {recipe.displayName}
|
<i className="fas fa-tools"></i> Construction: {recipe.displayName}
|
||||||
</h3>
|
</h3>
|
||||||
@ -29,6 +39,25 @@ const CraftModal = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="modal-body">
|
<div className="modal-body">
|
||||||
|
{/* Секція з картинкою предмета */}
|
||||||
|
<div className="item-preview-header">
|
||||||
|
<div className="item-icon-container">
|
||||||
|
<img
|
||||||
|
src={getFullTextureUrl(recipe.texture)}
|
||||||
|
alt={recipe.displayName}
|
||||||
|
className="item-display-icon"
|
||||||
|
/>
|
||||||
|
<div className="item-qty-badge">x{outputQty}</div>
|
||||||
|
</div>
|
||||||
|
<div className="item-header-info">
|
||||||
|
<span className="item-type-tag">PROTOTYPE_UNIT</span>
|
||||||
|
<p className="item-description">
|
||||||
|
{recipe.description ||
|
||||||
|
"Technical data encrypted or unavailable."}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="requirements-section">
|
<div className="requirements-section">
|
||||||
<h4>
|
<h4>
|
||||||
<i className="fas fa-list-ul"></i> Required Resources
|
<i className="fas fa-list-ul"></i> Required Resources
|
||||||
|
|||||||
@ -10,10 +10,11 @@
|
|||||||
animation: modalSlideUp 0.3s ease-out;
|
animation: modalSlideUp 0.3s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-headerr {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
justify-content: left;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding-bottom: 15px;
|
padding-bottom: 15px;
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
|||||||
@ -75,7 +75,7 @@ const DatapackDetailsModal = ({ data, onClose }) => {
|
|||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="modal-header">
|
<div className="modal-headerr">
|
||||||
<div className="modal-icon-big">
|
<div className="modal-icon-big">
|
||||||
{data.texture ? (
|
{data.texture ? (
|
||||||
<img src={`${config.serverUrl}/static/${data.texture}`} alt="" />
|
<img src={`${config.serverUrl}/static/${data.texture}`} alt="" />
|
||||||
|
|||||||
@ -12,133 +12,184 @@
|
|||||||
backdrop-filter: blur(4px);
|
backdrop-filter: blur(4px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-content {
|
.datapack-modal-content {
|
||||||
background: #12151a;
|
background: #0f1115;
|
||||||
border: 1px solid #00d2ff;
|
border: 1px solid rgba(0, 210, 255, 0.3);
|
||||||
border-radius: 8px;
|
width: 90%;
|
||||||
width: 100%;
|
max-width: 450px;
|
||||||
max-width: 400px;
|
border-radius: 12px;
|
||||||
padding: 25px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
box-shadow: 0 0 30px rgba(0, 210, 255, 0.2);
|
padding: 25px;
|
||||||
color: #fff;
|
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.8);
|
||||||
font-family: "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
animation: modalSlideUp 0.3s ease-out;
|
||||||
}
|
|
||||||
|
|
||||||
.modal-close {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 15px;
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: #888;
|
|
||||||
font-size: 28px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: color 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-close:hover {
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-header {
|
.modal-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 15px;
|
gap: 20px;
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
margin-bottom: 20px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 15px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-name {
|
.modal-icon-big {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-icon-big img {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-icon-big.common {
|
||||||
|
border-color: #888;
|
||||||
|
}
|
||||||
|
.modal-icon-big.rare {
|
||||||
|
border-color: #0070dd;
|
||||||
|
box-shadow: inset 0 0 10px rgba(0, 112, 221, 0.2);
|
||||||
|
}
|
||||||
|
.modal-icon-big.epic {
|
||||||
|
border-color: #a335ee;
|
||||||
|
box-shadow: inset 0 0 10px rgba(163, 53, 238, 0.2);
|
||||||
|
}
|
||||||
|
.modal-icon-big.legendary {
|
||||||
|
border-color: #ff8000;
|
||||||
|
box-shadow: inset 0 0 10px rgba(255, 128, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title-group h3 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1.2rem;
|
font-family: "Orbitron", sans-serif;
|
||||||
|
font-size: 1.3rem;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 1px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.rarity-badge {
|
.modal-title-group h3.common {
|
||||||
font-size: 0.7rem;
|
color: #fff;
|
||||||
padding: 2px 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
}
|
||||||
|
.modal-title-group h3.rare {
|
||||||
.item-name.common {
|
color: #00d2ff;
|
||||||
color: #ffffff;
|
|
||||||
}
|
}
|
||||||
.item-name.uncommon {
|
.modal-title-group h3.epic {
|
||||||
color: #1eff00;
|
|
||||||
}
|
|
||||||
.item-name.rare {
|
|
||||||
color: #0070dd;
|
|
||||||
}
|
|
||||||
.item-name.epic {
|
|
||||||
color: #a335ee;
|
color: #a335ee;
|
||||||
}
|
}
|
||||||
.item-name.legendary {
|
.modal-title-group h3.legendary {
|
||||||
color: #ff8000;
|
color: #ff8000;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-description {
|
.modal-raw-id {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: #888;
|
||||||
|
margin-top: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-description {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.5;
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
line-height: 1.4;
|
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-section h4 {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #00d2ff;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-left: 3px solid #00d2ff;
|
||||||
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.item-stats-container {
|
.item-stats-container {
|
||||||
background: rgba(0, 0, 0, 0.3);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-row {
|
.stat-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 5px 0;
|
padding: 8px 0;
|
||||||
font-size: 0.85rem;
|
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-row:last-child {
|
||||||
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-label {
|
.stat-label {
|
||||||
color: #00d2ff;
|
color: #888;
|
||||||
|
font-size: 0.85rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-value {
|
.stat-value {
|
||||||
color: #fff;
|
color: #00ff88;
|
||||||
|
font-family: monospace;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-equip {
|
.btn-equip {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
background: none;
|
background: rgba(0, 210, 255, 0.05);
|
||||||
border: 1px solid #00d2ff;
|
border: 1px solid #00d2ff;
|
||||||
color: #00d2ff;
|
color: #00d2ff;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
text-transform: uppercase;
|
font-family: "Orbitron", sans-serif;
|
||||||
font-weight: bold;
|
font-size: 0.8rem;
|
||||||
letter-spacing: 1px;
|
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-equip:hover {
|
.btn-equip:hover {
|
||||||
background: #00d2ff;
|
background: #00d2ff;
|
||||||
color: #000;
|
color: #000;
|
||||||
box-shadow: 0 0 15px rgba(0, 210, 255, 0.4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-equip.unequip {
|
.btn-equip.unequip {
|
||||||
border-color: #ff4444;
|
border-color: #ff4444;
|
||||||
color: #ff4444;
|
color: #ff4444;
|
||||||
|
background: rgba(255, 68, 68, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-equip.unequip:hover {
|
.btn-equip.unequip:hover {
|
||||||
background: #ff4444;
|
background: #ff4444;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
right: 15px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #444;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modalSlideUp {
|
||||||
|
from {
|
||||||
|
transform: translateY(20px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import "./ItemModal.css";
|
import "./ItemModal.css";
|
||||||
|
import { getServerUrl } from "../../../../config/api";
|
||||||
|
|
||||||
const ItemModal = ({
|
const ItemModal = ({
|
||||||
item,
|
item,
|
||||||
@ -12,23 +13,45 @@ const ItemModal = ({
|
|||||||
}) => {
|
}) => {
|
||||||
if (!item) return null;
|
if (!item) return null;
|
||||||
|
|
||||||
|
const CONNECT_URL = getServerUrl();
|
||||||
|
const ASSET_BASE_URL = `${CONNECT_URL}/static/`;
|
||||||
|
|
||||||
|
const getFullTextureUrl = (path) => {
|
||||||
|
if (!path) return "/assets/no-image.png";
|
||||||
|
if (path.startsWith("http")) return path;
|
||||||
|
return `${ASSET_BASE_URL}${path}`;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="modal-overlay" onClick={onClose}>
|
<div className="modal-overlay" onClick={onClose}>
|
||||||
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
|
<div
|
||||||
|
className="datapack-modal-content"
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
<button className="modal-close" onClick={onClose}>
|
<button className="modal-close" onClick={onClose}>
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="details-view">
|
<div className="modal-header">
|
||||||
<div className="details-header">
|
<div className={`modal-icon-big ${item.rarity}`}>
|
||||||
<h4 className={`item-name ${item.rarity}`}>
|
<img src={getFullTextureUrl(item.texture)} alt={item.displayName} />
|
||||||
{item.displayName || item.name}
|
|
||||||
</h4>
|
|
||||||
<span className={`rarity-badge ${item.rarity}`}>{item.rarity}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="modal-title-group">
|
||||||
|
<h3 className={item.rarity}>{item.displayName || item.name}</h3>
|
||||||
|
<div className="modal-raw-id">
|
||||||
|
{item.rarity?.toUpperCase()} SYSTEM_ID: {item.id}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p className="item-description">{item.description}</p>
|
<div className="details-section">
|
||||||
|
<p className="details-description">{item.description}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="details-section">
|
||||||
|
<h4>
|
||||||
|
<i className="fas fa-microchip"></i> Technical Specs
|
||||||
|
</h4>
|
||||||
<div className="item-stats-container">
|
<div className="item-stats-container">
|
||||||
{item.stats &&
|
{item.stats &&
|
||||||
Object.entries(item.stats).map(([statName, value]) => (
|
Object.entries(item.stats).map(([statName, value]) => (
|
||||||
@ -43,7 +66,9 @@ const ItemModal = ({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="modal-actions" style={{ marginTop: "20px" }}>
|
||||||
{isEquipped ? (
|
{isEquipped ? (
|
||||||
<button
|
<button
|
||||||
className="btn-equip unequip"
|
className="btn-equip unequip"
|
||||||
@ -52,7 +77,7 @@ const ItemModal = ({
|
|||||||
onClose();
|
onClose();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
DISCONNECT_SYSTEM
|
TERMINATE_CONNECTION
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
item.canEquip && (
|
item.canEquip && (
|
||||||
|
|||||||
@ -194,6 +194,9 @@
|
|||||||
.message {
|
.message {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
|
min-height: min-content;
|
||||||
|
padding: 4px 0;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.msg-time {
|
.msg-time {
|
||||||
@ -214,7 +217,12 @@
|
|||||||
|
|
||||||
.message.system .msg-text {
|
.message.system .msg-text {
|
||||||
color: #888;
|
color: #888;
|
||||||
|
word-break: break-all;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
display: inline-block;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-input-area {
|
.chat-input-area {
|
||||||
@ -411,3 +419,29 @@
|
|||||||
color: #ff3e3e;
|
color: #ff3e3e;
|
||||||
text-shadow: 0 0 8px rgba(255, 62, 62, 0.4);
|
text-shadow: 0 0 8px rgba(255, 62, 62, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.4;
|
||||||
|
min-height: min-content;
|
||||||
|
padding: 4px 0;
|
||||||
|
display: block;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg-text {
|
||||||
|
color: #fff;
|
||||||
|
word-break: break-all;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.system .msg-author {
|
||||||
|
color: #ff3e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message.system .msg-text {
|
||||||
|
color: #888;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user