trash models added, first attempt for remainign issues
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
* Clean UI without cluttered "Currently Processing" and "Up Next" sections
|
||||
*/
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { automationService, ProcessingState, AutomationRun, PipelineStage } from '../../services/automationService';
|
||||
import { automationService, ProcessingState, AutomationRun, PipelineStage, CurrentProcessingResponse } from '../../services/automationService';
|
||||
import { useToast } from '../ui/toast/ToastContainer';
|
||||
import Button from '../ui/button/Button';
|
||||
import IconButton from '../ui/button/IconButton';
|
||||
@@ -98,6 +98,7 @@ const CurrentProcessingCard: React.FC<CurrentProcessingCardProps> = ({
|
||||
pipelineOverview,
|
||||
}) => {
|
||||
const [processingState, setProcessingState] = useState<ProcessingState | null>(null);
|
||||
const [totalCreditsUsed, setTotalCreditsUsed] = useState<number>(currentRun.total_credits_used);
|
||||
const [isPausing, setIsPausing] = useState(false);
|
||||
const [isResuming, setIsResuming] = useState(false);
|
||||
const [isCancelling, setIsCancelling] = useState(false);
|
||||
@@ -110,12 +111,21 @@ const CurrentProcessingCard: React.FC<CurrentProcessingCardProps> = ({
|
||||
|
||||
const fetchState = async () => {
|
||||
try {
|
||||
const state = await automationService.getCurrentProcessing(siteId, runId);
|
||||
const response = await automationService.getCurrentProcessing(siteId, runId);
|
||||
if (!isMounted) return;
|
||||
setProcessingState(state);
|
||||
|
||||
if (response) {
|
||||
// Update processing state from nested state object
|
||||
setProcessingState(response.state);
|
||||
|
||||
// Update credits from the response
|
||||
if (response.total_credits_used !== undefined) {
|
||||
setTotalCreditsUsed(response.total_credits_used);
|
||||
}
|
||||
}
|
||||
|
||||
// If stage completed, trigger update
|
||||
if (state && state.processed_items >= state.total_items && state.total_items > 0) {
|
||||
if (response?.state && response.state.processed_items >= response.state.total_items && response.state.total_items > 0) {
|
||||
onUpdate();
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -323,7 +333,7 @@ const CurrentProcessingCard: React.FC<CurrentProcessingCardProps> = ({
|
||||
<BoltIcon className="w-4 h-4 text-warning-500" />
|
||||
<span className="text-xs font-medium text-gray-500 uppercase">Credits</span>
|
||||
</div>
|
||||
<span className="text-base font-bold text-warning-600">{currentRun.total_credits_used}</span>
|
||||
<span className="text-base font-bold text-warning-600">{totalCreditsUsed}</span>
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-gray-800 rounded-xl px-3 py-2.5 border border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
|
||||
@@ -5,7 +5,7 @@ import Badge from '../ui/badge/Badge';
|
||||
import SiteSetupChecklist from '../sites/SiteSetupChecklist';
|
||||
import SiteTypeBadge from '../sites/SiteTypeBadge';
|
||||
import { Site } from '../../services/api';
|
||||
import { BoxCubeIcon as SettingsIcon, EyeIcon, FileIcon } from '../../icons';
|
||||
import { BoxCubeIcon as SettingsIcon, EyeIcon, FileIcon, TrashBinIcon } from '../../icons';
|
||||
|
||||
interface SiteCardProps {
|
||||
site: Site;
|
||||
@@ -13,6 +13,7 @@ interface SiteCardProps {
|
||||
onToggle: (siteId: number, enabled: boolean) => void;
|
||||
onSettings: (site: Site) => void;
|
||||
onDetails: (site: Site) => void;
|
||||
onDelete?: (site: Site) => void;
|
||||
isToggling?: boolean;
|
||||
}
|
||||
|
||||
@@ -22,6 +23,7 @@ export default function SiteCard({
|
||||
onToggle,
|
||||
onSettings,
|
||||
onDetails,
|
||||
onDelete,
|
||||
isToggling = false,
|
||||
}: SiteCardProps) {
|
||||
const handleToggle = (enabled: boolean) => {
|
||||
@@ -126,6 +128,16 @@ export default function SiteCard({
|
||||
>
|
||||
Settings
|
||||
</Button>
|
||||
{onDelete && (
|
||||
<Button
|
||||
variant="outline"
|
||||
tone="destructive"
|
||||
size="sm"
|
||||
onClick={() => onDelete(site)}
|
||||
startIcon={<TrashBinIcon className="w-4 h-4" />}
|
||||
title="Delete site"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -20,7 +20,7 @@ export interface AIOperation {
|
||||
}
|
||||
|
||||
export interface AIOperationsData {
|
||||
period: '7d' | '30d' | '90d';
|
||||
period: 'today' | '7d' | '30d' | '90d';
|
||||
operations: AIOperation[];
|
||||
totals: {
|
||||
count: number;
|
||||
@@ -32,7 +32,7 @@ export interface AIOperationsData {
|
||||
|
||||
interface AIOperationsWidgetProps {
|
||||
data: AIOperationsData;
|
||||
onPeriodChange?: (period: '7d' | '30d' | '90d') => void;
|
||||
onPeriodChange?: (period: 'today' | '7d' | '30d' | '90d') => void;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ const operationConfig: Record<string, { label: string; icon: typeof GroupIcon; g
|
||||
const defaultConfig = { label: 'Other', icon: BoltIcon, gradient: 'from-gray-500 to-gray-600' };
|
||||
|
||||
const periods = [
|
||||
{ value: 'today', label: 'Today' },
|
||||
{ value: '7d', label: '7 days' },
|
||||
{ value: '30d', label: '30 days' },
|
||||
{ value: '90d', label: '90 days' },
|
||||
|
||||
@@ -48,7 +48,7 @@ export default function Home() {
|
||||
const [sites, setSites] = useState<Site[]>([]);
|
||||
const [sitesLoading, setSitesLoading] = useState(true);
|
||||
const [siteFilter, setSiteFilter] = useState<'all' | number>('all');
|
||||
const [aiPeriod, setAIPeriod] = useState<'7d' | '30d' | '90d'>('7d');
|
||||
const [aiPeriod, setAIPeriod] = useState<'today' | '7d' | '30d' | '90d'>('7d');
|
||||
const [showAddSite, setShowAddSite] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [subscription, setSubscription] = useState<Subscription | null>(null);
|
||||
@@ -171,7 +171,7 @@ export default function Home() {
|
||||
setLoading(true);
|
||||
|
||||
const siteId = siteFilter === 'all' ? undefined : siteFilter;
|
||||
const periodDays = aiPeriod === '7d' ? 7 : aiPeriod === '30d' ? 30 : 90;
|
||||
const periodDays = aiPeriod === 'today' ? 1 : aiPeriod === '7d' ? 7 : aiPeriod === '30d' ? 30 : 90;
|
||||
|
||||
// Fetch real dashboard stats from API
|
||||
const stats = await getDashboardStats({
|
||||
|
||||
@@ -423,6 +423,7 @@ export default function Sites() {
|
||||
onToggle={handleToggle}
|
||||
onSettings={handleSettings}
|
||||
onDetails={handleDetails}
|
||||
onDelete={handleDeleteSite}
|
||||
isToggling={togglingSiteId === site.id}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -85,9 +85,9 @@ export default function SiteDashboard() {
|
||||
});
|
||||
const [operations, setOperations] = useState<OperationStat[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [aiPeriod, setAiPeriod] = useState<'7d' | '30d' | '90d'>('7d');
|
||||
const [aiPeriod, setAiPeriod] = useState<'today' | '7d' | '30d' | '90d'>('7d');
|
||||
|
||||
const handlePeriodChange = (period: '7d' | '30d' | '90d') => {
|
||||
const handlePeriodChange = (period: 'today' | '7d' | '30d' | '90d') => {
|
||||
setAiPeriod(period);
|
||||
};
|
||||
|
||||
@@ -180,7 +180,7 @@ export default function SiteDashboard() {
|
||||
|
||||
// Load operation stats from real API data
|
||||
try {
|
||||
const periodDays = aiPeriod === '7d' ? 7 : aiPeriod === '30d' ? 30 : 90;
|
||||
const periodDays = aiPeriod === 'today' ? 1 : aiPeriod === '7d' ? 7 : aiPeriod === '30d' ? 30 : 90;
|
||||
const stats = await getDashboardStats({ site_id: Number(currentSiteId), days: periodDays });
|
||||
|
||||
// Map operation types from API to display types
|
||||
|
||||
@@ -78,6 +78,12 @@ export interface ProcessingState {
|
||||
remaining_count: number;
|
||||
}
|
||||
|
||||
export interface CurrentProcessingResponse {
|
||||
state: ProcessingState | null;
|
||||
total_credits_used: number;
|
||||
current_stage: number;
|
||||
}
|
||||
|
||||
// NEW: Types for unified run_progress endpoint
|
||||
export interface StageProgress {
|
||||
number: number;
|
||||
@@ -257,11 +263,12 @@ export const automationService = {
|
||||
|
||||
/**
|
||||
* Get current processing state for active automation run
|
||||
* Returns state with total_credits_used for real-time credits tracking
|
||||
*/
|
||||
getCurrentProcessing: async (
|
||||
siteId: number,
|
||||
runId: string
|
||||
): Promise<ProcessingState | null> => {
|
||||
): Promise<CurrentProcessingResponse | null> => {
|
||||
const response = await fetchAPI(
|
||||
buildUrl('/current_processing/', { site_id: siteId, run_id: runId })
|
||||
);
|
||||
|
||||
@@ -1068,47 +1068,94 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten
|
||||
{/* Action Buttons - Conditional based on status */}
|
||||
{content.status && (
|
||||
<div className="px-8 py-6 bg-gray-50 dark:bg-gray-900/30 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Draft status: Show Edit Content + Generate Images */}
|
||||
{content.status.toLowerCase() === 'draft' && (
|
||||
<>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => navigate(`/sites/${content.site_id}/posts/${content.id}/edit`)}
|
||||
startIcon={<PencilIcon className="w-4 h-4" />}
|
||||
>
|
||||
Edit Content
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
tone="brand"
|
||||
onClick={() => navigate(`/writer/images?contentId=${content.id}`)}
|
||||
startIcon={<ImageIcon className="w-4 h-4" />}
|
||||
>
|
||||
Generate Images
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<div className="flex items-center justify-between flex-wrap gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
{/* Draft status: Show Edit Content + Generate Images */}
|
||||
{content.status.toLowerCase() === 'draft' && (
|
||||
<>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => navigate(`/sites/${content.site_id}/posts/${content.id}/edit`)}
|
||||
startIcon={<PencilIcon className="w-4 h-4" />}
|
||||
>
|
||||
Edit Content
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
tone="brand"
|
||||
onClick={() => navigate(`/writer/images?contentId=${content.id}`)}
|
||||
startIcon={<ImageIcon className="w-4 h-4" />}
|
||||
>
|
||||
Generate Images
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Review status: Show Edit Content + Publish */}
|
||||
{content.status.toLowerCase() === 'review' && (
|
||||
<>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => navigate(`/sites/${content.site_id}/posts/${content.id}/edit`)}
|
||||
startIcon={<PencilIcon className="w-4 h-4" />}
|
||||
>
|
||||
Edit Content
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
tone="brand"
|
||||
onClick={() => navigate(`/writer/published?contentId=${content.id}&action=publish`)}
|
||||
startIcon={<BoltIcon className="w-4 h-4" />}
|
||||
>
|
||||
Publish
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Review status: Show Edit Content + Publish */}
|
||||
{content.status.toLowerCase() === 'review' && (
|
||||
<>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => navigate(`/sites/${content.site_id}/posts/${content.id}/edit`)}
|
||||
startIcon={<PencilIcon className="w-4 h-4" />}
|
||||
>
|
||||
Edit Content
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
tone="brand"
|
||||
onClick={() => navigate(`/writer/published?contentId=${content.id}&action=publish`)}
|
||||
startIcon={<BoltIcon className="w-4 h-4" />}
|
||||
>
|
||||
Publish
|
||||
</Button>
|
||||
</>
|
||||
{/* Publishing Status Display */}
|
||||
{content.site_status && (
|
||||
<div className="flex items-center gap-3 px-4 py-2 rounded-lg bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center gap-2">
|
||||
{content.site_status === 'published' && (
|
||||
<CheckCircleIcon className="w-5 h-5 text-success-500" />
|
||||
)}
|
||||
{content.site_status === 'scheduled' && (
|
||||
<ClockIcon className="w-5 h-5 text-brand-500" />
|
||||
)}
|
||||
{content.site_status === 'publishing' && (
|
||||
<ClockIcon className="w-5 h-5 text-warning-500 animate-pulse" />
|
||||
)}
|
||||
{content.site_status === 'failed' && (
|
||||
<XCircleIcon className="w-5 h-5 text-error-500" />
|
||||
)}
|
||||
{content.site_status === 'not_published' && (
|
||||
<FileTextIcon className="w-5 h-5 text-gray-400" />
|
||||
)}
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
{content.site_status === 'not_published' && 'Not Published'}
|
||||
{content.site_status === 'scheduled' && 'Scheduled'}
|
||||
{content.site_status === 'publishing' && 'Publishing...'}
|
||||
{content.site_status === 'published' && 'Published'}
|
||||
{content.site_status === 'failed' && 'Failed'}
|
||||
</span>
|
||||
</div>
|
||||
{content.scheduled_publish_at && content.site_status === 'scheduled' && (
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{formatDate(content.scheduled_publish_at)}
|
||||
</span>
|
||||
)}
|
||||
{content.external_url && content.site_status === 'published' && (
|
||||
<a
|
||||
href={content.external_url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm text-brand-600 hover:text-brand-700 dark:text-brand-400"
|
||||
>
|
||||
View on site →
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user