filter fixes
This commit is contained in:
@@ -371,44 +371,25 @@ export function createApprovedPageConfig(params: {
|
||||
key: 'status',
|
||||
label: 'Status',
|
||||
type: 'select',
|
||||
options: params.statusOptions || [
|
||||
{ value: '', label: 'All' },
|
||||
{ value: 'draft', label: 'Draft' },
|
||||
{ value: 'review', label: 'Review' },
|
||||
{ value: 'approved', label: 'Approved' },
|
||||
{ value: 'published', label: 'Published' },
|
||||
],
|
||||
options: params.statusOptions,
|
||||
},
|
||||
{
|
||||
key: 'site_status',
|
||||
label: 'Site Status',
|
||||
type: 'select',
|
||||
options: params.siteStatusOptions || [
|
||||
{ value: '', label: 'All' },
|
||||
{ value: 'not_published', label: 'Not Published' },
|
||||
{ value: 'scheduled', label: 'Scheduled' },
|
||||
{ value: 'publishing', label: 'Publishing' },
|
||||
{ value: 'published', label: 'Published' },
|
||||
{ value: 'failed', label: 'Failed' },
|
||||
],
|
||||
options: params.siteStatusOptions,
|
||||
},
|
||||
{
|
||||
key: 'content_type',
|
||||
label: 'Type',
|
||||
type: 'select',
|
||||
options: params.contentTypeOptions || [
|
||||
{ value: '', label: 'All Types' },
|
||||
...CONTENT_TYPE_OPTIONS,
|
||||
],
|
||||
options: params.contentTypeOptions,
|
||||
},
|
||||
{
|
||||
key: 'content_structure',
|
||||
label: 'Structure',
|
||||
type: 'select',
|
||||
options: params.contentStructureOptions || [
|
||||
{ value: '', label: 'All Structures' },
|
||||
...ALL_CONTENT_STRUCTURES,
|
||||
],
|
||||
options: params.contentStructureOptions,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -282,38 +282,13 @@ export const createClustersPageConfig = (
|
||||
key: 'status',
|
||||
label: 'Status',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Status' },
|
||||
// Use dynamic options if loaded (even if empty array)
|
||||
// Only fall back to defaults if statusOptions is undefined (not loaded yet)
|
||||
...(handlers.statusOptions !== undefined
|
||||
? handlers.statusOptions
|
||||
: [
|
||||
{ value: 'new', label: 'New' },
|
||||
{ value: 'mapped', label: 'Mapped' },
|
||||
]
|
||||
),
|
||||
],
|
||||
options: handlers.statusOptions,
|
||||
},
|
||||
{
|
||||
key: 'difficulty',
|
||||
label: 'Difficulty',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Difficulty' },
|
||||
// Use dynamic options if loaded (even if empty array)
|
||||
// Only fall back to defaults if difficultyOptions is undefined (not loaded yet)
|
||||
...(handlers.difficultyOptions !== undefined
|
||||
? handlers.difficultyOptions
|
||||
: [
|
||||
{ value: '1', label: '1 - Very Easy' },
|
||||
{ value: '2', label: '2 - Easy' },
|
||||
{ value: '3', label: '3 - Medium' },
|
||||
{ value: '4', label: '4 - Hard' },
|
||||
{ value: '5', label: '5 - Very Hard' },
|
||||
]
|
||||
),
|
||||
],
|
||||
options: handlers.difficultyOptions,
|
||||
},
|
||||
{
|
||||
key: 'volume',
|
||||
|
||||
@@ -387,55 +387,19 @@ export const createContentPageConfig = (
|
||||
key: 'content_type',
|
||||
label: 'Content Type',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Types' },
|
||||
...(handlers.contentTypeOptions !== undefined
|
||||
? handlers.contentTypeOptions
|
||||
: CONTENT_TYPE_OPTIONS
|
||||
),
|
||||
],
|
||||
options: handlers.contentTypeOptions,
|
||||
},
|
||||
{
|
||||
key: 'content_structure',
|
||||
label: 'Content Structure',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Structures' },
|
||||
...(handlers.contentStructureOptions !== undefined
|
||||
? handlers.contentStructureOptions
|
||||
: [
|
||||
{ value: 'article', label: 'Article' },
|
||||
{ value: 'guide', label: 'Guide' },
|
||||
{ value: 'comparison', label: 'Comparison' },
|
||||
{ value: 'review', label: 'Review' },
|
||||
{ value: 'listicle', label: 'Listicle' },
|
||||
{ value: 'landing_page', label: 'Landing Page' },
|
||||
{ value: 'business_page', label: 'Business Page' },
|
||||
{ value: 'service_page', label: 'Service Page' },
|
||||
{ value: 'general', label: 'General' },
|
||||
{ value: 'cluster_hub', label: 'Cluster Hub' },
|
||||
{ value: 'product_page', label: 'Product Page' },
|
||||
{ value: 'category_archive', label: 'Category Archive' },
|
||||
{ value: 'tag_archive', label: 'Tag Archive' },
|
||||
{ value: 'attribute_archive', label: 'Attribute Archive' },
|
||||
]
|
||||
),
|
||||
],
|
||||
options: handlers.contentStructureOptions,
|
||||
},
|
||||
{
|
||||
key: 'source',
|
||||
label: 'Source',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Sources' },
|
||||
...(handlers.sourceOptions !== undefined
|
||||
? handlers.sourceOptions
|
||||
: [
|
||||
{ value: 'igny8', label: 'IGNY8' },
|
||||
{ value: 'wordpress', label: 'WordPress' },
|
||||
]
|
||||
),
|
||||
],
|
||||
options: handlers.sourceOptions,
|
||||
},
|
||||
],
|
||||
headerMetrics: [
|
||||
|
||||
@@ -249,82 +249,26 @@ export const createIdeasPageConfig = (
|
||||
key: 'status',
|
||||
label: 'Status',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Status' },
|
||||
// Use dynamic options if loaded (even if empty array)
|
||||
// Only fall back to defaults if statusOptions is undefined (not loaded yet)
|
||||
...(handlers.statusOptions !== undefined
|
||||
? handlers.statusOptions
|
||||
: [
|
||||
{ value: 'new', label: 'New' },
|
||||
{ value: 'queued', label: 'Queued' },
|
||||
{ value: 'completed', label: 'Completed' },
|
||||
]
|
||||
),
|
||||
],
|
||||
options: handlers.statusOptions,
|
||||
},
|
||||
{
|
||||
key: 'content_structure',
|
||||
label: 'Structure',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Structures' },
|
||||
// Use dynamic options if loaded (even if empty array)
|
||||
// Only fall back to defaults if contentStructureOptions is undefined (not loaded yet)
|
||||
...(handlers.contentStructureOptions !== undefined
|
||||
? handlers.contentStructureOptions
|
||||
: [
|
||||
{ value: 'article', label: 'Article' },
|
||||
{ value: 'guide', label: 'Guide' },
|
||||
{ value: 'comparison', label: 'Comparison' },
|
||||
{ value: 'review', label: 'Review' },
|
||||
{ value: 'listicle', label: 'Listicle' },
|
||||
{ value: 'landing_page', label: 'Landing Page' },
|
||||
{ value: 'business_page', label: 'Business Page' },
|
||||
{ value: 'service_page', label: 'Service Page' },
|
||||
{ value: 'general', label: 'General' },
|
||||
{ value: 'cluster_hub', label: 'Cluster Hub' },
|
||||
{ value: 'product_page', label: 'Product Page' },
|
||||
{ value: 'category_archive', label: 'Category Archive' },
|
||||
{ value: 'tag_archive', label: 'Tag Archive' },
|
||||
{ value: 'attribute_archive', label: 'Attribute Archive' },
|
||||
]
|
||||
),
|
||||
],
|
||||
options: handlers.contentStructureOptions,
|
||||
},
|
||||
{
|
||||
key: 'content_type',
|
||||
label: 'Type',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Types' },
|
||||
// Use dynamic options if loaded (even if empty array)
|
||||
// Only fall back to defaults if contentTypeOptions is undefined (not loaded yet)
|
||||
...(handlers.contentTypeOptions !== undefined
|
||||
? handlers.contentTypeOptions
|
||||
: [
|
||||
{ value: 'post', label: 'Post' },
|
||||
{ value: 'page', label: 'Page' },
|
||||
{ value: 'product', label: 'Product' },
|
||||
{ value: 'taxonomy', label: 'Taxonomy' },
|
||||
]
|
||||
),
|
||||
],
|
||||
options: handlers.contentTypeOptions,
|
||||
},
|
||||
{
|
||||
key: 'keyword_cluster_id',
|
||||
label: 'Cluster',
|
||||
type: 'select',
|
||||
dynamicOptions: 'clusters',
|
||||
options: [
|
||||
{ value: '', label: 'All Clusters' },
|
||||
// Use dynamic cluster options if loaded (even if empty array)
|
||||
// Only fall back to full clusters list if clusterOptions is undefined (not loaded yet)
|
||||
...(handlers.clusterOptions !== undefined
|
||||
? handlers.clusterOptions
|
||||
: handlers.clusters.map((c) => ({ value: c.id.toString(), label: c.name }))
|
||||
),
|
||||
],
|
||||
options: handlers.clusterOptions,
|
||||
},
|
||||
],
|
||||
formFields: (clusters: Array<{ id: number; name: string }>) => [
|
||||
|
||||
@@ -197,20 +197,14 @@ export const createImagesPageConfig = (
|
||||
key: 'content_status',
|
||||
label: 'Content Status',
|
||||
type: 'select',
|
||||
options: handlers.contentStatusOptions || [
|
||||
{ value: '', label: 'All' },
|
||||
{ value: 'draft', label: 'Draft' },
|
||||
{ value: 'review', label: 'Review' },
|
||||
{ value: 'approved', label: 'Approved' },
|
||||
{ value: 'published', label: 'Published' },
|
||||
],
|
||||
options: handlers.contentStatusOptions,
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
label: 'Image Status',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Status' },
|
||||
{ value: '', label: 'All Image Status' },
|
||||
{ value: 'complete', label: 'Complete' },
|
||||
{ value: 'partial', label: 'Partial' },
|
||||
{ value: 'pending', label: 'Pending' },
|
||||
|
||||
@@ -245,32 +245,19 @@ export function createReviewPageConfig(params: {
|
||||
key: 'site_status',
|
||||
label: 'Site Status',
|
||||
type: 'select',
|
||||
options: params.siteStatusOptions || [
|
||||
{ value: '', label: 'All' },
|
||||
{ value: 'not_published', label: 'Not Published' },
|
||||
{ value: 'scheduled', label: 'Scheduled' },
|
||||
{ value: 'publishing', label: 'Publishing' },
|
||||
{ value: 'published', label: 'Published' },
|
||||
{ value: 'failed', label: 'Failed' },
|
||||
],
|
||||
options: params.siteStatusOptions,
|
||||
},
|
||||
{
|
||||
key: 'content_type',
|
||||
label: 'Type',
|
||||
type: 'select',
|
||||
options: params.contentTypeOptions || [
|
||||
{ value: '', label: 'All Types' },
|
||||
...CONTENT_TYPE_OPTIONS,
|
||||
],
|
||||
options: params.contentTypeOptions,
|
||||
},
|
||||
{
|
||||
key: 'content_structure',
|
||||
label: 'Structure',
|
||||
type: 'select',
|
||||
options: params.contentStructureOptions || [
|
||||
{ value: '', label: 'All Structures' },
|
||||
...ALL_CONTENT_STRUCTURES,
|
||||
],
|
||||
options: params.contentStructureOptions,
|
||||
},
|
||||
],
|
||||
headerMetrics: [
|
||||
|
||||
@@ -317,69 +317,25 @@ export const createTasksPageConfig = (
|
||||
key: 'status',
|
||||
label: 'Status',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Status' },
|
||||
...(handlers.statusOptions !== undefined
|
||||
? handlers.statusOptions
|
||||
: [
|
||||
{ value: 'queued', label: 'Queued' },
|
||||
{ value: 'completed', label: 'Completed' },
|
||||
]
|
||||
),
|
||||
],
|
||||
options: handlers.statusOptions,
|
||||
},
|
||||
{
|
||||
key: 'content_type',
|
||||
label: 'Content Type',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Types' },
|
||||
...(handlers.contentTypeOptions !== undefined
|
||||
? handlers.contentTypeOptions
|
||||
: CONTENT_TYPE_OPTIONS
|
||||
),
|
||||
],
|
||||
options: handlers.contentTypeOptions,
|
||||
},
|
||||
{
|
||||
key: 'content_structure',
|
||||
label: 'Content Structure',
|
||||
type: 'select',
|
||||
options: [
|
||||
{ value: '', label: 'All Structures' },
|
||||
...(handlers.contentStructureOptions !== undefined
|
||||
? handlers.contentStructureOptions
|
||||
: [
|
||||
{ value: 'article', label: 'Article' },
|
||||
{ value: 'guide', label: 'Guide' },
|
||||
{ value: 'comparison', label: 'Comparison' },
|
||||
{ value: 'review', label: 'Review' },
|
||||
{ value: 'listicle', label: 'Listicle' },
|
||||
{ value: 'landing_page', label: 'Landing Page' },
|
||||
{ value: 'business_page', label: 'Business Page' },
|
||||
{ value: 'service_page', label: 'Service Page' },
|
||||
{ value: 'general', label: 'General' },
|
||||
{ value: 'cluster_hub', label: 'Cluster Hub' },
|
||||
{ value: 'product_page', label: 'Product Page' },
|
||||
{ value: 'category_archive', label: 'Category Archive' },
|
||||
{ value: 'tag_archive', label: 'Tag Archive' },
|
||||
{ value: 'attribute_archive', label: 'Attribute Archive' },
|
||||
]
|
||||
),
|
||||
],
|
||||
options: handlers.contentStructureOptions,
|
||||
},
|
||||
{
|
||||
key: 'cluster_id',
|
||||
label: 'Cluster',
|
||||
type: 'select',
|
||||
options: (() => {
|
||||
return [
|
||||
{ value: '', label: 'All Clusters' },
|
||||
...(handlers.clusterOptions !== undefined
|
||||
? handlers.clusterOptions
|
||||
: handlers.clusters.map((c) => ({ value: c.id.toString(), label: c.name }))
|
||||
),
|
||||
];
|
||||
})(),
|
||||
options: handlers.clusterOptions,
|
||||
dynamicOptions: 'clusters',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -99,6 +99,7 @@ export default function Approved() {
|
||||
// Load dynamic filter options based on current site's data and applied filters
|
||||
// This implements cascading filters - each filter's options reflect what's available
|
||||
// given the other currently applied filters
|
||||
// APPROVED PAGE: Always constrain to approved+published statuses
|
||||
const loadFilterOptions = useCallback(async (currentFilters?: {
|
||||
status?: string;
|
||||
site_status?: string;
|
||||
@@ -109,7 +110,11 @@ export default function Approved() {
|
||||
if (!activeSite) return;
|
||||
|
||||
try {
|
||||
const options = await fetchWriterContentFilterOptions(activeSite.id, currentFilters);
|
||||
// Always pass status__in to constrain filter options to approved/published content only
|
||||
const options = await fetchWriterContentFilterOptions(activeSite.id, {
|
||||
...currentFilters,
|
||||
status__in: 'approved,published', // Base constraint for this page
|
||||
});
|
||||
setStatusOptions(options.statuses || []);
|
||||
setSiteStatusOptions(options.site_statuses || []);
|
||||
setContentTypeOptions(options.content_types || []);
|
||||
@@ -119,7 +124,7 @@ export default function Approved() {
|
||||
}
|
||||
}, [activeSite]);
|
||||
|
||||
// Load filter options when site changes (initial load with no filters)
|
||||
// Load filter options when site changes (initial load with approved/published constraint)
|
||||
useEffect(() => {
|
||||
loadFilterOptions();
|
||||
}, [activeSite]);
|
||||
@@ -175,6 +180,7 @@ export default function Approved() {
|
||||
...(searchTerm && { search: searchTerm }),
|
||||
// Default to approved+published if no status filter selected
|
||||
...(statusFilter ? { status: statusFilter } : { status__in: 'approved,published' }),
|
||||
...(siteStatusFilter && { site_status: siteStatusFilter }),
|
||||
...(contentTypeFilter && { content_type: contentTypeFilter }),
|
||||
...(contentStructureFilter && { content_structure: contentStructureFilter }),
|
||||
page: currentPage,
|
||||
@@ -184,13 +190,7 @@ export default function Approved() {
|
||||
|
||||
const data: ContentListResponse = await fetchContent(filters);
|
||||
|
||||
// Client-side filter for site_status if needed (backend may not support this filter yet)
|
||||
let filteredResults = data.results || [];
|
||||
if (siteStatusFilter) {
|
||||
filteredResults = filteredResults.filter(c => c.site_status === siteStatusFilter);
|
||||
}
|
||||
|
||||
setContent(filteredResults);
|
||||
setContent(data.results || []);
|
||||
setTotalCount(data.count || 0);
|
||||
setTotalPages(Math.ceil((data.count || 0) / pageSize));
|
||||
|
||||
@@ -771,6 +771,14 @@ export default function Approved() {
|
||||
setCurrentPage(1);
|
||||
}
|
||||
}}
|
||||
onFilterReset={() => {
|
||||
setSearchTerm('');
|
||||
setStatusFilter('');
|
||||
setSiteStatusFilter('');
|
||||
setContentTypeFilter('');
|
||||
setContentStructureFilter('');
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
pagination={{
|
||||
currentPage,
|
||||
totalPages,
|
||||
|
||||
@@ -77,6 +77,7 @@ export default function Content() {
|
||||
// Load dynamic filter options based on current site's data and applied filters
|
||||
// This implements cascading filters - each filter's options reflect what's available
|
||||
// given the other currently applied filters
|
||||
// DRAFTS PAGE: Always constrain to draft status
|
||||
const loadFilterOptions = useCallback(async (currentFilters?: {
|
||||
status?: string;
|
||||
site_status?: string;
|
||||
@@ -88,7 +89,11 @@ export default function Content() {
|
||||
if (!activeSite) return;
|
||||
|
||||
try {
|
||||
const options = await fetchWriterContentFilterOptions(activeSite.id, currentFilters);
|
||||
// Always pass status='draft' to constrain filter options to draft content only
|
||||
const options = await fetchWriterContentFilterOptions(activeSite.id, {
|
||||
...currentFilters,
|
||||
status: 'draft', // Base constraint for this page
|
||||
});
|
||||
setSourceOptions(options.sources || []);
|
||||
setContentTypeOptions(options.content_types || []);
|
||||
setContentStructureOptions(options.content_structures || []);
|
||||
@@ -97,7 +102,7 @@ export default function Content() {
|
||||
}
|
||||
}, [activeSite]);
|
||||
|
||||
// Load filter options when site changes (initial load with no filters)
|
||||
// Load filter options when site changes (initial load with draft constraint)
|
||||
useEffect(() => {
|
||||
loadFilterOptions();
|
||||
}, [activeSite]);
|
||||
@@ -105,13 +110,12 @@ export default function Content() {
|
||||
// Reload filter options when any filter changes (cascading filters)
|
||||
useEffect(() => {
|
||||
loadFilterOptions({
|
||||
status: statusFilter || undefined,
|
||||
content_type: contentTypeFilter || undefined,
|
||||
content_structure: contentStructureFilter || undefined,
|
||||
source: sourceFilter || undefined,
|
||||
search: searchTerm || undefined,
|
||||
});
|
||||
}, [statusFilter, contentTypeFilter, contentStructureFilter, sourceFilter, searchTerm, loadFilterOptions]);
|
||||
}, [contentTypeFilter, contentStructureFilter, sourceFilter, searchTerm, loadFilterOptions]);
|
||||
|
||||
|
||||
// Load total metrics for footer widget and header metrics (site-wide totals, no sector filter)
|
||||
@@ -422,6 +426,13 @@ export default function Content() {
|
||||
setCurrentPage(1);
|
||||
}
|
||||
}}
|
||||
onFilterReset={() => {
|
||||
setSearchTerm('');
|
||||
setSourceFilter('');
|
||||
setContentTypeFilter('');
|
||||
setContentStructureFilter('');
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
pagination={{
|
||||
currentPage,
|
||||
totalPages,
|
||||
|
||||
@@ -213,7 +213,7 @@ export default function Images() {
|
||||
setShowContent(true);
|
||||
setLoading(false);
|
||||
}
|
||||
}, [currentPage, statusFilter, sortBy, sortDirection, searchTerm, toast]);
|
||||
}, [currentPage, statusFilter, contentStatusFilter, sortBy, sortDirection, searchTerm, toast]);
|
||||
|
||||
useEffect(() => {
|
||||
loadImages();
|
||||
|
||||
@@ -893,6 +893,7 @@ export interface WriterContentFilterOptions {
|
||||
|
||||
export interface ContentFilterOptionsRequest {
|
||||
status?: string;
|
||||
status__in?: string; // Comma-separated list for base constraint (e.g., 'approved,published')
|
||||
site_status?: string;
|
||||
content_type?: string;
|
||||
content_structure?: string;
|
||||
@@ -907,6 +908,7 @@ export async function fetchWriterContentFilterOptions(
|
||||
const params = new URLSearchParams();
|
||||
if (siteId) params.append('site_id', siteId.toString());
|
||||
if (filters?.status) params.append('status', filters.status);
|
||||
if (filters?.status__in) params.append('status__in', filters.status__in);
|
||||
if (filters?.site_status) params.append('site_status', filters.site_status);
|
||||
if (filters?.content_type) params.append('content_type', filters.content_type);
|
||||
if (filters?.content_structure) params.append('content_structure', filters.content_structure);
|
||||
@@ -2582,6 +2584,7 @@ export interface ContentFilters {
|
||||
search?: string;
|
||||
status?: string;
|
||||
status__in?: string; // Comma-separated list of statuses (e.g., 'approved,published')
|
||||
site_status?: string; // Site publishing status (not_published, scheduled, published, failed)
|
||||
content_type?: string;
|
||||
content_structure?: string;
|
||||
source?: string;
|
||||
@@ -2674,6 +2677,11 @@ export async function fetchContent(filters: ContentFilters = {}): Promise<Conten
|
||||
if (filters.search) params.append('search', filters.search);
|
||||
if (filters.status) params.append('status', filters.status);
|
||||
if (filters.status__in) params.append('status__in', filters.status__in);
|
||||
if (filters.site_status) params.append('site_status', filters.site_status);
|
||||
if (filters.content_type) params.append('content_type', filters.content_type);
|
||||
if (filters.content_structure) params.append('content_structure', filters.content_structure);
|
||||
if (filters.source) params.append('source', filters.source);
|
||||
if (filters.cluster_id) params.append('cluster_id', filters.cluster_id.toString());
|
||||
if (filters.task_id) params.append('task_id', filters.task_id.toString());
|
||||
if (filters.site_id) params.append('site_id', filters.site_id.toString());
|
||||
if (filters.sector_id) params.append('sector_id', filters.sector_id.toString());
|
||||
|
||||
Reference in New Issue
Block a user