import React, { useState, useEffect, useCallback } from "react"; import { debugLog } from "../utils/config"; import TroubleshootingGuide from "./TroubleshootingGuide"; import "./DataViewer.css"; const DataViewer = ({ s3Url, onDownload, showPreviewOnly = false }) => { const [data, setData] = useState([]); const [columns, setColumns] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [currentPage, setCurrentPage] = useState(1); const [rowsPerPage] = useState(10); const fetchData = useCallback(async () => { try { setLoading(true); setError(null); debugLog("Fetching data from S3 URL:", s3Url); // Try multiple approaches to fetch the data let response; // Method 1: Direct fetch with CORS try { response = await fetch(s3Url, { method: "GET", headers: { Accept: "text/csv,text/plain,application/octet-stream,*/*", }, mode: "cors", }); } catch (corsError) { debugLog("CORS fetch failed, trying no-cors:", corsError); // Method 2: Try with no-cors mode (limited but might work) try { response = await fetch(s3Url, { method: "GET", mode: "no-cors", }); } catch (noCorsError) { debugLog("No-cors fetch also failed:", noCorsError); throw new Error( "Unable to preview data due to CORS restrictions. You can still download the file directly." ); } } if (!response.ok && response.status !== 0) { // If direct fetch fails, provide helpful error messages if (response.status === 403 || response.status === 401) { throw new Error( "Access denied. The file may require authentication or have expired." ); } else if (response.status === 404) { throw new Error( "File not found. The download link may have expired." ); } else { throw new Error( `Unable to fetch data (${response.status}). You can still download the file directly.` ); } } // For no-cors responses, we can't read the content if (response.type === "opaque") { throw new Error( "Preview not available due to CORS restrictions. Please download the file to view the data." ); } const csvText = await response.text(); if (!csvText || csvText.trim().length === 0) { throw new Error("The downloaded file appears to be empty"); } const parsedData = parseCSV(csvText); if (parsedData.length > 0) { setColumns(Object.keys(parsedData[0])); setData(parsedData); debugLog("Data parsed successfully:", { rows: parsedData.length, columns: Object.keys(parsedData[0]).length, sampleData: parsedData.slice(0, 2), }); } else { throw new Error("No valid data rows found in the file"); } } catch (err) { setError(err.message); debugLog("Error fetching data:", err); } finally { setLoading(false); } }, [s3Url]); useEffect(() => { if (s3Url) { fetchData(); } }, [s3Url, fetchData]); const parseCSV = (csvText) => { try { const lines = csvText.trim().split("\n"); if (lines.length < 2) return []; // Handle different CSV formats and potential quotes const parseCSVLine = (line) => { const result = []; let current = ""; let inQuotes = false; for (let i = 0; i < line.length; i++) { const char = line[i]; if (char === '"') { inQuotes = !inQuotes; } else if (char === "," && !inQuotes) { result.push(current.trim()); current = ""; } else { current += char; } } result.push(current.trim()); return result.map((value) => value.replace(/^"(.*)"$/, "$1")); // Remove outer quotes }; const headers = parseCSVLine(lines[0]); const rows = []; for (let i = 1; i < lines.length; i++) { if (lines[i].trim() === "") continue; // Skip empty lines const values = parseCSVLine(lines[i]); if (values.length > 0 && values.some((val) => val.trim() !== "")) { const row = {}; headers.forEach((header, index) => { row[header] = values[index] || ""; }); rows.push(row); } } return rows; } catch (err) { debugLog("Error parsing CSV:", err); throw new Error( "Failed to parse CSV data. The file format may be invalid." ); } }; const getPaginatedData = () => { const startIndex = (currentPage - 1) * rowsPerPage; const endIndex = startIndex + rowsPerPage; return data.slice(startIndex, endIndex); }; const totalPages = Math.ceil(data.length / rowsPerPage); const handleDownload = () => { if (onDownload) { onDownload(); } else { window.open(s3Url, "_blank"); } }; if (loading) { return (
Loading data preview...
{error}
The generated file appears to be empty.
Showing {getPaginatedData().length} of {data.length} rows •{" "} {columns.length} columns
{column} | ))}
---|
{row[column]} | ))}