UIPackage
Menu

Framework

Change language

Boilerplate repo

Triage Queue Dashboard

blockhealthcare

Emergency Department (ED) and Urgent Care patient acuity triage queue and room allocation board: live unit header, 4 ESI overview cards with target wait thresholds, searchable patient queue table with vitals snapshot, assigned room, attending care team, and quick clinical actions.

Also available for React ->

Installation

$npx shadcn-vue@latest add https://uipkge.dev/r/vue/triage-queue-dashboard.json
Named registry:npx shadcn-vue@latest add @uipkge/triage-queue-dashboardInstalls to:app/components/blocks/

Variants

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
classHTMLAttributes['class']optional
unitNamestring'Level 1 Trauma Center · Bay 4'optional
facilityNamestring'St. Jude Metropolitan Academic Medical Center'optional

Schema

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

PatientVitals
interface PatientVitals {
  bp: string
  hr: number
  spo2: number
  temp: string
  respRate: number
}
Caregiver
interface Caregiver {
  name: string
  role: 'RN' | 'MD' | 'PA' | 'NP'
  initials: string
  avatar?: string
}
TriagePatient
interface TriagePatient {
  id: string
  mrn: string
  name: string
  age: number
  gender: 'M' | 'F' | 'Other'
  acuity: EsiLevel
  acuityCode: string
  acuityLabel: string
  chiefComplaint: string
  symptoms: string
  waitTime: string
  waitMinutes: number
  vitals: PatientVitals
  assignedRoom: string
  roomType: 'trauma' | 'acute' | 'fast_track' | 'waiting'
  primaryNurse: Caregiver
  attendingPhysician: Caregiver
  clinicalStatus: string
}
AcuitySummaryCard
interface AcuitySummaryCard {
  level: EsiLevel
  title: string
  subtitle: string
  activeCount: number
  targetWait: string
  targetWindow: string
  badgeVariantClass: string
  indicatorDot?: string
  pulse: boolean
  description: string
}

Files installed (1)

  • app/components/blocks/TriageQueueDashboard.vue31.7 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { computed, ref } from 'vue'
    import { Bed, Clock, DoorOpen, HeartPulse, MoreHorizontal, Plus, RefreshCw, Search, Zap } from 'lucide-vue-next'
    import { cn } from '@/lib/utils'
    import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
    import { Badge } from '@/components/ui/badge'
    import { Button } from '@/components/ui/button'
    import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
    import {
      DropdownMenu,
      DropdownMenuContent,
      DropdownMenuItem,
      DropdownMenuLabel,
      DropdownMenuSeparator,
      DropdownMenuTrigger,
    } from '@/components/ui/dropdown-menu'
    import { Input } from '@/components/ui/input'
    import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
    import { Separator } from '@/components/ui/separator'
    import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
    
    export type EsiLevel = 'esi-1' | 'esi-2' | 'esi-3' | 'esi-4'
    
    export interface PatientVitals {
      bp: string
      hr: number
      spo2: number
      temp: string
      respRate: number
    }
    
    export interface Caregiver {
      name: string
      role: 'RN' | 'MD' | 'PA' | 'NP'
      initials: string
      avatar?: string
    }
    
    export interface TriagePatient {
      id: string
      mrn: string
      name: string
      age: number
      gender: 'M' | 'F' | 'Other'
      acuity: EsiLevel
      acuityCode: string
      acuityLabel: string
      chiefComplaint: string
      symptoms: string
      waitTime: string
      waitMinutes: number
      vitals: PatientVitals
      assignedRoom: string
      roomType: 'trauma' | 'acute' | 'fast_track' | 'waiting'
      primaryNurse: Caregiver
      attendingPhysician: Caregiver
      clinicalStatus: string
    }
    
    interface AcuitySummaryCard {
      level: EsiLevel
      title: string
      subtitle: string
      activeCount: number
      targetWait: string
      targetWindow: string
      badgeVariantClass: string
      indicatorDot?: string
      pulse: boolean
      description: string
    }
    
    const props = withDefaults(
      defineProps<{
        class?: HTMLAttributes['class']
        unitName?: string
        facilityName?: string
      }>(),
      {
        unitName: 'Level 1 Trauma Center · Bay 4',
        facilityName: 'St. Jude Metropolitan Academic Medical Center',
      },
    )
    
    const search = ref('')
    const acuityFilter = ref<string>('all')
    const locationFilter = ref<string>('all')
    const sortBy = ref<'wait-desc' | 'wait-asc' | 'acuity'>('acuity')
    
    const acuityCards: AcuitySummaryCard[] = [
      {
        level: 'esi-1',
        title: 'ESI 1 · Resuscitation',
        subtitle: 'Immediate Life Threat',
        activeCount: 1,
        targetWait: 'Immediate',
        targetWindow: '0 min wait',
        badgeVariantClass: 'border-destructive/30 bg-destructive/10 text-destructive',
        indicatorDot: 'bg-destructive',
        pulse: true,
        description: 'Hemodynamic instability, active airway compromise',
      },
      {
        level: 'esi-2',
        title: 'ESI 2 · Emergent',
        subtitle: 'High Risk / Severe Pain',
        activeCount: 3,
        targetWait: '< 15m wait',
        targetWindow: 'Max 15 min',
        badgeVariantClass: 'border-orange-500/30 bg-orange-500/10 text-orange-700 dark:text-orange-400',
        indicatorDot: 'bg-orange-500',
        pulse: false,
        description: 'Confused, lethargic, severe respiratory distress',
      },
      {
        level: 'esi-3',
        title: 'ESI 3 · Urgent',
        subtitle: 'Multiple Resources Needed',
        activeCount: 8,
        targetWait: '< 30m wait',
        targetWindow: 'Max 30 min',
        badgeVariantClass: 'border-warning/30 bg-warning/10 text-warning',
        indicatorDot: 'bg-warning',
        pulse: false,
        description: 'Stable vitals, 2+ diagnostic or lab orders needed',
      },
      {
        level: 'esi-4',
        title: 'ESI 4/5 · Non-Urgent',
        subtitle: 'Low Resource / Fast Track',
        activeCount: 6,
        targetWait: '< 60m wait',
        targetWindow: 'Max 60 min',
        badgeVariantClass: 'border-success/30 bg-success/10 text-success',
        indicatorDot: 'bg-success',
        pulse: false,
        description: 'Simple lacerations, routine medication re-eval',
      },
    ]
    
    const patients: TriagePatient[] = [
      {
        id: 'pt-1',
        mrn: 'MRN-94021',
        name: 'James Wilson',
        age: 62,
        gender: 'M',
        acuity: 'esi-1',
        acuityCode: 'ESI 1',
        acuityLabel: 'ESI 1 - Resuscitation',
        chiefComplaint: 'Severe Crushing Chest Pain',
        symptoms: 'Diaphoresis, radiating left jaw pain, dyspnea. STAT STEMI protocol initiated.',
        waitTime: '2 min',
        waitMinutes: 2,
        vitals: {
          bp: '88/54',
          hr: 124,
          spo2: 91,
          temp: '98.4°F',
          respRate: 28,
        },
        assignedRoom: 'Trauma Bay 2',
        roomType: 'trauma',
        primaryNurse: {
          name: 'Sarah Jenkins, RN',
          role: 'RN',
          initials: 'SJ',
          avatar: 'https://images.unsplash.com/photo-1559839734-2b71ea197ec2?w=120&auto=format&fit=crop&q=80',
        },
        attendingPhysician: {
          name: 'Dr. Marcus Chen, MD',
          role: 'MD',
          initials: 'MC',
          avatar: 'https://images.unsplash.com/photo-1622253692010-333f2da6031d?w=120&auto=format&fit=crop&q=80',
        },
        clinicalStatus: 'Cath Lab Alert · In Bay',
      },
      {
        id: 'pt-2',
        mrn: 'MRN-88319',
        name: 'Maria Garcia',
        age: 34,
        gender: 'F',
        acuity: 'esi-2',
        acuityCode: 'ESI 2',
        acuityLabel: 'ESI 2 - Emergent',
        chiefComplaint: 'Compound Tibia Fracture',
        symptoms: 'Post-motorcycle collision, visible bone deformity, uncontrolled bleeding controlled by tourniquet.',
        waitTime: '9 min',
        waitMinutes: 9,
        vitals: {
          bp: '138/86',
          hr: 106,
          spo2: 97,
          temp: '99.1°F',
          respRate: 22,
        },
        assignedRoom: 'Trauma Bay 4',
        roomType: 'trauma',
        primaryNurse: {
          name: 'Kevin Vance, RN',
          role: 'RN',
          initials: 'KV',
          avatar: 'https://images.unsplash.com/photo-1537368910025-700350fe46c7?w=120&auto=format&fit=crop&q=80',
        },
        attendingPhysician: {
          name: 'Dr. Lisa Thorne, MD',
          role: 'MD',
          initials: 'LT',
          avatar: 'https://images.unsplash.com/photo-1594824813637-e54e44e26210?w=120&auto=format&fit=crop&q=80',
        },
        clinicalStatus: 'Ortho Paged · IV Analgesia',
      },
      {
        id: 'pt-3',
        mrn: 'MRN-72915',
        name: 'Noah Miller',
        age: 8,
        gender: 'M',
        acuity: 'esi-2',
        acuityCode: 'ESI 2',
        acuityLabel: 'ESI 2 - Emergent',
        chiefComplaint: 'Acute Asthma Exacerbation',
        symptoms: 'Inspiratory and expiratory stridor, intercostal retractions, refractory to home albuterol.',
        waitTime: '14 min',
        waitMinutes: 14,
        vitals: {
          bp: '104/68',
          hr: 132,
          spo2: 92,
          temp: '99.8°F',
          respRate: 34,
        },
        assignedRoom: 'Bed 06 (Peds Acute)',
        roomType: 'acute',
        primaryNurse: {
          name: 'Angela Ray, RN',
          role: 'RN',
          initials: 'AR',
          avatar: 'https://images.unsplash.com/photo-1582750433449-648ed127bb54?w=120&auto=format&fit=crop&q=80',
        },
        attendingPhysician: {
          name: 'Dr. Marcus Chen, MD',
          role: 'MD',
          initials: 'MC',
          avatar: 'https://images.unsplash.com/photo-1622253692010-333f2da6031d?w=120&auto=format&fit=crop&q=80',
        },
        clinicalStatus: 'Duoneb #2 · Continuous O2',
      },
      {
        id: 'pt-4',
        mrn: 'MRN-51829',
        name: 'Sarah Lee',
        age: 45,
        gender: 'F',
        acuity: 'esi-3',
        acuityCode: 'ESI 3',
        acuityLabel: 'ESI 3 - Urgent',
        chiefComplaint: 'Right Lower Quadrant Abdominal Pain',
        symptoms: 'Severe pain x 14h, focal McBurney point tenderness, nausea, low-grade fever.',
        waitTime: '28 min',
        waitMinutes: 28,
        vitals: {
          bp: '126/82',
          hr: 92,
          spo2: 98,
          temp: '101.4°F',
          respRate: 18,
        },
        assignedRoom: 'Bed 14',
        roomType: 'acute',
        primaryNurse: {
          name: 'David Park, RN',
          role: 'RN',
          initials: 'DP',
          avatar: 'https://images.unsplash.com/photo-1612349317150-e413f6a5b16d?w=120&auto=format&fit=crop&q=80',
        },
        attendingPhysician: {
          name: 'Dr. Sophia Adams, MD',
          role: 'MD',
          initials: 'SA',
          avatar: 'https://images.unsplash.com/photo-1559839734-2b71ea197ec2?w=120&auto=format&fit=crop&q=80',
        },
        clinicalStatus: 'CT Scan Queued · NPO',
      },
      {
        id: 'pt-5',
        mrn: 'MRN-63910',
        name: 'Robert Chen',
        age: 58,
        gender: 'M',
        acuity: 'esi-3',
        acuityCode: 'ESI 3',
        acuityLabel: 'ESI 3 - Urgent',
        chiefComplaint: 'Suspected Renal Colic & Flank Pain',
        symptoms: 'Sudden onset severe right flank pain radiating to groin, gross hematuria, pain score 8/10.',
        waitTime: '42 min',
        waitMinutes: 42,
        vitals: {
          bp: '146/94',
          hr: 88,
          spo2: 99,
          temp: '98.4°F',
          respRate: 16,
        },
        assignedRoom: 'Bed 18',
        roomType: 'acute',
        primaryNurse: {
          name: 'Rachel Torres, RN',
          role: 'RN',
          initials: 'RT',
          avatar: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=120&auto=format&fit=crop&q=80',
        },
        attendingPhysician: {
          name: 'Dr. Sophia Adams, MD',
          role: 'MD',
          initials: 'SA',
          avatar: 'https://images.unsplash.com/photo-1559839734-2b71ea197ec2?w=120&auto=format&fit=crop&q=80',
        },
        clinicalStatus: 'Ultrasound Ordered · IV Fluids',
      },
      {
        id: 'pt-6',
        mrn: 'MRN-90423',
        name: 'Marcus Vance',
        age: 22,
        gender: 'M',
        acuity: 'esi-4',
        acuityCode: 'ESI 4',
        acuityLabel: 'ESI 4 - Non-Urgent',
        chiefComplaint: 'Forearm Deep Glass Laceration',
        symptoms: '4cm clean linear laceration, hemostasis achieved, neurovascular sensation intact distally.',
        waitTime: '54 min',
        waitMinutes: 54,
        vitals: {
          bp: '120/78',
          hr: 74,
          spo2: 100,
          temp: '98.6°F',
          respRate: 14,
        },
        assignedRoom: 'Fast Track 02',
        roomType: 'fast_track',
        primaryNurse: {
          name: 'Kevin Vance, RN',
          role: 'RN',
          initials: 'KV',
          avatar: 'https://images.unsplash.com/photo-1537368910025-700350fe46c7?w=120&auto=format&fit=crop&q=80',
        },
        attendingPhysician: {
          name: 'Dr. Emily Watson, MD',
          role: 'MD',
          initials: 'EW',
          avatar: 'https://images.unsplash.com/photo-1594824813637-e54e44e26210?w=120&auto=format&fit=crop&q=80',
        },
        clinicalStatus: 'Wound Irrigated · Suture Tray Ready',
      },
    ]
    
    const filteredPatients = computed(() => {
      return patients
        .filter((pt) => {
          // Search filter
          if (search.value.trim()) {
            const query = search.value.toLowerCase()
            const matchesName = pt.name.toLowerCase().includes(query)
            const matchesMrn = pt.mrn.toLowerCase().includes(query)
            const matchesComplaint = pt.chiefComplaint.toLowerCase().includes(query)
            const matchesRoom = pt.assignedRoom.toLowerCase().includes(query)
            if (!matchesName && !matchesMrn && !matchesComplaint && !matchesRoom) return false
          }
    
          // Acuity filter
          if (acuityFilter.value !== 'all') {
            if (pt.acuity !== acuityFilter.value) return false
          }
    
          // Location filter
          if (locationFilter.value !== 'all') {
            if (pt.roomType !== locationFilter.value) return false
          }
    
          return true
        })
        .sort((a, b) => {
          if (sortBy.value === 'wait-desc') return b.waitMinutes - a.waitMinutes
          if (sortBy.value === 'wait-asc') return a.waitMinutes - b.waitMinutes
          // Acuity default sorting: ESI 1 -> ESI 2 -> ESI 3 -> ESI 4
          const order: Record<EsiLevel, number> = { 'esi-1': 1, 'esi-2': 2, 'esi-3': 3, 'esi-4': 4 }
          return order[a.acuity] - order[b.acuity]
        })
    })
    
    function handleAcuityCardClick(level: EsiLevel) {
      if (acuityFilter.value === level) {
        acuityFilter.value = 'all'
      } else {
        acuityFilter.value = level
      }
    }
    
    function resetFilters() {
      search.value = ''
      acuityFilter.value = 'all'
      locationFilter.value = 'all'
      sortBy.value = 'acuity'
    }
    </script>
    
    <template>
      <div
        data-slot="triage-queue-dashboard"
        :class="cn('bg-background text-foreground flex w-full flex-col gap-6 font-sans', props.class)"
      >
        <!-- Live ED Header -->
        <header class="bg-card border-border rounded-xl border p-5 shadow-xs sm:p-6">
          <div class="flex flex-col gap-5 lg:flex-row lg:items-center lg:justify-between">
            <!-- Title & Operational Meta -->
            <div class="space-y-1.5">
              <div class="flex flex-wrap items-center gap-2.5">
                <div class="bg-primary/10 text-primary flex size-9 items-center justify-center rounded-lg">
                  <HeartPulse class="size-5" />
                </div>
                <div>
                  <h1 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl">
                    Emergency Department Triage Queue
                  </h1>
                  <p class="text-muted-foreground text-xs font-medium sm:text-sm">
                    {{ props.facilityName }}
                  </p>
                </div>
              </div>
    
              <div class="flex flex-wrap items-center gap-2 pt-1 text-xs">
                <Badge variant="outline" class="border-border bg-muted/50 text-foreground gap-1.5 font-medium">
                  <Bed class="text-primary size-3.5" />
                  {{ props.unitName }}
                </Badge>
    
                <Badge variant="outline" class="border-success/30 bg-success/10 text-success gap-1.5 font-medium">
                  <span class="bg-success size-1.5 animate-pulse rounded-full" />
                  18 In Unit · 4 Awaiting Triage
                </Badge>
    
                <span class="text-muted-foreground hidden items-center gap-1 sm:inline-flex">
                  <Clock class="size-3" />
                  Live Sync · Updated seconds ago
                </span>
              </div>
            </div>
    
            <!-- Primary Actions -->
            <div class="flex flex-wrap items-center gap-2.5">
              <Button variant="outline" size="sm" class="gap-1.5 text-xs font-medium">
                <RefreshCw class="text-muted-foreground size-3.5" />
                Refresh Queue
              </Button>
    
              <Button size="sm" class="gap-1.5 text-xs font-semibold shadow-xs">
                <Plus class="size-4" />
                Admit New Patient
              </Button>
            </div>
          </div>
        </header>
    
        <!-- 4 Acuity Overview Cards (ESI 1 to ESI 4/5) -->
        <section aria-label="Acuity Overview Cards" class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
          <Card
            v-for="card in acuityCards"
            :key="card.level"
            :class="
              cn(
                'border-border relative cursor-pointer transition-colors duration-150 select-none hover:shadow-sm',
                acuityFilter === card.level && 'ring-primary/40 bg-accent/40 ring-2',
              )
            "
            @click="handleAcuityCardClick(card.level)"
          >
            <CardContent class="p-4 sm:p-5">
              <div class="flex items-start justify-between gap-2">
                <div class="space-y-1">
                  <div class="flex items-center gap-1.5">
                    <span :class="cn('size-2 shrink-0 rounded-full', card.indicatorDot, card.pulse && 'animate-pulse')" />
                    <span class="text-muted-foreground text-xs font-medium tracking-wider uppercase">
                      {{ card.subtitle }}
                    </span>
                  </div>
                  <h2 class="text-foreground text-sm font-semibold sm:text-base">
                    {{ card.title }}
                  </h2>
                </div>
    
                <Badge variant="outline" :class="cn('gap-1 px-2 py-0.5 text-xs font-semibold', card.badgeVariantClass)">
                  <span v-if="card.pulse" class="bg-destructive size-1.5 animate-pulse rounded-full" />
                  {{ card.targetWait }}
                </Badge>
              </div>
    
              <div class="mt-4 flex flex-wrap items-baseline justify-between gap-x-2 gap-y-0.5">
                <div class="flex items-baseline gap-1.5">
                  <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums sm:text-3xl">
                    {{ card.activeCount }}
                  </span>
                  <span class="text-muted-foreground text-xs font-medium">active in queue</span>
                </div>
    
                <span class="text-muted-foreground text-xs tabular-nums">
                  {{ card.targetWindow }}
                </span>
              </div>
    
              <p class="text-muted-foreground mt-2 text-xs leading-relaxed">
                {{ card.description }}
              </p>
            </CardContent>
          </Card>
        </section>
    
        <!-- Patient Queue Table Card -->
        <Card class="border-border shadow-xs">
          <CardHeader class="border-border border-b p-4 sm:p-5">
            <div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
              <!-- Title & Active Filter Summary -->
              <div>
                <div class="flex items-center gap-2">
                  <CardTitle class="text-foreground text-base font-semibold sm:text-lg">
                    Patient Triage & Allocation Board
                  </CardTitle>
                  <Badge variant="secondary" class="font-mono text-xs tabular-nums">
                    {{ filteredPatients.length }} Patients
                  </Badge>
                </div>
                <CardDescription class="text-muted-foreground mt-0.5 text-xs">
                  Continuous monitoring of emergency severity index, vital signs, and room throughput.
                </CardDescription>
              </div>
    
              <!-- Controls: Search & Filters -->
              <div class="flex flex-wrap items-center gap-2.5">
                <!-- Search Field -->
                <div class="relative w-full min-w-[200px] sm:w-60">
                  <Search class="text-muted-foreground absolute top-1/2 left-2.5 size-4 -translate-y-1/2" />
                  <Input
                    v-model="search"
                    type="text"
                    placeholder="Search patient, MRN, bay..."
                    class="h-8 pl-8 text-xs sm:text-sm"
                  />
                </div>
    
                <!-- Acuity Level Filter -->
                <div class="w-36 sm:w-40">
                  <Select v-model="acuityFilter">
                    <SelectTrigger class="h-8 text-xs">
                      <SelectValue placeholder="All Acuities" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="all">All Acuities</SelectItem>
                      <SelectItem value="esi-1">ESI 1 - Resuscitation</SelectItem>
                      <SelectItem value="esi-2">ESI 2 - Emergent</SelectItem>
                      <SelectItem value="esi-3">ESI 3 - Urgent</SelectItem>
                      <SelectItem value="esi-4">ESI 4/5 - Non-Urgent</SelectItem>
                    </SelectContent>
                  </Select>
                </div>
    
                <!-- Location Filter -->
                <div class="w-32 sm:w-36">
                  <Select v-model="locationFilter">
                    <SelectTrigger class="h-8 text-xs">
                      <SelectValue placeholder="All Locations" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="all">All Locations</SelectItem>
                      <SelectItem value="trauma">Trauma Bays</SelectItem>
                      <SelectItem value="acute">Acute Beds</SelectItem>
                      <SelectItem value="fast_track">Fast Track</SelectItem>
                    </SelectContent>
                  </Select>
                </div>
    
                <!-- Reset Button -->
                <Button
                  v-if="search || acuityFilter !== 'all' || locationFilter !== 'all'"
                  variant="ghost"
                  size="sm"
                  class="h-8 px-2 text-xs"
                  @click="resetFilters"
                >
                  Clear
                </Button>
              </div>
            </div>
          </CardHeader>
    
          <CardContent class="p-0">
            <div class="overflow-x-auto">
              <Table>
                <TableHeader>
                  <TableRow class="hover:bg-transparent">
                    <TableHead class="w-[140px] text-xs font-semibold">Acuity (ESI)</TableHead>
                    <TableHead class="min-w-[200px] text-xs font-semibold">Patient Demographics</TableHead>
                    <TableHead class="min-w-[240px] text-xs font-semibold">Chief Complaint & Symptoms</TableHead>
                    <TableHead class="w-[110px] text-xs font-semibold">Wait Time</TableHead>
                    <TableHead class="min-w-[190px] text-xs font-semibold">Vitals Snapshot</TableHead>
                    <TableHead class="w-[150px] text-xs font-semibold">Bed / Room</TableHead>
                    <TableHead class="min-w-[160px] text-xs font-semibold">Attending Staff</TableHead>
                    <TableHead class="w-[60px] text-right text-xs font-semibold">
                      <span class="sr-only">Actions</span>
                    </TableHead>
                  </TableRow>
                </TableHeader>
    
                <TableBody>
                  <!-- Empty State -->
                  <TableRow v-if="filteredPatients.length === 0">
                    <TableCell colspan="8" class="h-48 text-center">
                      <div class="flex flex-col items-center justify-center gap-2">
                        <div class="bg-muted flex size-10 items-center justify-center rounded-full">
                          <Search class="text-muted-foreground size-5" />
                        </div>
                        <p class="text-foreground text-sm font-semibold">No patients match current filters</p>
                        <p class="text-muted-foreground text-xs">
                          Try clearing your search query or selecting "All Acuities".
                        </p>
                        <Button variant="outline" size="sm" class="mt-2 text-xs" @click="resetFilters">
                          Reset Filters
                        </Button>
                      </div>
                    </TableCell>
                  </TableRow>
    
                  <!-- Patient Queue Rows -->
                  <TableRow
                    v-for="pt in filteredPatients"
                    :key="pt.id"
                    :class="
                      cn('transition-colors', pt.acuity === 'esi-1' && 'bg-destructive/[0.03] dark:bg-destructive/10]')
                    "
                  >
                    <!-- Acuity ESI Badge -->
                    <TableCell class="align-top font-medium">
                      <div class="flex flex-col items-start gap-1">
                        <Badge
                          variant="outline"
                          :class="
                            cn(
                              'gap-1.5 px-2.5 py-1 text-xs font-semibold whitespace-nowrap',
                              pt.acuity === 'esi-1' && 'border-destructive/30 bg-destructive/10 text-destructive',
                              pt.acuity === 'esi-2' &&
                                'border-orange-500/30 bg-orange-500/10 text-orange-700 dark:text-orange-400',
                              pt.acuity === 'esi-3' && 'border-warning/30 bg-warning/10 text-warning',
                              pt.acuity === 'esi-4' && 'border-success/30 bg-success/10 text-success',
                            )
                          "
                        >
                          <span v-if="pt.acuity === 'esi-1'" class="bg-destructive size-1.5 animate-pulse rounded-full" />
                          {{ pt.acuityCode }}
                        </Badge>
                        <span class="text-muted-foreground text-xs font-medium">
                          {{
                            pt.acuity === 'esi-1'
                              ? 'Resuscitation'
                              : pt.acuity === 'esi-2'
                                ? 'Emergent'
                                : pt.acuity === 'esi-3'
                                  ? 'Urgent'
                                  : 'Non-Urgent'
                          }}
                        </span>
                      </div>
                    </TableCell>
    
                    <!-- Patient Demographics -->
                    <TableCell class="align-top">
                      <div class="space-y-1">
                        <div class="flex items-center gap-1.5">
                          <span class="text-foreground text-sm font-semibold tracking-tight">
                            {{ pt.name }}
                          </span>
                          <span class="text-muted-foreground text-xs font-medium"> ({{ pt.age }}{{ pt.gender }}) </span>
                        </div>
                        <div class="text-muted-foreground flex items-center gap-2 font-mono text-xs">
                          <span>{{ pt.mrn }}</span>
                        </div>
                      </div>
                    </TableCell>
    
                    <!-- Chief Complaint & Symptoms -->
                    <TableCell class="align-top">
                      <div class="space-y-1">
                        <p class="text-foreground text-xs font-semibold">
                          {{ pt.chiefComplaint }}
                        </p>
                        <p class="text-muted-foreground text-xs leading-relaxed">
                          {{ pt.symptoms }}
                        </p>
                      </div>
                    </TableCell>
    
                    <!-- Wait Time Counter -->
                    <TableCell class="align-top">
                      <div class="flex items-center gap-1.5 pt-0.5">
                        <Clock
                          :class="cn('size-3.5 shrink-0', pt.waitMinutes > 30 ? 'text-warning' : 'text-muted-foreground')"
                        />
                        <span
                          :class="
                            cn(
                              'text-xs font-semibold tabular-nums',
                              pt.waitMinutes > 40 ? 'text-warning' : 'text-foreground',
                            )
                          "
                        >
                          {{ pt.waitTime }}
                        </span>
                      </div>
                    </TableCell>
    
                    <!-- Vitals Snapshot -->
                    <TableCell class="align-top">
                      <div class="grid grid-cols-2 gap-x-3 gap-y-1 text-xs">
                        <div class="flex items-center justify-between gap-1 font-mono">
                          <span class="text-muted-foreground">BP:</span>
                          <span
                            :class="
                              cn(
                                'font-semibold tabular-nums',
                                pt.acuity === 'esi-1' ? 'text-destructive' : 'text-foreground',
                              )
                            "
                          >
                            {{ pt.vitals.bp }}
                          </span>
                        </div>
    
                        <div class="flex items-center justify-between gap-1 font-mono">
                          <span class="text-muted-foreground">HR:</span>
                          <span
                            :class="
                              cn('font-semibold tabular-nums', pt.vitals.hr > 110 ? 'text-destructive' : 'text-foreground')
                            "
                          >
                            {{ pt.vitals.hr }} bpm
                          </span>
                        </div>
    
                        <div class="flex items-center justify-between gap-1 font-mono">
                          <span class="text-muted-foreground">SpO2:</span>
                          <span
                            :class="
                              cn('font-semibold tabular-nums', pt.vitals.spo2 < 95 ? 'text-destructive' : 'text-foreground')
                            "
                          >
                            {{ pt.vitals.spo2 }}%
                          </span>
                        </div>
    
                        <div class="flex items-center justify-between gap-1 font-mono">
                          <span class="text-muted-foreground">Temp:</span>
                          <span class="text-foreground font-semibold tabular-nums">
                            {{ pt.vitals.temp }}
                          </span>
                        </div>
                      </div>
                    </TableCell>
    
                    <!-- Assigned Bed / Room -->
                    <TableCell class="align-top">
                      <div class="space-y-1">
                        <Badge
                          variant="outline"
                          class="border-border bg-muted/40 text-foreground gap-1 text-xs font-medium"
                        >
                          <Bed class="text-primary size-3" />
                          {{ pt.assignedRoom }}
                        </Badge>
                        <p class="text-muted-foreground text-xs">
                          {{ pt.clinicalStatus }}
                        </p>
                      </div>
                    </TableCell>
    
                    <!-- Attending Staff -->
                    <TableCell class="align-top">
                      <div class="space-y-1.5">
                        <div class="flex items-center gap-2">
                          <Avatar class="size-6">
                            <AvatarImage :src="pt.primaryNurse.avatar" :alt="pt.primaryNurse.name" />
                            <AvatarFallback class="bg-primary/10 text-primary text-xs font-medium">
                              {{ pt.primaryNurse.initials }}
                            </AvatarFallback>
                          </Avatar>
                          <span
                            class="text-foreground max-w-[130px] truncate text-xs leading-none font-medium"
                            :title="pt.primaryNurse.name"
                          >
                            {{ pt.primaryNurse.name }}
                          </span>
                        </div>
    
                        <div class="flex items-center gap-2">
                          <Avatar class="size-6">
                            <AvatarImage :src="pt.attendingPhysician.avatar" :alt="pt.attendingPhysician.name" />
                            <AvatarFallback class="bg-secondary text-secondary-foreground text-xs font-medium">
                              {{ pt.attendingPhysician.initials }}
                            </AvatarFallback>
                          </Avatar>
                          <span
                            class="text-muted-foreground max-w-[130px] truncate text-xs leading-none"
                            :title="pt.attendingPhysician.name"
                          >
                            {{ pt.attendingPhysician.name }}
                          </span>
                        </div>
                      </div>
                    </TableCell>
    
                    <!-- Actions Menu -->
                    <TableCell class="text-right align-top">
                      <DropdownMenu>
                        <DropdownMenuTrigger as-child>
                          <Button variant="ghost" size="icon-sm" class="size-8">
                            <MoreHorizontal class="size-4" />
                            <span class="sr-only">Patient actions</span>
                          </Button>
                        </DropdownMenuTrigger>
                        <DropdownMenuContent align="end" class="w-48">
                          <DropdownMenuLabel>Triage Actions</DropdownMenuLabel>
                          <DropdownMenuItem class="cursor-pointer gap-2">
                            <Bed class="size-4" />
                            Assign Room
                          </DropdownMenuItem>
                          <DropdownMenuItem class="cursor-pointer gap-2">
                            <HeartPulse class="size-4" />
                            Re-triage Acuity
                          </DropdownMenuItem>
                          <DropdownMenuItem class="cursor-pointer gap-2">
                            <Zap class="size-4" />
                            Order STAT Labs
                          </DropdownMenuItem>
                          <DropdownMenuSeparator />
                          <DropdownMenuItem class="cursor-pointer gap-2">
                            <DoorOpen class="size-4" />
                            Discharge / Transfer
                          </DropdownMenuItem>
                        </DropdownMenuContent>
                      </DropdownMenu>
                    </TableCell>
                  </TableRow>
                </TableBody>
              </Table>
            </div>
    
            <!-- Table Footer / Status Summary -->
            <div
              class="border-border bg-muted/20 text-muted-foreground flex flex-wrap items-center justify-between gap-3 border-t px-4 py-3 text-xs"
            >
              <div class="flex items-center gap-3 font-medium">
                <span>Level 1 Trauma Alert Protocol Active</span>
                <Separator orientation="vertical" class="h-3.5" />
                <span class="tabular-nums">Average Door-to-Triage: 4.2 min</span>
              </div>
    
              <div class="flex items-center gap-2">
                <span class="text-foreground font-medium">ED Occupancy:</span>
                <span class="text-foreground font-semibold tabular-nums">88% (22/25 Beds)</span>
              </div>
            </div>
          </CardContent>
        </Card>
      </div>
    </template>
    

Raw manifest:https://uipkge.dev/r/vue/triage-queue-dashboard.json