diff --git a/README.md b/README.md
index d4a9fbc..5aa3139 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# CaseForge
+# CaseOrion
CS-style case opening site with virtual credits, inventory, multiplayer item betting, shop, leaderboard, and an admin panel for cases / drops / rates.
diff --git a/client/index.html b/client/index.html
index 041d80d..26843be 100644
--- a/client/index.html
+++ b/client/index.html
@@ -12,7 +12,7 @@
href="https://fonts.googleapis.com/css2?family=Chakra+Petch:wght@500;600;700&family=IBM+Plex+Sans:wght@400;500;600&display=swap"
rel="stylesheet"
/>
-
CaseForge
+ CaseOrion
diff --git a/client/src/admin/AdminCategories.jsx b/client/src/admin/AdminCategories.jsx
new file mode 100644
index 0000000..c9fba4a
--- /dev/null
+++ b/client/src/admin/AdminCategories.jsx
@@ -0,0 +1,192 @@
+import { useCallback, useEffect, useMemo, useState } from 'react';
+import { api } from '../api';
+
+export default function AdminCategories() {
+ const [categories, setCategories] = useState([]);
+ const [error, setError] = useState('');
+ const [message, setMessage] = useState('');
+ const [query, setQuery] = useState('');
+ const [name, setName] = useState('');
+ const [editingId, setEditingId] = useState(null);
+ const [editName, setEditName] = useState('');
+ const [busy, setBusy] = useState(false);
+
+ const load = useCallback(async () => {
+ const data = await api.adminCategories();
+ setCategories(data.categories || []);
+ }, []);
+
+ useEffect(() => {
+ load().catch((err) => setError(err.message));
+ }, [load]);
+
+ const filtered = useMemo(() => {
+ const q = query.trim().toLowerCase();
+ if (!q) return categories;
+ return categories.filter((c) => c.name.toLowerCase().includes(q));
+ }, [categories, query]);
+
+ return (
+
+
+
+
Categories
+
+ {categories.length} categories · rename updates items · delete only when empty
+
+
+
setQuery(e.target.value)}
+ />
+
+
+ {error &&
{error}
}
+ {message &&
{message}
}
+
+
+
Create category
+
+
+
+
+
All categories
+ {filtered.length === 0 ? (
+
No categories match.
+ ) : (
+
+ {filtered.map((cat) => (
+ -
+ {editingId === cat.id ? (
+
+ ) : (
+ <>
+
+ {cat.name}
+
+ {cat.itemCount} item{cat.itemCount === 1 ? '' : 's'}
+
+
+
+
+
+
+ >
+ )}
+
+ ))}
+
+ )}
+
+
+ );
+}
diff --git a/client/src/admin/AdminDashboard.jsx b/client/src/admin/AdminDashboard.jsx
index 261f72f..27b1e90 100644
--- a/client/src/admin/AdminDashboard.jsx
+++ b/client/src/admin/AdminDashboard.jsx
@@ -31,7 +31,7 @@ export default function AdminDashboard() {
Overview
-
Live snapshot of CaseForge.
+
Live snapshot of CaseOrion.
diff --git a/client/src/admin/AdminItems.jsx b/client/src/admin/AdminItems.jsx
index e333421..97d8ccb 100644
--- a/client/src/admin/AdminItems.jsx
+++ b/client/src/admin/AdminItems.jsx
@@ -51,7 +51,11 @@ export default function AdminItems() {
]);
setItems(i.items);
setRarities(r.rarities);
- setCategories(c.categories || []);
+ setCategories(
+ (c.names || c.categories || []).map((entry) =>
+ typeof entry === 'string' ? entry : entry.name
+ )
+ );
setItemForm((f) => ({
...f,
rarity: f.rarity || r.rarities[0] || 'MilSpec',
diff --git a/client/src/admin/AdminLayout.jsx b/client/src/admin/AdminLayout.jsx
index 6609d43..e97a7da 100644
--- a/client/src/admin/AdminLayout.jsx
+++ b/client/src/admin/AdminLayout.jsx
@@ -6,6 +6,7 @@ const NAV = [
{ to: '/admin/players', label: 'Players' },
{ to: '/admin/cases', label: 'Cases' },
{ to: '/admin/items', label: 'Items' },
+ { to: '/admin/categories', label: 'Categories' },
{ to: '/admin/drops', label: 'Drops' },
];
@@ -17,7 +18,7 @@ export default function AdminLayout() {