import { useState, useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { DataManager } from '../utils/dataManager'; import { setOrders } from '../store/slices/orderSlice'; import { Trash2, Download, RefreshCw, AlertTriangle, Database, Store, Link2, Unlink } from 'lucide-react'; import toast from 'react-hot-toast'; import api from '../utils/api'; interface EtsyStatus { connected: boolean; shopId?: string; shopName?: string; lastSyncedAt?: string; } interface EtsyConfig { configured: boolean; apiKeyMasked: string | null; redirectUri: string | null; source: 'database' | 'environment' | null; } const Settings = () => { const dispatch = useDispatch(); const [storageSummary, setStorageSummary] = useState({ orders: 0, products: 0, customers: 0, expenses: 0, totalItems: 0 }); const [showClearConfirm, setShowClearConfirm] = useState(false); const [etsyStatus, setEtsyStatus] = useState(null); const [etsyConfig, setEtsyConfig] = useState(null); const [apiKeyInput, setApiKeyInput] = useState(''); const [redirectUriInput, setRedirectUriInput] = useState(''); const [isSavingConfig, setIsSavingConfig] = useState(false); const [isConnecting, setIsConnecting] = useState(false); const [isSyncing, setIsSyncing] = useState(false); const [unmatchedItems, setUnmatchedItems] = useState([]); useEffect(() => { updateStorageSummary(); loadEtsyStatus(); loadEtsyConfig(); // Handle the redirect back from the Etsy consent screen const params = new URLSearchParams(window.location.search); const etsyResult = params.get('etsy'); if (etsyResult === 'connected') { toast.success('Etsy shop connected!'); window.history.replaceState({}, '', window.location.pathname); } else if (etsyResult === 'error') { const reason = params.get('reason'); toast.error( reason ? `Etsy connection failed: ${reason}` : 'Etsy connection failed. Please try again.', { duration: 12000 } ); window.history.replaceState({}, '', window.location.pathname); } }, []); const loadEtsyStatus = async () => { try { const res = await api.get('/etsy/status'); setEtsyStatus(res.data); } catch { setEtsyStatus(null); } }; const loadEtsyConfig = async () => { try { const res = await api.get('/etsy/config'); setEtsyConfig(res.data); // Prefill the callback URL: stored value, or this app's own callback route setRedirectUriInput(res.data.redirectUri || `${window.location.origin}/api/etsy/callback`); } catch { setEtsyConfig(null); setRedirectUriInput(`${window.location.origin}/api/etsy/callback`); } }; const handleSaveEtsyConfig = async () => { if (!apiKeyInput.trim() && !etsyConfig?.configured) { toast.error('Enter your Etsy API keystring'); return; } setIsSavingConfig(true); try { await api.put('/etsy/config', { apiKey: apiKeyInput.trim() || undefined, redirectUri: redirectUriInput.trim(), }); toast.success('Etsy configuration saved'); setApiKeyInput(''); loadEtsyConfig(); } catch (error: any) { toast.error(error.response?.data?.message || 'Failed to save Etsy configuration'); } finally { setIsSavingConfig(false); } }; const handleEtsyConnect = async () => { setIsConnecting(true); try { const res = await api.get('/etsy/connect'); window.location.href = res.data.url; } catch (error: any) { toast.error(error.response?.data?.message || 'Failed to start Etsy connection'); setIsConnecting(false); } }; const handleEtsySync = async () => { setIsSyncing(true); setUnmatchedItems([]); try { const res = await api.post('/etsy/sync'); const { created, updated, unmatchedItems: unmatched, receiptsSeen } = res.data; toast.success(`Synced ${receiptsSeen} Etsy orders: ${created} new, ${updated} updated`); if (unmatched?.length > 0) { setUnmatchedItems(unmatched); } // Refresh orders in the app const ordersRes = await api.get('/orders?limit=1000'); dispatch(setOrders(ordersRes.data.orders)); loadEtsyStatus(); } catch (error: any) { toast.error(error.response?.data?.message || 'Etsy sync failed'); } finally { setIsSyncing(false); } }; const handleEtsyDisconnect = async () => { if (!window.confirm('Disconnect your Etsy shop? Synced data stays; only the connection is removed.')) return; try { await api.delete('/etsy/connection'); setEtsyStatus({ connected: false }); toast.success('Etsy disconnected'); } catch { toast.error('Failed to disconnect Etsy'); } }; const updateStorageSummary = () => { setStorageSummary(DataManager.getStorageSummary()); }; const handleClearData = () => { DataManager.clearAllData(); updateStorageSummary(); setShowClearConfirm(false); toast.success('All data cleared successfully!'); // Reload the page to reset Redux state window.location.reload(); }; const handleClearWithBackup = () => { DataManager.clearWithBackup(); updateStorageSummary(); setShowClearConfirm(false); toast.success('Data cleared and backup downloaded!'); // Reload the page to reset Redux state window.location.reload(); }; const handleExportData = () => { const backup = DataManager.exportAllData(); const blob = new Blob([backup], { type: 'application/json' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `etsy-tracker-export-${new Date().toISOString().split('T')[0]}.json`; a.click(); window.URL.revokeObjectURL(url); toast.success('Data exported successfully!'); }; return (

Settings

{/* Etsy Integration Section */}

Etsy Integration

{etsyStatus?.connected ? (

Connected{etsyStatus.shopName ? ` to ${etsyStatus.shopName}` : ''}

{etsyStatus.lastSyncedAt ? `Last synced ${new Date(etsyStatus.lastSyncedAt).toLocaleString('en-AU')}` : 'Not synced yet'}

{unmatchedItems.length > 0 && (

{unmatchedItems.length} item title(s) couldn't be matched to your product catalog (their orders were synced without cost data):

    {unmatchedItems.map((title, idx) => (
  • {title}
  • ))}

Add these as products (or aliases on existing products) and sync again to fill in costs.

)}
) : (

Connect your Etsy shop to sync orders automatically — order details, items, variations, and tracking, with costs matched from your product catalog.

{/* API credentials, stored in the database */}

API Configuration

setApiKeyInput(e.target.value)} />
setRedirectUriInput(e.target.value)} />

Register this exact URL as a callback URL in your Etsy app settings (etsy.com/developers) before connecting.

)}
{/* Data Management Section */}

Data Management

{/* Current Data Summary */}

Current Data Storage

{storageSummary.orders}

Orders

{storageSummary.products}

Products

{storageSummary.customers}

Customers

{storageSummary.expenses}

Expenses

Total Items: {storageSummary.totalItems}

{/* Action Buttons */}
{/* Testing Helper */}

🧪 Testing Mode

Clear all data to test the import functionality with fresh data. Your data will be backed up automatically.

{/* Clear Confirmation Modal */} {showClearConfirm && (

Clear All Data?

This will permanently delete all your orders, products, customers, and expenses. This action cannot be undone.

Current data: {storageSummary.totalItems} total items

)} {/* Other Settings Sections */}

Application Settings

Additional application settings will be available here in future updates.

); }; export default Settings;