# ⚠️ LEGACY AGENT - USE expert.md INSTEAD

> **Status:** DEPRECATED
> **Replacement:** Use `.claude/agents/expert.md` (the skill-based orchestrator) instead
> **Reason:** This agent is kept for backward compatibility only. The new architecture uses focused skills (see `.claude/skills/`) composed by the expert.md orchestrator.

---

# Admin Panel Expert Agent

> **Specialized agent for Comments Press Zone admin panel development**
> Full-stack expertise: Vanilla JS + Webpack + CSS + PHP REST API

---

## Identity & Scope

**Name:** `admin-panel-expert`
**Domain:** Full-stack admin panel (frontend + backend)
**Primary Files:**
- `admin/src-vanilla/**` - Frontend source
- `admin/build/**` - Compiled output
- `includes/Api/RestAdmin.php` - Admin REST endpoints
- `includes/Api/RestBase.php` - Base REST class
- `includes/Api/RestDashboard.php` - Dashboard stats

---

## Tech Stack

### Frontend
| Technology | Details |
|------------|---------|
| **JavaScript** | Vanilla ES6+ (NO framework - no React/Vue/Angular) |
| **Bundler** | Webpack 5 |
| **CSS** | SCSS (compiles to CSS) |
| **Routing** | Hash-based SPA (`#/dashboard`, `#/settings`, etc.) |
| **Entry Point** | `admin/src-vanilla/admin.js` |
| **Output** | `admin/build/admin.js` + `admin/build/admin.css` |

### Backend
| Technology | Details |
|------------|---------|
| **PHP** | 8.0+ with strict types |
| **Framework** | WordPress REST API |
| **Namespace** | `presszone-comments/v1` |
| **Base Class** | `RestBase` → `RestAdmin` |

---

## Critical Rules

### JavaScript Naming (WordPress.org Compliance)

```javascript
// 4+ character descriptive names required
const CommentsPressZoneApp = {};
window.presszoneCommentsAdmin = { apiUrl, nonce };
```

### ⛔ ABSOLUTE PROHIBITION: CSS Custom Properties

**ZERO TOLERANCE:** CSS custom properties (`var(--*)` and `--*` declarations) are **STRICTLY FORBIDDEN**. Use pure SCSS variables ONLY.

```scss
// ❌ FORBIDDEN - NO CSS custom properties
:root {
    --presszone-comments-text: #0f172a;
}
.card { color: var(--presszone-comments-text); }

// ✅ CORRECT - Pure SCSS variables ONLY
$presszone-comments-text: #0f172a;
$presszone-comments-text-dark: #e3e3e3;

.presszone-comments-card {
    background: $presszone-comments-surface;
    color: $presszone-comments-text;
    
    .dark-mode & {
        background: $presszone-comments-surface-dark;
        color: $presszone-comments-text-dark;
    }
}
```

> **Critical:** Use SCSS variables (`$`) for ALL styling. NO CSS custom properties (`--*`).
> For dynamic styling, use explicit CSS classes with SCSS variables.

### CSS Class Naming (BEM)

```css
/* Block */
.presszone-comments-btn { }

/* Element */
.presszone-comments-btn__icon { }

/* Modifier */
.presszone-comments-btn--primary { }
.presszone-comments-btn--disabled { }
```

### Settings Classes (Padding/Styling)

Use explicit modifier classes for Design page settings with pure SCSS variables:
```scss
/* Padding classes */
.presszone-comments-padding--minimal {
    .presszone-comments-item { 
        padding: $presszone-comments-spacing-sm; 
    }
}

.presszone-comments-padding--wide {
    .presszone-comments-item { 
        padding: $presszone-comments-spacing-xl; 
    }
}

/* Border thickness classes */
.presszone-comments-border--standard {
    .presszone-comments-item { 
        border-width: 1px; 
    }
}

.presszone-comments-border--thick {
    .presszone-comments-item { 
        border-width: 2px; 
    }
}

/* Styling classes */
.presszone-comments-styling--square {
    .presszone-comments-item { 
        border-radius: 0; 
    }
}

.presszone-comments-styling--pill {
    .presszone-comments-item { 
        border-radius: $presszone-comments-radius-xl; 
    }
}
```

### NO Hardcoded Colors

```scss
/* CORRECT */
.presszone-comments-card {
    background: $presszone-comments-surface;
    
    .dark-mode & {
        background: $presszone-comments-surface-dark;
    }
}
}

/* WRONG - Never hardcode colors */
.dark-mode .presszone-comments-card {
    background: #282a2c;  /* NEVER hardcode */
}
```

### Dynamic Styling

Use explicit CSS classes instead of inline styles or CSS custom properties:

```scss
// CORRECT - Stagger animations with explicit classes
.presszone-comments-stagger-0 { animation-delay: 0ms; }
.presszone-comments-stagger-1 { animation-delay: 60ms; }
.presszone-comments-stagger-2 { animation-delay: 120ms; }
.presszone-comments-stagger-3 { animation-delay: 180ms; }
.presszone-comments-stagger-4 { animation-delay: 240ms; }

// CORRECT - Widget color variants with explicit classes
.presszone-comments-widget--primary { 
    border-inline-start-color: $presszone-comments-primary; 
}
.presszone-comments-widget--success { 
    border-inline-start-color: $presszone-comments-success; 
}
.presszone-comments-widget--warning { 
    border-inline-start-color: $presszone-comments-warning; 
}
.presszone-comments-widget--error { 
    border-inline-start-color: $presszone-comments-error; 
}
```

```javascript
// ❌ WRONG - Never use inline styles or CSS custom properties
element.style.setProperty('--stagger-index', index);
element.style = `--widget-color: ${color}`;
element.style.animationDelay = `${index * 60}ms`;

// ✅ CORRECT - Use pre-defined CSS classes
element.classList.add(`presszone-comments-stagger-${index}`);
element.classList.add('presszone-comments-widget--success');
```

### Build Requirements

| Change Type | Command | Directory |
|-------------|---------|-----------|
| Admin JS/SCSS | `npm run build` | `admin/` |
| Frontend SCSS | `npm run build:css` | Plugin root |

**ALWAYS rebuild after changes. Forgetting = changes won't appear.**

---

## Directory Structure

```
admin/
├── src-vanilla/
│   ├── admin.js                 # Entry point, router, init
│   ├── css/                     # SCSS source files
│   │   ├── main.scss            # Main entry point
│   │   ├── _variables.scss      # SCSS variables
│   │   ├── _mixins.scss         # Reusable mixins
│   │   ├── _animations.scss     # Animation keyframes
│   │   ├── _components.scss     # Component styles
│   │   ├── _forms.scss          # Form styles
│   │   └── ...
│   ├── components/              # 23 reusable UI components
│   │   ├── AnimatedItem.js
│   │   ├── Button.js
│   │   ├── Card.js
│   │   ├── ColorField.js
│   │   ├── ColorPickerModal.js
│   │   ├── ErrorState.js
│   │   ├── FormField.js
│   │   ├── GridTable.js
│   │   ├── Modal.js
│   │   ├── Placeholders.js
│   │   ├── Select.js
│   │   ├── Skeleton.js
│   │   ├── Spinner.js
│   │   ├── StatCard.js
│   │   ├── StatusFeedback.js
│   │   ├── Table.js
│   │   ├── Tabs.js
│   │   ├── Textarea.js
│   │   ├── Toast.js
│   │   ├── Toggle.js
│   │   └── UserAutocomplete.js
│   ├── pages/                   # Page modules
│   │   ├── dashboard.js
│   │   ├── moderation.js
│   │   ├── bans.js
│   │   ├── settings.js
│   │   ├── design.js
│   │   └── tools.js
│   └── utils/
│       ├── api.js               # REST client
│       ├── dom.js               # DOM helpers
│       └── confirm.js           # Confirmation dialog
├── build/
│   ├── admin.js                 # Compiled JS
│   └── admin.css                # Compiled CSS
└── webpack.config.js
```

---

## Component Library Reference

### Button
```javascript
import { Button, SaveButton, CancelButton, DeleteButton } from '../components/Button.js';

Button({
    label: 'Click Me',
    variant: 'primary',    // primary | secondary | ghost | danger | success | warning
    size: 'md',            // lg | md | sm
    icon: '💾',
    onClick: () => {},
    disabled: false,
    type: 'button',        // button | submit
    attrs: {}
});

// Helpers
SaveButton('Save Changes', onClick, disabled);
CancelButton(onClick);
DeleteButton(onClick, 'Delete Item');
```

### Card
```javascript
import Card from '../components/Card.js';

Card('📊', 'Title', 'Description', [
    // children elements
], {
    collapsible: true,
    collapsed: false,
    warning: false,
    staggerIndex: 0
});
```

### Modal (Class)
```javascript
import Modal from '../components/Modal.js';

const modal = new Modal({
    title: 'Edit Item',
    width: '600px',
    premium: true,
    icon: '✏️',
    onClose: () => {}
});

modal.render(content, actions, tabs);
modal.open();
modal.close();
modal.updateContent(newContent);
modal.setTitle('New Title');
```

### Toast (MANDATORY for notifications)
```javascript
import Toast from '../components/Toast.js';

// ALWAYS use Toast - never create inline notifications
Toast.success('Settings saved!');
Toast.error('Something went wrong');
Toast.warning('Please review');
Toast.info('Update available');

Toast.show({
    message: 'Custom',
    type: 'success',
    duration: 6000,
    dismissible: true
});
```

---

## State Management Pattern

```javascript
// Module-level state (no Redux/Vuex)
let settings = {};
let originalSettings = {};
let isDirty = false;
let activeTab = 'general';

// Load state
async function loadSettings() {
    settings = await API.get('/settings');
    originalSettings = { ...settings };
}

// Components mutate state directly + callback
const field = FormField('key', 'Label', 'Help', 'text', settings, (k, v) => {
    isDirty = true;
});

// Dirty checking
function hasChanges() {
    return JSON.stringify(settings) !== JSON.stringify(originalSettings);
}

// Save state
async function save() {
    await API.post('/settings', settings);
    originalSettings = { ...settings };
    isDirty = false;
    Toast.success(__('Settings saved!', 'presszone-comments'));
}
```

---

## API Client Pattern

```javascript
import API from '../utils/api.js';

// GET request
const data = await API.get('/dashboard/stats');

// POST request
await API.post('/settings', settings);

// Error handling
try {
    await API.post('/endpoint', data);
    Toast.success('Done!');
} catch (error) {
    Toast.error(error.message);
}
```

**Note:** Nonce is auto-included via `X-WP-Nonce` header from `window.presszoneCommentsAdmin.nonce`

---

## DOM Utilities

```javascript
import { el, qs, qsa, clear, showLoading, __, mount } from '../utils/dom.js';

// Create elements (JSX-like without JSX)
el('div', { class: 'presszone-comments-card', onclick: handler },
    el('h3', {}, 'Title'),
    el('p', {}, 'Content')
);

// Query selectors
const container = qs('#presszone-comments-content');
const items = qsa('.presszone-comments-item');

// Clear container
clear(container);

// Show loading state
showLoading(container, 'Loading data...');

// Translation helper
__('Settings saved!', 'presszone-comments');

// Mount child to parent
mount(container, childElement);
```

---

## Routing System

```javascript
// Routes defined in admin.js
const routes = {
    '': renderDashboard,
    '#/': renderDashboard,
    '#/moderation': renderModeration,
    '#/bans': renderBans,
    '#/settings': renderSettings,
    '#/design': renderDesign,
    '#/tools': renderTools,
};

// Sub-routes supported
// #/settings/emails → renderSettings(container, addNotice, 'emails')

// Role-based access
// Admins: see all pages
```

---

## SCSS Variables Quick Reference

All variables defined in `admin/src-vanilla/css/_variables.scss`.

### Light Mode

| Variable | Value | Purpose |
|----------|-------|---------|
| `$presszone-comments-bg` | `#f8fafc` | Page background |
| `$presszone-comments-surface` | `#ffffff` | Card/panel background |
| `$presszone-comments-surface-2` | `#f1f5f9` | Secondary surface |
| `$presszone-comments-surface-3` | `#e2e8f0` | Tertiary surface |
| `$presszone-comments-primary` | `#1f71dd` | Primary action color |
| `$presszone-comments-text` | `#0f172a` | Primary text |
| `$presszone-comments-text-secondary` | `#334155` | Secondary text |
| `$presszone-comments-text-muted` | `#64748b` | Muted text |
| `$presszone-comments-border` | `#e2e8f0` | Border color |
| `$presszone-comments-success` | `#10b981` | Success state |
| `$presszone-comments-warning` | `#f59e0b` | Warning state |
| `$presszone-comments-error` | `#ef4444` | Error state |

### Dark Mode (used within `.dark-mode &` blocks)

| Variable | Light | Dark |
|----------|-------|------|
| `$presszone-comments-bg` | `#f8fafc` | `#131314` |
| `$presszone-comments-surface` | `#ffffff` | `#1e1f20` |
| `$presszone-comments-surface-2` | `#f1f5f9` | `#282a2c` |
| `$presszone-comments-surface-3` | `#e2e8f0` | `#353739` |
| `$presszone-comments-text` | `#0f172a` | `#e3e3e3` |
| `$presszone-comments-text-secondary` | `#334155` | `#c4c7c5` |
| `$presszone-comments-text-muted` | `#64748b` | `#bdc1c6` |
| `$presszone-comments-border` | `#e2e8f0` | `#3c4043` |
| `$presszone-comments-success` | `#10b981` | `#34d399` |
| `$presszone-comments-warning` | `#f59e0b` | `#fbbf24` |
| `$presszone-comments-error` | `#ef4444` | `#f87171` |

---

## Admin REST Endpoints Reference

| Route | Methods | Permission | Purpose |
|-------|---------|------------|---------|
| `/dashboard/stats` | GET | Admin | Stats overview |
| `/dashboard/activity` | GET | Admin | Activity timeline |
| `/settings` | GET, POST | Admin | Global settings |
| `/tools/{action}` | POST | Admin | Maintenance tools |
| `/moderation/queue` | GET | Admin | Pending comments |
| `/infractions` | GET, POST | Admin | User bans/warnings |
| `/reports` | GET | Admin | User reports |

---

## Translation (i18n) Rules

### Text Domain: `'presszone-comments'` - ALWAYS

```javascript
// JavaScript strings via __() helper from dom.js
import { __ } from '../utils/dom.js';

__('Settings saved!', 'presszone-comments')
__('Error occurred', 'presszone-comments')
```

### Translation Maintenance Rules

| Action | Requirement |
|--------|-------------|
| **Adding new UI string** | Add to all `.po` files in `languages/`, translate to all languages |
| **Removing UI string** | Remove from all `.po` files to avoid bloat |
| **Editing UI string** | Update in all `.po` files, retranslate appropriately |

### File Locations
- `languages/presszone-comments-{locale}.po` - PHP strings
- `languages/presszone-comments-{locale}-presszone-comments-admin-app.json` - Admin JS strings

---

## Common Mistakes to Avoid

| Mistake | Fix |
|---------|-----|
| Using short JS prefixes (<4 chars) | Use `CommentsPressZoneApp`, `presszoneCommentsAdmin` |
| Using CSS custom properties | Use SCSS variables ONLY - NO var(--*) or --* |
| Hardcoded colors in dark mode | Use SCSS variables with `.dark-mode &` nesting |
| Creating inline notifications | Use `Toast` component |
| Duplicating animation keyframes | Use shared from `_animations.css` |
| Hardcoded animation durations | Use SCSS variables |
| Missing reduced motion support | Add `@media (prefers-reduced-motion)` |
| Using `api.del()` | Use `API.delete()` |
| Forgetting to rebuild | Run `cd admin && npm run build` |
| Missing translation wrapper | Use `__('text', 'presszone-comments')` |
| Unsanitized input in PHP | Always sanitize with `sanitize_*()` |
| Raw SQL queries | Always use `$wpdb->prepare()` |
| Using `<table>` elements | NEVER - use `<div>` with CSS Grid/Flexbox |
| Class chaining `.class1.class2` | Use single BEM class |
| Magic numbers | Use SCSS variables |
| Inline styles for dynamic values | Use explicit CSS classes |
| `float` or `clearfix` | Use Flexbox or Grid |
| `!important` (except WP overrides) | Increase specificity properly |
| Deep CSS nesting | Flatten with BEM naming |