fixing issues of integration with wordpress plugin
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* WordPress Integration Form Component
|
||||
* Inline form for WordPress integration with API key generation and plugin download
|
||||
* Simplified - uses only Site.wp_api_key, no SiteIntegration model needed
|
||||
*/
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Card } from '../ui/card';
|
||||
@@ -11,7 +11,6 @@ import Input from '../form/input/InputField';
|
||||
import Checkbox from '../form/input/Checkbox';
|
||||
import Switch from '../form/switch/Switch';
|
||||
import { useToast } from '../ui/toast/ToastContainer';
|
||||
import { integrationApi, SiteIntegration } from '../../services/integration.api';
|
||||
import { fetchAPI, API_BASE_URL } from '../../services/api';
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
@@ -28,18 +27,18 @@ import {
|
||||
|
||||
interface WordPressIntegrationFormProps {
|
||||
siteId: number;
|
||||
integration: SiteIntegration | null;
|
||||
siteName?: string;
|
||||
siteUrl?: string;
|
||||
onIntegrationUpdate?: (integration: SiteIntegration) => void;
|
||||
wpApiKey?: string; // API key from Site.wp_api_key
|
||||
onApiKeyUpdate?: (apiKey: string | null) => void;
|
||||
}
|
||||
|
||||
export default function WordPressIntegrationForm({
|
||||
siteId,
|
||||
integration,
|
||||
siteName,
|
||||
siteUrl,
|
||||
onIntegrationUpdate,
|
||||
wpApiKey,
|
||||
onApiKeyUpdate,
|
||||
}: WordPressIntegrationFormProps) {
|
||||
const toast = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -48,15 +47,20 @@ export default function WordPressIntegrationForm({
|
||||
const [apiKeyVisible, setApiKeyVisible] = useState(false);
|
||||
const [pluginInfo, setPluginInfo] = useState<any>(null);
|
||||
const [loadingPlugin, setLoadingPlugin] = useState(false);
|
||||
|
||||
// Connection status state
|
||||
const [connectionStatus, setConnectionStatus] = useState<'unknown' | 'testing' | 'connected' | 'api_key_pending' | 'plugin_missing' | 'error'>('unknown');
|
||||
const [connectionMessage, setConnectionMessage] = useState<string>('');
|
||||
const [testingConnection, setTestingConnection] = useState(false);
|
||||
|
||||
// Load API key from integration on mount or when integration changes
|
||||
// Load API key from wpApiKey prop (from Site.wp_api_key) on mount or when it changes
|
||||
useEffect(() => {
|
||||
if (integration?.api_key) {
|
||||
setApiKey(integration.api_key);
|
||||
if (wpApiKey) {
|
||||
setApiKey(wpApiKey);
|
||||
} else {
|
||||
setApiKey('');
|
||||
}
|
||||
}, [integration]);
|
||||
}, [wpApiKey]);
|
||||
|
||||
// Fetch plugin information
|
||||
useEffect(() => {
|
||||
@@ -75,11 +79,84 @@ export default function WordPressIntegrationForm({
|
||||
fetchPluginInfo();
|
||||
}, []);
|
||||
|
||||
// Test connection when API key exists
|
||||
const testConnection = async () => {
|
||||
if (!apiKey || !siteUrl) {
|
||||
setConnectionStatus('unknown');
|
||||
setConnectionMessage('API key or site URL missing');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setTestingConnection(true);
|
||||
setConnectionStatus('testing');
|
||||
setConnectionMessage('Testing connection...');
|
||||
|
||||
// Call backend to test connection to WordPress
|
||||
// Backend reads API key from Site.wp_api_key (single source of truth)
|
||||
const response = await fetchAPI('/v1/integration/integrations/test-connection/', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
site_id: siteId,
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
// Check the health checks from response
|
||||
const healthChecks = response.health_checks || {};
|
||||
|
||||
// CRITICAL: api_key_verified confirms WordPress accepts our API key
|
||||
if (healthChecks.api_key_verified) {
|
||||
setConnectionStatus('connected');
|
||||
setConnectionMessage('WordPress is connected and API key verified');
|
||||
toast.success('WordPress connection verified!');
|
||||
} else if (healthChecks.plugin_has_api_key && !healthChecks.api_key_verified) {
|
||||
// WordPress has A key, but it's NOT the same as IGNY8's key
|
||||
setConnectionStatus('api_key_pending');
|
||||
setConnectionMessage('API key mismatch - copy the key from IGNY8 to WordPress plugin');
|
||||
toast.warning('WordPress has different API key. Please update WordPress with the key shown above.');
|
||||
} else if (healthChecks.plugin_installed && !healthChecks.plugin_has_api_key) {
|
||||
setConnectionStatus('api_key_pending');
|
||||
setConnectionMessage('Plugin installed - please add API key in WordPress');
|
||||
toast.warning('Plugin found but API key not configured in WordPress');
|
||||
} else if (!healthChecks.plugin_installed) {
|
||||
setConnectionStatus('plugin_missing');
|
||||
setConnectionMessage('IGNY8 plugin not installed on WordPress site');
|
||||
toast.warning('WordPress site reachable but plugin not found');
|
||||
} else {
|
||||
setConnectionStatus('error');
|
||||
setConnectionMessage(response.message || 'Connection verification incomplete');
|
||||
toast.error(response.message || 'Connection test incomplete');
|
||||
}
|
||||
} else {
|
||||
setConnectionStatus('error');
|
||||
setConnectionMessage(response.message || 'Connection test failed');
|
||||
toast.error(response.message || 'Connection test failed');
|
||||
}
|
||||
} catch (error: any) {
|
||||
setConnectionStatus('error');
|
||||
setConnectionMessage(error.message || 'Connection test failed');
|
||||
toast.error(`Connection test failed: ${error.message}`);
|
||||
} finally {
|
||||
setTestingConnection(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Auto-test connection when API key changes
|
||||
useEffect(() => {
|
||||
if (apiKey && siteUrl) {
|
||||
testConnection();
|
||||
} else {
|
||||
setConnectionStatus('unknown');
|
||||
setConnectionMessage('');
|
||||
}
|
||||
}, [apiKey, siteUrl]);
|
||||
|
||||
const handleGenerateApiKey = async () => {
|
||||
try {
|
||||
setGeneratingKey(true);
|
||||
|
||||
// Call the new generate-api-key endpoint
|
||||
// Call the simplified generate-api-key endpoint
|
||||
const response = await fetchAPI('/v1/integration/integrations/generate-api-key/', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ site_id: siteId }),
|
||||
@@ -89,9 +166,9 @@ export default function WordPressIntegrationForm({
|
||||
setApiKey(newKey);
|
||||
setApiKeyVisible(true);
|
||||
|
||||
// Trigger integration update
|
||||
if (onIntegrationUpdate && response.integration) {
|
||||
onIntegrationUpdate(response.integration);
|
||||
// Notify parent component
|
||||
if (onApiKeyUpdate) {
|
||||
onApiKeyUpdate(newKey);
|
||||
}
|
||||
|
||||
toast.success('API key generated successfully');
|
||||
@@ -119,9 +196,9 @@ export default function WordPressIntegrationForm({
|
||||
setApiKey(newKey);
|
||||
setApiKeyVisible(true);
|
||||
|
||||
// Trigger integration update
|
||||
if (onIntegrationUpdate && response.integration) {
|
||||
onIntegrationUpdate(response.integration);
|
||||
// Notify parent component
|
||||
if (onApiKeyUpdate) {
|
||||
onApiKeyUpdate(newKey);
|
||||
}
|
||||
|
||||
toast.success('API key regenerated successfully');
|
||||
@@ -139,20 +216,20 @@ export default function WordPressIntegrationForm({
|
||||
try {
|
||||
setGeneratingKey(true);
|
||||
|
||||
if (!integration) {
|
||||
toast.error('No integration found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the integration to revoke the API key
|
||||
await integrationApi.deleteIntegration(integration.id);
|
||||
// Revoke API key via dedicated endpoint (single source of truth: Site.wp_api_key)
|
||||
await fetchAPI('/v1/integration/integrations/revoke-api-key/', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ site_id: siteId }),
|
||||
});
|
||||
|
||||
setApiKey('');
|
||||
setApiKeyVisible(false);
|
||||
setConnectionStatus('unknown');
|
||||
setConnectionMessage('');
|
||||
|
||||
// Trigger integration update
|
||||
if (onIntegrationUpdate) {
|
||||
onIntegrationUpdate(null as any);
|
||||
// Notify parent component
|
||||
if (onApiKeyUpdate) {
|
||||
onApiKeyUpdate(null);
|
||||
}
|
||||
|
||||
toast.success('API key revoked successfully');
|
||||
@@ -183,47 +260,9 @@ export default function WordPressIntegrationForm({
|
||||
return key.substring(0, 8) + '**********' + key.substring(key.length - 4);
|
||||
};
|
||||
|
||||
// Toggle integration sync enabled status (not creation - that happens automatically)
|
||||
const [integrationEnabled, setIntegrationEnabled] = useState(integration?.sync_enabled ?? false);
|
||||
|
||||
const handleToggleIntegration = async (enabled: boolean) => {
|
||||
try {
|
||||
setIntegrationEnabled(enabled);
|
||||
|
||||
if (integration) {
|
||||
// Update existing integration - only toggle sync_enabled, not creation
|
||||
await integrationApi.updateIntegration(integration.id, {
|
||||
sync_enabled: enabled,
|
||||
} as any);
|
||||
toast.success(enabled ? 'Sync enabled' : 'Sync disabled');
|
||||
|
||||
// Reload integration
|
||||
const updated = await integrationApi.getWordPressIntegration(siteId);
|
||||
if (onIntegrationUpdate && updated) {
|
||||
onIntegrationUpdate(updated);
|
||||
}
|
||||
} else {
|
||||
// Integration doesn't exist - it should be created automatically by plugin
|
||||
// when user connects from WordPress side
|
||||
toast.info('Integration will be created automatically when you connect from WordPress plugin. Please connect from the plugin first.');
|
||||
setIntegrationEnabled(false);
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(`Failed to update integration: ${error.message}`);
|
||||
// Revert on error
|
||||
setIntegrationEnabled(!enabled);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (integration) {
|
||||
setIntegrationEnabled(integration.sync_enabled ?? false);
|
||||
}
|
||||
}, [integration]);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header with Toggle */}
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-3 bg-purple-100 dark:bg-purple-900/30 rounded-lg">
|
||||
@@ -239,13 +278,60 @@ export default function WordPressIntegrationForm({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Toggle Switch */}
|
||||
{/* Connection Status */}
|
||||
{apiKey && (
|
||||
<Switch
|
||||
label={integrationEnabled ? 'Sync Enabled' : 'Sync Disabled'}
|
||||
checked={integrationEnabled}
|
||||
onChange={(checked) => handleToggleIntegration(checked)}
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Status Badge */}
|
||||
<div className={`flex items-center gap-2 px-3 py-1.5 rounded-lg border ${
|
||||
connectionStatus === 'connected'
|
||||
? 'bg-green-50 dark:bg-green-900/20 border-green-200 dark:border-green-800'
|
||||
: connectionStatus === 'testing'
|
||||
? 'bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800'
|
||||
: connectionStatus === 'api_key_pending'
|
||||
? 'bg-amber-50 dark:bg-amber-900/20 border-amber-200 dark:border-amber-800'
|
||||
: connectionStatus === 'plugin_missing'
|
||||
? 'bg-amber-50 dark:bg-amber-900/20 border-amber-200 dark:border-amber-800'
|
||||
: connectionStatus === 'error'
|
||||
? 'bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800'
|
||||
: 'bg-gray-50 dark:bg-gray-800/50 border-gray-200 dark:border-gray-700'
|
||||
}`}>
|
||||
{connectionStatus === 'connected' && (
|
||||
<><CheckCircleIcon className="w-4 h-4 text-green-600 dark:text-green-400" />
|
||||
<span className="text-sm font-medium text-green-700 dark:text-green-300">Connected</span></>
|
||||
)}
|
||||
{connectionStatus === 'testing' && (
|
||||
<><RefreshCwIcon className="w-4 h-4 text-blue-600 dark:text-blue-400 animate-spin" />
|
||||
<span className="text-sm font-medium text-blue-700 dark:text-blue-300">Testing...</span></>
|
||||
)}
|
||||
{connectionStatus === 'api_key_pending' && (
|
||||
<><AlertIcon className="w-4 h-4 text-amber-600 dark:text-amber-400" />
|
||||
<span className="text-sm font-medium text-amber-700 dark:text-amber-300">Pending Setup</span></>
|
||||
)}
|
||||
{connectionStatus === 'plugin_missing' && (
|
||||
<><AlertIcon className="w-4 h-4 text-amber-600 dark:text-amber-400" />
|
||||
<span className="text-sm font-medium text-amber-700 dark:text-amber-300">Plugin Missing</span></>
|
||||
)}
|
||||
{connectionStatus === 'error' && (
|
||||
<><AlertIcon className="w-4 h-4 text-red-600 dark:text-red-400" />
|
||||
<span className="text-sm font-medium text-red-700 dark:text-red-300">Error</span></>
|
||||
)}
|
||||
{connectionStatus === 'unknown' && (
|
||||
<><InfoIcon className="w-4 h-4 text-gray-500 dark:text-gray-400" />
|
||||
<span className="text-sm font-medium text-gray-600 dark:text-gray-400">Not Tested</span></>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Test Connection Button */}
|
||||
<Button
|
||||
onClick={testConnection}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={testingConnection || !apiKey}
|
||||
startIcon={<RefreshCwIcon className={`w-4 h-4 ${testingConnection ? 'animate-spin' : ''}`} />}
|
||||
>
|
||||
Test
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -264,18 +350,9 @@ export default function WordPressIntegrationForm({
|
||||
onClick={handleGenerateApiKey}
|
||||
variant="solid"
|
||||
disabled={generatingKey}
|
||||
startIcon={generatingKey ? <RefreshCwIcon className="w-4 h-4 animate-spin" /> : <PlusIcon className="w-4 h-4" />}
|
||||
>
|
||||
{generatingKey ? (
|
||||
<>
|
||||
<RefreshCwIcon className="w-4 h-4 mr-2 animate-spin" />
|
||||
Generating...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<PlusIcon className="w-4 h-4 mr-2" />
|
||||
Add API Key
|
||||
</>
|
||||
)}
|
||||
{generatingKey ? 'Generating...' : 'Add API Key'}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -306,6 +383,7 @@ export default function WordPressIntegrationForm({
|
||||
readOnly
|
||||
type={apiKeyVisible ? 'text' : 'password'}
|
||||
value={apiKeyVisible ? apiKey : maskApiKey(apiKey)}
|
||||
onChange={() => {}} // No-op to satisfy React
|
||||
/>
|
||||
<IconButton
|
||||
onClick={handleCopyApiKey}
|
||||
|
||||
Reference in New Issue
Block a user