← back to IWasCute

src/components/licensor/LikenessRelease.tsx

365 lines

'use client'

import { useState } from 'react'
import { motion } from 'framer-motion'
import { ChevronLeft, ChevronRight, ShieldCheck, AlertTriangle } from 'lucide-react'
import clsx from 'clsx'

export interface LikenessData {
  isSubject: boolean
  isAdult: boolean
  editorial: boolean
  commercial: boolean
  aiTraining: boolean
  signature: string
  date: string
}

interface LikenessReleaseProps {
  likenessRelease: LikenessData
  onLikenessChange: (data: LikenessData) => void
  onNext: () => void
  onBack: () => void
}

function CheckboxField({
  id,
  label,
  description,
  checked,
  onChange,
  required,
  error,
}: {
  id: string
  label: string
  description?: string
  checked: boolean
  onChange: (v: boolean) => void
  required?: boolean
  error?: boolean
}) {
  return (
    <label
      htmlFor={id}
      className="flex items-start gap-4 p-4 rounded-2xl cursor-pointer transition-all"
      style={{
        background: checked
          ? 'rgba(181, 213, 197, 0.2)'
          : error
            ? 'rgba(255, 181, 167, 0.12)'
            : 'rgba(255,255,255,0.5)',
        border: `2px solid ${
          checked
            ? 'var(--color-sage)'
            : error
              ? 'var(--color-peach)'
              : 'var(--color-cream-dark)'
        }`,
      }}
    >
      <div className="relative mt-0.5 shrink-0">
        <input
          id={id}
          type="checkbox"
          checked={checked}
          onChange={(e) => onChange(e.target.checked)}
          required={required}
          className="sr-only"
          aria-required={required}
        />
        <div
          className="w-5 h-5 rounded-md border-2 flex items-center justify-center transition-all"
          style={{
            background: checked ? 'var(--color-sage-dark)' : 'white',
            borderColor: checked ? 'var(--color-sage-dark)' : 'var(--color-cream-dark)',
          }}
          aria-hidden
        >
          {checked && (
            <motion.svg
              initial={{ scale: 0 }}
              animate={{ scale: 1 }}
              width="11"
              height="11"
              viewBox="0 0 11 11"
              fill="none"
            >
              <path
                d="M2 5.5l2.5 2.5L9 3"
                stroke="white"
                strokeWidth="1.6"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
            </motion.svg>
          )}
        </div>
      </div>

      <div className="flex-1">
        <p
          className="text-sm font-semibold"
          style={{ color: 'var(--color-brown)' }}
        >
          {label}
          {required && (
            <span className="ml-1" style={{ color: 'var(--color-peach-dark)' }} aria-hidden>
              *
            </span>
          )}
        </p>
        {description && (
          <p className="mt-0.5 text-xs leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
            {description}
          </p>
        )}
      </div>
    </label>
  )
}

export default function LikenessRelease({
  likenessRelease,
  onLikenessChange,
  onNext,
  onBack,
}: LikenessReleaseProps) {
  const [attempted, setAttempted] = useState(false)

  const patch = (updates: Partial<LikenessData>) => {
    onLikenessChange({ ...likenessRelease, ...updates })
  }

  const atLeastOneUse =
    likenessRelease.editorial ||
    likenessRelease.commercial ||
    likenessRelease.aiTraining

  const canProceed =
    likenessRelease.isSubject &&
    likenessRelease.isAdult &&
    atLeastOneUse &&
    likenessRelease.signature.trim().length >= 2

  const handleNext = () => {
    setAttempted(true)
    if (canProceed) onNext()
  }

  return (
    <div className="flex flex-col gap-6">
      {/* Header */}
      <div>
        <h2
          className="text-2xl md:text-3xl font-bold tracking-tight"
          style={{ color: 'var(--color-brown)' }}
        >
          Likeness Release
        </h2>
        <p className="mt-2 text-base" style={{ color: 'var(--color-warm-gray)' }}>
          Confirm you are the person in these photos and grant licensing permissions.
        </p>
      </div>

      {/* Security badge */}
      <div
        className="flex items-center gap-3 p-4 rounded-2xl"
        style={{ background: 'rgba(181, 213, 197, 0.2)', border: '1px solid var(--color-sage)' }}
        role="note"
      >
        <ShieldCheck size={20} style={{ color: 'var(--color-sage-dark)' }} />
        <p className="text-sm" style={{ color: 'var(--color-warm-gray)' }}>
          Your signature is stored securely and is legally binding. You can revoke permissions at
          any time from your account.
        </p>
      </div>

      {/* Identity confirmations */}
      <section aria-labelledby="identity-heading">
        <h3
          id="identity-heading"
          className="text-base font-bold tracking-wide mb-3"
          style={{ color: 'var(--color-brown)' }}
        >
          Identity Confirmation
        </h3>
        <div className="flex flex-col gap-3">
          <CheckboxField
            id="is-subject"
            label="I confirm I am the person depicted in these photos"
            description="These are photos of me as a child. I am the subject whose likeness will be licensed."
            checked={likenessRelease.isSubject}
            onChange={(v) => patch({ isSubject: v })}
            required
            error={attempted && !likenessRelease.isSubject}
          />
          <CheckboxField
            id="is-adult"
            label="I am 18 years of age or older"
            description="You must be an adult to legally grant rights to your own likeness."
            checked={likenessRelease.isAdult}
            onChange={(v) => patch({ isAdult: v })}
            required
            error={attempted && !likenessRelease.isAdult}
          />
        </div>
      </section>

      {/* Usage permissions */}
      <section aria-labelledby="usage-heading">
        <h3
          id="usage-heading"
          className="text-base font-bold tracking-wide mb-1"
          style={{ color: 'var(--color-brown)' }}
        >
          I grant permission for these photos to be licensed for:
        </h3>
        <p className="text-xs mb-3" style={{ color: 'var(--color-warm-gray)' }}>
          Select at least one use type. You earn royalties for each license granted.
        </p>

        {attempted && !atLeastOneUse && (
          <motion.div
            initial={{ opacity: 0, y: -4 }}
            animate={{ opacity: 1, y: 0 }}
            className="flex items-center gap-2 mb-3 p-3 rounded-xl"
            style={{ background: 'rgba(255, 181, 167, 0.2)', border: '1px solid var(--color-peach)' }}
            role="alert"
          >
            <AlertTriangle size={14} style={{ color: 'var(--color-peach-dark)' }} />
            <p className="text-xs font-medium" style={{ color: 'var(--color-brown)' }}>
              Please select at least one use type.
            </p>
          </motion.div>
        )}

        <div className="flex flex-col gap-3">
          <CheckboxField
            id="editorial"
            label="Editorial use"
            description="News articles, blog posts, non-commercial publications, and journalism."
            checked={likenessRelease.editorial}
            onChange={(v) => patch({ editorial: v })}
            error={attempted && !atLeastOneUse}
          />
          <CheckboxField
            id="commercial"
            label="Commercial use"
            description="Advertisements, product packaging, marketing materials, and brand campaigns."
            checked={likenessRelease.commercial}
            onChange={(v) => patch({ commercial: v })}
            error={attempted && !atLeastOneUse}
          />
          <CheckboxField
            id="ai-training"
            label="AI training use"
            description="Use in AI model training datasets. Higher royalty rate applies."
            checked={likenessRelease.aiTraining}
            onChange={(v) => patch({ aiTraining: v })}
            error={attempted && !atLeastOneUse}
          />
        </div>
      </section>

      {/* Signature */}
      <section aria-labelledby="signature-heading">
        <h3
          id="signature-heading"
          className="text-base font-bold tracking-wide mb-3"
          style={{ color: 'var(--color-brown)' }}
        >
          Digital Signature
        </h3>

        <div className="flex flex-col gap-4">
          <div className="flex flex-col gap-1.5">
            <label
              htmlFor="signature"
              className="text-sm font-semibold"
              style={{ color: 'var(--color-brown)' }}
            >
              Type your full legal name to sign
              <span className="ml-1" style={{ color: 'var(--color-peach-dark)' }} aria-hidden>
                *
              </span>
            </label>
            <input
              id="signature"
              type="text"
              value={likenessRelease.signature}
              onChange={(e) => patch({ signature: e.target.value })}
              placeholder="Your full legal name"
              autoComplete="name"
              className={clsx(
                'w-full px-5 py-4 rounded-2xl text-lg signature-field',
                'border-2 outline-none transition-all duration-200'
              )}
              style={{
                background: 'rgba(255,255,255,0.8)',
                borderColor:
                  attempted && likenessRelease.signature.trim().length < 2
                    ? 'var(--color-peach)'
                    : likenessRelease.signature.trim().length >= 2
                      ? 'var(--color-sage)'
                      : 'var(--color-cream-dark)',
                color: 'var(--color-brown)',
              }}
              aria-required="true"
              aria-invalid={attempted && likenessRelease.signature.trim().length < 2}
            />
            {attempted && likenessRelease.signature.trim().length < 2 && (
              <p className="text-xs" style={{ color: 'var(--color-peach-dark)' }}>
                Please enter your full legal name.
              </p>
            )}
          </div>

          {/* Date */}
          <div
            className="flex items-center justify-between px-5 py-3 rounded-2xl"
            style={{ background: 'var(--color-cream-dark)' }}
          >
            <span className="text-sm font-medium" style={{ color: 'var(--color-warm-gray)' }}>
              Date signed
            </span>
            <span className="text-sm font-semibold" style={{ color: 'var(--color-brown)' }}>
              {likenessRelease.date}
            </span>
          </div>
        </div>
      </section>

      {/* Navigation */}
      <div className="flex items-center justify-between pt-2">
        <button
          onClick={onBack}
          className="flex items-center gap-2 px-6 py-3 rounded-3xl text-sm font-semibold tracking-wide transition-all hover:scale-105"
          style={{
            background: 'var(--color-cream-dark)',
            color: 'var(--color-warm-gray)',
          }}
        >
          <ChevronLeft size={16} />
          Back
        </button>

        <motion.button
          whileHover={{ scale: 1.03 }}
          whileTap={{ scale: 0.97 }}
          onClick={handleNext}
          className="flex items-center gap-2 px-8 py-3.5 rounded-3xl text-sm font-semibold tracking-wide transition-all duration-200"
          style={{
            background: 'var(--color-peach)',
            color: 'var(--color-brown)',
            opacity: canProceed ? 1 : 0.7,
          }}
        >
          Review &amp; Submit
          <ChevronRight size={16} />
        </motion.button>
      </div>
    </div>
  )
}