fixing issues of integration with wordpress plugin
This commit is contained in:
@@ -29,7 +29,6 @@ import {
|
||||
} from '../../services/api';
|
||||
import { useSiteStore } from '../../store/siteStore';
|
||||
import WordPressIntegrationForm from '../../components/sites/WordPressIntegrationForm';
|
||||
import { integrationApi, SiteIntegration } from '../../services/integration.api';
|
||||
import { GridIcon, PlugInIcon, PaperPlaneIcon, DocsIcon, BoltIcon, FileIcon, ChevronDownIcon, CloseIcon, PlusIcon, RefreshCwIcon, FileTextIcon, ImageIcon, SaveIcon, Loader2Icon, ArrowRightIcon, SettingsIcon, GlobeIcon, LayersIcon, CheckCircleIcon, CalendarIcon, InfoIcon } from '../../icons';
|
||||
import Badge from '../../components/ui/badge/Badge';
|
||||
import { Dropdown } from '../../components/ui/dropdown/Dropdown';
|
||||
@@ -45,8 +44,6 @@ export default function SiteSettings() {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [site, setSite] = useState<any>(null);
|
||||
const [wordPressIntegration, setWordPressIntegration] = useState<SiteIntegration | null>(null);
|
||||
const [integrationLoading, setIntegrationLoading] = useState(false);
|
||||
|
||||
// Site selector state
|
||||
const [sites, setSites] = useState<Site[]>([]);
|
||||
@@ -134,12 +131,10 @@ export default function SiteSettings() {
|
||||
useEffect(() => {
|
||||
if (siteId) {
|
||||
// Clear state when site changes
|
||||
setWordPressIntegration(null);
|
||||
setSite(null);
|
||||
|
||||
// Load new site data
|
||||
loadSite();
|
||||
loadIntegrations();
|
||||
loadIndustries();
|
||||
}
|
||||
}, [siteId]);
|
||||
@@ -248,17 +243,10 @@ export default function SiteSettings() {
|
||||
}
|
||||
};
|
||||
|
||||
const loadIntegrations = async () => {
|
||||
if (!siteId) return;
|
||||
try {
|
||||
setIntegrationLoading(true);
|
||||
const integration = await integrationApi.getWordPressIntegration(Number(siteId));
|
||||
setWordPressIntegration(integration);
|
||||
} catch (error: any) {
|
||||
// Integration might not exist, that's okay
|
||||
setWordPressIntegration(null);
|
||||
} finally {
|
||||
setIntegrationLoading(false);
|
||||
const handleApiKeyUpdate = (newApiKey: string | null) => {
|
||||
// Update site state with new API key
|
||||
if (site) {
|
||||
setSite({ ...site, wp_api_key: newApiKey });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -495,11 +483,6 @@ export default function SiteSettings() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleIntegrationUpdate = async (integration: SiteIntegration) => {
|
||||
setWordPressIntegration(integration);
|
||||
await loadIntegrations();
|
||||
};
|
||||
|
||||
const formatRelativeTime = (iso: string | null) => {
|
||||
if (!iso) return '-';
|
||||
const then = new Date(iso).getTime();
|
||||
@@ -516,83 +499,56 @@ export default function SiteSettings() {
|
||||
return `${months}mo ago`;
|
||||
};
|
||||
|
||||
// Integration status with authentication check
|
||||
// Integration status - tracks actual connection state
|
||||
const [integrationStatus, setIntegrationStatus] = useState<'connected' | 'configured' | 'not_configured'>('not_configured');
|
||||
const [testingAuth, setTestingAuth] = useState(false);
|
||||
|
||||
// Check basic configuration - integration must exist in DB and have sync_enabled
|
||||
// Check integration status based on API key presence (will be updated by WordPressIntegrationForm)
|
||||
useEffect(() => {
|
||||
const checkStatus = async () => {
|
||||
// Integration must exist in database and have sync_enabled = true
|
||||
if (wordPressIntegration && wordPressIntegration.id && wordPressIntegration.sync_enabled) {
|
||||
setIntegrationStatus('configured');
|
||||
// Test authentication
|
||||
testAuthentication();
|
||||
} else {
|
||||
setIntegrationStatus('not_configured');
|
||||
}
|
||||
};
|
||||
checkStatus();
|
||||
}, [wordPressIntegration, site]);
|
||||
|
||||
// Auto-refresh integration list periodically to detect plugin-created integrations
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
if (!wordPressIntegration) {
|
||||
loadIntegrations();
|
||||
}
|
||||
}, 5000); // Check every 5 seconds if integration doesn't exist
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [wordPressIntegration]);
|
||||
|
||||
// Test authentication with WordPress API
|
||||
const testAuthentication = async () => {
|
||||
if (testingAuth || !wordPressIntegration?.id) return;
|
||||
|
||||
try {
|
||||
setTestingAuth(true);
|
||||
const resp = await fetchAPI(`/v1/integration/integrations/${wordPressIntegration.id}/test_connection/`, {
|
||||
method: 'POST',
|
||||
body: {}
|
||||
});
|
||||
|
||||
if (resp && resp.success) {
|
||||
setIntegrationStatus('connected');
|
||||
} else {
|
||||
// Keep as 'configured' if auth fails
|
||||
setIntegrationStatus('configured');
|
||||
}
|
||||
} catch (err) {
|
||||
// Keep as 'configured' if auth test fails
|
||||
if (site?.wp_api_key) {
|
||||
// API key exists - mark as configured (actual connection tested in WordPressIntegrationForm)
|
||||
setIntegrationStatus('configured');
|
||||
} finally {
|
||||
setTestingAuth(false);
|
||||
} else {
|
||||
setIntegrationStatus('not_configured');
|
||||
}
|
||||
};
|
||||
}, [site?.wp_api_key]);
|
||||
|
||||
// Sync Now handler extracted
|
||||
// Sync Now handler - tests actual WordPress connection
|
||||
const [syncLoading, setSyncLoading] = useState(false);
|
||||
const [lastSyncTime, setLastSyncTime] = useState<string | null>(null);
|
||||
const handleManualSync = async () => {
|
||||
if (!site?.wp_api_key) {
|
||||
toast.error('WordPress API key not configured. Please generate an API key first.');
|
||||
return;
|
||||
}
|
||||
setSyncLoading(true);
|
||||
try {
|
||||
if (wordPressIntegration && wordPressIntegration.id) {
|
||||
const res = await integrationApi.syncIntegration(wordPressIntegration.id, 'metadata');
|
||||
if (res && res.success) {
|
||||
toast.success('WordPress structure synced successfully');
|
||||
if (res.last_sync_at) {
|
||||
setLastSyncTime(res.last_sync_at);
|
||||
}
|
||||
setTimeout(() => loadContentTypes(), 1500);
|
||||
// Test connection to WordPress using backend test endpoint
|
||||
// Backend reads API key from Site.wp_api_key (single source of truth)
|
||||
const res = await fetchAPI('/v1/integration/integrations/test-connection/', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
site_id: siteId,
|
||||
}),
|
||||
});
|
||||
if (res && res.success) {
|
||||
// Check health checks
|
||||
const healthChecks = res.health_checks || {};
|
||||
|
||||
if (healthChecks.plugin_has_api_key) {
|
||||
setIntegrationStatus('connected');
|
||||
toast.success('WordPress connection verified - fully connected!');
|
||||
} else if (healthChecks.plugin_installed) {
|
||||
setIntegrationStatus('configured');
|
||||
toast.warning('Plugin found but API key not configured in WordPress');
|
||||
} else {
|
||||
toast.error(res?.message || 'Sync failed to start');
|
||||
toast.warning('WordPress reachable but IGNY8 plugin not installed');
|
||||
}
|
||||
setLastSyncTime(new Date().toISOString());
|
||||
} else {
|
||||
toast.error('No integration configured. Please configure WordPress integration first.');
|
||||
toast.error(res?.message || 'Connection test failed');
|
||||
}
|
||||
} catch (err: any) {
|
||||
toast.error(`Sync failed: ${err?.message || String(err)}`);
|
||||
toast.error(`Connection test failed: ${err?.message || String(err)}`);
|
||||
} finally {
|
||||
setSyncLoading(false);
|
||||
}
|
||||
@@ -739,7 +695,7 @@ export default function SiteSettings() {
|
||||
/>
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
||||
{integrationStatus === 'connected' && 'Connected'}
|
||||
{integrationStatus === 'configured' && (testingAuth ? 'Testing...' : 'Configured')}
|
||||
{integrationStatus === 'configured' && 'Configured'}
|
||||
{integrationStatus === 'not_configured' && 'Not Configured'}
|
||||
</span>
|
||||
</div>
|
||||
@@ -1874,10 +1830,10 @@ export default function SiteSettings() {
|
||||
{activeTab === 'integrations' && siteId && (
|
||||
<WordPressIntegrationForm
|
||||
siteId={Number(siteId)}
|
||||
integration={wordPressIntegration}
|
||||
siteName={site?.name}
|
||||
siteUrl={site?.domain || site?.wp_url}
|
||||
onIntegrationUpdate={handleIntegrationUpdate}
|
||||
wpApiKey={site?.wp_api_key}
|
||||
onApiKeyUpdate={handleApiKeyUpdate}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
ContentListResponse,
|
||||
ContentFilters,
|
||||
fetchAPI,
|
||||
fetchWordPressStatus,
|
||||
deleteContent,
|
||||
bulkDeleteContent,
|
||||
} from '../../services/api';
|
||||
@@ -46,9 +45,12 @@ export default function Approved() {
|
||||
const [totalPublished, setTotalPublished] = useState(0);
|
||||
const [totalImagesCount, setTotalImagesCount] = useState(0);
|
||||
|
||||
// Filter state - default to approved status
|
||||
// Filter state
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [publishStatusFilter, setPublishStatusFilter] = useState('');
|
||||
const [statusFilter, setStatusFilter] = useState(''); // Status filter (draft/review/approved/published)
|
||||
const [siteStatusFilter, setSiteStatusFilter] = useState(''); // Site status filter (not_published/scheduled/published/failed)
|
||||
const [contentTypeFilter, setContentTypeFilter] = useState(''); // Content type filter (post/page/product/taxonomy)
|
||||
const [contentStructureFilter, setContentStructureFilter] = useState(''); // Content structure filter
|
||||
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
||||
|
||||
// Pagination state
|
||||
@@ -99,7 +101,10 @@ export default function Approved() {
|
||||
|
||||
const filters: ContentFilters = {
|
||||
...(searchTerm && { search: searchTerm }),
|
||||
status__in: 'approved,published', // Both approved and published content
|
||||
// Default to approved+published if no status filter selected
|
||||
...(statusFilter ? { status: statusFilter } : { status__in: 'approved,published' }),
|
||||
...(contentTypeFilter && { content_type: contentTypeFilter }),
|
||||
...(contentStructureFilter && { content_structure: contentStructureFilter }),
|
||||
page: currentPage,
|
||||
page_size: pageSize,
|
||||
ordering,
|
||||
@@ -107,34 +112,13 @@ export default function Approved() {
|
||||
|
||||
const data: ContentListResponse = await fetchContent(filters);
|
||||
|
||||
// Client-side filter for WordPress publish status if needed
|
||||
// Client-side filter for site_status if needed (backend may not support this filter yet)
|
||||
let filteredResults = data.results || [];
|
||||
if (publishStatusFilter === 'published') {
|
||||
filteredResults = filteredResults.filter(c => c.external_id);
|
||||
} else if (publishStatusFilter === 'not_published') {
|
||||
filteredResults = filteredResults.filter(c => !c.external_id);
|
||||
if (siteStatusFilter) {
|
||||
filteredResults = filteredResults.filter(c => c.site_status === siteStatusFilter);
|
||||
}
|
||||
|
||||
// Fetch WordPress status for published content
|
||||
const resultsWithWPStatus = await Promise.all(
|
||||
filteredResults.map(async (content) => {
|
||||
if (content.external_id) {
|
||||
try {
|
||||
const wpStatus = await fetchWordPressStatus(content.id);
|
||||
return {
|
||||
...content,
|
||||
wordpress_status: wpStatus.wordpress_status,
|
||||
};
|
||||
} catch (error) {
|
||||
console.warn(`Failed to fetch WP status for content ${content.id}:`, error);
|
||||
return content;
|
||||
}
|
||||
}
|
||||
return content;
|
||||
})
|
||||
);
|
||||
|
||||
setContent(resultsWithWPStatus);
|
||||
setContent(filteredResults);
|
||||
setTotalCount(data.count || 0);
|
||||
setTotalPages(Math.ceil((data.count || 0) / pageSize));
|
||||
|
||||
@@ -148,7 +132,7 @@ export default function Approved() {
|
||||
setShowContent(true);
|
||||
setLoading(false);
|
||||
}
|
||||
}, [currentPage, publishStatusFilter, sortBy, sortDirection, searchTerm, pageSize, toast]);
|
||||
}, [currentPage, statusFilter, siteStatusFilter, contentTypeFilter, contentStructureFilter, sortBy, sortDirection, searchTerm, pageSize, toast]);
|
||||
|
||||
useEffect(() => {
|
||||
loadContent();
|
||||
@@ -326,15 +310,17 @@ export default function Approved() {
|
||||
return createApprovedPageConfig({
|
||||
searchTerm,
|
||||
setSearchTerm,
|
||||
publishStatusFilter,
|
||||
setPublishStatusFilter,
|
||||
statusFilter,
|
||||
setStatusFilter,
|
||||
siteStatusFilter,
|
||||
setSiteStatusFilter,
|
||||
setCurrentPage,
|
||||
activeSector,
|
||||
onRowClick: (row: Content) => {
|
||||
navigate(`/writer/content/${row.id}`);
|
||||
},
|
||||
});
|
||||
}, [searchTerm, publishStatusFilter, activeSector, navigate]);
|
||||
}, [searchTerm, statusFilter, siteStatusFilter, contentTypeFilter, contentStructureFilter, activeSector, navigate]);
|
||||
|
||||
// Calculate header metrics - use totals from API calls (not page data)
|
||||
// This ensures metrics show correct totals across all pages, not just current page
|
||||
@@ -392,7 +378,10 @@ export default function Approved() {
|
||||
filters={pageConfig.filters}
|
||||
filterValues={{
|
||||
search: searchTerm,
|
||||
publishStatus: publishStatusFilter,
|
||||
status: statusFilter,
|
||||
site_status: siteStatusFilter,
|
||||
content_type: contentTypeFilter,
|
||||
content_structure: contentStructureFilter,
|
||||
}}
|
||||
primaryAction={{
|
||||
label: 'Publish to Site',
|
||||
@@ -403,8 +392,17 @@ export default function Approved() {
|
||||
onFilterChange={(key: string, value: any) => {
|
||||
if (key === 'search') {
|
||||
setSearchTerm(value);
|
||||
} else if (key === 'publishStatus') {
|
||||
setPublishStatusFilter(value);
|
||||
} else if (key === 'status') {
|
||||
setStatusFilter(value);
|
||||
setCurrentPage(1);
|
||||
} else if (key === 'site_status') {
|
||||
setSiteStatusFilter(value);
|
||||
setCurrentPage(1);
|
||||
} else if (key === 'content_type') {
|
||||
setContentTypeFilter(value);
|
||||
setCurrentPage(1);
|
||||
} else if (key === 'content_structure') {
|
||||
setContentStructureFilter(value);
|
||||
setCurrentPage(1);
|
||||
}
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user