diff --git a/client/src/views/GameInterface/tabs/ItemListTab.jsx b/client/src/views/GameInterface/tabs/ItemListTab.jsx
index b7d5883..e1dc69f 100644
--- a/client/src/views/GameInterface/tabs/ItemListTab.jsx
+++ b/client/src/views/GameInterface/tabs/ItemListTab.jsx
@@ -8,26 +8,30 @@ const ItemListTab = () => {
const [searchQuery, setSearchQuery] = useState("");
const [selectedCategory, setSelectedCategory] = useState("all");
const [selectedItem, setSelectedItem] = useState(null);
+ const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
const ASSET_BASE_URL = "http://localhost:5003/static/";
useEffect(() => {
+ const handleResize = () => setIsMobile(window.innerWidth <= 768);
+ window.addEventListener("resize", handleResize);
+
const itemsArray = Array.from(GameDataManager.items.keys()).map((id) =>
GameDataManager.getItem(id),
);
setAllItems(itemsArray);
setFilteredItems(itemsArray);
+
+ return () => window.removeEventListener("resize", handleResize);
}, []);
useEffect(() => {
let result = allItems;
-
if (selectedCategory !== "all") {
result = result.filter(
(item) => item.meta?.category === selectedCategory,
);
}
-
if (searchQuery) {
const q = searchQuery.toLowerCase();
result = result.filter(
@@ -36,7 +40,6 @@ const ItemListTab = () => {
item.id.toLowerCase().includes(q),
);
}
-
setFilteredItems(result);
}, [searchQuery, selectedCategory, allItems]);
@@ -50,19 +53,72 @@ const ItemListTab = () => {
...new Set(allItems.map((i) => i.meta?.category).filter(Boolean)),
];
+ const renderInspector = () => (
+
isMobile && setSelectedItem(null)}
+ >
+ {selectedItem && (
+
e.stopPropagation()}>
+ {isMobile && (
+
+ )}
+
+
+
+
})
+
+
+
{selectedItem.id}
+
{selectedItem.displayName}
+
+ {selectedItem.meta?.rarity?.toUpperCase()}
+
+
+
+
+
+
+
DATA_DESCRIPTION
+
{selectedItem.description}
+
+
+ {selectedItem.stats &&
+ Object.keys(selectedItem.stats).length > 0 && (
+
+
PARAMETER_READOUT
+
+ {Object.entries(selectedItem.stats).map(([k, v]) => (
+
+ {GameDataManager.getStatName(k)}
+ +{v}
+
+ ))}
+
+
+ )}
+
+
+ )}
+
+ );
+
return (
-
setSearchQuery(e.target.value)}
/>
-
{categories.map((cat) => (
))}
-
{filteredItems.map((item) => (
{
{item.displayName}
{item.id}
-
))}
- {filteredItems.length === 0 && (
-
NO_RECORDS_FOUND
- )}
-
-
- {selectedItem ? (
-
-
-
-
})
-
-
-
{selectedItem.id}
-
{selectedItem.displayName}
-
- {selectedItem.meta?.rarity?.toUpperCase()}
-
-
-
-
-
-
-
DATA_DESCRIPTION
-
{selectedItem.description}
-
-
- {selectedItem.stats &&
- Object.keys(selectedItem.stats).length > 0 && (
-
-
PARAMETER_READOUT
-
- {Object.entries(selectedItem.stats).map(([k, v]) => (
-
-
- {GameDataManager.getStatName(k)}
-
- +{v}
-
- ))}
-
-
- )}
-
-
-
OBJECT_METADATA
-
-
- CATEGORY
- {selectedItem.meta?.category || "general"}
-
-
- EQUIP_SLOT
- {selectedItem.meta?.equipmentSlot || "none"}
-
-
- STACK_LIMIT
- {selectedItem.meta?.stackable ? "64" : "1"}
-
-
-
-
-
- ) : (
-
-
-
AWAITING_OBJECT_SELECTION...
-
- )}
-
+ {(!isMobile || (isMobile && selectedItem)) && renderInspector()}
);
};
diff --git a/client/src/views/GameInterface/tabs/styles/ItemListTab.css b/client/src/views/GameInterface/tabs/styles/ItemListTab.css
index 18133b4..4858264 100644
--- a/client/src/views/GameInterface/tabs/styles/ItemListTab.css
+++ b/client/src/views/GameInterface/tabs/styles/ItemListTab.css
@@ -6,9 +6,9 @@
font-family: "Rajdhani", "Segoe UI", sans-serif;
border-top: 1px solid rgba(0, 255, 255, 0.1);
overflow: hidden;
+ position: relative;
}
-/* SIDEBAR & SEARCH */
.item-list-sidebar {
width: 380px;
border-right: 1px solid rgba(0, 255, 255, 0.1);
@@ -19,7 +19,6 @@
.search-box {
padding: 20px;
- position: relative;
}
.search-box input {
@@ -31,16 +30,8 @@
text-transform: uppercase;
letter-spacing: 1px;
font-size: 14px;
- transition: all 0.3s;
}
-.search-box input:focus {
- outline: none;
- border-color: #00ffff;
- box-shadow: 0 0 10px rgba(0, 255, 255, 0.2);
-}
-
-/* CATEGORY FILTERS */
.category-filters {
display: flex;
flex-wrap: wrap;
@@ -55,17 +46,14 @@
padding: 4px 10px;
font-size: 11px;
cursor: pointer;
- transition: 0.2s;
}
-.filter-btn.active,
-.filter-btn:hover {
+.filter-btn.active {
background: rgba(0, 255, 255, 0.1);
color: #00ffff;
border-color: #00ffff;
}
-/* ITEM LIST ROWS */
.items-list-scroll {
flex: 1;
overflow-y: auto;
@@ -78,60 +66,108 @@
padding: 10px;
margin-bottom: 8px;
background: rgba(255, 255, 255, 0.02);
- border-left: 3px solid transparent;
+ border-left: 3px solid #9d9d9d;
cursor: pointer;
- transition: all 0.2s;
- position: relative;
-}
-
-.item-row:hover {
- background: rgba(255, 255, 255, 0.05);
- transform: translateX(5px);
}
.item-row.selected {
background: rgba(0, 255, 255, 0.08);
- border-left-color: #00ffff;
}
.item-row-icon {
width: 44px;
height: 44px;
background: rgba(0, 0, 0, 0.5);
- border: 1px solid rgba(255, 255, 255, 0.1);
+ margin-right: 15px;
display: flex;
align-items: center;
justify-content: center;
- margin-right: 15px;
}
.item-row-icon img {
width: 32px;
height: 32px;
- object-fit: contain;
- image-rendering: pixelated; /* Щоб іконки були чіткими */
+ image-rendering: pixelated;
}
-.item-row-content {
+/* Inspector Base */
+.item-inspector {
flex: 1;
+ padding: 40px;
+ overflow-y: auto;
+}
+
+.inspector-header {
display: flex;
- flex-direction: column;
+ gap: 25px;
+ margin-bottom: 30px;
}
-.item-row-title {
- font-size: 15px;
- font-weight: 600;
- color: #fff;
- text-transform: uppercase;
+.header-visual {
+ width: 100px;
+ height: 100px;
+ background: rgba(0, 0, 0, 0.4);
+ border: 1px solid rgba(0, 255, 255, 0.2);
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
-.item-row-subtitle {
- font-size: 11px;
- color: #666;
- font-family: "Courier New", monospace;
+.header-visual img {
+ width: 64px;
+ height: 64px;
}
-/* RARITY COLORS */
+/* Mobile Modal Logic */
+@media (max-width: 768px) {
+ .item-list-sidebar {
+ width: 100%;
+ border: none;
+ }
+
+ .item-inspector.mobile-modal {
+ display: flex;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 3000;
+ background: rgba(0, 0, 0, 0.85);
+ backdrop-filter: blur(6px);
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ }
+
+ .inspector-content {
+ background: #0d121d;
+ border: 1px solid #00ffff44;
+ width: 100%;
+ max-height: 120vh;
+ overflow-y: auto;
+ position: relative;
+ padding: 20px;
+ box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
+ }
+
+ .close-inspector {
+ position: absolute;
+ top: 10px;
+ right: 15px;
+ background: none;
+ border: none;
+ color: #00ffff;
+ font-size: 30px;
+ cursor: pointer;
+ }
+
+ .item-inspector:not(.mobile-modal) {
+ display: none;
+ }
+}
+
+/* Rarity */
.item-row.legendary {
border-left-color: #ff8000;
}
@@ -141,67 +177,11 @@
.item-row.rare {
border-left-color: #0070dd;
}
-.item-row.common {
- border-left-color: #9d9d9d;
-}
-/* INSPECTOR PANEL */
-.item-inspector {
- flex: 1;
- padding: 40px;
- background: radial-gradient(
- circle at top right,
- rgba(0, 255, 255, 0.03),
- transparent
- );
- overflow-y: auto;
-}
-
-.inspector-header {
- display: flex;
- gap: 30px;
- margin-bottom: 40px;
- align-items: center;
-}
-
-.header-visual {
- width: 120px;
- height: 120px;
- background: rgba(0, 0, 0, 0.3);
- border: 2px solid rgba(0, 255, 255, 0.2);
- display: flex;
- align-items: center;
- justify-content: center;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
-}
-
-.header-visual img {
- width: 80px;
- height: 80px;
-}
-
-.header-info h1 {
- font-size: 32px;
- margin: 5px 0;
- text-transform: uppercase;
- letter-spacing: 2px;
-}
-
-.id-tag {
- font-family: monospace;
- color: #00ffff;
- opacity: 0.6;
- font-size: 14px;
-}
-
-/* SCROLLBAR CUSTOMIZATION */
-.items-list-scroll::-webkit-scrollbar {
- width: 4px;
-}
-.items-list-scroll::-webkit-scrollbar-thumb {
- background: rgba(0, 255, 255, 0.2);
-}
-
-.section-title {
- font-size: 16px;
+@media screen and (max-width: 600px) {
+ .section-title {
+ font-size: 16px;
+ padding: 0;
+ margin: 10px;
+ }
}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..e212e69
--- /dev/null
+++ b/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "gso-project-manager",
+ "version": "1.0.0",
+ "scripts": {
+ "install-all": "cd api && npm install && cd ../game-server && npm install && cd ../client && npm install",
+ "dev": "npx concurrently \"npm run dev-api\" \"npm run dev-server\" \"npm run dev-client\"",
+ "dev-api": "cd api && npm run start",
+ "dev-server": "cd game-server && npm run start",
+ "dev-client": "cd client && npm run dev"
+ },
+ "dependencies": {
+ "concurrently": "^8.2.2"
+ }
+}