{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"ecommerce-ecommerce-customer-reviews-review-cards-grid","type":"registry:block","title":"Review Cards Grid","files":[{"path":"components/blocks/ecommerce/ecommerce-customer-reviews/review-cards-grid.tsx","content":"import { Card, CardContent, CardHeader } from \"@/components/ui/card\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Star, ShieldCheck, User } from \"lucide-react\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\n\nexport default function ReviewCardsGrid() {\n  const reviews = [\n    {\n      id: 1,\n      name: \"Emily Chen\",\n      avatar:\n        \"https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=400&auto=format&fit=crop&ixlib=rb-4.0.3\",\n      rating: 5,\n      review:\n        \"Absolutely love my purchase! The quality is outstanding and it arrived faster than expected. Will definitely order again.\",\n      product: \"Wireless Headphones\",\n      verified: true,\n      date: \"2 days ago\",\n    },\n    {\n      id: 2,\n      name: \"Michael Rodriguez\",\n      avatar:\n        \"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?q=80&w=400&auto=format&fit=crop&ixlib=rb-4.0.3\",\n      rating: 5,\n      review:\n        \"Great customer service and the product exceeded my expectations. The packaging was also very professional.\",\n      product: \"Smartphone Case\",\n      verified: true,\n      date: \"1 week ago\",\n    },\n    {\n      id: 3,\n      name: \"Sarah Williams\",\n      avatar:\n        \"https://images.unsplash.com/photo-1438761681033-6461ffad8d80?q=80&w=400&auto=format&fit=crop&ixlib=rb-4.0.3\",\n      rating: 4,\n      review:\n        \"Really satisfied with this purchase. Good value for money and the delivery was on time. Highly recommend!\",\n      product: \"Laptop Stand\",\n      verified: true,\n      date: \"2 weeks ago\",\n    },\n    {\n      id: 4,\n      name: \"David Thompson\",\n      avatar:\n        \"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?q=80&w=400&auto=format&fit=crop&ixlib=rb-4.0.3\",\n      rating: 5,\n      review:\n        \"Excellent product quality and fast shipping. The item was exactly as described and works perfectly.\",\n      product: \"Bluetooth Speaker\",\n      verified: true,\n      date: \"3 weeks ago\",\n    },\n    {\n      id: 5,\n      name: \"Jessica Park\",\n      avatar:\n        \"https://images.unsplash.com/photo-1544005313-94ddf0286df2?q=80&w=400&auto=format&fit=crop&ixlib=rb-4.0.3\",\n      rating: 5,\n      review:\n        \"Amazing experience! The product is high quality and the customer support team was very helpful with my questions.\",\n      product: \"Wireless Charger\",\n      verified: true,\n      date: \"1 month ago\",\n    },\n    {\n      id: 6,\n      name: \"Robert Kim\",\n      avatar:\n        \"https://images.unsplash.com/photo-1500648767791-00dcc994a43e?q=80&w=400&auto=format&fit=crop&ixlib=rb-4.0.3\",\n      rating: 4,\n      review:\n        \"Very happy with my purchase. The build quality is solid and it does exactly what I needed it to do.\",\n      product: \"USB Hub\",\n      verified: true,\n      date: \"1 month ago\",\n    },\n  ];\n\n  return (\n    <div className=\"container mx-auto px-4 py-24 md:px-6 lg:py-32 2xl:max-w-[1400px]\">\n      <div className=\"mb-16 text-center\">\n        <h2 className=\"mb-4 text-3xl font-bold tracking-tight sm:text-4xl\">\n          Customer Reviews\n        </h2>\n        <p className=\"text-muted-foreground mx-auto max-w-2xl text-lg\">\n          See what our customers are saying about their shopping experience\n        </p>\n      </div>\n\n      <div className=\"grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3\">\n        {reviews.map((review) => (\n          <Card key={review.id} className=\"h-full gap-3\">\n            <CardHeader>\n              <div className=\"flex items-center gap-4\">\n                <Avatar>\n                  <AvatarImage\n                    width={48}\n                    height={48}\n                    src={review.avatar}\n                    className=\"object-cover\"\n                  />\n                  <AvatarFallback>\n                    <User className=\"size-4\" />\n                  </AvatarFallback>\n                </Avatar>\n                <div className=\"flex-1\">\n                  <div className=\"mb-1 flex items-center gap-2\">\n                    <h3 className=\"text-sm font-semibold\">{review.name}</h3>\n                    {review.verified && (\n                      <Badge variant=\"secondary\" className=\"text-xs\">\n                        <ShieldCheck className=\"mr-1 size-3\" />\n                        Verified\n                      </Badge>\n                    )}\n                  </div>\n                  <div className=\"flex items-center gap-2\">\n                    <div className=\"flex\">\n                      {[...Array(5)].map((_, i) => (\n                        <Star\n                          key={i}\n                          className={`size-4 ${\n                            i < review.rating\n                              ? \"fill-yellow-400 text-yellow-400\"\n                              : \"text-muted-foreground\"\n                          }`}\n                        />\n                      ))}\n                    </div>\n                    <span className=\"text-muted-foreground text-xs\">\n                      {review.date}\n                    </span>\n                  </div>\n                </div>\n              </div>\n            </CardHeader>\n            <CardContent className=\"flex flex-1 flex-col\">\n              <p className=\"mb-3 flex-1 text-sm leading-relaxed\">\n                {review.review}\n              </p>\n              <p className=\"text-muted-foreground text-xs font-medium\">\n                Product: {review.product}\n              </p>\n            </CardContent>\n          </Card>\n        ))}\n      </div>\n    </div>\n  );\n}\n","type":"registry:component","target":"components/blocks/ecommerce/ecommerce-customer-reviews/review-cards-grid.tsx"},{"path":"components/ui/card.tsx","content":"import * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Card({\n  className,\n  size = \"default\",\n  ...props\n}: React.ComponentProps<\"div\"> & { size?: \"default\" | \"sm\" }) {\n  return (\n    <div\n      data-slot=\"card\"\n      data-size={size}\n      className={cn(\n        \"group/card flex flex-col gap-6 overflow-hidden rounded-xl bg-card py-6 text-sm text-card-foreground shadow-xs ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction CardHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-header\"\n      className={cn(\n        \"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-6 group-data-[size=sm]/card:px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction CardTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-title\"\n      className={cn(\n        \"font-heading text-base leading-normal font-medium group-data-[size=sm]/card:text-sm\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction CardDescription({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-description\"\n      className={cn(\"text-sm text-muted-foreground\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction CardAction({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-action\"\n      className={cn(\n        \"col-start-2 row-span-2 row-start-1 self-start justify-self-end\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction CardContent({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-content\"\n      className={cn(\"px-6 group-data-[size=sm]/card:px-4\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction CardFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"card-footer\"\n      className={cn(\n        \"flex items-center rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport {\n  Card,\n  CardHeader,\n  CardFooter,\n  CardTitle,\n  CardAction,\n  CardDescription,\n  CardContent,\n}\n","type":"registry:ui","target":"components/ui/card.tsx"},{"path":"components/ui/badge.tsx","content":"import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst badgeVariants = cva(\n  \"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!\",\n  {\n    variants: {\n      variant: {\n        default: \"bg-primary text-primary-foreground [a]:hover:bg-primary/80\",\n        secondary:\n          \"bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80\",\n        destructive:\n          \"bg-destructive/10 text-destructive focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:focus-visible:ring-destructive/40 [a]:hover:bg-destructive/20\",\n        outline:\n          \"border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground\",\n        ghost:\n          \"hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50\",\n        link: \"text-primary underline-offset-4 hover:underline\",\n      },\n    },\n    defaultVariants: {\n      variant: \"default\",\n    },\n  }\n)\n\nfunction Badge({\n  className,\n  variant = \"default\",\n  asChild = false,\n  ...props\n}: React.ComponentProps<\"span\"> &\n  VariantProps<typeof badgeVariants> & { asChild?: boolean }) {\n  const Comp = asChild ? Slot.Root : \"span\"\n\n  return (\n    <Comp\n      data-slot=\"badge\"\n      data-variant={variant}\n      className={cn(badgeVariants({ variant }), className)}\n      {...props}\n    />\n  )\n}\n\nexport { Badge, badgeVariants }\n","type":"registry:ui","target":"components/ui/badge.tsx"},{"path":"components/ui/avatar.tsx","content":"\"use client\"\n\nimport * as React from \"react\"\nimport { Avatar as AvatarPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction Avatar({\n  className,\n  size = \"default\",\n  ...props\n}: React.ComponentProps<typeof AvatarPrimitive.Root> & {\n  size?: \"default\" | \"sm\" | \"lg\"\n}) {\n  return (\n    <AvatarPrimitive.Root\n      data-slot=\"avatar\"\n      data-size={size}\n      className={cn(\n        \"group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AvatarImage({\n  className,\n  ...props\n}: React.ComponentProps<typeof AvatarPrimitive.Image>) {\n  return (\n    <AvatarPrimitive.Image\n      data-slot=\"avatar-image\"\n      className={cn(\n        \"aspect-square size-full rounded-full object-cover\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AvatarFallback({\n  className,\n  ...props\n}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {\n  return (\n    <AvatarPrimitive.Fallback\n      data-slot=\"avatar-fallback\"\n      className={cn(\n        \"flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AvatarBadge({ className, ...props }: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"avatar-badge\"\n      className={cn(\n        \"absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none\",\n        \"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden\",\n        \"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2\",\n        \"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AvatarGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"avatar-group\"\n      className={cn(\n        \"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AvatarGroupCount({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"avatar-group-count\"\n      className={cn(\n        \"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport {\n  Avatar,\n  AvatarImage,\n  AvatarFallback,\n  AvatarGroup,\n  AvatarGroupCount,\n  AvatarBadge,\n}\n","type":"registry:ui","target":"components/ui/avatar.tsx"}],"description":"A grid of review cards showing star ratings, avatars, and short quotes. Use it to display many customer opinions at once on a product detail page.","dependencies":["class-variance-authority","lucide-react","radix-ui"]}