fixes but still nto fixed

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-15 04:13:54 +00:00
parent e02ba76451
commit 75785aa642
12 changed files with 1037 additions and 80 deletions

View File

@@ -0,0 +1,351 @@
# IGNY8 Filters Implementation
**Last Updated:** January 15, 2026
**Version:** 1.0.0
> This document describes the current filter implementation across all pages that use the `TablePageTemplate` component.
---
## Architecture Overview
### Component Hierarchy
```
Page Component (e.g., Keywords.tsx)
└── createPageConfig() function
├── Returns: columns, filters, formFields, headerMetrics
└── Uses: handlers (state setters, filter values)
└── TablePageTemplate.tsx
├── Renders filters based on FilterConfig[]
├── Manages filter visibility toggle
└── Passes filterValues and onFilterChange to children
```
### Filter Flow
1. **Page Component** defines filter state (`useState`)
2. **Config Function** creates `FilterConfig[]` with options
3. **TablePageTemplate** renders filter UI components
4. **Filter Change**`onFilterChange` callback → update state → re-fetch data
---
## Filter Types
| Type | Component | Description |
|------|-----------|-------------|
| `text` | `<Input>` | Text search input |
| `select` | `<SelectDropdown>` | Single-select dropdown |
| `custom` | `customRender()` | Custom component (e.g., volume range) |
| `daterange` | (planned) | Date range picker |
| `range` | (planned) | Numeric range |
---
## Backend FilterSet Classes
### Planner Module (`/backend/igny8_core/modules/planner/views.py`)
#### KeywordsFilter
```python
class KeywordsFilter(django_filters.FilterSet):
class Meta:
model = Keywords
fields = ['status', 'cluster_id', 'seed_keyword__country', 'seed_keyword_id', 'created_at__gte', 'created_at__lte']
```
#### ClustersFilter
```python
class ClustersFilter(django_filters.FilterSet):
class Meta:
model = Clusters
fields = ['status', 'created_at__gte', 'created_at__lte']
```
#### ContentIdeasFilter
```python
class ContentIdeasFilter(django_filters.FilterSet):
class Meta:
model = ContentIdeas
fields = ['status', 'keyword_cluster_id', 'content_type', 'content_structure', 'created_at__gte', 'created_at__lte']
```
### Writer Module (`/backend/igny8_core/modules/writer/views.py`)
#### TasksFilter
```python
class TasksFilter(django_filters.FilterSet):
class Meta:
model = Tasks
fields = ['status', 'cluster_id', 'content_type', 'content_structure', 'created_at__gte', 'created_at__lte']
```
#### ImagesFilter
```python
class ImagesFilter(django_filters.FilterSet):
class Meta:
model = Images
fields = ['task_id', 'content_id', 'image_type', 'status', 'created_at__gte', 'created_at__lte']
```
#### ContentFilter
```python
class ContentFilter(django_filters.FilterSet):
class Meta:
model = Content
fields = ['cluster_id', 'status', 'content_type', 'content_structure', 'source', 'created_at__gte', 'created_at__lte']
```
---
## Page-by-Page Filter Implementation
### 1. Keywords Page (`/planner/keywords`)
**Config File:** `frontend/src/config/pages/keywords.config.tsx`
**Page File:** `frontend/src/pages/Planner/Keywords.tsx`
| Filter Key | Type | Options | Backend Field |
|------------|------|---------|---------------|
| `search` | text | - | `search` (SearchFilter) |
| `status` | select | `new`, `mapped` | `status` |
| `country` | select | `US`, `CA`, `GB`, `AE`, `AU`, `IN`, `PK` | `seed_keyword__country` |
| `difficulty` | select | `1-5` (mapped labels) | Custom in `get_queryset()` |
| `cluster` | select | Dynamic (cluster list) | `cluster_id` |
| `volume` | custom | Min/Max inputs | Custom `volume_min`, `volume_max` |
**Special Handling:**
- Difficulty filter uses 1-5 scale that maps to raw score ranges (0-10, 11-30, 31-50, 51-70, 71-100)
- Volume range uses custom dropdown with min/max inputs
---
### 2. Clusters Page (`/planner/clusters`)
**Config File:** `frontend/src/config/pages/clusters.config.tsx`
**Page File:** `frontend/src/pages/Planner/Clusters.tsx`
| Filter Key | Type | Options | Backend Field |
|------------|------|---------|---------------|
| `search` | text | - | `search` (SearchFilter) |
| `status` | select | `new`, `mapped` | `status` |
| `difficulty` | select | `1-5` (mapped labels) | Custom filtering |
| `volume` | custom | Min/Max inputs | Custom `volume_min`, `volume_max` |
---
### 3. Ideas Page (`/planner/ideas`)
**Config File:** `frontend/src/config/pages/ideas.config.tsx`
**Page File:** `frontend/src/pages/Planner/Ideas.tsx`
| Filter Key | Type | Options | Backend Field |
|------------|------|---------|---------------|
| `search` | text | - | `search` (SearchFilter) |
| `status` | select | `new`, `queued`, `completed` | `status` |
| `content_structure` | select | 14 structure types | `content_structure` |
| `content_type` | select | `post`, `page`, `product`, `taxonomy` | `content_type` |
| `keyword_cluster_id` | select | Dynamic (cluster list) | `keyword_cluster_id` |
**Structure Options:**
- Post: `article`, `guide`, `comparison`, `review`, `listicle`
- Page: `landing_page`, `business_page`, `service_page`, `general`, `cluster_hub`
- Product: `product_page`
- Taxonomy: `category_archive`, `tag_archive`, `attribute_archive`
---
### 4. Content Page (`/writer/content`)
**Config File:** `frontend/src/config/pages/content.config.tsx`
**Page File:** `frontend/src/pages/Writer/Content.tsx`
| Filter Key | Type | Options | Backend Field |
|------------|------|---------|---------------|
| `search` | text | - | `search` (SearchFilter) |
| `status` | select | `draft`, `published` | `status` |
| `content_type` | select | `post`, `page`, `product`, `taxonomy` | `content_type` |
| `content_structure` | select | 14 structure types | `content_structure` |
| `source` | select | `igny8`, `wordpress` | `source` |
---
### 5. Review Page (`/writer/review`)
**Config File:** `frontend/src/config/pages/review.config.tsx`
**Page File:** `frontend/src/pages/Writer/Review.tsx`
| Filter Key | Type | Options | Backend Field |
|------------|------|---------|---------------|
| `search` | text | - | `search` (SearchFilter) |
| `status` | select | `draft`, `review`, `approved`, `published` | `status` |
| `site_status` | select | `not_published`, `scheduled`, `publishing`, `published`, `failed` | `site_status` |
| `content_type` | select | From `CONTENT_TYPE_OPTIONS` | `content_type` |
| `content_structure` | select | From `ALL_CONTENT_STRUCTURES` | `content_structure` |
---
### 6. Approved Page (`/writer/approved`)
**Config File:** `frontend/src/config/pages/approved.config.tsx`
**Page File:** `frontend/src/pages/Writer/Approved.tsx`
| Filter Key | Type | Options | Backend Field |
|------------|------|---------|---------------|
| `search` | text | - | `search` (SearchFilter) |
| `status` | select | `draft`, `review`, `approved`, `published` | `status` |
| `site_status` | select | `not_published`, `scheduled`, `publishing`, `published`, `failed` | `site_status` |
| `content_type` | select | From `CONTENT_TYPE_OPTIONS` | `content_type` |
| `content_structure` | select | From `ALL_CONTENT_STRUCTURES` | `content_structure` |
---
## Shared Constants
**File:** `frontend/src/config/structureMapping.ts`
```typescript
export const CONTENT_TYPE_OPTIONS = [
{ value: 'post', label: 'Post' },
{ value: 'page', label: 'Page' },
{ value: 'product', label: 'Product' },
{ value: 'taxonomy', label: 'Taxonomy' },
];
export const ALL_CONTENT_STRUCTURES = [
{ 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' },
];
```
---
## Difficulty Mapping
**File:** `frontend/src/utils/difficulty.ts`
The difficulty filter uses a 1-5 scale with human-readable labels:
| Value | Label | Raw Score Range |
|-------|-------|-----------------|
| 1 | Very Easy | 0-10 |
| 2 | Easy | 11-30 |
| 3 | Medium | 31-50 |
| 4 | Hard | 51-70 |
| 5 | Very Hard | 71-100 |
**Important:** The database stores raw SEO difficulty scores (0-100), but the UI displays and filters using the 1-5 scale. The mapping must be consistent between frontend display and backend filtering.
---
## Filter State Management Pattern
Each page follows this pattern:
```tsx
// 1. Define filter state
const [searchTerm, setSearchTerm] = useState('');
const [statusFilter, setStatusFilter] = useState('');
const [difficultyFilter, setDifficultyFilter] = useState('');
// 2. Pass to config function
const config = createPageConfig({
searchTerm,
setSearchTerm,
statusFilter,
setStatusFilter,
// ...handlers
});
// 3. Config returns filters array
filters: [
{ key: 'search', type: 'text', placeholder: '...' },
{ key: 'status', type: 'select', options: [...] },
]
// 4. TablePageTemplate renders filters
// 5. onFilterChange triggers state update
// 6. useEffect with dependencies re-fetches data
```
---
## API Query Parameters
When filters are applied, the frontend constructs API calls like:
```
GET /api/v1/planner/keywords/?status=new&cluster_id=123&search=term&page=1&page_size=50
GET /api/v1/planner/clusters/?status=mapped&difficulty=3&volume_min=100
GET /api/v1/planner/ideas/?content_structure=guide&content_type=post
GET /api/v1/writer/content/?status=draft&source=igny8
```
---
## TablePageTemplate Filter Rendering
The template handles filter rendering in `renderFiltersRow()`:
```tsx
{filters.map((filter) => {
if (filter.type === 'text') {
return <Input ... />;
}
if (filter.type === 'select') {
return <SelectDropdown ... />;
}
if (filter.type === 'custom' && filter.customRender) {
return filter.customRender();
}
})}
```
---
## Current Limitations
1. **Static Options**: Filter options are hardcoded in config files, not dynamically loaded from backend
2. **No Cascading**: Changing one filter doesn't update available options in other filters
3. **Difficulty Mapping**: Backend filters by exact match, not by mapped ranges
4. **No Filter Persistence**: Filters reset on page navigation
---
## Planned Improvements
1. **Dynamic Filter Options API**: Backend endpoint to return available options based on current data
2. **Cascading Filters**: When status is selected, only show clusters/types that exist with that status
3. **URL State**: Persist filter state in URL query parameters
4. **Filter Presets**: Save commonly used filter combinations
---
## File References
| Component | Path |
|-----------|------|
| TablePageTemplate | `frontend/src/templates/TablePageTemplate.tsx` |
| Keywords Config | `frontend/src/config/pages/keywords.config.tsx` |
| Clusters Config | `frontend/src/config/pages/clusters.config.tsx` |
| Ideas Config | `frontend/src/config/pages/ideas.config.tsx` |
| Content Config | `frontend/src/config/pages/content.config.tsx` |
| Review Config | `frontend/src/config/pages/review.config.tsx` |
| Approved Config | `frontend/src/config/pages/approved.config.tsx` |
| Structure Mapping | `frontend/src/config/structureMapping.ts` |
| Difficulty Utils | `frontend/src/utils/difficulty.ts` |
| Planner Views | `backend/igny8_core/modules/planner/views.py` |
| Writer Views | `backend/igny8_core/modules/writer/views.py` |