mirror of
https://github.com/christianvidalwolf-prog/Craze-Data-check.git
synced 2026-08-03 18:45:23 +02:00
feat: Initialize AI Studio project structure
Sets up a new project for an AI Studio application. Includes essential files like `package.json`, `vite.config.ts`, `tsconfig.json`, and a basic React application structure (`App.tsx`, `main.tsx`, `index.html`, `index.css`). Also adds a `README.md` with instructions for running locally, an `.env.example` for API key configuration, and a `.gitignore` to manage project dependencies and build artifacts.
This commit is contained in:
+169
@@ -0,0 +1,169 @@
|
||||
import React, { useState, useMemo } 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<AppState>({
|
||||
headers: [],
|
||||
data: [],
|
||||
fileName: '',
|
||||
fileDate: null,
|
||||
hasUnsavedChanges: false
|
||||
});
|
||||
const [activeModule, setActiveModule] = useState<'descriptions' | 'completeness' | 'matrix' | 'upload'>('descriptions');
|
||||
const [editingRowIndex, setEditingRowIndex] = useState<number | null>(null);
|
||||
|
||||
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
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<any[]>(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 (
|
||||
<div className="h-screen bg-slate-900 text-slate-200 flex flex-col font-sans overflow-hidden">
|
||||
<TopBar
|
||||
stats={stats}
|
||||
onExport={handleExport}
|
||||
hasData={appState.data.length > 0}
|
||||
hasUnsavedChanges={appState.hasUnsavedChanges}
|
||||
/>
|
||||
<div className="flex flex-1 overflow-hidden">
|
||||
<Sidebar activeModule={activeModule} setActiveModule={setActiveModule} />
|
||||
<main className="flex-1 overflow-auto relative p-6 bg-slate-950">
|
||||
{appState.data.length === 0 && activeModule !== 'upload' ? (
|
||||
<div className="flex flex-col items-center justify-center h-full text-slate-400">
|
||||
<p className="mb-4 text-lg">No data loaded.</p>
|
||||
<label className="cursor-pointer bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg shadow-lg transition-colors">
|
||||
Load Excel File
|
||||
<input type="file" accept=".xlsx, .xls" className="hidden" onChange={handleFileUpload} />
|
||||
</label>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{activeModule === 'descriptions' && (
|
||||
<ProductDescriptions
|
||||
data={appState.data}
|
||||
onEdit={(index) => setEditingRowIndex(index)}
|
||||
/>
|
||||
)}
|
||||
{activeModule === 'completeness' && (
|
||||
<DataCompleteness data={appState.data} headers={appState.headers} />
|
||||
)}
|
||||
{activeModule === 'matrix' && (
|
||||
<MatrixView data={appState.data} headers={appState.headers} />
|
||||
)}
|
||||
{activeModule === 'upload' && (
|
||||
<UploadReload
|
||||
appState={appState}
|
||||
onUpload={handleFileUpload}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
{editingRowIndex !== null && (
|
||||
<EditPanel
|
||||
row={appState.data[editingRowIndex]}
|
||||
rowIndex={editingRowIndex}
|
||||
onSave={handleSaveRow}
|
||||
onClose={() => setEditingRowIndex(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user