Add inventory store with WebSocket and REST API integration

This commit is contained in:
MayaTheShy
2026-03-21 16:43:31 -04:00
parent a06b5f5ce2
commit 7e95cf64c6

View File

@@ -0,0 +1,210 @@
import { create } from 'zustand';
const WS_URL = import.meta.env.VITE_WS_URL ||
`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`;
const API_URL = import.meta.env.VITE_API_URL ||
`${window.location.protocol}//${window.location.host}/api`;
console.log('🔌 WebSocket URL:', WS_URL);
console.log('📡 API URL:', API_URL);
export const useInventoryStore = create((set, get) => ({
// State
inventory: {
itemList: [],
grandTotal: 0,
chestCount: 0,
totalSlots: 0,
usedSlots: 0,
freeSlots: 0,
usedRatio: 0,
dropperOk: false,
barrelOk: false,
furnaceCount: 0,
furnaceStatus: {},
},
activity: {},
alerts: [],
smeltingPaused: false,
disabledRecipes: {},
smeltable: {},
craftable: [],
craftTurtleOk: false,
connected: false,
ws: null,
lastUpdate: 0,
searchQuery: '',
commandResult: null,
// WebSocket connection
connect: () => {
const ws = new WebSocket(WS_URL);
ws.onopen = () => {
console.log('✅ Connected to server');
set({ connected: true, ws });
};
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data.type === 'initial_state') {
set({
inventory: data.inventory || get().inventory,
activity: data.activity || {},
alerts: data.alerts || [],
smeltingPaused: data.smeltingPaused || false,
disabledRecipes: data.disabledRecipes || {},
smeltable: data.smeltable || {},
craftable: data.craftable || [],
craftTurtleOk: data.craftTurtleOk || false,
lastUpdate: data.lastUpdate || Date.now(),
});
} else if (data.type === 'state_update') {
set({
inventory: data.inventory || get().inventory,
activity: data.activity || get().activity,
alerts: data.alerts || get().alerts,
smeltingPaused: data.smeltingPaused !== undefined ? data.smeltingPaused : get().smeltingPaused,
disabledRecipes: data.disabledRecipes || get().disabledRecipes,
craftTurtleOk: data.craftTurtleOk !== undefined ? data.craftTurtleOk : get().craftTurtleOk,
lastUpdate: data.lastUpdate || Date.now(),
});
} else if (data.type === 'command_result') {
set({ commandResult: data });
// Clear after 5s
setTimeout(() => {
set((state) => {
if (state.commandResult === data) {
return { commandResult: null };
}
return {};
});
}, 5000);
}
} catch (error) {
console.error('Error processing message:', error);
}
};
ws.onclose = () => {
console.log('❌ Disconnected from server');
set({ connected: false, ws: null });
setTimeout(() => {
get().connect();
}, 3000);
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
},
// Search
setSearchQuery: (query) => set({ searchQuery: query }),
getFilteredItems: () => {
const { inventory, searchQuery } = get();
const items = inventory.itemList || [];
if (!searchQuery) return items;
const q = searchQuery.toLowerCase();
return items.filter((item) => {
const name = (item.displayName || item.name || '').toLowerCase();
return name.includes(q);
});
},
// Actions via REST API
orderItem: async (itemName, amount, dropperName) => {
try {
const response = await fetch(`${API_URL}/order`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ itemName, amount, dropperName }),
});
return await response.json();
} catch (error) {
console.error('❌ Error ordering item:', error);
return { success: false, error: error.message };
}
},
requestScan: async () => {
try {
const response = await fetch(`${API_URL}/scan`, { method: 'POST' });
return await response.json();
} catch (error) {
console.error('❌ Error requesting scan:', error);
return { success: false, error: error.message };
}
},
toggleSmelting: async () => {
try {
const response = await fetch(`${API_URL}/smelting/toggle`, { method: 'POST' });
return await response.json();
} catch (error) {
console.error('❌ Error toggling smelting:', error);
return { success: false, error: error.message };
}
},
toggleRecipe: async (recipe) => {
try {
const response = await fetch(`${API_URL}/recipes/toggle`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ recipe }),
});
return await response.json();
} catch (error) {
console.error('❌ Error toggling recipe:', error);
return { success: false, error: error.message };
}
},
enableAllRecipes: async () => {
try {
await fetch(`${API_URL}/recipes/enable-all`, { method: 'POST' });
} catch (error) {
console.error('❌ Error enabling all recipes:', error);
}
},
disableAllRecipes: async () => {
try {
await fetch(`${API_URL}/recipes/disable-all`, { method: 'POST' });
} catch (error) {
console.error('❌ Error disabling all recipes:', error);
}
},
craftItem: async (recipeIdx) => {
try {
const response = await fetch(`${API_URL}/craft`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ recipeIdx }),
});
return await response.json();
} catch (error) {
console.error('❌ Error crafting item:', error);
return { success: false, error: error.message };
}
},
sortBarrel: async (barrelName) => {
try {
const response = await fetch(`${API_URL}/sort-barrel`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ barrelName }),
});
return await response.json();
} catch (error) {
console.error('❌ Error sorting barrel:', error);
return { success: false, error: error.message };
}
},
}));