All files / components/packages PackageList.tsx

100% Statements 47/47
93.33% Branches 14/15
100% Functions 4/4
100% Lines 47/47

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 811x                         1x 14x 2x 2x   14x 14x 14x 34x 14x 14x   14x             34x   34x   34x 34x 34x 34x     34x 11x 11x 11x   11x 11x     34x 34x 34x 34x   34x   34x 34x 34x 34x 34x 34x     34x 22x 22x 77x 77x 77x 77x 22x 22x   34x 34x   34x  
'use client';
 
import { Check, Sparkles } from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { trackEvent } from '@/lib/analytics/track';
import { formatPrice, type Currency } from '@/lib/validations/package';
import type { Package } from '@/types/database';
 
interface PackageListProps {
  packages: Package[];
  profileId: string;
}
 
export function PackageList({ packages, profileId }: PackageListProps) {
  const handlePackageView = (packageId: string) => {
    trackEvent(profileId, 'package_view', { package_id: packageId });
  };
 
  return (
    <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
      {packages.map((pkg) => (
        <PackageCard key={pkg.id} package={pkg} onView={() => handlePackageView(pkg.id)} />
      ))}
    </div>
  );
}
 
interface PackageCardProps {
  package: Package;
  onView: () => void;
}
 
function PackageCard({ package: pkg, onView }: PackageCardProps) {
  // Parse features from JSON
  const features: string[] = Array.isArray(pkg.features) ? (pkg.features as string[]) : [];
 
  return (
    <Card
      className={`relative h-full ${pkg.is_featured ? 'border-primary' : ''}`}
      onMouseEnter={onView}
    >
      {/* Featured Badge */}
      {pkg.is_featured && (
        <div className="absolute -top-3 left-1/2 -translate-x-1/2">
          <span className="inline-flex items-center gap-1 rounded-full bg-primary px-3 py-1 text-xs font-medium text-primary-foreground">
            <Sparkles className="h-3 w-3" />
            Popular
          </span>
        </div>
      )}
 
      <CardHeader className={pkg.is_featured ? 'pt-8' : ''}>
        <CardTitle className="text-lg">{pkg.title}</CardTitle>
        {pkg.description && <p className="text-sm text-muted-foreground">{pkg.description}</p>}
      </CardHeader>
 
      <CardContent className="space-y-4">
        {/* Price */}
        <div>
          <span className="text-3xl font-bold">
            {formatPrice(pkg.price, pkg.currency as Currency)}
          </span>
          {pkg.duration && <span className="text-muted-foreground"> / {pkg.duration}</span>}
        </div>
 
        {/* Features */}
        {features.length > 0 && (
          <ul className="space-y-2">
            {features.map((feature, index) => (
              <li key={index} className="flex items-start gap-2 text-sm">
                <Check className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
                <span>{feature}</span>
              </li>
            ))}
          </ul>
        )}
      </CardContent>
    </Card>
  );
}