Menu

Change language

GitHub 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.

Installation

$npx uipkge-ng@latest add prescription-refill-manager
Manifest:https://uipkge.dev/r/angular/prescription-refill-manager.jsonInstalls to:src/app/components/blocks/prescription-refill-manager/

Variants

Loading interactive previews…

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 (2)

  • src/app/components/blocks/prescription-refill-manager/prescription-refill-manager.component.ts56.5 kB
    // Pharmacy patient portal: active prescriptions, refill requests, dosage schedules,
    // delivery tracking, and preferred pharmacy details with integrated dialogs.
    // Consolidates the 7 Vue files (manager + table + 5 dialogs + data + types) into one
    // standalone Angular block. Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'
    import {
      AlertCircle,
      ArrowLeftRight,
      Building2,
      Calendar,
      Check,
      CheckCircle2,
      Clock,
      FileText,
      Info,
      LucideAngularModule,
      MapPin,
      MoreHorizontal,
      Phone,
      Pill,
      Plus,
      RefreshCw,
      Search,
      ShieldAlert,
      Store,
      Truck,
    } from 'lucide-angular'
    import { cn } from '@/lib/utils'
    import { UiBadgeComponent } from '@/ui/badge/badge.component'
    import { UiButtonComponent } from '@/ui/button/button.component'
    import {
      UiCardComponent,
      UiCardContentComponent,
      UiCardDescriptionComponent,
      UiCardFooterComponent,
      UiCardHeaderComponent,
      UiCardTitleComponent,
    } from '@/ui/card/card.component'
    import {
      UiDialogComponent,
      UiDialogContentComponent,
      UiDialogDescriptionComponent,
      UiDialogFooterComponent,
      UiDialogHeaderComponent,
      UiDialogTitleComponent,
    } from '@/ui/dialog/dialog.component'
    import {
      UiDropdownMenuComponent,
      UiDropdownMenuContentComponent,
      UiDropdownMenuItemComponent,
      UiDropdownMenuLabelComponent,
      UiDropdownMenuSeparatorComponent,
      UiDropdownMenuTriggerComponent,
    } from '@/ui/dropdown-menu/dropdown-menu.component'
    import { UiInputComponent } from '@/ui/input/input.component'
    import { UiSeparatorComponent } from '@/ui/separator/separator.component'
    import {
      UiTableBodyComponent,
      UiTableCellComponent,
      UiTableComponent,
      UiTableHeadComponent,
      UiTableHeaderComponent,
      UiTableRowComponent,
    } from '@/ui/table/table.component'
    import { UiTextareaComponent } from '@/ui/textarea/textarea.component'
    
    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
    }
    
    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 },
          ],
        },
      },
    ]
    
    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,
      },
    ]
    
    type StatusFilter = 'all' | 'ready' | 'needs_auth' | 'in_transit'
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-prescription-refill-manager, [ui-prescription-refill-manager]',
      standalone: true,
      // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.
      host: { '[attr.class]': '"contents"' },
      imports: [
        LucideAngularModule,
        UiBadgeComponent,
        UiButtonComponent,
        UiCardComponent,
        UiCardContentComponent,
        UiCardDescriptionComponent,
        UiCardFooterComponent,
        UiCardHeaderComponent,
        UiCardTitleComponent,
        UiDialogComponent,
        UiDialogContentComponent,
        UiDialogDescriptionComponent,
        UiDialogFooterComponent,
        UiDialogHeaderComponent,
        UiDialogTitleComponent,
        UiDropdownMenuComponent,
        UiDropdownMenuContentComponent,
        UiDropdownMenuItemComponent,
        UiDropdownMenuLabelComponent,
        UiDropdownMenuSeparatorComponent,
        UiDropdownMenuTriggerComponent,
        UiInputComponent,
        UiSeparatorComponent,
        UiTableBodyComponent,
        UiTableCellComponent,
        UiTableComponent,
        UiTableHeadComponent,
        UiTableHeaderComponent,
        UiTableRowComponent,
        UiTextareaComponent,
      ],
      template: `
        <div data-slot="prescription-refill-manager" [class]="rootClass">
          <!-- 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">
                  <lucide-icon [img]="Pill" class="size-4" />
                </div>
                <h1 class="text-foreground text-xl font-bold tracking-tight sm:text-2xl">Prescriptions & Medications</h1>
                <span ui-badge variant="outline" class="border-primary/20 bg-primary/5 text-primary text-xs font-medium">
                  Active Care Plan
                </span>
              </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 ui-button variant="outline" class="gap-1.5 text-xs font-medium" (click)="handleOpenTransfer()">
                <lucide-icon [img]="ArrowLeftRight" class="size-3.5" />
                Transfer Prescription
              </button>
              <button ui-button variant="default" class="gap-1.5 text-xs font-medium shadow-xs" (click)="openRefillModal()">
                <lucide-icon [img]="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">
            <div ui-card class="shadow-xs">
              <div ui-card-content 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">
                  <lucide-icon [img]="Pill" class="size-5" />
                </div>
              </div>
            </div>
    
            <div ui-card class="border-warning/30 bg-warning/5 bg-warning/10 shadow-xs">
              <div ui-card-content 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>
                    <span
                      ui-badge
                      variant="outline"
                      class="border-warning/40 bg-warning/15 text-warning text-xs font-medium"
                    >
                      2 Action Needed
                    </span>
                  </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">
                  <lucide-icon [img]="AlertCircle" class="size-5" />
                </div>
              </div>
            </div>
    
            <div ui-card class="shadow-xs sm:col-span-2 lg:col-span-1">
              <div ui-card-content 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>
                    <span ui-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"></span>
                      1 En Route
                    </span>
                  </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">
                  <lucide-icon [img]="Truck" class="size-5" />
                </div>
              </div>
            </div>
          </section>
    
          <!-- Preferred Pharmacy Card -->
          <div ui-card class="border-border shadow-xs">
            <div ui-card-header 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"
                  >
                    <lucide-icon [img]="Store" class="text-primary size-4" />
                  </div>
                  <div class="space-y-0.5">
                    <div class="flex flex-wrap items-center gap-2">
                      <h3 ui-card-title class="text-base font-semibold">
                        {{ activePharmacy().name }} · {{ activePharmacy().subtitle }}
                      </h3>
                      <span ui-badge variant="secondary" class="gap-1 text-xs font-medium">
                        <lucide-icon [img]="CheckCircle2" class="text-success size-3" />
                        Preferred Pharmacy
                      </span>
                      <span
                        ui-badge
                        variant="outline"
                        class="border-success/30 bg-success/10 text-success text-xs font-medium"
                      >
                        {{ activePharmacy().hours }}
                      </span>
                    </div>
                    <p ui-card-description class="text-xs">
                      Default location for prescription fulfillment, drive-thru pick-ups, and transfers.
                    </p>
                  </div>
                </div>
    
                <button
                  ui-button
                  variant="outline"
                  size="sm"
                  class="shrink-0 gap-1.5 text-xs font-medium"
                  (click)="handleChangePharmacy()"
                >
                  <lucide-icon [img]="Building2" class="size-3.5" />
                  Change Pharmacy
                </button>
              </div>
            </div>
            <ui-separator />
            <div ui-card-content 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">
                  <lucide-icon [img]="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">
                  <lucide-icon [img]="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">
                  <lucide-icon [img]="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>
            </div>
          </div>
    
          <!-- Prescriptions List Table Section -->
          <div ui-card class="border-border shadow-xs">
            <div ui-card-header class="pb-3">
              <div class="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
                <div>
                  <h3 ui-card-title class="text-lg font-bold">
                    Active Prescriptions ({{ filteredPrescriptions().length }})
                  </h3>
                  <p ui-card-description class="text-xs">
                    Review remaining refills, dosage instructions, and ordering eligibility.
                  </p>
                </div>
    
                <div class="flex flex-wrap items-center gap-2">
                  <div class="relative w-full sm:w-64">
                    <lucide-icon
                      [img]="Search"
                      class="text-muted-foreground absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2"
                    />
                    <ui-input
                      [value]="searchQuery()"
                      (valueChange)="searchQuery.set($event)"
                      placeholder="Search medication, Rx#, doctor..."
                      class="h-8 w-full pr-3 pl-8 text-xs shadow-xs"
                    />
                  </div>
    
                  <div class="flex items-center gap-1">
                    <button
                      ui-button
                      variant="ghost"
                      size="sm"
                      [class]="filterButtonClass('all')"
                      (click)="statusFilter.set('all')"
                    >
                      All ({{ prescriptions().length }})
                    </button>
                    <button
                      ui-button
                      variant="ghost"
                      size="sm"
                      [class]="filterButtonClass('ready')"
                      (click)="statusFilter.set('ready')"
                    >
                      Ready for Refill (2)
                    </button>
                    <button
                      ui-button
                      variant="ghost"
                      size="sm"
                      [class]="filterButtonClass('in_transit')"
                      (click)="statusFilter.set('in_transit')"
                    >
                      In Transit (1)
                    </button>
                    <button
                      ui-button
                      variant="ghost"
                      size="sm"
                      [class]="filterButtonClass('needs_auth')"
                      (click)="statusFilter.set('needs_auth')"
                    >
                      Needs Auth (1)
                    </button>
                  </div>
                </div>
              </div>
            </div>
    
            <div class="overflow-x-auto">
              <table ui-table>
                <thead ui-table-header>
                  <tr ui-table-row class="hover:bg-transparent">
                    <th ui-table-head class="min-w-[220px] text-xs font-semibold">Medication & Rx#</th>
                    <th ui-table-head class="min-w-[240px] text-xs font-semibold">Directions & Dosage</th>
                    <th ui-table-head class="min-w-[170px] text-xs font-semibold">Refill Status</th>
                    <th ui-table-head class="min-w-[160px] text-xs font-semibold">Fill Dates / Schedule</th>
                    <th ui-table-head class="min-w-[180px] text-xs font-semibold">Prescribing Doctor</th>
                    <th ui-table-head class="min-w-[150px] text-right text-xs font-semibold">Quick Action</th>
                  </tr>
                </thead>
                <tbody ui-table-body>
                  @for (rx of filteredPrescriptions(); track rx.id) {
                    <tr ui-table-row [class]="prescriptionRowClass(rx)">
                      <td ui-table-cell 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>
                      </td>
    
                      <td ui-table-cell 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>
                      </td>
    
                      <td ui-table-cell class="py-4 align-top">
                        <div class="space-y-1.5">
                          @if (rx.status === 'ready') {
                            <span
                              ui-badge
                              variant="outline"
                              class="border-warning/40 bg-warning/10 text-warning gap-1 text-xs font-medium"
                            >
                              <lucide-icon [img]="AlertCircle" class="size-3" />
                              {{ rx.refillsRemaining }} refills remaining
                            </span>
                          } @else if (rx.status === 'needs_auth') {
                            <span
                              ui-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"
                            >
                              <lucide-icon [img]="ShieldAlert" class="size-3" />
                              0 refills - Needs MD authorization
                            </span>
                          } @else if (rx.status === 'in_transit') {
                            <span
                              ui-badge
                              variant="outline"
                              class="border-info/30 bg-info/10 text-info gap-1 text-xs font-medium"
                            >
                              <lucide-icon [img]="Truck" class="size-3" />
                              In Transit · Est. Arrival Today
                            </span>
                          } @else {
                            <span ui-badge variant="secondary" class="text-xs font-medium">
                              {{ rx.refillsRemaining }} of {{ rx.totalRefills }} remaining
                            </span>
                          }
    
                          <p class="text-muted-foreground text-xs tabular-nums">
                            Est. Copay: <strong class="text-foreground">\${{ rx.copayEst }}.00</strong>
                          </p>
                        </div>
                      </td>
    
                      <td ui-table-cell class="py-4 align-top text-xs">
                        <div class="space-y-1">
                          <div class="text-muted-foreground flex items-center gap-1.5">
                            <lucide-icon [img]="Calendar" class="size-3 shrink-0" />
                            <span>
                              Last Filled:
                              <strong class="text-foreground font-medium tabular-nums">{{ rx.lastFilledDate }}</strong>
                            </span>
                          </div>
                          <div [class]="dueDateClass(rx)">
                            <lucide-icon [img]="Clock" class="size-3 shrink-0" />
                            <span>Next Due: {{ rx.nextDueDate }}</span>
                          </div>
                          @if (rx.isDue) {
                            <span class="text-warning inline-block text-xs font-medium"> Refill window open </span>
                          }
                        </div>
                      </td>
    
                      <td ui-table-cell 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>
                      </td>
    
                      <td ui-table-cell class="py-4 text-right align-top">
                        <div class="flex items-center justify-end gap-1.5">
                          @if (rx.status === 'ready') {
                            <button
                              ui-button
                              variant="default"
                              size="sm"
                              class="h-8 gap-1 text-xs font-medium shadow-xs"
                              (click)="openRefillModal(rx.id)"
                            >
                              <lucide-icon [img]="RefreshCw" class="size-3" />
                              Order Refill
                            </button>
                          } @else if (rx.status === 'needs_auth') {
                            <button
                              ui-button
                              variant="outline"
                              size="sm"
                              class="border-destructive/30 text-destructive hover:bg-destructive/10 h-8 gap-1 text-xs font-medium"
                              (click)="handleOpenAuthRequest(rx)"
                            >
                              <lucide-icon [img]="Clock" class="size-3" />
                              Request Auth
                            </button>
                          } @else if (rx.status === 'in_transit') {
                            <button
                              ui-button
                              variant="secondary"
                              size="sm"
                              class="h-8 gap-1 text-xs font-medium"
                              (click)="handleOpenTracking(rx)"
                            >
                              <lucide-icon [img]="Truck" class="text-info size-3" />
                              Track
                            </button>
                          }
    
                          <ui-dropdown-menu>
                            <button
                              ui-button
                              ui-dropdown-menu-trigger
                              variant="ghost"
                              size="icon"
                              class="size-8"
                              [attr.aria-label]="'Prescription options for ' + rx.name"
                            >
                              <lucide-icon [img]="MoreHorizontal" class="size-4" />
                            </button>
                            <ui-dropdown-menu-content align="end" class="w-52">
                              <ui-dropdown-menu-label class="text-xs font-semibold">Rx Options</ui-dropdown-menu-label>
                              <ui-dropdown-menu-separator />
                              @if (rx.status === 'ready') {
                                <ui-dropdown-menu-item class="gap-2 text-xs" (select)="openRefillModal(rx.id)">
                                  <lucide-icon [img]="RefreshCw" class="text-primary size-3.5" />
                                  <span>Order Refill (30/90 Days)</span>
                                </ui-dropdown-menu-item>
                              }
                              @if (rx.status === 'needs_auth') {
                                <ui-dropdown-menu-item
                                  class="text-destructive gap-2 text-xs"
                                  (select)="handleOpenAuthRequest(rx)"
                                >
                                  <lucide-icon [img]="ShieldAlert" class="size-3.5" />
                                  <span>Request MD Renewal</span>
                                </ui-dropdown-menu-item>
                              }
                              @if (rx.packageTracking) {
                                <ui-dropdown-menu-item class="gap-2 text-xs" (select)="handleOpenTracking(rx)">
                                  <lucide-icon [img]="Truck" class="text-info size-3.5" />
                                  <span>Track USPS Delivery</span>
                                </ui-dropdown-menu-item>
                              }
                              <ui-dropdown-menu-item class="gap-2 text-xs" (select)="handleOpenTransfer()">
                                <lucide-icon [img]="ArrowLeftRight" class="size-3.5" />
                                <span>Transfer to Another Pharmacy</span>
                              </ui-dropdown-menu-item>
                              <ui-dropdown-menu-separator />
                              <ui-dropdown-menu-item class="gap-2 text-xs">
                                <lucide-icon [img]="FileText" class="text-muted-foreground size-3.5" />
                                <span>View Full Drug Monograph</span>
                              </ui-dropdown-menu-item>
                              <ui-dropdown-menu-item class="gap-2 text-xs">
                                <lucide-icon [img]="Phone" class="text-muted-foreground size-3.5" />
                                <span>Call Prescriber ({{ rx.prescriber.phone }})</span>
                              </ui-dropdown-menu-item>
                            </ui-dropdown-menu-content>
                          </ui-dropdown-menu>
                        </div>
                      </td>
                    </tr>
                  }
                </tbody>
              </table>
            </div>
    
            <div
              ui-card-footer
              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">
                <lucide-icon [img]="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>
            </div>
          </div>
    
          <!-- Refill Request Dialog -->
          <ui-dialog [open]="isRefillOpen()" (openChange)="onRefillOpenChange($event)">
            @if (selectedPrescription(); as rx) {
              <ui-dialog-content class="sm:max-w-xl">
                <div ui-dialog-header>
                  <div class="flex items-center gap-2">
                    <div class="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg">
                      <lucide-icon [img]="RefreshCw" class="size-4" />
                    </div>
                    <div>
                      <h2 ui-dialog-title class="text-lg font-bold">Request Prescription Refill</h2>
                      <p ui-dialog-description class="text-xs">
                        Select your supply duration, delivery method, and confirm your copay.
                      </p>
                    </div>
                  </div>
                </div>
    
                @if (orderSubmitted()) {
                  <div 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">
                        <lucide-icon [img]="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>{{ rx.name }}</strong> ({{ supplyDuration() }}-day supply) has been sent to
                        {{ activePharmacy().name }}.
                      </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">{{
                          rx.refillsRemaining - 1 >= 0 ? rx.refillsRemaining - 1 : 0
                        }}</strong>
                      </div>
                    </div>
    
                    <div ui-dialog-footer class="pt-2">
                      <button ui-button variant="default" class="w-full" (click)="isRefillOpen.set(false)">
                        Done & Return to Prescriptions
                      </button>
                    </div>
                  </div>
                } @else {
                  <div class="space-y-5 py-2">
                    <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">{{ rx.name }}</h4>
                          <p class="text-muted-foreground text-xs">{{ rx.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">
                            {{ rx.rxNumber }}
                          </span>
                          <p class="text-success text-xs font-medium">{{ rx.refillsRemaining }} refills remaining</p>
                        </div>
                      </div>
                    </div>
    
                    <div class="space-y-2">
                      <span class="text-foreground text-xs font-semibold">Supply Duration</span>
                      <div class="grid grid-cols-2 gap-2">
                        <button
                          ui-button
                          [variant]="supplyDuration() === '30' ? 'default' : 'outline'"
                          size="sm"
                          (click)="supplyDuration.set('30')"
                        >
                          30-Day Supply · \${{ rx.copayEst }}.00
                        </button>
                        <button
                          ui-button
                          [variant]="supplyDuration() === '90' ? 'default' : 'outline'"
                          size="sm"
                          (click)="supplyDuration.set('90')"
                        >
                          90-Day Supply · $20.00
                        </button>
                      </div>
                    </div>
    
                    <div class="space-y-2">
                      <span class="text-foreground text-xs font-semibold">Fulfillment Method</span>
                      <div class="grid grid-cols-2 gap-2">
                        <button
                          ui-button
                          [variant]="deliveryMethod() === 'pickup' ? 'default' : 'outline'"
                          size="sm"
                          class="gap-1.5"
                          (click)="deliveryMethod.set('pickup')"
                        >
                          <lucide-icon [img]="Store" class="size-3.5" />
                          In-Store Pickup
                        </button>
                        <button
                          ui-button
                          [variant]="deliveryMethod() === 'delivery' ? 'default' : 'outline'"
                          size="sm"
                          class="gap-1.5"
                          (click)="deliveryMethod.set('delivery')"
                        >
                          <lucide-icon [img]="Truck" class="size-3.5" />
                          Home Delivery
                        </button>
                      </div>
                    </div>
    
                    <div ui-dialog-footer class="pt-2">
                      <button ui-button variant="outline" (click)="isRefillOpen.set(false)">Cancel</button>
                      <button ui-button variant="default" class="gap-1.5 shadow-xs" (click)="submitRefillOrder()">
                        <lucide-icon [img]="RefreshCw" class="size-3.5" />
                        Place Refill Order
                      </button>
                    </div>
                  </div>
                }
              </ui-dialog-content>
            }
          </ui-dialog>
    
          <!-- Tracking Dialog -->
          <ui-dialog [open]="isTrackingOpen()" (openChange)="isTrackingOpen.set($event)">
            @if (trackingPrescription(); as rx) {
              <ui-dialog-content class="sm:max-w-lg">
                <div ui-dialog-header>
                  <div class="flex items-center gap-2">
                    <div class="bg-info/10 text-info flex size-8 items-center justify-center rounded-lg">
                      <lucide-icon [img]="Truck" class="size-4" />
                    </div>
                    <div>
                      <h2 ui-dialog-title class="text-lg font-bold">Delivery & Tracking Status</h2>
                      <p ui-dialog-description class="text-xs">Tracking details for {{ rx.name }}</p>
                    </div>
                  </div>
                </div>
    
                @if (rx.packageTracking; as tracking) {
                  <div class="space-y-4 py-2 text-xs">
                    <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">{{ tracking.carrier }}</strong>
                      </div>
                      <div class="flex items-center justify-between">
                        <span class="text-muted-foreground">Tracking Number:</span>
                        <strong class="text-foreground font-mono">{{ tracking.trackingNumber }}</strong>
                      </div>
                      <div class="flex items-center justify-between">
                        <span class="text-muted-foreground">Status:</span>
                        <span ui-badge variant="outline" class="border-info/30 bg-info/10 text-info text-xs font-semibold">
                          {{ tracking.status }}
                        </span>
                      </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">{{ tracking.estimatedArrival }}</strong>
                      </div>
                    </div>
    
                    <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">
                        @for (step of tracking.steps; track step.title) {
                          <div class="relative space-y-0.5">
                            <div [class]="trackingDotClass(step)"></div>
                            <p
                              [class]="
                                step.current ? 'text-info text-xs font-semibold' : 'text-foreground text-xs font-semibold'
                              "
                            >
                              {{ step.title }}
                            </p>
                            <p class="text-muted-foreground text-xs tabular-nums">{{ step.time }}</p>
                          </div>
                        }
                      </div>
                    </div>
                  </div>
                }
    
                <div ui-dialog-footer>
                  <button ui-button variant="outline" class="w-full sm:w-auto" (click)="isTrackingOpen.set(false)">
                    Close Tracking
                  </button>
                </div>
              </ui-dialog-content>
            }
          </ui-dialog>
    
          <!-- Transfer Prescription Dialog -->
          <ui-dialog [open]="isTransferOpen()" (openChange)="onTransferOpenChange($event)">
            <ui-dialog-content class="sm:max-w-lg">
              <div ui-dialog-header>
                <div class="flex items-center gap-2">
                  <div class="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg">
                    <lucide-icon [img]="ArrowLeftRight" class="size-4" />
                  </div>
                  <div>
                    <h2 ui-dialog-title class="text-lg font-bold">Transfer Prescription</h2>
                    <p ui-dialog-description class="text-xs">
                      Move an existing prescription from another pharmacy to {{ activePharmacy().name }}.
                    </p>
                  </div>
                </div>
              </div>
    
              @if (transferSubmitted()) {
                <div 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">
                      <lucide-icon [img]="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>
                  <div ui-dialog-footer>
                    <button ui-button variant="default" class="w-full" (click)="isTransferOpen.set(false)">Done</button>
                  </div>
                </div>
              } @else {
                <div 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>
                    <ui-input
                      [value]="transferForm().pharmacyName"
                      (valueChange)="patchTransfer({ pharmacyName: $event })"
                      placeholder="e.g. Walgreens, Rite Aid, Walmart..."
                      class="h-9 text-xs shadow-xs"
                    />
                  </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>
                      <ui-input
                        [value]="transferForm().phone"
                        (valueChange)="patchTransfer({ phone: $event })"
                        type="tel"
                        placeholder="(555) 000-0000"
                        class="h-9 text-xs shadow-xs"
                      />
                    </div>
                    <div class="space-y-1">
                      <label class="text-foreground text-xs font-medium">Existing Rx Number (if known)</label>
                      <ui-input
                        [value]="transferForm().rxNumber"
                        (valueChange)="patchTransfer({ rxNumber: $event })"
                        placeholder="e.g. RX-123456"
                        class="h-9 font-mono text-xs shadow-xs"
                      />
                    </div>
                  </div>
    
                  <div class="space-y-1">
                    <label class="text-foreground text-xs font-medium">Medication Name & Strength</label>
                    <ui-input
                      [value]="transferForm().medicationName"
                      (valueChange)="patchTransfer({ medicationName: $event })"
                      placeholder="e.g. Lipitor 20mg or Levothyroxine 50mcg"
                      class="h-9 text-xs shadow-xs"
                    />
                  </div>
    
                  <div class="space-y-1">
                    <label class="text-foreground text-xs font-medium"
                      >Special Instructions / Prescriber Notes (Optional)</label
                    >
                    <ui-textarea
                      [value]="transferForm().notes"
                      (valueChange)="patchTransfer({ notes: $event })"
                      [rows]="2"
                      placeholder="Any additional notes for the receiving pharmacist..."
                      class="text-xs shadow-xs"
                    />
                  </div>
    
                  <div ui-dialog-footer class="pt-2">
                    <button ui-button variant="outline" (click)="isTransferOpen.set(false)">Cancel</button>
                    <button ui-button variant="default" class="gap-1.5 shadow-xs" (click)="submitTransfer()">
                      <lucide-icon [img]="ArrowLeftRight" class="size-3.5" />
                      Initiate Transfer
                    </button>
                  </div>
                </div>
              }
            </ui-dialog-content>
          </ui-dialog>
    
          <!-- Change Preferred Pharmacy Dialog -->
          <ui-dialog [open]="isChangePharmacyOpen()" (openChange)="isChangePharmacyOpen.set($event)">
            <ui-dialog-content class="sm:max-w-lg">
              <div ui-dialog-header>
                <div class="flex items-center gap-2">
                  <div class="bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg">
                    <lucide-icon [img]="Building2" class="size-4" />
                  </div>
                  <div>
                    <h2 ui-dialog-title class="text-lg font-bold">Select Preferred Pharmacy</h2>
                    <p ui-dialog-description class="text-xs">
                      Choose the location where your local orders and drive-thru pickups are routed.
                    </p>
                  </div>
                </div>
              </div>
    
              <div class="space-y-3 py-2 text-xs">
                @for (pharmacy of availablePharmacies(); track pharmacy.id) {
                  <div [class]="pharmacyOptionClass(pharmacy)" (click)="tempSelectedPharmacyId.set(pharmacy.id)">
                    <div class="space-y-1">
                      <div class="flex items-center gap-2">
                        <span class="text-foreground font-bold">{{ pharmacy.name }}</span>
                        @if (pharmacy.id === activePharmacy().id) {
                          <span ui-badge variant="secondary" class="text-xs">Current</span>
                        }
                      </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]="pharmacyRadioClass(pharmacy)">
                        @if (tempSelectedPharmacyId() === pharmacy.id) {
                          <div class="bg-background size-1.5 rounded-full"></div>
                        }
                      </div>
                    </div>
                  </div>
                }
              </div>
    
              <div ui-dialog-footer class="pt-2">
                <button ui-button variant="outline" (click)="isChangePharmacyOpen.set(false)">Cancel</button>
                <button ui-button variant="default" (click)="savePharmacyChange()">Save Preferred Pharmacy</button>
              </div>
            </ui-dialog-content>
          </ui-dialog>
    
          <!-- Request MD Authorization Dialog -->
          <ui-dialog [open]="isAuthRequestOpen()" (openChange)="isAuthRequestOpen.set($event)">
            @if (authRequestedRx(); as rx) {
              <ui-dialog-content class="sm:max-w-md">
                <div ui-dialog-header>
                  <div class="flex items-center gap-2">
                    <div class="bg-destructive/10 text-destructive flex size-8 items-center justify-center rounded-lg">
                      <lucide-icon [img]="ShieldAlert" class="size-4" />
                    </div>
                    <div>
                      <h2 ui-dialog-title class="text-lg font-bold">Request Prescriber Authorization</h2>
                      <p ui-dialog-description class="text-xs">Renew zero-refill prescription with your physician.</p>
                    </div>
                  </div>
                </div>
    
                @if (authRequestSubmitted()) {
                  <div 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">
                      <lucide-icon [img]="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>{{ rx.name }}</strong> has been electronically transmitted to
                      <strong>{{ rx.prescriber.name }}</strong
                      >.
                    </p>
                    <div ui-dialog-footer class="pt-2">
                      <button ui-button variant="default" class="w-full" (click)="isAuthRequestOpen.set(false)">
                        Done
                      </button>
                    </div>
                  </div>
                } @else {
                  <div 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">{{ rx.name }}</p>
                      <p class="text-muted-foreground">{{ rx.genericFor }}</p>
                      <p class="text-muted-foreground">
                        Prescribing Physician: <strong class="text-foreground">{{ rx.prescriber.name }}</strong>
                      </p>
                      <p class="text-muted-foreground">Clinic: {{ rx.prescriber.clinic }} ({{ rx.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>
    
                    <div ui-dialog-footer>
                      <button ui-button variant="outline" (click)="isAuthRequestOpen.set(false)">Cancel</button>
                      <button ui-button variant="default" class="gap-1.5" (click)="authRequestSubmitted.set(true)">
                        <lucide-icon [img]="Check" class="size-3.5" />
                        Send Renewal Request
                      </button>
                    </div>
                  </div>
                }
              </ui-dialog-content>
            }
          </ui-dialog>
        </div>
      `,
    })
    export class UiPrescriptionRefillManagerComponent {
      protected readonly AlertCircle = AlertCircle
      protected readonly ArrowLeftRight = ArrowLeftRight
      protected readonly Building2 = Building2
      protected readonly Calendar = Calendar
      protected readonly Check = Check
      protected readonly CheckCircle2 = CheckCircle2
      protected readonly Clock = Clock
      protected readonly FileText = FileText
      protected readonly Info = Info
      protected readonly MapPin = MapPin
      protected readonly MoreHorizontal = MoreHorizontal
      protected readonly Phone = Phone
      protected readonly Pill = Pill
      protected readonly Plus = Plus
      protected readonly RefreshCw = RefreshCw
      protected readonly Search = Search
      protected readonly ShieldAlert = ShieldAlert
      protected readonly Store = Store
      protected readonly Truck = Truck
    
      @Input('class') className?: string
      @Input() set initialPharmacyId(v: string | undefined) {
        const found = this.availablePharmacies().find((p) => p.id === v)
        if (found) this.activePharmacy.set(found)
      }
    
      readonly prescriptions = signal<Prescription[]>(defaultPrescriptions.map((p) => ({ ...p })))
      readonly availablePharmacies = signal<Pharmacy[]>(defaultAvailablePharmacies.map((p) => ({ ...p })))
      readonly activePharmacy = signal<Pharmacy>({ ...defaultAvailablePharmacies[0] })
    
      readonly searchQuery = signal('')
      readonly statusFilter = signal<StatusFilter>('all')
    
      readonly isRefillOpen = signal(false)
      readonly selectedPrescriptionId = signal('rx-1')
      readonly supplyDuration = signal<'30' | '90'>('90')
      readonly deliveryMethod = signal<'pickup' | 'delivery'>('delivery')
      readonly orderSubmitted = signal(false)
    
      readonly isTrackingOpen = signal(false)
      readonly trackingPrescription = signal<Prescription | null>(null)
    
      readonly isTransferOpen = signal(false)
      readonly transferForm = signal<TransferFormState>({
        pharmacyName: '',
        phone: '',
        rxNumber: '',
        medicationName: '',
        notes: '',
      })
      readonly transferSubmitted = signal(false)
    
      readonly isChangePharmacyOpen = signal(false)
      readonly tempSelectedPharmacyId = signal(defaultAvailablePharmacies[0].id)
    
      readonly isAuthRequestOpen = signal(false)
      readonly authRequestedRx = signal<Prescription | null>(null)
      readonly authRequestSubmitted = signal(false)
    
      readonly filteredPrescriptions = computed(() => {
        const q = this.searchQuery().toLowerCase()
        const f = this.statusFilter()
        return this.prescriptions().filter((rx) => {
          const matchesSearch =
            rx.name.toLowerCase().includes(q) ||
            rx.rxNumber.toLowerCase().includes(q) ||
            rx.genericFor.toLowerCase().includes(q) ||
            rx.prescriber.name.toLowerCase().includes(q)
          if (!matchesSearch) return false
          if (f === 'all') return true
          return rx.status === f
        })
      })
    
      readonly selectedPrescription = computed<Prescription>(() => {
        return this.prescriptions().find((rx) => rx.id === this.selectedPrescriptionId()) ?? this.prescriptions()[0]
      })
    
      readonly estimatedCopay = computed(() =>
        this.supplyDuration() === '90' ? 20.0 : this.selectedPrescription().copayEst,
      )
    
      get rootClass(): string {
        return cn('bg-background text-foreground w-full space-y-6', this.className)
      }
    
      filterButtonClass(filter: StatusFilter): string {
        const active = this.statusFilter() === filter
        return cn(
          'h-8 text-xs font-medium',
          active && 'bg-accent text-accent-foreground',
          active && filter === 'ready' && 'text-warning',
          active && filter === 'needs_auth' && 'text-destructive',
        )
      }
    
      prescriptionRowClass(rx: Prescription): string {
        return cn('transition-colors', rx.status === 'ready' && 'bg-warning/[0.02] bg-warning/[0.04]')
      }
    
      dueDateClass(rx: Prescription): string {
        return cn(
          'flex items-center gap-1.5 font-medium tabular-nums',
          rx.isDue ? 'text-warning font-semibold' : 'text-muted-foreground',
        )
      }
    
      trackingDotClass(step: { done: boolean; current?: boolean }): string {
        return 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',
        )
      }
    
      pharmacyOptionClass(pharmacy: Pharmacy): string {
        return cn(
          'flex cursor-pointer items-start justify-between rounded-xl border p-3.5 transition-colors',
          this.tempSelectedPharmacyId() === pharmacy.id
            ? 'border-primary bg-primary/5 ring-primary ring-1'
            : 'border-border bg-card hover:bg-muted/40',
        )
      }
    
      pharmacyRadioClass(pharmacy: Pharmacy): string {
        return cn(
          'flex size-4 items-center justify-center rounded-full border',
          this.tempSelectedPharmacyId() === pharmacy.id
            ? 'border-primary bg-primary text-primary-foreground'
            : 'border-muted-foreground/40',
        )
      }
    
      openRefillModal(prescriptionId?: string): void {
        if (prescriptionId) {
          this.selectedPrescriptionId.set(prescriptionId)
        } else {
          const readyMed = this.prescriptions().find((r) => r.status === 'ready')
          this.selectedPrescriptionId.set(readyMed ? readyMed.id : this.prescriptions()[0].id)
        }
        this.orderSubmitted.set(false)
        this.isRefillOpen.set(true)
      }
    
      onRefillOpenChange(open: boolean): void {
        this.isRefillOpen.set(open)
        if (!open) this.orderSubmitted.set(false)
      }
    
      submitRefillOrder(): void {
        this.orderSubmitted.set(true)
        this.prescriptions.update((prev) =>
          prev.map((rx) =>
            rx.id === this.selectedPrescriptionId()
              ? { ...rx, refillsRemaining: Math.max(0, rx.refillsRemaining - 1) }
              : rx,
          ),
        )
      }
    
      handleOpenTracking(rx: Prescription): void {
        this.trackingPrescription.set(rx)
        this.isTrackingOpen.set(true)
      }
    
      handleOpenAuthRequest(rx: Prescription): void {
        this.authRequestedRx.set(rx)
        this.authRequestSubmitted.set(false)
        this.isAuthRequestOpen.set(true)
      }
    
      handleOpenTransfer(): void {
        this.transferForm.set({ pharmacyName: '', phone: '', rxNumber: '', medicationName: '', notes: '' })
        this.transferSubmitted.set(false)
        this.isTransferOpen.set(true)
      }
    
      onTransferOpenChange(open: boolean): void {
        this.isTransferOpen.set(open)
        if (!open) this.transferSubmitted.set(false)
      }
    
      patchTransfer(patch: Partial<TransferFormState>): void {
        this.transferForm.update((prev) => ({ ...prev, ...patch }))
      }
    
      submitTransfer(): void {
        this.transferSubmitted.set(true)
      }
    
      handleChangePharmacy(): void {
        this.tempSelectedPharmacyId.set(this.activePharmacy().id)
        this.isChangePharmacyOpen.set(true)
      }
    
      savePharmacyChange(): void {
        const chosen = this.availablePharmacies().find((p) => p.id === this.tempSelectedPharmacyId())
        if (chosen) this.activePharmacy.set(chosen)
        this.isChangePharmacyOpen.set(false)
      }
    }
    
  • src/app/components/blocks/prescription-refill-manager/index.ts0.2 kB

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