diff --git a/web/client/src/components/CraftingPanel.jsx b/web/client/src/components/CraftingPanel.jsx
index 2b40a4f..6a14f26 100644
--- a/web/client/src/components/CraftingPanel.jsx
+++ b/web/client/src/components/CraftingPanel.jsx
@@ -35,9 +35,9 @@ function CraftingPanel() {
{(craftable || []).map((recipe, idx) => (
-
+
- {formatItemName(recipe.output)}
+ {formatItemName(recipe.output || '')}
Produces {recipe.count || 1}
@@ -45,8 +45,8 @@ function CraftingPanel() {
{recipe.slots && Object.entries(recipe.slots).map(([slot, item]) => (
-
- {formatItemName(item)}
+
+ {formatItemName(item || '')}
))}
diff --git a/web/client/src/components/ErrorBoundary.jsx b/web/client/src/components/ErrorBoundary.jsx
new file mode 100644
index 0000000..fadd3ef
--- /dev/null
+++ b/web/client/src/components/ErrorBoundary.jsx
@@ -0,0 +1,54 @@
+import React from 'react';
+
+class ErrorBoundary extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { hasError: false, error: null };
+ }
+
+ static getDerivedStateFromError(error) {
+ return { hasError: true, error };
+ }
+
+ componentDidCatch(error, errorInfo) {
+ console.error('ErrorBoundary caught:', error, errorInfo);
+ }
+
+ render() {
+ if (this.state.hasError) {
+ return (
+
+
⚠️ Something went wrong
+
+ {this.state.error?.message || 'Unknown error'}
+
+
+
+ );
+ }
+
+ return this.props.children;
+ }
+}
+
+export default ErrorBoundary;
diff --git a/web/client/src/components/InventoryGrid.jsx b/web/client/src/components/InventoryGrid.jsx
index ee66c43..3268a0f 100644
--- a/web/client/src/components/InventoryGrid.jsx
+++ b/web/client/src/components/InventoryGrid.jsx
@@ -97,10 +97,10 @@ function InventoryGrid() {
{selectedItem && (
-
+
-
{formatItemName(selectedItem.name)}
- {selectedItem.name}
+ {formatItemName(selectedItem.name || '')}
+ {selectedItem.name || ''}
diff --git a/web/client/src/components/SmeltingPanel.jsx b/web/client/src/components/SmeltingPanel.jsx
index 4651a26..3fa3547 100644
--- a/web/client/src/components/SmeltingPanel.jsx
+++ b/web/client/src/components/SmeltingPanel.jsx
@@ -17,7 +17,7 @@ function SmeltingPanel() {
const furnaceStatus = inventory.furnaceStatus || {};
const furnaceEntries = Object.entries(furnaceStatus);
- const smeltableEntries = Object.entries(smeltable);
+ const smeltableEntries = Object.entries(smeltable || {});
return (
@@ -44,31 +44,31 @@ function SmeltingPanel() {
{furnaceEntries.length > 0 ? (
{furnaceEntries.map(([name, status]) => (
-
-
{name.replace(/^minecraft:/, '')}
+
+
{(name || '').replace(/^minecraft:/, '')}
- {status.input && (
+ {status && status.input && (
In:
{status.input.count || 0}
)}
- {status.fuel && (
+ {status && status.fuel && (
Fuel:
{status.fuel.count || 0}
)}
- {status.output && (
+ {status && status.output && (
Out:
{status.output.count || 0}
)}
- {!status.input && !status.fuel && !status.output && (
+ {(!status || (!status.input && !status.fuel && !status.output)) && (
Empty
)}
@@ -90,6 +90,8 @@ function SmeltingPanel() {
{smeltableEntries.map(([input, recipe]) => {
const isDisabled = disabledRecipes[input];
+ const result = (recipe && recipe.result) || '';
+ const furnaces = (recipe && recipe.furnaces) || [];
return (
→
-
- {formatItemName(recipe.result)}
+
+ {formatItemName(result)}
- {(recipe.furnaces || []).map((f) => (
+ {furnaces.map((f) => (
- {f.replace(/^minecraft:/, '')}
+ {(f || '').replace(/^minecraft:/, '')}
))}