DraftReviewPill.tsx3.1 KBView on GitHub
/**
 * DraftReviewPill Component
 *
 * Pill-shaped sticky notification that appears at the bottom of the conversation canvas
 * when there are drafts ready for review. Clicking opens review mode.
 */

import { motion, AnimatePresence } from 'motion/react';
import { ChevronRight, Check, X } from 'lucide-react';
import { Button } from '@/components/ui/button';

export interface DraftReviewPillProps {
  /** Number of pending drafts */
  pendingCount: number;
  /** Callback when the pill is clicked */
  onOpenReview: () => void;
  /** Callback to accept all drafts */
  onAcceptAll: () => void;
  /** Callback to reject all drafts */
  onRejectAll: () => void;
}

export function DraftReviewPill({
  pendingCount,
  onOpenReview,
  onAcceptAll,
  onRejectAll,
}: DraftReviewPillProps) {
  return (
    <AnimatePresence>
      {pendingCount > 0 && (
        <motion.div
          initial={{ y: 100, opacity: 0 }}
          animate={{ y: 0, opacity: 1 }}
          exit={{ y: 100, opacity: 0 }}
          transition={{ type: 'spring', damping: 25, stiffness: 300 }}
          className="absolute bottom-4 left-1/2 z-10 -translate-x-1/2"
        >
          <div className="flex items-center gap-2 rounded-full border border-border bg-card px-2 py-2 shadow-md">
            {/* Reject All button */}
            <Button
              variant="ghost"
              size="sm"
              onClick={(e) => {
                e.stopPropagation();
                onRejectAll();
              }}
              className="h-7 rounded-full cursor-pointer text-muted-foreground hover:bg-red-50 hover:text-red-600 dark:hover:bg-red-950/50"
            >
              <X className="mr-1 h-3.5 w-3.5" />
              Reject All
            </Button>

            {/* Main pill content (click to open review) - Primary action */}
            <button
              onClick={onOpenReview}
              className="group flex cursor-pointer items-center gap-2.5 rounded-full bg-foreground px-3 py-1.5 transition-all duration-200 hover:bg-foreground/90"
            >
              <div className="flex items-center gap-2">
                <div className="flex h-5 w-5 items-center justify-center rounded-full bg-background text-xs font-semibold text-foreground">
                  {pendingCount}
                </div>
                <span className="text-sm font-medium text-background">
                  Review Draft{pendingCount !== 1 ? 's' : ''}
                </span>
              </div>
              <ChevronRight className="h-4 w-4 text-background/70 transition-colors group-hover:text-background" />
            </button>

            {/* Accept All button */}
            <Button
              variant="ghost"
              size="sm"
              onClick={(e) => {
                e.stopPropagation();
                onAcceptAll();
              }}
              className="h-7 rounded-full cursor-pointer hover:bg-green-50 hover:text-green-600 dark:hover:bg-green-950/50"
            >
              <Check className="mr-1 h-3.5 w-3.5" />
              Save All
            </Button>
          </div>
        </motion.div>
      )}
    </AnimatePresence>
  );
}