Menu

Change language

GitHub repo

Integrations — Featured Pairs

blockmarketing

Three promoted integrations, each a card pairing the service with what the connection actually does, the three setup steps it takes, and its live connection state.

Installation

$npx uipkge-ng@latest add integrations-featured-pairs
Manifest:https://uipkge.dev/r/angular/integrations-featured-pairs.jsonInstalls to:src/app/components/blocks/integrations-featured-pairs/

Variants

Loading interactive previews…

Schema

Type aliases exported from this item's source. Use these to shape the data you pass in.

FeaturedIntegration
interface FeaturedIntegration {
  mark: string
  name: string
  summary: string
  steps: string[]
  status: string
  healthy: boolean
  meta: string
}

Files installed (2)

  • src/app/components/blocks/integrations-featured-pairs/integrations-featured-pairs.component.ts5.5 kB
    // Three promoted integrations, each a card pairing the service with what the
    // connection actually does, the three setup steps it takes, and its live
    // connection state. Port of the React block 1:1.
    import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
    import { ArrowRight, Check, LucideAngularModule, Plug } 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'
    
    interface FeaturedIntegration {
      mark: string
      name: string
      summary: string
      steps: string[]
      status: string
      healthy: boolean
      meta: string
    }
    
    const featured: FeaturedIntegration[] = [
      {
        mark: 'SF',
        name: 'Snowflake',
        summary: 'Query your warehouse directly. Metrics resolve against live tables instead of a nightly copy.',
        steps: ['Create a read-only role', 'Paste the account URL', 'Pick the databases to expose'],
        status: 'Connected',
        healthy: true,
        meta: 'Read-only · no data egress',
      },
      {
        mark: 'OK',
        name: 'Okta',
        summary: 'Group membership drives row-level scope, so access changes the moment HR changes it.',
        steps: ['Add the SAML app', 'Enable SCIM provisioning', 'Map groups to scopes'],
        status: 'Connected',
        healthy: true,
        meta: 'SAML + SCIM',
      },
      {
        mark: 'SL',
        name: 'Slack',
        summary: 'Drift and freshness alerts land in the channel that owns the metric, with the run attached.',
        steps: ['Install the app', 'Choose default channels', 'Route alerts per metric owner'],
        status: 'Needs re-auth',
        healthy: false,
        meta: 'Token expires every 90 days',
      },
    ]
    
    @Component({
      changeDetection: ChangeDetectionStrategy.Eager,
      selector: 'ui-integrations-featured-pairs, [ui-integrations-featured-pairs]',
      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,
      ],
      template: `
        <section data-slot="integrations-featured-pairs" [class]="rootClass">
          <div class="mx-auto max-w-6xl px-6 py-20 lg:py-28">
            <div class="max-w-2xl">
              <span ui-badge variant="secondary">Featured integrations</span>
              <h2 class="mt-4 text-3xl font-semibold tracking-tight sm:text-4xl">The three most teams connect first</h2>
              <p class="text-muted-foreground mt-3 text-lg">
                Each one is three steps and read-only. What the connection actually does is written on the card.
              </p>
            </div>
    
            <div class="mt-10 grid gap-4 md:grid-cols-3">
              @for (integration of featured; track integration.name) {
                <div ui-card>
                  <div ui-card-content class="flex h-full flex-col p-6">
                    <div class="flex items-center gap-3">
                      <span
                        class="border-border text-muted-foreground flex size-10 items-center justify-center rounded-lg border font-mono text-sm font-semibold"
                        aria-hidden="true"
                      >
                        {{ integration.mark }}
                      </span>
                      <div class="min-w-0">
                        <p class="truncate text-sm font-semibold">{{ integration.name }}</p>
                        <p class="text-muted-foreground truncate text-xs">{{ integration.meta }}</p>
                      </div>
                    </div>
    
                    <p class="text-muted-foreground mt-4 text-sm leading-relaxed">{{ integration.summary }}</p>
    
                    <ui-separator class="my-5" />
    
                    <p class="text-muted-foreground font-mono text-xs tracking-[0.14em] uppercase">Setup</p>
                    <ol class="mt-3 space-y-2">
                      @for (step of integration.steps; track step; let i = $index) {
                        <li class="text-foreground flex items-start gap-2.5 text-sm">
                          <span class="text-muted-foreground/70 mt-px font-mono text-xs">{{ i + 1 }}</span>
                          <span>{{ step }}</span>
                        </li>
                      }
                    </ol>
    
                    <div class="mt-auto flex items-center justify-between gap-3 pt-6">
                      <span ui-badge [variant]="integration.healthy ? 'secondary' : 'outline'" class="gap-1.5">
                        @if (integration.healthy) {
                          <lucide-icon [img]="Check" class="size-3" aria-hidden="true" />
                        } @else {
                          <lucide-icon [img]="Plug" class="size-3" aria-hidden="true" />
                        }
                        {{ integration.status }}
                      </span>
                      <button ui-button variant="ghost" size="sm" class="h-auto px-2 py-1">
                        Docs
                        <lucide-icon [img]="ArrowRight" class="ml-1 size-3.5" aria-hidden="true" />
                      </button>
                    </div>
                  </div>
                </div>
              }
            </div>
          </div>
        </section>
      `,
    })
    export class UiIntegrationsFeaturedPairsComponent {
      protected readonly ArrowRight = ArrowRight
      protected readonly Check = Check
      protected readonly Plug = Plug
    
      protected readonly featured = featured
    
      @Input('class') className?: string
    
      get rootClass(): string {
        return cn('bg-background', this.className)
      }
    }
    
  • src/app/components/blocks/integrations-featured-pairs/index.ts0.1 kB

Raw manifest:https://uipkge.dev/r/angular/integrations-featured-pairs.json