feat: Complete Stage 2 frontend refactor

- Removed deprecated fields from Content and Task models, including entity_type, sync_status, and cluster_role.
- Updated Content model to include new fields: content_type, content_structure, taxonomy_terms, source, external_id, and cluster_id.
- Refactored Writer module components (Content, ContentView, Dashboard, Tasks) to align with new schema.
- Enhanced Dashboard metrics and removed unused filters.
- Implemented ClusterDetail page to display cluster information and associated content.
- Updated API service interfaces to reflect changes in data structure.
- Adjusted sorting and filtering logic across various components to accommodate new field names and types.
- Improved user experience by providing loading states and error handling in data fetching.
This commit is contained in:
IGNY8 VPS (Salman)
2025-11-25 18:17:17 +00:00
parent a5ef36016c
commit 807ced7527
19 changed files with 1045 additions and 740 deletions

View File

@@ -37,7 +37,6 @@ export default function Content() {
const [searchTerm, setSearchTerm] = useState('');
const [statusFilter, setStatusFilter] = useState('');
const [sourceFilter, setSourceFilter] = useState('');
const [syncStatusFilter, setSyncStatusFilter] = useState('');
const [selectedIds, setSelectedIds] = useState<string[]>([]);
// Pagination state
@@ -46,7 +45,7 @@ export default function Content() {
const [totalCount, setTotalCount] = useState(0);
// Sorting state
const [sortBy, setSortBy] = useState<string>('generated_at');
const [sortBy, setSortBy] = useState<string>('created_at');
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
const [showContent, setShowContent] = useState(false);
@@ -59,13 +58,12 @@ export default function Content() {
setLoading(true);
setShowContent(false);
try {
const ordering = sortBy ? `${sortDirection === 'desc' ? '-' : ''}${sortBy}` : '-generated_at';
const ordering = sortBy ? `${sortDirection === 'desc' ? '-' : ''}${sortBy}` : '-created_at';
const filters: ContentFilters = {
...(searchTerm && { search: searchTerm }),
...(statusFilter && { status: statusFilter }),
...(sourceFilter && { source: sourceFilter }),
...(syncStatusFilter && { sync_status: syncStatusFilter }),
page: currentPage,
page_size: pageSize,
ordering,
@@ -224,7 +222,6 @@ export default function Content() {
search: searchTerm,
status: statusFilter,
source: sourceFilter,
sync_status: syncStatusFilter,
}}
onFilterChange={(key: string, value: any) => {
if (key === 'search') {
@@ -235,9 +232,6 @@ export default function Content() {
} else if (key === 'source') {
setSourceFilter(value);
setCurrentPage(1);
} else if (key === 'sync_status') {
setSyncStatusFilter(value);
setCurrentPage(1);
}
}}
pagination={{
@@ -257,7 +251,7 @@ export default function Content() {
}}
headerMetrics={headerMetrics}
onRowAction={handleRowAction}
getItemDisplayName={(row: ContentType) => row.meta_title || row.title || `Content #${row.id}`}
getItemDisplayName={(row: ContentType) => row.title || `Content #${row.id}`}
/>
{/* Module Metrics Footer */}
@@ -274,17 +268,10 @@ export default function Content() {
{
title: 'Draft',
value: content.filter(c => c.status === 'draft').length.toLocaleString(),
subtitle: `${content.filter(c => c.status === 'review').length} in review`,
subtitle: `${content.filter(c => c.status === 'published').length} published`,
icon: <TaskIcon className="w-5 h-5" />,
accentColor: 'blue',
},
{
title: 'Synced',
value: content.filter(c => c.sync_status === 'synced').length.toLocaleString(),
subtitle: `${content.filter(c => c.sync_status === 'pending').length} pending`,
icon: <CheckCircleIcon className="w-5 h-5" />,
accentColor: 'purple',
},
]}
progress={{
label: 'Content Publishing Progress',

View File

@@ -57,8 +57,8 @@ export default function ContentView() {
return (
<>
<PageMeta
title={content ? `${content.meta_title || content.title || `Content #${content.id}`} - IGNY8` : 'Content View - IGNY8'}
description={content?.meta_description || 'View content details and metadata'}
title={content ? `${content.title || `Content #${content.id}`} - IGNY8` : 'Content View - IGNY8'}
description='View content details and metadata'
/>
<ContentViewTemplate
content={content}

View File

@@ -20,12 +20,13 @@ import {
PaperPlaneIcon,
PlugInIcon,
} from "../../icons";
import {
import {
fetchTasks,
fetchContent,
fetchContentImages,
fetchTaxonomies,
fetchAttributes
// TODO: Stage 3/4 - Add when taxonomy and attribute endpoints are implemented
// fetchTaxonomies,
// fetchAttributes
} from "../../services/api";
import { useSiteStore } from "../../store/siteStore";
import { useSectorStore } from "../../store/sectorStore";
@@ -43,7 +44,6 @@ interface WriterStats {
content: {
total: number;
drafts: number;
review: number;
published: number;
totalWordCount: number;
avgWordCount: number;
@@ -85,12 +85,11 @@ export default function WriterDashboard() {
try {
setLoading(true);
const [tasksRes, contentRes, imagesRes, taxonomiesRes, attributesRes] = await Promise.all([
// TODO: Stage 3/4 - Re-enable when taxonomy and attribute endpoints are implemented
const [tasksRes, contentRes, imagesRes] = await Promise.all([
fetchTasks({ page_size: 1000, sector_id: activeSector?.id }),
fetchContent({ page_size: 1000, sector_id: activeSector?.id }),
fetchContentImages({ sector_id: activeSector?.id }),
fetchTaxonomies({ sector_id: activeSector?.id }),
fetchAttributes({ sector_id: activeSector?.id }),
]);
const tasks = tasksRes.results || [];
@@ -101,9 +100,8 @@ export default function WriterDashboard() {
let totalWordCount = 0;
tasks.forEach(t => {
tasksByStatus[t.status || 'pending'] = (tasksByStatus[t.status || 'pending'] || 0) + 1;
if (t.status === 'pending') pendingTasks++;
else if (t.status === 'in_progress') inProgressTasks++;
tasksByStatus[t.status || 'queued'] = (tasksByStatus[t.status || 'queued'] || 0) + 1;
if (t.status === 'queued') pendingTasks++;
else if (t.status === 'completed') completedTasks++;
if (t.word_count) totalWordCount += t.word_count;
});
@@ -112,14 +110,12 @@ export default function WriterDashboard() {
const content = contentRes.results || [];
let drafts = 0;
let review = 0;
let published = 0;
let contentTotalWordCount = 0;
const contentByType: Record<string, number> = {};
content.forEach(c => {
if (c.status === 'draft') drafts++;
else if (c.status === 'review') review++;
else if (c.status === 'published') published++;
if (c.word_count) contentTotalWordCount += c.word_count;
});
@@ -149,11 +145,9 @@ export default function WriterDashboard() {
const contentThisMonth = Math.floor(content.length * 0.7);
const publishRate = content.length > 0 ? Math.round((published / content.length) * 100) : 0;
const taxonomies = taxonomiesRes.results || [];
const attributes = attributesRes.results || [];
const taxonomyCount = taxonomies.length;
const attributeCount = attributes.length;
// TODO: Stage 3/4 - Re-enable when taxonomy and attribute endpoints are implemented
const taxonomyCount = 0; // taxonomiesRes.results?.length || 0
const attributeCount = 0; // attributesRes.results?.length || 0
setStats({
tasks: {
@@ -168,7 +162,6 @@ export default function WriterDashboard() {
content: {
total: content.length,
drafts,
review,
published,
totalWordCount: contentTotalWordCount,
avgWordCount: contentAvgWordCount,
@@ -423,7 +416,7 @@ export default function WriterDashboard() {
enabled: true
},
xaxis: {
categories: ['Drafts', 'In Review', 'Published'],
categories: ['Drafts', 'Published'],
labels: {
style: {
fontFamily: 'Outfit'
@@ -444,7 +437,7 @@ export default function WriterDashboard() {
const series = [{
name: 'Content',
data: [stats.content.drafts, stats.content.review, stats.content.published]
data: [stats.content.drafts, stats.content.published]
}];
return { options, series };

View File

@@ -49,7 +49,6 @@ export default function Tasks() {
const [structureFilter, setStructureFilter] = useState('');
const [typeFilter, setTypeFilter] = useState('');
const [sourceFilter, setSourceFilter] = useState('');
const [entityTypeFilter, setEntityTypeFilter] = useState(''); // Stage 3: Entity type filter
const [selectedIds, setSelectedIds] = useState<string[]>([]);
// Pagination state
@@ -71,8 +70,8 @@ export default function Tasks() {
description: '',
keywords: '',
cluster_id: null,
content_structure: 'blog_post',
content_type: 'blog_post',
content_structure: 'article',
content_type: 'post',
status: 'queued',
word_count: 0,
});
@@ -145,7 +144,6 @@ export default function Tasks() {
...(clusterFilter && { cluster_id: clusterFilter }),
...(structureFilter && { content_structure: structureFilter }),
...(typeFilter && { content_type: typeFilter }),
...(entityTypeFilter && { entity_type: entityTypeFilter }), // Stage 3: Entity type filter
page: currentPage,
page_size: pageSize,
ordering,
@@ -533,8 +531,8 @@ export default function Tasks() {
description: '',
keywords: '',
cluster_id: null,
content_structure: 'blog_post',
content_type: 'blog_post',
content_structure: 'article',
content_type: 'post',
status: 'queued',
word_count: 0,
});
@@ -588,7 +586,6 @@ export default function Tasks() {
content_structure: structureFilter,
content_type: typeFilter,
source: sourceFilter,
entity_type: entityTypeFilter, // Stage 3: Entity type filter
}}
onFilterChange={(key, value) => {
const stringValue = value === null || value === undefined ? '' : String(value);
@@ -604,8 +601,6 @@ export default function Tasks() {
setTypeFilter(stringValue);
} else if (key === 'source') {
setSourceFilter(stringValue);
} else if (key === 'entity_type') { // Stage 3: Entity type filter
setEntityTypeFilter(stringValue);
}
setCurrentPage(1);
}}
@@ -616,8 +611,8 @@ export default function Tasks() {
description: row.description || '',
keywords: row.keywords || '',
cluster_id: row.cluster_id || null,
content_structure: row.content_structure || 'blog_post',
content_type: row.content_type || 'blog_post',
content_structure: row.content_structure || 'article',
content_type: row.content_type || 'post',
status: row.status || 'queued',
word_count: row.word_count || 0,
});