|
@@ -39,6 +39,7 @@ export function IntegrationsContent() {
|
|
|
const [loading, setLoading] = useState(true);
|
|
const [loading, setLoading] = useState(true);
|
|
|
const [showConnectDialog, setShowConnectDialog] = useState(false);
|
|
const [showConnectDialog, setShowConnectDialog] = useState(false);
|
|
|
const [selectedPlatform, setSelectedPlatform] = useState<string | null>(null);
|
|
const [selectedPlatform, setSelectedPlatform] = useState<string | null>(null);
|
|
|
|
|
+ const [syncingStores, setSyncingStores] = useState<Set<string>>(new Set());
|
|
|
const { toast } = useToast();
|
|
const { toast } = useToast();
|
|
|
|
|
|
|
|
const fetchStores = async () => {
|
|
const fetchStores = async () => {
|
|
@@ -62,7 +63,46 @@ export function IntegrationsContent() {
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
const data = await response.json();
|
|
|
if (data.success) {
|
|
if (data.success) {
|
|
|
- setConnectedShops(data.stores || []);
|
|
|
|
|
|
|
+ const stores = data.stores || [];
|
|
|
|
|
+ setConnectedShops(stores);
|
|
|
|
|
+
|
|
|
|
|
+ // Update syncingStores - remove stores that finished syncing
|
|
|
|
|
+ setSyncingStores(prev => {
|
|
|
|
|
+ const newSyncing = new Set(prev);
|
|
|
|
|
+ stores.forEach((store: ConnectedStore) => {
|
|
|
|
|
+ if (prev.has(store.id) && store.sync_status !== 'syncing') {
|
|
|
|
|
+ newSyncing.delete(store.id);
|
|
|
|
|
+
|
|
|
|
|
+ // Show completion notification
|
|
|
|
|
+ if (store.sync_status === 'completed') {
|
|
|
|
|
+ const stats = store.alt_data?.last_sync_stats;
|
|
|
|
|
+ let description = `${store.store_name || 'Store'} data synchronized successfully.`;
|
|
|
|
|
+
|
|
|
|
|
+ if (stats) {
|
|
|
|
|
+ const items = [];
|
|
|
|
|
+ if (stats.products?.synced) items.push(`${stats.products.synced} products`);
|
|
|
|
|
+ if (stats.orders?.synced) items.push(`${stats.orders.synced} orders`);
|
|
|
|
|
+ if (stats.customers?.synced) items.push(`${stats.customers.synced} customers`);
|
|
|
|
|
+ if (items.length > 0) {
|
|
|
|
|
+ description = `Synced: ${items.join(', ')}`;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ toast({
|
|
|
|
|
+ title: "Sync Complete",
|
|
|
|
|
+ description,
|
|
|
|
|
+ });
|
|
|
|
|
+ } else if (store.sync_status === 'error') {
|
|
|
|
|
+ toast({
|
|
|
|
|
+ title: "Sync Failed",
|
|
|
|
|
+ description: store.sync_error || "An error occurred during synchronization.",
|
|
|
|
|
+ variant: "destructive",
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ return newSyncing;
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('Error fetching stores:', error);
|
|
console.error('Error fetching stores:', error);
|
|
@@ -138,6 +178,17 @@ export function IntegrationsContent() {
|
|
|
fetchStores();
|
|
fetchStores();
|
|
|
}, []);
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
+ // Poll for sync status updates
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (syncingStores.size === 0) return;
|
|
|
|
|
+
|
|
|
|
|
+ const pollInterval = setInterval(() => {
|
|
|
|
|
+ fetchStores();
|
|
|
|
|
+ }, 3000); // Poll every 3 seconds
|
|
|
|
|
+
|
|
|
|
|
+ return () => clearInterval(pollInterval);
|
|
|
|
|
+ }, [syncingStores]);
|
|
|
|
|
+
|
|
|
const platforms = [
|
|
const platforms = [
|
|
|
{
|
|
{
|
|
|
id: "shopify",
|
|
id: "shopify",
|
|
@@ -242,13 +293,16 @@ export function IntegrationsContent() {
|
|
|
throw new Error(data.error || 'Failed to trigger sync');
|
|
throw new Error(data.error || 'Failed to trigger sync');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Add store to syncing set to start polling
|
|
|
|
|
+ setSyncingStores(prev => new Set(prev).add(storeId));
|
|
|
|
|
+
|
|
|
toast({
|
|
toast({
|
|
|
title: "Sync Started",
|
|
title: "Sync Started",
|
|
|
description: `Data synchronization started for ${storeName}`,
|
|
description: `Data synchronization started for ${storeName}`,
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- // Refresh stores list to show updated sync status
|
|
|
|
|
- setTimeout(() => fetchStores(), 1000);
|
|
|
|
|
|
|
+ // Refresh stores list immediately to show syncing status
|
|
|
|
|
+ setTimeout(() => fetchStores(), 500);
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('Error triggering sync:', error);
|
|
console.error('Error triggering sync:', error);
|
|
|
toast({
|
|
toast({
|
|
@@ -279,6 +333,38 @@ export function IntegrationsContent() {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ const getSyncStats = (shop: ConnectedStore) => {
|
|
|
|
|
+ const stats = shop.alt_data?.last_sync_stats;
|
|
|
|
|
+ if (!stats || shop.sync_status !== 'completed') return null;
|
|
|
|
|
+
|
|
|
|
|
+ const items = [];
|
|
|
|
|
+ if (stats.products?.synced !== undefined) {
|
|
|
|
|
+ items.push({ label: 'Products', count: stats.products.synced, errors: stats.products.errors || 0 });
|
|
|
|
|
+ }
|
|
|
|
|
+ if (stats.orders?.synced !== undefined) {
|
|
|
|
|
+ items.push({ label: 'Orders', count: stats.orders.synced, errors: stats.orders.errors || 0 });
|
|
|
|
|
+ }
|
|
|
|
|
+ if (stats.customers?.synced !== undefined) {
|
|
|
|
|
+ items.push({ label: 'Customers', count: stats.customers.synced, errors: stats.customers.errors || 0 });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (items.length === 0) return null;
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="flex flex-wrap gap-2 mt-2">
|
|
|
|
|
+ {items.map((item) => (
|
|
|
|
|
+ <div key={item.label} className="text-xs bg-slate-700 px-2 py-1 rounded">
|
|
|
|
|
+ <span className="text-slate-400">{item.label}:</span>{' '}
|
|
|
|
|
+ <span className="text-green-400 font-semibold">{item.count}</span>
|
|
|
|
|
+ {item.errors > 0 && (
|
|
|
|
|
+ <span className="text-red-400 ml-1">({item.errors} errors)</span>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ );
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<div className="flex-1 space-y-6 p-8 bg-slate-900">
|
|
<div className="flex-1 space-y-6 p-8 bg-slate-900">
|
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center justify-between">
|
|
@@ -514,6 +600,7 @@ export function IntegrationsContent() {
|
|
|
{new Date(shop.sync_completed_at).toLocaleString()}
|
|
{new Date(shop.sync_completed_at).toLocaleString()}
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
|
|
+ {getSyncStats(shop)}
|
|
|
</div>
|
|
</div>
|
|
|
</td>
|
|
</td>
|
|
|
<td className="p-4">
|
|
<td className="p-4">
|