Comparison — Migration Path
blockmarketingMigration-focused comparison pairing what a team keeps, what changes, and what is retired when moving from an existing tool, with an effort estimate per row.
Installation
$pnpm dlx uipkge-ng@latest add comparison-migration-path$npx uipkge-ng@latest add comparison-migration-path$yarn dlx uipkge-ng@latest add comparison-migration-path$bunx uipkge-ng@latest add comparison-migration-pathManifest:
https://uipkge.dev/r/angular/comparison-migration-path.jsonInstalls to:src/app/components/blocks/comparison-migration-path/Variants
Loading interactive previews…
Loading interactive previews…
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
MigrationPathRowinterface MigrationPathRow {
area: string
verdict: MigrationVerdict
detail: string
effort: string
}Files installed (2)
src/app/components/blocks/comparison-migration-path/comparison-migration-path.component.ts6.2 kB
// Migration-focused comparison pairing what a team keeps, what changes, and what is // retired when moving from an existing tool, with an effort estimate per row. // Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { ArrowRight, Check, LucideAngularModule, Minus, RefreshCw } 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 } from '@/ui/card/card.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' import { UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, } from '@/ui/table/table.component' export type MigrationVerdict = 'keep' | 'change' | 'retire' export interface MigrationPathRow { area: string verdict: MigrationVerdict detail: string effort: string } // verdict drives the icon and tone, so rows stay declarative and a new row // cannot forget to style itself. const defaultRows: MigrationPathRow[] = [ { area: 'Warehouse and models', verdict: 'keep', detail: 'Untouched. We read from them, nothing is rewritten.', effort: 'None', }, { area: 'dbt project', verdict: 'keep', detail: 'Imported as metric sources; your DAG keeps running.', effort: 'Half a day', }, { area: 'Dashboard layouts', verdict: 'change', detail: 'Rebuilt against certified metrics, usually faster than the original.', effort: '1–2 weeks', }, { area: 'Metric formulas', verdict: 'change', detail: 'Moved from dashboard config into versioned files.', effort: '1 week', }, { area: 'Per-dashboard permissions', verdict: 'retire', detail: 'Replaced by row-level scope from your IdP.', effort: 'None', }, { area: 'Bespoke extract jobs', verdict: 'retire', detail: 'Deleted once the metrics they fed are certified.', effort: 'None', }, ] const verdictMeta: Record<MigrationVerdict, { label: string; class: string }> = { keep: { label: 'Keep', class: 'text-success' }, change: { label: 'Change', class: 'text-muted-foreground' }, retire: { label: 'Retire', class: 'text-muted-foreground/60' }, } @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-comparison-migration-path, [ui-comparison-migration-path]', standalone: true, // React renders the root <section> itself: the host stays out of layout and `class` goes to the root. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiSeparatorComponent, UiTableBodyComponent, UiTableCellComponent, UiTableComponent, UiTableHeadComponent, UiTableHeaderComponent, UiTableRowComponent, ], template: ` <section data-slot="comparison-migration-path" [class]="rootClass"> <div class="mx-auto max-w-5xl px-6 py-20 lg:py-28"> <div class="max-w-2xl"> <span ui-badge variant="secondary">Migration</span> <h2 class="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl">What actually changes</h2> <p class="text-muted-foreground mt-3 text-lg"> Most of the stack stays exactly where it is. The effort column is measured from real rollouts. </p> </div> <div ui-card class="mt-10"> <div ui-card-content class="p-0"> <table ui-table> <thead ui-table-header> <tr ui-table-row> <th ui-table-head>Area</th> <th ui-table-head class="w-28">Verdict</th> <th ui-table-head>What happens</th> <th ui-table-head class="w-32 text-right">Effort</th> </tr> </thead> <tbody ui-table-body> @for (row of rows; track row.area) { <tr ui-table-row> <td ui-table-cell class="font-medium">{{ row.area }}</td> <td ui-table-cell> <span [class]="'inline-flex items-center gap-1.5 text-xs ' + verdictClass(row.verdict)"> @if (row.verdict === 'keep') { <lucide-icon [img]="Check" class="size-3.5" aria-hidden="true" /> } @else if (row.verdict === 'change') { <lucide-icon [img]="RefreshCw" class="size-3.5" aria-hidden="true" /> } @else { <lucide-icon [img]="Minus" class="size-3.5" aria-hidden="true" /> } {{ verdictLabel(row.verdict) }} </span> </td> <td ui-table-cell class="text-muted-foreground text-sm">{{ row.detail }}</td> <td ui-table-cell class="text-right font-mono text-xs">{{ row.effort }}</td> </tr> } </tbody> </table> <ui-separator /> <p class="text-muted-foreground px-6 py-4 text-xs leading-relaxed"> Effort assumes one engineer and one analyst part-time. Teams with more than about 200 existing dashboards usually stage the rebuild by department rather than doing it in one pass. </p> </div> </div> <button ui-button class="mt-6"> Get a migration estimate <lucide-icon [img]="ArrowRight" class="ml-2 size-4" aria-hidden="true" /> </button> </div> </section> `, }) export class UiComparisonMigrationPathComponent { protected readonly ArrowRight = ArrowRight protected readonly Check = Check protected readonly Minus = Minus protected readonly RefreshCw = RefreshCw @Input() rows: MigrationPathRow[] = defaultRows @Input('class') className?: string get rootClass(): string { return cn('bg-background', this.className) } verdictLabel(v: MigrationVerdict): string { return verdictMeta[v].label } verdictClass(v: MigrationVerdict): string { return verdictMeta[v].class } }src/app/components/blocks/comparison-migration-path/index.ts0.1 kB
export { UiComparisonMigrationPathComponent, type MigrationVerdict, type MigrationPathRow, } from './comparison-migration-path.component'
Raw manifest:https://uipkge.dev/r/angular/comparison-migration-path.json