UIPackage
Menu

Framework

Change language

Boilerplate repo

Data Product Card

blockdevops

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.

Also available for Vue ->

Installation

$npx shadcn@latest add https://uipkge.dev/r/react/data-product-card.json
Named registry:npx shadcn@latest add @uipkge-react/data-product-cardInstalls to:components/blocks/

Variants

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
productDataProductDataoptional
variant'full' | 'compact' | 'minimal'optional
classNamestringoptional

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

OutputPort
interface OutputPort {
  id: string
  name: string
  type: 'snowflake' | 'kafka' | 'graphql' | 's3' | 'postgres'
  typeLabel: string
  uri: string
  latency: string
  status: 'online' | 'degraded' | 'syncing'
}
DataProductData
interface DataProductData {
  id: string
  name: string
  version: string
  domain: string
  tier: 'Gold Tier 1' | 'Silver Tier 2' | 'Bronze Tier 3'
  description: string
  slaCompliance: number
  freshness: string
  completenessPct: number
  activeConsumersCount: number
  owner: {
    name: string
    role: string
    team: string
    avatar?: string
    fallback: string
  }
  outputPorts: OutputPort[]
  tags: string[]
  certifiedCompliance: string
  lastUpdated: string
}

Files installed (1)

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

Raw manifest:https://uipkge.dev/r/react/data-product-card.json