{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "contractor-timesheet-approval",
  "title": "Contractor Timesheet Approval",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/contractor-timesheet-approval/contractor-timesheet-approval.component.ts",
      "content": "// Deel/Remote style international contractor invoice approval, hourly timesheet verification\n// against GitHub PRs & deliverables, multi-currency payout calculation, overview KPI metrics,\n// and itemized daily timesheet slide-over drawer. Port of the Vue/React block 1:1.\nimport { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core'\nimport {\n  Calendar,\n  Check,\n  CheckCheck,\n  CheckCircle2,\n  Clock,\n  FileSpreadsheet,\n  Globe,\n  LucideAngularModule,\n  RotateCcw,\n  Search,\n} 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 {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\nimport { UiTimesheetDetailSheetComponent } from './timesheet-detail-sheet.component'\nimport { UiTimesheetKpiCardsComponent } from './timesheet-kpi-cards.component'\nimport { UiTimesheetTableRowComponent } from './timesheet-table-row.component'\nimport { initialSubmissions } from './timesheet-data'\nimport type { ApprovalStatus, ContractorSubmission, DailyTimesheetEntry, DeliverableType } from './timesheet-types'\n\nexport type { ApprovalStatus, ContractorSubmission, DailyTimesheetEntry, DeliverableType }\n\nexport type TimesheetStatusFilter = 'all' | 'pending' | 'approved' | 'disputed'\nexport type CurrencyDisplay = 'both' | 'usd' | 'local'\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-contractor-timesheet-approval, [ui-contractor-timesheet-approval]',\n  standalone: true,\n  // The block renders its own root <div>: keep this wrapper out of the layout.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiSeparatorComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n    UiTimesheetDetailSheetComponent,\n    UiTimesheetKpiCardsComponent,\n    UiTimesheetTableRowComponent,\n  ],\n  template: `\n    <div data-slot=\"contractor-timesheet-approval\" [class]=\"rootClass\">\n      <!-- Header Section -->\n      <div class=\"border-border/80 flex flex-col gap-4 border-b pb-6 lg:flex-row lg:items-center lg:justify-between\">\n        <div class=\"space-y-1.5\">\n          <div class=\"flex flex-wrap items-center gap-2\">\n            <span ui-badge variant=\"outline\" class=\"border-border text-muted-foreground text-xs font-medium\">\n              <lucide-icon [img]=\"Globe\" class=\"text-primary mr-1 size-3\" />\n              Global Workforce · HRMS\n            </span>\n            <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs font-medium\">\n              <lucide-icon [img]=\"Calendar\" class=\"text-muted-foreground size-3.5\" />\n              <span>Pay Cycle:</span>\n              <span class=\"text-foreground font-semibold\">{{ payCycle }}</span>\n            </div>\n            @if (pendingCount() > 0) {\n              <span ui-badge variant=\"outline\" class=\"border-warning/30 bg-warning/10 text-warning text-xs font-medium\">\n                <span class=\"bg-warning mr-1.5 size-1.5 animate-pulse rounded-full\"></span>\n                {{ pendingCount() }} Invoices Pending Review\n              </span>\n            } @else {\n              <span ui-badge variant=\"outline\" class=\"border-success/30 bg-success/10 text-success text-xs font-medium\">\n                <lucide-icon [img]=\"CheckCircle2\" class=\"text-success mr-1 size-3\" />\n                All Invoices Reviewed\n              </span>\n            }\n          </div>\n          <h1 class=\"text-foreground text-2xl font-bold tracking-tight sm:text-3xl\">\n            {{ title }}\n          </h1>\n          <p class=\"text-muted-foreground max-w-3xl text-sm leading-relaxed\">\n            {{ subtitle }}\n          </p>\n        </div>\n\n        <!-- Action Buttons -->\n        <div class=\"flex flex-wrap items-center gap-2.5 pt-1 lg:pt-0\">\n          <button\n            ui-button\n            variant=\"outline\"\n            size=\"sm\"\n            class=\"border-border hover:bg-muted h-9 gap-1.5 text-xs font-medium shadow-xs\"\n            (click)=\"resetData()\"\n          >\n            <lucide-icon [img]=\"RotateCcw\" class=\"text-muted-foreground size-3.5\" />\n            Reset Demo\n          </button>\n\n          <button\n            ui-button\n            variant=\"default\"\n            size=\"sm\"\n            [disabled]=\"pendingCount() === 0 || isApprovingAll()\"\n            class=\"h-9 gap-2 text-xs font-medium shadow-xs\"\n            (click)=\"approveAllPending()\"\n          >\n            @if (isApprovingAll()) {\n              <lucide-icon [img]=\"Clock\" class=\"size-3.5 animate-spin\" />\n              Processing Batch...\n            } @else if (bulkApprovalDone()) {\n              <lucide-icon [img]=\"Check\" class=\"text-success size-3.5\" />\n              Batch Approved!\n            } @else {\n              <lucide-icon [img]=\"CheckCheck\" class=\"size-3.5\" />\n              <span>Approve All Pending ({{ formatUsd(pendingAmountUsd()) }})</span>\n            }\n          </button>\n        </div>\n      </div>\n\n      <!-- 4 Billing Overview KPI Cards -->\n      <ui-timesheet-kpi-cards\n        [pendingAmountUsd]=\"pendingAmountUsd()\"\n        [pendingCount]=\"pendingCount()\"\n        [totalHoursLogged]=\"totalHoursLogged()\"\n        [scheduledPayoutsTotal]=\"scheduledPayoutsTotal()\"\n        [formatUsd]=\"formatUsdFn\"\n      />\n\n      <!-- Filter Toolbar -->\n      <div ui-card class=\"border-border bg-card shadow-xs\">\n        <div ui-card-content class=\"p-4\">\n          <div class=\"flex flex-col gap-3 md:flex-row md:items-center md:justify-between\">\n            <!-- Search input -->\n            <div class=\"relative max-w-md flex-1\">\n              <lucide-icon\n                [img]=\"Search\"\n                class=\"text-muted-foreground absolute top-1/2 left-3 size-4 -translate-y-1/2\"\n              />\n              <input\n                [value]=\"searchQuery()\"\n                (input)=\"searchQuery.set($any($event.target).value)\"\n                type=\"text\"\n                placeholder=\"Search contractor, role, country, or PR...\"\n                class=\"border-input bg-background text-foreground placeholder:text-muted-foreground focus-visible:ring-ring w-full rounded-md border py-1.5 pr-3 pl-9 text-xs focus-visible:ring-2 focus-visible:outline-hidden\"\n              />\n            </div>\n\n            <!-- Filter buttons & Currency Toggle -->\n            <div class=\"flex flex-wrap items-center gap-2\">\n              <!-- Status filter tabs -->\n              <div class=\"border-border bg-muted/40 inline-flex rounded-lg border p-0.5 text-xs\">\n                <button type=\"button\" [class]=\"statusTabClass('all')\" (click)=\"selectedStatus.set('all')\">\n                  All ({{ submissions().length }})\n                </button>\n                <button type=\"button\" [class]=\"statusTabClass('pending')\" (click)=\"selectedStatus.set('pending')\">\n                  Pending ({{ pendingCount() }})\n                </button>\n                <button type=\"button\" [class]=\"statusTabClass('approved')\" (click)=\"selectedStatus.set('approved')\">\n                  Approved ({{ approvedCount() }})\n                </button>\n                <button type=\"button\" [class]=\"statusTabClass('disputed')\" (click)=\"selectedStatus.set('disputed')\">\n                  Disputed ({{ disputedCount() }})\n                </button>\n              </div>\n\n              <ui-separator orientation=\"vertical\" class=\"hidden h-6 md:block\" />\n\n              <!-- Currency toggle selector -->\n              <div class=\"border-border bg-muted/40 inline-flex rounded-lg border p-0.5 text-xs\">\n                <button type=\"button\" [class]=\"currencyTabClass('both')\" (click)=\"currencyDisplay.set('both')\">\n                  Dual FX\n                </button>\n                <button type=\"button\" [class]=\"currencyTabClass('usd')\" (click)=\"currencyDisplay.set('usd')\">\n                  USD ($)\n                </button>\n                <button type=\"button\" [class]=\"currencyTabClass('local')\" (click)=\"currencyDisplay.set('local')\">\n                  Local\n                </button>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <!-- Contractor Invoices & Timesheets Table -->\n      <div ui-card class=\"border-border bg-card overflow-hidden shadow-xs\">\n        <div class=\"overflow-x-auto\">\n          <table ui-table>\n            <thead ui-table-header class=\"bg-muted/40\">\n              <tr ui-table-row class=\"border-border hover:bg-transparent\">\n                <th\n                  ui-table-head\n                  class=\"text-muted-foreground min-w-[240px] text-xs font-semibold tracking-wider uppercase\"\n                >\n                  Contractor & Location\n                </th>\n                <th\n                  ui-table-head\n                  class=\"text-muted-foreground min-w-[180px] text-xs font-semibold tracking-wider uppercase\"\n                >\n                  Role & Rate\n                </th>\n                <th\n                  ui-table-head\n                  class=\"text-muted-foreground min-w-[140px] text-xs font-semibold tracking-wider uppercase\"\n                >\n                  Hours Logged\n                </th>\n                <th\n                  ui-table-head\n                  class=\"text-muted-foreground min-w-[200px] text-xs font-semibold tracking-wider uppercase\"\n                >\n                  Invoice Gross Amount\n                </th>\n                <th\n                  ui-table-head\n                  class=\"text-muted-foreground min-w-[220px] text-xs font-semibold tracking-wider uppercase\"\n                >\n                  Timesheet Verification\n                </th>\n                <th\n                  ui-table-head\n                  class=\"text-muted-foreground min-w-[120px] text-xs font-semibold tracking-wider uppercase\"\n                >\n                  Status\n                </th>\n                <th\n                  ui-table-head\n                  class=\"text-muted-foreground min-w-[180px] text-right text-xs font-semibold tracking-wider uppercase\"\n                >\n                  Actions\n                </th>\n              </tr>\n            </thead>\n            <tbody ui-table-body>\n              @if (filteredSubmissions().length > 0) {\n                @for (contractor of filteredSubmissions(); track contractor.id) {\n                  <tr\n                    ui-timesheet-table-row\n                    class=\"border-border hover:bg-muted/30 transition-colors\"\n                    [contractor]=\"contractor\"\n                    [currencyDisplay]=\"currencyDisplay()\"\n                    [formatUsd]=\"formatUsdFn\"\n                    [formatLocal]=\"formatLocalFn\"\n                    (openDrawer)=\"openDrawer($event)\"\n                    (approveInvoice)=\"approveInvoice($event)\"\n                    (disputeInvoice)=\"disputeInvoice($event)\"\n                  ></tr>\n                }\n              } @else {\n                <tr ui-table-row>\n                  <td ui-table-cell colspan=\"7\" class=\"h-36 text-center\">\n                    <div class=\"flex flex-col items-center justify-center gap-1.5\">\n                      <lucide-icon [img]=\"FileSpreadsheet\" class=\"text-muted-foreground/50 size-8\" />\n                      <p class=\"text-foreground text-sm font-medium\">No invoices match your filter</p>\n                      <p class=\"text-muted-foreground text-xs\">\n                        Try adjusting your search query or switching the status filter tab.\n                      </p>\n                    </div>\n                  </td>\n                </tr>\n              }\n            </tbody>\n          </table>\n        </div>\n      </div>\n\n      <!-- Timesheet Detail Drawer / Slide-Over Component -->\n      <ui-timesheet-detail-sheet\n        [open]=\"isDrawerOpen()\"\n        [selectedContractor]=\"selectedContractor()\"\n        [formatUsd]=\"formatUsdFn\"\n        [formatLocal]=\"formatLocalFn\"\n        (openChange)=\"isDrawerOpen.set($event)\"\n        (approve)=\"handleApproveDrawer()\"\n        (dispute)=\"handleDisputeDrawer()\"\n      />\n    </div>\n  `,\n})\nexport class UiContractorTimesheetApprovalComponent {\n  protected readonly Calendar = Calendar\n  protected readonly Check = Check\n  protected readonly CheckCheck = CheckCheck\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Clock = Clock\n  protected readonly FileSpreadsheet = FileSpreadsheet\n  protected readonly Globe = Globe\n  protected readonly RotateCcw = RotateCcw\n  protected readonly Search = Search\n\n  @Input() title = 'Contractor Invoices & Timesheets'\n  @Input() subtitle =\n    'Review international contractor hours, verify commit logs & deliverables, and execute multi-currency invoice approvals.'\n  @Input() payCycle = 'August 2026 · Bi-Weekly #2'\n  @Input() set initialData(v: ContractorSubmission[] | undefined) {\n    if (v) this.submissions.set(structuredClone(v))\n  }\n  @Input('class') className?: string\n\n  readonly submissions = signal<ContractorSubmission[]>(structuredClone(initialSubmissions))\n  readonly searchQuery = signal('')\n  readonly selectedStatus = signal<TimesheetStatusFilter>('all')\n  readonly currencyDisplay = signal<CurrencyDisplay>('both')\n  readonly selectedContractorId = signal<string | null>(null)\n  readonly isDrawerOpen = signal(false)\n  readonly isApprovingAll = signal(false)\n  readonly bulkApprovalDone = signal(false)\n\n  private bulkDoneTimer: ReturnType<typeof setTimeout> | undefined\n  private approveAllTimer: ReturnType<typeof setTimeout> | undefined\n\n  constructor() {\n    inject(DestroyRef).onDestroy(() => {\n      clearTimeout(this.bulkDoneTimer)\n      clearTimeout(this.approveAllTimer)\n    })\n  }\n\n  readonly pendingSubmissions = computed(() => this.submissions().filter((s) => s.status === 'pending'))\n  readonly pendingCount = computed(() => this.pendingSubmissions().length)\n  readonly pendingAmountUsd = computed(() =>\n    this.pendingSubmissions().reduce((acc, curr) => acc + curr.grossAmountUsd, 0),\n  )\n\n  readonly approvedSubmissions = computed(() => this.submissions().filter((s) => s.status === 'approved'))\n  readonly approvedCount = computed(() => this.approvedSubmissions().length)\n\n  readonly disputedCount = computed(() => this.submissions().filter((s) => s.status === 'disputed').length)\n\n  readonly totalHoursLogged = computed(() => this.submissions().reduce((acc, curr) => acc + curr.hoursLogged, 0))\n\n  readonly scheduledPayoutsTotal = computed(() => 42800.0)\n\n  readonly filteredSubmissions = computed(() => {\n    const status = this.selectedStatus()\n    const query = this.searchQuery().trim().toLowerCase()\n    return this.submissions().filter((item) => {\n      const matchesStatus = status === 'all' ? true : item.status === status\n      if (!query) return matchesStatus\n      const matchesSearch =\n        item.name.toLowerCase().includes(query) ||\n        item.role.toLowerCase().includes(query) ||\n        item.country.toLowerCase().includes(query) ||\n        item.city.toLowerCase().includes(query) ||\n        item.invoiceNumber.toLowerCase().includes(query) ||\n        item.verificationBadge.label.toLowerCase().includes(query)\n      return matchesStatus && matchesSearch\n    })\n  })\n\n  readonly selectedContractor = computed(() => this.submissions().find((c) => c.id === this.selectedContractorId()))\n\n  // Bound function references for the row + sheet inputs (same shape as the Vue function props).\n  readonly formatUsdFn = (val: number): string => this.formatUsd(val)\n  readonly formatLocalFn = (val: number, currencyCode: string, currencySymbol: string): string =>\n    this.formatLocal(val, currencyCode, currencySymbol)\n\n  get rootClass(): string {\n    return cn('text-foreground w-full space-y-6', this.className)\n  }\n\n  statusTabClass(status: TimesheetStatusFilter): string {\n    const active = this.selectedStatus() === status\n    const activeTone =\n      status === 'pending'\n        ? 'bg-background text-warning text-warning shadow-xs'\n        : status === 'approved'\n          ? 'bg-background text-success text-success shadow-xs'\n          : status === 'disputed'\n            ? 'bg-background text-destructive text-destructive shadow-xs'\n            : 'bg-background text-foreground shadow-xs'\n    return cn(\n      'rounded-md px-2.5 py-1 font-medium transition-colors',\n      active ? activeTone : 'text-muted-foreground hover:text-foreground',\n    )\n  }\n\n  currencyTabClass(mode: CurrencyDisplay): string {\n    return cn(\n      'rounded-md px-2 py-1 font-medium transition-colors',\n      this.currencyDisplay() === mode\n        ? 'bg-background text-foreground shadow-xs'\n        : 'text-muted-foreground hover:text-foreground',\n    )\n  }\n\n  formatUsd(val: number): string {\n    return new Intl.NumberFormat('en-US', {\n      style: 'currency',\n      currency: 'USD',\n      minimumFractionDigits: 2,\n      maximumFractionDigits: 2,\n    }).format(val)\n  }\n\n  formatLocal(val: number, currencyCode: string, currencySymbol: string): string {\n    if (currencyCode === 'JPY') {\n      return `${currencySymbol}${new Intl.NumberFormat('ja-JP').format(val)}`\n    }\n    return `${currencySymbol}${new Intl.NumberFormat('en-US', {\n      minimumFractionDigits: 2,\n      maximumFractionDigits: 2,\n    }).format(val)} ${currencyCode}`\n  }\n\n  openDrawer(contractor: ContractorSubmission): void {\n    this.selectedContractorId.set(contractor.id)\n    this.isDrawerOpen.set(true)\n  }\n\n  approveInvoice(id: string): void {\n    this.submissions.update((prev) => prev.map((s) => (s.id === id ? { ...s, status: 'approved' as const } : s)))\n  }\n\n  disputeInvoice(id: string): void {\n    this.submissions.update((prev) => prev.map((s) => (s.id === id ? { ...s, status: 'disputed' as const } : s)))\n  }\n\n  handleApproveDrawer(): void {\n    const id = this.selectedContractorId()\n    if (id) this.approveInvoice(id)\n    this.isDrawerOpen.set(false)\n  }\n\n  handleDisputeDrawer(): void {\n    const id = this.selectedContractorId()\n    if (id) this.disputeInvoice(id)\n    this.isDrawerOpen.set(false)\n  }\n\n  approveAllPending(): void {\n    if (this.pendingCount() === 0) return\n    this.isApprovingAll.set(true)\n    clearTimeout(this.approveAllTimer)\n    this.approveAllTimer = setTimeout(() => {\n      this.submissions.update((prev) =>\n        prev.map((s) => (s.status === 'pending' ? { ...s, status: 'approved' as const } : s)),\n      )\n      this.isApprovingAll.set(false)\n      this.bulkApprovalDone.set(true)\n      clearTimeout(this.bulkDoneTimer)\n      this.bulkDoneTimer = setTimeout(() => {\n        this.bulkApprovalDone.set(false)\n      }, 3000)\n    }, 400)\n  }\n\n  resetData(): void {\n    this.submissions.set(structuredClone(initialSubmissions))\n    this.selectedStatus.set('all')\n    this.searchQuery.set('')\n    this.bulkApprovalDone.set(false)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/contractor-timesheet-approval/contractor-timesheet-approval.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/contractor-timesheet-approval/timesheet-detail-sheet.component.ts",
      "content": "import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'\nimport {\n  Check,\n  CheckCircle2,\n  FileCheck,\n  FileCode,\n  GitPullRequest,\n  LucideAngularModule,\n  ShieldCheck,\n  XCircle,\n} from 'lucide-angular'\nimport { UiAvatarComponent, UiAvatarFallbackComponent } from '@/ui/avatar/avatar.component'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiSheetCloseComponent,\n  UiSheetComponent,\n  UiSheetContentComponent,\n  UiSheetDescriptionComponent,\n  UiSheetFooterComponent,\n  UiSheetHeaderComponent,\n  UiSheetTitleComponent,\n} from '@/ui/sheet/sheet.component'\nimport type { ContractorSubmission } from './timesheet-types'\n\n/** Itemized daily timesheet slide-over drawer: verification callout, day-by-day activity log, compliance box. */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-timesheet-detail-sheet, [ui-timesheet-detail-sheet]',\n  standalone: true,\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiAvatarComponent,\n    UiAvatarFallbackComponent,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiSheetCloseComponent,\n    UiSheetComponent,\n    UiSheetContentComponent,\n    UiSheetDescriptionComponent,\n    UiSheetFooterComponent,\n    UiSheetHeaderComponent,\n    UiSheetTitleComponent,\n  ],\n  template: `\n    <div ui-sheet [open]=\"open\" (openChange)=\"openChange.emit($event)\">\n      <div\n        ui-sheet-content\n        side=\"right\"\n        class=\"bg-background text-foreground flex w-full flex-col overflow-y-auto p-0 sm:max-w-xl md:max-w-2xl\"\n      >\n        @if (selectedContractor; as contractor) {\n          <!-- Drawer Header -->\n          <div ui-sheet-header class=\"border-border/80 space-y-3 border-b px-6 py-5 text-left\">\n            <div class=\"flex items-start justify-between gap-4\">\n              <div class=\"flex items-center gap-3\">\n                <ui-avatar class=\"border-border size-12 border\">\n                  <ui-avatar-fallback class=\"bg-primary/10 text-primary text-sm font-bold\">\n                    {{ contractor.initials }}\n                  </ui-avatar-fallback>\n                </ui-avatar>\n                <div>\n                  <div class=\"flex items-center gap-2\">\n                    <h2 ui-sheet-title class=\"text-foreground text-lg font-bold\">\n                      {{ contractor.name }}\n                    </h2>\n                    <span class=\"text-base\" [attr.title]=\"contractor.country\">\n                      {{ contractor.flag }}\n                    </span>\n                  </div>\n                  <p ui-sheet-description class=\"text-muted-foreground text-xs\">\n                    {{ contractor.role }} · {{ contractor.city }}\n                  </p>\n                </div>\n              </div>\n\n              <!-- Status Badge in Header -->\n              @if (contractor.status === 'pending') {\n                <span\n                  ui-badge\n                  variant=\"outline\"\n                  class=\"border-warning/30 bg-warning/10 text-warning shrink-0 text-xs font-medium\"\n                >\n                  <span class=\"bg-warning mr-1.5 size-1.5 rounded-full\"></span>\n                  Pending Review\n                </span>\n              } @else if (contractor.status === 'approved') {\n                <span\n                  ui-badge\n                  variant=\"outline\"\n                  class=\"border-success/30 bg-success/10 text-success shrink-0 text-xs font-medium\"\n                >\n                  <lucide-icon [img]=\"Check\" class=\"text-success mr-1 size-3\" />\n                  Approved\n                </span>\n              } @else {\n                <span\n                  ui-badge\n                  variant=\"outline\"\n                  class=\"border-destructive/30 bg-destructive/10 text-destructive shrink-0 text-xs font-medium\"\n                >\n                  <lucide-icon [img]=\"XCircle\" class=\"text-destructive mr-1 size-3\" />\n                  Disputed\n                </span>\n              }\n            </div>\n\n            <!-- Header Quick Stats Grid -->\n            <div class=\"grid grid-cols-3 gap-2 pt-2 text-xs\">\n              <div class=\"border-border bg-muted/40 rounded-lg border p-2.5\">\n                <span class=\"text-muted-foreground block\">Invoice Gross</span>\n                <span class=\"text-foreground text-sm font-bold tabular-nums\">\n                  {{ formatUsd(contractor.grossAmountUsd) }}\n                </span>\n                <span class=\"text-muted-foreground/70 block text-xs tabular-nums\">\n                  {{ formatLocal(contractor.grossAmountLocal, contractor.currencyCode, contractor.currencySymbol) }}\n                </span>\n              </div>\n              <div class=\"border-border bg-muted/40 rounded-lg border p-2.5\">\n                <span class=\"text-muted-foreground block\">Total Hours</span>\n                <span class=\"text-foreground text-sm font-bold tabular-nums\">\n                  {{ contractor.hoursLogged.toFixed(1) }} hrs\n                </span>\n                <span class=\"text-success text-success block text-xs font-medium tabular-nums\">\n                  {{ contractor.regularHours.toFixed(0) }}h reg · {{ contractor.overtimeHours.toFixed(0) }}h ot\n                </span>\n              </div>\n              <div class=\"border-border bg-muted/40 rounded-lg border p-2.5\">\n                <span class=\"text-muted-foreground block\">Contract Rate</span>\n                <span class=\"text-foreground text-sm font-bold tabular-nums\">\n                  {{ formatUsd(contractor.hourlyRateUsd) }}/hr\n                </span>\n                <span class=\"text-muted-foreground/70 block truncate text-xs\">\n                  {{ contractor.taxCompliance.jurisdiction }}\n                </span>\n              </div>\n            </div>\n          </div>\n\n          <!-- Drawer Body: Timesheet Breakdown -->\n          <div class=\"flex-1 space-y-6 overflow-y-auto px-6 py-5\">\n            <!-- Verification Callout Banner -->\n            <div\n              class=\"border-success/20 bg-success/10 text-success flex items-start gap-3 rounded-lg border p-3.5 text-xs\"\n            >\n              <lucide-icon [img]=\"ShieldCheck\" class=\"text-success mt-0.5 size-4 shrink-0\" />\n              <div class=\"space-y-0.5\">\n                <p class=\"text-success font-semibold\">\n                  {{ contractor.verificationBadge.label }}\n                </p>\n                <p class=\"text-success leading-normal\">\n                  {{ contractor.verificationBadge.subtext }}. Automated hash verification confirmed timestamps match\n                  developer activity during the billing window.\n                </p>\n              </div>\n            </div>\n\n            <!-- Daily Task Itemization -->\n            <div class=\"space-y-3\">\n              <div class=\"flex items-center justify-between gap-x-2\">\n                <h2 class=\"text-muted-foreground text-xs font-semibold\">\n                  Daily Itemized Activity Log ({{ contractor.dailyEntries.length }} Work Days)\n                </h2>\n                <span class=\"text-muted-foreground font-mono text-xs\">\n                  {{ contractor.payPeriod }}\n                </span>\n              </div>\n\n              <div class=\"space-y-2.5\">\n                @for (entry of contractor.dailyEntries; track entry.id) {\n                  <div\n                    class=\"border-border bg-card hover:border-border/80 space-y-2 rounded-lg border p-3.5 transition-colors\"\n                  >\n                    <div class=\"flex items-center justify-between gap-x-2\">\n                      <div class=\"flex items-center gap-2\">\n                        <span class=\"text-foreground text-xs font-semibold\">{{ entry.day }}</span>\n                        <span ui-badge variant=\"outline\" class=\"border-border text-muted-foreground h-4 py-0 text-xs\">\n                          {{ entry.date }}\n                        </span>\n                      </div>\n                      <div class=\"flex items-center gap-2\">\n                        <span class=\"text-foreground text-xs font-bold tabular-nums\">\n                          {{ entry.hours.toFixed(1) }} hrs\n                        </span>\n                        <span class=\"text-muted-foreground text-xs tabular-nums\">\n                          ({{ formatUsd(entry.hours * contractor.hourlyRateUsd) }})\n                        </span>\n                      </div>\n                    </div>\n\n                    <p class=\"text-muted-foreground text-xs leading-relaxed\">\n                      {{ entry.task }}\n                    </p>\n\n                    <!-- Linked Deliverables (PR / Commit / Figma / CI) -->\n                    <div class=\"flex flex-wrap items-center gap-2 pt-1\">\n                      <div\n                        class=\"border-border bg-muted/50 text-foreground inline-flex items-center gap-1.5 rounded-md border px-2 py-0.5 font-mono text-xs\"\n                      >\n                        @if (entry.deliverableType === 'github_pr') {\n                          <lucide-icon [img]=\"GitPullRequest\" class=\"text-chart-1 size-3\" />\n                          <span>{{ entry.referenceId }}</span>\n                          <span class=\"text-muted-foreground\">({{ entry.referenceLabel }})</span>\n                          @if (entry.commitCount) {\n                            <span class=\"text-muted-foreground\">· {{ entry.commitCount }} commits</span>\n                          }\n                        } @else if (entry.deliverableType === 'figma') {\n                          <lucide-icon [img]=\"FileCode\" class=\"text-chart-2 size-3\" />\n                          <span>{{ entry.referenceId }}</span>\n                          <span class=\"text-muted-foreground\">({{ entry.referenceLabel }})</span>\n                        } @else {\n                          <lucide-icon [img]=\"CheckCircle2\" class=\"text-success size-3\" />\n                          <span>{{ entry.referenceId }}</span>\n                          <span class=\"text-muted-foreground\">({{ entry.referenceLabel }})</span>\n                        }\n                      </div>\n\n                      <span\n                        ui-badge\n                        variant=\"outline\"\n                        class=\"border-success/20 bg-success/10 text-success h-5 gap-1 py-0 text-xs\"\n                      >\n                        <lucide-icon [img]=\"Check\" class=\"size-2.5\" />\n                        Verified\n                      </span>\n                    </div>\n                  </div>\n                }\n              </div>\n            </div>\n\n            <!-- Tax & Bank Payout Compliance Box -->\n            <div class=\"border-border bg-muted/20 space-y-2.5 rounded-lg border p-4 text-xs\">\n              <h3 class=\"text-foreground flex items-center gap-1.5 font-bold\">\n                <lucide-icon [img]=\"FileCheck\" class=\"text-primary size-3.5\" />\n                Compliance & Disbursement Routing\n              </h3>\n              <div class=\"grid grid-cols-2 gap-3 pt-1\">\n                <div>\n                  <span class=\"text-muted-foreground block\">Tax Form Status:</span>\n                  <span class=\"text-foreground font-medium\">\n                    Form W-8BEN ({{ contractor.taxCompliance.w8benStatus.toUpperCase() }})\n                  </span>\n                  <span class=\"text-muted-foreground block font-mono text-xs\">{{\n                    contractor.taxCompliance.taxIdMasked\n                  }}</span>\n                </div>\n                <div>\n                  <span class=\"text-muted-foreground block\">Disbursement Route:</span>\n                  <span class=\"text-foreground font-medium\"> {{ contractor.paymentMethod.type }} Payout </span>\n                  <span class=\"text-muted-foreground block text-xs\">{{ contractor.paymentMethod.bankName }}</span>\n                </div>\n              </div>\n            </div>\n          </div>\n\n          <!-- Drawer Footer Actions -->\n          <div\n            ui-sheet-footer\n            class=\"border-border/80 bg-muted/20 flex flex-row items-center justify-between gap-3 border-t px-6 py-4\"\n          >\n            <button ui-button ui-sheet-close variant=\"outline\" size=\"sm\" class=\"text-xs\">Close</button>\n\n            <div class=\"flex items-center gap-2\">\n              @if (contractor.status !== 'disputed') {\n                <button\n                  ui-button\n                  variant=\"outline\"\n                  size=\"sm\"\n                  class=\"border-destructive/30 text-destructive hover:bg-destructive/10 text-destructive text-xs\"\n                  (click)=\"dispute.emit()\"\n                >\n                  <lucide-icon [img]=\"XCircle\" class=\"mr-1.5 size-3.5\" />\n                  Dispute Invoice\n                </button>\n              }\n\n              @if (contractor.status !== 'approved') {\n                <button ui-button variant=\"default\" size=\"sm\" class=\"text-xs shadow-xs\" (click)=\"approve.emit()\">\n                  <lucide-icon [img]=\"Check\" class=\"mr-1.5 size-3.5\" />\n                  Approve ({{ formatUsd(contractor.grossAmountUsd) }})\n                </button>\n              } @else {\n                <span class=\"text-success flex items-center gap-1 text-xs font-semibold\">\n                  <lucide-icon [img]=\"CheckCircle2\" class=\"size-4\" />\n                  Approved & Queued\n                </span>\n              }\n            </div>\n          </div>\n        }\n      </div>\n    </div>\n  `,\n})\nexport class UiTimesheetDetailSheetComponent {\n  @Input() open = false\n  @Input() selectedContractor?: ContractorSubmission\n  @Input() formatUsd: (val: number) => string = (v) => String(v)\n  @Input() formatLocal: (val: number, currencyCode: string, currencySymbol: string) => string = (v) => String(v)\n  @Output() readonly openChange = new EventEmitter<boolean>()\n  @Output() readonly approve = new EventEmitter<void>()\n  @Output() readonly dispute = new EventEmitter<void>()\n\n  protected readonly Check = Check\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly FileCheck = FileCheck\n  protected readonly FileCode = FileCode\n  protected readonly GitPullRequest = GitPullRequest\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly XCircle = XCircle\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/contractor-timesheet-approval/timesheet-detail-sheet.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/contractor-timesheet-approval/timesheet-kpi-cards.component.ts",
      "content": "import { ChangeDetectionStrategy, Component, Input } from '@angular/core'\nimport { Clock, LucideAngularModule, ShieldCheck, Timer, Users } from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\n\n/** Four billing overview KPI cards: pending approval amount, total hours, scheduled payouts, active contractors. */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-timesheet-kpi-cards, [ui-timesheet-kpi-cards]',\n  standalone: true,\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [LucideAngularModule, UiCardComponent, UiCardContentComponent, UiCardHeaderComponent, UiCardTitleComponent],\n  template: `\n    <div [class]=\"rootClass\">\n      <!-- Card 1: Pending Approval Amount -->\n      <div ui-card class=\"border-border bg-card shadow-xs transition-colors\">\n        <div ui-card-header class=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n          <h3 ui-card-title class=\"text-muted-foreground text-xs font-medium\">Pending Approval Amount</h3>\n          <div\n            class=\"border-warning/20 bg-warning/10 text-warning flex size-8 items-center justify-center rounded-lg border\"\n          >\n            <lucide-icon [img]=\"Clock\" class=\"size-4\" />\n          </div>\n        </div>\n        <div ui-card-content class=\"space-y-1\">\n          <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">\n            {{ formatUsd(pendingAmountUsd) }}\n          </div>\n          <p class=\"text-muted-foreground text-xs\">\n            <span class=\"text-warning text-warning font-semibold tabular-nums\"> {{ pendingCount }} invoices </span>\n            awaiting your approval\n          </p>\n          <div class=\"pt-2\">\n            <span\n              class=\"bg-muted text-muted-foreground inline-flex items-center rounded-md px-2 py-0.5 text-xs tabular-nums\"\n            >\n              £3,800 · ¥810k · R$41k\n            </span>\n          </div>\n        </div>\n      </div>\n\n      <!-- Card 2: Total Contractor Hours -->\n      <div ui-card class=\"border-border bg-card shadow-xs transition-colors\">\n        <div ui-card-header class=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n          <h3 ui-card-title class=\"text-muted-foreground text-xs font-medium\">Total Contractor Hours</h3>\n          <div class=\"border-info/20 bg-info/10 text-info flex size-8 items-center justify-center rounded-lg border\">\n            <lucide-icon [img]=\"Timer\" class=\"size-4\" />\n          </div>\n        </div>\n        <div ui-card-content class=\"space-y-1\">\n          <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">\n            {{ totalHoursLogged.toFixed(1) }} hrs\n          </div>\n          <p class=\"text-muted-foreground text-xs\">\n            <span class=\"text-foreground font-medium tabular-nums\">160.0 regular</span>\n            · 80.0 overtime / project\n          </p>\n          <div class=\"pt-2\">\n            <span class=\"bg-info/10 text-info inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium\">\n              100% Timesheets submitted\n            </span>\n          </div>\n        </div>\n      </div>\n\n      <!-- Card 3: Approved & Scheduled Payouts -->\n      <div ui-card class=\"border-border bg-card shadow-xs transition-colors\">\n        <div ui-card-header class=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n          <h3 ui-card-title class=\"text-muted-foreground text-xs font-medium\">Approved & Scheduled</h3>\n          <div\n            class=\"border-success/20 bg-success/10 text-success flex size-8 items-center justify-center rounded-lg border\"\n          >\n            <lucide-icon [img]=\"ShieldCheck\" class=\"size-4\" />\n          </div>\n        </div>\n        <div ui-card-content class=\"space-y-1\">\n          <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">\n            {{ formatUsd(scheduledPayoutsTotal) }}\n          </div>\n          <p class=\"text-muted-foreground text-xs\">\n            <span class=\"text-success text-success font-medium tabular-nums\"> 5 invoices </span>\n            batched for Aug 25 payout\n          </p>\n          <div class=\"pt-2\">\n            <span class=\"bg-muted text-muted-foreground inline-flex items-center rounded-md px-2 py-0.5 text-xs\">\n              Direct SEPA · ACH · SWIFT\n            </span>\n          </div>\n        </div>\n      </div>\n\n      <!-- Card 4: Active International Contractors -->\n      <div ui-card class=\"border-border bg-card shadow-xs transition-colors\">\n        <div ui-card-header class=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n          <h3 ui-card-title class=\"text-muted-foreground text-xs font-medium\">Active Contractors</h3>\n          <div\n            class=\"border-chart-1/20 bg-chart-1/10 text-chart-1 flex size-8 items-center justify-center rounded-lg border\"\n          >\n            <lucide-icon [img]=\"Users\" class=\"size-4\" />\n          </div>\n        </div>\n        <div ui-card-content class=\"space-y-1\">\n          <div class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">8 Contractors</div>\n          <p class=\"text-muted-foreground text-xs\">Across 5 countries (UK, JP, BR, FR, CA)</p>\n          <div class=\"pt-2\">\n            <span\n              class=\"bg-chart-1/10 text-chart-1 inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium\"\n            >\n              100% W-8BEN Tax Valid\n            </span>\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiTimesheetKpiCardsComponent {\n  @Input() pendingAmountUsd = 0\n  @Input() pendingCount = 0\n  @Input() totalHoursLogged = 0\n  @Input() scheduledPayoutsTotal = 0\n  @Input() formatUsd: (val: number) => string = (v) => String(v)\n  @Input('class') className?: string\n\n  get rootClass(): string {\n    return cn('grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4', this.className)\n  }\n\n  protected readonly Clock = Clock\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly Timer = Timer\n  protected readonly Users = Users\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/contractor-timesheet-approval/timesheet-kpi-cards.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/contractor-timesheet-approval/timesheet-table-row.component.ts",
      "content": "import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'\nimport {\n  AlertCircle,\n  Check,\n  CheckCircle2,\n  Download,\n  Eye,\n  FileText,\n  LucideAngularModule,\n  MoreHorizontal,\n  ShieldCheck,\n  XCircle,\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  UiDropdownMenuComponent,\n  UiDropdownMenuContentComponent,\n  UiDropdownMenuItemComponent,\n  UiDropdownMenuLabelComponent,\n  UiDropdownMenuSeparatorComponent,\n  UiDropdownMenuTriggerComponent,\n} from '@/ui/dropdown-menu/dropdown-menu.component'\nimport { UiTableCellComponent } from '@/ui/table/table.component'\nimport type { ContractorSubmission } from './timesheet-types'\n\n/**\n * One contractor invoice row. Attribute selector (`tr[ui-timesheet-table-row]`) so the host\n * IS the table row — a wrapper element inside <tbody> would break table layout.\n */\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'tr[ui-timesheet-table-row]',\n  standalone: true,\n  // Same host contract as UiTableRow (data-slot + base row classes) merged with the\n  // consumer `class`, so the row needs no wrapper element inside <tbody>.\n  host: { '[attr.data-slot]': '\"table-row\"', '[attr.data-uipkge]': '\"\"', '[class]': 'hostClass' },\n  imports: [\n    LucideAngularModule,\n    UiAvatarComponent,\n    UiAvatarFallbackComponent,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiDropdownMenuComponent,\n    UiDropdownMenuContentComponent,\n    UiDropdownMenuItemComponent,\n    UiDropdownMenuLabelComponent,\n    UiDropdownMenuSeparatorComponent,\n    UiDropdownMenuTriggerComponent,\n    UiTableCellComponent,\n  ],\n  template: `\n    <!-- Contractor & Location -->\n    <td ui-table-cell class=\"py-4\">\n      <div class=\"flex items-center gap-3\">\n        <ui-avatar class=\"border-border size-10 shrink-0 border\">\n          <ui-avatar-fallback class=\"bg-primary/10 text-primary text-xs font-semibold\">\n            {{ contractor.initials }}\n          </ui-avatar-fallback>\n        </ui-avatar>\n        <div class=\"space-y-0.5\">\n          <div class=\"flex items-center gap-1.5\">\n            <button\n              type=\"button\"\n              class=\"text-foreground focus-visible:ring-ring cursor-pointer rounded-xs text-left text-sm font-semibold hover:underline focus-visible:ring-2 focus-visible:outline-none\"\n              (click)=\"openDrawer.emit(contractor)\"\n            >\n              {{ contractor.name }}\n            </button>\n            <span class=\"text-sm\" [attr.title]=\"contractor.country\">{{ contractor.flag }}</span>\n          </div>\n          <p class=\"text-muted-foreground flex items-center gap-1 text-xs\">\n            <span>{{ contractor.city }}</span>\n            <span>•</span>\n            <span class=\"text-muted-foreground/80 font-mono\">{{ contractor.invoiceNumber }}</span>\n          </p>\n        </div>\n      </div>\n    </td>\n\n    <!-- Role & Rate -->\n    <td ui-table-cell class=\"py-4\">\n      <div class=\"space-y-0.5\">\n        <p class=\"text-foreground text-xs font-medium\">{{ contractor.role }}</p>\n        <p class=\"text-muted-foreground text-xs tabular-nums\">\n          {{ formatUsd(contractor.hourlyRateUsd) }}/hr\n          <span class=\"text-muted-foreground/70\">\n            ({{ formatLocal(contractor.hourlyRateLocal, contractor.currencyCode, contractor.currencySymbol) }}/hr)\n          </span>\n        </p>\n      </div>\n    </td>\n\n    <!-- Hours Logged with daily breakdown expander button -->\n    <td ui-table-cell class=\"py-4\">\n      <div class=\"space-y-1\">\n        <div class=\"flex items-center gap-1.5\">\n          <span class=\"text-foreground text-sm font-bold tabular-nums\">\n            {{ contractor.hoursLogged.toFixed(1) }} hrs\n          </span>\n        </div>\n        <div class=\"text-muted-foreground flex items-center gap-1 text-xs tabular-nums\">\n          <span class=\"text-success font-medium\"> {{ contractor.regularHours.toFixed(0) }}h reg </span>\n          @if (contractor.overtimeHours > 0) {\n            <span>+</span>\n            <span class=\"text-info font-medium\"> {{ contractor.overtimeHours.toFixed(0) }}h ot </span>\n          }\n        </div>\n      </div>\n    </td>\n\n    <!-- Invoice Gross (Local & USD) -->\n    <td ui-table-cell class=\"py-4\">\n      <div class=\"space-y-1\">\n        @if (currencyDisplay === 'both') {\n          <div class=\"flex items-baseline gap-1.5\">\n            <span class=\"text-foreground text-sm font-bold tabular-nums\">\n              {{ formatUsd(contractor.grossAmountUsd) }}\n            </span>\n            <span class=\"text-muted-foreground text-xs font-normal\">USD</span>\n          </div>\n          <p class=\"text-muted-foreground text-xs tabular-nums\">\n            {{ formatLocal(contractor.grossAmountLocal, contractor.currencyCode, contractor.currencySymbol) }}\n            <span class=\"text-muted-foreground/60 text-xs\">(FX: {{ contractor.fxRate.toFixed(4) }})</span>\n          </p>\n        } @else if (currencyDisplay === 'usd') {\n          <div class=\"text-foreground text-sm font-bold tabular-nums\">\n            {{ formatUsd(contractor.grossAmountUsd) }} USD\n          </div>\n          <p class=\"text-muted-foreground text-xs tabular-nums\">Rate: {{ formatUsd(contractor.hourlyRateUsd) }}/hr</p>\n        } @else {\n          <div class=\"text-foreground text-sm font-bold tabular-nums\">\n            {{ formatLocal(contractor.grossAmountLocal, contractor.currencyCode, contractor.currencySymbol) }}\n          </div>\n          <p class=\"text-muted-foreground text-xs tabular-nums\">Conv: {{ formatUsd(contractor.grossAmountUsd) }} USD</p>\n        }\n      </div>\n    </td>\n\n    <!-- Timesheet Verification Status -->\n    <td ui-table-cell class=\"py-4\">\n      <div class=\"space-y-1\">\n        <span ui-badge variant=\"outline\" class=\"border-success/25 bg-success/10 text-success gap-1 text-xs font-medium\">\n          <lucide-icon [img]=\"CheckCircle2\" class=\"text-success size-3\" />\n          {{ contractor.verificationBadge.label }}\n        </span>\n        <p\n          class=\"text-muted-foreground max-w-[200px] truncate text-xs\"\n          [attr.title]=\"contractor.verificationBadge.subtext\"\n        >\n          {{ contractor.verificationBadge.subtext }}\n        </p>\n      </div>\n    </td>\n\n    <!-- Approval Status -->\n    <td ui-table-cell class=\"py-4\">\n      @if (contractor.status === 'pending') {\n        <span ui-badge variant=\"outline\" class=\"border-warning/30 bg-warning/10 text-warning text-xs font-medium\">\n          <span class=\"bg-warning mr-1.5 size-1.5 rounded-full\"></span>\n          Pending Review\n        </span>\n      } @else if (contractor.status === 'approved') {\n        <span ui-badge variant=\"outline\" class=\"border-success/30 bg-success/10 text-success text-xs font-medium\">\n          <lucide-icon [img]=\"Check\" class=\"text-success mr-1 size-3\" />\n          Approved\n        </span>\n      } @else {\n        <span\n          ui-badge\n          variant=\"outline\"\n          class=\"border-destructive/30 bg-destructive/10 text-destructive text-xs font-medium\"\n        >\n          <lucide-icon [img]=\"XCircle\" class=\"text-destructive mr-1 size-3\" />\n          Disputed\n        </span>\n      }\n    </td>\n\n    <!-- Action Buttons -->\n    <td ui-table-cell class=\"py-4 text-right\">\n      <div class=\"flex items-center justify-end gap-1.5\">\n        <button\n          ui-button\n          variant=\"outline\"\n          size=\"sm\"\n          class=\"border-border hover:bg-muted h-8 gap-1 px-2.5 text-xs font-medium\"\n          (click)=\"openDrawer.emit(contractor)\"\n        >\n          <lucide-icon [img]=\"Eye\" class=\"text-muted-foreground size-3.5\" />\n          Timesheet\n        </button>\n\n        @if (contractor.status === 'pending') {\n          <button\n            ui-button\n            variant=\"default\"\n            size=\"sm\"\n            class=\"h-8 px-2.5 text-xs font-medium shadow-xs\"\n            (click)=\"approveInvoice.emit(contractor.id)\"\n          >\n            <lucide-icon [img]=\"Check\" class=\"mr-1 size-3.5\" />\n            Approve\n          </button>\n        }\n\n        <!-- Dropdown Menu for Secondary Actions -->\n        <ui-dropdown-menu>\n          <button\n            ui-button\n            ui-dropdown-menu-trigger\n            variant=\"ghost\"\n            size=\"icon\"\n            class=\"text-muted-foreground hover:text-foreground size-8\"\n          >\n            <lucide-icon [img]=\"MoreHorizontal\" class=\"size-4\" />\n            <span class=\"sr-only\">Open contractor menu</span>\n          </button>\n          <ui-dropdown-menu-content align=\"end\" class=\"w-48\">\n            <div ui-dropdown-menu-label class=\"text-muted-foreground text-xs\">\n              Invoice {{ contractor.invoiceNumber }}\n            </div>\n            <ui-dropdown-menu-item class=\"cursor-pointer text-xs\" (select)=\"openDrawer.emit(contractor)\">\n              <lucide-icon [img]=\"FileText\" class=\"text-muted-foreground mr-2 size-3.5\" />\n              Itemized Timesheet\n            </ui-dropdown-menu-item>\n            @if (contractor.status !== 'approved') {\n              <ui-dropdown-menu-item\n                class=\"text-success cursor-pointer text-xs\"\n                (select)=\"approveInvoice.emit(contractor.id)\"\n              >\n                <lucide-icon [img]=\"Check\" class=\"mr-2 size-3.5\" />\n                Approve Invoice\n              </ui-dropdown-menu-item>\n            }\n            @if (contractor.status !== 'disputed') {\n              <ui-dropdown-menu-item\n                class=\"text-destructive cursor-pointer text-xs\"\n                (select)=\"disputeInvoice.emit(contractor.id)\"\n              >\n                <lucide-icon [img]=\"AlertCircle\" class=\"mr-2 size-3.5\" />\n                Dispute / Reject\n              </ui-dropdown-menu-item>\n            }\n            <ui-dropdown-menu-separator />\n            <ui-dropdown-menu-item class=\"cursor-pointer text-xs\">\n              <lucide-icon [img]=\"Download\" class=\"text-muted-foreground mr-2 size-3.5\" />\n              Download Invoice PDF\n            </ui-dropdown-menu-item>\n            <ui-dropdown-menu-item class=\"cursor-pointer text-xs\">\n              <lucide-icon [img]=\"ShieldCheck\" class=\"text-muted-foreground mr-2 size-3.5\" />\n              View W-8BEN Form\n            </ui-dropdown-menu-item>\n          </ui-dropdown-menu-content>\n        </ui-dropdown-menu>\n      </div>\n    </td>\n  `,\n})\nexport class UiTimesheetTableRowComponent {\n  @Input({ required: true }) contractor!: ContractorSubmission\n  @Input() currencyDisplay: 'both' | 'usd' | 'local' = 'both'\n  @Input() formatUsd: (val: number) => string = (v) => String(v)\n  @Input() formatLocal: (val: number, currencyCode: string, currencySymbol: string) => string = (v) => String(v)\n  @Input('class') className?: string\n  @Output() readonly openDrawer = new EventEmitter<ContractorSubmission>()\n  @Output() readonly approveInvoice = new EventEmitter<string>()\n  @Output() readonly disputeInvoice = new EventEmitter<string>()\n\n  get hostClass(): string {\n    return cn(\n      'table-row hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors duration-150',\n      this.className,\n    )\n  }\n\n  protected readonly AlertCircle = AlertCircle\n  protected readonly Check = Check\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Download = Download\n  protected readonly Eye = Eye\n  protected readonly FileText = FileText\n  protected readonly MoreHorizontal = MoreHorizontal\n  protected readonly ShieldCheck = ShieldCheck\n  protected readonly XCircle = XCircle\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/contractor-timesheet-approval/timesheet-table-row.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/contractor-timesheet-approval/timesheet-types.ts",
      "content": "export type ApprovalStatus = 'pending' | 'approved' | 'disputed'\nexport type DeliverableType = 'github_pr' | 'figma' | 'ci_pipeline' | 'code_review'\n\nexport interface DailyTimesheetEntry {\n  id: string\n  date: string\n  day: string\n  hours: number\n  regularHours: number\n  overtimeHours: number\n  task: string\n  deliverableType: DeliverableType\n  referenceId: string\n  referenceLabel: string\n  commitCount?: number\n  verified: boolean\n}\n\nexport interface ContractorSubmission {\n  id: string\n  name: string\n  initials: string\n  avatarUrl?: string\n  role: string\n  country: string\n  countryCode: string\n  flag: string\n  city: string\n  contractType: string\n  hourlyRateUsd: number\n  hourlyRateLocal: number\n  currencyCode: string\n  currencySymbol: string\n  fxRate: number\n  hoursLogged: number\n  regularHours: number\n  overtimeHours: number\n  grossAmountLocal: number\n  grossAmountUsd: number\n  verificationBadge: {\n    label: string\n    subtext: string\n    status: 'verified' | 'manual'\n  }\n  status: ApprovalStatus\n  invoiceNumber: string\n  submittedAt: string\n  payPeriod: string\n  taxCompliance: {\n    w8benStatus: 'valid' | 'pending' | 'exempt'\n    taxIdMasked: string\n    jurisdiction: string\n  }\n  paymentMethod: {\n    type: 'SWIFT' | 'SEPA' | 'Local Wire' | 'ACH'\n    accountMasked: string\n    bankName: string\n  }\n  dailyEntries: DailyTimesheetEntry[]\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/contractor-timesheet-approval/timesheet-types.ts"
    },
    {
      "path": "packages/registry-angular/blocks/contractor-timesheet-approval/timesheet-data.ts",
      "content": "import type { ContractorSubmission } from './timesheet-types'\n\nexport const initialSubmissions: ContractorSubmission[] = [\n  {\n    id: 'ctr-1',\n    name: \"Liam O'Connor\",\n    initials: 'LO',\n    role: 'Senior React Engineer',\n    country: 'United Kingdom',\n    countryCode: 'GB',\n    flag: '🇬🇧',\n    city: 'London, UK',\n    contractType: 'Hourly Contract · $97.00/hr',\n    hourlyRateUsd: 97.0,\n    hourlyRateLocal: 76.0,\n    currencyCode: 'GBP',\n    currencySymbol: '£',\n    fxRate: 1.2763,\n    hoursLogged: 50.0,\n    regularHours: 40.0,\n    overtimeHours: 10.0,\n    grossAmountLocal: 3800.0,\n    grossAmountUsd: 4850.0,\n    verificationBadge: {\n      label: 'Verified with GitHub PRs',\n      subtext: '14 PRs merged · 46 verified commits',\n      status: 'verified',\n    },\n    status: 'pending',\n    invoiceNumber: 'INV-2026-UK-0841',\n    submittedAt: 'Aug 21, 2026 · 17:45 UTC',\n    payPeriod: 'Aug 08 – Aug 21, 2026',\n    taxCompliance: {\n      w8benStatus: 'valid',\n      taxIdMasked: 'GB-UTR ••••••8912',\n      jurisdiction: 'HMRC UK (Non-US Source)',\n    },\n    paymentMethod: {\n      type: 'SWIFT',\n      accountMasked: 'Barclays •••• 4421',\n      bankName: 'Barclays Bank UK PLC',\n    },\n    dailyEntries: [\n      {\n        id: 'entry-1-1',\n        date: '2026-08-17',\n        day: 'Mon, Aug 17',\n        hours: 10.0,\n        regularHours: 8.0,\n        overtimeHours: 2.0,\n        task: 'Refactoring OKLCH Color Engine & AST Parser for sub-theme resolution',\n        deliverableType: 'github_pr',\n        referenceId: 'PR #412',\n        referenceLabel: 'feature/v4-ast-parser',\n        commitCount: 12,\n        verified: true,\n      },\n      {\n        id: 'entry-1-2',\n        date: '2026-08-18',\n        day: 'Tue, Aug 18',\n        hours: 10.0,\n        regularHours: 8.0,\n        overtimeHours: 2.0,\n        task: 'SSR Hydration Mismatch Resolver for cross-framework Island Architectures',\n        deliverableType: 'github_pr',\n        referenceId: 'PR #415',\n        referenceLabel: 'fix/island-hydration',\n        commitCount: 8,\n        verified: true,\n      },\n      {\n        id: 'entry-1-3',\n        date: '2026-08-19',\n        day: 'Wed, Aug 19',\n        hours: 10.0,\n        regularHours: 8.0,\n        overtimeHours: 2.0,\n        task: 'Design token runtime compilation and memoized theme class cache',\n        deliverableType: 'github_pr',\n        referenceId: 'PR #418',\n        referenceLabel: 'perf/token-runtime',\n        commitCount: 7,\n        verified: true,\n      },\n      {\n        id: 'entry-1-4',\n        date: '2026-08-20',\n        day: 'Thu, Aug 20',\n        hours: 10.0,\n        regularHours: 8.0,\n        overtimeHours: 2.0,\n        task: 'Figma token sync GitHub Action workflow optimization and schema tests',\n        deliverableType: 'github_pr',\n        referenceId: 'PR #419',\n        referenceLabel: 'ci/figma-token-sync',\n        commitCount: 9,\n        verified: true,\n      },\n      {\n        id: 'entry-1-5',\n        date: '2026-08-21',\n        day: 'Fri, Aug 21',\n        hours: 10.0,\n        regularHours: 8.0,\n        overtimeHours: 2.0,\n        task: 'Reka UI combobox accessible keyboard navigation bridge and focus ring parity',\n        deliverableType: 'github_pr',\n        referenceId: 'PR #423',\n        referenceLabel: 'feat/reka-combobox-a11y',\n        commitCount: 10,\n        verified: true,\n      },\n    ],\n  },\n  {\n    id: 'ctr-2',\n    name: 'Yuki Tanaka',\n    initials: 'YT',\n    role: 'UI/UX Designer',\n    country: 'Japan',\n    countryCode: 'JP',\n    flag: '🇯🇵',\n    city: 'Tokyo, JP',\n    contractType: 'Hourly Contract · $90.00/hr',\n    hourlyRateUsd: 90.0,\n    hourlyRateLocal: 13500,\n    currencyCode: 'JPY',\n    currencySymbol: '¥',\n    fxRate: 0.00667,\n    hoursLogged: 60.0,\n    regularHours: 40.0,\n    overtimeHours: 20.0,\n    grossAmountLocal: 810000,\n    grossAmountUsd: 5400.0,\n    verificationBadge: {\n      label: 'Figma Files & Handoff Verified',\n      subtext: '8 wireframes & design spec approved',\n      status: 'verified',\n    },\n    status: 'pending',\n    invoiceNumber: 'INV-2026-JP-0219',\n    submittedAt: 'Aug 21, 2026 · 14:10 UTC',\n    payPeriod: 'Aug 08 – Aug 21, 2026',\n    taxCompliance: {\n      w8benStatus: 'valid',\n      taxIdMasked: 'JP-NTA ••••••4401',\n      jurisdiction: 'National Tax Agency Japan',\n    },\n    paymentMethod: {\n      type: 'Local Wire',\n      accountMasked: 'MUFG •••• 9102',\n      bankName: 'Mitsubishi UFJ Financial Group',\n    },\n    dailyEntries: [\n      {\n        id: 'entry-2-1',\n        date: '2026-08-17',\n        day: 'Mon, Aug 17',\n        hours: 12.0,\n        regularHours: 8.0,\n        overtimeHours: 4.0,\n        task: 'Enterprise Multi-currency Invoicing Flow User Journey Map & Edge Cases',\n        deliverableType: 'figma',\n        referenceId: 'Node #492:102',\n        referenceLabel: 'figma.com/@uipkge/multi-currency-flow',\n        commitCount: 6,\n        verified: true,\n      },\n      {\n        id: 'entry-2-2',\n        date: '2026-08-18',\n        day: 'Tue, Aug 18',\n        hours: 12.0,\n        regularHours: 8.0,\n        overtimeHours: 4.0,\n        task: 'Contractor Compliance Document Verification UX Specs & Empty States',\n        deliverableType: 'figma',\n        referenceId: 'Node #495:88',\n        referenceLabel: 'figma.com/@uipkge/compliance-docs',\n        commitCount: 5,\n        verified: true,\n      },\n      {\n        id: 'entry-2-3',\n        date: '2026-08-19',\n        day: 'Wed, Aug 19',\n        hours: 12.0,\n        regularHours: 8.0,\n        overtimeHours: 4.0,\n        task: 'Mobile responsive Timesheet approval breakpoints, drawers, and gesture specs',\n        deliverableType: 'figma',\n        referenceId: 'Node #501:14',\n        referenceLabel: 'figma.com/@uipkge/mobile-timesheet',\n        commitCount: 8,\n        verified: true,\n      },\n      {\n        id: 'entry-2-4',\n        date: '2026-08-20',\n        day: 'Thu, Aug 20',\n        hours: 12.0,\n        regularHours: 8.0,\n        overtimeHours: 4.0,\n        task: 'Interactive Design System Token Library for Dark/Light Themes & High-Contrast',\n        deliverableType: 'figma',\n        referenceId: 'Node #510:33',\n        referenceLabel: 'figma.com/@uipkge/tokens-library',\n        commitCount: 11,\n        verified: true,\n      },\n      {\n        id: 'entry-2-5',\n        date: '2026-08-21',\n        day: 'Fri, Aug 21',\n        hours: 12.0,\n        regularHours: 8.0,\n        overtimeHours: 4.0,\n        task: 'Usability testing session synthesis, prototype refinements, and engineering handoff',\n        deliverableType: 'figma',\n        referenceId: 'Node #514:02',\n        referenceLabel: 'figma.com/@uipkge/handoff-v2',\n        commitCount: 4,\n        verified: true,\n      },\n    ],\n  },\n  {\n    id: 'ctr-3',\n    name: 'Mateo Silva',\n    initials: 'MS',\n    role: 'Go Backend Specialist',\n    country: 'Brazil',\n    countryCode: 'BR',\n    flag: '🇧🇷',\n    city: 'São Paulo, BR',\n    contractType: 'Hourly Contract · $91.11/hr',\n    hourlyRateUsd: 91.11,\n    hourlyRateLocal: 455.55,\n    currencyCode: 'BRL',\n    currencySymbol: 'R$',\n    fxRate: 0.2,\n    hoursLogged: 90.0,\n    regularHours: 40.0,\n    overtimeHours: 50.0,\n    grossAmountLocal: 41000.0,\n    grossAmountUsd: 8200.0,\n    verificationBadge: {\n      label: 'Verified with GitHub PRs',\n      subtext: 'Kafka partitioning & Redis idempotency engine',\n      status: 'verified',\n    },\n    status: 'pending',\n    invoiceNumber: 'INV-2026-BR-0572',\n    submittedAt: 'Aug 21, 2026 · 19:20 UTC',\n    payPeriod: 'Aug 08 – Aug 21, 2026',\n    taxCompliance: {\n      w8benStatus: 'valid',\n      taxIdMasked: 'BR-CNPJ ••••••7100',\n      jurisdiction: 'Receita Federal do Brasil',\n    },\n    paymentMethod: {\n      type: 'SWIFT',\n      accountMasked: 'Itaú Unibanco •••• 8831',\n      bankName: 'Banco Itaú S.A.',\n    },\n    dailyEntries: [\n      {\n        id: 'entry-3-1',\n        date: '2026-08-17',\n        day: 'Mon, Aug 17',\n        hours: 18.0,\n        regularHours: 8.0,\n        overtimeHours: 10.0,\n        task: 'Distributed transaction saga orchestrator for multi-currency payout reconciliation',\n        deliverableType: 'github_pr',\n        referenceId: 'PR #289',\n        referenceLabel: 'feat/saga-orchestrator',\n        commitCount: 15,\n        verified: true,\n      },\n      {\n        id: 'entry-3-2',\n        date: '2026-08-18',\n        day: 'Tue, Aug 18',\n        hours: 18.0,\n        regularHours: 8.0,\n        overtimeHours: 10.0,\n        task: 'Zero-downtime PostgreSQL table partitioning for timesheet audit logs & retention',\n        deliverableType: 'github_pr',\n        referenceId: 'PR #294',\n        referenceLabel: 'db/pg-partitioning',\n        commitCount: 7,\n        verified: true,\n      },\n      {\n        id: 'entry-3-3',\n        date: '2026-08-19',\n        day: 'Wed, Aug 19',\n        hours: 18.0,\n        regularHours: 8.0,\n        overtimeHours: 10.0,\n        task: 'Webhook delivery retry backoff with Kafka dead-letter queue & idempotency keys',\n        deliverableType: 'github_pr',\n        referenceId: 'PR #298',\n        referenceLabel: 'feat/kafka-dlq-retries',\n        commitCount: 19,\n        verified: true,\n      },\n      {\n        id: 'entry-3-4',\n        date: '2026-08-20',\n        day: 'Thu, Aug 20',\n        hours: 18.0,\n        regularHours: 8.0,\n        overtimeHours: 10.0,\n        task: 'Prometheus latency metric instrumentation & Grafana alert rules for payment workers',\n        deliverableType: 'github_pr',\n        referenceId: 'PR #302',\n        referenceLabel: 'obs/prometheus-metrics',\n        commitCount: 5,\n        verified: true,\n      },\n      {\n        id: 'entry-3-5',\n        date: '2026-08-21',\n        day: 'Fri, Aug 21',\n        hours: 18.0,\n        regularHours: 8.0,\n        overtimeHours: 10.0,\n        task: 'Payment gateway failover simulation & multi-region load testing at 25k req/s',\n        deliverableType: 'github_pr',\n        referenceId: 'PR #307',\n        referenceLabel: 'test/gateway-failover',\n        commitCount: 12,\n        verified: true,\n      },\n    ],\n  },\n  {\n    id: 'ctr-4',\n    name: 'Chloe Dubois',\n    initials: 'CD',\n    role: 'QA Automation Lead',\n    country: 'France',\n    countryCode: 'FR',\n    flag: '🇫🇷',\n    city: 'Paris, FR',\n    contractType: 'Hourly Contract · $92.00/hr',\n    hourlyRateUsd: 92.0,\n    hourlyRateLocal: 85.0,\n    currencyCode: 'EUR',\n    currencySymbol: '€',\n    fxRate: 1.0824,\n    hoursLogged: 40.0,\n    regularHours: 40.0,\n    overtimeHours: 0.0,\n    grossAmountLocal: 3400.0,\n    grossAmountUsd: 3680.0,\n    verificationBadge: {\n      label: 'Playwright E2E Suites 100% Pass',\n      subtext: 'CI Run #8821 verified · 342 browser tests green',\n      status: 'verified',\n    },\n    status: 'approved',\n    invoiceNumber: 'INV-2026-FR-1108',\n    submittedAt: 'Aug 20, 2026 · 16:30 UTC',\n    payPeriod: 'Aug 08 – Aug 21, 2026',\n    taxCompliance: {\n      w8benStatus: 'valid',\n      taxIdMasked: 'FR-SIRET ••••••3921',\n      jurisdiction: 'DGFiP France (EU VAT Reg)',\n    },\n    paymentMethod: {\n      type: 'SEPA',\n      accountMasked: 'BNP Paribas •••• 5590',\n      bankName: 'BNP Paribas S.A.',\n    },\n    dailyEntries: [\n      {\n        id: 'entry-4-1',\n        date: '2026-08-17',\n        day: 'Mon, Aug 17',\n        hours: 8.0,\n        regularHours: 8.0,\n        overtimeHours: 0.0,\n        task: 'Playwright end-to-end multi-currency payment checkout regression suite',\n        deliverableType: 'ci_pipeline',\n        referenceId: 'CI Run #8812',\n        referenceLabel: 'e2e-checkout-matrix',\n        commitCount: 8,\n        verified: true,\n      },\n      {\n        id: 'entry-4-2',\n        date: '2026-08-18',\n        day: 'Tue, Aug 18',\n        hours: 8.0,\n        regularHours: 8.0,\n        overtimeHours: 0.0,\n        task: 'Visual regression tests for dark mode and high-contrast accessibility across viewports',\n        deliverableType: 'ci_pipeline',\n        referenceId: 'CI Run #8815',\n        referenceLabel: 'visual-regression-a11y',\n        commitCount: 6,\n        verified: true,\n      },\n      {\n        id: 'entry-4-3',\n        date: '2026-08-19',\n        day: 'Wed, Aug 19',\n        hours: 8.0,\n        regularHours: 8.0,\n        overtimeHours: 0.0,\n        task: 'Automated timesheet submission stress testing & concurrent approval race conditions',\n        deliverableType: 'ci_pipeline',\n        referenceId: 'CI Run #8818',\n        referenceLabel: 'load-timesheet-concurrency',\n        commitCount: 14,\n        verified: true,\n      },\n      {\n        id: 'entry-4-4',\n        date: '2026-08-20',\n        day: 'Thu, Aug 20',\n        hours: 8.0,\n        regularHours: 8.0,\n        overtimeHours: 0.0,\n        task: 'Cross-browser matrix testing (Chromium, Firefox, WebKit) on macOS/Linux runners',\n        deliverableType: 'ci_pipeline',\n        referenceId: 'CI Run #8820',\n        referenceLabel: 'cross-browser-ci',\n        commitCount: 4,\n        verified: true,\n      },\n      {\n        id: 'entry-4-5',\n        date: '2026-08-21',\n        day: 'Fri, Aug 21',\n        hours: 8.0,\n        regularHours: 8.0,\n        overtimeHours: 0.0,\n        task: 'CI test sharding optimization reducing full regression duration from 18m to 3m 12s',\n        deliverableType: 'ci_pipeline',\n        referenceId: 'CI Run #8821',\n        referenceLabel: 'ci-sharding-speedup',\n        commitCount: 5,\n        verified: true,\n      },\n    ],\n  },\n]\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/contractor-timesheet-approval/timesheet-data.ts"
    },
    {
      "path": "packages/registry-angular/blocks/contractor-timesheet-approval/index.ts",
      "content": "export {\n  UiContractorTimesheetApprovalComponent,\n  type ApprovalStatus,\n  type ContractorSubmission,\n  type CurrencyDisplay,\n  type DailyTimesheetEntry,\n  type DeliverableType,\n  type TimesheetStatusFilter,\n} from './contractor-timesheet-approval.component'\nexport { UiTimesheetDetailSheetComponent } from './timesheet-detail-sheet.component'\nexport { UiTimesheetKpiCardsComponent } from './timesheet-kpi-cards.component'\nexport { UiTimesheetTableRowComponent } from './timesheet-table-row.component'\nexport { initialSubmissions } from './timesheet-data'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/contractor-timesheet-approval/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/dropdown-menu.json",
    "https://uipkge.dev/r/angular/separator.json",
    "https://uipkge.dev/r/angular/sheet.json",
    "https://uipkge.dev/r/angular/table.json"
  ],
  "description": "Deel/Remote style international contractor invoice approval, hourly timesheet verification against GitHub PRs & deliverables, multi-currency payout calculation, overview KPI metrics, and itemized daily timesheet slide-over drawer.",
  "categories": [
    "finance",
    "app",
    "billing",
    "productivity"
  ]
}