temproary docs uplaoded

This commit is contained in:
IGNY8 VPS (Salman)
2026-03-23 09:02:49 +00:00
parent cb6eca4483
commit 128b186865
113 changed files with 68897 additions and 0 deletions

View File

@@ -0,0 +1,683 @@
# IGNY8 Frontend Component System
**Last Updated:** January 20, 2026
**Version:** 1.8.4
> **🔒 ENFORCED BY ESLINT** - Violations will trigger warnings/errors during build.
> This document is the single source of truth for all UI components.
---
## Quick Reference Card
| Element | Component | Import Path |
|---------|-----------|-------------|
| Button | `Button` | `components/ui/button/Button` |
| Icon Button | `IconButton` | `components/ui/button/IconButton` |
| Button Group | `ButtonGroup` | `components/ui/button-group/ButtonGroup` |
| Text Input | `InputField` | `components/form/input/InputField` |
| Checkbox | `Checkbox` | `components/form/input/Checkbox` |
| Radio | `Radio` | `components/form/input/Radio` |
| File Upload | `FileInput` | `components/form/input/FileInput` |
| Textarea | `TextArea` | `components/form/input/TextArea` |
| Dropdown | `Select` | `components/form/Select` |
| Searchable Dropdown | `SelectDropdown` | `components/form/SelectDropdown` |
| Multi-Select | `MultiSelect` | `components/form/MultiSelect` |
| Toggle Switch | `Switch` | `components/form/switch/Switch` |
| Badge | `Badge` | `components/ui/badge/Badge` |
| Card | `Card` | `components/ui/card/Card` |
| Modal | `Modal` | `components/ui/modal` |
| Alert | `Alert` | `components/ui/alert/Alert` |
| Spinner | `Spinner` | `components/ui/spinner/Spinner` |
| Tabs | `Tabs` | `components/ui/tabs/Tabs` |
| Tooltip | `Tooltip` | `components/ui/tooltip/Tooltip` |
| Calendar Tooltip | `CalendarItemTooltip` | `components/ui/tooltip/CalendarItemTooltip` |
| Toast | `useToast` | `components/ui/toast/ToastContainer` |
| Icons | `*Icon` | `icons` (e.g., `../../icons`) |
---
## 1. BUTTONS
### Button (Standard)
```tsx
import Button from '../../components/ui/button/Button';
<Button
variant="primary" // primary | secondary | outline | ghost | gradient
tone="brand" // brand | success | warning | danger | neutral
size="md" // xs | sm | md | lg | xl | 2xl
shape="rounded" // rounded | pill
startIcon={<Icon />} // Icon before text
endIcon={<Icon />} // Icon after text
onClick={handler}
disabled={false}
fullWidth={false}
type="button" // button | submit | reset
className="" // Additional classes
>
Button Text
</Button>
```
**Common Patterns:**
```tsx
// Primary action
<Button variant="primary" tone="brand">Save</Button>
// Success/confirm
<Button variant="primary" tone="success">Approve</Button>
// Danger/destructive
<Button variant="primary" tone="danger">Delete</Button>
// Secondary/cancel
<Button variant="outline" tone="neutral">Cancel</Button>
// With icon
<Button startIcon={<PlusIcon className="w-4 h-4" />}>Add Item</Button>
```
### IconButton (Icon-only)
```tsx
import IconButton from '../../components/ui/button/IconButton';
<IconButton
icon={<CloseIcon />} // Required: Icon component
variant="ghost" // solid | outline | ghost
tone="neutral" // brand | success | warning | danger | neutral
size="sm" // xs | sm | md | lg
shape="rounded" // rounded | circle
onClick={handler}
disabled={false}
title="Close" // Required: Accessibility label
aria-label="Close" // Optional: Override aria-label
/>
```
**Common Patterns:**
```tsx
// Close button
<IconButton icon={<CloseIcon />} variant="ghost" tone="neutral" title="Close" />
// Delete button
<IconButton icon={<TrashBinIcon />} variant="ghost" tone="danger" title="Delete" />
// Edit button
<IconButton icon={<PencilIcon />} variant="ghost" tone="brand" title="Edit" />
// Add button (circular)
<IconButton icon={<PlusIcon />} variant="solid" tone="brand" shape="circle" title="Add" />
```
---
## 2. FORM INPUTS
### InputField (Text/Number/Email/Password)
```tsx
import InputField from '../../components/form/input/InputField';
<InputField
type="text" // text | number | email | password | date | time
label="Field Label"
placeholder="Enter value..."
value={value}
onChange={(e) => setValue(e.target.value)}
id="field-id"
name="field-name"
disabled={false}
error={false} // Shows error styling
success={false} // Shows success styling
hint="Helper text" // Text below input
min="0" // For number inputs
max="100" // For number inputs
step={1} // For number inputs
/>
```
### TextArea (Multi-line)
```tsx
import TextArea from '../../components/form/input/TextArea';
<TextArea
placeholder="Enter description..."
rows={4}
value={value}
onChange={(value) => setValue(value)}
disabled={false}
error={false}
hint="Helper text"
/>
```
### Checkbox
```tsx
import Checkbox from '../../components/form/input/Checkbox';
<Checkbox
label="Accept terms"
checked={checked}
onChange={(checked) => setChecked(checked)}
id="checkbox-id"
disabled={false}
/>
```
### Radio
```tsx
import Radio from '../../components/form/input/Radio';
<Radio
id="radio-option-1"
name="radio-group"
value="option1"
label="Option 1"
checked={selected === 'option1'}
onChange={(value) => setSelected(value)}
/>
```
### Select (Dropdown)
```tsx
import Select from '../../components/form/Select';
<Select
options={[
{ value: 'opt1', label: 'Option 1' },
{ value: 'opt2', label: 'Option 2' },
]}
placeholder="Select..."
defaultValue=""
onChange={(value) => setValue(value)}
className=""
/>
```
### SelectDropdown (Searchable)
```tsx
import SelectDropdown from '../../components/form/SelectDropdown';
<SelectDropdown
label="Select Item"
options={options}
value={value}
onChange={(value) => setValue(value)}
placeholder="Search..."
searchable={true}
/>
```
### Switch (Toggle)
```tsx
import Switch from '../../components/form/switch/Switch';
<Switch
label="Enable feature"
checked={enabled} // Controlled mode
onChange={(checked) => setEnabled(checked)}
disabled={false}
color="blue" // blue | gray
/>
```
### FileInput
```tsx
import FileInput from '../../components/form/input/FileInput';
<FileInput
onChange={(files) => handleFiles(files)}
accept=".csv,.json"
multiple={false}
/>
```
---
## 3. DISPLAY COMPONENTS
### Badge
```tsx
import Badge from '../../components/ui/badge/Badge';
<Badge
tone="success" // brand | success | warning | danger | info | neutral | purple | indigo | pink | teal | cyan | blue
variant="soft" // solid | soft | outline | light
size="sm" // xs | sm | md
startIcon={<Icon />}
endIcon={<Icon />}
>
Label
</Badge>
```
**Status Badge Patterns:**
```tsx
<Badge tone="success" variant="soft">Active</Badge>
<Badge tone="warning" variant="soft">Pending</Badge>
<Badge tone="danger" variant="soft">Failed</Badge>
<Badge tone="info" variant="soft">Draft</Badge>
<Badge tone="neutral" variant="soft">Archived</Badge>
```
### Card
```tsx
import { Card, CardTitle, CardContent, CardDescription, CardAction, CardIcon } from '../../components/ui/card/Card';
<Card
variant="surface" // surface | panel | frosted | borderless | gradient
padding="md" // none | sm | md | lg
shadow="sm" // none | sm | md
>
<CardIcon><Icon /></CardIcon>
<CardTitle>Title</CardTitle>
<CardDescription>Description text</CardDescription>
<CardContent>Main content</CardContent>
<CardAction onClick={handler}>Action</CardAction>
</Card>
```
### Alert
```tsx
import Alert from '../../components/ui/alert/Alert';
<Alert
variant="success" // success | error | warning | info
title="Alert Title"
message="Alert message text"
showLink={false}
linkHref="#"
linkText="Learn more"
/>
```
### Modal
```tsx
import { Modal } from '../../components/ui/modal';
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
showCloseButton={true}
isFullscreen={false}
>
<div className="p-6">
Modal content
</div>
</Modal>
```
### Spinner
```tsx
import { Spinner } from '../../components/ui/spinner/Spinner';
<Spinner
size="md" // sm | md | lg
color="primary" // primary | success | error | warning | info
/>
```
### Tooltip
```tsx
import { Tooltip } from '../../components/ui/tooltip/Tooltip';
<Tooltip text="Tooltip text" placement="top">
<span>Hover target</span>
</Tooltip>
```
### Toast (Notifications)
```tsx
import { useToast } from '../../components/ui/toast/ToastContainer';
const toast = useToast();
// Show notifications
toast.success('Success', 'Operation completed');
toast.error('Error', 'Something went wrong');
toast.warning('Warning', 'Please review');
toast.info('Info', 'Here is some information');
```
---
## 4. ICONS
### Importing Icons
```tsx
// Always import from central icons folder
import { PlusIcon, CloseIcon, CheckCircleIcon } from '../../icons';
// Use consistent sizing
<PlusIcon className="w-4 h-4" /> // Small (in buttons, badges)
<PlusIcon className="w-5 h-5" /> // Medium (standalone)
<PlusIcon className="w-6 h-6" /> // Large (headers, features)
```
### Available Icons
**Core Icons:**
- `PlusIcon`, `CloseIcon`, `CheckCircleIcon`, `AlertIcon`, `InfoIcon`, `ErrorIcon`
- `BoltIcon`, `ArrowUpIcon`, `ArrowDownIcon`, `ArrowRightIcon`, `ArrowLeftIcon`
- `PencilIcon`, `TrashBinIcon`, `DownloadIcon`, `CopyIcon`
- `EyeIcon`, `EyeCloseIcon`, `LockIcon`, `UserIcon`
- `FolderIcon`, `FileIcon`, `GridIcon`, `ListIcon`
- `ChevronDownIcon`, `ChevronUpIcon`, `ChevronLeftIcon`, `ChevronRightIcon`
- `AngleDownIcon`, `AngleUpIcon`, `AngleLeftIcon`, `AngleRightIcon`
**Module Icons:**
- `TaskIcon`, `PageIcon`, `TableIcon`, `CalendarIcon`
- `PlugInIcon`, `DocsIcon`, `MailIcon`, `ChatIcon`
- `PieChartIcon`, `BoxCubeIcon`, `GroupIcon`
- `ShootingStarIcon`, `DollarLineIcon`
**Aliases (for compatibility):**
- `TrashIcon``TrashBinIcon`
- `XIcon` / `XMarkIcon``CloseIcon`
- `SearchIcon``GridIcon`
- `SettingsIcon``BoxCubeIcon`
- `FilterIcon``ListIcon`
### Adding New Icons
1. Add SVG file to `src/icons/`
2. Export in `src/icons/index.ts`:
```ts
import { ReactComponent as NewIcon } from "./new-icon.svg?react";
export { NewIcon };
```
---
## 5. FOLDER STRUCTURE
```
src/
├── components/
│ ├── ui/ # UI Components (display/interaction)
│ │ ├── accordion/
│ │ ├── alert/
│ │ │ ├── Alert.tsx
│ │ │ └── AlertModal.tsx
│ │ ├── avatar/
│ │ ├── badge/
│ │ │ └── Badge.tsx
│ │ ├── breadcrumb/
│ │ ├── button/
│ │ │ ├── Button.tsx # ← Standard button
│ │ │ ├── IconButton.tsx # ← Icon-only button
│ │ │ └── ButtonWithTooltip.tsx
│ │ ├── button-group/
│ │ ├── card/
│ │ ├── dataview/
│ │ ├── dropdown/
│ │ ├── list/
│ │ ├── modal/
│ │ ├── pagination/
│ │ ├── progress/
│ │ ├── ribbon/
│ │ ├── spinner/
│ │ ├── table/
│ │ ├── tabs/
│ │ ├── toast/
│ │ ├── tooltip/
│ │ └── videos/
│ │
│ └── form/ # Form Components (inputs)
│ ├── input/
│ │ ├── InputField.tsx # ← Text/number/email inputs
│ │ ├── Checkbox.tsx # ← Checkbox
│ │ ├── Radio.tsx # ← Radio button
│ │ ├── RadioSm.tsx # ← Small radio button
│ │ ├── FileInput.tsx # ← File upload
│ │ └── TextArea.tsx # ← Multi-line text
│ ├── switch/
│ │ └── Switch.tsx # ← Toggle switch
│ ├── Select.tsx # ← Dropdown select
│ ├── SelectDropdown.tsx # ← Searchable dropdown
│ ├── MultiSelect.tsx # ← Multi-select dropdown
│ ├── Label.tsx # ← Form labels
│ ├── Form.tsx # ← Form wrapper
│ └── date-picker.tsx # ← Date picker
├── icons/ # All SVG icons
│ ├── index.ts # ← Export all icons from here
│ ├── plus.svg
│ ├── close.svg
│ └── ... (50+ icons)
└── styles/
└── design-system.css # ← Global design tokens
```
---
## 6. ESLINT ENFORCEMENT
### Rules File Location
`eslint-plugin-igny8-design-system.cjs` (project root)
### Active Rules
| Rule | Severity | Description |
|------|----------|-------------|
| `no-raw-button` | warn | Use `Button` or `IconButton` instead of `<button>` |
| `no-raw-input` | warn | Use `InputField`, `Checkbox`, `Radio`, `FileInput` instead of `<input>` |
| `no-raw-select` | warn | Use `Select` or `SelectDropdown` instead of `<select>` |
| `no-raw-textarea` | warn | Use `TextArea` instead of `<textarea>` |
| `no-restricted-imports` | error | Block imports from `@heroicons/*`, `lucide-react`, `@mui/icons-material` |
### Running Lint Check
```bash
npm run lint
```
### Viewing Violations
```bash
npm run lint 2>&1 | grep "igny8-design-system"
```
---
## 7. MIGRATION EXAMPLES
### Raw Button → Button Component
```tsx
// ❌ BEFORE
<button
className="px-4 py-2 bg-brand-500 text-white rounded hover:bg-brand-600"
onClick={handleClick}
>
Save
</button>
// ✅ AFTER
<Button variant="primary" tone="brand" onClick={handleClick}>
Save
</Button>
```
### Raw Button → IconButton
```tsx
// ❌ BEFORE
<button
className="p-1 hover:bg-gray-100 rounded"
onClick={onClose}
>
<CloseIcon className="w-4 h-4" />
</button>
// ✅ AFTER
<IconButton
icon={<CloseIcon />}
variant="ghost"
tone="neutral"
size="sm"
onClick={onClose}
title="Close"
/>
```
### Raw Input → InputField
```tsx
// ❌ BEFORE
<input
type="text"
className="border rounded px-3 py-2 focus:ring-2"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Enter name"
/>
// ✅ AFTER
<InputField
type="text"
label="Name"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Enter name"
/>
```
### Raw Select → Select Component
```tsx
// ❌ BEFORE
<select
className="border rounded px-3 py-2"
value={status}
onChange={(e) => setStatus(e.target.value)}
>
<option value="">Select status</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
// ✅ AFTER
<Select
options={[
{ value: 'active', label: 'Active' },
{ value: 'inactive', label: 'Inactive' },
]}
placeholder="Select status"
defaultValue={status}
onChange={(value) => setStatus(value)}
/>
```
### External Icon → Internal Icon
```tsx
// ❌ BEFORE
import { XIcon } from '@heroicons/react/24/outline';
<XIcon className="w-5 h-5" />
// ✅ AFTER
import { CloseIcon } from '../../icons';
<CloseIcon className="w-5 h-5" />
```
---
## 8. SITE-SPECIFIC COMPONENTS (v1.3.2)
### SiteInfoBar
Reusable site info header component for site-specific pages.
```tsx
import { SiteInfoBar } from '../../components/common/SiteInfoBar';
<SiteInfoBar
site={currentSite} // Site object with name, domain, etc.
onSiteChange={handleSiteChange} // Optional: Callback when site changes
showSelector={true} // Whether to show site selector dropdown
/>
```
### OnboardingWizard Components
Located in `components/onboarding/`:
```tsx
import { OnboardingWizard } from '../../components/onboarding/OnboardingWizard';
import {
Step1Welcome,
Step2AddSite,
Step3ConnectIntegration,
Step4AddKeywords,
Step5Complete,
} from '../../components/onboarding/steps';
```
### CalendarItemTooltip
Tooltip specifically designed for calendar items.
```tsx
import { CalendarItemTooltip } from '../../components/ui/tooltip';
<CalendarItemTooltip
item={contentItem} // Content object
onView={handleView} // Optional: View callback
onRemove={handleRemove} // Optional: Remove callback
/>
```
---
## 9. LIVE REFERENCE
View all components with live examples at: `/ui-elements`
This page shows every component with all prop variations.
---
## 10. AI AGENT INSTRUCTIONS
When working on this codebase, AI agents MUST:
1. **Never use raw HTML elements** (`<button>`, `<input>`, `<select>`, `<textarea>`)
2. **Import icons only from `src/icons`** - never from external libraries
3. **Follow the import paths** specified in this document
4. **Check ESLint** after making changes: `npm run lint`
5. **Use semantic color tokens** - never hardcoded hex values or Tailwind defaults
### Color Rules
```tsx
// ✅ CORRECT - Use semantic tokens
className="bg-brand-500 text-gray-900 border-success-500"
// ❌ WRONG - Tailwind defaults (DISABLED)
className="bg-blue-500 text-slate-900 border-green-500"
// ❌ WRONG - Hardcoded hex
className="bg-[#0077B6]"
style={{ color: '#DC2626' }}
```
5. **Reference this document** for correct component usage
6. **Use consistent icon sizing**: `className="w-4 h-4"` for small, `w-5 h-5` for medium
If a component doesn't exist, create it in `components/ui/` or `components/form/` first.

View File

@@ -0,0 +1,242 @@
# IGNY8 Design System Guide
> **Single Source of Truth for UI Components**
>
> 🔒 **STYLE LOCKED** - This design system is enforced by ESLint. All frontend code must comply.
**Last Updated:** January 20, 2026
**Version:** 1.8.4
---
## Quick Links
| Resource | Path | Description |
|----------|------|-------------|
| **Component System** | [COMPONENT-SYSTEM.md](COMPONENT-SYSTEM.md) | Full component reference with props, examples, and usage |
| **Design Tokens Doc** | [DESIGN-TOKENS.md](DESIGN-TOKENS.md) | Detailed token documentation |
| **ESLint Plugin** | `frontend/eslint/` | Custom rules enforcing design system |
| **Live Demo** | `/ui-elements` route | Interactive component showcase |
| **CSS Tokens** | `frontend/src/styles/design-system.css` | CSS variables source file |
| **Icons** | `frontend/src/icons/` | All SVG icons |
---
## 🎨 Color System (CRITICAL!)
### Only 6 Base Colors in Entire System
All colors derive from 6 primary hex values defined in `design-system.css`:
| Token | Hex | Purpose |
|-------|-----|---------|
| `--color-primary` | #0077B6 | Brand Blue - main CTA, links |
| `--color-success` | #2CA18E | Success Green - confirmations |
| `--color-warning` | #D9A12C | Warning Amber - alerts |
| `--color-danger` | #A12C40 | Danger Red - errors, destructive |
| `--color-purple` | #2C40A1 | Purple - premium features |
| `--color-gray-base` | #667085 | Neutral gray - text, borders |
### Tailwind Color Utilities
**⚠️ Tailwind default colors are DISABLED!** Only use:
```css
/* ✅ AVAILABLE */
brand-50 through brand-950 /* Primary blue scale */
gray-25 through gray-950 /* Neutral scale */
success-25 through success-950 /* Green scale */
error-25 through error-950 /* Red scale */
warning-25 through warning-950 /* Amber scale */
purple-25 through purple-950 /* Purple scale */
/* ❌ DISABLED - These will NOT work */
blue-*, red-*, green-*, emerald-*, amber-*, indigo-*,
pink-*, rose-*, sky-*, teal-*, cyan-*, etc.
```
### Usage Examples
```tsx
// ✅ CORRECT
<div className="bg-brand-500 text-white">Primary Button</div>
<div className="text-gray-700 bg-gray-50">Card content</div>
<Badge tone="success">Active</Badge>
<Alert variant="error" message="Failed" />
// ❌ WRONG
<div className="bg-blue-500">...</div> // Default blue disabled
<div className="bg-[#0693e3]">...</div> // Hardcoded hex
<div style={{ color: '#ff0000' }}>...</div> // Inline style
```
---
## Core Principles
### 1. Use Components, Never Raw HTML
```tsx
// ❌ NEVER
<button className="...">Click</button>
<input type="text" className="..." />
<select className="...">...</select>
<textarea className="..."></textarea>
// ✅ ALWAYS
<Button variant="primary">Click</Button>
<InputField type="text" label="Name" />
<Select options={options} />
<TextArea rows={4} />
```
### 2. Import Icons from Central Location
```tsx
// ❌ NEVER
import { XIcon } from '@heroicons/react/24/outline';
import { Trash } from 'lucide-react';
// ✅ ALWAYS
import { CloseIcon, TrashBinIcon } from '../../icons';
```
### 3. Consistent Sizing
```tsx
// Icons in buttons/badges
<Icon className="w-4 h-4" />
// Standalone icons
<Icon className="w-5 h-5" />
// Large/header icons
<Icon className="w-6 h-6" />
```
---
## Component Quick Reference
| Need | Component | Import |
|------|-----------|--------|
| Action button | `Button` | `components/ui/button/Button` |
| Icon-only button | `IconButton` | `components/ui/button/IconButton` |
| Text input | `InputField` | `components/form/input/InputField` |
| Checkbox | `Checkbox` | `components/form/input/Checkbox` |
| Radio | `Radio` | `components/form/input/Radio` |
| Dropdown | `Select` | `components/form/Select` |
| Multi-line text | `TextArea` | `components/form/input/TextArea` |
| Toggle | `Switch` | `components/form/switch/Switch` |
| Status label | `Badge` | `components/ui/badge/Badge` |
| Container | `Card` | `components/ui/card/Card` |
| Popup | `Modal` | `components/ui/modal` |
| Loading | `Spinner` | `components/ui/spinner/Spinner` |
| Notification | `useToast` | `components/ui/toast/ToastContainer` |
**→ See [COMPONENT-SYSTEM.md](docs/30-FRONTEND/COMPONENT-SYSTEM.md) for full props and examples**
---
## ESLint Enforcement
### Rules
| Rule | Level | Action |
|------|-------|--------|
| `no-raw-button` | warn | Use `Button` or `IconButton` |
| `no-raw-input` | warn | Use `InputField`, `Checkbox`, `Radio` |
| `no-raw-select` | warn | Use `Select` or `SelectDropdown` |
| `no-raw-textarea` | warn | Use `TextArea` |
| `no-icon-children` | warn | Use `startIcon`/`endIcon` props instead of icon children |
| `no-restricted-imports` | error | Block `@heroicons/*`, `lucide-react`, `@mui/icons-material` |
### Check Violations
```bash
# Inside container
docker exec -it igny8_frontend npm run lint
# Or directly
cd frontend
npm run lint
```
### Plugin Location
The custom ESLint plugin is at: `frontend/eslint/eslint-plugin-igny8-design-system.cjs`
---
## For AI Agents
When working on this codebase:
1. **Read first**: [docs/30-FRONTEND/COMPONENT-SYSTEM.md](docs/30-FRONTEND/COMPONENT-SYSTEM.md)
2. **Never use**: `<button>`, `<input>`, `<select>`, `<textarea>` tags
3. **Import icons from**: `src/icons` only
4. **Verify after changes**: `npm run lint`
5. **Reference pages**: Planner and Writer modules use correct patterns
### Correct Import Paths
```tsx
// From a page in src/pages/
import Button from '../components/ui/button/Button';
import IconButton from '../components/ui/button/IconButton';
import InputField from '../components/form/input/InputField';
import { PlusIcon, CloseIcon } from '../icons';
// From a component in src/components/
import Button from '../../components/ui/button/Button';
import { PlusIcon } from '../../icons';
// From a nested component
// Adjust ../ based on depth
```
---
## File Structure
```
frontend/
├── eslint/
│ └── eslint-plugin-igny8-design-system.cjs # Custom rules
├── src/
│ ├── components/
│ │ ├── ui/ # Display components
│ │ │ ├── button/ # Button, IconButton
│ │ │ ├── badge/ # Badge
│ │ │ ├── card/ # Card
│ │ │ ├── modal/ # Modal
│ │ │ └── ...
│ │ └── form/ # Form components
│ │ ├── input/ # InputField, Checkbox, Radio, TextArea
│ │ ├── switch/ # Switch
│ │ ├── Select.tsx
│ │ └── ...
│ ├── icons/ # All SVG icons
│ │ └── index.ts # Export all icons
│ └── styles/
│ └── design-system.css # Design tokens
docs/
└── 30-FRONTEND/
└── COMPONENT-SYSTEM.md # Full component documentation
```
---
## Migration Checklist
When fixing violations:
- [ ] Replace `<button>` with `Button` or `IconButton`
- [ ] Replace `<input type="text/email/password/number">` with `InputField`
- [ ] Replace `<input type="checkbox">` with `Checkbox`
- [ ] Replace `<input type="radio">` with `Radio`
- [ ] Replace `<select>` with `Select` or `SelectDropdown`
- [ ] Replace `<textarea>` with `TextArea`
- [ ] Replace external icon imports with `src/icons`
- [ ] Run `npm run lint` to verify
- [ ] Run `npm run build` to confirm no errors

View File

@@ -0,0 +1,311 @@
# IGNY8 Design Guide
> **🔒 MANDATORY** - All frontend code MUST follow these standards
> **Version:** 1.8.3
> **Last Updated:** January 20, 2026
---
## Quick Reference
| Need | Use | Import From |
|------|-----|-------------|
| Button | `<Button>` | `components/ui/button/Button` |
| Icon button | `<IconButton>` | `components/ui/button/IconButton` |
| Text input | `<InputField>` | `components/form/input/InputField` |
| Dropdown | `<Select>` | `components/form/Select` |
| Checkbox | `<Checkbox>` | `components/form/input/Checkbox` |
| Toggle | `<Switch>` | `components/form/switch/Switch` |
| Status label | `<Badge>` | `components/ui/badge/Badge` |
| Card | `<Card>` | `components/ui/card/Card` |
| Modal | `<Modal>` | `components/ui/modal` |
| Toast | `useToast()` | `components/ui/toast/ToastContainer` |
| Table | `<Table>` | `components/tables/Table` |
**Full component docs:** [docs/30-FRONTEND/COMPONENT-SYSTEM.md](docs/30-FRONTEND/COMPONENT-SYSTEM.md)
---
## 🎨 Color System
### Only 6 Base Colors (No Exceptions!)
| Token | Hex | Usage |
|-------|-----|-------|
| `brand` | #0077B6 | Primary actions, links |
| `success` | #2CA18E | Success states |
| `warning` | #D9A12C | Warnings, alerts |
| `danger/error` | #A12C40 | Errors, destructive |
| `purple` | #2C40A1 | Premium features |
| `gray` | #667085 | Text, borders, neutrals |
### Tailwind Classes
```tsx
// ✅ CORRECT - Use semantic colors
<div className="bg-brand-500 text-white">Primary</div>
<div className="bg-success-100 text-success-700">Success</div>
<div className="bg-error-100 text-error-700">Error</div>
<div className="text-gray-700 bg-gray-50">Neutral</div>
// ❌ WRONG - Default Tailwind colors are DISABLED
<div className="bg-blue-500">...</div> // Will not work
<div className="bg-red-500">...</div> // Will not work
<div className="bg-[#ff0000]">...</div> // Hardcoded - forbidden
```
---
## Component Rules
### Rule 1: NEVER Use Raw HTML Elements
```tsx
// ❌ NEVER
<button onClick={...}>Click</button>
<input type="text" />
<select>...</select>
<textarea></textarea>
// ✅ ALWAYS
<Button onClick={...}>Click</Button>
<InputField type="text" label="Name" />
<Select options={options} />
<TextArea rows={4} />
```
### Rule 2: Icons from Central Location Only
```tsx
// ❌ NEVER
import { XIcon } from '@heroicons/react/24/outline';
import { Trash } from 'lucide-react';
// ✅ ALWAYS
import { CloseIcon, TrashBinIcon } from '../../icons';
```
### Rule 3: Consistent Icon Sizing
```tsx
// In buttons/badges
<Icon className="w-4 h-4" />
// Standalone
<Icon className="w-5 h-5" />
// Headers/large
<Icon className="w-6 h-6" />
```
---
## Button Variants
```tsx
// Primary action (main CTA)
<Button variant="primary">Save Changes</Button>
// Secondary action
<Button variant="secondary">Cancel</Button>
// Danger/destructive
<Button variant="danger">Delete</Button>
// Outline style
<Button variant="outline">Learn More</Button>
// Ghost (minimal)
<Button variant="ghost">Skip</Button>
// With icon
<Button variant="primary" leftIcon={<PlusIcon />}>Add Item</Button>
// Icon only
<IconButton icon={<SettingsIcon />} aria-label="Settings" />
```
---
## Form Patterns
### Input Fields
```tsx
<InputField
label="Email"
type="email"
placeholder="you@example.com"
error={errors.email}
required
/>
```
### Select Dropdowns
```tsx
<Select
label="Country"
options={[
{ value: 'us', label: 'United States' },
{ value: 'uk', label: 'United Kingdom' },
]}
value={country}
onChange={setCountry}
/>
```
### Checkboxes & Switches
```tsx
// Checkbox
<Checkbox
label="I agree to terms"
checked={agreed}
onChange={setAgreed}
/>
// Toggle switch
<Switch
label="Enable notifications"
checked={enabled}
onChange={setEnabled}
/>
```
---
## Badge Tones
```tsx
<Badge tone="default">Draft</Badge>
<Badge tone="success">Active</Badge>
<Badge tone="warning">Pending</Badge>
<Badge tone="error">Failed</Badge>
<Badge tone="info">New</Badge>
```
---
## Modal Pattern
```tsx
const [isOpen, setIsOpen] = useState(false);
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Confirm Delete"
>
<p>Are you sure you want to delete this item?</p>
<div className="flex gap-3 mt-4">
<Button variant="secondary" onClick={() => setIsOpen(false)}>
Cancel
</Button>
<Button variant="danger" onClick={handleDelete}>
Delete
</Button>
</div>
</Modal>
```
---
## Toast Notifications
```tsx
import { useToast } from '../components/ui/toast/ToastContainer';
const { showToast } = useToast();
// Success
showToast('Changes saved successfully', 'success');
// Error
showToast('Failed to save changes', 'error');
// Warning
showToast('Your session is about to expire', 'warning');
// Info
showToast('New features available', 'info');
```
---
## Spacing & Layout
### Standard Spacing Scale
| Class | Size | Usage |
|-------|------|-------|
| `gap-2` | 8px | Tight grouping |
| `gap-3` | 12px | Default spacing |
| `gap-4` | 16px | Section spacing |
| `gap-6` | 24px | Major sections |
### Card Layout
```tsx
<Card>
<Card.Header>
<h3>Title</h3>
</Card.Header>
<Card.Body>
Content here
</Card.Body>
<Card.Footer>
<Button>Action</Button>
</Card.Footer>
</Card>
```
---
## Typography
```tsx
// Page titles
<h1 className="text-2xl font-semibold text-gray-900">Page Title</h1>
// Section headers
<h2 className="text-lg font-medium text-gray-900">Section</h2>
// Card titles
<h3 className="text-base font-medium text-gray-900">Card Title</h3>
// Body text
<p className="text-sm text-gray-700">Body content</p>
// Secondary/muted text
<span className="text-sm text-gray-500">Helper text</span>
```
---
## ESLint Enforcement
These rules are **enforced via ESLint**:
| Rule | Level | Enforces |
|------|-------|----------|
| `no-raw-button` | warn | Use `<Button>` component |
| `no-raw-input` | warn | Use `<InputField>` component |
| `no-raw-select` | warn | Use `<Select>` component |
| `no-raw-textarea` | warn | Use `<TextArea>` component |
| `design-system-colors` | error | Only semantic colors |
| `no-hardcoded-colors` | error | No hex codes in className |
---
## Live Demo
Visit `/ui-elements` in the app to see all components in action.
---
## Related Docs
- [COMPONENT-SYSTEM.md](docs/30-FRONTEND/COMPONENT-SYSTEM.md) - Full component API
- [DESIGN-TOKENS.md](docs/30-FRONTEND/DESIGN-TOKENS.md) - CSS variables
- [PAGES.md](docs/30-FRONTEND/PAGES.md) - Route structure

View File

@@ -0,0 +1,459 @@
# Design System & Component Guidelines
**Last Updated:** January 20, 2026
**Version:** 1.8.4
> 🔒 **STYLE SYSTEM LOCKED** - This design system is **LOCKED**. Read this entire document before making any styling changes.
## 🎨 Design Token System
**Single Source of Truth**: `/src/styles/design-system.css`
⚠️ **CRITICAL**: Only 6 hex color values exist in the entire system. Everything else is derived using `color-mix()`.
### The 6 Base Colors
| Token | Hex | Purpose |
|-------|-----|---------|
| `--color-primary` | `#0077B6` | Brand blue |
| `--color-success` | `#00B894` | Success green |
| `--color-warning` | `#F59E0B` | Warning amber |
| `--color-danger` | `#DC2626` | Error red |
| `--color-purple` | `#7C3AED` | Premium purple |
| `--color-gray-base` | `#667085` | Neutral gray |
### Color Scales (Derived)
Each base color generates a full scale (50-950) via `color-mix()`:
```css
/* Example: brand color scale */
--color-brand-50 /* Lightest */
--color-brand-100
--color-brand-200
--color-brand-300
--color-brand-400
--color-brand-500 /* Base = --color-primary */
--color-brand-600
--color-brand-700
--color-brand-800
--color-brand-900
--color-brand-950 /* Darkest */
```
### Tailwind Color Classes
**Available (Use These):**
```css
/* Brand */ bg-brand-50 ... bg-brand-950, text-brand-*, border-brand-*
/* Success */ bg-success-50 ... bg-success-950, text-success-*, border-success-*
/* Warning */ bg-warning-50 ... bg-warning-950, text-warning-*, border-warning-*
/* Error */ bg-error-50 ... bg-error-950, text-error-*, border-error-*
/* Purple */ bg-purple-50 ... bg-purple-950, text-purple-*, border-purple-*
/* Gray */ bg-gray-50 ... bg-gray-950, text-gray-*, border-gray-*
/* Info */ bg-info-50 ... bg-info-950 (alias for brand)
```
**DISABLED (DO NOT USE):**
```css
/* These Tailwind defaults are DISABLED and won't work */
blue-*, red-*, green-*, yellow-*, orange-*, indigo-*, violet-*,
pink-*, rose-*, cyan-*, teal-*, emerald-*, lime-*, amber-*,
slate-*, zinc-*, neutral-*, stone-*
```
### Using Colors
**✅ DO:**
```tsx
// Use semantic Tailwind utilities
<div className="bg-brand-500 text-white">Primary action</div>
<div className="bg-success-100 text-success-700">Success message</div>
<div className="bg-error-50 border-error-500">Error alert</div>
// Use CSS variables for custom cases
<div className="bg-[var(--color-primary)]">Custom</div>
// Use React components with tone prop
<Button tone="brand">Primary</Button>
<Button tone="success">Approve</Button>
<Badge tone="warning">Pending</Badge>
```
**❌ DON'T:**
```tsx
// Don't use Tailwind default colors (DISABLED)
<div className="bg-blue-500">Won't work!</div>
<div className="text-slate-700">Won't work!</div>
// Don't hardcode hex values
<div className="bg-[#0077B6]">Bad!</div>
<div style={{ backgroundColor: '#DC2626' }}>Bad!</div>
```
## 🔒 Style System Lock Status
**DO NOT:**
- ❌ Use Tailwind default color classes (blue-*, red-*, green-*, etc.)
- ❌ Hardcode hex color values anywhere
- ❌ Use inline styles for colors/spacing/typography
- ❌ Import from external icon libraries (lucide-react, @heroicons)
- ❌ Create new CSS classes without documenting
- ❌ Duplicate existing styling patterns
**DO:**
- ✅ Use semantic color tokens (brand-*, success-*, etc.)
- ✅ Import icons from `src/icons`
- ✅ Use React components (Button, Badge, Card, InputField)
- ✅ Run `npm run lint` to check for violations
- ✅ Check `/ui-elements` for component examples
---
## Module Color Scheme (v1.3.2)
Each module has a distinct color for visual identification:
| Module | Color | Tailwind Classes |
|--------|-------|------------------|
| **Planner** (Keywords/Clusters/Ideas) | Purple | `bg-purple-*`, `text-purple-*` |
| **Writer** (Tasks/Content/Images) | Green | `bg-success-*`, `text-success-*` |
| **Automation** | Blue | `bg-brand-*`, `text-brand-*` |
| **Publisher** | Orange | `bg-warning-*`, `text-warning-*` |
| **Billing** | Purple | `bg-purple-*`, `text-purple-*` |
| **Settings** | Gray | `bg-gray-*`, `text-gray-*` |
---
## Core Principles
### 1. **Reuse Existing Components Only**
-**DO**: Use existing components from `/src/components/ui/` and `/src/components/common/`
-**DON'T**: Create new components unless explicitly required and approved
-**DON'T**: Duplicate existing component functionality
### 2. **No Inline Styles**
-**DO**: Use Tailwind CSS utility classes
-**DO**: Use existing CSS classes from the design system
-**DO**: Use existing component props for styling
-**DON'T**: Use inline `style` attributes
-**DON'T**: Use inline `style={{}}` in JSX
### 3. **Component Consistency**
- All UI Elements pages should follow the same structure:
- Use `PageMeta` for SEO
- Use `PageBreadcrumb` for navigation
- Use `ComponentCard` for consistent card styling
- Import and use existing reusable components
### 4. **Available Component Libraries**
#### UI Components (`/src/components/ui/`)
- `alert/` - Alert components
- `avatar/` - Avatar components
- `badge/` - Badge components
- `breadcrumb/` - Breadcrumb navigation
- `button/` - Button components
- `button-group/` - Button group components
- `card/` - Card components
- `dropdown/` - Dropdown menu components
- `images/` - Image components
- `list/` - List components
- `modal/` - Modal components
- `pagination/` - Pagination components
- `progress/` - Progress bar components
- `ribbon/` - Ribbon components
- `spinner/` - Spinner/loading components
- `tabs/` - Tab components
- `table/` - Table components
- `toast/` - Toast notification components
- `tooltip/` - Tooltip components
- `videos/` - Video components
#### Common Components (`/src/components/common/`)
- `ComponentCard` - Consistent card wrapper for component showcases
- `PageBreadcrumb` - Breadcrumb navigation
- `PageMeta` - SEO meta tags
- `ConfirmDialog` - Confirmation dialogs
- `FormModal` - Form modals (when available)
### 5. **Styling Guidelines**
- Use Tailwind CSS classes exclusively
- Use design tokens from `tokens.css` via CSS variables: `var(--color-primary)`
- Use Tailwind brand color utilities: `bg-brand-500`, `text-brand-500`, etc.
- Use React components for buttons, badges, cards (Button, Badge, Card)
- Use dark mode variants: `dark:bg-gray-900`, `dark:text-white`, etc.
- Maintain consistent spacing using Tailwind spacing scale
- Use existing utility classes: `shadow-theme-xs`, `text-theme-sm`, etc.
### 5a. **Deprecated Patterns (DO NOT USE)**
-`.igny8-bg-*`, `.igny8-text-*`, `.igny8-border-*` utility classes
-`var(--igny8-blue)` - use `var(--color-primary)` instead
- ❌ Hardcoded hex colors like `#0693e3`
- ❌ Inline `style={{ color: ... }}` attributes
**Exception**: `.igny8-table-*` and `.igny8-header-metric-*` classes are still active and should be used for table/header components.
### 6. **When Creating New Components is Acceptable**
Only create new components if:
1. Explicitly requested by the user
2. Documented in an approved feature request
3. No existing component can be adapted or extended
### 7. **Checking for Existing Components**
Before implementing any UI element:
1. Search existing components in `/src/components/ui/`
2. Check common components in `/src/components/common/`
3. Review existing UI Elements pages for patterns
4. Check if similar functionality exists in other modules
---
## 📝 Typography Scale
Typography tokens are defined in `design-system.css` and should be used consistently:
### Title Sizes (for page/section headings)
| Token | Size | Line Height | Use Case |
|-------|------|-------------|----------|
| `--text-title-2xl` | 72px | 90px | Hero sections only |
| `--text-title-xl` | 60px | 72px | Landing page headings |
| `--text-title-lg` | 48px | 60px | Major section headers |
| `--text-title-md` | 36px | 44px | Page titles |
| `--text-title-sm` | 30px | 38px | Section subtitles |
### Theme Sizes (for body text)
| Token | Size | Line Height | Use Case |
|-------|------|-------------|----------|
| `--text-theme-xl` | 20px | 30px | Large body text, intro paragraphs |
| `--text-theme-sm` | 14px | 20px | Standard body text, menu items |
| `--text-theme-xs` | 12px | 18px | Small text, labels, captions |
### Usage in Tailwind
```tsx
// Use Tailwind utilities mapped to tokens
<h1 className="text-title-md">Page Title</h1>
<p className="text-theme-sm">Body text</p>
<span className="text-theme-xs">Caption</span>
// Or use the custom utility classes
<h2 className="text-2xl font-semibold">Section Title</h2>
<p className="text-base">Body text</p>
```
### Font Weights
- `font-normal` (400) - Body text
- `font-medium` (500) - Labels, menu items
- `font-semibold` (600) - Headings, emphasis
- `font-bold` (700) - Strong emphasis
---
## 🎨 Module Colors
Module-specific colors are defined in `src/config/colors.config.ts`:
| Module | Primary Color | Usage |
|--------|---------------|-------|
| Planner (Keywords) | `brand-500` (blue) | Icons, progress bars, badges |
| Planner (Clusters) | `purple-500` | Icons, progress bars, badges |
| Planner (Ideas) | `purple-600` | Icons, progress bars, badges |
| Writer (Tasks) | `success-600` (green) | Icons, progress bars, badges |
| Writer (Content) | `success-500` | Icons, progress bars, badges |
| Writer (Images) | `purple-500` | Icons, progress bars, badges |
| Automation | `brand-500` | Pipeline cards |
| Publisher | `warning-500` (amber) | Calendar, scheduling |
| Billing | `purple-500` | Credit displays |
```tsx
import { MODULE_COLORS } from '@/config/colors.config';
// Use in components
<div className={MODULE_COLORS.keywords.bg}>Keywords Section</div>
<span className={MODULE_COLORS.clusters.text}>Cluster Label</span>
```
---
**Last Updated**: January 3, 2026
**Status**: Active Design System Rules - 🔒 LOCKED
---
## 🚨 MANDATORY COMPONENT & STYLING RULES
> **FOR ALL DEVELOPERS AND AI AGENTS**: This section defines the ONLY allowed sources for components, styling, and colors. Violating these rules will result in inconsistent UI and technical debt.
### ALLOWED COMPONENT SOURCES
**ONLY import components from these locations:**
| Category | Allowed Path | Components |
|----------|--------------|------------|
| **UI Components** | `@/components/ui/` or relative `../components/ui/` | Button, Card, Modal, Alert, Badge, Dropdown, Tooltip, Spinner, Tabs, Toast, Pagination, Progress, Avatar, Breadcrumb |
| **Common Components** | `@/components/common/` | PageHeader, ComponentCard, ConfirmDialog, FormModal, TablePageTemplate |
| **Icons** | `@/icons` | All SVG icons (local icons only - external libraries disabled) |
| **Templates** | `@/templates/` | TablePageTemplate, ContentViewTemplate |
### BANNED IMPORTS (DO NOT USE)
```tsx
// ❌ BANNED - Material UI
import { Button, Dialog, Alert, Box } from '@mui/material';
import { SomeIcon } from '@mui/icons-material';
// ❌ BANNED - Chakra UI
import { Button } from '@chakra-ui/react';
// ❌ BANNED - Ant Design
import { Button } from 'antd';
// ❌ BANNED - Custom inline components
const MyButton = () => <button className="...">; // If used more than once, create in ui/
```
### ALLOWED CSS/STYLING SOURCES
**ONLY these CSS files should be used:**
| File | Purpose | When to Use |
|------|---------|-------------|
| `src/styles/design-system.css` | ALL design tokens, colors, shadows, typography | Single source - auto-imported via main.tsx |
**DO NOT CREATE OR USE:**
-`src/styles/global.css` - **Deleted** (consolidated into design-system.css)
-`src/styles/tokens.css` - **Deleted** (consolidated into design-system.css)
-`src/styles/index.css` - **Deleted** (consolidated into design-system.css)
-`components/shared/blocks/blocks.css` - For marketing only
-`components/shared/layouts/layouts.css` - For marketing only
- ❌ Any new `.css` files in component folders
### COLOR USAGE RULES
**Allowed Color Methods:**
```tsx
// ✅ Tailwind brand colors (defined in index.css @theme)
className="bg-brand-500 text-brand-600 border-brand-200"
className="bg-success-500 text-warning-600 bg-error-50"
className="bg-gray-100 text-gray-700 border-gray-300"
// ✅ CSS variables from tokens.css
className="bg-[var(--color-primary)]"
style={{ '--accent': 'var(--color-success)' }}
// ✅ Dark mode variants
className="bg-white dark:bg-gray-900 text-gray-900 dark:text-white"
```
**BANNED Color Methods:**
```tsx
// ❌ Hardcoded hex colors
className="bg-[#0693e3]"
className="text-[#ff7a00]"
style={{ color: '#4c1d95' }}
// ❌ Arbitrary Tailwind colors not in tokens
className="bg-[#4c1d95]"
// ❌ Inline SVG fills with hardcoded colors (use currentColor)
<svg fill="#F04438"> // ❌
<svg fill="currentColor" className="text-error-500"> // ✅
```
### COMPONENT-SPECIFIC RULES
#### Button Component
```tsx
// ✅ CORRECT - Use Button from ui/
import Button from '@/components/ui/button/Button';
<Button variant="primary" tone="brand" size="md">
Click Me
</Button>
// ❌ WRONG
import { Button } from '@mui/material';
<button className="bg-blue-500 px-4 py-2">Click</button> // Use Button component
```
#### Modal/Dialog Component
```tsx
// ✅ CORRECT - Use Modal from ui/
import { Modal } from '@/components/ui/modal';
<Modal isOpen={open} onClose={() => setOpen(false)}>
<div className="p-6">Modal content</div>
</Modal>
// ❌ WRONG
import { Dialog } from '@mui/material';
```
#### Alert Component
```tsx
// ✅ CORRECT
import Alert from '@/components/ui/alert/Alert';
<Alert variant="success" title="Success" message="Action completed" />
// ❌ WRONG
import { Alert } from '@mui/material';
```
#### Badge Component
```tsx
// ✅ CORRECT
import Badge from '@/components/ui/badge/Badge';
<Badge tone="success" variant="soft">Active</Badge>
// ❌ WRONG
import { Chip } from '@mui/material';
```
### FOLDER STRUCTURE FOR NEW COMPONENTS
If a new component is absolutely necessary:
```
src/components/ui/
├── new-component/
│ ├── NewComponent.tsx # Main component
│ └── index.ts # Export barrel
```
**Requirements for new components:**
1. Must be approved in a feature request
2. Must follow existing component patterns (see Button.tsx, Badge.tsx)
3. Must use design tokens for all colors
4. Must support dark mode
5. Must be documented in this file
6. Must NOT duplicate existing functionality
### REFERENCE: PROPERLY STYLED PAGES
**Use these pages as reference for correct styling:**
| Module | Pages | Path |
|--------|-------|------|
| Planner | Keywords, Clusters, Ideas | `src/pages/Planner/` |
| Writer | Review, Approved, Content | `src/pages/Writer/` |
| Publisher | ContentCalendar, PublishingQueue | `src/pages/Publisher/` |
| Sites | List, Settings | `src/pages/Sites/` |
| Dashboard | Home | `src/pages/Dashboard/` |
| Setup | SetupWizard (Onboarding) | `src/pages/Setup/` |
### AUDIT CHECKLIST FOR CODE REVIEW
Before merging any PR, verify:
- [ ] No imports from `@mui/material` or `@mui/icons-material`
- [ ] All buttons use `Button` from `ui/button/Button`
- [ ] All modals use `Modal` from `ui/modal`
- [ ] All alerts use `Alert` from `ui/alert/Alert`
- [ ] No hardcoded hex colors (search for `#[0-9a-fA-F]{3,6}`)
- [ ] No new CSS files created
- [ ] Dark mode variants included where needed
- [ ] Component props match design system (tone, variant, size)

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` |

View File

@@ -0,0 +1,101 @@
# Frontend Page Audit (In Progress)
**Last Updated:** January 20, 2026
**Goal:** Verify each pages functions, API usage, and flow consistency.
---
## Audit Scope (Current Batch)
- Auth pages: Sign In, Sign Up, Forgot Password, Reset Password, Verify Email, Unsubscribe
- Payment page
---
## Auth Pages
### Sign In
- **Route:** `/signin`
- **File:** `frontend/src/pages/AuthPages/SignIn.tsx`
- **Components:** `PageMeta`, `AuthLayout`, `SignInForm`
- **API usage:** none in page (handled by `SignInForm`)
- **Notes:** Page is a wrapper; all auth logic is inside `SignInForm`.
### Sign Up
- **Route:** `/signup`
- **File:** `frontend/src/pages/AuthPages/SignUp.tsx`
- **Components:** `PageMeta`, `SignUpFormUnified`, `GridShape`
- **API usage:** `GET /v1/auth/plans/` (public) for plan list
- **Behavior:**
- Reads `plan` query param to preselect plan
- Defaults to first active plan if no match
- Sorts plans by price ascending
- **Notes:**
- Geo detection removed; country selected in form
- Payment selection deferred to `/account/plans`
### Forgot Password
- **Route:** `/forgot-password`
- **File:** `frontend/src/pages/AuthPages/ForgotPassword.tsx`
- **Components:** `PageMeta`, icons, local form
- **API usage:** `POST /v1/auth/password-reset/` with `{ email }`
- **Behavior:**
- Always shows success state to prevent email enumeration
### Reset Password
- **Route:** `/reset-password?token=...`
- **File:** `frontend/src/pages/AuthPages/ResetPassword.tsx`
- **Components:** `PageMeta`, icons, form
- **API usage:** `POST /v1/auth/password-reset/confirm/` with `{ token, new_password, new_password_confirm }`
- **Behavior:**
- If no `token`, redirects to `/forgot-password`
- Validates password strength client-side
- Handles expired/invalid token states
### Verify Email
- **Route:** `/verify-email?token=...`
- **File:** `frontend/src/pages/AuthPages/VerifyEmail.tsx`
- **Components:** `PageMeta`, icons
- **API usage:** `POST /v1/auth/users/verify_email/` with `{ token }`
- **Behavior:**
- Requires query param `token`
- Handles expired/invalid token state
### Unsubscribe
- **Route:** `/unsubscribe`
- **File:** `frontend/src/pages/AuthPages/Unsubscribe.tsx`
- **Components:** `PageMeta`, icons
- **Behavior:**
- Displays guidance and redirects to `/account/settings?tab=notifications` after 5 seconds
- Notes that transactional emails are not unsubscribable
- **Potential issue:** redirect requires auth; unauthenticated users will be sent to sign-in flow.
---
## Payment Page
### Payment
- **Route:** `/payment`
- **File:** `frontend/src/pages/Payment.tsx`
- **Components:** `InputField`, `TextArea`, `Button`
- **Store usage:** `useAuthStore` for user/account plan
- **Behavior:**
- Reads `plan` from query string or current account plan
- Generates mailto for offline payment confirmation
- Redirects to `/pricing` if plan or user missing
- **Potential issue:** `/pricing` is not defined in `App.tsx` routes (may exist in marketing app).
---
## Next Audit Batch
- Dashboard and core workflow pages
- Sites pages (dashboard, settings, sync, deployment)
- Planner and Writer pages

View File

@@ -0,0 +1,182 @@
# Page Requirements - Site & Sector Selectors
**Last Verified:** December 27, 2025
**Version:** 1.2.0
This document outlines all pages in the application and their requirements for site/sector selectors.
## Legend
- **Site Selector**: Whether the page needs a site selector dropdown
- **Sector Selector**: Whether the page needs a sector selector dropdown
- **Implementation**: How the selectors should behave on this page
- **Next Action**: Recommended workflow guidance for Planner/Writer pages
---
## Planner Module (Content Planning)
| Page | Route | Site Selector | Sector Selector | Implementation | Recommended Next Action |
|------|-------|---------------|-----------------|----------------|-------------------------|
| Keywords | `/planner/keywords` | ✅ Required | ✅ Required | Filter keywords by site/sector | `{count} selected → Auto-Cluster` OR `{clustered} clustered → Generate Ideas` |
| Clusters | `/planner/clusters` | ✅ Required | ✅ Required | Filter clusters by site/sector | `{count} selected → Expand Clusters` OR `{ready} ready → Generate Ideas` |
| Cluster Detail | `/planner/clusters/:id` | ✅ Read-only | ✅ Read-only | Display only (inherited from cluster) | `Back to Clusters` OR `Generate Ideas from Cluster` |
| Ideas | `/planner/ideas` | ✅ Required | ✅ Required | Filter ideas by site/sector | `{count} selected → Create Tasks` OR `{approved} approved → Create Tasks` |
---
## Writer Module (Content Creation)
| Page | Route | Site Selector | Sector Selector | Implementation | Recommended Next Action |
|------|-------|---------------|-----------------|----------------|-------------------------|
| Tasks | `/writer/tasks` | ✅ Required | ✅ Required | Filter tasks by site/sector | `{count} selected → Generate Content` OR `{ready} ready → Generate Content` |
| Content | `/writer/content` | ✅ Required | ✅ Required | Filter content by site/sector | `{count} selected → Generate Images` OR `{draft} drafts → Add Images` |
| Images | `/writer/images` | ✅ Required | ✅ Required | Filter images by site/sector | `{count} selected → Submit for Review` OR `{ready} ready → Submit for Review` |
| Review | `/writer/review` | ✅ Required | ✅ Required | Filter review items by site/sector | `{count} selected → Approve Selected` OR `{reviewed} reviewed → Approve All` |
| Approved | `/writer/approved` | ✅ Required | ✅ Required | Filter approved items by site/sector | `{count} selected → Sync to WordPress` OR `View All Sites` |
---
## Linker Module (Internal Linking)
| Page | Route | Site Selector | Sector Selector | Implementation | Recommended Next Action |
|------|-------|---------------|-----------------|----------------|-------------------------|
| Content List | `/linker/content` | ✅ Required | ✅ Optional | Filter content by site/sector | N/A |
---
## Optimizer Module (Content Optimization)
| Page | Route | Site Selector | Sector Selector | Implementation | Recommended Next Action |
|------|-------|---------------|-----------------|----------------|-------------------------|
| Content Selector | `/optimizer/content` | ✅ Required | ✅ Optional | Filter content for optimization | N/A |
| Analysis Preview | `/optimizer/analyze/:id` | ✅ Read-only | ❌ Not needed | Display only (inherited from content) | N/A |
---
## Thinker Module (AI Configuration) - Admin Only
| Page | Route | Site Selector | Sector Selector | Implementation | Recommended Next Action |
|------|-------|---------------|-----------------|----------------|-------------------------|
| Prompts | `/thinker/prompts` | ❌ Global | ❌ Global | System-wide prompts | N/A |
| Author Profiles | `/thinker/author-profiles` | ❌ Global | ❌ Global | System-wide profiles | N/A |
| Strategies | `/thinker/strategies` | ❌ Global | ❌ Global | System-wide strategies | N/A |
| Image Testing | `/thinker/image-testing` | ❌ Global | ❌ Global | Testing interface | N/A |
---
## Sites Module (Site Management)
| Page | Route | Site Selector | Sector Selector | Implementation | Recommended Next Action |
|------|-------|---------------|-----------------|----------------|-------------------------|
| Site List | `/sites` | ❌ Shows all | ❌ Not applicable | Lists all sites | N/A |
| Site Dashboard | `/sites/:id` | ✅ Read-only | ❌ Not needed | Inherited from route param | N/A |
| Site Content | `/sites/:id/content` | ✅ Read-only | ✅ Optional | Filter by sector within site | N/A |
| Page Manager | `/sites/:id/pages` | ✅ Read-only | ❌ Not needed | Inherited from route param | N/A |
| Site Settings | `/sites/:id/settings` | ✅ Read-only | ❌ Not needed | Inherited from route param | N/A |
| Sync Dashboard | `/sites/:id/sync` | ✅ Read-only | ❌ Not needed | Inherited from route param | N/A |
| Deployment Panel | `/sites/:id/deploy` | ✅ Read-only | ❌ Not needed | Inherited from route param | N/A |
---
## Account & Billing
| Page | Route | Site Selector | Sector Selector | Implementation | Recommended Next Action |
|------|-------|---------------|-----------------|----------------|-------------------------|
| Account Settings | `/account/settings` | ❌ Not needed | ❌ Not needed | Account-level settings | N/A |
| Plans & Billing | `/account/plans` | ❌ Not needed | ❌ Not needed | Account-level billing | N/A |
| Purchase Credits | `/account/purchase-credits` | ❌ Not needed | ❌ Not needed | Account-level purchase | N/A |
| Usage Analytics | `/account/usage` | ✅ Optional | ❌ Not needed | Filter usage by site | N/A |
| Content Settings | `/account/content-settings` | ❌ Not needed | ❌ Not needed | Account-level settings | N/A |
---
## Settings (Admin)
| Page | Route | Site Selector | Sector Selector | Implementation | Recommended Next Action |
|------|-------|---------------|-----------------|----------------|-------------------------|
| General Settings | `/settings` | ❌ Not needed | ❌ Not needed | System-wide settings | N/A |
| Users | `/settings/users` | ❌ Not needed | ❌ Not needed | User management | N/A |
| Subscriptions | `/settings/subscriptions` | ❌ Not needed | ❌ Not needed | Subscription management | N/A |
| System | `/settings/system` | ❌ Not needed | ❌ Not needed | System configuration | N/A |
| Account | `/settings/account` | ❌ Not needed | ❌ Not needed | Account settings | N/A |
| AI Settings | `/settings/ai` | ❌ Not needed | ❌ Not needed | AI model configuration | N/A |
| Plans | `/settings/plans` | ❌ Not needed | ❌ Not needed | Plan management | N/A |
| Industries | `/settings/industries` | ❌ Not needed | ❌ Not needed | Industry reference data | N/A |
| Integration | `/settings/integration` | ❌ Not needed | ❌ Not needed | API integrations | N/A |
| Publishing | `/settings/publishing` | ❌ Not needed | ❌ Not needed | Publishing defaults | N/A |
| Sites | `/settings/sites` | ❌ Not needed | ❌ Not needed | Site configuration | N/A |
---
## Reference Data
| Page | Route | Site Selector | Sector Selector | Implementation | Recommended Next Action |
|------|-------|---------------|-----------------|----------------|-------------------------|
| Seed Keywords | `/reference/seed-keywords` | ✅ Optional | ✅ Optional | Filter reference keywords | N/A |
| Industries | `/reference/industries` | ❌ Not needed | ❌ Not needed | Global reference data | N/A |
---
## Setup
| Page | Route | Site Selector | Sector Selector | Implementation | Recommended Next Action |
|------|-------|---------------|-----------------|----------------|-------------------------|
| Add Keywords | `/setup/add-keywords` | ✅ Required | ✅ Required | Target site/sector for import | N/A |
---
## Other Pages
| Page | Route | Site Selector | Sector Selector | Implementation | Recommended Next Action |
|------|-------|---------------|-----------------|----------------|-------------------------|
| Home Dashboard | `/` | ✅ Optional | ❌ Not needed | Overview all sites or filter | N/A |
| Help | `/help` | ❌ Not needed | ❌ Not needed | Documentation | N/A |
| Components | `/components` | ❌ Not needed | ❌ Not needed | Design system showcase | N/A |
---
## Implementation Notes
### Site Selector Behavior
- **Required**: User must select a site before content is displayed
- **Optional**: Shows all sites by default, can filter to specific site
- **Read-only**: Shows the current site but cannot be changed (inherited from route/context)
- **Not needed**: Page operates at account/system level
### Sector Selector Behavior
- **Required**: User must select both site and sector
- **Optional**: Shows all sectors by default within selected site
- **Read-only**: Shows current sector but cannot be changed
- **Not needed**: Page doesn't operate at sector level
### Next Action Patterns (Planner/Writer)
The next action button should follow this pattern:
1. If items are selected → Action on selected items
2. If no selection but ready items exist → Workflow progression action
3. If nothing actionable → Hide or disable
Example:
```tsx
nextAction={selectedIds.length > 0 ? {
label: 'Process Selected',
message: `${selectedIds.length} items selected`,
onClick: handleBulkAction,
} : workflowStats.ready > 0 ? {
label: 'Continue to Next Step',
href: '/next/page',
message: `${workflowStats.ready} items ready`,
} : undefined}
```
---
## Workflow Pipeline (Planner → Writer)
```
Keywords → Clusters → Ideas → Tasks → Content → Images → Review → Approved
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
Cluster Expand Create Generate Generate Submit Approve Sync to
Keywords Ideas Tasks Content Images Review Content WordPress
```
Each page's "next action" guides users through this pipeline.

View File

@@ -0,0 +1,358 @@
# Frontend Pages & Routes
> **Auto-verified against:** `frontend/src/App.tsx`
> **Last Verified:** January 20, 2026
> **Version:** 1.8.3
> **Total Routes:** 100
---
## Route Configuration
Routes defined in `/frontend/src/App.tsx`:
| Guard | Purpose |
|-------|---------|
| `ProtectedRoute` | Requires authentication |
| `AdminRoute` | Requires staff/admin role |
| `AppLayout` | Shared layout (sidebar + header) |
---
## Public Routes (No Auth Required)
| Route | File | Description |
|-------|------|-------------|
| `/signin` | `AuthPages/SignIn.tsx` | User login |
| `/signup` | `AuthPages/SignUp.tsx` | Account registration |
| `/signup/pk` | `AuthPages/SignUp.tsx` | Legacy route → main signup |
| `/payment` | `Payment.tsx` | Payment page |
| `/forgot-password` | `AuthPages/ForgotPassword.tsx` | Request password reset |
| `/reset-password` | `AuthPages/ResetPassword.tsx` | Set new password |
| `/verify-email` | `AuthPages/VerifyEmail.tsx` | Email verification |
| `/unsubscribe` | `AuthPages/Unsubscribe.tsx` | Email unsubscribe |
| `/terms` | `legal/Terms.tsx` | Terms of Service |
| `/privacy` | `legal/Privacy.tsx` | Privacy Policy |
---
## Dashboard
| Route | File | Description |
|-------|------|-------------|
| `/` | `Dashboard/Home.tsx` | Main dashboard with workflow widgets |
---
## Setup & Keywords Library
| Route | File | Description |
|-------|------|-------------|
| `/setup/wizard` | `Setup/SetupWizard.tsx` | Onboarding wizard |
| `/keywords-library` | `Setup/IndustriesSectorsKeywords.tsx` | Keywords library |
**Redirects:**
- `/setup/add-keywords``/keywords-library`
- `/setup/industries-sectors-keywords``/keywords-library`
---
## Sites Management
| Route | File | Description |
|-------|------|-------------|
| `/sites` | `Sites/List.tsx` | Sites list |
| `/sites/:id` | `Sites/Dashboard.tsx` | Site dashboard |
| `/sites/:id/pages` | `Sites/PageManager.tsx` | Page manager |
| `/sites/:id/pages/new` | `Sites/PageManager.tsx` | New page |
| `/sites/:id/pages/:pageId/edit` | `Sites/PageManager.tsx` | Edit page |
| `/sites/:id/content` | `Sites/Content.tsx` | Site content overview |
| `/sites/:id/content/structure` | `Sites/ContentStructure.tsx` | Content structure |
| `/sites/:id/settings` | `Sites/Settings.tsx` | Site settings |
| `/sites/:id/sync` | `Sites/SyncDashboard.tsx` | Sync dashboard |
| `/sites/:id/deploy` | `Sites/DeploymentPanel.tsx` | Deployment panel |
| `/sites/:id/posts/:postId` | `Sites/PostEditor.tsx` | Post editor |
| `/sites/:id/posts/:postId/edit` | `Sites/PostEditor.tsx` | Post editor |
**Redirects:**
- `/sites/:id/publishing-queue``/publisher/content-calendar`
---
## Planner
| Route | File | Description |
|-------|------|-------------|
| `/planner` | → `/planner/keywords` | Redirect |
| `/planner/keywords` | `Planner/Keywords.tsx` | Keyword management |
| `/planner/clusters` | `Planner/Clusters.tsx` | Cluster listing |
| `/planner/clusters/:id` | `Planner/ClusterDetail.tsx` | Cluster detail |
| `/planner/ideas` | `Planner/Ideas.tsx` | Content ideas |
---
## Writer
| Route | File | Description |
|-------|------|-------------|
| `/writer` | → `/writer/tasks` | Redirect |
| `/writer/tasks` | `Writer/Tasks.tsx` | Task queue |
| `/writer/content` | `Writer/Content.tsx` | Content list |
| `/writer/content/:id` | `Writer/ContentView.tsx` | Content detail |
| `/writer/images` | `Writer/Images.tsx` | Images by content |
| `/writer/review` | `Writer/Review.tsx` | Review queue |
| `/writer/approved` | `Writer/Approved.tsx` | Approved content |
**Redirects:**
- `/writer/drafts``/writer/content`
- `/writer/published``/writer/approved`
---
## Automation
| Route | File | Description |
|-------|------|-------------|
| `/automation` | → `/automation/overview` | Redirect |
| `/automation/overview` | `Automation/AutomationOverview.tsx` | Run history |
| `/automation/runs/:runId` | `Automation/AutomationRunDetail.tsx` | Run detail |
| `/automation/run` | `Automation/AutomationPage.tsx` | Run execution |
**Redirects:**
- `/automation/settings``/sites/settings?tab=automation`
---
## Publisher
| Route | File | Description |
|-------|------|-------------|
| `/publisher` | → `/publisher/content-calendar` | Redirect |
| `/publisher/content-calendar` | `Publisher/ContentCalendar.tsx` | Content calendar |
---
## Linker (Optional Module)
| Route | File | Description |
|-------|------|-------------|
| `/linker` | → `/linker/content` | Redirect |
| `/linker/content` | `Linker/ContentList.tsx` | Linker content list |
---
## Optimizer (Optional Module)
| Route | File | Description |
|-------|------|-------------|
| `/optimizer` | → `/optimizer/content` | Redirect |
| `/optimizer/content` | `Optimizer/ContentSelector.tsx` | Content selector |
| `/optimizer/analyze/:id` | `Optimizer/AnalysisPreview.tsx` | Analysis preview |
---
## Thinker (Admin Only)
| Route | File | Description |
|-------|------|-------------|
| `/thinker` | → `/thinker/prompts` | Redirect |
| `/thinker/prompts` | `Thinker/Prompts.tsx` | Prompt management |
| `/thinker/author-profiles` | `Thinker/AuthorProfiles.tsx` | Author profiles |
| `/thinker/profile` | `Thinker/Profile.tsx` | Profile settings |
| `/thinker/strategies` | `Thinker/Strategies.tsx` | Strategies |
| `/thinker/image-testing` | `Thinker/ImageTesting.tsx` | Image testing |
---
## Billing
| Route | File | Description |
|-------|------|-------------|
| `/billing` | → `/billing/overview` | Redirect |
| `/billing/overview` | `Settings/CreditsAndBilling.tsx` | Billing overview |
| `/billing/credits` | `Billing/Credits.tsx` | Credits listing |
| `/billing/transactions` | `Billing/Transactions.tsx` | Transactions |
| `/billing/usage` | `Billing/Usage.tsx` | Usage |
---
## Account
| Route | File | Description |
|-------|------|-------------|
| `/account/notifications` | `account/NotificationsPage.tsx` | Notifications |
| `/account/settings` | `account/AccountSettingsPage.tsx` | Account settings |
| `/account/settings/profile` | `account/AccountSettingsPage.tsx` | Profile tab |
| `/account/settings/team` | `account/AccountSettingsPage.tsx` | Team tab |
| `/account/plans` | `account/PlansAndBillingPage.tsx` | Plans & billing |
| `/account/plans/upgrade` | `account/PlansAndBillingPage.tsx` | Upgrade tab |
| `/account/plans/history` | `account/PlansAndBillingPage.tsx` | History tab |
| `/account/usage` | `account/UsageDashboardPage.tsx` | Usage dashboard |
| `/account/content-settings` | `account/ContentSettingsPage.tsx` | Content settings |
| `/account/content-settings/publishing` | `account/ContentSettingsPage.tsx` | Publishing tab |
| `/account/content-settings/images` | `account/ContentSettingsPage.tsx` | Images tab |
**Redirects:**
- `/account/team``/account/settings/team`
- `/account/purchase-credits``/account/plans`
- `/account/usage/logs``/account/usage`
- `/account/usage/credits``/account/usage`
- `/account/usage/insights``/account/usage`
- `/account/usage/activity``/account/usage`
---
## Reference Data
| Route | File | Description |
|-------|------|-------------|
| `/reference/seed-keywords` | `Reference/SeedKeywords.tsx` | Seed keywords |
| `/reference/industries` | `Reference/Industries.tsx` | Industries |
---
## Settings (Admin)
| Route | File | Description |
|-------|------|-------------|
| `/settings` | `Settings/General.tsx` | General settings |
| `/settings/users` | `Settings/Users.tsx` | Users |
| `/settings/subscriptions` | `Settings/Subscriptions.tsx` | Subscriptions |
| `/settings/system` | `Settings/System.tsx` | System settings |
| `/settings/account` | `Settings/Account.tsx` | Account settings |
| `/settings/plans` | `Settings/Plans.tsx` | Plans |
| `/settings/industries` | `Settings/Industries.tsx` | Industries |
| `/settings/integration` | `Settings/Integration.tsx` | Integrations |
| `/settings/publishing` | `Settings/Publishing.tsx` | Publishing settings |
| `/settings/sites` | `Settings/Sites.tsx` | Sites settings |
**Redirects:**
- `/settings/profile``/account/settings`
- `/settings/import-export``/`
---
## Help
| Route | File | Description |
|-------|------|-------------|
| `/help` | `Help/Help.tsx` | Help center |
---
## Internal Pages (Dev Only)
| Route | File | Description |
|-------|------|-------------|
| `/components` | `Components.tsx` | Component showcase |
| `/ui-elements` | `UIElements.tsx` | Design system reference |
---
## Fallback
| Route | File | Description |
|-------|------|-------------|
| `*` | `OtherPage/NotFound.tsx` | 404 page |
---
## Page Files Directory
```
frontend/src/pages/
├── account/
│ ├── AccountSettingsPage.tsx
│ ├── ContentSettingsPage.tsx
│ ├── NotificationsPage.tsx
│ ├── PlansAndBillingPage.tsx
│ ├── PurchaseCreditsPage.tsx
│ ├── UsageAnalyticsPage.tsx
│ └── UsageDashboardPage.tsx
├── AuthPages/
│ ├── AuthPageLayout.tsx
│ ├── ForgotPassword.tsx
│ ├── ResetPassword.tsx
│ ├── SignIn.tsx
│ ├── SignUp.tsx
│ ├── Unsubscribe.tsx
│ └── VerifyEmail.tsx
├── Automation/
│ ├── AutomationOverview.tsx
│ ├── AutomationPage.tsx
│ ├── AutomationRunDetail.tsx
│ └── PipelineSettings.tsx
├── Billing/
│ ├── Credits.tsx
│ ├── Transactions.tsx
│ └── Usage.tsx
├── Dashboard/
│ └── Home.tsx
├── Help/
│ └── Help.tsx
├── legal/
│ ├── Privacy.tsx
│ └── Terms.tsx
├── Linker/
│ └── ContentList.tsx
├── Optimizer/
│ ├── AnalysisPreview.tsx
│ └── ContentSelector.tsx
├── OtherPage/
│ └── NotFound.tsx
├── Planner/
│ ├── ClusterDetail.tsx
│ ├── Clusters.tsx
│ ├── Ideas.tsx
│ └── Keywords.tsx
├── Publisher/
│ ├── ContentCalendar.tsx
│ └── PublishSettings.tsx
├── Reference/
│ ├── Industries.tsx
│ └── SeedKeywords.tsx
├── Settings/
│ ├── Account.tsx
│ ├── CreditsAndBilling.tsx
│ ├── General.tsx
│ ├── Industries.tsx
│ ├── Integration.tsx
│ ├── Plans.tsx
│ ├── Publishing.tsx
│ ├── Sites.tsx
│ ├── Subscriptions.tsx
│ ├── System.tsx
│ ├── Users.tsx
│ └── WordPressIntegrationDebug.tsx
├── Setup/
│ ├── IndustriesSectorsKeywords.tsx
│ └── SetupWizard.tsx
├── Sites/
│ ├── AIAutomationSettings.tsx
│ ├── Content.tsx
│ ├── ContentStructure.tsx
│ ├── Dashboard.tsx
│ ├── DeploymentPanel.tsx
│ ├── List.tsx
│ ├── PageManager.tsx
│ ├── PostEditor.tsx
│ ├── PublishingQueue.tsx
│ ├── Settings.tsx
│ └── SyncDashboard.tsx
├── Thinker/
│ ├── AuthorProfiles.tsx
│ ├── ImageTesting.tsx
│ ├── Profile.tsx
│ ├── Prompts.tsx
│ └── Strategies.tsx
├── Writer/
│ ├── Approved.tsx
│ ├── Content.tsx
│ ├── ContentView.tsx
│ ├── Images.tsx
│ ├── Review.tsx
│ └── Tasks.tsx
├── Components.tsx
├── Payment.tsx
└── UIElements.tsx
```

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,516 @@
# Zustand State Management
**Last Verified:** January 20, 2026
**Version:** 1.8.4
**Framework:** Zustand 4 with persist middleware
---
## Store Architecture
All stores in `/frontend/src/store/` use Zustand with TypeScript.
**Key Patterns:**
- `persist` middleware for localStorage persistence
- Async actions for API calls
- Selectors for derived state
**Available Stores (11 total):**
| Store | File | Purpose |
|-------|------|---------|
| Auth | `authStore.ts` | Authentication, user session, account |
| Site | `siteStore.ts` | Site selection/management |
| Sector | `sectorStore.ts` | Sector management within sites |
| Planner | `plannerStore.ts` | Planner module state |
| Billing | `billingStore.ts` | Credits, billing info |
| Notification | `notificationStore.ts` | App notifications |
| Settings | `settingsStore.ts` | User preferences |
| Module | `moduleStore.ts` | Module enable/disable state |
| Column Visibility | `columnVisibilityStore.ts` | Table column preferences |
| Page Size | `pageSizeStore.ts` | Table pagination preferences |
| Onboarding | `onboardingStore.ts` | Onboarding wizard state |
---
## Auth Store (`authStore.ts`)
**Purpose:** User authentication and session management
```typescript
interface AuthState {
user: User | null;
account: Account | null;
isAuthenticated: boolean;
accessToken: string | null;
refreshToken: string | null;
isLoading: boolean;
error: string | null;
}
interface AuthActions {
login(email: string, password: string): Promise<void>;
logout(): void;
register(data: RegisterData): Promise<void>;
refreshAccessToken(): Promise<void>;
fetchUser(): Promise<void>;
updateUser(data: UserUpdate): Promise<void>;
}
```
**Persistence:** `accessToken`, `refreshToken` in localStorage
**Usage:**
```typescript
const { user, login, logout, isAuthenticated } = useAuthStore();
```
---
## Site Store (`siteStore.ts`)
**Purpose:** Site selection and management
```typescript
interface SiteState {
sites: Site[];
currentSite: Site | null;
isLoading: boolean;
error: string | null;
}
interface SiteActions {
fetchSites(): Promise<void>;
createSite(data: SiteCreate): Promise<Site>;
updateSite(id: string, data: SiteUpdate): Promise<Site>;
deleteSite(id: string): Promise<void>;
setCurrentSite(site: Site): void;
}
```
**Persistence:** `currentSite.id` in localStorage
**Auto-selection:** If no site selected and sites exist, auto-selects first site
---
## Sector Store (`sectorStore.ts`)
**Purpose:** Sector selection and management within sites
```typescript
interface SectorState {
sectors: Sector[];
currentSector: Sector | null;
isLoading: boolean;
error: string | null;
}
interface SectorActions {
fetchSectors(siteId: string): Promise<void>;
createSector(data: SectorCreate): Promise<Sector>;
updateSector(id: string, data: SectorUpdate): Promise<Sector>;
deleteSector(id: string): Promise<void>;
setCurrentSector(sector: Sector): void;
}
```
**Persistence:** `currentSector.id` in localStorage
**Dependency:** Reloads when `currentSite` changes
---
## Module Store (`moduleStore.ts`)
**Purpose:** Track which modules are enabled/disabled
```typescript
interface ModuleState {
modules: ModuleSettings;
isLoading: boolean;
error: string | null;
}
interface ModuleSettings {
planner_enabled: boolean;
writer_enabled: boolean;
linker_enabled: boolean;
optimizer_enabled: boolean;
automation_enabled: boolean;
integration_enabled: boolean;
publisher_enabled: boolean;
}
interface ModuleActions {
fetchModules(): Promise<void>;
updateModules(settings: Partial<ModuleSettings>): Promise<void>;
isModuleEnabled(module: ModuleName): boolean;
}
```
**Usage:**
```typescript
const { isModuleEnabled } = useModuleStore();
if (isModuleEnabled('planner')) { /* show planner */ }
```
**Currently used for:** Sidebar visibility only
---
## Billing Store (`billingStore.ts`)
**Purpose:** Credit balance and usage tracking
```typescript
interface BillingState {
balance: CreditBalance | null;
usage: CreditUsage[];
limits: PlanLimits | null;
isLoading: boolean;
error: string | null;
}
interface CreditBalance {
ideaCredits: number;
contentCredits: number;
imageCredits: number;
optimizationCredits: number;
}
interface BillingActions {
fetchBalance(): Promise<void>;
fetchUsage(period?: string): Promise<void>;
fetchLimits(): Promise<void>;
}
```
**Refresh triggers:**
- After content generation
- After image generation
- After optimization (when implemented)
---
## Planner Store (`plannerStore.ts`)
**Purpose:** Keywords, clusters, and content ideas state
```typescript
interface PlannerState {
keywords: Keyword[];
clusters: Cluster[];
ideas: ContentIdea[];
selectedKeywords: string[];
filters: KeywordFilters;
isLoading: boolean;
error: string | null;
}
interface PlannerActions {
fetchKeywords(siteId: string, sectorId?: string): Promise<void>;
createKeyword(data: KeywordCreate): Promise<Keyword>;
bulkDeleteKeywords(ids: string[]): Promise<void>;
autoCluster(keywordIds: string[]): Promise<void>;
generateIdeas(clusterId: string): Promise<void>;
setFilters(filters: Partial<KeywordFilters>): void;
selectKeywords(ids: string[]): void;
}
```
---
## Writer Store (`writerStore.ts`)
**Purpose:** Tasks and content management
```typescript
interface WriterState {
tasks: Task[];
content: Content[];
currentContent: Content | null;
filters: TaskFilters;
isLoading: boolean;
isGenerating: boolean;
error: string | null;
}
interface WriterActions {
fetchTasks(siteId: string, sectorId?: string): Promise<void>;
createTask(data: TaskCreate): Promise<Task>;
generateContent(taskId: string): Promise<Content>;
fetchContent(contentId: string): Promise<Content>;
updateContent(id: string, data: ContentUpdate): Promise<Content>;
generateImages(contentId: string): Promise<void>;
publishToWordPress(contentId: string): Promise<void>;
}
```
**Generation state:** `isGenerating` tracks active AI operations
---
## Automation Store (`automationStore.ts`)
**Purpose:** Automation pipeline state and control
```typescript
interface AutomationState {
config: AutomationConfig | null;
currentRun: AutomationRun | null;
pipeline: PipelineOverview | null;
history: AutomationRun[];
logs: AutomationLog[];
isLoading: boolean;
error: string | null;
}
interface AutomationActions {
fetchConfig(siteId: string): Promise<void>;
updateConfig(data: ConfigUpdate): Promise<void>;
startRun(siteId: string): Promise<void>;
pauseRun(runId: string): Promise<void>;
resumeRun(runId: string): Promise<void>;
cancelRun(runId: string): Promise<void>;
fetchPipeline(siteId: string): Promise<void>;
fetchLogs(runId: string): Promise<void>;
}
```
**Polling:** Active runs trigger status polling every 5 seconds
---
## Integration Store (`integrationStore.ts`)
**Purpose:** WordPress integration management
```typescript
interface IntegrationState {
integrations: SiteIntegration[];
currentIntegration: SiteIntegration | null;
syncStatus: SyncStatus | null;
isLoading: boolean;
isSyncing: boolean;
error: string | null;
}
interface IntegrationActions {
fetchIntegrations(siteId: string): Promise<void>;
createIntegration(data: IntegrationCreate): Promise<SiteIntegration>;
testConnection(id: string): Promise<TestResult>;
triggerSync(id: string): Promise<void>;
fetchSyncStatus(id: string): Promise<SyncStatus>;
}
```
---
## UI Store (`uiStore.ts`)
**Purpose:** UI state (sidebar, modals, notifications)
```typescript
interface UIState {
sidebarOpen: boolean;
sidebarCollapsed: boolean;
theme: 'light' | 'dark' | 'system';
notifications: Notification[];
}
interface UIActions {
toggleSidebar(): void;
collapseSidebar(): void;
setTheme(theme: Theme): void;
addNotification(notification: Notification): void;
removeNotification(id: string): void;
}
```
**Persistence:** `theme`, `sidebarCollapsed` in localStorage
---
## Notification Store (`notificationStore.ts`) - v1.2.0
**Purpose:** In-app notifications for AI task completions and system events
```typescript
type NotificationType = 'success' | 'error' | 'warning' | 'info';
type NotificationCategory = 'ai_task' | 'system' | 'info';
interface Notification {
id: string;
apiId?: number; // Server ID for synced notifications
type: NotificationType;
category: NotificationCategory;
title: string;
message: string;
timestamp: Date;
read: boolean;
actionLabel?: string;
actionHref?: string;
metadata?: {
taskId?: string;
functionName?: string;
count?: number;
credits?: number;
};
}
interface NotificationStore {
notifications: Notification[];
unreadCount: number;
isLoading: boolean;
lastFetched: Date | null;
// Actions
addNotification(notification: Omit<Notification, 'id' | 'timestamp' | 'read'>): void;
markAsRead(id: string): void;
markAllAsRead(): void;
removeNotification(id: string): void;
clearAll(): void;
fetchFromAPI(): Promise<void>;
syncWithAPI(): Promise<void>;
}
```
**Features:**
- In-memory queue for optimistic UI updates
- API sync for persistent notifications
- Auto-dismissal with configurable timeout
- Read/unread state tracking
- Category-based filtering (ai_task, system, info)
**API Integration:** Uses `notifications.api.ts` service:
- `fetchNotifications()` - List with pagination
- `fetchUnreadCount()` - Unread count
- `markNotificationRead()` - Mark single as read
- `markAllNotificationsRead()` - Mark all as read
- `deleteNotification()` - Delete notification
**Usage:**
```typescript
const { notifications, unreadCount, addNotification, markAsRead } = useNotificationStore();
// Add AI task completion notification
addNotification({
type: 'success',
category: 'ai_task',
title: 'Content Generated',
message: 'Generated 5 articles successfully',
metadata: { count: 5, credits: 250 }
});
```
---
## Store Dependencies
```
authStore
└── siteStore (loads after auth)
└── sectorStore (loads when site changes)
├── plannerStore (scoped to site/sector)
├── writerStore (scoped to site/sector)
├── automationStore (scoped to site)
└── integrationStore (scoped to site)
notificationStore (global, polls/syncs independently)
moduleStore (global, loads once per account)
billingStore (global, loads once per account)
onboardingStore (global, syncs with UserSettings backend)
uiStore (local only, no API)
```
---
## Onboarding Store (`onboardingStore.ts`) (v1.3.2)
**Purpose:** Manages onboarding wizard and guide screen state
```typescript
interface OnboardingState {
isGuideDismissed: boolean;
isGuideVisible: boolean;
isLoading: boolean;
lastSyncedAt: Date | null;
}
interface OnboardingActions {
dismissGuide(): Promise<void>;
showGuide(): void;
toggleGuide(): void;
loadFromBackend(): Promise<void>;
syncToBackend(dismissed: boolean): Promise<void>;
}
```
**Persistence:**
- Local: `onboarding-storage` in localStorage
- Backend: `UserSettings` with key `workflow_guide_dismissed`
**Features:**
- Cross-device sync via backend UserSettings
- Rate-limited sync (max every 5 minutes)
- Graceful fallback if backend unavailable
**Usage:**
```typescript
const { isGuideVisible, dismissGuide, showGuide } = useOnboardingStore();
// Show onboarding wizard
if (isGuideVisible) {
return <OnboardingWizard onDismiss={dismissGuide} />;
}
// Re-show guide button
<Button onClick={showGuide}>Show Guide</Button>
```
---
## Store Files Location
```
frontend/src/store/
├── authStore.ts
├── siteStore.ts
├── sectorStore.ts
├── moduleStore.ts
├── billingStore.ts
├── notificationStore.ts # v1.2.0
├── onboardingStore.ts
├── pageSizeStore.ts
├── plannerStore.ts
├── writerStore.ts
├── automationStore.ts
├── integrationStore.ts
├── uiStore.ts
└── index.ts (re-exports)
```
---
## Best Practices
1. **Always scope to site/sector** when fetching module data
2. **Check module enabled** before showing features
3. **Handle loading states** in UI components
4. **Clear store on logout** to prevent data leaks
5. **Use selectors** for derived/filtered data
---
## Planned Changes
| Item | Description | Priority |
|------|-------------|----------|
| Add `linkerStore` | State for internal linking results | Medium |
| Add `optimizerStore` | State for optimization results | Medium |
| Better error typing | Typed error codes per store | Low |
| DevTools integration | Zustand devtools middleware | Low |