Spaces:
Running
Running
| import React, { useState, useEffect } from "react"; | |
| import "./App.css"; | |
| import Step1 from "./components/Step1"; | |
| import Step2 from "./components/Step2"; | |
| import Step3 from "./components/Step3"; | |
| import Step4 from "./components/Step4"; | |
| import { config as appConfig, debugLog } from "./utils/config"; | |
| import { ApiService } from "./utils/apiService"; | |
| // Make ApiService available globally for debugging | |
| if (process.env.NODE_ENV === "development") { | |
| window.ApiService = ApiService; | |
| } | |
| function App() { | |
| const [apiKey, setApiKey] = useState(""); | |
| const [isApiKeyValid, setIsApiKeyValid] = useState(false); // Explicitly initialized to false | |
| const [uploadedFile, setUploadedFile] = useState(null); | |
| const [s3Link, setS3Link] = useState(""); | |
| const [fileMetadata, setFileMetadata] = useState({ | |
| fileSizeBytes: 0, | |
| sourceFileRows: 0, | |
| }); | |
| const [config, setConfig] = useState({ | |
| numRows: appConfig.defaultNumRecords, | |
| targetColumn: "", | |
| }); | |
| const [generatedDataLink, setGeneratedDataLink] = useState(""); | |
| // Check for previously stored and valid API key on app load | |
| useEffect(() => { | |
| const checkStoredApiKey = async () => { | |
| try { | |
| debugLog("Checking for stored API key on app initialization"); | |
| const storedKeyStatus = await ApiService.verifyStoredApiKey(); | |
| if (storedKeyStatus.valid) { | |
| setIsApiKeyValid(true); | |
| debugLog("Valid stored API key found", storedKeyStatus); | |
| } else { | |
| setIsApiKeyValid(false); | |
| debugLog("No valid stored API key", storedKeyStatus); | |
| } | |
| } catch (error) { | |
| debugLog("Error checking stored API key", error); | |
| setIsApiKeyValid(false); | |
| } | |
| }; | |
| checkStoredApiKey(); | |
| }, []); | |
| // Log app initialization in debug mode | |
| useEffect(() => { | |
| debugLog("Syncora app initialized", { | |
| defaultNumRecords: appConfig.defaultNumRecords, | |
| maxFileSizeMB: appConfig.maxFileSizeMB, | |
| apiBaseUrl: appConfig.apiBaseUrl, | |
| }); | |
| }, []); | |
| const steps = [ | |
| { number: 1, title: "API Key Validation", component: Step1, icon: "π" }, | |
| { number: 2, title: "Upload Files", component: Step2, icon: "π" }, | |
| { number: 3, title: "Configuration", component: Step3, icon: "βοΈ" }, | |
| { number: 4, title: "Generate Data", component: Step4, icon: "π" }, | |
| ]; | |
| // Helper function to check if a step should be enabled | |
| const isStepEnabled = (stepNumber) => { | |
| switch (stepNumber) { | |
| case 1: | |
| return true; // Step 1 is always enabled | |
| case 2: | |
| const step2Enabled = isApiKeyValid; | |
| debugLog(`Step 2 enabled check: ${step2Enabled}`, { | |
| isApiKeyValid, | |
| apiKeyLength: apiKey.length, | |
| hasApiKey: !!apiKey, | |
| }); | |
| return step2Enabled; // Step 2 enabled when API key is valid | |
| case 3: | |
| return isApiKeyValid && uploadedFile && s3Link; // Step 3 enabled when file is uploaded | |
| case 4: | |
| return isApiKeyValid && uploadedFile && s3Link && config.targetColumn; // Step 4 enabled when config is complete | |
| default: | |
| return false; | |
| } | |
| }; | |
| const renderStep = (stepNumber) => { | |
| const step = steps[stepNumber - 1]; | |
| const StepComponent = step.component; | |
| const enabled = isStepEnabled(stepNumber); | |
| return ( | |
| <div className={`step-wrapper ${enabled ? "enabled" : "disabled"}`}> | |
| <StepComponent | |
| apiKey={apiKey} | |
| setApiKey={setApiKey} | |
| isApiKeyValid={isApiKeyValid} | |
| setIsApiKeyValid={setIsApiKeyValid} | |
| uploadedFile={uploadedFile} | |
| setUploadedFile={setUploadedFile} | |
| s3Link={s3Link} | |
| setS3Link={setS3Link} | |
| fileMetadata={fileMetadata} | |
| setFileMetadata={setFileMetadata} | |
| config={config} | |
| setConfig={setConfig} | |
| generatedDataLink={generatedDataLink} | |
| setGeneratedDataLink={setGeneratedDataLink} | |
| stepNumber={stepNumber} | |
| stepTitle={step.title} | |
| stepIcon={step.icon} | |
| enabled={enabled} | |
| /> | |
| </div> | |
| ); | |
| }; | |
| return ( | |
| <div className="App"> | |
| <header className="app-header"> | |
| <div className="header-content"> | |
| <div> | |
| <h1>Syncora</h1> | |
| <p>AI-Powered Data Generation Platform</p> | |
| </div> | |
| <div className="progress-indicator"> | |
| {steps.map((step, index) => { | |
| const stepNumber = step.number; | |
| const isEnabled = isStepEnabled(stepNumber); | |
| const isCompleted = | |
| stepNumber < steps.length | |
| ? isStepEnabled(stepNumber + 1) | |
| : stepNumber === 4 && generatedDataLink; | |
| return ( | |
| <div | |
| key={step.number} | |
| className={`progress-step ${ | |
| isCompleted | |
| ? "completed" | |
| : isEnabled | |
| ? "active" | |
| : "disabled" | |
| }`} | |
| title={`Step ${step.number}: ${step.title}`} | |
| /> | |
| ); | |
| })} | |
| </div> | |
| </div> | |
| </header> | |
| <div className="main-container"> | |
| {/* Left Column */} | |
| <div className="steps-column"> | |
| {/* Step 1 */} | |
| <div className="step-content">{renderStep(1)}</div> | |
| {/* Step 2 */} | |
| <div className="step-content">{renderStep(2)}</div> | |
| </div> | |
| {/* Right Column */} | |
| <div className="steps-column"> | |
| {/* Step 3 */} | |
| <div className="step-content">{renderStep(3)}</div> | |
| {/* Step 4 */} | |
| <div className="step-content">{renderStep(4)}</div> | |
| </div> | |
| </div> | |
| {/* Floating Help Button */} | |
| <button | |
| className="floating-help" | |
| onClick={() => window.open("https://docs.syncora.ai/help", "_blank")} | |
| title="Get Help" | |
| > | |
| ? | |
| </button> | |
| </div> | |
| ); | |
| } | |
| export default App; | |