
3D Extruded Buildings & Urban Footprints
Real-world 3D building extrusions with dynamic sunlight shadows, terrain DEM elevations, and pitch/bearing camera controls.
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 ->$pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/prescription-refill-manager.json$npx shadcn-vue@latest add https://uipkge.dev/r/vue/prescription-refill-manager.json$yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/prescription-refill-manager.json$bunx shadcn-vue@latest add https://uipkge.dev/r/vue/prescription-refill-manager.jsonnpx shadcn-vue@latest add @uipkge/prescription-refill-managerInstalls to:app/components/blocks/prescription-refill-manager/| Name | Type / Values | Default | Required |
|---|---|---|---|
class | HTMLAttributes['class'] | — | optional |
Type aliases exported from this item's source. Use these to shape the data you pass in.
Prescriptioninterface 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 }[]
}
}Pharmacyinterface Pharmacy {
id: string
name: string
subtitle: string
address: string
phone: string
fax: string
hours: string
isDriveThru: boolean
}TransferFormStateinterface TransferFormState {
pharmacyName: string
phone: string
rxNumber: string
medicationName: string
notes: string
}<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>
<script setup lang="ts">
import { Truck } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { cn } from '@/lib/utils'
import type { Prescription } from './prescription-refill-manager-types'
const open = defineModel<boolean>('open', { required: true })
defineProps<{
prescription: Prescription | null
}>()
</script>
<template>
<!-- Tracking Modal / Dialog -->
<Dialog v-model:open="open">
<DialogContent v-if="prescription" class="sm:max-w-lg">
<DialogHeader>
<div class="flex items-center gap-2">
<div class="bg-info/10 text-info flex size-8 items-center justify-center rounded-lg">
<Truck class="size-4" />
</div>
<div>
<DialogTitle class="text-lg font-bold">Delivery & Tracking Status</DialogTitle>
<DialogDescription class="text-xs"> Tracking details for {{ prescription.name }} </DialogDescription>
</div>
</div>
</DialogHeader>
<div v-if="prescription.packageTracking" class="space-y-4 py-2 text-xs">
<!-- Tracking Header Banner -->
<div class="bg-muted/40 border-border space-y-2 rounded-xl border p-4">
<div class="flex items-center justify-between">
<span class="text-muted-foreground">Carrier & Service:</span>
<strong class="text-foreground">{{ prescription.packageTracking.carrier }}</strong>
</div>
<div class="flex items-center justify-between">
<span class="text-muted-foreground">Tracking Number:</span>
<strong class="text-foreground font-mono">{{ prescription.packageTracking.trackingNumber }}</strong>
</div>
<div class="flex items-center justify-between">
<span class="text-muted-foreground">Status:</span>
<Badge variant="outline" class="border-info/30 bg-info/10 text-info text-xs font-semibold">
{{ prescription.packageTracking.status }}
</Badge>
</div>
<div class="flex items-center justify-between border-t pt-1">
<span class="text-muted-foreground">Estimated Delivery:</span>
<strong class="text-foreground font-bold">{{ prescription.packageTracking.estimatedArrival }}</strong>
</div>
</div>
<!-- Step Timeline Tracker -->
<div class="space-y-3 pl-2">
<p class="text-foreground text-xs font-semibold">Package History Timeline</p>
<div class="border-border relative ml-2 space-y-4 border-l pl-4">
<div v-for="(step, idx) in prescription.packageTracking.steps" :key="idx" class="relative space-y-0.5">
<div
:class="
cn(
'ring-background absolute top-0.5 -left-[23px] flex size-3.5 items-center justify-center rounded-full ring-2',
step.current
? 'bg-info ring-info animate-pulse'
: step.done
? 'bg-primary text-primary-foreground'
: 'bg-muted border',
)
"
/>
<p :class="cn('text-xs font-semibold', step.current ? 'text-info' : 'text-foreground')">
{{ step.title }}
</p>
<p class="text-muted-foreground text-xs tabular-nums">{{ step.time }}</p>
</div>
</div>
</div>
</div>
<DialogFooter>
<Button variant="outline" class="w-full sm:w-auto" @click="open = false"> Close Tracking </Button>
</DialogFooter>
</DialogContent>
</Dialog>
</template>
<script setup lang="ts">
import { ArrowLeftRight, CheckCircle2 } from 'lucide-vue-next'
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import type { TransferFormState } from './prescription-refill-manager-types'
const open = defineModel<boolean>('open', { required: true })
const transferForm = defineModel<TransferFormState>('transferForm', { required: true })
const transferSubmitted = defineModel<boolean>('transferSubmitted', { required: true })
defineProps<{
pharmacyName: string
}>()
function submitTransfer() {
transferSubmitted.value = true
}
</script>
<template>
<!-- Transfer Prescription Modal / Dialog -->
<Dialog v-model:open="open">
<DialogContent class="sm:max-w-lg">
<DialogHeader>
<div class="flex items-center gap-2">
<div class="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg">
<ArrowLeftRight class="size-4" />
</div>
<div>
<DialogTitle class="text-lg font-bold">Transfer Prescription</DialogTitle>
<DialogDescription class="text-xs">
Move an existing prescription from another pharmacy to {{ pharmacyName }}.
</DialogDescription>
</div>
</div>
</DialogHeader>
<div v-if="transferSubmitted" class="space-y-4 py-4">
<div class="border-success/30 bg-success/10 text-success space-y-2 rounded-xl border p-5 text-center">
<div class="bg-success/20 text-success mx-auto flex size-12 items-center justify-center rounded-full">
<CheckCircle2 class="size-6" />
</div>
<h3 class="text-base font-bold">Transfer Request Submitted!</h3>
<p class="text-success/90 mx-auto max-w-sm text-xs">
Our clinical pharmacy team will contact your previous pharmacy to transfer the prescription. This typically
takes 24–48 hours.
</p>
</div>
<DialogFooter>
<Button variant="default" class="w-full" @click="open = false"> Done </Button>
</DialogFooter>
</div>
<div v-else class="space-y-3.5 py-2 text-xs">
<div class="space-y-1">
<label class="text-foreground text-xs font-medium">Current / Previous Pharmacy Name</label>
<input
v-model="transferForm.pharmacyName"
type="text"
placeholder="e.g. Walgreens, Rite Aid, Walmart..."
class="border-input bg-background text-foreground focus-visible:ring-ring h-9 w-full rounded-md border px-3 text-xs shadow-xs focus-visible:ring-2 focus-visible:outline-none"
/>
</div>
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
<div class="space-y-1">
<label class="text-foreground text-xs font-medium">Pharmacy Phone Number</label>
<input
v-model="transferForm.phone"
type="tel"
placeholder="(555) 000-0000"
class="border-input bg-background text-foreground focus-visible:ring-ring h-9 w-full rounded-md border px-3 text-xs shadow-xs focus-visible:ring-2 focus-visible:outline-none"
/>
</div>
<div class="space-y-1">
<label class="text-foreground text-xs font-medium">Existing Rx Number (if known)</label>
<input
v-model="transferForm.rxNumber"
type="text"
placeholder="e.g. RX-123456"
class="border-input bg-background text-foreground focus-visible:ring-ring h-9 w-full rounded-md border px-3 font-mono text-xs shadow-xs focus-visible:ring-2 focus-visible:outline-none"
/>
</div>
</div>
<div class="space-y-1">
<label class="text-foreground text-xs font-medium">Medication Name & Strength</label>
<input
v-model="transferForm.medicationName"
type="text"
placeholder="e.g. Lipitor 20mg or Levothyroxine 50mcg"
class="border-input bg-background text-foreground focus-visible:ring-ring h-9 w-full rounded-md border px-3 text-xs shadow-xs focus-visible:ring-2 focus-visible:outline-none"
/>
</div>
<div class="space-y-1">
<label class="text-foreground text-xs font-medium">Special Instructions / Prescriber Notes (Optional)</label>
<textarea
v-model="transferForm.notes"
rows="2"
placeholder="Any additional notes for the receiving pharmacist..."
class="border-input bg-background text-foreground focus-visible:ring-ring w-full rounded-md border p-2 text-xs shadow-xs focus-visible:ring-2 focus-visible:outline-none"
/>
</div>
<DialogFooter class="pt-2">
<Button variant="outline" @click="open = false">Cancel</Button>
<Button variant="default" class="gap-1.5 shadow-xs" @click="submitTransfer">
<ArrowLeftRight class="size-3.5" />
Initiate Transfer
</Button>
</DialogFooter>
</div>
</DialogContent>
</Dialog>
</template>
<script setup lang="ts">
import { Building2 } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { cn } from '@/lib/utils'
import type { Pharmacy } from './prescription-refill-manager-types'
const open = defineModel<boolean>('open', { required: true })
const selectedPharmacyId = defineModel<string>('selectedPharmacyId', { required: true })
defineProps<{
pharmacies: Pharmacy[]
activePharmacyId: string
}>()
defineEmits<{
save: []
}>()
</script>
<template>
<!-- Change Preferred Pharmacy Modal / Dialog -->
<Dialog v-model:open="open">
<DialogContent class="sm:max-w-lg">
<DialogHeader>
<div class="flex items-center gap-2">
<div class="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg">
<Building2 class="size-4" />
</div>
<div>
<DialogTitle class="text-lg font-bold">Select Preferred Pharmacy</DialogTitle>
<DialogDescription class="text-xs">
Choose the location where your local orders and drive-thru pickups are routed.
</DialogDescription>
</div>
</div>
</DialogHeader>
<div class="space-y-3 py-2 text-xs">
<div
v-for="pharmacy in pharmacies"
:key="pharmacy.id"
:class="
cn(
'flex cursor-pointer items-start justify-between rounded-xl border p-3.5 transition-colors',
selectedPharmacyId === pharmacy.id
? 'border-primary bg-primary/5 ring-primary ring-1'
: 'border-border bg-card hover:bg-muted/40',
)
"
@click="selectedPharmacyId = pharmacy.id"
>
<div class="space-y-1">
<div class="flex items-center gap-2">
<span class="text-foreground font-bold">{{ pharmacy.name }}</span>
<Badge v-if="pharmacy.id === activePharmacyId" variant="secondary" class="text-xs">Current</Badge>
</div>
<p class="text-muted-foreground">{{ pharmacy.address }}</p>
<div class="text-muted-foreground flex items-center gap-3 pt-1">
<span
>Phone: <strong class="text-foreground font-medium">{{ pharmacy.phone }}</strong></span
>
<span>{{ pharmacy.hours }}</span>
</div>
</div>
<div class="pt-0.5">
<div
:class="
cn(
'flex size-4 items-center justify-center rounded-full border',
selectedPharmacyId === pharmacy.id
? 'border-primary bg-primary text-primary-foreground'
: 'border-muted-foreground/40',
)
"
>
<div v-if="selectedPharmacyId === pharmacy.id" class="bg-background size-1.5 rounded-full" />
</div>
</div>
</div>
</div>
<DialogFooter class="pt-2">
<Button variant="outline" @click="open = false">Cancel</Button>
<Button variant="default" @click="$emit('save')">Save Preferred Pharmacy</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</template>
<script setup lang="ts">
import { Check, CheckCircle2, ShieldAlert } from 'lucide-vue-next'
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import type { Prescription } from './prescription-refill-manager-types'
const open = defineModel<boolean>('open', { required: true })
const submitted = defineModel<boolean>('submitted', { required: true })
defineProps<{
prescription: Prescription | null
}>()
</script>
<template>
<!-- Request MD Authorization Modal / Dialog -->
<Dialog v-model:open="open">
<DialogContent v-if="prescription" class="sm:max-w-md">
<DialogHeader>
<div class="flex items-center gap-2">
<div class="bg-destructive/10 text-destructive flex size-8 items-center justify-center rounded-lg">
<ShieldAlert class="size-4" />
</div>
<div>
<DialogTitle class="text-lg font-bold">Request Prescriber Authorization</DialogTitle>
<DialogDescription class="text-xs"> Renew zero-refill prescription with your physician. </DialogDescription>
</div>
</div>
</DialogHeader>
<div v-if="submitted" class="space-y-3 py-4 text-center">
<div class="bg-success/20 text-success mx-auto flex size-12 items-center justify-center rounded-full">
<CheckCircle2 class="size-6" />
</div>
<h3 class="text-foreground text-sm font-bold">Authorization Request Sent!</h3>
<p class="text-muted-foreground text-xs">
A renewal request for <strong>{{ prescription.name }}</strong> has been electronically transmitted to
<strong>{{ prescription.prescriber.name }}</strong
>.
</p>
<DialogFooter class="pt-2">
<Button variant="default" class="w-full" @click="open = false">Done</Button>
</DialogFooter>
</div>
<div v-else class="space-y-4 py-2 text-xs">
<div class="bg-muted/40 space-y-1.5 rounded-xl border p-3.5">
<p class="text-foreground font-bold">{{ prescription.name }}</p>
<p class="text-muted-foreground">{{ prescription.genericFor }}</p>
<p class="text-muted-foreground">
Prescribing Physician: <strong class="text-foreground">{{ prescription.prescriber.name }}</strong>
</p>
<p class="text-muted-foreground">
Clinic: {{ prescription.prescriber.clinic }} ({{ prescription.prescriber.phone }})
</p>
</div>
<p class="text-muted-foreground leading-relaxed">
Because this prescription has 0 refills remaining, we will send an electronic request (e-Prescription Renewal)
directly to your doctor's office.
</p>
<DialogFooter>
<Button variant="outline" @click="open = false">Cancel</Button>
<Button variant="default" class="gap-1.5" @click="submitted = true">
<Check class="size-3.5" />
Send Renewal Request
</Button>
</DialogFooter>
</div>
</DialogContent>
</Dialog>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { Check, CheckCircle2, RefreshCw, Store, Truck } from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
import { Separator } from '@/components/ui/separator'
import { cn } from '@/lib/utils'
import type { Pharmacy, Prescription } from './prescription-refill-manager-types'
const props = defineProps<{
open: boolean
prescription: Prescription
activePharmacy: Pharmacy
}>()
const emit = defineEmits<{
'update:open': [value: boolean]
submit: [details: { prescriptionId: string; supplyDuration: '30' | '90'; deliveryMethod: 'pickup' | 'delivery' }]
}>()
const supplyDuration = ref<'30' | '90'>('90')
const deliveryMethod = ref<'pickup' | 'delivery'>('delivery')
const orderSubmitted = ref(false)
watch(
() => props.open,
(isOpen) => {
if (isOpen) {
orderSubmitted.value = false
}
},
)
const estimatedCopay = computed(() => {
if (supplyDuration.value === '90') {
return 20.0
}
return props.prescription.copayEst
})
const estimatedInsuranceSavings = computed(() => {
if (supplyDuration.value === '90') {
return 75.0
}
return 35.0
})
function handleSubmit() {
orderSubmitted.value = true
emit('submit', {
prescriptionId: props.prescription.id,
supplyDuration: supplyDuration.value,
deliveryMethod: deliveryMethod.value,
})
}
</script>
<template>
<Dialog :open="open" @update:open="emit('update:open', $event)">
<DialogContent class="sm:max-w-xl">
<DialogHeader>
<div class="flex items-center gap-2">
<div class="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg">
<RefreshCw class="size-4" />
</div>
<div>
<DialogTitle class="text-lg font-bold">Request Prescription Refill</DialogTitle>
<DialogDescription class="text-xs">
Select your supply duration, delivery method, and confirm your copay.
</DialogDescription>
</div>
</div>
</DialogHeader>
<!-- If order submitted success view -->
<div v-if="orderSubmitted" class="space-y-4 py-4">
<div class="border-success/30 bg-success/10 text-success space-y-2 rounded-xl border p-5 text-center">
<div class="bg-success/20 text-success mx-auto flex size-12 items-center justify-center rounded-full">
<CheckCircle2 class="size-6" />
</div>
<h3 class="text-base font-bold">Refill Order Successfully Placed!</h3>
<p class="text-success/90 mx-auto max-w-md text-xs">
Your order for <strong>{{ prescription.name }}</strong> ({{ supplyDuration }}-day supply) has been sent to
{{ activePharmacy.name }}.
</p>
<p class="pt-1 font-mono text-xs font-medium">
Order Ref: #ORD-{{ Math.floor(100000 + Math.random() * 900000) }}
</p>
</div>
<div class="bg-muted/40 text-muted-foreground space-y-1.5 rounded-lg border p-3 text-xs">
<div class="flex justify-between">
<span>Fulfillment Method:</span>
<strong class="text-foreground">{{
deliveryMethod === 'delivery' ? 'Express Home Delivery (1-2 Days)' : 'In-Store Pickup'
}}</strong>
</div>
<div class="flex justify-between">
<span>Estimated Copay:</span>
<strong class="text-foreground font-mono tabular-nums">${{ estimatedCopay.toFixed(2) }}</strong>
</div>
<div class="flex justify-between">
<span>Remaining Refills After This Order:</span>
<strong class="text-foreground tabular-nums">{{ Math.max(0, prescription.refillsRemaining - 1) }}</strong>
</div>
</div>
<DialogFooter class="pt-2">
<Button variant="default" class="w-full" @click="emit('update:open', false)">
Done & Return to Prescriptions
</Button>
</DialogFooter>
</div>
<!-- Form view -->
<div v-else class="space-y-5 py-2">
<!-- Medication Selector / Summary Box -->
<div class="bg-muted/40 border-border space-y-3 rounded-xl border p-4">
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
<div class="space-y-0.5">
<p class="text-muted-foreground text-xs font-semibold">Selected Prescription</p>
<h4 class="text-foreground text-sm font-bold">{{ prescription.name }}</h4>
<p class="text-muted-foreground text-xs">{{ prescription.genericFor }}</p>
</div>
<div class="space-y-0.5 text-left sm:text-right">
<span class="bg-background rounded border px-2 py-0.5 font-mono text-xs font-medium">
{{ prescription.rxNumber }}
</span>
<p class="text-success text-xs font-medium">{{ prescription.refillsRemaining }} refills remaining</p>
</div>
</div>
<div class="text-muted-foreground flex items-center justify-between border-t pt-2.5 text-xs">
<span>
Prescriber:
<strong class="text-foreground font-medium">{{ prescription.prescriber.name }}</strong>
</span>
<span>
Directions:
<strong class="text-foreground font-medium">{{ prescription.instructions }}</strong>
</span>
</div>
</div>
<!-- Quantity / Supply Selector -->
<div class="space-y-2.5">
<label class="text-foreground flex items-center justify-between text-xs font-semibold">
<span>Choose Supply Quantity</span>
<span class="text-muted-foreground font-normal">Insurance Tier 1 Preferred</span>
</label>
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
<!-- 30-Day Supply Option -->
<button
type="button"
:class="
cn(
'flex cursor-pointer flex-col items-start rounded-xl border p-3.5 text-left transition-colors duration-150',
supplyDuration === '30'
? 'border-primary bg-primary/5 ring-primary ring-1'
: 'border-border bg-card hover:bg-muted/50',
)
"
@click="supplyDuration = '30'"
>
<div class="mb-1 flex w-full items-center justify-between">
<span class="text-foreground text-xs font-bold">30-Day Supply</span>
<span class="text-foreground font-mono text-xs font-bold tabular-nums">$10.00</span>
</div>
<p class="text-muted-foreground text-xs">Standard monthly bottle (30 tablets)</p>
<span class="text-muted-foreground mt-2 text-xs">Local pickup or standard mail</span>
</button>
<!-- 90-Day Supply Option (Recommended) -->
<button
type="button"
:class="
cn(
'relative flex cursor-pointer flex-col items-start rounded-xl border p-3.5 text-left transition-colors duration-150',
supplyDuration === '90'
? 'border-primary bg-primary/5 ring-primary ring-1'
: 'border-border bg-card hover:bg-muted/50',
)
"
@click="supplyDuration = '90'"
>
<div class="mb-1 flex w-full items-center justify-between">
<span class="text-foreground text-xs font-bold">90-Day Mail Order</span>
<span class="text-foreground font-mono text-xs font-bold tabular-nums">$20.00</span>
</div>
<p class="text-muted-foreground text-xs">3-Month Supply (90 tablets)</p>
<Badge variant="outline" class="border-success/40 bg-success/10 text-success mt-2 text-xs font-semibold">
Save $10 on Copay
</Badge>
</button>
</div>
</div>
<!-- Delivery Method Selector -->
<div class="space-y-2.5">
<label class="text-foreground text-xs font-semibold">Select Delivery & Fulfillment Method</label>
<RadioGroup v-model="deliveryMethod" class="gap-2.5">
<!-- Free Express Home Delivery -->
<label
:class="
cn(
'flex cursor-pointer items-start gap-3 rounded-xl border p-3 transition-colors',
deliveryMethod === 'delivery'
? 'border-primary bg-primary/5 ring-primary ring-1'
: 'border-border bg-card hover:bg-muted/30',
)
"
>
<RadioGroupItem value="delivery" id="delivery-option" class="mt-0.5" />
<div class="flex-1 space-y-0.5">
<div class="flex items-center justify-between">
<span class="text-foreground flex items-center gap-1.5 text-xs font-bold">
<Truck class="text-primary size-3.5" />
Free Express Home Delivery
</span>
<Badge variant="outline" class="border-primary/30 bg-primary/10 text-primary text-xs">
FREE 2-Day
</Badge>
</div>
<p class="text-muted-foreground text-xs">
Ships to: <strong>1420 Ocean Ave, Apt 4B, Santa Monica, CA</strong>
</p>
<p class="text-muted-foreground text-xs">Estimated arrival in 1–2 business days via USPS Priority</p>
</div>
</label>
<!-- Pharmacy Pickup -->
<label
:class="
cn(
'flex cursor-pointer items-start gap-3 rounded-xl border p-3 transition-colors',
deliveryMethod === 'pickup'
? 'border-primary bg-primary/5 ring-primary ring-1'
: 'border-border bg-card hover:bg-muted/30',
)
"
>
<RadioGroupItem value="pickup" id="pickup-option" class="mt-0.5" />
<div class="flex-1 space-y-0.5">
<div class="flex items-center justify-between">
<span class="text-foreground flex items-center gap-1.5 text-xs font-bold">
<Store class="text-primary size-3.5" />
In-Store / Drive-Thru Pickup
</span>
<span class="text-success text-xs font-bold">Ready Today</span>
</div>
<p class="text-muted-foreground text-xs">{{ activePharmacy.name }} · {{ activePharmacy.address }}</p>
<p class="text-muted-foreground text-xs">
Ready for pickup in ~2 hours. 24-Hour drive-thru accessible.
</p>
</div>
</label>
</RadioGroup>
</div>
<!-- Copay and Cost Breakdown -->
<div class="bg-muted/50 space-y-2 rounded-xl border p-3.5 text-xs">
<div class="text-muted-foreground flex justify-between">
<span>Medication Retail Cost:</span>
<span class="font-mono tabular-nums line-through"
>${{ (estimatedCopay + estimatedInsuranceSavings).toFixed(2) }}</span
>
</div>
<div class="text-success flex justify-between">
<span>Insurance Coverage (Aetna Choice POS II):</span>
<span class="font-mono tabular-nums">-${{ estimatedInsuranceSavings.toFixed(2) }}</span>
</div>
<div class="text-muted-foreground flex justify-between">
<span>Shipping & Handling:</span>
<span class="text-success font-medium">FREE</span>
</div>
<Separator />
<div class="text-foreground flex items-center justify-between pt-0.5 text-sm font-bold">
<span>Estimated Out-of-Pocket:</span>
<span class="text-primary font-mono text-base tabular-nums">${{ estimatedCopay.toFixed(2) }}</span>
</div>
</div>
<DialogFooter class="flex items-center justify-between gap-2 pt-2 sm:justify-end">
<Button variant="outline" @click="emit('update:open', false)">Cancel</Button>
<Button variant="default" class="gap-1.5 shadow-xs" @click="handleSubmit">
<Check class="size-4" />
Submit Refill Order
</Button>
</DialogFooter>
</div>
</DialogContent>
</Dialog>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import {
AlertCircle,
ArrowLeftRight,
Calendar,
Clock,
FileText,
Info,
MoreHorizontal,
Phone,
RefreshCw,
Search,
ShieldAlert,
Truck,
} from 'lucide-vue-next'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Card, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
import { cn } from '@/lib/utils'
import type { Prescription } from './prescription-refill-manager-types'
const props = defineProps<{
prescriptions: Prescription[]
}>()
const emit = defineEmits<{
refill: [rxId: string]
tracking: [rx: Prescription]
priorAuth: [rx: Prescription]
transfer: []
}>()
const searchQuery = ref('')
const statusFilter = ref<'all' | 'ready' | 'needs_auth' | 'in_transit'>('all')
const filteredPrescriptions = computed(() => {
return props.prescriptions.filter((rx) => {
const matchesSearch =
rx.name.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
rx.rxNumber.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
rx.genericFor.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
rx.prescriber.name.toLowerCase().includes(searchQuery.value.toLowerCase())
if (!matchesSearch) return false
if (statusFilter.value === 'all') return true
return rx.status === statusFilter.value
})
})
</script>
<template>
<Card class="border-border shadow-xs">
<CardHeader class="pb-3">
<div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
<div>
<CardTitle class="text-lg font-bold">Active Prescriptions ({{ filteredPrescriptions.length }})</CardTitle>
<CardDescription class="text-xs">
Review remaining refills, dosage instructions, and ordering eligibility.
</CardDescription>
</div>
<!-- Search and Filter Bar -->
<div class="flex flex-wrap items-center gap-2">
<div class="relative w-full sm:w-64">
<Search class="text-muted-foreground absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2" />
<input
v-model="searchQuery"
type="text"
placeholder="Search medication, Rx#, doctor..."
class="border-input bg-background text-foreground focus-visible:ring-ring h-8 w-full rounded-md border pr-3 pl-8 text-xs shadow-xs focus-visible:ring-2 focus-visible:outline-none"
/>
</div>
<div class="flex items-center gap-1">
<Button
variant="ghost"
size="sm"
:class="cn('h-8 text-xs font-medium', statusFilter === 'all' && 'bg-accent text-accent-foreground')"
@click="statusFilter = 'all'"
>
All ({{ prescriptions.length }})
</Button>
<Button
variant="ghost"
size="sm"
:class="
cn(
'h-8 text-xs font-medium',
statusFilter === 'ready' && 'bg-accent text-accent-foreground text-warning',
)
"
@click="statusFilter = 'ready'"
>
Ready for Refill (2)
</Button>
<Button
variant="ghost"
size="sm"
:class="
cn('h-8 text-xs font-medium', statusFilter === 'in_transit' && 'bg-accent text-accent-foreground')
"
@click="statusFilter = 'in_transit'"
>
In Transit (1)
</Button>
<Button
variant="ghost"
size="sm"
:class="
cn(
'h-8 text-xs font-medium',
statusFilter === 'needs_auth' && 'bg-accent text-accent-foreground text-destructive',
)
"
@click="statusFilter = 'needs_auth'"
>
Needs Auth (1)
</Button>
</div>
</div>
</div>
</CardHeader>
<div class="overflow-x-auto">
<Table>
<TableHeader>
<TableRow class="hover:bg-transparent">
<TableHead class="min-w-[220px] text-xs font-semibold">Medication & Rx#</TableHead>
<TableHead class="min-w-[240px] text-xs font-semibold">Directions & Dosage</TableHead>
<TableHead class="min-w-[170px] text-xs font-semibold">Refill Status</TableHead>
<TableHead class="min-w-[160px] text-xs font-semibold">Fill Dates / Schedule</TableHead>
<TableHead class="min-w-[180px] text-xs font-semibold">Prescribing Doctor</TableHead>
<TableHead class="min-w-[150px] text-right text-xs font-semibold">Quick Action</TableHead>
</TableRow>
</TableHeader>
<TableBody>
<TableRow
v-for="rx in filteredPrescriptions"
:key="rx.id"
:class="cn('transition-colors', rx.status === 'ready' && 'bg-warning/[0.02] bg-warning/[0.04]')"
>
<!-- Medication Name & Rx# -->
<TableCell class="py-4 align-top">
<div class="space-y-1">
<div class="flex items-center gap-1.5">
<span class="text-foreground text-sm font-semibold">{{ rx.name }}</span>
</div>
<p class="text-muted-foreground text-xs">{{ rx.genericFor }}</p>
<div class="flex items-center gap-2 pt-0.5">
<span class="bg-muted text-muted-foreground rounded px-1.5 py-0.5 font-mono text-xs font-medium">
{{ rx.rxNumber }}
</span>
<span class="text-muted-foreground text-xs">NDC: {{ rx.ndc }}</span>
</div>
</div>
</TableCell>
<!-- Directions & Dosage -->
<TableCell class="py-4 align-top">
<div class="space-y-1">
<p class="text-foreground text-xs leading-relaxed font-medium">
{{ rx.instructions }}
</p>
<p class="text-muted-foreground text-xs">
Form: <span class="text-foreground font-medium">{{ rx.dosageForm }}</span> · Qty:
<span class="font-mono tabular-nums">{{ rx.daysSupply }} Days</span>
</p>
</div>
</TableCell>
<!-- Refill Status Badge -->
<TableCell class="py-4 align-top">
<div class="space-y-1.5">
<div v-if="rx.status === 'ready'">
<Badge
variant="outline"
class="border-warning/40 bg-warning/10 text-warning gap-1 text-xs font-medium"
>
<AlertCircle class="size-3" />
{{ rx.refillsRemaining }} refills remaining
</Badge>
</div>
<div v-else-if="rx.status === 'needs_auth'">
<Badge
variant="destructive"
class="bg-destructive/15 text-destructive dark:text-destructive-foreground dark:bg-destructive/30 gap-1 border-transparent text-xs font-medium"
>
<ShieldAlert class="size-3" />
0 refills - Needs MD authorization
</Badge>
</div>
<div v-else-if="rx.status === 'in_transit'">
<Badge variant="outline" class="border-info/30 bg-info/10 text-info gap-1 text-xs font-medium">
<Truck class="size-3" />
In Transit · Est. Arrival Today
</Badge>
</div>
<div v-else>
<Badge variant="secondary" class="text-xs font-medium">
{{ rx.refillsRemaining }} of {{ rx.totalRefills }} remaining
</Badge>
</div>
<p class="text-muted-foreground text-xs tabular-nums">
Est. Copay: <strong class="text-foreground">${{ rx.copayEst }}.00</strong>
</p>
</div>
</TableCell>
<!-- Fill Dates / Schedule -->
<TableCell class="py-4 align-top text-xs">
<div class="space-y-1">
<div class="text-muted-foreground flex items-center gap-1.5">
<Calendar class="size-3 shrink-0" />
<span>
Last Filled:
<strong class="text-foreground font-medium tabular-nums">{{ rx.lastFilledDate }}</strong>
</span>
</div>
<div
:class="
cn(
'flex items-center gap-1.5 font-medium tabular-nums',
rx.isDue ? 'text-warning font-semibold' : 'text-muted-foreground',
)
"
>
<Clock class="size-3 shrink-0" />
<span>Next Due: {{ rx.nextDueDate }}</span>
</div>
<span v-if="rx.isDue" class="text-warning inline-block text-xs font-medium"> Refill window open </span>
</div>
</TableCell>
<!-- Prescribing Doctor -->
<TableCell class="py-4 align-top text-xs">
<div class="space-y-0.5">
<p class="text-foreground font-medium">{{ rx.prescriber.name }}</p>
<p class="text-muted-foreground">{{ rx.prescriber.specialty }}</p>
<p class="text-muted-foreground truncate text-xs">{{ rx.prescriber.clinic }}</p>
</div>
</TableCell>
<!-- Quick Action & Dropdown Menu -->
<TableCell class="py-4 text-right align-top">
<div class="flex items-center justify-end gap-1.5">
<!-- Ready state: Order Refill Button -->
<template v-if="rx.status === 'ready'">
<Button
variant="default"
size="sm"
class="h-8 gap-1 text-xs font-medium shadow-xs"
@click="emit('refill', rx.id)"
>
<RefreshCw class="size-3" />
Order Refill
</Button>
</template>
<!-- Needs Auth State: Request MD Auth -->
<template v-else-if="rx.status === 'needs_auth'">
<Button
variant="outline"
size="sm"
class="border-destructive/30 text-destructive hover:bg-destructive/10 h-8 gap-1 text-xs font-medium"
@click="emit('priorAuth', rx)"
>
<Clock class="size-3" />
Request Auth
</Button>
</template>
<!-- In Transit State: Track Package -->
<template v-else-if="rx.status === 'in_transit'">
<Button
variant="secondary"
size="sm"
class="h-8 gap-1 text-xs font-medium"
@click="emit('tracking', rx)"
>
<Truck class="text-info size-3" />
Track
</Button>
</template>
<!-- Row Dropdown Menu -->
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button variant="ghost" size="icon" class="size-8" aria-label="Prescription options">
<MoreHorizontal class="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" class="w-52">
<DropdownMenuLabel class="text-xs font-semibold">Rx Options</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem v-if="rx.status === 'ready'" class="gap-2 text-xs" @click="emit('refill', rx.id)">
<RefreshCw class="text-primary size-3.5" />
Order Refill (30/90 Days)
</DropdownMenuItem>
<DropdownMenuItem
v-if="rx.status === 'needs_auth'"
class="text-destructive gap-2 text-xs"
@click="emit('priorAuth', rx)"
>
<ShieldAlert class="size-3.5" />
Request MD Renewal
</DropdownMenuItem>
<DropdownMenuItem v-if="rx.packageTracking" class="gap-2 text-xs" @click="emit('tracking', rx)">
<Truck class="text-info size-3.5" />
Track USPS Delivery
</DropdownMenuItem>
<DropdownMenuItem class="gap-2 text-xs" @click="emit('transfer')">
<ArrowLeftRight class="size-3.5" />
Transfer to Another Pharmacy
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem class="gap-2 text-xs">
<FileText class="text-muted-foreground size-3.5" />
View Full Drug Monograph
</DropdownMenuItem>
<DropdownMenuItem class="gap-2 text-xs">
<Phone class="text-muted-foreground size-3.5" />
Call Prescriber ({{ rx.prescriber.phone }})
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</TableCell>
</TableRow>
</TableBody>
</Table>
</div>
<CardFooter
class="bg-muted/30 border-border text-muted-foreground flex flex-col justify-between gap-3 border-t p-4 text-xs sm:flex-row sm:items-center"
>
<div class="flex items-center gap-2">
<Info class="text-primary size-3.5 shrink-0" />
<span>Refills requested before 3:00 PM are processed same-day by our clinical pharmacy team.</span>
</div>
<div class="flex items-center gap-3">
<span>Automatic Refill Program: <strong class="text-foreground font-medium">Enabled</strong></span>
</div>
</CardFooter>
</Card>
</template>
import type { Pharmacy, Prescription } from './prescription-refill-manager-types'
export const defaultPrescriptions: Prescription[] = [
{
id: 'rx-1',
rxNumber: 'RX-849201',
name: 'Metformin HCl 500mg',
genericFor: 'Generic for Glucophage',
dosageForm: 'Oral Tablet',
instructions: 'Take 1 tablet by mouth twice daily with meals',
refillsRemaining: 2,
totalRefills: 5,
lastFilledDate: 'Jul 24, 2026',
nextDueDate: 'Aug 24, 2026',
isDue: true,
prescriber: {
name: 'Dr. Emily Vance, MD',
specialty: 'Internal Medicine',
clinic: 'Wilshire Medical Center',
phone: '(310) 555-0142',
},
status: 'ready',
daysSupply: 30,
copayEst: 10,
ndc: '50090-0819-0',
},
{
id: 'rx-2',
rxNumber: 'RX-849202',
name: 'Lisinopril 20mg',
genericFor: 'Generic for Prinivil / Zestril',
dosageForm: 'Oral Tablet',
instructions: 'Take 1 tablet by mouth daily in the morning',
refillsRemaining: 3,
totalRefills: 6,
lastFilledDate: 'Jul 10, 2026',
nextDueDate: 'Aug 10, 2026',
isDue: true,
prescriber: {
name: 'Dr. Marcus Chen, MD',
specialty: 'Cardiology',
clinic: 'Pacific Heart & Vascular Institute',
phone: '(310) 555-0188',
},
status: 'ready',
daysSupply: 30,
copayEst: 10,
ndc: '68180-0518-01',
},
{
id: 'rx-3',
rxNumber: 'RX-774920',
name: 'Albuterol HFA Inhaler',
genericFor: 'ProAir HFA 90mcg Inhalation Aerosol',
dosageForm: 'Inhalation Aerosol (8.5g canister)',
instructions: 'Inhale 2 puffs every 4-6 hours as needed for wheezing or shortness of breath',
refillsRemaining: 0,
totalRefills: 3,
lastFilledDate: 'Jun 15, 2026',
nextDueDate: 'Aug 18, 2026',
isDue: false,
prescriber: {
name: 'Dr. Sarah Lin, MD',
specialty: 'Pulmonology',
clinic: 'Westside Respiratory Clinic',
phone: '(310) 555-0195',
},
status: 'needs_auth',
daysSupply: 30,
copayEst: 15,
ndc: '59310-0579-22',
},
{
id: 'rx-4',
rxNumber: 'RX-910384',
name: 'Atorvastatin Calcium 40mg',
genericFor: 'Generic for Lipitor',
dosageForm: 'Oral Tablet',
instructions: 'Take 1 tablet by mouth once daily at bedtime',
refillsRemaining: 1,
totalRefills: 4,
lastFilledDate: 'Aug 19, 2026',
nextDueDate: 'Nov 19, 2026',
isDue: false,
prescriber: {
name: 'Dr. Marcus Chen, MD',
specialty: 'Cardiology',
clinic: 'Pacific Heart & Vascular Institute',
phone: '(310) 555-0188',
},
status: 'in_transit',
daysSupply: 90,
copayEst: 20,
ndc: '00071-0157-23',
packageTracking: {
carrier: 'USPS Priority Rx Express',
trackingNumber: '9400 1118 9956 2831 4092 11',
status: 'Out for Delivery',
estimatedArrival: 'Today by 6:00 PM',
steps: [
{ title: 'Prescription Refill Ordered', time: 'Aug 19, 9:30 AM', done: true },
{ title: 'Verified & Dispensed by Pharmacist', time: 'Aug 19, 2:15 PM', done: true },
{ title: 'Shipped from Regional Fulfillment', time: 'Aug 20, 8:00 AM', done: true },
{ title: 'Out for Delivery', time: 'Aug 21, 8:45 AM', done: true, current: true },
],
},
},
]
export const defaultAvailablePharmacies: Pharmacy[] = [
{
id: 'cvs-4829',
name: 'CVS Pharmacy #4829',
subtitle: '24-Hour Drive-Thru',
address: '4500 Wilshire Blvd, Los Angeles, CA 90010',
phone: '(213) 555-0198',
fax: '(213) 555-0199',
hours: 'Open 24 Hours · Drive-Thru Open',
isDriveThru: true,
},
{
id: 'walgreens-1204',
name: 'Walgreens Pharmacy #1204',
subtitle: 'Full Service Pharmacy',
address: '3724 W Olympic Blvd, Los Angeles, CA 90019',
phone: '(323) 555-0144',
fax: '(323) 555-0145',
hours: 'Mon-Sun: 8:00 AM – 10:00 PM',
isDriveThru: false,
},
{
id: 'kaiser-sunset',
name: 'Kaiser Sunset Medical Center Pharmacy',
subtitle: 'Specialty & Mail Hub',
address: '4760 Sunset Blvd, Los Angeles, CA 90027',
phone: '(323) 555-0177',
fax: '(323) 555-0178',
hours: 'Mon-Fri: 7:00 AM – 9:00 PM · Sat-Sun: 8:00 AM – 6:00 PM',
isDriveThru: true,
},
]
export type PrescriptionStatus = 'ready' | 'needs_auth' | 'in_transit' | 'active'
export 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 }[]
}
}
export interface Pharmacy {
id: string
name: string
subtitle: string
address: string
phone: string
fax: string
hours: string
isDriveThru: boolean
}
export interface TransferFormState {
pharmacyName: string
phone: string
rxNumber: string
medicationName: string
notes: string
}
Raw manifest:https://uipkge.dev/r/vue/prescription-refill-manager.json