trash models added, first attempt for remainign issues

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-12 13:39:42 +00:00
parent 28cb698579
commit 7d4d309677
20 changed files with 1084 additions and 106 deletions

View File

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

View File

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

View File

@@ -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' },

View File

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

View File

@@ -423,6 +423,7 @@ export default function Sites() {
onToggle={handleToggle}
onSettings={handleSettings}
onDetails={handleDetails}
onDelete={handleDeleteSite}
isToggling={togglingSiteId === site.id}
/>
))}

View File

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

View File

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

View File

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