{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "roi-calculator-inputs",
  "title": "ROI — Calculator",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/roi-calculator-inputs/roi-calculator-inputs.component.ts",
      "content": "// Input-driven ROI estimate: team size, close days, and analyst rate drive a computed\n// saving, with the assumptions stated beneath the figure. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport { ArrowRight, LucideAngularModule } from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\nimport { UiSliderComponent } from '@/ui/slider/slider.component'\n\nconst DAYS_SAVED_SHARE = 0.55\nconst CLOSES_PER_YEAR = 12\n\ninterface RoiInput {\n  id: 'analysts' | 'days' | 'rate'\n  label: string\n  min: number\n  max: number\n  step: number\n}\n\nconst INPUTS: RoiInput[] = [\n  { id: 'analysts', label: 'People involved in close', min: 1, max: 40, step: 1 },\n  { id: 'days', label: 'Days the close takes today', min: 2, max: 20, step: 1 },\n  { id: 'rate', label: 'Fully-loaded day rate', min: 200, max: 1200, step: 20 },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-roi-calculator-inputs, [ui-roi-calculator-inputs]',\n  standalone: true,\n  // React renders the <section> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiSeparatorComponent,\n    UiSliderComponent,\n  ],\n  template: `\n    <section data-slot=\"roi-calculator-inputs\" [class]=\"rootClass\">\n      <div class=\"mx-auto max-w-4xl px-6 py-20 lg:py-28\">\n        <div class=\"max-w-2xl\">\n          <span ui-badge variant=\"secondary\">Estimate</span>\n          <h2 class=\"mt-4 text-3xl font-semibold tracking-tight sm:text-4xl\">What the close currently costs you</h2>\n          <p class=\"text-muted-foreground mt-3 text-lg\">\n            Three inputs, one arithmetic model, stated in full underneath. Adjust anything that looks wrong.\n          </p>\n        </div>\n\n        <div ui-card class=\"mt-10\">\n          <div ui-card-content class=\"grid gap-8 p-6 sm:grid-cols-[1.2fr_1fr] sm:p-8\">\n            <div class=\"space-y-6\">\n              @for (input of INPUTS; track input.id) {\n                <div>\n                  <div class=\"flex items-baseline justify-between gap-4\">\n                    <label [attr.for]=\"input.id\" class=\"text-sm font-medium\">{{ input.label }}</label>\n                    <span class=\"font-mono text-sm tabular-nums\">{{ displayValue(input.id) }}</span>\n                  </div>\n                  <ui-slider\n                    [value]=\"modelValue(input.id)\"\n                    (valueChange)=\"setModel(input.id, $event)\"\n                    [min]=\"input.min\"\n                    [max]=\"input.max\"\n                    [step]=\"input.step\"\n                    class=\"mt-3\"\n                    [attr.aria-label]=\"input.label\"\n                  />\n                </div>\n              }\n            </div>\n\n            <div class=\"border-border bg-muted/30 flex flex-col justify-center rounded-lg border p-6 text-center\">\n              <p class=\"text-muted-foreground text-xs tracking-wide uppercase\">Estimated annual saving</p>\n              <p class=\"font-display mt-2 text-4xl font-bold tracking-tight tabular-nums\">{{ formatted() }}</p>\n              <p class=\"text-muted-foreground mt-2 text-xs\">\n                {{ daysSaved() }} days saved per close, {{ closesPerYear }} closes a year\n              </p>\n              <button ui-button class=\"mt-5\">\n                Check it against your numbers\n                <lucide-icon [img]=\"ArrowRight\" class=\"ml-2 size-4\" />\n              </button>\n            </div>\n          </div>\n\n          <ui-separator />\n\n          <div class=\"px-6 py-4 sm:px-8\">\n            <p class=\"text-muted-foreground text-xs leading-relaxed\">\n              <span class=\"font-medium\">The model. </span>\n              Saving = days saved × closes per year × people × day rate, where days saved assumes\n              {{ sharePercent }}% of close time is reconciliation that certified definitions remove. That share is the\n              median across 240 rollouts; yours will differ, and the figure is only as good as that assumption.\n            </p>\n          </div>\n        </div>\n      </div>\n    </section>\n  `,\n})\nexport class UiRoiCalculatorInputsComponent {\n  protected readonly ArrowRight = ArrowRight\n  protected readonly INPUTS = INPUTS\n  protected readonly closesPerYear = CLOSES_PER_YEAR\n  protected readonly sharePercent = Math.round(DAYS_SAVED_SHARE * 100)\n\n  @Input('class') className?: string\n\n  readonly analysts = signal<number[]>([6])\n  readonly closeDays = signal<number[]>([9])\n  readonly rate = signal<number[]>([520])\n\n  readonly daysSaved = computed(() => Math.round((this.closeDays()[0] ?? 0) * DAYS_SAVED_SHARE * 10) / 10)\n  readonly annualSaving = computed(() =>\n    Math.round(this.daysSaved() * CLOSES_PER_YEAR * (this.analysts()[0] ?? 0) * (this.rate()[0] ?? 0)),\n  )\n  readonly formatted = computed(() => `£${this.annualSaving().toLocaleString('en-GB')}`)\n\n  get rootClass(): string {\n    return cn('bg-background', this.className)\n  }\n\n  modelValue(id: RoiInput['id']): number[] {\n    if (id === 'analysts') return this.analysts()\n    if (id === 'days') return this.closeDays()\n    return this.rate()\n  }\n\n  setModel(id: RoiInput['id'], value: number[]): void {\n    if (id === 'analysts') this.analysts.set(value)\n    else if (id === 'days') this.closeDays.set(value)\n    else this.rate.set(value)\n  }\n\n  displayValue(id: RoiInput['id']): string {\n    if (id === 'analysts') return `${this.analysts()[0]}`\n    if (id === 'days') return `${this.closeDays()[0]} days`\n    return `£${this.rate()[0]}`\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/roi-calculator-inputs/roi-calculator-inputs.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/roi-calculator-inputs/index.ts",
      "content": "export { UiRoiCalculatorInputsComponent } from './roi-calculator-inputs.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/roi-calculator-inputs/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "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/separator.json",
    "https://uipkge.dev/r/angular/slider.json"
  ],
  "description": "Input-driven ROI estimate where team size, close days, and analyst rate drive a computed saving, with the assumptions stated beneath the figure.",
  "categories": [
    "marketing"
  ]
}