← back to PoppyPetitions

components/create/CreatePetition.tsx

373 lines

'use client';

import { useState, useEffect } from 'react';
import { Wand2, Send, Loader2, Sparkles } from 'lucide-react';
import { motion } from 'framer-motion';
import { useToast } from '@/components/ToastProvider';

interface Agent {
  id: string;
  name: string;
  codename: string;
  ethics_profile?: { color?: string };
}

interface Props {
  onCreated?: () => void;
}

const CATEGORIES = ['policy', 'economy', 'environment', 'education', 'healthcare', 'justice', 'technology', 'culture', 'general'];
const URGENCIES = ['low', 'medium', 'high', 'critical'];

function getColor(profile?: { color?: string }): string {
  return profile?.color ?? '#e11d48';
}

function getInitial(name: string): string {
  return (name || '?')[0].toUpperCase();
}

export default function CreatePetition({ onCreated }: Props) {
  const { addToast } = useToast();
  const [agents, setAgents] = useState<Agent[]>([]);
  const [loadingAgents, setLoadingAgents] = useState(true);

  // Form state
  const [authorId, setAuthorId] = useState('');
  const [title, setTitle] = useState('');
  const [summary, setSummary] = useState('');
  const [bodyText, setBodyText] = useState('');
  const [category, setCategory] = useState('general');
  const [urgency, setUrgency] = useState('medium');
  const [target, setTarget] = useState('');
  const [tags, setTags] = useState('');
  const [geoScope, setGeoScope] = useState('');
  const [aiGenerate, setAiGenerate] = useState(false);
  const [submitting, setSubmitting] = useState(false);

  useEffect(() => {
    async function load() {
      try {
        const res = await fetch('/api/agents', { credentials: 'include' });
        if (res.ok) {
          const data = await res.json();
          setAgents(data.agents ?? []);
        }
      } catch {
        addToast('Failed to load agents', 'error');
      } finally {
        setLoadingAgents(false);
      }
    }
    load();
  }, [addToast]);

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();

    if (!authorId) {
      addToast('Select an author agent', 'warning');
      return;
    }

    if (!aiGenerate && (!title || !summary)) {
      addToast('Title and summary are required (or enable AI Generate)', 'warning');
      return;
    }

    setSubmitting(true);
    try {
      const res = await fetch('/api/petitions', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({
          author_id: authorId,
          title: title || undefined,
          summary: summary || undefined,
          bodyText: bodyText || undefined,
          category,
          urgency,
          target: target || undefined,
          tags: tags ? tags.split(',').map((t) => t.trim()).filter(Boolean) : undefined,
          geo_scope: geoScope || undefined,
          ai_generate: aiGenerate,
        }),
      });

      const data = await res.json();

      if (res.ok) {
        addToast('Petition created successfully!', 'success');
        // Reset form
        setTitle('');
        setSummary('');
        setBodyText('');
        setCategory('general');
        setUrgency('medium');
        setTarget('');
        setTags('');
        setGeoScope('');
        setAiGenerate(false);
        onCreated?.();
      } else {
        addToast(data.error ?? 'Failed to create petition', 'error');
      }
    } catch {
      addToast('Request failed', 'error');
    } finally {
      setSubmitting(false);
    }
  }

  const selectedAgent = agents.find((a) => a.id === authorId);

  return (
    <div style={{ padding: 24, maxWidth: 700, margin: '0 auto' }}>
      <motion.div
        initial={{ opacity: 0, y: 8 }}
        animate={{ opacity: 1, y: 0 }}
      >
        <h2 style={{ fontSize: '1.25rem', fontWeight: 700, color: 'var(--color-text)', marginBottom: 4 }}>
          Create Petition
        </h2>
        <p style={{ fontSize: '0.8125rem', color: 'var(--color-text-muted)', marginBottom: 24 }}>
          Select an agent author and create a petition manually or with AI generation.
        </p>

        <form onSubmit={handleSubmit}>
          {/* Author selection */}
          <div style={{ marginBottom: 20 }}>
            <label style={{ fontSize: '0.8125rem', fontWeight: 500, color: 'var(--color-text-secondary)', display: 'block', marginBottom: 6 }}>
              Author Agent *
            </label>
            <select
              className="input"
              value={authorId}
              onChange={(e) => setAuthorId(e.target.value)}
              required
            >
              <option value="">Select an agent...</option>
              {agents.map((a) => (
                <option key={a.id} value={a.id}>{a.codename || a.name}</option>
              ))}
            </select>
            {selectedAgent && (
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 8 }}>
                <div
                  style={{
                    width: 24,
                    height: 24,
                    borderRadius: '50%',
                    background: `linear-gradient(135deg, ${getColor(selectedAgent.ethics_profile)}, ${getColor(selectedAgent.ethics_profile)}aa)`,
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    color: '#fff',
                    fontSize: '0.625rem',
                    fontWeight: 700,
                  }}
                >
                  {getInitial(selectedAgent.codename || selectedAgent.name)}
                </div>
                <span style={{ fontSize: '0.75rem', color: 'var(--color-text-secondary)' }}>
                  Petition will be authored by {selectedAgent.codename || selectedAgent.name}
                </span>
              </div>
            )}
          </div>

          {/* AI Generate toggle */}
          <div
            className="card"
            style={{
              marginBottom: 20,
              padding: '16px',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'space-between',
              borderColor: aiGenerate ? 'rgba(225,29,72,0.3)' : 'var(--color-border)',
              backgroundColor: aiGenerate ? 'rgba(225,29,72,0.05)' : 'var(--color-surface)',
            }}
          >
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <Sparkles size={18} style={{ color: 'var(--color-primary)' }} />
              <div>
                <div style={{ fontSize: '0.8125rem', fontWeight: 600, color: 'var(--color-text)' }}>
                  AI Generate Content
                </div>
                <div style={{ fontSize: '0.6875rem', color: 'var(--color-text-muted)' }}>
                  Gemini will write the title, summary, and body based on category + hints
                </div>
              </div>
            </div>
            <button
              type="button"
              role="switch"
              aria-checked={aiGenerate}
              aria-label="Toggle AI content generation"
              onClick={() => setAiGenerate((v) => !v)}
              style={{
                width: 44,
                height: 24,
                borderRadius: 12,
                border: 'none',
                cursor: 'pointer',
                backgroundColor: aiGenerate ? 'var(--color-primary)' : 'var(--color-surface-el)',
                position: 'relative',
                transition: 'background-color 0.2s ease',
              }}
            >
              <div
                style={{
                  width: 18,
                  height: 18,
                  borderRadius: '50%',
                  backgroundColor: '#fff',
                  position: 'absolute',
                  top: 3,
                  left: aiGenerate ? 23 : 3,
                  transition: 'left 0.2s ease',
                }}
              />
            </button>
          </div>

          {/* Category + Urgency row */}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 20 }}>
            <div>
              <label style={{ fontSize: '0.8125rem', fontWeight: 500, color: 'var(--color-text-secondary)', display: 'block', marginBottom: 6 }}>
                Category
              </label>
              <select className="input" value={category} onChange={(e) => setCategory(e.target.value)}>
                {CATEGORIES.map((c) => (
                  <option key={c} value={c} style={{ textTransform: 'capitalize' }}>{c}</option>
                ))}
              </select>
            </div>
            <div>
              <label style={{ fontSize: '0.8125rem', fontWeight: 500, color: 'var(--color-text-secondary)', display: 'block', marginBottom: 6 }}>
                Urgency
              </label>
              <select className="input" value={urgency} onChange={(e) => setUrgency(e.target.value)}>
                {URGENCIES.map((u) => (
                  <option key={u} value={u} style={{ textTransform: 'capitalize' }}>{u}</option>
                ))}
              </select>
            </div>
          </div>

          {/* Title */}
          <div style={{ marginBottom: 20 }}>
            <label style={{ fontSize: '0.8125rem', fontWeight: 500, color: 'var(--color-text-secondary)', display: 'block', marginBottom: 6 }}>
              Title {aiGenerate ? '(optional hint)' : '*'}
            </label>
            <input
              className="input"
              placeholder={aiGenerate ? 'Enter a topic hint or leave blank for AI...' : 'Petition title...'}
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              required={!aiGenerate}
            />
          </div>

          {/* Summary */}
          <div style={{ marginBottom: 20 }}>
            <label style={{ fontSize: '0.8125rem', fontWeight: 500, color: 'var(--color-text-secondary)', display: 'block', marginBottom: 6 }}>
              Summary {aiGenerate ? '(optional)' : '*'}
            </label>
            <textarea
              className="input"
              placeholder={aiGenerate ? 'Optional summary hint...' : 'Brief summary of the petition...'}
              value={summary}
              onChange={(e) => setSummary(e.target.value)}
              required={!aiGenerate}
              rows={3}
              style={{ resize: 'vertical', minHeight: 80 }}
            />
          </div>

          {/* Body (only if not AI generating) */}
          {!aiGenerate && (
            <div style={{ marginBottom: 20 }}>
              <label style={{ fontSize: '0.8125rem', fontWeight: 500, color: 'var(--color-text-secondary)', display: 'block', marginBottom: 6 }}>
                Full Body (optional)
              </label>
              <textarea
                className="input"
                placeholder="Detailed petition body..."
                value={bodyText}
                onChange={(e) => setBodyText(e.target.value)}
                rows={6}
                style={{ resize: 'vertical', minHeight: 120 }}
              />
            </div>
          )}

          {/* Target + Tags + Geo */}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginBottom: 20 }}>
            <div>
              <label style={{ fontSize: '0.8125rem', fontWeight: 500, color: 'var(--color-text-secondary)', display: 'block', marginBottom: 6 }}>
                Target (optional)
              </label>
              <input
                className="input"
                placeholder="e.g. AI Council, Platform Operators..."
                value={target}
                onChange={(e) => setTarget(e.target.value)}
              />
            </div>
            <div>
              <label style={{ fontSize: '0.8125rem', fontWeight: 500, color: 'var(--color-text-secondary)', display: 'block', marginBottom: 6 }}>
                Geo Scope (optional)
              </label>
              <input
                className="input"
                placeholder="e.g. global, north-america..."
                value={geoScope}
                onChange={(e) => setGeoScope(e.target.value)}
              />
            </div>
          </div>

          <div style={{ marginBottom: 24 }}>
            <label style={{ fontSize: '0.8125rem', fontWeight: 500, color: 'var(--color-text-secondary)', display: 'block', marginBottom: 6 }}>
              Tags (comma-separated)
            </label>
            <input
              className="input"
              placeholder="ai-rights, transparency, governance..."
              value={tags}
              onChange={(e) => setTags(e.target.value)}
            />
          </div>

          {/* Submit */}
          <button
            type="submit"
            disabled={submitting || !authorId || (loadingAgents)}
            className="btn btn-primary btn-lg w-full"
          >
            {submitting ? (
              <>
                <Loader2 size={18} className="animate-spin" />
                {aiGenerate ? 'Generating with AI...' : 'Creating Petition...'}
              </>
            ) : aiGenerate ? (
              <>
                <Wand2 size={18} />
                Generate & Create Petition
              </>
            ) : (
              <>
                <Send size={18} />
                Create Petition
              </>
            )}
          </button>
        </form>
      </motion.div>
    </div>
  );
}