import React, { useState, useMemo, useEffect } from 'react'; import * as XLSX from 'xlsx'; import { AppState, ExcelRow, COLUMNS } from './types'; import { Sidebar } from './components/Sidebar'; import { TopBar } from './components/TopBar'; import { ProductDescriptions } from './components/ProductDescriptions'; import { DataCompleteness } from './components/DataCompleteness'; import { MatrixView } from './components/MatrixView'; import { UploadReload } from './components/UploadReload'; import { EditPanel } from './components/EditPanel'; export default function App() { const [appState, setAppState] = useState({ headers: [], data: [], fileName: '', fileDate: null, hasUnsavedChanges: false }); const [activeModule, setActiveModule] = useState<'descriptions' | 'completeness' | 'matrix' | 'upload'>('descriptions'); const [editingRowIndex, setEditingRowIndex] = useState(null); const [isLoadingDefault, setIsLoadingDefault] = useState(true); const [defaultLoadError, setDefaultLoadError] = useState(null); useEffect(() => { const loadDefaultData = async () => { // Use different proxies as fallbacks const proxies = [ (url: string) => `https://api.allorigins.win/raw?url=${encodeURIComponent(url)}`, (url: string) => `https://cors-anywhere.herokuapp.com/${url}`, // Often needs manual activation but we'll try (url: string) => `https://thingproxy.freeboard.io/fetch/${url}` ]; const dropboxUrl = 'https://dl.dropboxusercontent.com/scl/fi/nkyabkq2jdwfudof2ovrl/Data-Matrix.xlsx?rlkey=1cz5fuabwipqqivihii7sybw0&st=io0g4wvb&dl=1'; setIsLoadingDefault(true); setDefaultLoadError(null); let lastError = null; for (const getProxyUrl of proxies) { try { const proxyUrl = getProxyUrl(dropboxUrl); console.log(`Attempting to fetch via: ${proxyUrl}`); const response = await fetch(proxyUrl); if (!response.ok) { throw new Error(`Proxy returned status ${response.status}: ${response.statusText}`); } const arrayBuffer = await response.arrayBuffer(); if (arrayBuffer.byteLength < 100) { throw new Error("File too small, possibly empty or error page"); } const wb = XLSX.read(arrayBuffer, { type: 'array' }); const wsname = wb.SheetNames[0]; const ws = wb.Sheets[wsname]; const data = XLSX.utils.sheet_to_json(ws, { header: 1 }); if (data.length > 0) { setAppState({ headers: data[0], data: data.slice(1), fileName: 'Data-Matrix.xlsx (Cloud Sync)', fileDate: new Date(), hasUnsavedChanges: false }); setActiveModule('descriptions'); setIsLoadingDefault(false); return; // Success! } } catch (err) { console.error(`Failed with proxy:`, err); lastError = err; } } // If we reach here, all proxies failed setDefaultLoadError(lastError instanceof Error ? lastError.message : 'All connection attempts failed'); setIsLoadingDefault(false); }; loadDefaultData(); }, []); const handleFileUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; if (appState.hasUnsavedChanges) { if (!window.confirm('You have unsaved changes. Are you sure you want to load a new file and discard them?')) { e.target.value = ''; return; } } const reader = new FileReader(); reader.onload = (evt) => { const bstr = evt.target?.result; const wb = XLSX.read(bstr, { type: 'binary' }); const wsname = wb.SheetNames[0]; const ws = wb.Sheets[wsname]; const data = XLSX.utils.sheet_to_json(ws, { header: 1 }); if (data.length > 0) { setAppState({ headers: data[0], data: data.slice(1), fileName: file.name, fileDate: new Date(), hasUnsavedChanges: false }); setActiveModule('descriptions'); } }; reader.readAsBinaryString(file); }; const handleExport = () => { if (appState.data.length === 0) return; const wsData = [appState.headers, ...appState.data]; const ws = XLSX.utils.aoa_to_sheet(wsData); const wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, 'Products'); const dateStr = new Date().toISOString().split('T')[0]; XLSX.writeFile(wb, `CRAZE_Products_Updated_${dateStr}.xlsx`); setAppState(prev => ({ ...prev, hasUnsavedChanges: false })); }; const handleSaveRow = (rowIndex: number, updatedRow: ExcelRow) => { setAppState(prev => { const newData = [...prev.data]; newData[rowIndex] = updatedRow; return { ...prev, data: newData, hasUnsavedChanges: true }; }); setEditingRowIndex(null); }; const stats = useMemo(() => { if (appState.data.length === 0) return null; let missingDeLong = 0; let missingEnLong = 0; let missingDeShort = 0; let missingEnShort = 0; let fullyComplete = 0; appState.data.forEach(row => { const deLong = row[COLUMNS.LONG_DE]; const enLong = row[COLUMNS.LONG_EN]; const deShort = row[COLUMNS.SHORT_DE]; const enShort = row[COLUMNS.SHORT_EN]; if (!deLong) missingDeLong++; if (!enLong) missingEnLong++; if (!deShort) missingDeShort++; if (!enShort) missingEnShort++; if (deLong && enLong && deShort && enShort) fullyComplete++; }); return { total: appState.data.length, missingDeLong, missingEnLong, missingDeShort, missingEnShort, fullyComplete }; }, [appState.data]); return (
0} hasUnsavedChanges={appState.hasUnsavedChanges} />
{isLoadingDefault ? (

Loading latest data...

) : defaultLoadError && appState.data.length === 0 ? (

Failed to auto-load data: {defaultLoadError}

) : appState.data.length === 0 && activeModule !== 'upload' ? (

No data loaded.

) : ( <> {activeModule === 'descriptions' && ( setEditingRowIndex(index)} /> )} {activeModule === 'completeness' && ( )} {activeModule === 'matrix' && ( )} {activeModule === 'upload' && ( )} )}
{editingRowIndex !== null && ( setEditingRowIndex(null)} /> )}
); }