This commit is contained in:
alorig
2025-11-28 15:46:38 +05:00
parent 1aead06939
commit bcdbbfe233
2 changed files with 104 additions and 55 deletions

View File

@@ -1005,13 +1005,29 @@ class ContentIdeasViewSet(SiteSectorModelViewSet):
) )
queryset = self.get_queryset() queryset = self.get_queryset()
ideas = queryset.filter(id__in=ids, status='new') # Only queue 'new' ideas # Get ALL requested ideas first (don't filter by status yet)
all_ideas = queryset.filter(id__in=ids)
# Check which ones can be queued (status='new')
queueable_ideas = all_ideas.filter(status='new')
from igny8_core.modules.writer.models import Tasks from igny8_core.modules.writer.models import Tasks
created_tasks = [] created_tasks = []
errors = [] errors = []
for idea in ideas: skipped = []
# Add skipped ideas (not 'new' status)
for idea in all_ideas:
if idea.status != 'new':
skipped.append({
'idea_id': idea.id,
'title': idea.idea_title,
'reason': f'Already {idea.status}'
})
# Process queueable ideas
for idea in queueable_ideas:
try: try:
# Direct copy - no mapping needed # Direct copy - no mapping needed
task = Tasks.objects.create( task = Tasks.objects.create(
@@ -1037,23 +1053,43 @@ class ContentIdeasViewSet(SiteSectorModelViewSet):
idea.status = 'scheduled' idea.status = 'scheduled'
idea.save() idea.save()
except Exception as e: except Exception as e:
errors.append({'idea_id': idea.id, 'error': str(e)}) errors.append({
'idea_id': idea.id,
'title': idea.idea_title,
'error': str(e)
})
if errors: # Return appropriate response based on results
if len(created_tasks) == 0 and (errors or skipped):
# Complete failure
return error_response( return error_response(
error=f'Failed to create {len(errors)} tasks', error=f'Failed to queue any ideas: {len(errors)} errors, {len(skipped)} skipped',
errors=errors, errors=errors if errors else skipped,
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
elif errors:
# Partial success - some created, some failed
return success_response(
data={
'created_count': len(created_tasks),
'task_ids': created_tasks,
'errors': errors,
'skipped': skipped,
},
message=f'Queued {len(created_tasks)} ideas ({len(errors)} failed, {len(skipped)} skipped)',
request=request
)
else:
# Complete success
return success_response(
data={
'created_count': len(created_tasks),
'task_ids': created_tasks,
'skipped': skipped,
},
message=f'Successfully queued {len(created_tasks)} ideas to writer' + (f' ({len(skipped)} already scheduled)' if skipped else ''),
request=request request=request
) )
return success_response(
data={
'created_count': len(created_tasks),
'task_ids': created_tasks,
},
message=f'Successfully queued {len(created_tasks)} ideas to writer',
request=request
)
# REMOVED: generate_idea action - idea generation function removed # REMOVED: generate_idea action - idea generation function removed

View File

@@ -208,48 +208,61 @@ export const createContentPageConfig = (
}, },
}, },
{ {
key: 'prompts_status', key: 'content_status_indicators',
label: 'Prompts', label: 'Status',
sortable: false, sortable: false,
width: '110px', width: '100px',
align: 'center' as const,
render: (_value: any, row: Content) => { render: (_value: any, row: Content) => {
const hasPrompts = row.has_image_prompts; const hasPrompts = row.has_image_prompts || false;
const hasImages = row.has_generated_images || false;
return ( return (
<Badge color={hasPrompts ? 'success' : 'warning'} size="sm" variant="light"> <div className="flex items-center justify-center gap-2">
{hasPrompts ? ( {/* Prompts Icon */}
<span className="flex items-center gap-1"> <div
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"> className={`w-5 h-5 flex items-center justify-center flex-shrink-0 ${
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" /> hasPrompts ? 'text-purple-500 dark:text-purple-400' : 'text-gray-300 dark:text-gray-600'
</svg> }`}
Ready title={hasPrompts ? 'Prompts ready' : 'No prompts'}
</span> >
) : ( <svg
'No Prompts' xmlns="http://www.w3.org/2000/svg"
)} viewBox="0 0 24 24"
</Badge> fill="none"
); stroke="currentColor"
}, strokeWidth="2"
}, strokeLinecap="round"
{ strokeLinejoin="round"
key: 'images_status', className="w-4 h-4"
label: 'Images', >
sortable: false, <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
width: '110px', </svg>
render: (_value: any, row: Content) => { </div>
const hasImages = row.has_generated_images;
return ( {/* Images Icon */}
<Badge color={hasImages ? 'success' : 'warning'} size="sm" variant="light"> <div
{hasImages ? ( className={`w-5 h-5 flex items-center justify-center flex-shrink-0 ${
<span className="flex items-center gap-1"> hasImages ? 'text-green-500 dark:text-green-400' : 'text-gray-300 dark:text-gray-600'
<svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"> }`}
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" /> title={hasImages ? 'Images generated' : 'No images'}
</svg> >
Generated <svg
</span> xmlns="http://www.w3.org/2000/svg"
) : ( viewBox="0 0 24 24"
'No Images' fill="none"
)} stroke="currentColor"
</Badge> strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className="w-4 h-4"
>
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
<circle cx="8.5" cy="8.5" r="1.5" />
<polyline points="21 15 16 10 5 21" />
</svg>
</div>
</div>
); );
}, },
}, },