temproary docs uplaoded
This commit is contained in:
@@ -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.
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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` |
|
||||
101
v2/Live Docs on Server/igny8-app-docs/30-FRONTEND/PAGE-AUDIT.md
Normal file
101
v2/Live Docs on Server/igny8-app-docs/30-FRONTEND/PAGE-AUDIT.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Frontend Page Audit (In Progress)
|
||||
|
||||
**Last Updated:** January 20, 2026
|
||||
**Goal:** Verify each page’s 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
|
||||
@@ -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.
|
||||
358
v2/Live Docs on Server/igny8-app-docs/30-FRONTEND/PAGES.md
Normal file
358
v2/Live Docs on Server/igny8-app-docs/30-FRONTEND/PAGES.md
Normal 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
516
v2/Live Docs on Server/igny8-app-docs/30-FRONTEND/STORES.md
Normal file
516
v2/Live Docs on Server/igny8-app-docs/30-FRONTEND/STORES.md
Normal 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 |
|
||||
Reference in New Issue
Block a user