From 4bea79a76d3da18052fbfe1b2f0bee862ae4ed3e Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Sat, 29 Nov 2025 07:20:26 +0000 Subject: [PATCH] 123 --- frontend/src/App.tsx | 44 +- .../components/common/ContentImageCell.tsx | 13 +- frontend/src/config/pages/clusters.config.tsx | 29 +- frontend/src/config/pages/content.config.tsx | 103 ++-- frontend/src/config/pages/ideas.config.tsx | 50 +- frontend/src/config/pages/images.config.tsx | 43 +- frontend/src/config/pages/keywords.config.tsx | 34 +- .../src/config/pages/published.config.tsx | 52 +- frontend/src/config/pages/review.config.tsx | 137 ++--- frontend/src/config/pages/tasks.config.tsx | 59 ++- frontend/src/hooks/useColumnPreferences.ts | 71 +++ frontend/src/layout/AppSidebar.tsx | 9 +- .../src/pages/ContentManager/Dashboard.tsx | 474 ------------------ frontend/src/pages/Settings/DebugStatus.tsx | 48 +- frontend/src/pages/Settings/Publishing.tsx | 4 +- frontend/src/pages/Sites/Dashboard.tsx | 7 - frontend/src/pages/Sites/List.tsx | 14 +- frontend/src/pages/Sites/Preview.tsx | 223 -------- frontend/src/pages/Writer/Content.tsx | 35 +- frontend/src/pages/Writer/Review.tsx | 8 +- .../src/templates/ContentViewTemplate.tsx | 51 +- 21 files changed, 443 insertions(+), 1065 deletions(-) create mode 100644 frontend/src/hooks/useColumnPreferences.ts delete mode 100644 frontend/src/pages/ContentManager/Dashboard.tsx delete mode 100644 frontend/src/pages/Sites/Preview.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 3982f674..0c722110 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -94,14 +94,10 @@ const SiteDashboard = lazy(() => import("./pages/Sites/Dashboard")); const SiteContent = lazy(() => import("./pages/Sites/Content")); const PageManager = lazy(() => import("./pages/Sites/PageManager")); const PostEditor = lazy(() => import("./pages/Sites/PostEditor")); -const SitePreview = lazy(() => import("./pages/Sites/Preview")); const SiteSettings = lazy(() => import("./pages/Sites/Settings")); const SyncDashboard = lazy(() => import("./pages/Sites/SyncDashboard")); const DeploymentPanel = lazy(() => import("./pages/Sites/DeploymentPanel")); -// Content Manager Module - Lazy loaded -const ContentManagerDashboard = lazy(() => import("./pages/ContentManager/Dashboard")); - // Help - Lazy loaded const Help = lazy(() => import("./pages/Help/Help")); const Docs = lazy(() => import("./pages/Help/Docs")); @@ -210,8 +206,8 @@ export default function App() { } /> - {/* Writer Module - Redirect dashboard to content */} - } /> + {/* Writer Module - Redirect dashboard to tasks */} + } /> @@ -258,37 +254,6 @@ export default function App() { } /> - {/* Content Manager Module Routes */} - - - - } /> - - - - } /> - - - - } /> - - - - } /> - - - - } /> - - - - } /> {/* Linker Module - Redirect dashboard to content */} } /> @@ -532,11 +497,6 @@ export default function App() { } /> - - - - } /> diff --git a/frontend/src/components/common/ContentImageCell.tsx b/frontend/src/components/common/ContentImageCell.tsx index 85027bcd..d643adb5 100644 --- a/frontend/src/components/common/ContentImageCell.tsx +++ b/frontend/src/components/common/ContentImageCell.tsx @@ -20,9 +20,11 @@ export interface ContentImageData { interface ContentImageCellProps { image: ContentImageData | null; maxPromptLength?: number; + showPrompt?: boolean; // New prop to control prompt visibility + onImageClick?: () => void; // Optional click handler } -export default function ContentImageCell({ image, maxPromptLength = 100 }: ContentImageCellProps) { +export default function ContentImageCell({ image, maxPromptLength = 100, showPrompt = false, onImageClick }: ContentImageCellProps) { const [showFullPrompt, setShowFullPrompt] = useState(false); // Check if image_path is a valid local file path (not a URL) @@ -65,8 +67,8 @@ export default function ContentImageCell({ image, maxPromptLength = 100 }: Conte return (
- {/* Prompt Text */} - {prompt && ( + {/* Prompt Text - Only show if showPrompt is true */} + {showPrompt && prompt && (

{displayPrompt} @@ -83,7 +85,10 @@ export default function ContentImageCell({ image, maxPromptLength = 100 }: Conte )} {/* Image Display */} -

+
{image.status === 'pending' && (
diff --git a/frontend/src/config/pages/clusters.config.tsx b/frontend/src/config/pages/clusters.config.tsx index 25be90be..4b02fd25 100644 --- a/frontend/src/config/pages/clusters.config.tsx +++ b/frontend/src/config/pages/clusters.config.tsx @@ -118,8 +118,8 @@ export const createClustersPageConfig = ( ...(showSectorColumn ? [{ ...sectorColumn, render: (value: string, row: Cluster) => ( - - {row.sector_name || '-'} + + {row.sector_name || '-'} ), }] : []), @@ -156,7 +156,6 @@ export const createClustersPageConfig = ( align: 'center' as const, render: (value: number) => { const difficultyNum = getDifficultyNumber(value); - const difficultyBadgeVariant = 'light'; const difficultyBadgeColor = typeof difficultyNum === 'number' && difficultyNum === 1 ? 'success' @@ -170,12 +169,8 @@ export const createClustersPageConfig = ( ? 'error' : 'light'; return typeof difficultyNum === 'number' ? ( - - {difficultyNum} + + {difficultyNum} ) : ( difficultyNum @@ -194,14 +189,14 @@ export const createClustersPageConfig = ( ...statusColumn, sortable: true, sortField: 'status', - render: (value: string) => ( - - {value} - - ), + render: (value: string) => { + const properCase = value ? value.charAt(0).toUpperCase() + value.slice(1) : '-'; + return ( + + {properCase} + + ); + }, }, { ...createdColumn, diff --git a/frontend/src/config/pages/content.config.tsx b/frontend/src/config/pages/content.config.tsx index ec60b83f..c6f7a23f 100644 --- a/frontend/src/config/pages/content.config.tsx +++ b/frontend/src/config/pages/content.config.tsx @@ -82,7 +82,7 @@ export const createContentPageConfig = ( statusFilter: string; setStatusFilter: (value: string) => void; setCurrentPage: (page: number) => void; - onViewContent?: (row: Content) => void; + onRowClick?: (row: Content) => void; } ): ContentPageConfig => { const showSectorColumn = !handlers.activeSector; @@ -99,14 +99,11 @@ export const createContentPageConfig = ( ...titleColumn, sortable: true, sortField: 'title', - toggleable: true, - toggleContentKey: 'content_html', - toggleContentLabel: 'Generated Content', render: (value: string, row: Content) => (
- {handlers.onViewContent ? ( + {handlers.onRowClick ? ( + ) : ( + - )} -
+ ); }, }); diff --git a/frontend/src/config/pages/keywords.config.tsx b/frontend/src/config/pages/keywords.config.tsx index 656a505d..ca9abfcf 100644 --- a/frontend/src/config/pages/keywords.config.tsx +++ b/frontend/src/config/pages/keywords.config.tsx @@ -151,8 +151,8 @@ export const createKeywordsPageConfig = ( ...(showSectorColumn ? [{ ...sectorColumn, render: (value: string, row: Keyword) => ( - - {row.sector_name || '-'} + + {row.sector_name || '-'} ), }] : []), @@ -175,26 +175,21 @@ export const createKeywordsPageConfig = ( align: 'center' as const, render: (value: number) => { const difficultyNum = getDifficultyNumber(value); - const difficultyBadgeVariant = 'light'; const difficultyBadgeColor = typeof difficultyNum === 'number' && difficultyNum === 1 ? 'success' : typeof difficultyNum === 'number' && difficultyNum === 2 ? 'success' : typeof difficultyNum === 'number' && difficultyNum === 3 - ? 'warning' + ? 'amber' : typeof difficultyNum === 'number' && difficultyNum === 4 ? 'error' : typeof difficultyNum === 'number' && difficultyNum === 5 ? 'error' - : 'light'; + : 'gray'; return typeof difficultyNum === 'number' ? ( - - {difficultyNum} + + {difficultyNum} ) : ( difficultyNum @@ -220,13 +215,10 @@ export const createKeywordsPageConfig = ( return 'info'; // Blue for informational or default }; + const properCase = value ? value.charAt(0).toUpperCase() + value.slice(1) : '-'; return ( - - {value} + + {properCase} ); }, @@ -236,18 +228,20 @@ export const createKeywordsPageConfig = ( sortable: true, sortField: 'status', render: (value: string) => { + const properCase = value ? value.charAt(0).toUpperCase() + value.slice(1) : '-'; return ( - {value} + {properCase} ); }, diff --git a/frontend/src/config/pages/published.config.tsx b/frontend/src/config/pages/published.config.tsx index 315c4bc7..94417082 100644 --- a/frontend/src/config/pages/published.config.tsx +++ b/frontend/src/config/pages/published.config.tsx @@ -92,14 +92,14 @@ export function createPublishedPageConfig(params: { sortField: 'status', width: '120px', render: (value: string) => { - const statusConfig: Record = { - draft: { color: 'warning', label: 'Draft' }, + const statusConfig: Record = { + draft: { color: 'amber', label: 'Draft' }, published: { color: 'success', label: 'Published' }, }; - const config = statusConfig[value] || { color: 'warning' as const, label: value }; + const config = statusConfig[value] || { color: 'amber' as const, label: value }; return ( - - {config.label} + + {config.label} ); }, @@ -112,15 +112,15 @@ export function createPublishedPageConfig(params: { render: (_value: any, row: Content) => { if (row.external_id && row.external_url) { return ( - + - Published + Published ); } return ( - - Not Published + + Not Published ); }, @@ -130,24 +130,34 @@ export function createPublishedPageConfig(params: { label: 'Type', sortable: true, sortField: 'content_type', - width: '120px', - render: (value: string) => ( - - {TYPE_LABELS[value] || value || '-'} - - ), + width: '110px', + render: (value: string) => { + const label = TYPE_LABELS[value] || value || '-'; + const properCase = label.charAt(0).toUpperCase() + label.slice(1); + return ( + + {properCase} + + ); + }, }, { key: 'content_structure', label: 'Structure', sortable: true, sortField: 'content_structure', - width: '150px', - render: (value: string) => ( - - {STRUCTURE_LABELS[value] || value || '-'} - - ), + width: '130px', + render: (value: string) => { + const label = STRUCTURE_LABELS[value] || value || '-'; + const properCase = label.split(/[_\s]+/).map(word => + word.charAt(0).toUpperCase() + word.slice(1) + ).join(' '); + return ( + + {properCase} + + ); + }, }, { key: 'word_count', diff --git a/frontend/src/config/pages/review.config.tsx b/frontend/src/config/pages/review.config.tsx index 658d9837..9ded6b52 100644 --- a/frontend/src/config/pages/review.config.tsx +++ b/frontend/src/config/pages/review.config.tsx @@ -52,87 +52,104 @@ export function createReviewPageConfig(params: { setStatusFilter: (value: string) => void; setCurrentPage: (page: number) => void; activeSector: { id: number; name: string } | null; + onRowClick?: (row: Content) => void; }): ReviewPageConfig { const showSectorColumn = !params.activeSector; const columns: ColumnConfig[] = [ - { - key: 'categories', - label: 'Categories', - sortable: false, - width: '180px', - render: (_value: any, row: Content) => { - const categories = row.taxonomy_terms_data?.filter((t: any) => t.taxonomy_type === 'category') || []; - if (!categories.length) return -; - return ( -
- {categories.map((cat: any) => ( - {cat.name} - ))} -
- ); - }, - toggleable: true, - defaultVisible: false, - }, - { - key: 'tags', - label: 'Tags', - sortable: false, - width: '180px', - render: (_value: any, row: Content) => { - const tags = row.taxonomy_terms_data?.filter((t: any) => t.taxonomy_type === 'tag') || []; - if (!tags.length) return -; - return ( -
- {tags.map((tag: any) => ( - {tag.name} - ))} -
- ); - }, - toggleable: true, - defaultVisible: false, - }, { key: 'title', label: 'Title', sortable: true, sortField: 'title', - toggleable: true, - toggleContentKey: 'content_html', - toggleContentLabel: 'Generated Content', render: (value: string, row: Content) => (
- - {value || `Content #${row.id}`} - + {params.onRowClick ? ( + + ) : ( + + {value || `Content #${row.id}`} + + )}
), }, + { + key: 'categories', + label: 'Categories', + sortable: false, + width: '180px', + render: (_value: any, row: Content) => { + const categories = row.taxonomy_terms_data?.filter((t: any) => t.taxonomy_type === 'category') || []; + if (!categories.length) return -; + return ( +
+ {categories.map((cat: any) => ( + {cat.name} + ))} +
+ ); + }, + toggleable: true, + defaultVisible: true, + }, + { + key: 'tags', + label: 'Tags', + sortable: false, + width: '180px', + render: (_value: any, row: Content) => { + const tags = row.taxonomy_terms_data?.filter((t: any) => t.taxonomy_type === 'tag') || []; + if (!tags.length) return -; + return ( +
+ {tags.map((tag: any) => ( + {tag.name} + ))} +
+ ); + }, + toggleable: true, + defaultVisible: true, + }, { key: 'content_type', label: 'Type', sortable: true, sortField: 'content_type', - width: '120px', - render: (value: string) => ( - - {TYPE_LABELS[value] || value || '-'} - - ), + width: '110px', + render: (value: string) => { + const label = TYPE_LABELS[value] || value || '-'; + const properCase = label.charAt(0).toUpperCase() + label.slice(1); + return ( + + {properCase} + + ); + }, }, { key: 'content_structure', label: 'Structure', sortable: true, sortField: 'content_structure', - width: '150px', - render: (value: string) => ( - - {STRUCTURE_LABELS[value] || value || '-'} - - ), + width: '130px', + render: (value: string) => { + const label = STRUCTURE_LABELS[value] || value || '-'; + const properCase = label.split(/[_\s]+/).map(word => + word.charAt(0).toUpperCase() + word.slice(1) + ).join(' '); + return ( + + {properCase} + + ); + }, }, { key: 'cluster_name', @@ -145,8 +162,8 @@ export function createReviewPageConfig(params: { return -; } return ( - - {clusterName} + + {clusterName} ); }, @@ -186,8 +203,8 @@ export function createReviewPageConfig(params: { sortable: false, width: '120px', render: (value: string, row: Content) => ( - - {row.sector_name || '-'} + + {row.sector_name || '-'} ), }); diff --git a/frontend/src/config/pages/tasks.config.tsx b/frontend/src/config/pages/tasks.config.tsx index c5ab7d4f..aa1335b9 100644 --- a/frontend/src/config/pages/tasks.config.tsx +++ b/frontend/src/config/pages/tasks.config.tsx @@ -116,8 +116,8 @@ export const createTasksPageConfig = ( {displayTitle} {isSiteBuilder && ( - - Site Builder + + Site Builder )}
@@ -128,8 +128,8 @@ export const createTasksPageConfig = ( ...(showSectorColumn ? [{ ...sectorColumn, render: (value: string, row: Task) => ( - - {row.sector_name || '-'} + + {row.sector_name || '-'} ), }] : []), @@ -153,53 +153,60 @@ export const createTasksPageConfig = ( return -; } return ( - - {taxonomyName} + + {taxonomyName} ); }, }, { key: 'content_type', - label: 'Content Type', + label: 'Type', sortable: true, sortField: 'content_type', - width: '120px', - render: (value: string) => ( - - {TYPE_LABELS[value] || value || '-'} - - ), + width: '110px', + render: (value: string) => { + const label = TYPE_LABELS[value] || value || '-'; + const properCase = label.charAt(0).toUpperCase() + label.slice(1); + return ( + + {properCase} + + ); + }, }, { key: 'content_structure', label: 'Structure', sortable: true, sortField: 'content_structure', - width: '150px', - render: (value: string) => ( - - {STRUCTURE_LABELS[value] || value || '-'} - - ), + width: '130px', + render: (value: string) => { + const label = STRUCTURE_LABELS[value] || value || '-'; + const properCase = label.split(/[_\s]+/).map(word => + word.charAt(0).toUpperCase() + word.slice(1) + ).join(' '); + return ( + + {properCase} + + ); + }, }, { ...statusColumn, sortable: true, sortField: 'status', render: (value: string) => { - const statusColors: Record = { - queued: 'warning', + const statusColors: Record = { + queued: 'amber', completed: 'success', }; const label = value ? value.replace('_', ' ') : ''; const formatted = label ? label.charAt(0).toUpperCase() + label.slice(1) : ''; return ( - - {formatted} + + {formatted} ); }, diff --git a/frontend/src/hooks/useColumnPreferences.ts b/frontend/src/hooks/useColumnPreferences.ts new file mode 100644 index 00000000..0e544150 --- /dev/null +++ b/frontend/src/hooks/useColumnPreferences.ts @@ -0,0 +1,71 @@ +/** + * useColumnPreferences Hook + * Manages column visibility persistence in localStorage per page + */ + +import { useState, useEffect, useCallback } from 'react'; + +interface UseColumnPreferencesOptions { + storageKey: string; // Unique key for each page (e.g., 'writer_tasks_columns') + defaultColumns: string[]; // Default visible column keys +} + +export function useColumnPreferences({ storageKey, defaultColumns }: UseColumnPreferencesOptions) { + // Initialize visible columns from localStorage or defaults + const [visibleColumns, setVisibleColumns] = useState(() => { + try { + const stored = localStorage.getItem(storageKey); + if (stored) { + const parsed = JSON.parse(stored); + if (Array.isArray(parsed) && parsed.length > 0) { + return parsed; + } + } + } catch (error) { + console.warn('Failed to load column preferences from localStorage:', error); + } + return defaultColumns; + }); + + // Save to localStorage whenever visibleColumns changes + useEffect(() => { + try { + localStorage.setItem(storageKey, JSON.stringify(visibleColumns)); + } catch (error) { + console.warn('Failed to save column preferences to localStorage:', error); + } + }, [storageKey, visibleColumns]); + + // Toggle column visibility + const toggleColumn = useCallback((columnKey: string) => { + setVisibleColumns((prev) => { + if (prev.includes(columnKey)) { + // Don't allow hiding the last column + if (prev.length === 1) { + return prev; + } + return prev.filter((key) => key !== columnKey); + } else { + return [...prev, columnKey]; + } + }); + }, []); + + // Check if a column is visible + const isColumnVisible = useCallback( + (columnKey: string) => visibleColumns.includes(columnKey), + [visibleColumns] + ); + + // Reset to defaults + const resetToDefaults = useCallback(() => { + setVisibleColumns(defaultColumns); + }, [defaultColumns]); + + return { + visibleColumns, + toggleColumn, + isColumnVisible, + resetToDefaults, + }; +} diff --git a/frontend/src/layout/AppSidebar.tsx b/frontend/src/layout/AppSidebar.tsx index 7869013a..996f4ba5 100644 --- a/frontend/src/layout/AppSidebar.tsx +++ b/frontend/src/layout/AppSidebar.tsx @@ -130,17 +130,10 @@ const AppSidebar: React.FC = () => { workflowItems.push({ icon: , name: "Writer", - path: "/writer/content", // Default to content, submenus shown as in-page navigation + path: "/writer/tasks", // Default to tasks, submenus shown as in-page navigation }); } - // Add Content Manager (always enabled - single item, no dropdown) - workflowItems.push({ - icon: , - name: "Content Manager", - path: "/content-manager", // Default to all content, submenus shown as in-page navigation - }); - // Add Linker if enabled (single item, no dropdown) if (moduleEnabled('linker')) { workflowItems.push({ diff --git a/frontend/src/pages/ContentManager/Dashboard.tsx b/frontend/src/pages/ContentManager/Dashboard.tsx deleted file mode 100644 index 1078cbf0..00000000 --- a/frontend/src/pages/ContentManager/Dashboard.tsx +++ /dev/null @@ -1,474 +0,0 @@ -/** - * Content Manager Module - Main Dashboard - * Full-featured CMS with site selector, standard filtering, and WYSIWYG editing - */ -import { useState, useEffect, useMemo } from 'react'; -import { useNavigate } from 'react-router-dom'; -import PageMeta from '../../components/common/PageMeta'; -import PageHeader from '../../components/common/PageHeader'; -import ModuleNavigationTabs from '../../components/navigation/ModuleNavigationTabs'; -import { Card } from '../../components/ui/card'; -import Button from '../../components/ui/button/Button'; -import { useToast } from '../../components/ui/toast/ToastContainer'; -import { fetchAPI } from '../../services/api'; -import { useSiteStore } from '../../store/siteStore'; -import { useSectorStore } from '../../store/sectorStore'; -import { - PencilIcon, - EyeIcon, - TrashBinIcon, - PlusIcon, - FileIcon -} from '../../icons'; -import { Search, Filter } from 'lucide-react'; - -interface ContentItem { - id: number; - title: string; - status: string; - updated_at: string; - source: string; - content_type?: string; - content_structure?: string; - cluster_name?: string; - external_url?: string; - created_at: string; -} - -const STATUS_OPTIONS = [ - { value: '', label: 'All Statuses' }, - { value: 'draft', label: 'Draft' }, - { value: 'published', label: 'Published' }, - { value: 'scheduled', label: 'Scheduled' }, -]; - -const SOURCE_OPTIONS = [ - { value: '', label: 'All Sources' }, - { value: 'igny8', label: 'IGNY8 Generated' }, - { value: 'wordpress', label: 'WordPress' }, - { value: 'shopify', label: 'Shopify' }, - { value: 'custom', label: 'Custom API' }, -]; - -const CONTENT_TYPE_OPTIONS = [ - { value: '', label: 'All Types' }, - { value: 'post', label: 'Blog Post' }, - { value: 'page', label: 'Page' }, - { value: 'product', label: 'Product' }, -]; - -// Content type icon and color mapping -const getContentTypeStyle = (contentType?: string) => { - switch (contentType?.toLowerCase()) { - case 'post': - return { - icon: '📝', - color: 'text-blue-600 dark:text-blue-400', - bgColor: 'bg-blue-100 dark:bg-blue-900/30', - }; - case 'page': - return { - icon: '📄', - color: 'text-green-600 dark:text-green-400', - bgColor: 'bg-green-100 dark:bg-green-900/30', - }; - case 'product': - return { - icon: '🛍️', - color: 'text-purple-600 dark:text-purple-400', - bgColor: 'bg-purple-100 dark:bg-purple-900/30', - }; - default: - return { - icon: '📋', - color: 'text-gray-600 dark:text-gray-400', - bgColor: 'bg-gray-100 dark:bg-gray-900/30', - }; - } -}; - -// Status badge styling -const getStatusBadge = (status: string) => { - switch (status?.toLowerCase()) { - case 'published': - return 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'; - case 'draft': - return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400'; - case 'scheduled': - return 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400'; - default: - return 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-400'; - } -}; - -export default function ContentManagerDashboard() { - const navigate = useNavigate(); - const toast = useToast(); - const { activeSite } = useSiteStore(); - const { activeSector } = useSectorStore(); - - const [content, setContent] = useState([]); - const [loading, setLoading] = useState(true); - const [searchTerm, setSearchTerm] = useState(''); - const [statusFilter, setStatusFilter] = useState(''); - const [sourceFilter, setSourceFilter] = useState(''); - const [contentTypeFilter, setContentTypeFilter] = useState(''); - const [sortBy, setSortBy] = useState<'created_at' | 'updated_at' | 'title'>('created_at'); - const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc'); - const [currentPage, setCurrentPage] = useState(1); - const [totalPages, setTotalPages] = useState(1); - const [totalCount, setTotalCount] = useState(0); - const pageSize = 20; - - // Navigation tabs for Content Manager module - const navigationTabs = [ - { id: 'content', label: 'All Content', path: '/content-manager' }, - { id: 'posts', label: 'Posts', path: '/content-manager/posts' }, - { id: 'pages', label: 'Pages', path: '/content-manager/pages' }, - ]; - - useEffect(() => { - if (activeSite?.id) { - loadContent(); - } - }, [activeSite, currentPage, statusFilter, sourceFilter, contentTypeFilter, searchTerm, sortBy, sortDirection]); - - const loadContent = async () => { - if (!activeSite?.id) { - setLoading(false); - return; - } - - try { - setLoading(true); - const params = new URLSearchParams({ - site_id: activeSite.id.toString(), - page: currentPage.toString(), - page_size: pageSize.toString(), - ordering: sortDirection === 'desc' ? `-${sortBy}` : sortBy, - }); - - if (searchTerm) { - params.append('search', searchTerm); - } - if (statusFilter) { - params.append('status', statusFilter); - } - if (sourceFilter) { - params.append('source', sourceFilter); - } - if (contentTypeFilter) { - params.append('content_type', contentTypeFilter); - } - - const data = await fetchAPI(`/v1/writer/content/?${params.toString()}`); - const contentList = Array.isArray(data?.results) ? data.results : Array.isArray(data) ? data : []; - setContent(contentList); - setTotalCount(data?.count || contentList.length); - setTotalPages(data?.total_pages || Math.ceil((data?.count || contentList.length) / pageSize)); - } catch (error: any) { - toast.error(`Failed to load content: ${error.message}`); - setContent([]); - } finally { - setLoading(false); - } - }; - - const handleDelete = async (id: number) => { - if (!confirm('Are you sure you want to delete this content?')) return; - - try { - await fetchAPI(`/v1/writer/content/${id}/`, { - method: 'DELETE', - }); - toast.success('Content deleted successfully'); - loadContent(); - } catch (error: any) { - toast.error(`Failed to delete content: ${error.message}`); - } - }; - - const handleClearFilters = () => { - setSearchTerm(''); - setStatusFilter(''); - setSourceFilter(''); - setContentTypeFilter(''); - setSortBy('created_at'); - setSortDirection('desc'); - setCurrentPage(1); - }; - - const hasActiveFilters = searchTerm || statusFilter || sourceFilter || contentTypeFilter; - - return ( -
- - - , color: 'purple' }} - /> - - - - {/* Action Bar */} -
-
- {totalCount} total items - {activeSite && • {activeSite.name}} -
- -
- - {/* Standard Filter Bar */} - -
- {/* Search and Primary Filters */} -
- {/* Search */} -
- - { - setSearchTerm(e.target.value); - setCurrentPage(1); - }} - className="w-full pl-10 pr-3 py-2 border border-gray-300 dark:border-gray-700 rounded-lg dark:bg-gray-800 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent" - /> -
- - {/* Status Filter */} - - - {/* Content Type Filter */} - - - {/* Source Filter */} - - - {/* Sort */} - -
- - {/* Clear Filters */} - {hasActiveFilters && ( -
- -
- )} -
-
- - {/* No Site Selected Warning */} - {!activeSite && ( - -
- -

No Site Selected

-

Please select a site from the dropdown above to manage content.

-
-
- )} - - {/* Content List */} - {activeSite && ( - <> - {loading ? ( - -
Loading content...
-
- ) : content.length === 0 ? ( - -

- {hasActiveFilters ? 'No content matches your filters' : 'No content found'} -

- {!hasActiveFilters && ( - - )} -
- ) : ( - <> - -
- {content.map((item) => { - const typeStyle = getContentTypeStyle(item.content_type); - - return ( -
-
- {/* Content Info */} -
-
- {/* Content Type Icon */} - - {typeStyle.icon} - - - {/* Title */} -

- {item.title || `Content #${item.id}`} -

- - {/* Status Badge */} - - {item.status} - -
- - {/* Meta Information */} -
- {item.content_type && ( - - {item.content_type} - - )} - {item.content_structure && ( - {item.content_structure} - )} - - - {item.source} - - {item.cluster_name && ( - Cluster: {item.cluster_name} - )} - - Updated {new Date(item.updated_at).toLocaleDateString()} - -
-
- - {/* Action Buttons */} -
- {/* View - Opens content detail view in Writer */} - - {/* Edit - Opens post editor */} - - {/* Delete */} - -
-
-
- ); - })} -
-
- - {/* Pagination */} - {totalPages > 1 && ( -
- - - Page {currentPage} of {totalPages} - - -
- )} - - )} - - )} -
- ); -} diff --git a/frontend/src/pages/Settings/DebugStatus.tsx b/frontend/src/pages/Settings/DebugStatus.tsx index 93121c1c..6ddff77e 100644 --- a/frontend/src/pages/Settings/DebugStatus.tsx +++ b/frontend/src/pages/Settings/DebugStatus.tsx @@ -458,45 +458,6 @@ export default function DebugStatus() { return checks; }, []); - // Check Content Manager module health (taxonomy relations) - const checkContentManagerModule = useCallback(async (): Promise => { - const checks: HealthCheck[] = []; - - // Check Taxonomy endpoint - try { - const { response: taxonomyResp, data: taxonomyData } = await apiCall('/v1/writer/taxonomy/'); - - if (taxonomyResp.ok && taxonomyData?.success !== false) { - checks.push({ - name: 'Taxonomy System', - description: 'Content taxonomy endpoint', - status: 'healthy', - message: `Found ${taxonomyData?.count || 0} taxonomy items`, - lastChecked: new Date().toISOString(), - }); - } else { - checks.push({ - name: 'Taxonomy System', - description: 'Content taxonomy endpoint', - status: 'error', - message: taxonomyData?.error || `Failed with ${taxonomyResp.status}`, - details: 'Check ContentTaxonomyRelation through model and field mappings', - lastChecked: new Date().toISOString(), - }); - } - } catch (error: any) { - checks.push({ - name: 'Taxonomy System', - description: 'Content taxonomy endpoint', - status: 'error', - message: error.message || 'Network error', - lastChecked: new Date().toISOString(), - }); - } - - return checks; - }, []); - // Run all health checks const runAllChecks = useCallback(async () => { setLoading(true); @@ -506,12 +467,11 @@ export default function DebugStatus() { const schemaCheck = await checkDatabaseSchemaMapping(); // Run module checks in parallel - const [writerChecks, plannerChecks, sitesChecks, integrationChecks, contentMgrChecks] = await Promise.all([ + const [writerChecks, plannerChecks, sitesChecks, integrationChecks] = await Promise.all([ checkWriterModule(), checkPlannerModule(), checkSitesModule(), checkIntegrationModule(), - checkContentManagerModule(), ]); // Build module health results @@ -541,11 +501,6 @@ export default function DebugStatus() { description: 'External platform sync (WordPress, etc.)', checks: integrationChecks, }, - { - module: 'Content Manager', - description: 'Taxonomy and content organization', - checks: contentMgrChecks, - }, ]; setModuleHealths(moduleHealthResults); @@ -560,7 +515,6 @@ export default function DebugStatus() { checkPlannerModule, checkSitesModule, checkIntegrationModule, - checkContentManagerModule, ]); // Run checks on mount diff --git a/frontend/src/pages/Settings/Publishing.tsx b/frontend/src/pages/Settings/Publishing.tsx index 6952ceb8..d06337ee 100644 --- a/frontend/src/pages/Settings/Publishing.tsx +++ b/frontend/src/pages/Settings/Publishing.tsx @@ -8,8 +8,8 @@ import { Card } from '../../components/ui/card'; import Button from '../../components/ui/button/Button'; import Checkbox from '../../components/form/input/Checkbox'; import Label from '../../components/form/Label'; -import Input from '../../components/form/input/Input'; -import Select from '../../components/form/input/Select'; +import Input from '../../components/form/input/InputField'; +import Select from '../../components/form/SelectDropdown'; import PublishingRules, { PublishingRule } from '../../components/publishing/PublishingRules'; import { useToast } from '../../components/ui/toast/ToastContainer'; import { fetchAPI } from '../../services/api'; diff --git a/frontend/src/pages/Sites/Dashboard.tsx b/frontend/src/pages/Sites/Dashboard.tsx index 55fda5fd..9864331d 100644 --- a/frontend/src/pages/Sites/Dashboard.tsx +++ b/frontend/src/pages/Sites/Dashboard.tsx @@ -202,13 +202,6 @@ export default function SiteDashboard() {

)}
- - )} -
- )} - -
- ); - } - - return ( -
- - - {!isFullscreen && ( -
-
-

- Site Preview -

-

- Live preview of your deployed site -

-
-
- - - -
-
- )} - - {isFullscreen && ( -
- -
- )} - - -
-