# Multilingual Press Zone - Admin Panel Architecture

> **CRITICAL**: This document defines the WordPress admin interface (customer-facing admin panel in WordPress dashboard), NOT the Press.zone backend panel.

---

## Architecture Overview

### Two Separate Admin Systems

**IMPORTANT DISTINCTION:**

1. **WordPress Admin Panel** (This Document)
   - **Location**: WordPress Dashboard → Multilingual Press Zone menu
   - **Technology**: Vanilla JS + Webpack 5 + SCSS
   - **Purpose**: Customer-facing plugin settings and translation management
   - **Users**: WordPress site administrators and translators
   - **Design**: Copy comments-press-zone admin panel EXACTLY

2. **Press.zone Backend Panel** (Separate System)
   - **Location**: Press.zone React admin at `backend-app/admin-panel/`
   - **Technology**: React 18 + TypeScript + TailwindCSS
   - **Purpose**: Press.zone infrastructure management (licensing, support, analytics)
   - **Users**: Press.zone team members only
   - **Integration**: WordPress plugin communicates with api.press.zone

---

## Technology Stack

### Core Technologies

| Technology | Version | Purpose |
|-----------|---------|---------|
| **Vanilla JavaScript** | ES6+ | Component logic, application state |
| **Webpack** | 5.89.0+ | Module bundling, code splitting |
| **Babel** | 7.23.0+ | ES6+ transpilation |
| **SCSS** | 1.69.0+ | Styling with variables and mixins |
| **WordPress i18n** | Built-in | Translation ready |

### Build Dependencies

```json
{
  "devDependencies": {
    "@babel/core": "^7.23.0",
    "@babel/preset-env": "^7.23.0",
    "babel-loader": "^9.1.3",
    "css-loader": "^6.8.1",
    "mini-css-extract-plugin": "^2.7.6",
    "sass": "^1.69.0",
    "sass-loader": "^13.3.0",
    "webpack": "^5.89.0",
    "webpack-cli": "^5.1.4"
  }
}
```

### Browser Support

- Target: `> 1%, not dead` (Babel preset-env)
- WordPress 6.0+ compatibility
- Modern browsers (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+)

---

## Project Structure

```
multilingual-press-zone/
├── admin/
│   ├── src-vanilla/                    # Source files (ES6+)
│   │   ├── admin.js                    # Entry point, router, shell
│   │   ├── components/                 # Reusable UI components
│   │   │   ├── Button.js               # (Copy from comments-press-zone)
│   │   │   ├── Card.js                 # (Copy from comments-press-zone)
│   │   │   ├── Modal.js                # (Copy from comments-press-zone)
│   │   │   ├── Tabs.js                 # (Copy from comments-press-zone)
│   │   │   ├── Toggle.js               # (Copy from comments-press-zone)
│   │   │   ├── FormField.js            # (Copy from comments-press-zone)
│   │   │   ├── Select.js               # (Copy from comments-press-zone)
│   │   │   ├── Textarea.js             # (Copy from comments-press-zone)
│   │   │   ├── ColorField.js           # (Copy from comments-press-zone)
│   │   │   ├── StatCard.js             # (Copy from comments-press-zone)
│   │   │   ├── Table.js                # (Copy from comments-press-zone)
│   │   │   ├── GridTable.js            # (Copy from comments-press-zone)
│   │   │   ├── EmptyState.js           # (Copy from comments-press-zone)
│   │   │   ├── ErrorState.js           # (Copy from comments-press-zone)
│   │   │   ├── Spinner.js              # (Copy from comments-press-zone)
│   │   │   ├── Skeleton.js             # (Copy from comments-press-zone)
│   │   │   ├── Toast.js                # (Copy from comments-press-zone)
│   │   │   ├── UserAutocomplete.js     # (Copy from comments-press-zone)
│   │   │   ├── TagInput.js             # (Copy from comments-press-zone)
│   │   │   ├── TileSelect.js           # (Copy from comments-press-zone)
│   │   │   ├── ColorPickerModal.js     # (Copy from comments-press-zone)
│   │   │   ├── StatusFeedback.js       # (Copy from comments-press-zone)
│   │   │   ├── InfractionModal.js      # (Copy from comments-press-zone)
│   │   │   ├── Placeholders.js         # (Copy from comments-press-zone)
│   │   │   ├── AnimatedItem.js         # (Copy from comments-press-zone)
│   │   │   ├── SortableList.js         # NEW: Language reordering
│   │   │   ├── ProgressBar.js          # NEW: Translation progress
│   │   │   ├── Badge.js                # NEW: Status indicators
│   │   │   ├── LinkSelector.js         # NEW: URL translation mapping
│   │   │   └── FlagPicker.js           # NEW: Language flag selection
│   │   ├── pages/                      # Page-level components
│   │   │   ├── dashboard.js            # Dashboard overview
│   │   │   ├── languages.js            # Language management
│   │   │   ├── translations.js         # Translation editor
│   │   │   ├── settings.js             # Plugin settings
│   │   │   └── licensing.js            # License management (api.press.zone)
│   │   ├── utils/                      # Utility functions
│   │   │   ├── dom.js                  # DOM helpers (el, mount, clear, __)
│   │   │   ├── api.js                  # WordPress REST API client
│   │   │   └── storage.js              # LocalStorage helpers
│   │   ├── css/                        # SCSS entry point
│   │   │   └── main.scss               # Imports all styles
│   │   └── styles/                     # SCSS modules
│   │       ├── _variables.scss         # Colors, spacing, breakpoints
│   │       ├── _mixins.scss            # Reusable SCSS mixins
│   │       ├── _reset.scss             # CSS reset
│   │       ├── _layout.scss            # Grid, sidebar, container
│   │       ├── _components.scss        # Component styles
│   │       └── _pages.scss             # Page-specific styles
│   ├── build/                          # Webpack output (gitignored)
│   │   ├── admin.js                    # Bundled JS
│   │   ├── admin.css                   # Bundled CSS
│   │   └── admin.js.map                # Source maps (dev only)
│   ├── webpack.config.js               # Webpack configuration
│   ├── package.json                    # npm dependencies
│   └── package-lock.json
├── includes/
│   └── Admin/
│       └── Dashboard.php               # PHP controller (enqueues assets)
```

---

## Component Strategy: "Build First, Assemble Like Lego"

### Phase 1: Copy Existing Components (25 components)

**Source**: `/wordpress/wp-content/plugins/comments-press-zone/admin/src-vanilla/components/`

**Critical**: Copy EXACTLY as-is, preserving:
- Class naming conventions (e.g., `presszone-comments-btn` → `presszone-mpz-btn`)
- Function signatures
- CSS variable usage
- Event handling patterns
- i18n text domain (update to `multilingual-press-zone`)

**Components to Copy**:
1. Button.js
2. Card.js
3. Modal.js
4. Tabs.js
5. Toggle.js
6. FormField.js
7. Select.js
8. Textarea.js
9. ColorField.js
10. StatCard.js
11. Table.js
12. GridTable.js
13. EmptyState.js
14. ErrorState.js
15. Spinner.js
16. Skeleton.js
17. Toast.js
18. UserAutocomplete.js
19. TagInput.js
20. TileSelect.js
21. ColorPickerModal.js
22. StatusFeedback.js
23. InfractionModal.js
24. Placeholders.js
25. AnimatedItem.js

### Phase 2: Build New Components (5 components)

**Required for MPZ functionality**:

#### 1. SortableList.js
- **Purpose**: Drag-and-drop language priority reordering
- **Features**: Touch support, visual feedback, auto-save
- **API**: `SortableList({ items, onReorder, itemRenderer })`

#### 2. ProgressBar.js
- **Purpose**: Translation completion percentage
- **Features**: Animated fill, color thresholds (0-30% red, 30-70% yellow, 70-100% green)
- **API**: `ProgressBar({ value, max, label, showPercentage })`

#### 3. Badge.js
- **Purpose**: Language status indicators (Active, Draft, Disabled)
- **Features**: Color variants, icon support, size variants
- **API**: `Badge({ label, variant, size, icon })`

#### 4. LinkSelector.js
- **Purpose**: URL structure selection for translations
- **Features**: Preview generation, slug editing, permalink structure awareness
- **API**: `LinkSelector({ postId, language, currentUrl, onUrlChange })`

#### 5. FlagPicker.js
- **Purpose**: Visual language flag selection
- **Features**: Search/filter, country code mapping, custom flag upload
- **API**: `FlagPicker({ selectedCode, onSelect, allowCustom })`

### Phase 3: Assemble Pages (5 pages)

**Use components like Lego blocks**. Pages are compositions of components with business logic.

Example:
```javascript
// pages/languages.js
import { Card } from '../components/Card.js';
import { Button } from '../components/Button.js';
import { SortableList } from '../components/SortableList.js';
import { Badge } from '../components/Badge.js';

export async function renderLanguages(container) {
    const languages = await fetchLanguages();
    
    const languageList = SortableList({
        items: languages,
        onReorder: saveLanguageOrder,
        itemRenderer: (lang) => Card({
            title: lang.name,
            badge: Badge({ label: lang.status, variant: lang.active ? 'success' : 'secondary' }),
            actions: [
                Button({ label: 'Edit', variant: 'ghost', onClick: () => editLanguage(lang.id) }),
                Button({ label: 'Delete', variant: 'danger', onClick: () => deleteLanguage(lang.id) })
            ]
        })
    });
    
    mount(container, languageList);
}
```

---

## Webpack Configuration

### webpack.config.js

```javascript
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
    mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',
    entry: {
        admin: './src-vanilla/admin.js',
    },
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: '[name].js',
        clean: true,
    },
    devtool: process.env.NODE_ENV === 'production' ? false : 'source-map',
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            ['@babel/preset-env', {
                                targets: '> 1%, not dead',
                                modules: false,
                            }],
                        ],
                    },
                },
            },
            {
                test: /\.scss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'sass-loader',
                ],
            },
        ],
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css',
        }),
    ],
    resolve: {
        extensions: ['.js'],
    },
    optimization: {
        minimize: process.env.NODE_ENV === 'production',
    },
    externals: {
        // WordPress globals that are already loaded
        '@wordpress/i18n': 'wp.i18n',
    },
};
```

### Build Commands

```json
{
  "scripts": {
    "build": "webpack --mode production",
    "start": "webpack --mode development --watch",
    "lint:js": "eslint src-vanilla/",
    "format": "prettier --write src-vanilla/"
  }
}
```

---

## Application Architecture

### Entry Point: admin.js

**Responsibilities**:
1. Import main SCSS (`./css/main.scss`)
2. Initialize hash-based router
3. Render application shell (sidebar + main content area)
4. Handle page navigation
5. Mount global modals and toasts

**Pattern** (from comments-press-zone):
```javascript
import './css/main.scss';
import { el, mount, clear, __ } from './utils/dom.js';
import Toast from './components/Toast.js';
import Modal from './components/Modal.js';

// Global state
let currentState = {
    page: 'dashboard',
    subPath: '',
    loading: false
};

// Application shell
function App() {
    const container = el('div', { class: 'presszone-mpz-admin' });
    
    const navItems = [
        { id: 'dashboard', label: __('Dashboard', 'multilingual-press-zone'), icon: '📊' },
        { id: 'languages', label: __('Languages', 'multilingual-press-zone'), icon: '🌍' },
        { id: 'translations', label: __('Translations', 'multilingual-press-zone'), icon: '📝' },
        { id: 'settings', label: __('Settings', 'multilingual-press-zone'), icon: '⚙️' },
        { id: 'licensing', label: __('Licensing', 'multilingual-press-zone'), icon: '🔑' }
    ];
    
    const sidebar = Sidebar({
        items: navItems,
        activeId: currentState.page,
        onNavigate: (id) => { window.location.hash = `#/${id}`; }
    });
    
    const mainContent = el('main', {
        class: 'presszone-mpz-admin-main',
        id: 'presszone-mpz-content'
    });
    
    const modalContainer = el('div', { id: 'presszone-mpz-modal-container' });
    
    mount(container, sidebar);
    mount(container, mainContent);
    mount(container, modalContainer);
    
    return container;
}

// Hash router
async function router() {
    window.addNotice = (message, type = 'success') => Toast.show({ message, type });
    
    const root = document.getElementById('presszone-mpz-admin-root');
    if (!root) return;
    
    const hash = window.location.hash.substring(2) || 'dashboard';
    const [page, subPath] = hash.split('/');
    
    currentState.page = page;
    currentState.subPath = subPath || '';
    
    // Re-render shell if needed
    if (!document.getElementById('presszone-mpz-content')) {
        clear(root);
        mount(root, App());
    }
    
    const contentArea = document.getElementById('presszone-mpz-content');
    
    // Render page content with code splitting
    try {
        switch (page) {
            case 'dashboard': {
                const { renderDashboard } = await import('./pages/dashboard.js');
                await renderDashboard(contentArea);
                break;
            }
            case 'languages': {
                const { renderLanguages } = await import('./pages/languages.js');
                await renderLanguages(contentArea, window.addNotice);
                break;
            }
            // ... other routes
        }
    } catch (err) {
        console.error('MPZ: Page render error', err);
        contentArea.innerHTML = `<div class="presszone-mpz-error"><h2>Render Error</h2><p>${err.message}</p></div>`;
    }
}

// Initialize
window.addEventListener('hashchange', router);
if (document.readyState === 'complete' || document.readyState === 'interactive') {
    router();
} else {
    document.addEventListener('DOMContentLoaded', router);
}
```

---

## DOM Utilities

### utils/dom.js

**CRITICAL**: Copy from comments-press-zone EXACTLY. Provides:

```javascript
/**
 * Create DOM element with attributes and children
 * @param {string} tag - Element tag name
 * @param {object} attrs - Attributes (className → class, onclick handlers)
 * @param {...(Node|string)} children - Child elements or text
 */
export function el(tag, attrs = {}, ...children) {
    const element = document.createElement(tag);
    
    Object.entries(attrs).forEach(([key, value]) => {
        if (value === undefined || value === null) return;
        
        if (key === 'class' || key === 'className') {
            element.className = value;
        } else if (key.startsWith('on') && typeof value === 'function') {
            element.addEventListener(key.substring(2).toLowerCase(), value);
        } else {
            element.setAttribute(key, value);
        }
    });
    
    children.flat().forEach(child => {
        if (typeof child === 'string') {
            element.appendChild(document.createTextNode(child));
        } else if (child instanceof Node) {
            element.appendChild(child);
        }
    });
    
    return element;
}

/**
 * Mount element(s) to container
 */
export function mount(container, ...elements) {
    elements.flat().forEach(el => {
        if (el instanceof Node) container.appendChild(el);
    });
}

/**
 * Clear container
 */
export function clear(container) {
    while (container.firstChild) {
        container.removeChild(container.firstChild);
    }
}

/**
 * WordPress i18n wrapper
 */
export function __(text, domain = 'multilingual-press-zone') {
    return wp.i18n.__(text, domain);
}
```

---

## WordPress REST API Integration

### utils/api.js

**Pattern**: Wrapper around `wp.apiFetch` with nonce handling.

```javascript
/**
 * Fetch from WordPress REST API
 * Automatically includes nonce, handles errors
 */
export async function api(endpoint, options = {}) {
    const { method = 'GET', data, params } = options;
    
    // Build URL with query params
    let url = `${window.presszoneMultilingualAdmin.restUrl}${endpoint}`;
    if (params) {
        const query = new URLSearchParams(params).toString();
        url += `?${query}`;
    }
    
    const fetchOptions = {
        path: url,
        method,
        headers: {
            'X-WP-Nonce': window.presszoneMultilingualAdmin.nonce
        }
    };
    
    if (data && (method === 'POST' || method === 'PUT' || method === 'PATCH')) {
        fetchOptions.data = data;
    }
    
    try {
        const response = await wp.apiFetch(fetchOptions);
        return { success: true, data: response };
    } catch (error) {
        console.error('API Error:', error);
        return { success: false, error: error.message || 'Unknown error' };
    }
}

// Convenience methods
export const get = (endpoint, params) => api(endpoint, { method: 'GET', params });
export const post = (endpoint, data) => api(endpoint, { method: 'POST', data });
export const put = (endpoint, data) => api(endpoint, { method: 'PUT', data });
export const del = (endpoint) => api(endpoint, { method: 'DELETE' });
```

---

## Styling Architecture

### Design System: Copy from comments-press-zone

**CRITICAL**: Use EXACT same design tokens for consistency across Press.zone plugins.

#### CSS Variables (from comments-press-zone)

```scss
// _variables.scss
:root {
    // Colors
    --presszone-primary: #2563eb;
    --presszone-primary-hover: #1d4ed8;
    --presszone-secondary: #64748b;
    --presszone-danger: #dc2626;
    --presszone-success: #16a34a;
    --presszone-warning: #f59e0b;
    
    --presszone-bg: #ffffff;
    --presszone-bg-secondary: #f8fafc;
    --presszone-border: #e2e8f0;
    --presszone-text: #1e293b;
    --presszone-text-muted: #64748b;
    
    // Dark mode
    body.dark-mode & {
        --presszone-bg: #0f172a;
        --presszone-bg-secondary: #1e293b;
        --presszone-border: #334155;
        --presszone-text: #f1f5f9;
        --presszone-text-muted: #94a3b8;
    }
    
    // Spacing
    --presszone-spacing-xs: 0.25rem;
    --presszone-spacing-sm: 0.5rem;
    --presszone-spacing-md: 1rem;
    --presszone-spacing-lg: 1.5rem;
    --presszone-spacing-xl: 2rem;
    
    // Typography
    --presszone-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    --presszone-font-size-sm: 0.875rem;
    --presszone-font-size-base: 1rem;
    --presszone-font-size-lg: 1.125rem;
    --presszone-font-size-xl: 1.25rem;
    
    // Border radius
    --presszone-radius-sm: 0.25rem;
    --presszone-radius-md: 0.5rem;
    --presszone-radius-lg: 0.75rem;
    
    // Transitions
    --presszone-transition: all 0.2s ease;
}
```

#### Component Naming Convention

**Pattern**: BEM with Press.zone prefix

```scss
.presszone-mpz-{component} {
    // Base styles
    
    &__element {
        // Element styles
    }
    
    &--modifier {
        // Modifier styles
    }
    
    &:hover, &:focus {
        // Interactive states
    }
}
```

**Examples**:
- `.presszone-mpz-btn` (Button base)
- `.presszone-mpz-btn--primary` (Primary variant)
- `.presszone-mpz-btn--lg` (Large size)
- `.presszone-mpz-card` (Card base)
- `.presszone-mpz-card__header` (Card header element)

### Dark Mode Integration

**Theme's dark mode is primary controller**:
- Class: `body.dark-mode`
- Storage: `localStorage('presszone-dark-mode')`
- Event: `presszone:dark-change`

```javascript
// Listen for theme dark mode changes
document.addEventListener('presszone:dark-change', (e) => {
    // React to dark mode change if needed
    console.log('Dark mode:', e.detail.enabled);
});
```

---

## PHP Integration

### includes/Admin/Dashboard.php

**Responsibilities**:
1. Enqueue admin assets (JS, CSS)
2. Pass PHP data to JavaScript (nonces, REST endpoints, plugin version)
3. Render admin root element
4. Handle WordPress admin menu

```php
<?php
declare(strict_types=1);

namespace MultilingualPressZone\Admin;

if (!defined('ABSPATH')) {
    exit;
}

class Dashboard {
    private string $plugin_version;
    private string $plugin_slug;
    
    public function __construct(string $version, string $slug) {
        $this->plugin_version = $version;
        $this->plugin_slug = $slug;
        
        add_action('admin_menu', [$this, 'addAdminMenu']);
        add_action('admin_enqueue_scripts', [$this, 'enqueueAssets']);
    }
    
    /**
     * Add WordPress admin menu
     */
    public function addAdminMenu(): void {
        add_menu_page(
            __('Multilingual Press Zone', 'multilingual-press-zone'),
            __('Multilingual', 'multilingual-press-zone'),
            'manage_options',
            $this->plugin_slug,
            [$this, 'renderDashboard'],
            'dashicons-translation',
            30
        );
    }
    
    /**
     * Enqueue admin assets
     */
    public function enqueueAssets(string $hook_suffix): void {
        // Only load on our admin page
        if (strpos($hook_suffix, $this->plugin_slug) === false) {
            return;
        }
        
        $assets_url = plugin_dir_url(dirname(__DIR__)) . 'admin/build/';
        
        // Enqueue JS
        wp_enqueue_script(
            'presszone-multilingual-admin',
            $assets_url . 'admin.js',
            ['wp-api-fetch', 'wp-i18n'],
            $this->plugin_version,
            true
        );
        
        // Enqueue CSS
        wp_enqueue_style(
            'presszone-multilingual-admin',
            $assets_url . 'admin.css',
            [],
            $this->plugin_version
        );
        
        // Pass data to JavaScript
        wp_localize_script(
            'presszone-multilingual-admin',
            'presszoneMultilingualAdmin',
            [
                'restUrl' => rest_url('multilingual-press-zone/v1'),
                'nonce' => wp_create_nonce('wp_rest'),
                'version' => $this->plugin_version,
                'assetsUrl' => $assets_url,
                'siteUrl' => get_site_url(),
                'adminUrl' => admin_url(),
                'currentUser' => wp_get_current_user()->ID,
                'capabilities' => [
                    'manage_languages' => current_user_can('manage_options'),
                    'translate_content' => current_user_can('edit_posts'),
                ],
            ]
        );
        
        // Set translations for wp.i18n
        wp_set_script_translations(
            'presszone-multilingual-admin',
            'multilingual-press-zone',
            plugin_dir_path(dirname(__DIR__)) . 'languages'
        );
    }
    
    /**
     * Render admin dashboard root element
     */
    public function renderDashboard(): void {
        ?>
        <div class="wrap">
            <div id="presszone-mpz-admin-root"></div>
        </div>
        <?php
    }
}
```

---

## Licensing Integration with api.press.zone

### License Management Page (pages/licensing.js)

**Integration with Press.zone backend**:

```javascript
import { el, mount, clear, __ } from '../utils/dom.js';
import { Card } from '../components/Card.js';
import { Button } from '../components/Button.js';
import { FormField } from '../components/FormField.js';
import { Badge } from '../components/Badge.js';
import { Spinner } from '../components/Spinner.js';

/**
 * Fetch license status from api.press.zone
 */
async function fetchLicenseStatus() {
    const apiKey = window.presszoneMultilingualAdmin.licenseKey;
    
    try {
        const response = await fetch('https://api.press.zone/v1/multilingual/license/status', {
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${apiKey}`,
                'Content-Type': 'application/json'
            }
        });
        
        if (!response.ok) throw new Error('Failed to fetch license');
        
        const data = await response.json();
        return { success: true, data };
    } catch (error) {
        return { success: false, error: error.message };
    }
}

/**
 * Activate license via api.press.zone
 */
async function activateLicense(licenseKey, siteUrl) {
    try {
        const response = await fetch('https://api.press.zone/v1/multilingual/license/activate', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                license_key: licenseKey,
                site_url: siteUrl
            })
        });
        
        if (!response.ok) {
            const error = await response.json();
            throw new Error(error.message || 'Activation failed');
        }
        
        const data = await response.json();
        
        // Save license key to WordPress options
        await wp.apiFetch({
            path: '/multilingual-press-zone/v1/settings',
            method: 'POST',
            data: {
                license_key: licenseKey,
                license_status: data.status
            }
        });
        
        return { success: true, data };
    } catch (error) {
        return { success: false, error: error.message };
    }
}

/**
 * Render licensing page
 */
export async function renderLicensing(container, addNotice) {
    clear(container);
    
    // Show loading spinner
    mount(container, Spinner());
    
    // Fetch current license status
    const { success, data, error } = await fetchLicenseStatus();
    
    clear(container);
    
    if (!success || !data) {
        // Show activation form
        renderActivationForm(container, addNotice);
        return;
    }
    
    // Show license details
    renderLicenseDetails(container, data, addNotice);
}

function renderActivationForm(container, addNotice) {
    let licenseKey = '';
    
    const form = Card({
        title: __('Activate License', 'multilingual-press-zone'),
        children: [
            el('p', { class: 'presszone-mpz-description' },
                __('Enter your license key to activate Multilingual Press Zone and receive updates.', 'multilingual-press-zone')
            ),
            FormField({
                label: __('License Key', 'multilingual-press-zone'),
                type: 'text',
                placeholder: 'MPZ-XXXX-XXXX-XXXX-XXXX',
                value: licenseKey,
                onChange: (value) => { licenseKey = value; }
            }),
            Button({
                label: __('Activate License', 'multilingual-press-zone'),
                variant: 'primary',
                onClick: async () => {
                    if (!licenseKey.trim()) {
                        addNotice(__('Please enter a license key', 'multilingual-press-zone'), 'error');
                        return;
                    }
                    
                    const siteUrl = window.presszoneMultilingualAdmin.siteUrl;
                    const { success, data, error } = await activateLicense(licenseKey, siteUrl);
                    
                    if (success) {
                        addNotice(__('License activated successfully!', 'multilingual-press-zone'), 'success');
                        renderLicensing(container, addNotice); // Refresh page
                    } else {
                        addNotice(error || __('Activation failed', 'multilingual-press-zone'), 'error');
                    }
                }
            }),
            el('p', { class: 'presszone-mpz-help' },
                __('Don\'t have a license? ', 'multilingual-press-zone'),
                el('a', { href: 'https://press.zone/multilingual', target: '_blank' },
                    __('Purchase one now', 'multilingual-press-zone')
                )
            )
        ]
    });
    
    mount(container, form);
}

function renderLicenseDetails(container, licenseData, addNotice) {
    const tierBadge = Badge({
        label: licenseData.tier.toUpperCase(),
        variant: licenseData.tier === 'enterprise' ? 'success' : 'primary'
    });
    
    const statusBadge = Badge({
        label: licenseData.status === 'active' ? __('Active', 'multilingual-press-zone') : __('Inactive', 'multilingual-press-zone'),
        variant: licenseData.status === 'active' ? 'success' : 'danger'
    });
    
    const card = Card({
        title: __('License Information', 'multilingual-press-zone'),
        children: [
            el('div', { class: 'presszone-mpz-license-info' },
                el('div', { class: 'presszone-mpz-license-row' },
                    el('span', { class: 'presszone-mpz-license-label' }, __('Status:', 'multilingual-press-zone')),
                    statusBadge
                ),
                el('div', { class: 'presszone-mpz-license-row' },
                    el('span', { class: 'presszone-mpz-license-label' }, __('Tier:', 'multilingual-press-zone')),
                    tierBadge
                ),
                el('div', { class: 'presszone-mpz-license-row' },
                    el('span', { class: 'presszone-mpz-license-label' }, __('Sites:', 'multilingual-press-zone')),
                    el('span', {}, `${licenseData.sites_used} / ${licenseData.sites_allowed}`)
                ),
                el('div', { class: 'presszone-mpz-license-row' },
                    el('span', { class: 'presszone-mpz-license-label' }, __('Updates Valid Until:', 'multilingual-press-zone')),
                    el('span', {}, new Date(licenseData.updates_until).toLocaleDateString())
                )
            ),
            el('div', { class: 'presszone-mpz-license-actions' },
                Button({
                    label: __('Manage Subscription', 'multilingual-press-zone'),
                    variant: 'primary',
                    onClick: () => {
                        // Open Press.zone dashboard in new tab
                        window.open('https://press.zone/account/multilingual', '_blank');
                    }
                }),
                Button({
                    label: __('Deactivate License', 'multilingual-press-zone'),
                    variant: 'ghost',
                    onClick: async () => {
                        if (!confirm(__('Are you sure you want to deactivate this license?', 'multilingual-press-zone'))) {
                            return;
                        }
                        
                        // TODO: Implement deactivation
                        addNotice(__('License deactivated', 'multilingual-press-zone'), 'success');
                    }
                })
            )
        ]
    });
    
    mount(container, card);
}
```

---

## Build Process

### Development Workflow

```bash
# Install dependencies
cd admin/
npm install

# Start development mode (watch mode)
npm run start

# Build for production
npm run build

# Lint JavaScript
npm run lint:js

# Format code
npm run format
```

### Production Build

```bash
# From plugin root
cd admin/
npm run build

# Output:
# admin/build/admin.js (minified, no source maps)
# admin/build/admin.css (minified)
```

### File Sizes (Target)

| File | Development | Production |
|------|-------------|-----------|
| admin.js | ~500 KB | ~150 KB |
| admin.css | ~80 KB | ~30 KB |

---

## Security Considerations

### Nonce Validation

All REST API requests include WordPress nonce in `X-WP-Nonce` header.

### XSS Prevention

- Use `el()` function for DOM creation (auto-escapes text nodes)
- Never use `innerHTML` with user data
- Use `textContent` for user-provided text

### CSRF Protection

WordPress REST API nonces provide CSRF protection automatically.

### License Key Storage

- Never expose license keys in JavaScript
- Store in WordPress options table (encrypted)
- Pass API bearer token only, not full license key

---

## Testing Strategy

### Manual Testing Checklist

- [ ] Component rendering in light/dark modes
- [ ] Responsive layout (desktop, tablet, mobile)
- [ ] Hash navigation (back/forward browser buttons)
- [ ] Form validation and error handling
- [ ] REST API integration (success/error states)
- [ ] License activation/deactivation flow
- [ ] Translation string coverage (i18n)
- [ ] Browser compatibility (Chrome, Firefox, Safari, Edge)

### Automated Testing (Future)

- Jest for component unit tests
- Playwright for E2E tests
- Accessibility audits with axe-core

---

## Migration from React (If Needed)

**NOT APPLICABLE**: MPZ is built from scratch with Vanilla JS. No React migration needed.

**Note**: Press.zone backend panel remains React-based. Only WordPress admin uses Vanilla JS.

---

## Performance Optimization

### Code Splitting

Webpack dynamic imports for pages:
```javascript
const { renderDashboard } = await import('./pages/dashboard.js');
```

### Bundle Size Optimization

- Tree-shaking via ES6 modules
- Minification in production
- No external dependencies (use WordPress globals)

### Caching Strategy

- Webpack cache-busting via `[contenthash]` (future enhancement)
- Browser caching headers via WordPress

---

## Accessibility (a11y)

### Requirements

- Keyboard navigation for all interactive elements
- `aria-label` on icon-only buttons
- `aria-expanded` on dropdowns/accordions
- Focus visible outlines (`:focus-visible`)
- Semantic HTML (`<nav>`, `<main>`, `<button>`)
- Screen reader announcements for dynamic content

### Testing Tools

- WAVE browser extension
- axe DevTools
- Keyboard-only navigation testing

---

## Internationalization (i18n)

### Text Domain

`multilingual-press-zone` (use consistently across all files)

### Translation Functions

```javascript
import { __ } from '../utils/dom.js';

const label = __('Dashboard', 'multilingual-press-zone');
```

### PHP Translation Loading

```php
wp_set_script_translations(
    'presszone-multilingual-admin',
    'multilingual-press-zone',
    plugin_dir_path(__DIR__) . 'languages'
);
```

---

## Summary: Key Takeaways

1. **Vanilla JS + Webpack 5 + SCSS** (NOT React)
2. **Copy 25 components from comments-press-zone EXACTLY**
3. **Build 5 new MPZ-specific components**
4. **Assemble pages like Lego blocks**
5. **WordPress admin panel** (customer-facing), NOT Press.zone backend panel
6. **License management via api.press.zone API**
7. **Hash-based routing with code splitting**
8. **Exact same design system as comments-press-zone** (colors, fonts, animations)
9. **Dark mode integration** via theme's `body.dark-mode` class
10. **i18n ready** with WordPress translation functions

---

## Related Documents

- `UI-UX-SPECIFICATIONS.md` - Detailed designs for all 5 admin screens
- `LICENSING-IMPLEMENTATION-PLAN.md` - License server architecture
- `API-DOCUMENTATION.md` - REST endpoint reference
- `PHASE1-CORE-FOUNDATION.md` - Week 3 admin panel implementation tasks
- `comments-press-zone/admin/` - Reference implementation
