fixes
This commit is contained in:
@@ -44,16 +44,12 @@ class CreditUsageLogSerializer(serializers.ModelSerializer):
|
|||||||
read_only_fields = ['created_at', 'account']
|
read_only_fields = ['created_at', 'account']
|
||||||
|
|
||||||
def get_client_cost(self, obj) -> str:
|
def get_client_cost(self, obj) -> str:
|
||||||
"""Calculate client-facing cost from credits * default_credit_price_usd"""
|
"""Calculate client-facing cost from credits * default_credit_price_usd (price per credit)"""
|
||||||
from igny8_core.business.billing.models import BillingConfiguration
|
from igny8_core.business.billing.models import BillingConfiguration
|
||||||
try:
|
|
||||||
config = BillingConfiguration.get_config()
|
config = BillingConfiguration.get_config()
|
||||||
price_per_credit = config.default_credit_price_usd
|
price_per_credit = config.default_credit_price_usd
|
||||||
client_cost = Decimal(obj.credits_used) * price_per_credit
|
client_cost = Decimal(obj.credits_used) * price_per_credit
|
||||||
return str(client_cost.quantize(Decimal('0.0001')))
|
return str(client_cost.quantize(Decimal('0.0001')))
|
||||||
except Exception:
|
|
||||||
# Fallback to cost_usd if billing config unavailable
|
|
||||||
return str(obj.cost_usd) if obj.cost_usd else '0.0000'
|
|
||||||
|
|
||||||
def get_site_name(self, obj) -> Optional[str]:
|
def get_site_name(self, obj) -> Optional[str]:
|
||||||
"""Get the site name if available"""
|
"""Get the site name if available"""
|
||||||
|
|||||||
@@ -6,23 +6,17 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import { Card } from '../ui/card';
|
import { Card } from '../ui/card';
|
||||||
import Button from '../ui/button/Button';
|
import Button from '../ui/button/Button';
|
||||||
import IconButton from '../ui/button/IconButton';
|
import IconButton from '../ui/button/IconButton';
|
||||||
import Label from '../form/Label';
|
|
||||||
import Input from '../form/input/InputField';
|
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 { useToast } from '../ui/toast/ToastContainer';
|
||||||
import { fetchAPI, API_BASE_URL } from '../../services/api';
|
import { fetchAPI, API_BASE_URL } from '../../services/api';
|
||||||
import {
|
import {
|
||||||
CheckCircleIcon,
|
|
||||||
AlertIcon,
|
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
CopyIcon,
|
CopyIcon,
|
||||||
TrashBinIcon,
|
TrashBinIcon,
|
||||||
GlobeIcon,
|
GlobeIcon,
|
||||||
KeyIcon,
|
|
||||||
RefreshCwIcon,
|
RefreshCwIcon,
|
||||||
InfoIcon
|
InfoIcon,
|
||||||
} from '../../icons';
|
} from '../../icons';
|
||||||
|
|
||||||
interface WordPressIntegrationFormProps {
|
interface WordPressIntegrationFormProps {
|
||||||
@@ -283,56 +277,40 @@ export default function WordPressIntegrationForm({
|
|||||||
|
|
||||||
{/* Connection Status & Test Button */}
|
{/* Connection Status & Test Button */}
|
||||||
{apiKey && (
|
{apiKey && (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex flex-col items-end gap-2">
|
||||||
{/* Status Indicator - Uses theme colors from design-system */}
|
{/* Connection Status Indicator */}
|
||||||
<div className={`flex items-center gap-2 px-3 py-1.5 rounded-lg border ${
|
<div className="flex items-center gap-3 px-4 py-2 rounded-lg bg-gray-50 dark:bg-gray-800">
|
||||||
connectionStatus === 'connected'
|
<span
|
||||||
? 'bg-success-50 dark:bg-success-900/20 border-success-200 dark:border-success-800'
|
className={`inline-block w-4 h-4 rounded-full ${
|
||||||
: connectionStatus === 'testing'
|
connectionStatus === 'connected' ? 'bg-success-500' :
|
||||||
? 'bg-brand-50 dark:bg-brand-900/20 border-brand-200 dark:border-brand-800'
|
connectionStatus === 'testing' ? 'bg-brand-500 animate-pulse' :
|
||||||
: connectionStatus === 'api_key_pending'
|
connectionStatus === 'api_key_pending' ? 'bg-warning-500' :
|
||||||
? 'bg-warning-50 dark:bg-warning-900/20 border-warning-200 dark:border-warning-800'
|
connectionStatus === 'plugin_missing' ? 'bg-warning-500' :
|
||||||
: connectionStatus === 'plugin_missing'
|
connectionStatus === 'error' ? 'bg-error-500' :
|
||||||
? 'bg-warning-50 dark:bg-warning-900/20 border-warning-200 dark:border-warning-800'
|
'bg-brand-500'
|
||||||
: connectionStatus === 'error'
|
}`}
|
||||||
? 'bg-error-50 dark:bg-error-900/20 border-error-200 dark:border-error-800'
|
/>
|
||||||
: 'bg-gray-50 dark:bg-gray-800/50 border-gray-200 dark:border-gray-700'
|
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
||||||
}`}>
|
{connectionStatus === 'connected' && 'Connected'}
|
||||||
{connectionStatus === 'connected' && (
|
{connectionStatus === 'testing' && 'Testing...'}
|
||||||
<><CheckCircleIcon className="w-4 h-4 text-success-600 dark:text-success-400" />
|
{connectionStatus === 'api_key_pending' && 'Pending Setup'}
|
||||||
<span className="text-sm font-medium text-success-700 dark:text-success-300">Connected</span></>
|
{connectionStatus === 'plugin_missing' && 'Plugin Missing'}
|
||||||
)}
|
{connectionStatus === 'error' && 'Error'}
|
||||||
{connectionStatus === 'testing' && (
|
{connectionStatus === 'unknown' && 'Configured'}
|
||||||
<><RefreshCwIcon className="w-4 h-4 text-brand-600 dark:text-brand-400 animate-spin" />
|
</span>
|
||||||
<span className="text-sm font-medium text-brand-700 dark:text-brand-300">Testing...</span></>
|
|
||||||
)}
|
|
||||||
{connectionStatus === 'api_key_pending' && (
|
|
||||||
<><AlertIcon className="w-4 h-4 text-warning-600 dark:text-warning-400" />
|
|
||||||
<span className="text-sm font-medium text-warning-700 dark:text-warning-300">Pending Setup</span></>
|
|
||||||
)}
|
|
||||||
{connectionStatus === 'plugin_missing' && (
|
|
||||||
<><AlertIcon className="w-4 h-4 text-warning-600 dark:text-warning-400" />
|
|
||||||
<span className="text-sm font-medium text-warning-700 dark:text-warning-300">Plugin Missing</span></>
|
|
||||||
)}
|
|
||||||
{connectionStatus === 'error' && (
|
|
||||||
<><AlertIcon className="w-4 h-4 text-error-600 dark:text-error-400" />
|
|
||||||
<span className="text-sm font-medium text-error-700 dark:text-error-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>
|
</div>
|
||||||
|
|
||||||
{/* Test Connection Button - IconButton only */}
|
{/* Test Connection Button */}
|
||||||
<IconButton
|
<Button
|
||||||
onClick={testConnection}
|
onClick={testConnection}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
tone="brand"
|
tone="brand"
|
||||||
|
size="sm"
|
||||||
disabled={testingConnection || !apiKey}
|
disabled={testingConnection || !apiKey}
|
||||||
title="Test Connection"
|
startIcon={<RefreshCwIcon className={`w-4 h-4 ${testingConnection ? 'animate-spin' : ''}`} />}
|
||||||
icon={<RefreshCwIcon className={`w-4 h-4 ${testingConnection ? 'animate-spin' : ''}`} />}
|
>
|
||||||
/>
|
Test Connection
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -499,19 +499,6 @@ export default function SiteSettings() {
|
|||||||
return `${months}mo ago`;
|
return `${months}mo ago`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Integration status - tracks actual connection state
|
|
||||||
const [integrationStatus, setIntegrationStatus] = useState<'connected' | 'configured' | 'not_configured'>('not_configured');
|
|
||||||
|
|
||||||
// Check integration status based on API key presence (will be updated by WordPressIntegrationForm)
|
|
||||||
useEffect(() => {
|
|
||||||
if (site?.wp_api_key) {
|
|
||||||
// API key exists - mark as configured (actual connection tested in WordPressIntegrationForm)
|
|
||||||
setIntegrationStatus('configured');
|
|
||||||
} else {
|
|
||||||
setIntegrationStatus('not_configured');
|
|
||||||
}
|
|
||||||
}, [site?.wp_api_key]);
|
|
||||||
|
|
||||||
// Sync Now handler - tests actual WordPress connection
|
// Sync Now handler - tests actual WordPress connection
|
||||||
const [syncLoading, setSyncLoading] = useState(false);
|
const [syncLoading, setSyncLoading] = useState(false);
|
||||||
const [lastSyncTime, setLastSyncTime] = useState<string | null>(null);
|
const [lastSyncTime, setLastSyncTime] = useState<string | null>(null);
|
||||||
@@ -535,10 +522,8 @@ export default function SiteSettings() {
|
|||||||
const healthChecks = res.health_checks || {};
|
const healthChecks = res.health_checks || {};
|
||||||
|
|
||||||
if (healthChecks.plugin_has_api_key) {
|
if (healthChecks.plugin_has_api_key) {
|
||||||
setIntegrationStatus('connected');
|
|
||||||
toast.success('WordPress connection verified - fully connected!');
|
toast.success('WordPress connection verified - fully connected!');
|
||||||
} else if (healthChecks.plugin_installed) {
|
} else if (healthChecks.plugin_installed) {
|
||||||
setIntegrationStatus('configured');
|
|
||||||
toast.warning('Plugin found but API key not configured in WordPress');
|
toast.warning('Plugin found but API key not configured in WordPress');
|
||||||
} else {
|
} else {
|
||||||
toast.warning('WordPress reachable but IGNY8 plugin not installed');
|
toast.warning('WordPress reachable but IGNY8 plugin not installed');
|
||||||
@@ -684,21 +669,6 @@ export default function SiteSettings() {
|
|||||||
Publishing
|
Publishing
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Integration Status Indicator - Larger */}
|
|
||||||
<div className="flex items-center gap-3 px-4 py-2 rounded-lg bg-gray-50 dark:bg-gray-800 flex-shrink-0">
|
|
||||||
<span
|
|
||||||
className={`inline-block w-3 h-3 rounded-full ${
|
|
||||||
integrationStatus === 'connected' ? 'bg-success-500' :
|
|
||||||
integrationStatus === 'configured' ? 'bg-brand-500' : 'bg-gray-300'
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
|
||||||
{integrationStatus === 'connected' && 'Connected'}
|
|
||||||
{integrationStatus === 'configured' && 'Configured'}
|
|
||||||
{integrationStatus === 'not_configured' && 'Not Configured'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user