Record Detail Page
blocklayoutAdmin record detail page: breadcrumb header with status badge and Edit / Delete actions, two-column layout with a summary card (avatar, identity, key metrics), Details / Activity tabs (labeled description-list sections and an icon timeline feed), plus a side panel with owner metadata, relative timestamps, tag badges, and a recent-orders mini list. All data is hardcoded inline for you to replace.
Installation
$pnpm dlx uipkge-ng@latest add record-detail-page$npx uipkge-ng@latest add record-detail-page$yarn dlx uipkge-ng@latest add record-detail-page$bunx uipkge-ng@latest add record-detail-pageManifest:
https://uipkge.dev/r/angular/record-detail-page.jsonInstalls to:src/app/components/blocks/record-detail-page/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.
DataListRowinterface DataListRow {
label: string
value: string
}Sectioninterface Section {
title: string
icon: LucideIconData
rows: DataListRow[]
}ActivityEventinterface ActivityEvent {
icon: LucideIconData
sentence: string
time: string
}Orderinterface Order {
id: string
amount: string
status: 'Paid' | 'Pending' | 'Refunded'
}Files installed (2)
src/app/components/blocks/record-detail-page/record-detail-page.component.ts12 kB
// Admin record detail page: breadcrumb header with status badge and Edit / Delete // actions, two-column layout with a summary card (avatar, identity, key metrics), // Details / Activity tabs (labeled description-list sections and an icon timeline feed), // plus a side panel with owner metadata, relative timestamps, tag badges, and a // recent-orders mini list. All data is hardcoded inline for you to replace. // Port of the React block 1:1. import { ChangeDetectionStrategy, Component, Input } from '@angular/core' import { Building2, ChevronRight, CreditCard, FileText, LucideAngularModule, Mail, MessageSquare, Pencil, Plus, ReceiptText, Trash2, UserPlus, type LucideIconData, } from 'lucide-angular' import { cn } from '@/lib/utils' import { UiAvatarComponent, UiAvatarFallbackComponent, UiAvatarImageComponent } from '@/ui/avatar/avatar.component' import { UiBadgeComponent } from '@/ui/badge/badge.component' import { UiButtonComponent } from '@/ui/button/button.component' import { UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, } from '@/ui/card/card.component' import { UiSeparatorComponent } from '@/ui/separator/separator.component' import { UiTabsComponent, UiTabsContentComponent, UiTabsListComponent, UiTabsTriggerComponent, } from '@/ui/tabs/tabs.component' interface DataListRow { label: string value: string } interface Section { title: string icon: LucideIconData rows: DataListRow[] } interface ActivityEvent { icon: LucideIconData sentence: string time: string } interface Order { id: string amount: string status: 'Paid' | 'Pending' | 'Refunded' } const summaryRows: DataListRow[] = [ { label: 'Account ID', value: 'CUS-004182' }, { label: 'Plan', value: 'Business annual' }, { label: 'Lifetime value', value: '$48,200' }, ] const sections: Section[] = [ { title: 'Contact', icon: Mail, rows: [ { label: 'Email', value: '[email protected]' }, { label: 'Phone', value: '+1 (415) 555-0134' }, { label: 'Website', value: 'acmecorp.com' }, { label: 'Address', value: '500 Market St, San Francisco, CA' }, ], }, { title: 'Company', icon: Building2, rows: [ { label: 'Industry', value: 'Logistics & supply chain' }, { label: 'Employees', value: '240–500' }, { label: 'Tax ID', value: 'US-84-2917365' }, { label: 'Payment terms', value: 'Net 30' }, ], }, ] const activity: ActivityEvent[] = [ { icon: ReceiptText, sentence: 'Invoice INV-2091 paid — $12,400', time: '2h ago' }, { icon: MessageSquare, sentence: 'Priya left a note on the renewal deal', time: 'Yesterday' }, { icon: UserPlus, sentence: 'Dana Whitfield added as billing contact', time: '3d ago' }, { icon: FileText, sentence: 'Contract Q3-amendment.pdf uploaded', time: '1w ago' }, ] const tags = ['enterprise', 'renewal-q4', 'priority-support'] const orders: Order[] = [ { id: 'ORD-8841', amount: '$12,400.00', status: 'Paid' }, { id: 'ORD-8790', amount: '$3,180.50', status: 'Pending' }, { id: 'ORD-8612', amount: '$940.00', status: 'Refunded' }, ] @Component({ changeDetection: ChangeDetectionStrategy.Eager, selector: 'ui-record-detail-page, [ui-record-detail-page]', standalone: true, // The block renders its own root <div>: keep this wrapper out of the layout. host: { '[attr.class]': '"contents"' }, imports: [ LucideAngularModule, UiAvatarComponent, UiAvatarFallbackComponent, UiAvatarImageComponent, UiBadgeComponent, UiButtonComponent, UiCardComponent, UiCardContentComponent, UiCardDescriptionComponent, UiCardHeaderComponent, UiCardTitleComponent, UiSeparatorComponent, UiTabsComponent, UiTabsContentComponent, UiTabsListComponent, UiTabsTriggerComponent, ], template: ` <div data-slot="record-detail-page" [class]="rootClass"> <div class="flex flex-wrap items-center justify-between gap-4"> <div class="flex min-w-0 items-center gap-1.5"> <button ui-button variant="link" size="sm" class="text-muted-foreground h-auto min-h-6 px-0"> Customers </button> <lucide-icon [img]="ChevronRight" class="text-muted-foreground size-4 shrink-0" aria-hidden="true" /> <span class="truncate text-sm font-semibold">Acme Corp</span> <span ui-badge variant="secondary" class="ml-2">Active</span> </div> <div class="flex items-center gap-2"> <button ui-button variant="outline" size="sm"> <lucide-icon [img]="Pencil" class="size-4" aria-hidden="true" /> Edit </button> <button ui-button variant="outline" size="sm" class="text-destructive hover:text-destructive"> <lucide-icon [img]="Trash2" class="size-4" aria-hidden="true" /> Delete </button> </div> </div> <div class="mt-6 grid gap-6 lg:grid-cols-3"> <div class="space-y-6 lg:col-span-2"> <div ui-card> <div ui-card-header class="flex-row items-center gap-4 space-y-0"> <ui-avatar size="2xl" class="size-14"> <ui-avatar-image src="https://i.pravatar.cc/112?img=32" alt="Acme Corp logo" /> <ui-avatar-fallback>AC</ui-avatar-fallback> </ui-avatar> <div class="min-w-0"> <h3 ui-card-title class="text-lg">Acme Corp</h3> <p ui-card-description class="truncate">billing@acmecorp.com · Customer since March 2023</p> </div> </div> <div ui-card-content> <ui-separator class="mb-4" /> <dl class="grid gap-x-8 gap-y-3 sm:grid-cols-3"> @for (row of summaryRows; track row.label) { <div> <dt class="text-muted-foreground text-xs">{{ row.label }}</dt> <dd class="mt-0.5 text-sm font-medium">{{ row.value }}</dd> </div> } </dl> </div> </div> <ui-tabs defaultValue="details"> <ui-tabs-list> <button ui-tabs-trigger value="details">Details</button> <button ui-tabs-trigger value="activity">Activity</button> </ui-tabs-list> <ui-tabs-content value="details" class="space-y-6 pt-4"> @for (section of sections; track section.title) { <section> <h3 class="flex items-center gap-2 text-sm font-semibold"> <lucide-icon [img]="section.icon" class="text-muted-foreground size-4" aria-hidden="true" /> {{ section.title }} </h3> <dl class="border-border mt-3 divide-y rounded-lg border"> @for (row of section.rows; track row.label) { <div class="px-4 py-2.5 sm:flex sm:items-center sm:gap-4"> <dt class="text-muted-foreground text-xs sm:w-36 sm:text-sm">{{ row.label }}</dt> <dd class="mt-0.5 text-sm font-medium sm:mt-0">{{ row.value }}</dd> </div> } </dl> </section> } </ui-tabs-content> <ui-tabs-content value="activity" class="pt-4"> <ol class="before:bg-border relative space-y-6 before:absolute before:inset-y-1 before:left-[15px] before:w-px" > @for (event of activity; track event.sentence) { <li class="relative flex items-start gap-4 pl-0"> <span class="bg-background ring-border relative z-10 flex size-8 shrink-0 items-center justify-center rounded-full ring-1" > <lucide-icon [img]="event.icon" class="text-muted-foreground size-4" aria-hidden="true" /> </span> <div class="min-w-0 pt-1"> <p class="truncate text-sm">{{ event.sentence }}</p> <time class="text-muted-foreground text-xs">{{ event.time }}</time> </div> </li> } </ol> </ui-tabs-content> </ui-tabs> </div> <div class="space-y-6"> <div ui-card> <div ui-card-header> <h3 ui-card-title class="text-sm font-medium">Record meta</h3> </div> <div ui-card-content class="space-y-4"> <div class="flex items-center gap-3"> <ui-avatar class="size-8"> <ui-avatar-image src="https://i.pravatar.cc/64?img=47" alt="Priya Raman" /> <ui-avatar-fallback>PR</ui-avatar-fallback> </ui-avatar> <div class="min-w-0"> <p class="text-muted-foreground text-xs">Owner</p> <p class="truncate text-sm font-medium">Priya Raman</p> </div> </div> <ui-separator /> <dl class="space-y-3"> <div class="flex items-center justify-between gap-4"> <dt class="text-muted-foreground text-xs">Created</dt> <dd class="text-sm font-medium">Mar 14, 2023</dd> </div> <div class="flex items-center justify-between gap-4"> <dt class="text-muted-foreground text-xs">Updated</dt> <dd class="text-sm font-medium">2 hours ago</dd> </div> </dl> <ui-separator /> <div> <p class="text-muted-foreground mb-2 text-xs">Tags</p> <div class="flex flex-wrap gap-1.5"> @for (tag of tags; track tag) { <span ui-badge variant="secondary">{{ tag }}</span> } </div> </div> </div> </div> <div ui-card> <div ui-card-header class="flex-row items-center justify-between space-y-0"> <h3 ui-card-title class="text-sm font-medium">Recent orders</h3> <button ui-button variant="ghost" size="icon" aria-label="View all orders"> <lucide-icon [img]="CreditCard" class="size-4" aria-hidden="true" /> </button> </div> <div ui-card-content> <ul class="divide-y"> @for (order of orders; track order.id) { <li class="flex items-center gap-3 py-2.5 first:pt-0 last:pb-0"> <span class="font-mono text-xs">{{ order.id }}</span> <span class="text-muted-foreground ml-auto text-sm tabular-nums">{{ order.amount }}</span> <span ui-badge [variant]=" order.status === 'Paid' ? 'default' : order.status === 'Pending' ? 'secondary' : 'outline' " class="min-w-[72px] justify-center" > {{ order.status }} </span> </li> } </ul> <button ui-button variant="outline" size="sm" class="mt-4 w-full"> <lucide-icon [img]="Plus" class="size-4" aria-hidden="true" /> New order </button> </div> </div> </div> </div> </div> `, }) export class UiRecordDetailPageComponent { protected readonly ChevronRight = ChevronRight protected readonly CreditCard = CreditCard protected readonly Pencil = Pencil protected readonly Plus = Plus protected readonly Trash2 = Trash2 protected readonly summaryRows = summaryRows protected readonly sections = sections protected readonly activity = activity protected readonly tags = tags protected readonly orders = orders @Input('class') className?: string get rootClass(): string { return cn('bg-background text-foreground w-full', this.className) } }src/app/components/blocks/record-detail-page/index.ts0.1 kB
export { UiRecordDetailPageComponent } from './record-detail-page.component'
Raw manifest:https://uipkge.dev/r/angular/record-detail-page.json