← back to Watches

src/components/RecentSales.jsx

142 lines

import React from 'react';
import { motion, AnimatePresence } from 'framer-motion';

/**
 * Format price with currency
 */
function formatPrice(price, currency = 'USD') {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency,
    minimumFractionDigits: 0,
    maximumFractionDigits: 0
  }).format(price);
}

/**
 * Format date to readable string
 */
function formatDate(dateStr) {
  const date = new Date(dateStr);
  return new Intl.DateTimeFormat('en-US', {
    month: 'short',
    day: 'numeric',
    year: 'numeric'
  }).format(date);
}

/**
 * Get condition badge color
 */
function getConditionColor(condition) {
  const colors = {
    'Like New': 'bg-green-900/50 text-green-300 border-green-700/50',
    'Excellent': 'bg-blue-900/50 text-blue-300 border-blue-700/50',
    'Very Good': 'bg-amber-900/50 text-amber-300 border-amber-700/50',
    'Good': 'bg-gray-700/50 text-gray-300 border-gray-600/50',
    'Fair': 'bg-orange-900/50 text-orange-300 border-orange-700/50'
  };
  return colors[condition] || colors['Good'];
}

/**
 * RecentSales - Shows recent sales history
 * @param {Array} sales - Array of sale objects { date, price, condition, source }
 * @param {number} limit - Max number of sales to show
 * @param {string} className - Additional CSS classes
 */
export default function RecentSales({ sales = [], limit = 5, className = '' }) {
  // Handle empty state
  if (!sales || sales.length === 0) {
    return (
      <div className={`text-gray-500 text-center py-4 ${className}`}>
        <svg
          className="w-8 h-8 mx-auto mb-2 opacity-50"
          fill="none"
          viewBox="0 0 24 24"
          stroke="currentColor"
        >
          <path
            strokeLinecap="round"
            strokeLinejoin="round"
            strokeWidth={1.5}
            d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
          />
        </svg>
        <p className="text-sm">No recent sales data</p>
      </div>
    );
  }

  // Sort by date (most recent first) and limit
  const sortedSales = [...sales]
    .sort((a, b) => new Date(b.date) - new Date(a.date))
    .slice(0, limit);

  return (
    <motion.div
      className={`${className}`}
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.4 }}
    >
      <h4 className="text-gray-400 text-sm font-medium mb-3">Recent Sales</h4>

      <div className="space-y-2">
        <AnimatePresence mode="popLayout">
          {sortedSales.map((sale, index) => (
            <motion.div
              key={`${sale.date}-${sale.price}-${index}`}
              className="bg-gray-800/30 border border-gray-700/50 rounded-lg p-3 hover:border-amber-500/30 transition-colors"
              initial={{ opacity: 0, x: -20 }}
              animate={{ opacity: 1, x: 0 }}
              exit={{ opacity: 0, x: 20 }}
              transition={{ delay: index * 0.05, duration: 0.3 }}
              layout
            >
              {/* Mobile: Stack layout */}
              <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
                <div className="flex items-center justify-between sm:justify-start gap-3">
                  <span className="text-amber-400 font-semibold">
                    {formatPrice(sale.price)}
                  </span>
                  <span className={`px-2 py-0.5 text-xs rounded-full border ${getConditionColor(sale.condition)}`}>
                    {sale.condition}
                  </span>
                </div>

                <div className="flex items-center justify-between sm:justify-end gap-3 text-xs text-gray-500">
                  <span>{formatDate(sale.date)}</span>
                  <span className="text-gray-600">•</span>
                  {sale.href ? (
                    <a
                      href={sale.href}
                      target="_blank"
                      rel="noopener noreferrer"
                      className="text-blue-400 hover:text-blue-300 hover:underline flex items-center gap-1"
                      onClick={(e) => e.stopPropagation()}
                    >
                      {sale.source}
                      <svg className="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
                      </svg>
                    </a>
                  ) : (
                    <span>{sale.source}</span>
                  )}
                </div>
              </div>
            </motion.div>
          ))}
        </AnimatePresence>
      </div>

      {sales.length > limit && (
        <p className="text-gray-500 text-xs mt-2 text-center">
          Showing {limit} of {sales.length} sales
        </p>
      )}
    </motion.div>
  );
}