1 line
3.4 KiB
TypeScript

import React,{useState,useMemo,useTransition,useCallback}from'react';const useDebounce=<T,>(value:T,delay:number=300):T=>{const[debouncedValue,setDebouncedValue]=useState(value);React.useEffect(()=>{const handler=setTimeout(()=>setDebouncedValue(value),delay);return()=>clearTimeout(handler)},[value,delay]);return debouncedValue};const useToast=()=>{const[toasts,setToasts]=useState<Array<{id:number;message:string;type:string}>>([]);const showToast=useCallback((message:string,type:string='info')=>{const id=Date.now();setToasts(prev=>[...prev,{id,message,type}]);setTimeout(()=>setToasts(prev=>prev.filter(t=>t.id!==id)),3000)},[]);return{toasts,showToast}};interface ImportJob{id:string;filename:string;records:number;status:string;date:string}const mockJobs:ImportJob[]=[{id:'j1',filename:'contacts.csv',records:150,status:'completed',date:'2024-02-13'},{id:'j2',filename:'products.csv',records:45,status:'processing',date:'2024-02-12'},{id:'j3',filename:'orders.csv',records:320,status:'completed',date:'2024-02-11'}];const App:React.FC=()=>{const[jobs,setJobs]=useState(mockJobs);const[isPending,startTransition]=useTransition();const{toasts,showToast}=useToast();const stats=useMemo(()=>({total:jobs.length,completed:jobs.filter(j=>j.status==='completed').length,totalRecords:jobs.reduce((sum,j)=>sum+j.records,0)}),[jobs]);const handleImport=()=>{showToast('Import started','success')};const handleExport=()=>{showToast('Export started','success')};return(<div className="app-container"><header className="app-header"><h1>Import / Export</h1><p className="subtitle">CSV import/export UI</p></header><div className="stats-grid"><div className="stat-card"><div className="stat-value">{stats.total}</div><div className="stat-label">Total Jobs</div></div><div className="stat-card"><div className="stat-value">{stats.completed}</div><div className="stat-label">Completed</div></div><div className="stat-card"><div className="stat-value">{stats.totalRecords}</div><div className="stat-label">Records Processed</div></div></div><div className="action-panel"><div className="panel-section"><h2>Import CSV</h2><div className="file-input-wrapper"><input type="file" id="csv-import" accept=".csv" className="file-input"/><label htmlFor="csv-import" className="file-label">Choose CSV File</label></div><button className="btn btn-primary" onClick={handleImport}>Start Import</button></div><div className="panel-section"><h2>Export CSV</h2><select className="select-input"><option>All Records</option><option>Filtered Records</option><option>Current View</option></select><button className="btn btn-success" onClick={handleExport}>Export to CSV</button></div></div><div className="jobs-list"><h2>Recent Jobs</h2>{jobs.length===0?(<div className="empty-state"><div className="empty-icon">📁</div><h3>No jobs yet</h3><p>Start importing or exporting data</p></div>):(<div className="data-grid">{jobs.map(job=>(<div key={job.id} className="data-card"><div className="card-header"><h3>{job.filename}</h3><span className={`badge badge-${job.status}`}>{job.status}</span></div><div className="card-body"><div className="card-stat"><span className="label">Records:</span><span className="value">{job.records}</span></div><div className="card-stat"><span className="label">Date:</span><span className="value">{job.date}</span></div></div></div>))}</div>)}</div><div className="toast-container">{toasts.map(toast=>(<div key={toast.id} className={`toast toast-${toast.type}`}>{toast.message}</div>))}</div></div>)};export default App;