import React, { useState, useEffect } from "react"; import { CssBaseline, Box, useColorScheme, } from "@mui/material"; import Header from "./components/Header"; import Footer from "./components/Footer"; import MainPage from "./components/MainPage"; import ApiKeyPage from "./components/ApiKeyPage"; import "./App.css"; // Utility function to generate a cryptographically secure API key function generateApiKey() { const array = new Uint8Array(16); // Use crypto.getRandomValues if available (browser), fallback for tests if (typeof crypto !== "undefined" && crypto.getRandomValues) { crypto.getRandomValues(array); } else { // Fallback for test environments - not cryptographically secure for (let i = 0; i < array.length; i++) { array[i] = Math.floor(Math.random() * 256); } } return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join( "", ); } // JMESPath Testing Tool - Main Application Component function App() { const [currentPage, setCurrentPage] = useState("main"); // 'main' or 'apikey' const [theme, setTheme] = useState(() => { // Load theme from localStorage or default to 'auto' return localStorage.getItem("theme") || "auto"; }); const [shellType, setShellType] = useState(() => { // Load shell type from localStorage or default to 'bash' return localStorage.getItem("jmespath-shell-type") || "bash"; }); const [showReloadButton, setShowReloadButton] = useState(false); const [currentStateGuid, setCurrentStateGuid] = useState(null); const [jmespathExpression, setJmespathExpression] = useState("people[0].name"); const [jsonData, setJsonData] = useState(`{ "people": [ { "name": "John Doe", "age": 30, "city": "New York" }, { "name": "Jane Smith", "age": 25, "city": "Los Angeles" } ], "total": 2 }`); const [apiKey, setApiKey] = useState(() => { // Load API key from localStorage or generate new one const stored = localStorage.getItem("jmespath-api-key"); if (stored && /^[0-9a-f]{32}$/i.test(stored)) { return stored; } const newKey = generateApiKey(); localStorage.setItem("jmespath-api-key", newKey); return newKey; }); const getApiHeaders = () => ({ "Accept": "application/json", "x-api-key": apiKey, }); const { setMode } = useColorScheme(); // Load sample data from API on startup and setup periodic state checking useEffect(() => { // Sync initial theme from localStorage with MUI color scheme const initialMode = theme === 'auto' ? 'system' : theme; setMode(initialMode); loadSampleData(); // Check for state changes every 5 seconds const interval = setInterval(checkStateChange, 5000); return () => clearInterval(interval); // eslint-disable-next-line react-hooks/exhaustive-deps }, [apiKey]); // Check if state has changed (new data uploaded) const checkStateChange = async () => { try { const response = await fetch("/api/v1/state", { headers: getApiHeaders(), }); if (response.ok) { const stateData = await response.json(); if (stateData.state && stateData.state !== currentStateGuid) { setShowReloadButton(true); } } } catch (error) { // Silently handle state check errors } }; // Load sample data from API const loadSampleData = async () => { try { setShowReloadButton(false); const response = await fetch("/api/v1/sample", { headers: getApiHeaders(), }); if (response.ok) { const data = await response.json(); if (data) { setJsonData(JSON.stringify(data, null, 2)); } // Update current state GUID const stateResponse = await fetch("/api/v1/state", { headers: getApiHeaders(), }); if (stateResponse.ok) { const stateData = await stateResponse.json(); setCurrentStateGuid(stateData.state); } } } catch (error) { console.error("Failed to load sample data:", error); } }; // Regenerate API key const regenerateApiKey = () => { const newKey = generateApiKey(); setApiKey(newKey); localStorage.setItem("jmespath-api-key", newKey); setShowReloadButton(false); setCurrentStateGuid(null); }; const handleThemeChange = (newTheme) => { setTheme(newTheme); const muiMode = newTheme === "auto" ? "system" : newTheme; setMode(muiMode); localStorage.setItem("theme", newTheme); }; const handlePageChange = (newPage) => { setCurrentPage(newPage); }; const handleShellTypeChange = (newShellType) => { setShellType(newShellType); localStorage.setItem("jmespath-shell-type", newShellType); }; return ( <>
{/* Main Content Section - flex-grow to fill space */} {currentPage === "main" ? ( ) : ( )}