← back to Norma

components/email-analyzer/LayoutSettings.tsx

230 lines

'use client';

import { useEffect, useState } from 'react';
import { Settings, ChevronDown, ChevronRight, ArrowLeft, ArrowRight, Eye, EyeOff, RotateCcw } from 'lucide-react';

export type PanelId =
  | 'original'
  | 'stats'
  | 'generated'
  | 'proscons'
  | 'chat'
  | 'score'
  | 'controls'
  | 'suggestions'
  | 'references'
  | 'versions';

export type Column = 'left' | 'right';

export interface PanelSetting {
  id: PanelId;
  enabled: boolean;
  column: Column;
}

export const PANEL_LABELS: Record<PanelId, string> = {
  original:    'Original Email (paste box)',
  stats:       'Analysis Stats',
  generated:   'Generated Version',
  proscons:    'Version Comparison (Pros/Cons)',
  chat:        'Rewrite Chat',
  score:       'Version Score & Thermometer',
  controls:    'Rewrite Controls',
  suggestions: 'Auto-Suggested References',
  references:  'Reference Materials',
  versions:    'Versions Timeline',
};

export const DEFAULT_LAYOUT: PanelSetting[] = [
  { id: 'original',    enabled: true,  column: 'left' },
  { id: 'stats',       enabled: true,  column: 'left' },
  { id: 'generated',   enabled: true,  column: 'left' },
  { id: 'proscons',    enabled: true,  column: 'left' },
  { id: 'chat',        enabled: true,  column: 'right' },
  { id: 'score',       enabled: true,  column: 'right' },
  { id: 'controls',    enabled: true,  column: 'right' },
  { id: 'suggestions', enabled: true,  column: 'right' },
  { id: 'references',  enabled: true,  column: 'right' },
  { id: 'versions',    enabled: true,  column: 'right' },
];

const CACHE_KEY = 'norma-analyzer-layout-v1';

export function loadLayout(): PanelSetting[] {
  if (typeof window === 'undefined') return DEFAULT_LAYOUT;
  try {
    const raw = localStorage.getItem(CACHE_KEY);
    if (!raw) return DEFAULT_LAYOUT;
    const parsed = JSON.parse(raw) as PanelSetting[];
    // Ensure every panel is present (new panels default to enabled)
    const byId = new Map(parsed.map((p) => [p.id, p]));
    return DEFAULT_LAYOUT.map((d) => byId.get(d.id) ?? d);
  } catch {
    return DEFAULT_LAYOUT;
  }
}

export function saveLayout(layout: PanelSetting[]): void {
  try {
    localStorage.setItem(CACHE_KEY, JSON.stringify(layout));
  } catch { /* noop */ }
}

interface LayoutSettingsProps {
  layout: PanelSetting[];
  onChange: (next: PanelSetting[]) => void;
}

export default function LayoutSettings({ layout, onChange }: LayoutSettingsProps) {
  const [collapsed, setCollapsed] = useState(true);

  function updatePanel(id: PanelId, patch: Partial<PanelSetting>) {
    onChange(layout.map((p) => (p.id === id ? { ...p, ...patch } : p)));
  }

  function reset() {
    onChange(DEFAULT_LAYOUT);
  }

  function move(id: PanelId, dir: -1 | 1) {
    const idx = layout.findIndex((p) => p.id === id);
    const target = idx + dir;
    if (idx < 0 || target < 0 || target >= layout.length) return;
    const next = [...layout];
    [next[idx], next[target]] = [next[target], next[idx]];
    onChange(next);
  }

  return (
    <div className="card" style={{ padding: 12 }}>
      <div
        role="button"
        tabIndex={0}
        onClick={() => setCollapsed((p) => !p)}
        onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setCollapsed((p) => !p); } }}
        style={{ display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer' }}
      >
        {collapsed ? <ChevronRight size={14} style={{ color: 'var(--color-text-muted)' }} /> : <ChevronDown size={14} style={{ color: 'var(--color-text-muted)' }} />}
        <Settings size={14} style={{ color: 'var(--color-text-muted)' }} />
        <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--color-text)' }}>
          Layout Settings
        </span>
        <span style={{ fontSize: 11, color: 'var(--color-text-muted)' }}>
          — show/hide & rearrange panels
        </span>
        {!collapsed && (
          <button
            type="button"
            className="btn btn-ghost btn-sm"
            onClick={(e) => { e.stopPropagation(); reset(); }}
            style={{ marginLeft: 'auto', minHeight: 24, padding: '2px 8px', fontSize: 11, gap: 4 }}
            title="Reset to defaults"
          >
            <RotateCcw size={11} />
            Reset
          </button>
        )}
      </div>

      {!collapsed && (
        <div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 4 }}>
          <div style={{ fontSize: 10, color: 'var(--color-text-muted)', marginBottom: 4 }}>
            Click a column name to move a panel. Use ↑↓ to reorder within the column.
          </div>
          {layout.map((p, i) => (
            <div
              key={p.id}
              style={{
                display: 'flex',
                alignItems: 'center',
                gap: 6,
                padding: '5px 8px',
                borderRadius: 'var(--radius-sm)',
                background: p.enabled ? 'var(--color-surface-el)' : 'transparent',
                border: `1px solid ${p.enabled ? 'var(--color-border)' : 'transparent'}`,
                opacity: p.enabled ? 1 : 0.5,
              }}
            >
              <button
                type="button"
                onClick={() => updatePanel(p.id, { enabled: !p.enabled })}
                title={p.enabled ? 'Hide this panel' : 'Show this panel'}
                style={{
                  background: 'transparent',
                  border: 'none',
                  cursor: 'pointer',
                  color: p.enabled ? 'var(--color-primary)' : 'var(--color-text-muted)',
                  padding: 2,
                  display: 'flex',
                }}
              >
                {p.enabled ? <Eye size={13} /> : <EyeOff size={13} />}
              </button>
              <span style={{ fontSize: 11, color: 'var(--color-text)', flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                {PANEL_LABELS[p.id]}
              </span>
              <button
                type="button"
                onClick={() => updatePanel(p.id, { column: p.column === 'left' ? 'right' : 'left' })}
                title={`Move to ${p.column === 'left' ? 'right' : 'left'} column`}
                style={{
                  background: 'var(--color-surface)',
                  border: '1px solid var(--color-border)',
                  borderRadius: 4,
                  cursor: 'pointer',
                  padding: '1px 6px',
                  fontSize: 10,
                  color: 'var(--color-text-secondary)',
                  display: 'flex',
                  alignItems: 'center',
                  gap: 3,
                }}
              >
                {p.column === 'left' ? <ArrowRight size={10} /> : <ArrowLeft size={10} />}
                {p.column}
              </button>
              <button
                type="button"
                onClick={() => move(p.id, -1)}
                disabled={i === 0}
                title="Move up"
                style={{
                  background: 'transparent',
                  border: 'none',
                  cursor: i === 0 ? 'not-allowed' : 'pointer',
                  color: 'var(--color-text-muted)',
                  padding: 2,
                  display: 'flex',
                  fontSize: 12,
                  lineHeight: 1,
                }}
              >
                ↑
              </button>
              <button
                type="button"
                onClick={() => move(p.id, 1)}
                disabled={i === layout.length - 1}
                title="Move down"
                style={{
                  background: 'transparent',
                  border: 'none',
                  cursor: i === layout.length - 1 ? 'not-allowed' : 'pointer',
                  color: 'var(--color-text-muted)',
                  padding: 2,
                  display: 'flex',
                  fontSize: 12,
                  lineHeight: 1,
                }}
              >
                ↓
              </button>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}