{
  "$schema": "https://shadcn-vue.com/schema/registry-item.json",
  "name": "data-product-card",
  "title": "Data Product Card",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-vue/blocks/data-product-card/DataProductCard.vue",
      "content": "<script setup lang=\"ts\">\nimport type { HTMLAttributes } from 'vue'\nimport { computed, ref } from 'vue'\nimport {\n  Activity,\n  Check,\n  ChevronRight,\n  Copy,\n  Database,\n  HardDrive,\n  KeyRound,\n  Layers,\n  Radio,\n  Server,\n  Award,\n  ShieldCheck,\n  Users,\n  Zap,\n} from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\nimport { Avatar, AvatarFallback } from '@/components/ui/avatar'\nimport { Badge } from '@/components/ui/badge'\nimport { Button } from '@/components/ui/button'\nimport { Card, CardContent, CardDescription, CardFooter, CardHeader } from '@/components/ui/card'\nimport { Progress } from '@/components/ui/progress'\nimport { Separator } from '@/components/ui/separator'\n\nexport interface OutputPort {\n  id: string\n  name: string\n  type: 'snowflake' | 'kafka' | 'graphql' | 's3' | 'postgres'\n  typeLabel: string\n  uri: string\n  latency: string\n  status: 'online' | 'degraded' | 'syncing'\n}\n\nexport interface DataProductData {\n  id: string\n  name: string\n  version: string\n  domain: string\n  tier: 'Gold Tier 1' | 'Silver Tier 2' | 'Bronze Tier 3'\n  description: string\n  slaCompliance: number\n  freshness: string\n  completenessPct: number\n  activeConsumersCount: number\n  owner: {\n    name: string\n    role: string\n    team: string\n    avatar?: string\n    fallback: string\n  }\n  outputPorts: OutputPort[]\n  tags: string[]\n  certifiedCompliance: string\n  lastUpdated: string\n}\n\nconst DEFAULT_DATA_PRODUCT: DataProductData = {\n  id: 'dp-cust-360',\n  name: 'dp_customer_360_profile',\n  version: 'v3.2.0',\n  domain: 'Customer Intelligence & Growth',\n  tier: 'Gold Tier 1',\n  description:\n    'Unified customer identity graph consolidating CRM accounts, Stripe transaction volumes, and behavioral product telemetry into clean semantic marts.',\n  slaCompliance: 99.8,\n  freshness: 'Updated 8m ago · Hourly sync',\n  completenessPct: 99.4,\n  activeConsumersCount: 14,\n  owner: {\n    name: 'Marcus Vance',\n    role: 'Staff Data Architect',\n    team: 'Data Platform Squad',\n    fallback: 'MV',\n  },\n  outputPorts: [\n    {\n      id: 'port-snow',\n      name: 'analytics.gold_customer_360',\n      type: 'snowflake',\n      typeLabel: 'Snowflake Table',\n      uri: 'snowflake://analytics_prod.dw/analytics.gold_customer_360',\n      latency: '< 1h Batch SLA',\n      status: 'online',\n    },\n    {\n      id: 'port-kafka',\n      name: 'events.customer.updates.v2',\n      type: 'kafka',\n      typeLabel: 'Kafka Stream',\n      uri: 'kafka://events.kafka.internal:9092/events.customer.updates.v2',\n      latency: '< 100ms Event Time',\n      status: 'online',\n    },\n    {\n      id: 'port-gql',\n      name: 'GraphQL Query Port',\n      type: 'graphql',\n      typeLabel: 'GraphQL API',\n      uri: 'https://api.uipkge.dev/graphql?query=customer360',\n      latency: '< 45ms p99',\n      status: 'online',\n    },\n    {\n      id: 'port-s3',\n      name: 'Parquet Lake Export',\n      type: 's3',\n      typeLabel: 'Parquet S3 Lake',\n      uri: 's3://lakehouse-gold/customer_360/snapshot_latest.parquet',\n      latency: '24h Partitioned',\n      status: 'online',\n    },\n  ],\n  tags: ['#customer360', '#revenue', '#identity', '#gold_marts', '#dbt_certified'],\n  certifiedCompliance: 'SOC2 Type II & GDPR Art. 15 Compliant',\n  lastUpdated: 'Aug 21, 2026',\n}\n\ninterface Props {\n  product?: DataProductData\n  variant?: 'full' | 'compact' | 'minimal'\n  class?: HTMLAttributes['class']\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n  variant: 'full',\n})\n\nconst activeProduct = computed(() => props.product ?? DEFAULT_DATA_PRODUCT)\nconst copiedPortId = ref<string | null>(null)\nconst accessRequested = ref(false)\nconst isQueryingPort = ref(false)\nconst selectedPortId = ref<string>(DEFAULT_DATA_PRODUCT.outputPorts[0].id)\n\nconst selectedPort = computed(() => {\n  return (\n    activeProduct.value.outputPorts.find((p) => p.id === selectedPortId.value) ?? activeProduct.value.outputPorts[0]\n  )\n})\n\nfunction copyUri(port: OutputPort) {\n  if (typeof navigator !== 'undefined' && navigator.clipboard) {\n    navigator.clipboard.writeText(port.uri)\n    copiedPortId.value = port.id\n    setTimeout(() => {\n      if (copiedPortId.value === port.id) {\n        copiedPortId.value = null\n      }\n    }, 2000)\n  }\n}\n\nfunction handleRequestAccess() {\n  accessRequested.value = true\n  setTimeout(() => {\n    accessRequested.value = false\n  }, 4000)\n}\n\nfunction handleQueryPort() {\n  isQueryingPort.value = true\n  setTimeout(() => {\n    isQueryingPort.value = false\n  }, 1200)\n}\n</script>\n\n<template>\n  <div data-slot=\"data-product-card\" :class=\"cn('w-full', props.class)\">\n    <Card\n      class=\"border-border bg-card text-card-foreground hover:border-primary/40 relative flex flex-col overflow-hidden shadow-xs transition-colors duration-200 hover:shadow-sm\"\n    >\n      <CardHeader class=\"space-y-3 p-5 pb-3\">\n        <!-- Badges & Governance Ribbon -->\n        <div class=\"flex flex-wrap items-center justify-between gap-2\">\n          <div class=\"flex flex-wrap items-center gap-1.5\">\n            <!-- Domain Tag -->\n            <Badge\n              wrap\n              variant=\"outline\"\n              class=\"border-border bg-muted/30 text-muted-foreground gap-1 text-xs font-normal\"\n            >\n              <Layers class=\"text-primary size-3\" aria-hidden=\"true\" />\n              <span>{{ activeProduct.domain }}</span>\n            </Badge>\n\n            <!-- Tier Tag -->\n            <Badge\n              wrap\n              variant=\"secondary\"\n              class=\"border-success/30 bg-success/10 text-success font-mono text-xs font-semibold\"\n            >\n              <Award class=\"size-3\" aria-hidden=\"true\" />\n              {{ activeProduct.tier }}\n            </Badge>\n          </div>\n\n          <!-- Compliance Seal -->\n          <div class=\"text-muted-foreground flex items-center gap-1 text-xs\">\n            <ShieldCheck class=\"text-success size-3.5\" aria-hidden=\"true\" />\n            <span class=\"hidden font-medium sm:inline\">{{ activeProduct.certifiedCompliance }}</span>\n            <span class=\"font-medium sm:hidden\">SOC2 Certified</span>\n          </div>\n        </div>\n\n        <!-- Title & Version Lockup -->\n        <div class=\"space-y-1\">\n          <div class=\"flex flex-wrap items-center justify-between gap-2\">\n            <div class=\"flex items-baseline gap-2\">\n              <h3 class=\"text-foreground font-mono text-base font-bold tracking-tight break-all sm:text-lg\">\n                {{ activeProduct.name }}\n              </h3>\n              <span class=\"text-muted-foreground font-mono text-xs font-medium\">\n                {{ activeProduct.version }}\n              </span>\n            </div>\n\n            <Badge wrap variant=\"outline\" class=\"text-muted-foreground gap-1 text-xs font-normal\">\n              <Activity class=\"text-success size-3 animate-pulse\" aria-hidden=\"true\" />\n              <span class=\"font-mono tabular-nums\">{{ activeProduct.slaCompliance }}% SLA</span>\n            </Badge>\n          </div>\n\n          <!-- Business Value Description -->\n          <CardDescription class=\"text-muted-foreground text-xs leading-relaxed sm:text-sm\">\n            {{ activeProduct.description }}\n          </CardDescription>\n        </div>\n      </CardHeader>\n\n      <CardContent class=\"flex-1 space-y-4 p-5 pt-0\">\n        <!-- 4 Key Telemetry Metrics Strip -->\n        <div\n          class=\"border-border/80 bg-muted/20 grid grid-cols-2 gap-2 rounded-lg border p-2.5 text-xs sm:grid-cols-4 sm:p-3\"\n        >\n          <div class=\"space-y-0.5\">\n            <span class=\"text-muted-foreground block\">SLA Compliance</span>\n            <div class=\"flex items-center gap-1.5\">\n              <span class=\"text-success text-success font-mono text-sm font-bold tabular-nums\">\n                {{ activeProduct.slaCompliance }}%\n              </span>\n              <span class=\"text-success font-semibold\">✓</span>\n            </div>\n            <Progress :model-value=\"activeProduct.slaCompliance\" class=\"bg-muted h-1.5\" />\n          </div>\n\n          <div class=\"space-y-0.5\">\n            <span class=\"text-muted-foreground block\">Data Freshness</span>\n            <span\n              class=\"text-foreground block truncate font-mono text-xs font-semibold\"\n              :title=\"activeProduct.freshness\"\n            >\n              {{ activeProduct.freshness }}\n            </span>\n            <span class=\"text-muted-foreground text-xs\">SLA: &lt; 1h Target</span>\n          </div>\n\n          <div class=\"space-y-0.5\">\n            <span class=\"text-muted-foreground block\">Completeness</span>\n            <span class=\"text-foreground block font-mono text-sm font-bold tabular-nums\">\n              {{ activeProduct.completenessPct }}%\n            </span>\n            <span class=\"text-muted-foreground text-xs\">0.6% Max Nulls</span>\n          </div>\n\n          <div class=\"space-y-0.5\">\n            <span class=\"text-muted-foreground block\">Active Consumers</span>\n            <div class=\"flex items-center gap-1\">\n              <Users class=\"text-primary size-3.5\" aria-hidden=\"true\" />\n              <span class=\"text-foreground font-mono text-sm font-bold tabular-nums\">\n                {{ activeProduct.activeConsumersCount }} Apps\n              </span>\n            </div>\n            <span class=\"text-muted-foreground text-xs\">Certified Live Ports</span>\n          </div>\n        </div>\n\n        <!-- Multi-Modal Output Ports Selector -->\n        <div class=\"space-y-2\">\n          <div class=\"flex items-center justify-between\">\n            <span class=\"text-foreground text-xs font-semibold tracking-wide uppercase\">\n              Certified Multi-Modal Output Ports ({{ activeProduct.outputPorts.length }})\n            </span>\n            <span class=\"text-muted-foreground text-xs\">Click port to inspect URI</span>\n          </div>\n\n          <div class=\"grid grid-cols-1 gap-2 sm:grid-cols-2\">\n            <button\n              v-for=\"port in activeProduct.outputPorts\"\n              :key=\"port.id\"\n              type=\"button\"\n              :class=\"[\n                'group flex items-start justify-between rounded-lg border p-2.5 text-left transition-[colors,transform] duration-150 active:scale-[0.98]',\n                selectedPortId === port.id\n                  ? 'border-primary/50 bg-primary/5 ring-primary/30 ring-1'\n                  : 'border-border bg-card hover:border-primary/25 hover:bg-muted/40',\n              ]\"\n              @click=\"selectedPortId = port.id\"\n            >\n              <div class=\"flex min-w-0 items-start gap-2.5\">\n                <div\n                  class=\"bg-muted text-foreground mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-md\"\n                >\n                  <Database v-if=\"port.type === 'snowflake'\" class=\"text-info size-3.5\" aria-hidden=\"true\" />\n                  <Radio v-else-if=\"port.type === 'kafka'\" class=\"text-warning size-3.5\" aria-hidden=\"true\" />\n                  <Server v-else-if=\"port.type === 'graphql'\" class=\"text-chart-1 size-3.5\" aria-hidden=\"true\" />\n                  <HardDrive v-else class=\"text-success size-3.5\" aria-hidden=\"true\" />\n                </div>\n                <div class=\"min-w-0 space-y-0.5\">\n                  <div class=\"flex items-center gap-1.5\">\n                    <span class=\"text-foreground truncate text-xs font-semibold\">{{ port.typeLabel }}</span>\n                    <span class=\"bg-success size-1.5 shrink-0 rounded-full\" aria-hidden=\"true\" />\n                  </div>\n                  <p class=\"text-muted-foreground truncate font-mono text-xs\">{{ port.name }}</p>\n                  <p class=\"text-muted-foreground text-xs font-normal\">{{ port.latency }}</p>\n                </div>\n              </div>\n\n              <div class=\"shrink-0 pt-0.5 pl-1\">\n                <span\n                  v-if=\"selectedPortId === port.id\"\n                  class=\"bg-primary text-primary-foreground flex size-4 items-center justify-center rounded-full text-xs font-bold\"\n                >\n                  ✓\n                </span>\n                <ChevronRight\n                  v-else\n                  class=\"text-muted-foreground/40 group-hover:text-foreground size-4\"\n                  aria-hidden=\"true\"\n                />\n              </div>\n            </button>\n          </div>\n        </div>\n\n        <!-- Active Selected Port URI Inspector Box -->\n        <div\n          class=\"border-border bg-muted/40 flex flex-col gap-2 rounded-lg border p-3 text-xs sm:flex-row sm:items-center sm:justify-between\"\n        >\n          <div class=\"min-w-0 space-y-0.5\">\n            <span class=\"text-muted-foreground text-xs font-medium\">Selected Endpoint URI:</span>\n            <p class=\"text-foreground truncate font-mono text-xs font-medium select-all\">\n              {{ selectedPort.uri }}\n            </p>\n          </div>\n\n          <div class=\"flex shrink-0 items-center gap-1.5 pt-1 sm:pt-0\">\n            <Button\n              variant=\"outline\"\n              size=\"sm\"\n              class=\"h-7 gap-1 px-2 text-xs transition-transform duration-150 active:scale-95\"\n              @click=\"copyUri(selectedPort)\"\n            >\n              <Check v-if=\"copiedPortId === selectedPort.id\" class=\"text-success size-3\" aria-hidden=\"true\" />\n              <Copy v-else class=\"size-3\" aria-hidden=\"true\" />\n              <span>{{ copiedPortId === selectedPort.id ? 'Copied' : 'Copy URI' }}</span>\n            </Button>\n          </div>\n        </div>\n\n        <!-- Product Owner & Tags Bar -->\n        <div class=\"flex flex-wrap items-center justify-between gap-3 pt-1\">\n          <!-- Owner Profile -->\n          <div class=\"flex items-center gap-2\">\n            <Avatar class=\"border-border size-7 border\">\n              <AvatarFallback class=\"bg-primary/10 text-primary text-xs font-semibold\">\n                {{ activeProduct.owner.fallback }}\n              </AvatarFallback>\n            </Avatar>\n            <div>\n              <p class=\"text-foreground text-xs leading-none font-medium\">\n                {{ activeProduct.owner.name }}\n              </p>\n              <p class=\"text-muted-foreground text-xs\">\n                {{ activeProduct.owner.team }}\n              </p>\n            </div>\n          </div>\n\n          <!-- Tags -->\n          <div class=\"flex flex-wrap items-center gap-1\">\n            <Badge\n              wrap\n              v-for=\"tag in activeProduct.tags.slice(0, 3)\"\n              :key=\"tag\"\n              variant=\"outline\"\n              class=\"text-muted-foreground border-border/70 text-xs font-normal\"\n            >\n              {{ tag }}\n            </Badge>\n          </div>\n        </div>\n      </CardContent>\n\n      <Separator />\n\n      <!-- Footer Action Controls -->\n      <CardFooter class=\"flex flex-wrap items-center justify-between gap-2 p-4 sm:px-5\">\n        <div class=\"text-muted-foreground text-xs\">\n          Last contract verified: <span class=\"text-foreground font-medium\">{{ activeProduct.lastUpdated }}</span>\n        </div>\n\n        <div class=\"flex items-center gap-2\">\n          <Button\n            variant=\"outline\"\n            size=\"sm\"\n            class=\"h-8 gap-1.5 text-xs font-medium active:scale-[0.98]\"\n            :disabled=\"isQueryingPort\"\n            @click=\"handleQueryPort\"\n          >\n            <Zap v-if=\"!isQueryingPort\" class=\"text-warning size-3.5\" aria-hidden=\"true\" />\n            <span v-if=\"isQueryingPort\">Running Test Query...</span>\n            <span v-else>Query Port</span>\n          </Button>\n\n          <Button\n            size=\"sm\"\n            class=\"h-8 gap-1.5 text-xs font-semibold shadow-xs active:scale-[0.98]\"\n            :disabled=\"accessRequested\"\n            @click=\"handleRequestAccess\"\n          >\n            <KeyRound class=\"size-3.5\" aria-hidden=\"true\" />\n            <span>{{ accessRequested ? 'Access Requested ✓' : 'Request Access' }}</span>\n          </Button>\n        </div>\n      </CardFooter>\n    </Card>\n  </div>\n</template>\n",
      "type": "registry:block",
      "target": "~/app/components/blocks/DataProductCard.vue"
    }
  ],
  "dependencies": [
    "lucide-vue-next"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/vue/avatar.json",
    "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/progress.json",
    "https://uipkge.dev/r/vue/separator.json"
  ],
  "description": "Data Mesh architectural Data Product Card (DPC) featuring SLA compliance health, multi-modal output ports (Snowflake, Kafka, GraphQL, Parquet S3), domain ownership, consumer counts, and access request triggers.",
  "categories": [
    "devops",
    "app",
    "data",
    "analytics"
  ]
}