This commit is contained in:
IGNY8 VPS (Salman)
2026-01-13 01:52:40 +00:00
parent 47a5a8b1da
commit 5ce0d02636
3 changed files with 34 additions and 90 deletions

View File

@@ -44,16 +44,12 @@ class CreditUsageLogSerializer(serializers.ModelSerializer):
read_only_fields = ['created_at', 'account']
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
try:
config = BillingConfiguration.get_config()
price_per_credit = config.default_credit_price_usd
client_cost = Decimal(obj.credits_used) * price_per_credit
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'
config = BillingConfiguration.get_config()
price_per_credit = config.default_credit_price_usd
client_cost = Decimal(obj.credits_used) * price_per_credit
return str(client_cost.quantize(Decimal('0.0001')))
def get_site_name(self, obj) -> Optional[str]:
"""Get the site name if available"""

View File

@@ -6,23 +6,17 @@ import React, { useState, useEffect } from 'react';
import { Card } from '../ui/card';
import Button from '../ui/button/Button';
import IconButton from '../ui/button/IconButton';
import Label from '../form/Label';
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 { fetchAPI, API_BASE_URL } from '../../services/api';
import {
CheckCircleIcon,
AlertIcon,
DownloadIcon,
PlusIcon,
CopyIcon,
TrashBinIcon,
GlobeIcon,
KeyIcon,
RefreshCwIcon,
InfoIcon
InfoIcon,
} from '../../icons';
interface WordPressIntegrationFormProps {
@@ -283,56 +277,40 @@ export default function WordPressIntegrationForm({
{/* Connection Status & Test Button */}
{apiKey && (
<div className="flex items-center gap-3">
{/* Status Indicator - Uses theme colors from design-system */}
<div className={`flex items-center gap-2 px-3 py-1.5 rounded-lg border ${
connectionStatus === 'connected'
? 'bg-success-50 dark:bg-success-900/20 border-success-200 dark:border-success-800'
: connectionStatus === 'testing'
? 'bg-brand-50 dark:bg-brand-900/20 border-brand-200 dark:border-brand-800'
: connectionStatus === 'api_key_pending'
? 'bg-warning-50 dark:bg-warning-900/20 border-warning-200 dark:border-warning-800'
: connectionStatus === 'plugin_missing'
? 'bg-warning-50 dark:bg-warning-900/20 border-warning-200 dark:border-warning-800'
: 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'
}`}>
{connectionStatus === 'connected' && (
<><CheckCircleIcon className="w-4 h-4 text-success-600 dark:text-success-400" />
<span className="text-sm font-medium text-success-700 dark:text-success-300">Connected</span></>
)}
{connectionStatus === 'testing' && (
<><RefreshCwIcon className="w-4 h-4 text-brand-600 dark:text-brand-400 animate-spin" />
<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 className="flex flex-col items-end gap-2">
{/* Connection Status Indicator */}
<div className="flex items-center gap-3 px-4 py-2 rounded-lg bg-gray-50 dark:bg-gray-800">
<span
className={`inline-block w-4 h-4 rounded-full ${
connectionStatus === 'connected' ? 'bg-success-500' :
connectionStatus === 'testing' ? 'bg-brand-500 animate-pulse' :
connectionStatus === 'api_key_pending' ? 'bg-warning-500' :
connectionStatus === 'plugin_missing' ? 'bg-warning-500' :
connectionStatus === 'error' ? 'bg-error-500' :
'bg-brand-500'
}`}
/>
<span className="text-sm font-medium text-gray-700 dark:text-gray-200">
{connectionStatus === 'connected' && 'Connected'}
{connectionStatus === 'testing' && 'Testing...'}
{connectionStatus === 'api_key_pending' && 'Pending Setup'}
{connectionStatus === 'plugin_missing' && 'Plugin Missing'}
{connectionStatus === 'error' && 'Error'}
{connectionStatus === 'unknown' && 'Configured'}
</span>
</div>
{/* Test Connection Button - IconButton only */}
<IconButton
{/* Test Connection Button */}
<Button
onClick={testConnection}
variant="outline"
tone="brand"
size="sm"
disabled={testingConnection || !apiKey}
title="Test Connection"
icon={<RefreshCwIcon className={`w-4 h-4 ${testingConnection ? 'animate-spin' : ''}`} />}
/>
startIcon={<RefreshCwIcon className={`w-4 h-4 ${testingConnection ? 'animate-spin' : ''}`} />}
>
Test Connection
</Button>
</div>
)}
</div>

View File

@@ -499,19 +499,6 @@ export default function SiteSettings() {
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
const [syncLoading, setSyncLoading] = useState(false);
const [lastSyncTime, setLastSyncTime] = useState<string | null>(null);
@@ -535,10 +522,8 @@ export default function SiteSettings() {
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.warning('WordPress reachable but IGNY8 plugin not installed');
@@ -684,21 +669,6 @@ export default function SiteSettings() {
Publishing
</Button>
</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>