Newer
Older
CNCTools / ReferenceSurfaceGenerator / frontend / src / App.js
import React, { useState } from 'react';
import axios from 'axios';
import { Container, Navbar, Card, ProgressBar, Alert, Button } from 'react-bootstrap';

// This will be our main component for handling uploads and progress
const UploadComponent = () => {
  const [file, setFile] = useState(null);
  const [progress, setProgress] = useState(0);
  const [statusMessage, setStatusMessage] = useState('Upload a file to begin.');
  const [downloadUrl, setDownloadUrl] = useState(null);
  const [error, setError] = useState(null);

  const handleFileChange = (event) => {
    setFile(event.target.files[0]);
    setStatusMessage(event.target.files[0] ? event.target.files[0].name : 'Upload a file to begin.');
    setProgress(0);
    setDownloadUrl(null);
    setError(null);
  };

  const handleUpload = async () => {
    if (!file) return;

    setProgress(0);
    setDownloadUrl(null);
    setError(null);
    setStatusMessage('Uploading file...');

    const formData = new FormData();
    formData.append('file', file);

    try {
      // Step 1: Upload the file to the backend (will be proxied by the dev server)
      const uploadResponse = await axios.post('/upload/', formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
        },
      });

      const { file_id, filename } = uploadResponse.data;
      setStatusMessage('File uploaded. Initializing processing...');

      // Step 2: Establish a WebSocket connection (proxied by the dev server)
      const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
      const wsHost = window.location.hostname;
      const wsPort = window.location.port ? `:${window.location.port}` : '';
      const ws = new WebSocket(`${wsProtocol}//${wsHost}${wsPort}/ws/${file_id}/${filename}`);

      ws.onopen = () => {
        setStatusMessage('Connection established. Starting processing...');
      };

      ws.onmessage = (event) => {
        const data = JSON.parse(event.data);
        
        if (data.status === 'processing') {
          setProgress(data.progress);
          setStatusMessage(data.message);
        } else if (data.status === 'complete') {
          setProgress(data.progress);
          setStatusMessage(data.message);
          setDownloadUrl(data.download_url); // This will be a relative URL like /download/xyz.dxf
          ws.close();
        } else if (data.status === 'error') {
          setError(data.message);
          setProgress(0);
          ws.close();
        }
      };

      ws.onerror = (event) => {
        console.error("WebSocket error observed:", event);
        setError('A connection error occurred. Could not get progress updates.');
      };

      ws.onclose = () => {
        console.log('WebSocket connection closed.');
        // If the process isn't complete, show a message
        if (!downloadUrl && !error) {
            setStatusMessage('Connection closed.');
        }
      };

    } catch (err) {
      console.error(err);
      setError(err.response?.data?.detail || 'File upload failed. Please try again.');
    }
  };

  return (
    <Card>
      <Card.Body>
        <Card.Title>Mesh Simplifier</Card.Title>
        <Card.Text>
          Select a <code>.obj</code>, <code>.stl</code>, or <code>.3mf</code> file to process. The tool will generate a DXF file containing the layered profiles of your model.
        </Card.Text>
        <div className="input-group mb-3">
          <input type="file" className="form-control" id="inputGroupFile02" onChange={handleFileChange} accept=".obj,.stl,.3mf" />
        </div>
        
        {file && (
          <Button variant="primary" onClick={handleUpload} className="mb-3">
            Start Processing
          </Button>
        )}

        <hr />

        <h6>Processing Status</h6>
        <p className="text-muted">{statusMessage}</p>
        <ProgressBar now={progress} label={`${progress}%`} animated />

        {error && (
          <Alert variant="danger" className="mt-3">
            {error}
          </Alert>
        )}

        {downloadUrl && (
          <div className="mt-4 text-center">
            <Button variant="success" href={downloadUrl}>
              Download DXF File
            </Button>
          </div>
        )}
      </Card.Body>
    </Card>
  );
};


function App() {
  return (
    <div>
      <Navbar bg="dark" variant="dark">
        <Container>
          <Navbar.Brand href="#home">DXF Curve Generator</Navbar.Brand>
        </Container>
      </Navbar>
      <Container className="mt-5">
        <UploadComponent />
      </Container>
    </div>
  );
}

export default App;