refactor: Decentralize data fetching and add axis initialization

Refactor data fetching architecture from centralized App state to
component-local data management for improved maintainability and
data freshness guarantees.

Changes:
- SettingsModal: Fetch config data on modal open
- RecipePanel: Fetch recipe list on panel open
- IOMonitorPage: Fetch IO list on page mount with real-time updates
- Remove unnecessary props drilling through component hierarchy
- Simplify App.tsx by removing centralized config/recipes state

New feature:
- Add InitializeModal for sequential axis initialization (X, Y, Z)
- Each axis initializes with 3-second staggered start
- Progress bar animation for each axis
- Auto-close on completion

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-25 01:35:32 +09:00
parent 27cc2507cf
commit 362263ab05
10 changed files with 631 additions and 201 deletions

View File

@@ -35,17 +35,13 @@ const INITIAL_IO: IOPoint[] = [
// --- MAIN APP ---
export default function App() {
const [activeTab, setActiveTab] = useState<'recipe' | 'motion' | 'camera' | 'setting' | null>(null);
const [activeIOTab, setActiveIOTab] = useState<'in' | 'out'>('in');
const [activeTab, setActiveTab] = useState<'recipe' | 'motion' | 'camera' | 'setting' | 'initialize' | null>(null);
const [systemState, setSystemState] = useState<SystemState>(SystemState.IDLE);
const [recipes, setRecipes] = useState<Recipe[]>([]);
const [currentRecipe, setCurrentRecipe] = useState<Recipe | null>(null);
const [currentRecipe, setCurrentRecipe] = useState<Recipe>({ id: '0', name: 'No Recipe', lastModified: '-' });
const [robotTarget, setRobotTarget] = useState<RobotTarget>({ x: 0, y: 0, z: 0 });
const [logs, setLogs] = useState<LogEntry[]>([]);
const [ioPoints, setIoPoints] = useState<IOPoint[]>([]);
const [currentTime, setCurrentTime] = useState(new Date());
const [config, setConfig] = useState<ConfigItem[] | null>(null);
const [isConfigRefreshing, setIsConfigRefreshing] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [isHostConnected, setIsHostConnected] = useState(false);
const videoRef = useRef<HTMLVideoElement>(null);
@@ -94,39 +90,20 @@ export default function App() {
useEffect(() => {
const initSystem = async () => {
addLog("SYSTEM STARTED", "info");
// Initial IO data will be loaded by HomePage when it mounts
try {
const ioStr = await comms.getIOList();
const ioData = JSON.parse(ioStr);
setIoPoints(ioData);
addLog("IO LIST LOADED", "info");
const recipeStr = await comms.getRecipeList();
const recipeData = JSON.parse(recipeStr);
setRecipes(recipeData);
if (recipeData.length > 0) setCurrentRecipe(recipeData[0]);
addLog("RECIPE LIST LOADED", "info");
} catch (e) {
addLog("FAILED TO LOAD SYSTEM DATA", "error");
// Fallback to empty or keep initial if needed
addLog("FAILED TO LOAD IO DATA", "error");
}
setIsLoading(false);
};
initSystem();
}, []);
// -- CONFIG FETCHING (for settings modal) --
const fetchConfig = React.useCallback(async () => {
setIsConfigRefreshing(true);
try {
const configStr = await comms.getConfig();
setConfig(JSON.parse(configStr));
addLog("CONFIG REFRESHED", "info");
} catch (e) {
addLog("CONFIG REFRESH FAILED", "error");
}
setIsConfigRefreshing(false);
}, []);
const addLog = (msg: string, type: 'info' | 'warning' | 'error' = 'info') => {
setLogs(prev => [{ id: Date.now() + Math.random(), timestamp: new Date().toLocaleTimeString(), message: msg, type }, ...prev].slice(0, 50));
};
@@ -171,7 +148,6 @@ export default function App() {
const handleSaveConfig = async (newConfig: ConfigItem[]) => {
try {
await comms.saveConfig(JSON.stringify(newConfig));
setConfig(newConfig);
addLog("CONFIGURATION SAVED", "info");
} catch (e) {
console.error(e);
@@ -202,10 +178,7 @@ export default function App() {
currentTime={currentTime}
isHostConnected={isHostConnected}
robotTarget={robotTarget}
onTabChange={(tab) => {
setActiveTab(tab);
if (tab === null) setActiveIOTab('in'); // Reset IO tab when closing
}}
onTabChange={setActiveTab}
activeTab={activeTab}
isLoading={isLoading}
>
@@ -215,13 +188,10 @@ export default function App() {
element={
<HomePage
systemState={systemState}
currentRecipe={currentRecipe || { id: '0', name: 'No Recipe', lastModified: '-' }}
recipes={recipes}
currentRecipe={currentRecipe}
robotTarget={robotTarget}
logs={logs}
ioPoints={ioPoints}
config={config}
isConfigRefreshing={isConfigRefreshing}
doorStates={doorStates}
isLowPressure={isLowPressure}
isEmergencyStop={isEmergencyStop}
@@ -230,7 +200,6 @@ export default function App() {
onMove={moveAxis}
onControl={handleControl}
onSaveConfig={handleSaveConfig}
onFetchConfig={fetchConfig}
onCloseTab={() => setActiveTab(null)}
videoRef={videoRef}
/>
@@ -240,10 +209,7 @@ export default function App() {
path="/io-monitor"
element={
<IOMonitorPage
ioPoints={ioPoints}
onToggle={toggleIO}
activeIOTab={activeIOTab}
onIOTabChange={setActiveIOTab}
/>
}
/>