diff --git a/docs/plans/FREE_ACCOUNT_OPTIONS_ANALYSIS.md b/docs/plans/implemented/FREE_ACCOUNT_OPTIONS_ANALYSIS.md similarity index 100% rename from docs/plans/FREE_ACCOUNT_OPTIONS_ANALYSIS.md rename to docs/plans/implemented/FREE_ACCOUNT_OPTIONS_ANALYSIS.md diff --git a/frontend/src/pages/Publisher/ContentCalendar.tsx b/frontend/src/pages/Publisher/ContentCalendar.tsx index f2d49bc2..364d05e3 100644 --- a/frontend/src/pages/Publisher/ContentCalendar.tsx +++ b/frontend/src/pages/Publisher/ContentCalendar.tsx @@ -192,52 +192,45 @@ export default function ContentCalendar() { // IMPORTANT: Since content is ordered by -created_at, we need to fetch items by specific site_status // Otherwise old scheduled/failed items will be on later pages and won't load + // NOTE: fetchAPI doesn't support params object - must use URL query strings + + const siteId = activeSite.id; console.log('[ContentCalendar] ========== SITE FILTERING DEBUG =========='); - console.log('[ContentCalendar] Active site ID:', activeSite.id); + console.log('[ContentCalendar] Active site ID:', siteId); console.log('[ContentCalendar] Active site name:', activeSite.name); console.log('[ContentCalendar] Fetching content with multiple targeted queries...'); // Fetch scheduled items (all of them, regardless of page) - const scheduledResponse = await fetchAPI('/v1/writer/content/', { - params: { - site_id: activeSite.id, - page_size: 1000, - site_status: 'scheduled', // Filter specifically for scheduled - } - }); + const scheduledResponse = await fetchAPI( + `/v1/writer/content/?site_id=${siteId}&page_size=1000&site_status=scheduled` + ); // Fetch failed items (all of them) - const failedResponse = await fetchAPI('/v1/writer/content/', { - params: { - site_id: activeSite.id, - page_size: 1000, - site_status: 'failed', // Filter specifically for failed - } - }); + const failedResponse = await fetchAPI( + `/v1/writer/content/?site_id=${siteId}&page_size=1000&site_status=failed` + ); - // Fetch approved items (for sidebar drag-drop) - const approvedResponse = await fetchAPI('/v1/writer/content/', { - params: { - site_id: activeSite.id, - page_size: 100, - status: 'approved', // Approved workflow status - } - }); + // Fetch review items (for review count display) + const reviewResponse = await fetchAPI( + `/v1/writer/content/?site_id=${siteId}&page_size=100&status=review` + ); - // Fetch published items (with external_id) for display - const publishedResponse = await fetchAPI('/v1/writer/content/', { - params: { - site_id: activeSite.id, - page_size: 100, - ordering: '-updated_at', // Most recently published first - } - }); + // Fetch approved items (for sidebar drag-drop) - not yet published to site + const approvedResponse = await fetchAPI( + `/v1/writer/content/?site_id=${siteId}&page_size=100&status=approved&site_status=not_published` + ); + + // Fetch published items (site_status=published) for display + const publishedResponse = await fetchAPI( + `/v1/writer/content/?site_id=${siteId}&page_size=100&site_status=published&ordering=-updated_at` + ); // Combine all results, removing duplicates by ID const allItems = [ ...(scheduledResponse.results || []), ...(failedResponse.results || []), + ...(reviewResponse.results || []), ...(approvedResponse.results || []), ...(publishedResponse.results || []), ]; @@ -251,6 +244,7 @@ export default function ContentCalendar() { console.log('[ContentCalendar] ========== DATA LOAD DEBUG =========='); console.log('[ContentCalendar] Scheduled query returned:', scheduledResponse.results?.length, 'items'); console.log('[ContentCalendar] Failed query returned:', failedResponse.results?.length, 'items'); + console.log('[ContentCalendar] Review query returned:', reviewResponse.results?.length, 'items'); console.log('[ContentCalendar] Approved query returned:', approvedResponse.results?.length, 'items'); console.log('[ContentCalendar] Published query returned:', publishedResponse.results?.length, 'items'); console.log('[ContentCalendar] Total unique items after deduplication:', uniqueItems.length); diff --git a/frontend/src/pages/Writer/Approved.tsx b/frontend/src/pages/Writer/Approved.tsx index 6ff06419..db433115 100644 --- a/frontend/src/pages/Writer/Approved.tsx +++ b/frontend/src/pages/Writer/Approved.tsx @@ -290,8 +290,10 @@ export default function Approved() { }) }); - if (response.success && response.data?.results?.[0]?.success) { - const result = response.data.results[0]; + // Note: fetchAPI unwraps the response, so response IS the data object + // Response format: { success: true, results: [{ success: true, url: '...', external_id: '...' }] } + if (response.success && response.results?.[0]?.success) { + const result = response.results[0]; // Phase 3: Processing (50-75%) setSinglePublishState(prev => prev ? { ...prev, progress: 50, statusMessage: 'Processing response...' } : null); @@ -313,7 +315,7 @@ export default function Approved() { loadContent(); } else { - throw new Error(response.error || 'Failed to publish'); + throw new Error(response.error || response.results?.[0]?.error || 'Failed to publish'); } } catch (error: any) { console.error('Publish error:', error); @@ -412,6 +414,8 @@ export default function Approved() { try { const contentIds = selectedIds.map(id => parseInt(id)); + // Note: fetchAPI unwraps success_response, so response IS the data object + // Response format: { scheduled_count: N, schedule_preview: [...], site_settings: {...} } const response = await fetchAPI('/v1/writer/content/bulk_schedule_preview/', { method: 'POST', body: JSON.stringify({ @@ -420,11 +424,13 @@ export default function Approved() { }) }); - if (response.success) { - setBulkSchedulePreview(response.data); + // fetchAPI throws on error, so if we get here, response is successful + // Response is the unwrapped data object + if (response && response.schedule_preview) { + setBulkSchedulePreview(response); setShowBulkSchedulePreviewModal(true); } else { - throw new Error(response.error || 'Failed to generate preview'); + throw new Error('Invalid response from server'); } } catch (error: any) { toast.error(`Failed to generate schedule preview: ${error.message}`); @@ -437,6 +443,7 @@ export default function Approved() { try { const contentIds = selectedIds.map(id => parseInt(id)); + // Note: fetchAPI unwraps success_response, so response IS the data object const response = await fetchAPI('/v1/writer/content/bulk_schedule/', { method: 'POST', body: JSON.stringify({ @@ -446,14 +453,15 @@ export default function Approved() { }) }); - if (response.success) { - toast.success(`Scheduled ${response.data.scheduled_count} item(s)`); + // fetchAPI throws on error, so if we get here, response is successful + if (response && response.scheduled_count !== undefined) { + toast.success(`Scheduled ${response.scheduled_count} item(s)`); setShowBulkSchedulePreviewModal(false); setBulkSchedulePreview(null); setSelectedIds([]); loadContent(); } else { - throw new Error(response.error || 'Failed to schedule'); + throw new Error('Invalid response from server'); } } catch (error: any) { toast.error(`Failed to schedule: ${error.message}`); @@ -568,9 +576,10 @@ export default function Approved() { }) }); - // Handle response - if (response.success && response.data?.results?.[0]?.success) { - const result = response.data.results[0]; + // Handle response - fetchAPI unwraps the data object + // Response format: { success: true, results: [{ success: true, url: '...', external_id: '...' }] } + if (response.success && response.results?.[0]?.success) { + const result = response.results[0]; // Animate to completion setBulkPublishQueue(prev => prev.map((item, idx) => @@ -589,7 +598,7 @@ export default function Approved() { } : item )); } else { - throw new Error(response.error || 'Unknown error'); + throw new Error(response.error || response.results?.[0]?.error || 'Unknown error'); } } catch (error: any) { console.error(`Error publishing content ${queue[i].contentId}:`, error);