fixing issues of integration with wordpress plugin

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-12 23:25:47 +00:00
parent ad828a9fcd
commit 5c3aa90e91
18 changed files with 1414 additions and 427 deletions

View File

@@ -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>

View File

@@ -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);
}
}}