{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "case-study-filterable-list",
  "title": "Case Studies — Filterable List",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/case-study-filterable-list/CaseStudyFilterableList.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { ArrowUpRight } from 'lucide-vue-next'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent } from '@/components/ui/card'\nimport { Separator } from '@/components/ui/separator'\nimport { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'\n\nconst studies = [\n  {\n    company: 'Northwind Logistics',\n    industry: 'Logistics',\n    result: '5 days off the monthly close',\n    stack: 'Snowflake · dbt · Okta',\n    readTime: '8 min',\n  },\n  {\n    company: 'Halden Health',\n    industry: 'Healthcare',\n    result: '71% fewer ad-hoc data requests',\n    stack: 'BigQuery · Airflow',\n    readTime: '6 min',\n  },\n  {\n    company: 'Verity Retail Group',\n    industry: 'Retail',\n    result: '2.4pp tighter forecast band',\n    stack: 'Redshift · dbt',\n    readTime: '7 min',\n  },\n  {\n    company: 'Calder Manufacturing',\n    industry: 'Manufacturing',\n    result: '11 spreadsheets retired',\n    stack: 'Postgres · Dagster',\n    readTime: '5 min',\n  },\n  {\n    company: 'Ridgeway Freight',\n    industry: 'Logistics',\n    result: 'Per-shipment landed cost',\n    stack: 'Snowflake · Fivetran',\n    readTime: '9 min',\n  },\n  {\n    company: 'Ashford Clinics',\n    industry: 'Healthcare',\n    result: 'PHI-scoped self-serve reporting',\n    stack: 'BigQuery · Entra ID',\n    readTime: '6 min',\n  },\n]\n\nconst ALL = 'All'\nconst industries = computed(() => [ALL, ...new Set(studies.map((study) => study.industry))])\nconst active = ref(ALL)\nconst visible = computed(() =>\n  active.value === ALL ? studies : studies.filter((study) => study.industry === active.value),\n)\n\n// Single-select emits '' when the pressed chip is pressed again; fall back to\n// ALL so the list is never empty by accident.\nfunction onChange(value: unknown) {\n  active.value = typeof value === 'string' && value ? value : ALL\n}\n</script>\n\n<template>\n  <section data-slot=\"case-study-filterable-list\" class=\"bg-background\">\n    <div class=\"mx-auto max-w-4xl px-6 py-20 lg:py-28\">\n      <div class=\"flex flex-wrap items-end justify-between gap-6\">\n        <div>\n          <Badge variant=\"secondary\">Customer stories</Badge>\n          <h2 class=\"mt-4 text-3xl font-semibold tracking-tight sm:text-4xl\">Read one close to your own</h2>\n        </div>\n\n        <ToggleGroup\n          :model-value=\"active\"\n          type=\"single\"\n          variant=\"outline\"\n          size=\"sm\"\n          class=\"flex-nowrap overflow-x-auto\"\n          aria-label=\"Filter case studies by industry\"\n          @update:model-value=\"onChange\"\n        >\n          <ToggleGroupItem v-for=\"industry in industries\" :key=\"industry\" :value=\"industry\">\n            {{ industry }}\n          </ToggleGroupItem>\n        </ToggleGroup>\n      </div>\n\n      <Card class=\"mt-8\">\n        <CardContent class=\"p-2\">\n          <div v-if=\"!visible.length\" class=\"px-4 py-16 text-center\">\n            <p class=\"text-sm font-medium\">No stories in that industry yet.</p>\n            <Button variant=\"outline\" size=\"sm\" class=\"mt-4\" @click=\"active = ALL\">Show all</Button>\n          </div>\n\n          <ul v-else>\n            <li v-for=\"(study, index) in visible\" :key=\"study.company\">\n              <a\n                href=\"#\"\n                class=\"hover:bg-muted focus-visible:ring-ring group flex items-center gap-4 rounded-lg p-4 transition-colors focus-visible:ring-2 focus-visible:outline-none\"\n              >\n                <div class=\"min-w-0 flex-1\">\n                  <div class=\"flex flex-wrap items-center gap-2\">\n                    <p class=\"text-sm font-semibold\">{{ study.company }}</p>\n                    <Badge variant=\"outline\">{{ study.industry }}</Badge>\n                  </div>\n                  <p class=\"mt-1 text-sm\">{{ study.result }}</p>\n                  <p class=\"text-muted-foreground mt-1 font-mono text-xs\">{{ study.stack }}</p>\n                </div>\n                <span class=\"text-muted-foreground shrink-0 text-xs\">{{ study.readTime }}</span>\n                <ArrowUpRight\n                  class=\"text-muted-foreground size-4 shrink-0 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5\"\n                  aria-hidden=\"true\"\n                />\n              </a>\n              <Separator v-if=\"index < visible.length - 1\" />\n            </li>\n          </ul>\n        </CardContent>\n      </Card>\n    </div>\n  </section>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/CaseStudyFilterableList.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/badge.json",
    "https://uipkge.dev/r/vue/button.json",
    "https://uipkge.dev/r/vue/card.json",
    "https://uipkge.dev/r/vue/separator.json",
    "https://uipkge.dev/r/vue/toggle-group.json"
  ],
  "description": "Filterable index of customer stories with industry chips above rows carrying the headline result, stack, and read time, plus an empty state.",
  "categories": [
    "marketing"
  ],
  "deprecated": true,
  "replacedBy": "case-study"
}