{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "referral-program-widget",
  "title": "Referral Program Widget",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/referral-program-widget/referral-program-widget.component.ts",
      "content": "// Rewardful / Viral Loops style customer referral program and milestone reward tracker:\n// balance summary header, shareable link + code copy actions with social sharing, a 4-tier\n// milestone stepper with progress, and a recent referrals activity table. Port of the\n// React block 1:1.\nimport { ChangeDetectionStrategy, Component, DestroyRef, Input, inject, signal } from '@angular/core'\nimport {\n  Check,\n  Clock,\n  Copy,\n  Gift,\n  Lock,\n  LucideAngularModule,\n  Mail,\n  Share2,\n  Target,\n  Trophy,\n  Users,\n  Wallet,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport { UiProgressComponent } from '@/ui/progress/progress.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\n\nexport interface MilestoneTier {\n  id: string\n  tier: number\n  referralsRequired: number\n  title: string\n  reward: string\n  description: string\n  status: 'completed' | 'in-progress' | 'upcoming'\n}\n\nexport interface ReferralActivity {\n  id: string\n  name: string\n  email: string\n  initials: string\n  signedUpAt: string\n  status: 'paid' | 'pending'\n  rewardAmount: string\n}\n\nconst tiers: MilestoneTier[] = [\n  {\n    id: 'tier-1',\n    tier: 1,\n    referralsRequired: 1,\n    title: '$25 Account Credit',\n    reward: '$25 Credit',\n    description: 'Instant credit applied directly to your renewal balance.',\n    status: 'completed',\n  },\n  {\n    id: 'tier-2',\n    tier: 2,\n    referralsRequired: 5,\n    title: '$125 Credit + Exclusive Swag Kit',\n    reward: '$125 + Swag Kit',\n    description: 'Premium organic cotton hoodie and engineering desk kit.',\n    status: 'completed',\n  },\n  {\n    id: 'tier-3',\n    tier: 3,\n    referralsRequired: 10,\n    title: '$250 Credit + 1-Year Free Pro Plan',\n    reward: '$250 + Pro Plan',\n    description: '6 of 10 referred · 4 more needed to unlock full Pro tier.',\n    status: 'in-progress',\n  },\n  {\n    id: 'tier-4',\n    tier: 4,\n    referralsRequired: 25,\n    title: '$1,000 Credit + VIP Lifetime Access',\n    reward: '$1,000 + VIP Pass',\n    description: 'Direct Slack channel access to core founders and private beta track.',\n    status: 'upcoming',\n  },\n]\n\nconst recentReferrals: ReferralActivity[] = [\n  {\n    id: 'ref-1',\n    name: 'Marcus Vance',\n    email: 'marcus.v@acme.com',\n    initials: 'MV',\n    signedUpAt: 'Oct 14, 2024',\n    status: 'paid',\n    rewardAmount: '$25.00',\n  },\n  {\n    id: 'ref-2',\n    name: 'Sarah Jenkins',\n    email: 's.jenkins@designcorp.io',\n    initials: 'SJ',\n    signedUpAt: 'Oct 02, 2024',\n    status: 'paid',\n    rewardAmount: '$25.00',\n  },\n  {\n    id: 'ref-3',\n    name: 'Devon Lane',\n    email: 'devon@lane.co',\n    initials: 'DL',\n    signedUpAt: 'Sep 28, 2024',\n    status: 'pending',\n    rewardAmount: '$25.00',\n  },\n  {\n    id: 'ref-4',\n    name: 'Priya Patel',\n    email: 'priya.patel@fintech.io',\n    initials: 'PP',\n    signedUpAt: 'Sep 15, 2024',\n    status: 'paid',\n    rewardAmount: '$25.00',\n  },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-referral-program-widget, [ui-referral-program-widget]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiAvatarComponent,\n    UiAvatarFallbackComponent,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiInputComponent,\n    UiProgressComponent,\n    UiSeparatorComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n  ],\n  template: `\n    <div data-slot=\"referral-program-widget\" [class]=\"rootClass\">\n      <!-- Header Section -->\n      <div class=\"space-y-4\">\n        <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n          <div>\n            <div class=\"flex items-center gap-2\">\n              <span ui-badge variant=\"secondary\" class=\"gap-1.5 font-medium\">\n                <lucide-icon [img]=\"Gift\" class=\"text-primary size-3.5\" />\n                Give $25, Get $25\n              </span>\n            </div>\n            <h1 class=\"text-foreground mt-2 text-2xl font-bold tracking-tight sm:text-3xl\">\n              Refer Friends &amp; Earn Credits\n            </h1>\n            <p class=\"text-muted-foreground mt-1 max-w-2xl text-sm sm:text-base\">\n              Give your network $25 off their first year, and earn $25 credit for every active referral.\n            </p>\n          </div>\n        </div>\n\n        <!-- Referral Balance Card -->\n        <div ui-card class=\"border-border/80 shadow-xs\">\n          <div ui-card-content class=\"p-4 sm:p-6\">\n            <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n              <div class=\"flex items-center gap-4\">\n                <div\n                  class=\"bg-primary/10 text-primary border-primary/20 flex size-12 shrink-0 items-center justify-center rounded-xl border\"\n                >\n                  <lucide-icon [img]=\"Wallet\" class=\"size-6\" />\n                </div>\n                <div>\n                  <p class=\"text-muted-foreground text-xs font-medium tracking-wider uppercase\">Referral Balance</p>\n                  <div class=\"mt-0.5 flex flex-wrap items-baseline gap-2\">\n                    <span class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums sm:text-3xl\">\n                      $150.00\n                    </span>\n                    <span class=\"text-muted-foreground text-sm font-medium\">Available Credit</span>\n                    <span class=\"text-foreground text-sm font-semibold tabular-nums\">6 Successful Referrals</span>\n                  </div>\n                </div>\n              </div>\n              <div class=\"flex items-center gap-2\">\n                <span ui-badge variant=\"outline\" class=\"text-xs font-medium tabular-nums\">1 Pending ($25.00)</span>\n                <button ui-button size=\"sm\" variant=\"outline\">Redeem Credits</button>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <!-- Share Your Referral Link Card -->\n      <div ui-card class=\"border-border/80 shadow-xs\">\n        <div ui-card-header class=\"pb-4\">\n          <div class=\"flex items-center gap-2\">\n            <lucide-icon [img]=\"Share2\" class=\"text-primary size-4\" />\n            <h3 ui-card-title class=\"text-base font-semibold\">Share Your Referral Link</h3>\n          </div>\n          <p ui-card-description>\n            Send your personal invite link or share directly across social channels to earn rewards instantly.\n          </p>\n        </div>\n        <div ui-card-content class=\"space-y-4\">\n          <!-- Referral URL & Copy Action -->\n          <div class=\"flex flex-col gap-2 sm:flex-row\">\n            <div class=\"relative flex-1\">\n              <ui-input\n                [value]=\"referralUrl\"\n                [readOnly]=\"true\"\n                class=\"bg-muted/30 font-mono text-xs sm:text-sm\"\n                aria-label=\"Your custom referral URL\"\n              />\n            </div>\n            <button\n              ui-button\n              type=\"button\"\n              [variant]=\"copiedLink() ? 'outline' : 'default'\"\n              class=\"shrink-0 gap-1.5\"\n              (click)=\"copyToClipboard(referralUrl, 'link')\"\n            >\n              @if (copiedLink()) {\n                <lucide-icon [img]=\"Check\" class=\"text-success size-4\" />\n              } @else {\n                <lucide-icon [img]=\"Copy\" class=\"size-4\" />\n              }\n              <span>{{ copiedLink() ? 'Copied Link' : 'Copy Link' }}</span>\n            </button>\n          </div>\n\n          <ui-separator />\n\n          <!-- Social Share & Code Bar -->\n          <div class=\"flex flex-wrap items-center justify-between gap-2.5\">\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <a\n                ui-button\n                [href]=\"twitterShareUrl\"\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                variant=\"outline\"\n                size=\"sm\"\n                class=\"gap-1.5 text-xs font-medium\"\n              >\n                <svg class=\"size-3.5 shrink-0\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n                  <path\n                    d=\"M18.24 2.25h3.31l-7.23 8.26 8.5 11.24h-6.66l-5.21-6.82-5.97 6.82H1.68l7.73-8.84L1.25 2.25h6.83l4.71 6.23zm-1.16 17.52h1.83L7.08 4.13H5.12z\"\n                  />\n                </svg>\n                Share to X\n              </a>\n              <a\n                ui-button\n                [href]=\"linkedinShareUrl\"\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                variant=\"outline\"\n                size=\"sm\"\n                class=\"gap-1.5 text-xs font-medium\"\n              >\n                <svg class=\"size-3.5 shrink-0\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n                  <path\n                    d=\"M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.14 1.45-2.14 2.94v5.67H9.35V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.46v6.28zM5.34 7.43a2.06 2.06 0 110-4.13 2.06 2.06 0 010 4.13zm1.78 13.02H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.73v20.54C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.73V1.73C24 .77 23.2 0 22.22 0z\"\n                  />\n                </svg>\n                Share to LinkedIn\n              </a>\n              <a ui-button [href]=\"emailShareUrl\" variant=\"outline\" size=\"sm\" class=\"gap-1.5 text-xs font-medium\">\n                <lucide-icon [img]=\"Mail\" class=\"size-3.5 shrink-0\" />\n                Share via Email\n              </a>\n            </div>\n\n            <button\n              ui-button\n              type=\"button\"\n              variant=\"outline\"\n              size=\"sm\"\n              class=\"gap-1.5 text-xs font-medium\"\n              (click)=\"copyToClipboard(referralCode, 'code')\"\n            >\n              @if (copiedCode()) {\n                <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" />\n              } @else {\n                <lucide-icon [img]=\"Copy\" class=\"text-muted-foreground size-3.5\" />\n              }\n              <span>Invite Code:</span>\n              <span class=\"text-foreground font-mono font-semibold\">{{ referralCode }}</span>\n              @if (copiedCode()) {\n                <span class=\"text-success text-xs font-medium\">Copied!</span>\n              }\n            </button>\n          </div>\n        </div>\n      </div>\n\n      <!-- Milestone Rewards Progress Stepper -->\n      <div ui-card class=\"border-border/80 shadow-xs\">\n        <div ui-card-header class=\"pb-4\">\n          <div class=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between\">\n            <div>\n              <div class=\"flex items-center gap-2\">\n                <lucide-icon [img]=\"Trophy\" class=\"text-warning size-4\" />\n                <h3 ui-card-title class=\"text-base font-semibold\">Milestone Rewards</h3>\n              </div>\n              <p ui-card-description>\n                Reach referral milestones to unlock credit bonuses, swag, and exclusive plan access.\n              </p>\n            </div>\n            <span ui-badge variant=\"outline\" class=\"w-fit gap-1 text-xs tabular-nums\">\n              <lucide-icon [img]=\"Target\" class=\"text-primary size-3\" />\n              Tier 3 In Progress · 6 / 10 Referrals\n            </span>\n          </div>\n        </div>\n        <div ui-card-content class=\"space-y-6\">\n          <!-- Progress Stepper Track Header -->\n          <div class=\"border-border/70 bg-muted/40 space-y-2 rounded-lg border p-4\">\n            <div class=\"flex flex-wrap items-center justify-between gap-2 text-xs sm:text-sm\">\n              <span class=\"text-foreground font-medium\">\n                Current Target: Tier 3 ($250 Credit + 1-Year Free Pro Plan)\n              </span>\n              <span class=\"text-muted-foreground font-semibold tabular-nums\">60% (6 of 10 referrals)</span>\n            </div>\n            <ui-progress [value]=\"60\" class=\"h-2\" />\n            <p class=\"text-muted-foreground text-xs\">4 more successful referrals needed to unlock Tier 3 rewards.</p>\n          </div>\n\n          <!-- 4 Milestone Tiers Grid -->\n          <div class=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n            @for (tier of tiers; track tier.id) {\n              <div [class]=\"tierClass(tier)\">\n                <div class=\"space-y-2.5\">\n                  <div class=\"flex items-center justify-between gap-2\">\n                    <span class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">\n                      Tier {{ tier.tier }} · {{ tier.referralsRequired }}\n                      {{ tier.referralsRequired === 1 ? 'Referral' : 'Referrals' }}\n                    </span>\n                    @if (tier.status === 'completed') {\n                      <span ui-badge variant=\"success\" class=\"gap-1 text-xs\">\n                        <lucide-icon [img]=\"Check\" class=\"size-3\" />\n                        Completed\n                      </span>\n                    } @else if (tier.status === 'in-progress') {\n                      <span ui-badge variant=\"default\" class=\"gap-1 text-xs\">In Progress (6/10)</span>\n                    } @else {\n                      <span ui-badge variant=\"secondary\" class=\"text-muted-foreground gap-1 text-xs\">\n                        <lucide-icon [img]=\"Lock\" class=\"size-3\" />\n                        Upcoming\n                      </span>\n                    }\n                  </div>\n                  <div>\n                    <p class=\"text-foreground text-sm font-semibold\">{{ tier.title }}</p>\n                    <p class=\"text-muted-foreground mt-1 text-xs leading-relaxed\">{{ tier.description }}</p>\n                  </div>\n                </div>\n                <div class=\"border-border/50 mt-4 border-t pt-3\">\n                  <div class=\"flex items-center justify-between text-xs\">\n                    <span class=\"text-muted-foreground\">Reward Perk:</span>\n                    <span class=\"text-foreground font-semibold tabular-nums\">{{ tier.reward }}</span>\n                  </div>\n                </div>\n              </div>\n            }\n          </div>\n        </div>\n      </div>\n\n      <!-- Recent Referrals Activity Table -->\n      <div ui-card class=\"border-border/80 shadow-xs\">\n        <div ui-card-header class=\"pb-4\">\n          <div class=\"flex items-center justify-between gap-2\">\n            <div>\n              <div class=\"flex items-center gap-2\">\n                <lucide-icon [img]=\"Users\" class=\"text-primary size-4\" />\n                <h3 ui-card-title class=\"text-base font-semibold\">Recent Referrals</h3>\n              </div>\n              <p ui-card-description>Track recent invitees, activation statuses, and credited referral payouts.</p>\n            </div>\n            <span ui-badge variant=\"outline\" class=\"text-xs font-medium tabular-nums\">4 Recent Signups</span>\n          </div>\n        </div>\n        <div ui-card-content class=\"p-0 sm:p-6 sm:pt-0\">\n          <div class=\"overflow-x-auto\">\n            <table ui-table density=\"cozy\">\n              <thead ui-table-header>\n                <tr ui-table-row>\n                  <th ui-table-head>Invitee</th>\n                  <th ui-table-head>Signed Up</th>\n                  <th ui-table-head>Status</th>\n                  <th ui-table-head class=\"text-right\">Reward Amount</th>\n                </tr>\n              </thead>\n              <tbody ui-table-body>\n                @for (referral of recentReferrals; track referral.id) {\n                  <tr ui-table-row>\n                    <td ui-table-cell>\n                      <div class=\"flex items-center gap-3\">\n                        <ui-avatar class=\"size-8 shrink-0\">\n                          <ui-avatar-fallback class=\"text-xs font-semibold\">{{ referral.initials }}</ui-avatar-fallback>\n                        </ui-avatar>\n                        <div class=\"min-w-0\">\n                          <p class=\"text-foreground truncate text-sm font-medium\">{{ referral.name }}</p>\n                          <p class=\"text-muted-foreground truncate text-xs\">{{ referral.email }}</p>\n                        </div>\n                      </div>\n                    </td>\n                    <td ui-table-cell class=\"text-muted-foreground text-xs whitespace-nowrap sm:text-sm\">\n                      {{ referral.signedUpAt }}\n                    </td>\n                    <td ui-table-cell>\n                      @if (referral.status === 'paid') {\n                        <span ui-badge variant=\"success\" class=\"gap-1 text-xs\">\n                          <lucide-icon [img]=\"Check\" class=\"size-3\" />\n                          Reward Paid $25\n                        </span>\n                      } @else {\n                        <span ui-badge variant=\"warning\" class=\"gap-1 text-xs\">\n                          <lucide-icon [img]=\"Clock\" class=\"size-3\" />\n                          Pending Activation\n                        </span>\n                      }\n                    </td>\n                    <td ui-table-cell class=\"text-right text-xs font-medium tabular-nums sm:text-sm\">\n                      <span\n                        [class]=\"referral.status === 'paid' ? 'text-success font-semibold' : 'text-muted-foreground'\"\n                      >\n                        {{ referral.status === 'paid' ? '+' + referral.rewardAmount : referral.rewardAmount }}\n                      </span>\n                    </td>\n                  </tr>\n                }\n              </tbody>\n            </table>\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiReferralProgramWidgetComponent {\n  protected readonly Check = Check\n  protected readonly Clock = Clock\n  protected readonly Copy = Copy\n  protected readonly Gift = Gift\n  protected readonly Lock = Lock\n  protected readonly Mail = Mail\n  protected readonly Share2 = Share2\n  protected readonly Target = Target\n  protected readonly Trophy = Trophy\n  protected readonly Users = Users\n  protected readonly Wallet = Wallet\n\n  protected readonly tiers = tiers\n  protected readonly recentReferrals = recentReferrals\n\n  @Input() referralUrl = 'https://uipkge.dev/invite/elena-rostova'\n  @Input() referralCode = 'ELENA25'\n  @Input('class') className?: string\n\n  readonly copiedLink = signal(false)\n  readonly copiedCode = signal(false)\n  private timers: ReturnType<typeof setTimeout>[] = []\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => this.timers.forEach((t) => clearTimeout(t)))\n  }\n\n  get rootClass(): string {\n    return cn('mx-auto w-full max-w-5xl space-y-6', this.className)\n  }\n\n  get twitterShareUrl(): string {\n    const text = 'Get $25 off your first year with my invite link:'\n    return `https://twitter.com/intent/tweet?text=${encodeURIComponent(text)}&url=${encodeURIComponent(this.referralUrl)}`\n  }\n\n  get linkedinShareUrl(): string {\n    return `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(this.referralUrl)}`\n  }\n\n  get emailShareUrl(): string {\n    const subject = 'Get $25 off your subscription'\n    const body = `Hey,\\n\\nUse my invite link to get $25 off your first year:\\n${this.referralUrl}\\n\\nUse invite code ${this.referralCode} at checkout!`\n    return `mailto:?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`\n  }\n\n  tierClass(tier: MilestoneTier): string {\n    return cn(\n      'relative flex flex-col justify-between rounded-lg border p-4 transition-colors',\n      tier.status === 'completed' && 'border-success/30 bg-success/5',\n      tier.status === 'in-progress' && 'border-primary/50 bg-primary/5 ring-primary/20 shadow-xs ring-1',\n      tier.status === 'upcoming' && 'border-border/60 bg-muted/20 opacity-80',\n    )\n  }\n\n  async copyToClipboard(text: string, type: 'link' | 'code'): Promise<void> {\n    try {\n      if (navigator?.clipboard?.writeText) {\n        await navigator.clipboard.writeText(text)\n      }\n    } catch {\n      // Fallback if clipboard API is unavailable\n    }\n    if (type === 'link') {\n      this.copiedLink.set(true)\n      this.timers.push(setTimeout(() => this.copiedLink.set(false), 2000))\n    } else {\n      this.copiedCode.set(true)\n      this.timers.push(setTimeout(() => this.copiedCode.set(false), 2000))\n    }\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/referral-program-widget/referral-program-widget.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/referral-program-widget/index.ts",
      "content": "export {\n  UiReferralProgramWidgetComponent,\n  type MilestoneTier,\n  type ReferralActivity,\n} from './referral-program-widget.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/referral-program-widget/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/avatar.json",
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/progress.json",
    "https://uipkge.dev/r/angular/separator.json",
    "https://uipkge.dev/r/angular/table.json"
  ],
  "description": "Rewardful and Viral Loops-style customer referral program and milestone reward tracker. Features a balance summary header with available credits and referral counts, shareable link and code copy actions with quick social sharing, a 4-tier milestone stepper with dynamic progress, and a recent referrals activity table with payout statuses.",
  "categories": [
    "marketing",
    "dashboard"
  ]
}