fix
This commit is contained in:
@@ -147,15 +147,13 @@ const AdminBilling: React.FC = () => {
|
||||
Admin controls for credits, pricing, and user billing
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
href="/admin/igny8_core/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-700"
|
||||
<Button
|
||||
variant="outline"
|
||||
startIcon={<PlugInIcon className="w-4 h-4" />}
|
||||
onClick={() => window.open('/admin/igny8_core/', '_blank')}
|
||||
>
|
||||
<PlugInIcon className="w-4 h-4 mr-2" />
|
||||
Django Admin
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* System Stats */}
|
||||
@@ -163,30 +161,26 @@ const AdminBilling: React.FC = () => {
|
||||
<EnhancedMetricCard
|
||||
title="Total Users"
|
||||
value={stats?.total_users || 0}
|
||||
icon={UserIcon}
|
||||
color="blue"
|
||||
iconColor="text-blue-500"
|
||||
icon={<UserIcon />}
|
||||
accentColor="blue"
|
||||
/>
|
||||
<EnhancedMetricCard
|
||||
title="Active Users"
|
||||
value={stats?.active_users || 0}
|
||||
icon={CheckCircleIcon}
|
||||
color="green"
|
||||
iconColor="text-green-500"
|
||||
icon={<CheckCircleIcon />}
|
||||
accentColor="green"
|
||||
/>
|
||||
<EnhancedMetricCard
|
||||
title="Credits Issued"
|
||||
value={stats?.total_credits_issued || 0}
|
||||
icon={DollarLineIcon}
|
||||
color="amber"
|
||||
iconColor="text-amber-500"
|
||||
icon={<DollarLineIcon />}
|
||||
accentColor="orange"
|
||||
/>
|
||||
<EnhancedMetricCard
|
||||
title="Credits Used"
|
||||
value={stats?.total_credits_used || 0}
|
||||
icon={BoltIcon}
|
||||
color="purple"
|
||||
iconColor="text-purple-500"
|
||||
icon={<BoltIcon />}
|
||||
accentColor="purple"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -231,28 +225,28 @@ const AdminBilling: React.FC = () => {
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<ComponentCard title="Quick Actions">
|
||||
<div className="space-y-3">
|
||||
<Button
|
||||
variant="primary"
|
||||
<Button
|
||||
variant="primary"
|
||||
fullWidth
|
||||
startIcon={<UserIcon className="w-4 h-4" />}
|
||||
onClick={() => setActiveTab('users')}
|
||||
>
|
||||
<UserIcon className="w-4 h-4 mr-2" />
|
||||
Manage User Credits
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
<Button
|
||||
variant="secondary"
|
||||
fullWidth
|
||||
startIcon={<DollarLineIcon className="w-4 h-4" />}
|
||||
onClick={() => setActiveTab('pricing')}
|
||||
>
|
||||
<DollarLineIcon className="w-4 h-4 mr-2" />
|
||||
Update Credit Costs
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
<Button
|
||||
variant="outline"
|
||||
fullWidth
|
||||
startIcon={<PlugInIcon className="w-4 h-4" />}
|
||||
onClick={() => window.open('/admin/igny8_core/creditcostconfig/', '_blank')}
|
||||
>
|
||||
<PlugInIcon className="w-4 h-4 mr-2" />
|
||||
Full Admin Panel
|
||||
</Button>
|
||||
</div>
|
||||
@@ -309,7 +303,7 @@ const AdminBilling: React.FC = () => {
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-4 whitespace-nowrap">
|
||||
<Badge variant="info">{user.subscription_plan || 'Free'}</Badge>
|
||||
<Badge tone="info">{user.subscription_plan || 'Free'}</Badge>
|
||||
</td>
|
||||
<td className="px-4 py-4 whitespace-nowrap text-right font-bold text-amber-600 dark:text-amber-400">
|
||||
{user.credits}
|
||||
@@ -432,7 +426,7 @@ const AdminBilling: React.FC = () => {
|
||||
{config.cost}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-center">
|
||||
<Badge variant={config.is_active ? 'success' : 'warning'}>
|
||||
<Badge tone={config.is_active ? 'success' : 'warning'}>
|
||||
{config.is_active ? 'Active' : 'Inactive'}
|
||||
</Badge>
|
||||
</td>
|
||||
|
||||
@@ -54,6 +54,7 @@ const AutomationPage: React.FC = () => {
|
||||
const [pipelineOverview, setPipelineOverview] = useState<PipelineStage[]>([]);
|
||||
const [metrics, setMetrics] = useState<any>(null);
|
||||
const [showConfigModal, setShowConfigModal] = useState(false);
|
||||
const [showProcessingCard, setShowProcessingCard] = useState<boolean>(true);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [estimate, setEstimate] = useState<{ estimated_credits: number; current_balance: number; sufficient: boolean } | null>(null);
|
||||
|
||||
@@ -147,6 +148,10 @@ const AutomationPage: React.FC = () => {
|
||||
setCurrentRun(runData.run);
|
||||
setEstimate(estimateData);
|
||||
setPipelineOverview(pipelineData.stages);
|
||||
// show processing card when there's a current run
|
||||
if (runData.run) {
|
||||
setShowProcessingCard(true);
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error('Failed to load automation data');
|
||||
console.error(error);
|
||||
@@ -160,6 +165,8 @@ const AutomationPage: React.FC = () => {
|
||||
try {
|
||||
const data = await automationService.getCurrentRun(activeSite.id);
|
||||
setCurrentRun(data.run);
|
||||
// ensure processing card is visible when a run exists
|
||||
if (data.run) setShowProcessingCard(true);
|
||||
} catch (error) {
|
||||
console.error('Failed to poll current run', error);
|
||||
}
|
||||
@@ -251,22 +258,27 @@ const AutomationPage: React.FC = () => {
|
||||
};
|
||||
|
||||
const handlePause = async () => {
|
||||
if (!currentRun) return;
|
||||
if (!currentRun || !activeSite) return;
|
||||
try {
|
||||
await automationService.pause(currentRun.run_id);
|
||||
await automationService.pause(activeSite.id, currentRun.run_id);
|
||||
toast.success('Automation paused');
|
||||
loadCurrentRun();
|
||||
// refresh run and pipeline/metrics
|
||||
await loadCurrentRun();
|
||||
await loadPipelineOverview();
|
||||
await loadMetrics();
|
||||
} catch (error) {
|
||||
toast.error('Failed to pause automation');
|
||||
}
|
||||
};
|
||||
|
||||
const handleResume = async () => {
|
||||
if (!currentRun) return;
|
||||
if (!currentRun || !activeSite) return;
|
||||
try {
|
||||
await automationService.resume(currentRun.run_id);
|
||||
await automationService.resume(activeSite.id, currentRun.run_id);
|
||||
toast.success('Automation resumed');
|
||||
loadCurrentRun();
|
||||
await loadCurrentRun();
|
||||
await loadPipelineOverview();
|
||||
await loadMetrics();
|
||||
} catch (error) {
|
||||
toast.error('Failed to resume automation');
|
||||
}
|
||||
@@ -278,8 +290,13 @@ const AutomationPage: React.FC = () => {
|
||||
await automationService.updateConfig(activeSite.id, newConfig);
|
||||
toast.success('Configuration saved');
|
||||
setShowConfigModal(false);
|
||||
loadData();
|
||||
// Optimistically update config locally and refresh data
|
||||
setConfig((prev) => ({ ...(prev as AutomationConfig), ...newConfig } as AutomationConfig));
|
||||
await loadPipelineOverview();
|
||||
await loadMetrics();
|
||||
await loadCurrentRun();
|
||||
} catch (error) {
|
||||
console.error('Failed to save config:', error);
|
||||
toast.error('Failed to save configuration');
|
||||
}
|
||||
};
|
||||
@@ -665,18 +682,21 @@ const AutomationPage: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* Current Processing Card - Shows real-time automation progress */}
|
||||
{currentRun && (currentRun.status === 'running' || currentRun.status === 'paused') && activeSite && (
|
||||
{currentRun && showProcessingCard && activeSite && (
|
||||
<CurrentProcessingCard
|
||||
runId={currentRun.run_id}
|
||||
siteId={activeSite.id}
|
||||
currentRun={currentRun}
|
||||
onUpdate={() => {
|
||||
// Refresh current run status
|
||||
loadCurrentRun();
|
||||
pipelineOverview={pipelineOverview}
|
||||
onUpdate={async () => {
|
||||
// Refresh current run status, pipeline overview and metrics (no full page reload)
|
||||
await loadCurrentRun();
|
||||
await loadPipelineOverview();
|
||||
await loadMetrics();
|
||||
}}
|
||||
onClose={() => {
|
||||
// Card will remain in DOM but user acknowledged it
|
||||
// Can add state here to minimize it if needed
|
||||
// hide the processing card until next run
|
||||
setShowProcessingCard(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -692,7 +712,8 @@ const AutomationPage: React.FC = () => {
|
||||
const isComplete = currentRun && currentRun.current_stage > stage.number;
|
||||
const result = currentRun ? (currentRun[`stage_${stage.number}_result` as keyof AutomationRun] as any) : null;
|
||||
const processed = result ? Object.values(result).reduce((sum: number, val) => typeof val === 'number' ? sum + val : sum, 0) : 0;
|
||||
const progressPercent = stage.pending > 0 ? Math.round((processed / (processed + stage.pending)) * 100) : 0;
|
||||
const total = (stage.pending ?? 0) + processed;
|
||||
const progressPercent = total > 0 ? Math.round((processed / total) * 100) : 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -787,7 +808,8 @@ const AutomationPage: React.FC = () => {
|
||||
const isComplete = currentRun && currentRun.current_stage > stage.number;
|
||||
const result = currentRun ? (currentRun[`stage_${stage.number}_result` as keyof AutomationRun] as any) : null;
|
||||
const processed = result ? Object.values(result).reduce((sum: number, val) => typeof val === 'number' ? sum + val : sum, 0) : 0;
|
||||
const progressPercent = stage.pending > 0 ? Math.round((processed / (processed + stage.pending)) * 100) : 0;
|
||||
const total = (stage.pending ?? 0) + processed;
|
||||
const progressPercent = total > 0 ? Math.round((processed / total) * 100) : 0;
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -113,11 +113,14 @@ const CreditsAndBilling: React.FC = () => {
|
||||
Manage your credits, view transactions, and monitor usage
|
||||
</p>
|
||||
</div>
|
||||
<Button variant="primary" onClick={() => {
|
||||
// TODO: Link to purchase credits page
|
||||
toast?.info('Purchase credits feature coming soon');
|
||||
}}>
|
||||
<DollarLineIcon className="w-4 h-4 mr-2" />
|
||||
<Button
|
||||
variant="primary"
|
||||
startIcon={<DollarLineIcon className="w-4 h-4" />}
|
||||
onClick={() => {
|
||||
// TODO: Link to purchase credits page
|
||||
toast?.info('Purchase credits feature coming soon');
|
||||
}}
|
||||
>
|
||||
Purchase Credits
|
||||
</Button>
|
||||
</div>
|
||||
@@ -127,31 +130,27 @@ const CreditsAndBilling: React.FC = () => {
|
||||
<EnhancedMetricCard
|
||||
title="Current Balance"
|
||||
value={balance?.credits || 0}
|
||||
icon={BoltIcon}
|
||||
color="amber"
|
||||
iconColor="text-amber-500"
|
||||
icon={<BoltIcon />}
|
||||
accentColor="orange"
|
||||
/>
|
||||
<EnhancedMetricCard
|
||||
title="Monthly Included"
|
||||
value={balance?.monthly_credits_included || 0}
|
||||
subtitle={balance?.subscription_plan || 'Free'}
|
||||
icon={CheckCircleIcon}
|
||||
color="green"
|
||||
iconColor="text-green-500"
|
||||
icon={<CheckCircleIcon />}
|
||||
accentColor="green"
|
||||
/>
|
||||
<EnhancedMetricCard
|
||||
title="Bonus Credits"
|
||||
value={balance?.bonus_credits || 0}
|
||||
icon={DollarLineIcon}
|
||||
color="blue"
|
||||
iconColor="text-blue-500"
|
||||
icon={<DollarLineIcon />}
|
||||
accentColor="blue"
|
||||
/>
|
||||
<EnhancedMetricCard
|
||||
title="Total This Month"
|
||||
value={usageLogs.reduce((sum, log) => sum + log.credits_used, 0)}
|
||||
icon={TimeIcon}
|
||||
color="purple"
|
||||
iconColor="text-purple-500"
|
||||
icon={<TimeIcon />}
|
||||
accentColor="purple"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -201,7 +200,7 @@ const CreditsAndBilling: React.FC = () => {
|
||||
<div key={transaction.id} className="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant={getTransactionTypeColor(transaction.transaction_type)}>
|
||||
<Badge tone={getTransactionTypeColor(transaction.transaction_type) as any}>
|
||||
{transaction.transaction_type}
|
||||
</Badge>
|
||||
<span className="text-sm text-gray-900 dark:text-white">
|
||||
@@ -290,7 +289,7 @@ const CreditsAndBilling: React.FC = () => {
|
||||
{new Date(transaction.created_at).toLocaleDateString()}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<Badge variant={getTransactionTypeColor(transaction.transaction_type)}>
|
||||
<Badge tone={getTransactionTypeColor(transaction.transaction_type) as any}>
|
||||
{transaction.transaction_type}
|
||||
</Badge>
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user