{
  "name": "review-09-block",
  "type": "registry:block",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "badge",
    "button",
    "dropdown-menu",
    "input",
    "https://ui.stackzero.co/r/star-rating-fractions.json"
  ],
  "files": [
    {
      "type": "registry:block",
      "content": "\"use client\";\n\nimport StarRating from \"@/components/commerce-ui/star-rating-fractions\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { Input } from \"@/components/ui/input\";\nimport { Check, Search, SlidersHorizontal, X } from \"lucide-react\";\nimport { useState } from \"react\";\n\ninterface Review_09Props {\n  onFilterChange?: (filters: ReviewFilters) => void;\n  totalReviews?: number;\n  averageRating?: number;\n  commonKeywords?: string[];\n}\n\ninterface ReviewFilters {\n  ratings: number[];\n  sortBy: string;\n  keywords: string[];\n  searchTerm: string;\n}\n\nfunction Review_09({\n  averageRating = 4.8,\n  commonKeywords = [\n    \"Clean\",\n    \"Comfortable\",\n    \"Great location\",\n    \"Spacious\",\n    \"Value for money\",\n    \"Friendly host\",\n    \"Quiet\",\n    \"Well equipped\",\n  ],\n  onFilterChange,\n  totalReviews = 214,\n}: Review_09Props) {\n  const [filters, setFilters] = useState<ReviewFilters>({\n    keywords: [\"Clean\", \"Comfortable\"],\n    ratings: [4, 5],\n    searchTerm: \"\",\n    sortBy: \"Most recent\",\n  });\n\n  const [showMobileFilters, setShowMobileFilters] = useState(false);\n\n  const sortOptions = [\n    \"Most recent\",\n    \"Highest rated\",\n    \"Lowest rated\",\n    \"Most helpful\",\n  ];\n\n  const handleFilterChange = (updatedFilters: Partial<ReviewFilters>) => {\n    const newFilters = { ...filters, ...updatedFilters };\n    setFilters(newFilters);\n    onFilterChange?.(newFilters);\n  };\n\n  const toggleKeyword = (keyword: string) => {\n    const newKeywords = filters.keywords.includes(keyword)\n      ? filters.keywords.filter((k) => k !== keyword)\n      : [...filters.keywords, keyword];\n\n    handleFilterChange({ keywords: newKeywords });\n  };\n\n  const toggleRating = (rating: number) => {\n    const newRatings = filters.ratings.includes(rating)\n      ? filters.ratings.filter((r) => r !== rating)\n      : [...filters.ratings, rating];\n\n    handleFilterChange({ ratings: newRatings });\n  };\n\n  return (\n    <div className=\"space-y-4 rounded-md border p-4\">\n      <div className=\"flex flex-col justify-between gap-4 border-b border-gray-200 pb-4 sm:flex-row sm:items-center dark:border-gray-800\">\n        <div className=\"space-y-1\">\n          <h3 className=\"text-xl font-semibold text-gray-900 dark:text-white\">\n            Reviews ({totalReviews})\n          </h3>\n          <div className=\"flex items-center gap-2\">\n            <StarRating value={averageRating} readOnly iconSize={16} />\n            <span className=\"font-medium\">{averageRating}</span>\n          </div>\n        </div>\n\n        <div className=\"flex gap-2\">\n          <div className=\"relative flex-1 sm:max-w-[240px]\">\n            <Search className=\"absolute top-2.5 left-2.5 h-4 w-4 text-gray-500\" />\n            <Input\n              placeholder=\"Search reviews\"\n              className=\"pl-9\"\n              value={filters.searchTerm}\n              onChange={(e) =>\n                handleFilterChange({ searchTerm: e.target.value })\n              }\n            />\n            {filters.searchTerm && (\n              <Button\n                variant=\"ghost\"\n                size=\"icon\"\n                className=\"absolute top-0 right-0 h-full rounded-l-none\"\n                onClick={() => handleFilterChange({ searchTerm: \"\" })}\n              >\n                <X className=\"h-4 w-4\" />\n              </Button>\n            )}\n          </div>\n\n          <Button\n            variant=\"outline\"\n            onClick={() => setShowMobileFilters(!showMobileFilters)}\n            className=\"sm:hidden\"\n          >\n            <SlidersHorizontal className=\"mr-2 h-4 w-4\" />\n            Filter\n          </Button>\n\n          <DropdownMenu>\n            <DropdownMenuTrigger asChild>\n              <Button variant=\"outline\" className=\"hidden sm:flex\">\n                Sort: {filters.sortBy}\n              </Button>\n            </DropdownMenuTrigger>\n            <DropdownMenuContent align=\"end\" className=\"w-[200px]\">\n              {sortOptions.map((option) => (\n                <DropdownMenuItem\n                  key={option}\n                  className=\"flex items-center justify-between\"\n                  onClick={() => handleFilterChange({ sortBy: option })}\n                >\n                  {option}\n                  {filters.sortBy === option && <Check className=\"h-4 w-4\" />}\n                </DropdownMenuItem>\n              ))}\n            </DropdownMenuContent>\n          </DropdownMenu>\n        </div>\n      </div>\n\n      <div\n        className={`space-y-5 ${showMobileFilters ? \"block\" : \"hidden sm:block\"}`}\n      >\n        <div className=\"space-y-3\">\n          <h4 className=\"text-sm font-medium text-gray-900 dark:text-white\">\n            Rating\n          </h4>\n          <div className=\"space-y-2\">\n            {[5, 4, 3, 2, 1].map((rating) => (\n              <div\n                key={rating}\n                className={`flex cursor-pointer items-center gap-2 rounded-md p-2 transition-colors ${\n                  filters.ratings.includes(rating)\n                    ? \"bg-gray-100 dark:bg-gray-800\"\n                    : \"hover:bg-gray-50 dark:hover:bg-gray-900\"\n                }`}\n                onClick={() => toggleRating(rating)}\n              >\n                <input\n                  type=\"checkbox\"\n                  checked={filters.ratings.includes(rating)}\n                  onChange={() => toggleRating(rating)}\n                  className=\"h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500\"\n                />\n                <StarRating\n                  value={rating}\n                  readOnly\n                  iconSize={16}\n                  maxStars={5}\n                />\n                <span className=\"text-sm text-gray-600 dark:text-gray-400\">\n                  {rating} {rating === 1 ? \"star\" : \"stars\"} & above\n                </span>\n              </div>\n            ))}\n            <div\n              className={`flex cursor-pointer items-center gap-2 rounded-md p-2 transition-colors ${\n                filters.ratings.length === 0\n                  ? \"bg-gray-100 dark:bg-gray-800\"\n                  : \"hover:bg-gray-50 dark:hover:bg-gray-900\"\n              }`}\n              onClick={() => handleFilterChange({ ratings: [] })}\n            >\n              <input\n                type=\"checkbox\"\n                checked={filters.ratings.length === 0}\n                onChange={() => handleFilterChange({ ratings: [] })}\n                className=\"h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500\"\n              />\n              <span className=\"text-sm text-gray-600 dark:text-gray-400\">\n                All ratings\n              </span>\n            </div>\n          </div>\n        </div>\n\n        <div className=\"space-y-3\">\n          <h4 className=\"text-sm font-medium text-gray-900 dark:text-white\">\n            Popular mentions\n          </h4>\n          <div className=\"flex flex-wrap gap-2\">\n            {commonKeywords.map((keyword) => (\n              <Badge\n                key={keyword}\n                variant={\n                  filters.keywords.includes(keyword) ? \"default\" : \"outline\"\n                }\n                className=\"cursor-pointer\"\n                onClick={() => toggleKeyword(keyword)}\n              >\n                {keyword}\n              </Badge>\n            ))}\n          </div>\n        </div>\n\n        <div className=\"flex justify-between border-t border-gray-200 pt-4 sm:hidden dark:border-gray-800\">\n          <Button\n            variant=\"outline\"\n            onClick={() => {\n              setFilters({\n                keywords: [],\n                ratings: [],\n                searchTerm: \"\",\n                sortBy: \"Most recent\",\n              });\n              setShowMobileFilters(false);\n            }}\n          >\n            Reset filters\n          </Button>\n          <Button onClick={() => setShowMobileFilters(false)}>\n            Show {totalReviews} reviews\n          </Button>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default Review_09;\nexport type { Review_09Props };\n",
      "path": "/components/commerce-ui/blocks/reviews/review-09.tsx",
      "target": "components/commerce-ui/review-09.tsx"
    }
  ]
}