← back to Norma

app/pulse/petitions/create/page.tsx

440 lines

'use client';

import React, { useRef, useState } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import {
  ArrowLeft, Send, Loader2, CheckCircle2, Target, Tag,
  FileText, Users, AlertCircle,
} from 'lucide-react';

/* ────────────────────────── Colors ────────────────────────── */

const C = {
  darkGreen: '#1B4332',
  gold: '#DAA520',
  cream: '#FAFAF5',
  text: '#1a1a1a',
  textLight: '#fafafa',
  textMuted: '#6b7280',
  border: '#d1d5db',
  borderLight: '#e5e7eb',
  success: '#059669',
  error: '#dc2626',
};

/* ────────────────────────── Categories ────────────────────────── */

const CATEGORIES = [
  { value: 'debt_cancellation', label: 'Debt Cancellation' },
  { value: 'consumer_protection', label: 'Consumer Protection' },
  { value: 'voting_rights', label: 'Voting Rights' },
  { value: 'legal', label: 'Legal' },
  { value: 'education', label: 'Education' },
  { value: 'environment', label: 'Environment' },
  { value: 'healthcare', label: 'Healthcare' },
  { value: 'other', label: 'Other' },
];

const GOAL_OPTIONS = [
  { value: 1000, label: '1,000' },
  { value: 5000, label: '5,000' },
  { value: 10000, label: '10,000' },
  { value: 25000, label: '25,000' },
  { value: 50000, label: '50,000' },
  { value: 100000, label: '100,000' },
];

/* ────────────────────────── Component ────────────────────────── */

export default function CreatePetitionPage() {
  const router = useRouter();

  const [title, setTitle] = useState('');
  const [body, setBody] = useState('');
  const [target, setTarget] = useState('');
  const [category, setCategory] = useState('other');
  const [tagsInput, setTagsInput] = useState('');
  const [signatureGoal, setSignatureGoal] = useState(10000);

  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [success, setSuccess] = useState<{ id: string; title: string } | null>(null);

  const inFlight = useRef(false);

  const canSubmit = title.trim().length >= 5 && !submitting;

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    if (inFlight.current || !canSubmit) return;
    inFlight.current = true;

    setSubmitting(true);
    setError(null);

    try {
      const res = await fetch('/api/pulse/petitions', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          title: title.trim(),
          body: body.trim() || undefined,
          target: target.trim() || undefined,
          category,
          tags: tagsInput.trim() || undefined,
          signature_goal: signatureGoal,
        }),
      });

      const data = await res.json();

      if (!res.ok) {
        setError(data.error || 'Something went wrong. Please try again.');
        return;
      }

      setSuccess({ id: data.petition.id, title: data.petition.title });
    } catch (err) {
      setError('Network error. Please check your connection and try again.');
    } finally {
      setSubmitting(false);
      inFlight.current = false;
    }
  }

  /* ── Success state ── */
  if (success) {
    return (
      <div style={{ maxWidth: 640, margin: '0 auto', padding: '48px 24px 80px' }}>
        <div style={{
          backgroundColor: '#fff', borderRadius: 20, padding: '48px 32px',
          textAlign: 'center', border: `2px solid ${C.success}`,
          boxShadow: '0 4px 20px rgba(5,150,105,0.1)',
        }}>
          <CheckCircle2 size={56} style={{ color: C.success, marginBottom: 20 }} />
          <h1 style={{ fontSize: 28, fontWeight: 'bold', color: C.text, margin: '0 0 12px', fontFamily: 'Georgia, serif' }}>
            Petition Created!
          </h1>
          <p style={{ fontSize: 16, color: C.textMuted, margin: '0 0 8px', fontFamily: 'system-ui, sans-serif', lineHeight: '24px' }}>
            Your petition <strong style={{ color: C.text }}>&ldquo;{success.title}&rdquo;</strong> is now live.
          </p>
          <p style={{ fontSize: 14, color: C.textMuted, margin: '0 0 32px', fontFamily: 'system-ui, sans-serif' }}>
            Share it to start collecting signatures.
          </p>
          <div style={{ display: 'flex', gap: 12, justifyContent: 'center', flexWrap: 'wrap' }}>
            <Link
              href={`/pulse/petition/${success.id}`}
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 8,
                backgroundColor: C.darkGreen, color: C.textLight, textDecoration: 'none',
                fontSize: 15, fontWeight: 600, fontFamily: 'system-ui, sans-serif',
                padding: '14px 28px', borderRadius: 9999,
              }}
            >
              View Petition
            </Link>
            <Link
              href="/pulse/petitions"
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 8,
                backgroundColor: 'transparent', color: C.darkGreen, textDecoration: 'none',
                fontSize: 15, fontWeight: 600, fontFamily: 'system-ui, sans-serif',
                padding: '14px 28px', borderRadius: 9999,
                border: `1px solid ${C.border}`,
              }}
            >
              Browse Petitions
            </Link>
          </div>
        </div>
      </div>
    );
  }

  /* ── Form ── */
  return (
    <div style={{ maxWidth: 720, margin: '0 auto', padding: '32px 24px 80px' }}>
      {/* Back link */}
      <Link
        href="/pulse/petitions"
        style={{
          display: 'inline-flex', alignItems: 'center', gap: 6,
          color: C.darkGreen, textDecoration: 'none', fontSize: 14, fontWeight: 500,
          fontFamily: 'system-ui, sans-serif', marginBottom: 24,
        }}
      >
        <ArrowLeft size={16} />
        Back to Petitions
      </Link>

      {/* Header */}
      <div style={{ marginBottom: 32 }}>
        <h1 style={{
          fontSize: 'clamp(28px, 4vw, 40px)', fontWeight: 'bold', color: C.text,
          margin: '0 0 8px', fontFamily: 'Georgia, serif',
        }}>
          Create a Petition
        </h1>
        <p style={{ fontSize: 16, color: C.textMuted, margin: 0, fontFamily: 'system-ui, sans-serif', lineHeight: '24px' }}>
          Start a petition to make your voice heard. Fill in the details below and we will publish it for others to sign.
        </p>
      </div>

      {/* Form card */}
      <form onSubmit={handleSubmit}>
        <div style={{
          backgroundColor: '#fff', borderRadius: 20, padding: '32px 28px',
          border: `1px solid ${C.borderLight}`,
          boxShadow: '0 1px 3px rgba(0,0,0,0.06)',
        }}>
          {/* Error banner */}
          {error && (
            <div style={{
              display: 'flex', alignItems: 'center', gap: 10,
              backgroundColor: 'rgba(220,38,38,0.06)', border: `1px solid rgba(220,38,38,0.2)`,
              borderRadius: 12, padding: '12px 16px', marginBottom: 24,
            }}>
              <AlertCircle size={18} style={{ color: C.error, flexShrink: 0 }} />
              <span style={{ fontSize: 14, color: C.error, fontFamily: 'system-ui, sans-serif' }}>{error}</span>
            </div>
          )}

          {/* Title */}
          <div style={{ marginBottom: 24 }}>
            <label
              htmlFor="petition-title"
              style={{
                display: 'flex', alignItems: 'center', gap: 6, fontSize: 14, fontWeight: 600,
                color: C.text, marginBottom: 8, fontFamily: 'system-ui, sans-serif',
              }}
            >
              <FileText size={15} style={{ color: C.darkGreen }} />
              Petition Title <span style={{ color: C.error }}>*</span>
            </label>
            <input
              id="petition-title"
              type="text"
              placeholder="e.g., Expand Access to Education for Public Service Workers"
              value={title}
              onChange={e => setTitle(e.target.value)}
              maxLength={300}
              required
              style={{
                width: '100%', padding: '14px 16px', borderRadius: 12,
                border: `1px solid ${C.border}`, backgroundColor: C.cream,
                fontSize: 15, fontFamily: 'system-ui, sans-serif', color: C.text,
                outline: 'none', boxSizing: 'border-box',
              }}
              onFocus={e => (e.target.style.borderColor = C.darkGreen)}
              onBlur={e => (e.target.style.borderColor = C.border)}
            />
            <div style={{
              display: 'flex', justifyContent: 'space-between', marginTop: 6,
              fontSize: 12, color: C.textMuted, fontFamily: 'system-ui, sans-serif',
            }}>
              <span>{title.length < 5 && title.length > 0 ? 'At least 5 characters required' : ''}</span>
              <span>{title.length}/300</span>
            </div>
          </div>

          {/* Body */}
          <div style={{ marginBottom: 24 }}>
            <label
              htmlFor="petition-body"
              style={{
                display: 'flex', alignItems: 'center', gap: 6, fontSize: 14, fontWeight: 600,
                color: C.text, marginBottom: 8, fontFamily: 'system-ui, sans-serif',
              }}
            >
              Description
            </label>
            <textarea
              id="petition-body"
              placeholder="Describe why this petition matters and what change you want to see..."
              value={body}
              onChange={e => setBody(e.target.value)}
              rows={6}
              style={{
                width: '100%', padding: '14px 16px', borderRadius: 12,
                border: `1px solid ${C.border}`, backgroundColor: C.cream,
                fontSize: 15, fontFamily: 'system-ui, sans-serif', color: C.text,
                outline: 'none', boxSizing: 'border-box', resize: 'vertical',
                lineHeight: '22px',
              }}
              onFocus={e => (e.target.style.borderColor = C.darkGreen)}
              onBlur={e => (e.target.style.borderColor = C.border)}
            />
          </div>

          {/* Target */}
          <div style={{ marginBottom: 24 }}>
            <label
              htmlFor="petition-target"
              style={{
                display: 'flex', alignItems: 'center', gap: 6, fontSize: 14, fontWeight: 600,
                color: C.text, marginBottom: 8, fontFamily: 'system-ui, sans-serif',
              }}
            >
              <Target size={15} style={{ color: C.darkGreen }} />
              Target (Who should act?)
            </label>
            <input
              id="petition-target"
              type="text"
              placeholder="e.g., U.S. Department of Education, Congress, Governor"
              value={target}
              onChange={e => setTarget(e.target.value)}
              maxLength={200}
              style={{
                width: '100%', padding: '14px 16px', borderRadius: 12,
                border: `1px solid ${C.border}`, backgroundColor: C.cream,
                fontSize: 15, fontFamily: 'system-ui, sans-serif', color: C.text,
                outline: 'none', boxSizing: 'border-box',
              }}
              onFocus={e => (e.target.style.borderColor = C.darkGreen)}
              onBlur={e => (e.target.style.borderColor = C.border)}
            />
          </div>

          {/* Category */}
          <div style={{ marginBottom: 24 }}>
            <label
              htmlFor="petition-category"
              style={{
                display: 'flex', alignItems: 'center', gap: 6, fontSize: 14, fontWeight: 600,
                color: C.text, marginBottom: 8, fontFamily: 'system-ui, sans-serif',
              }}
            >
              Category
            </label>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
              {CATEGORIES.map(cat => (
                <button
                  key={cat.value}
                  type="button"
                  onClick={() => setCategory(cat.value)}
                  style={{
                    padding: '8px 18px', borderRadius: 9999, fontSize: 13,
                    fontFamily: 'system-ui, sans-serif', fontWeight: 500, cursor: 'pointer',
                    border: category === cat.value ? `2px solid ${C.darkGreen}` : `1px solid ${C.border}`,
                    backgroundColor: category === cat.value ? `${C.darkGreen}0a` : 'transparent',
                    color: category === cat.value ? C.darkGreen : C.text,
                    transition: 'all 150ms',
                  }}
                >
                  {cat.label}
                </button>
              ))}
            </div>
          </div>

          {/* Tags */}
          <div style={{ marginBottom: 24 }}>
            <label
              htmlFor="petition-tags"
              style={{
                display: 'flex', alignItems: 'center', gap: 6, fontSize: 14, fontWeight: 600,
                color: C.text, marginBottom: 8, fontFamily: 'system-ui, sans-serif',
              }}
            >
              <Tag size={15} style={{ color: C.darkGreen }} />
              Tags
            </label>
            <input
              id="petition-tags"
              type="text"
              placeholder="e.g., student debt, education, relief (comma-separated)"
              value={tagsInput}
              onChange={e => setTagsInput(e.target.value)}
              maxLength={200}
              style={{
                width: '100%', padding: '14px 16px', borderRadius: 12,
                border: `1px solid ${C.border}`, backgroundColor: C.cream,
                fontSize: 15, fontFamily: 'system-ui, sans-serif', color: C.text,
                outline: 'none', boxSizing: 'border-box',
              }}
              onFocus={e => (e.target.style.borderColor = C.darkGreen)}
              onBlur={e => (e.target.style.borderColor = C.border)}
            />
            <p style={{ fontSize: 12, color: C.textMuted, margin: '6px 0 0', fontFamily: 'system-ui, sans-serif' }}>
              Separate tags with commas. Up to 10 tags.
            </p>
          </div>

          {/* Signature Goal */}
          <div style={{ marginBottom: 32 }}>
            <label
              style={{
                display: 'flex', alignItems: 'center', gap: 6, fontSize: 14, fontWeight: 600,
                color: C.text, marginBottom: 8, fontFamily: 'system-ui, sans-serif',
              }}
            >
              <Users size={15} style={{ color: C.darkGreen }} />
              Signature Goal
            </label>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
              {GOAL_OPTIONS.map(opt => (
                <button
                  key={opt.value}
                  type="button"
                  onClick={() => setSignatureGoal(opt.value)}
                  style={{
                    padding: '8px 18px', borderRadius: 9999, fontSize: 13,
                    fontFamily: 'system-ui, sans-serif', fontWeight: 500, cursor: 'pointer',
                    border: signatureGoal === opt.value ? `2px solid ${C.gold}` : `1px solid ${C.border}`,
                    backgroundColor: signatureGoal === opt.value ? `${C.gold}15` : 'transparent',
                    color: signatureGoal === opt.value ? C.darkGreen : C.text,
                    transition: 'all 150ms',
                  }}
                >
                  {opt.label}
                </button>
              ))}
            </div>
          </div>

          {/* Divider */}
          <div style={{ borderTop: `1px solid ${C.borderLight}`, marginBottom: 24 }} />

          {/* Submit */}
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 12 }}>
            <p style={{ fontSize: 13, color: C.textMuted, margin: 0, fontFamily: 'system-ui, sans-serif' }}>
              <span style={{ color: C.error }}>*</span> Required field
            </p>
            <button
              type="submit"
              disabled={!canSubmit}
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 8,
                backgroundColor: canSubmit ? C.gold : C.borderLight,
                color: canSubmit ? C.darkGreen : C.textMuted,
                border: 'none', borderRadius: 9999, padding: '14px 32px',
                fontSize: 16, fontWeight: 700, fontFamily: 'system-ui, sans-serif',
                cursor: canSubmit ? 'pointer' : 'not-allowed',
                transition: 'all 150ms',
              }}
              onMouseEnter={e => { if (canSubmit) e.currentTarget.style.backgroundColor = '#c8941a'; }}
              onMouseLeave={e => { if (canSubmit) e.currentTarget.style.backgroundColor = C.gold; }}
            >
              {submitting ? (
                <>
                  <Loader2 size={18} style={{ animation: 'spin 1s linear infinite' }} />
                  Submitting...
                </>
              ) : (
                <>
                  <Send size={18} />
                  Publish Petition
                </>
              )}
            </button>
          </div>
        </div>
      </form>

      <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
    </div>
  );
}