|
|
@@ -41,15 +41,33 @@ interface ConnectedStore {
|
|
|
connectedAt?: string;
|
|
|
[key: string]: any;
|
|
|
};
|
|
|
+ // Note: Supabase returns this as an object (not array) due to UNIQUE constraint on store_id
|
|
|
store_sync_config?: {
|
|
|
enabled: boolean;
|
|
|
sync_frequency: string;
|
|
|
products_access_policy?: 'sync' | 'api_only' | 'not_allowed';
|
|
|
customers_access_policy?: 'sync' | 'api_only' | 'not_allowed';
|
|
|
orders_access_policy?: 'sync' | 'api_only' | 'not_allowed';
|
|
|
+ last_sync_at?: string;
|
|
|
+ } | {
|
|
|
+ enabled: boolean;
|
|
|
+ sync_frequency: string;
|
|
|
+ products_access_policy?: 'sync' | 'api_only' | 'not_allowed';
|
|
|
+ customers_access_policy?: 'sync' | 'api_only' | 'not_allowed';
|
|
|
+ orders_access_policy?: 'sync' | 'api_only' | 'not_allowed';
|
|
|
+ last_sync_at?: string;
|
|
|
}[];
|
|
|
}
|
|
|
|
|
|
+// Helper function to get sync config from store (handles both array and object formats)
|
|
|
+function getSyncConfig(store: ConnectedStore) {
|
|
|
+ if (!store.store_sync_config) return null;
|
|
|
+ if (Array.isArray(store.store_sync_config)) {
|
|
|
+ return store.store_sync_config[0] || null;
|
|
|
+ }
|
|
|
+ return store.store_sync_config;
|
|
|
+}
|
|
|
+
|
|
|
export function IntegrationsContent() {
|
|
|
const { t } = useTranslation();
|
|
|
const [connectedShops, setConnectedShops] = useState<ConnectedStore[]>([]);
|
|
|
@@ -85,8 +103,9 @@ export function IntegrationsContent() {
|
|
|
const stores = data.stores || [];
|
|
|
// Debug: Log the sync config for each store
|
|
|
stores.forEach((store: ConnectedStore) => {
|
|
|
+ const syncConfig = getSyncConfig(store);
|
|
|
console.log(`[DEBUG] Store ${store.store_name}:`, {
|
|
|
- enabled: store.store_sync_config?.[0]?.enabled,
|
|
|
+ enabled: syncConfig?.enabled,
|
|
|
sync_config: store.store_sync_config
|
|
|
});
|
|
|
});
|
|
|
@@ -348,19 +367,17 @@ export function IntegrationsContent() {
|
|
|
console.log('[DEBUG] Toggle called:', { storeId, storeName, currentEnabled, newEnabled });
|
|
|
|
|
|
// Optimistic UI update - immediately update the local state
|
|
|
+ // Note: Supabase returns store_sync_config as an object (not array) due to UNIQUE constraint
|
|
|
setConnectedShops(prev => prev.map(shop => {
|
|
|
if (shop.id === storeId) {
|
|
|
- // Ensure store_sync_config is always an array with proper structure
|
|
|
- const currentConfig = Array.isArray(shop.store_sync_config) && shop.store_sync_config.length > 0
|
|
|
- ? shop.store_sync_config[0]
|
|
|
- : { enabled: false, sync_frequency: 'hourly' };
|
|
|
+ const currentConfig = getSyncConfig(shop) || { enabled: false, sync_frequency: 'hourly' };
|
|
|
|
|
|
return {
|
|
|
...shop,
|
|
|
- store_sync_config: [{
|
|
|
+ store_sync_config: {
|
|
|
...currentConfig,
|
|
|
enabled: newEnabled
|
|
|
- }]
|
|
|
+ }
|
|
|
};
|
|
|
}
|
|
|
return shop;
|
|
|
@@ -404,17 +421,14 @@ export function IntegrationsContent() {
|
|
|
// Revert the optimistic update on error
|
|
|
setConnectedShops(prev => prev.map(shop => {
|
|
|
if (shop.id === storeId) {
|
|
|
- // Ensure store_sync_config is always an array with proper structure
|
|
|
- const existingConfig = Array.isArray(shop.store_sync_config) && shop.store_sync_config.length > 0
|
|
|
- ? shop.store_sync_config[0]
|
|
|
- : { enabled: false, sync_frequency: 'hourly' };
|
|
|
+ const existingConfig = getSyncConfig(shop) || { enabled: false, sync_frequency: 'hourly' };
|
|
|
|
|
|
return {
|
|
|
...shop,
|
|
|
- store_sync_config: [{
|
|
|
+ store_sync_config: {
|
|
|
...existingConfig,
|
|
|
enabled: currentEnabled // Revert to original value
|
|
|
- }]
|
|
|
+ }
|
|
|
};
|
|
|
}
|
|
|
return shop;
|
|
|
@@ -482,7 +496,7 @@ export function IntegrationsContent() {
|
|
|
|
|
|
const getDataAccessBadges = (shop: ConnectedStore) => {
|
|
|
// Use the store_sync_config policies instead of the old data_access_permissions
|
|
|
- const syncConfig = shop.store_sync_config?.[0];
|
|
|
+ const syncConfig = getSyncConfig(shop);
|
|
|
const productsPolicy = syncConfig?.products_access_policy || 'sync';
|
|
|
const customersPolicy = syncConfig?.customers_access_policy || 'api_only';
|
|
|
const ordersPolicy = syncConfig?.orders_access_policy || 'api_only';
|
|
|
@@ -711,7 +725,9 @@ export function IntegrationsContent() {
|
|
|
</Card>
|
|
|
) : (
|
|
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
|
- {connectedShops.map((shop) => (
|
|
|
+ {connectedShops.map((shop) => {
|
|
|
+ const shopSyncConfig = getSyncConfig(shop);
|
|
|
+ return (
|
|
|
<Card key={shop.id} className="bg-slate-800 border-slate-700 hover:border-slate-600 transition-all">
|
|
|
<CardContent className="p-5">
|
|
|
{/* Header Section */}
|
|
|
@@ -770,9 +786,9 @@ export function IntegrationsContent() {
|
|
|
<span className="text-xs text-slate-400">{t('integrations.syncStatus')}</span>
|
|
|
{getSyncStatusBadge(shop)}
|
|
|
</div>
|
|
|
- {shop.store_sync_config?.[0]?.last_sync_at && shop.sync_status === 'completed' && (
|
|
|
+ {shopSyncConfig?.last_sync_at && shop.sync_status === 'completed' && (
|
|
|
<div className="text-xs text-slate-500">
|
|
|
- {t('integrations.lastSync')}: {new Date(shop.store_sync_config[0].last_sync_at).toLocaleString()}
|
|
|
+ {t('integrations.lastSync')}: {new Date(shopSyncConfig.last_sync_at).toLocaleString()}
|
|
|
</div>
|
|
|
)}
|
|
|
{shop.sync_error && (
|
|
|
@@ -792,11 +808,11 @@ export function IntegrationsContent() {
|
|
|
<div className="text-right">
|
|
|
<div className="text-xs text-slate-400 mb-1">{t('integrations.autoSync')}</div>
|
|
|
<Switch
|
|
|
- checked={shop.store_sync_config?.[0]?.enabled ?? false}
|
|
|
+ checked={shopSyncConfig?.enabled ?? false}
|
|
|
onCheckedChange={() => handleToggleStoreEnabled(
|
|
|
shop.id,
|
|
|
shop.store_name || 'this store',
|
|
|
- shop.store_sync_config?.[0]?.enabled ?? false
|
|
|
+ shopSyncConfig?.enabled ?? false
|
|
|
)}
|
|
|
disabled={shop.sync_status === 'syncing'}
|
|
|
title={shop.sync_status === 'syncing' ? 'Cannot change while syncing' : 'Toggle background sync'}
|
|
|
@@ -861,7 +877,7 @@ export function IntegrationsContent() {
|
|
|
</Button>
|
|
|
</CardContent>
|
|
|
</Card>
|
|
|
- ))}
|
|
|
+ );})}
|
|
|
</div>
|
|
|
)}
|
|
|
</div>
|
|
|
@@ -921,7 +937,9 @@ export function IntegrationsContent() {
|
|
|
{selectedStore && t('integrations.dataAccessDialog.description', { storeName: selectedStore.store_name || 'this store' })}
|
|
|
</DialogDescription>
|
|
|
</DialogHeader>
|
|
|
- {selectedStore && (
|
|
|
+ {selectedStore && (() => {
|
|
|
+ const selectedSyncConfig = getSyncConfig(selectedStore);
|
|
|
+ return (
|
|
|
<DataAccessSettings
|
|
|
storeId={selectedStore.id}
|
|
|
storeName={selectedStore.store_name || 'this store'}
|
|
|
@@ -930,30 +948,30 @@ export function IntegrationsContent() {
|
|
|
allow_order_access: true,
|
|
|
allow_product_access: true
|
|
|
}}
|
|
|
- currentPolicies={selectedStore.store_sync_config?.[0] ? {
|
|
|
- products_access_policy: selectedStore.store_sync_config[0].products_access_policy || 'sync',
|
|
|
- customers_access_policy: selectedStore.store_sync_config[0].customers_access_policy || 'api_only',
|
|
|
- orders_access_policy: selectedStore.store_sync_config[0].orders_access_policy || 'api_only'
|
|
|
+ currentPolicies={selectedSyncConfig ? {
|
|
|
+ products_access_policy: selectedSyncConfig.products_access_policy || 'sync',
|
|
|
+ customers_access_policy: selectedSyncConfig.customers_access_policy || 'api_only',
|
|
|
+ orders_access_policy: selectedSyncConfig.orders_access_policy || 'api_only'
|
|
|
} : undefined}
|
|
|
onPermissionsUpdated={handlePermissionsUpdated}
|
|
|
onPoliciesUpdated={(newPolicies) => {
|
|
|
// Update the store in the list with the new policies
|
|
|
- setConnectedShops(prev => prev.map(shop =>
|
|
|
- shop.id === selectedStore?.id
|
|
|
- ? {
|
|
|
- ...shop,
|
|
|
- store_sync_config: [{
|
|
|
- ...(shop.store_sync_config?.[0] || { enabled: true, sync_frequency: 'hourly' }),
|
|
|
- products_access_policy: newPolicies.products_access_policy,
|
|
|
- customers_access_policy: newPolicies.customers_access_policy,
|
|
|
- orders_access_policy: newPolicies.orders_access_policy
|
|
|
- }]
|
|
|
- }
|
|
|
- : shop
|
|
|
- ));
|
|
|
+ setConnectedShops(prev => prev.map(shop => {
|
|
|
+ if (shop.id !== selectedStore?.id) return shop;
|
|
|
+ const currentConfig = getSyncConfig(shop) || { enabled: true, sync_frequency: 'hourly' };
|
|
|
+ return {
|
|
|
+ ...shop,
|
|
|
+ store_sync_config: {
|
|
|
+ ...currentConfig,
|
|
|
+ products_access_policy: newPolicies.products_access_policy,
|
|
|
+ customers_access_policy: newPolicies.customers_access_policy,
|
|
|
+ orders_access_policy: newPolicies.orders_access_policy
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }));
|
|
|
}}
|
|
|
/>
|
|
|
- )}
|
|
|
+ );})()}
|
|
|
<div className="flex justify-end pt-4 border-t border-slate-700">
|
|
|
<Button
|
|
|
variant="outline"
|