UIPackage
Menu

Framework

Change language

Boilerplate repo

Prescription Refill Manager

blockhealthcare

Pharmacy patient portal for managing active prescriptions, refill requests, dosage schedules, delivery tracking, and preferred pharmacy details with an integrated refill order dialog.

Also available for React ->

Installation

$npx shadcn-vue@latest add https://uipkge.dev/r/vue/prescription-refill-manager.json
Named registry:npx shadcn-vue@latest add @uipkge/prescription-refill-managerInstalls to:app/components/blocks/prescription-refill-manager/

Variants

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
classHTMLAttributes['class']optional

Schema

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

Prescription
interface Prescription {
  id: string
  rxNumber: string
  name: string
  genericFor: string
  dosageForm: string
  instructions: string
  refillsRemaining: number
  totalRefills: number
  lastFilledDate: string
  nextDueDate: string
  isDue: boolean
  prescriber: {
    name: string
    specialty: string
    clinic: string
    phone: string
  }
  status: PrescriptionStatus
  daysSupply: number
  copayEst: number
  ndc: string
  packageTracking?: {
    carrier: string
    trackingNumber: string
    status: string
    estimatedArrival: string
    steps: { title: string; time: string; done: boolean; current?: boolean }[]
  }
}
Pharmacy
interface Pharmacy {
  id: string
  name: string
  subtitle: string
  address: string
  phone: string
  fax: string
  hours: string
  isDriveThru: boolean
}
TransferFormState
interface TransferFormState {
  pharmacyName: string
  phone: string
  rxNumber: string
  medicationName: string
  notes: string
}

Files installed (9)

  • app/components/blocks/prescription-refill-manager/PrescriptionRefillManager.vue12.1 kB
    <script setup lang="ts">
    import { computed, ref } from 'vue'
    import type { HTMLAttributes } from 'vue'
    import {
      AlertCircle,
      ArrowLeftRight,
      Building2,
      CheckCircle2,
      Clock,
      MapPin,
      Phone,
      Pill,
      Plus,
      Store,
      Truck,
    } from 'lucide-vue-next'
    import { cn } from '@/lib/utils'
    import { Badge } from '@/components/ui/badge'
    import { Button } from '@/components/ui/button'
    import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
    import { Separator } from '@/components/ui/separator'
    import ChangePharmacyDialog from './ChangePharmacyDialog.vue'
    import PrescriptionsTable from './PrescriptionsTable.vue'
    import PriorAuthDialog from './PriorAuthDialog.vue'
    import RefillOrderDialog from './RefillOrderDialog.vue'
    import TrackingDialog from './TrackingDialog.vue'
    import TransferDialog from './TransferDialog.vue'
    import { defaultAvailablePharmacies, defaultPrescriptions } from './prescription-refill-manager-data'
    import type { Pharmacy, Prescription, TransferFormState } from './prescription-refill-manager-types'
    
    export type { Pharmacy, Prescription, PrescriptionStatus } from './prescription-refill-manager-types'
    
    const props = defineProps<{
      class?: HTMLAttributes['class']
    }>()
    
    const prescriptions = ref<Prescription[]>([...defaultPrescriptions])
    const availablePharmacies = ref<Pharmacy[]>([...defaultAvailablePharmacies])
    
    // State variables
    const activePharmacy = ref<Pharmacy>(availablePharmacies.value[0])
    
    // Refill modal state
    const isRefillOpen = ref(false)
    const selectedPrescriptionId = ref<string>('rx-1')
    
    // Tracking modal state
    const isTrackingOpen = ref(false)
    const trackingPrescription = ref<Prescription | null>(null)
    
    // Transfer modal state
    const isTransferOpen = ref(false)
    const transferForm = ref<TransferFormState>({
      pharmacyName: '',
      phone: '',
      rxNumber: '',
      medicationName: '',
      notes: '',
    })
    const transferSubmitted = ref(false)
    
    // Change pharmacy modal state
    const isChangePharmacyOpen = ref(false)
    const tempSelectedPharmacyId = ref(activePharmacy.value.id)
    
    // Prescribing MD Auth Request modal state
    const isAuthRequestOpen = ref(false)
    const authRequestedRx = ref<Prescription | null>(null)
    const authRequestSubmitted = ref(false)
    
    const selectedPrescription = computed(() => {
      return prescriptions.value.find((rx) => rx.id === selectedPrescriptionId.value) || prescriptions.value[0]
    })
    
    // Action handlers
    function openRefillModal(prescriptionId?: string) {
      if (prescriptionId) {
        selectedPrescriptionId.value = prescriptionId
      } else {
        // Default to the first ready prescription
        const readyMed = prescriptions.value.find((r) => r.status === 'ready')
        selectedPrescriptionId.value = readyMed ? readyMed.id : prescriptions.value[0].id
      }
      isRefillOpen.value = true
    }
    
    function handleOpenTracking(rx: Prescription) {
      trackingPrescription.value = rx
      isTrackingOpen.value = true
    }
    
    function handleOpenAuthRequest(rx: Prescription) {
      authRequestedRx.value = rx
      authRequestSubmitted.value = false
      isAuthRequestOpen.value = true
    }
    
    function handleOpenTransfer() {
      transferForm.value = {
        pharmacyName: '',
        phone: '',
        rxNumber: '',
        medicationName: '',
        notes: '',
      }
      transferSubmitted.value = false
      isTransferOpen.value = true
    }
    
    function handleChangePharmacy() {
      tempSelectedPharmacyId.value = activePharmacy.value.id
      isChangePharmacyOpen.value = true
    }
    
    function savePharmacyChange() {
      const chosen = availablePharmacies.value.find((p) => p.id === tempSelectedPharmacyId.value)
      if (chosen) {
        activePharmacy.value = chosen
      }
      isChangePharmacyOpen.value = false
    }
    </script>
    
    <template>
      <div
        data-slot="prescription-refill-manager"
        :class="cn('bg-background text-foreground w-full space-y-6', props.class)"
      >
        <!-- Header Section -->
        <header
          class="bg-card border-border flex flex-col justify-between gap-4 rounded-xl border p-5 shadow-xs sm:p-6 md:flex-row md:items-center"
        >
          <div class="space-y-1">
            <div class="flex flex-wrap items-center gap-2.5">
              <div class="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg">
                <Pill class="size-4" />
              </div>
              <h1 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl">Prescriptions & Medications</h1>
              <Badge variant="outline" class="border-primary/20 bg-primary/5 text-primary text-xs font-medium">
                Active Care Plan
              </Badge>
            </div>
            <p class="text-muted-foreground text-sm">
              Manage recurring refills, dosage schedules, and home delivery tracking.
            </p>
          </div>
    
          <div class="flex flex-wrap items-center gap-2.5">
            <Button variant="outline" class="gap-1.5 text-xs font-medium" @click="handleOpenTransfer">
              <ArrowLeftRight class="size-3.5" />
              Transfer Prescription
            </Button>
            <Button variant="default" class="gap-1.5 text-xs font-medium shadow-xs" @click="openRefillModal()">
              <Plus class="size-3.5" />
              Request Refill
            </Button>
          </div>
        </header>
    
        <!-- 3 Medication Status Metric Cards -->
        <section class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
          <!-- Active Prescriptions -->
          <Card class="shadow-xs">
            <CardContent class="flex items-center justify-between p-5">
              <div class="space-y-1">
                <p class="text-muted-foreground text-xs font-medium">Active Prescriptions</p>
                <div class="flex items-baseline gap-2">
                  <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums">4</span>
                  <span class="text-muted-foreground text-xs">Total on file</span>
                </div>
                <p class="text-muted-foreground text-xs">All active maintenance therapies</p>
              </div>
              <div class="bg-primary/10 text-primary flex size-11 shrink-0 items-center justify-center rounded-xl">
                <Pill class="size-5" />
              </div>
            </CardContent>
          </Card>
    
          <!-- Refills Ready for Order -->
          <Card class="border-warning/30 bg-warning/5 bg-warning/10 shadow-xs">
            <CardContent class="flex items-center justify-between p-5">
              <div class="space-y-1">
                <div class="flex items-center gap-1.5">
                  <p class="text-warning text-xs font-semibold">Refills Ready for Order</p>
                  <Badge variant="outline" class="border-warning/40 bg-warning/15 text-warning text-xs font-medium">
                    2 Action Needed
                  </Badge>
                </div>
                <div class="flex items-baseline gap-2">
                  <span class="text-warning text-warning text-2xl font-bold tracking-tight tabular-nums">2</span>
                  <span class="text-warning/80 text-xs">Meds eligible now</span>
                </div>
                <p class="text-warning/80 text-xs">Metformin HCl & Lisinopril due</p>
              </div>
              <div class="bg-warning/20 text-warning flex size-11 shrink-0 items-center justify-center rounded-xl">
                <AlertCircle class="size-5" />
              </div>
            </CardContent>
          </Card>
    
          <!-- In Delivery / Transit -->
          <Card class="shadow-xs sm:col-span-2 lg:col-span-1">
            <CardContent class="flex items-center justify-between p-5">
              <div class="space-y-1">
                <div class="flex items-center gap-1.5">
                  <p class="text-muted-foreground text-xs font-medium">In Delivery / Transit</p>
                  <Badge variant="outline" class="border-info/30 bg-info/10 text-info gap-1 text-xs font-medium">
                    <span class="bg-info size-1.5 animate-pulse rounded-full" />
                    1 En Route
                  </Badge>
                </div>
                <div class="flex items-baseline gap-2">
                  <span class="text-foreground text-2xl font-bold tracking-tight tabular-nums">1</span>
                  <span class="text-muted-foreground text-xs">Package tracked</span>
                </div>
                <p class="text-muted-foreground text-xs">Atorvastatin 40mg · USPS Priority</p>
              </div>
              <div class="bg-info/10 text-info flex size-11 shrink-0 items-center justify-center rounded-xl">
                <Truck class="size-5" />
              </div>
            </CardContent>
          </Card>
        </section>
    
        <!-- Preferred Pharmacy Card -->
        <Card class="border-border shadow-xs">
          <CardHeader class="pb-3">
            <div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
              <div class="flex items-start gap-3">
                <div
                  class="bg-muted text-foreground mt-0.5 flex size-9 shrink-0 items-center justify-center rounded-lg border"
                >
                  <Store class="text-primary size-4" />
                </div>
                <div class="space-y-0.5">
                  <div class="flex flex-wrap items-center gap-2">
                    <CardTitle class="text-base font-semibold">
                      {{ activePharmacy.name }} · {{ activePharmacy.subtitle }}
                    </CardTitle>
                    <Badge variant="secondary" class="gap-1 text-xs font-medium">
                      <CheckCircle2 class="text-success size-3" />
                      Preferred Pharmacy
                    </Badge>
                    <Badge variant="outline" class="border-success/30 bg-success/10 text-success text-xs font-medium">
                      {{ activePharmacy.hours }}
                    </Badge>
                  </div>
                  <CardDescription class="text-xs">
                    Default location for prescription fulfillment, drive-thru pick-ups, and transfers.
                  </CardDescription>
                </div>
              </div>
    
              <Button
                variant="outline"
                size="sm"
                class="shrink-0 gap-1.5 text-xs font-medium"
                @click="handleChangePharmacy"
              >
                <Building2 class="size-3.5" />
                Change Pharmacy
              </Button>
            </div>
          </CardHeader>
          <Separator />
          <CardContent class="pt-3">
            <div class="grid grid-cols-1 gap-3 text-xs sm:grid-cols-3">
              <div class="text-muted-foreground flex items-center gap-2">
                <MapPin class="text-primary size-3.5 shrink-0" />
                <span class="text-foreground truncate font-medium">{{ activePharmacy.address }}</span>
              </div>
              <div class="text-muted-foreground flex items-center gap-2">
                <Phone class="text-primary size-3.5 shrink-0" />
                <span
                  >Phone: <strong class="text-foreground font-medium">{{ activePharmacy.phone }}</strong></span
                >
              </div>
              <div class="text-muted-foreground flex items-center gap-2">
                <Clock class="text-primary size-3.5 shrink-0" />
                <span>Rx Counter: <strong class="text-foreground font-medium">Ready in 2 hrs</strong></span>
              </div>
            </div>
          </CardContent>
        </Card>
    
        <!-- Prescriptions List Table Section -->
        <PrescriptionsTable
          :prescriptions="prescriptions"
          @refill="openRefillModal"
          @tracking="handleOpenTracking"
          @prior-auth="handleOpenAuthRequest"
          @transfer="handleOpenTransfer"
        />
    
        <!-- Refill Request Modal / Dialog -->
        <RefillOrderDialog
          v-model:open="isRefillOpen"
          :prescription="selectedPrescription"
          :active-pharmacy="activePharmacy"
        />
    
        <!-- Tracking Modal / Dialog -->
        <TrackingDialog v-model:open="isTrackingOpen" :prescription="trackingPrescription" />
    
        <!-- Transfer Prescription Modal / Dialog -->
        <TransferDialog
          v-model:open="isTransferOpen"
          v-model:transfer-form="transferForm"
          v-model:transfer-submitted="transferSubmitted"
          :pharmacy-name="activePharmacy.name"
        />
    
        <!-- Change Preferred Pharmacy Modal / Dialog -->
        <ChangePharmacyDialog
          v-model:open="isChangePharmacyOpen"
          v-model:selected-pharmacy-id="tempSelectedPharmacyId"
          :pharmacies="availablePharmacies"
          :active-pharmacy-id="activePharmacy.id"
          @save="savePharmacyChange"
        />
    
        <!-- Request MD Authorization Modal / Dialog -->
        <PriorAuthDialog
          v-model:open="isAuthRequestOpen"
          v-model:submitted="authRequestSubmitted"
          :prescription="authRequestedRx"
        />
      </div>
    </template>
    
  • app/components/blocks/prescription-refill-manager/TrackingDialog.vue3.7 kB
  • app/components/blocks/prescription-refill-manager/TransferDialog.vue5.1 kB
  • app/components/blocks/prescription-refill-manager/ChangePharmacyDialog.vue3.3 kB
  • app/components/blocks/prescription-refill-manager/PriorAuthDialog.vue3.1 kB
  • app/components/blocks/prescription-refill-manager/RefillOrderDialog.vue12.6 kB
  • app/components/blocks/prescription-refill-manager/PrescriptionsTable.vue14.4 kB
  • app/components/blocks/prescription-refill-manager/prescription-refill-manager-data.ts4.1 kB
  • app/components/blocks/prescription-refill-manager/prescription-refill-manager-types.ts1 kB

Raw manifest:https://uipkge.dev/r/vue/prescription-refill-manager.json