{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "time-tracker-timesheet",
  "title": "Time Tracker Timesheet",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/time-tracker-timesheet/time-tracker-timesheet.component.ts",
      "content": "// Toggl/Harvest-style live time tracker: running stopwatch hero bar with project rate\n// select, billable switch, 4 weekly summary cards, and a daily grouped timesheet ledger\n// with resume/delete actions. Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, DestroyRef, Input, computed, inject, signal } from '@angular/core'\nimport {\n  Briefcase,\n  Calendar,\n  CircleDollarSign,\n  Clock,\n  DollarSign,\n  Download,\n  LucideAngularModule,\n  Play,\n  Plus,\n  Square,\n  Trash2,\n  TrendingUp,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport {\n  UiSelectComponent,\n  UiSelectContentComponent,\n  UiSelectItemComponent,\n  UiSelectTriggerComponent,\n  UiSelectValueComponent,\n} from '@/ui/select/select.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\nimport { UiSwitchComponent } from '@/ui/switch/switch.component'\nimport {\n  UiTableBodyComponent,\n  UiTableCellComponent,\n  UiTableComponent,\n  UiTableHeadComponent,\n  UiTableHeaderComponent,\n  UiTableRowComponent,\n} from '@/ui/table/table.component'\n\nexport interface ProjectOption {\n  id: string\n  name: string\n  client: string\n  rate: number\n  rateLabel: string\n  color: string\n  badgeClass: string\n  dotClass: string\n}\n\nexport interface TimeEntry {\n  id: string\n  task: string\n  projectId: string\n  projectName: string\n  clientName: string\n  projectBadgeClass: string\n  date: string\n  dateLabel: string\n  startTime: string\n  endTime: string\n  durationSeconds: number\n  isBillable: boolean\n  rate: number\n  amount: number\n}\n\nconst projects: ProjectOption[] = [\n  {\n    id: 'acme',\n    name: 'Acme Corp',\n    client: 'Acme Enterprises',\n    rate: 150,\n    rateLabel: '$150/hr',\n    color: 'blue',\n    badgeClass: 'bg-info/10 text-info border-info/20',\n    dotClass: 'bg-info',\n  },\n  {\n    id: 'stripe',\n    name: 'Stripe Retainer',\n    client: 'Stripe, Inc.',\n    rate: 175,\n    rateLabel: '$175/hr',\n    color: 'chart-2',\n    badgeClass: 'bg-chart-2/10 text-chart-2 border-chart-2/20',\n    dotClass: 'bg-chart-2',\n  },\n  {\n    id: 'linear',\n    name: 'Linear Sync',\n    client: 'Linear Orbit',\n    rate: 160,\n    rateLabel: '$160/hr',\n    color: 'chart-3',\n    badgeClass: 'bg-chart-3/10 text-chart-3 border-chart-3/20',\n    dotClass: 'bg-chart-3',\n  },\n  {\n    id: 'uipkge',\n    name: 'UIPKGE Core',\n    client: 'Internal OSS',\n    rate: 0,\n    rateLabel: 'Internal · $0/hr',\n    color: 'emerald',\n    badgeClass: 'bg-success/10 text-success border-success/20',\n    dotClass: 'bg-success',\n  },\n]\n\nconst initialEntries: TimeEntry[] = [\n  {\n    id: 'entry-1',\n    task: 'Refactoring OKLCH Color Engine & AST Parser',\n    projectId: 'acme',\n    projectName: 'Acme Corp',\n    clientName: 'Acme Enterprises',\n    projectBadgeClass: 'bg-info/10 text-info border-info/20',\n    date: '2026-08-21',\n    dateLabel: 'Today · Friday, Aug 21',\n    startTime: '09:00',\n    endTime: '11:30',\n    durationSeconds: 9000,\n    isBillable: true,\n    rate: 150,\n    amount: 375,\n  },\n  {\n    id: 'entry-2',\n    task: 'Webhook idempotency retry queue architecture',\n    projectId: 'stripe',\n    projectName: 'Stripe Retainer',\n    clientName: 'Stripe, Inc.',\n    projectBadgeClass: 'bg-chart-2/10 text-chart-2 border-chart-2/20',\n    date: '2026-08-21',\n    dateLabel: 'Today · Friday, Aug 21',\n    startTime: '13:00',\n    endTime: '15:45',\n    durationSeconds: 9900,\n    isBillable: true,\n    rate: 175,\n    amount: 481.25,\n  },\n  {\n    id: 'entry-3',\n    task: 'Weekly sprint retrospective & design token review',\n    projectId: 'uipkge',\n    projectName: 'UIPKGE Core',\n    clientName: 'Internal OSS',\n    projectBadgeClass: 'bg-success/10 text-success border-success/20',\n    date: '2026-08-21',\n    dateLabel: 'Today · Friday, Aug 21',\n    startTime: '16:00',\n    endTime: '17:30',\n    durationSeconds: 5400,\n    isBillable: false,\n    rate: 0,\n    amount: 0,\n  },\n  {\n    id: 'entry-4',\n    task: 'Bidirectional GraphQL sync engine & schema migrations',\n    projectId: 'linear',\n    projectName: 'Linear Sync',\n    clientName: 'Linear Orbit',\n    projectBadgeClass: 'bg-chart-3/10 text-chart-3 border-chart-3/20',\n    date: '2026-08-20',\n    dateLabel: 'Yesterday · Thursday, Aug 20',\n    startTime: '09:30',\n    endTime: '14:00',\n    durationSeconds: 16200,\n    isBillable: true,\n    rate: 160,\n    amount: 720,\n  },\n  {\n    id: 'entry-5',\n    task: 'Design system Figma token export automation',\n    projectId: 'acme',\n    projectName: 'Acme Corp',\n    clientName: 'Acme Enterprises',\n    projectBadgeClass: 'bg-info/10 text-info border-info/20',\n    date: '2026-08-20',\n    dateLabel: 'Yesterday · Thursday, Aug 20',\n    startTime: '14:45',\n    endTime: '17:30',\n    durationSeconds: 9900,\n    isBillable: true,\n    rate: 150,\n    amount: 412.5,\n  },\n]\n\nexport function formatDuration(totalSeconds: number): string {\n  const hours = Math.floor(totalSeconds / 3600)\n  const minutes = Math.floor((totalSeconds % 3600) / 60)\n  const seconds = totalSeconds % 60\n  return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`\n}\n\nexport function formatCurrency(val: number): string {\n  return new Intl.NumberFormat('en-US', {\n    style: 'currency',\n    currency: 'USD',\n    minimumFractionDigits: 2,\n  }).format(val)\n}\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-time-tracker-timesheet, [ui-time-tracker-timesheet]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiInputComponent,\n    UiSelectComponent,\n    UiSelectContentComponent,\n    UiSelectItemComponent,\n    UiSelectTriggerComponent,\n    UiSelectValueComponent,\n    UiSeparatorComponent,\n    UiSwitchComponent,\n    UiTableComponent,\n    UiTableBodyComponent,\n    UiTableCellComponent,\n    UiTableHeadComponent,\n    UiTableHeaderComponent,\n    UiTableRowComponent,\n  ],\n  template: `\n    <div data-slot=\"time-tracker-timesheet\" [class]=\"rootClass\">\n      <!-- Header Section -->\n      <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"space-y-1\">\n          <div class=\"flex items-center gap-2.5\">\n            <h2 class=\"text-foreground text-2xl font-bold tracking-tight sm:text-3xl\">{{ title() }}</h2>\n            <span ui-badge variant=\"outline\" class=\"border-border hidden text-xs font-medium sm:inline-flex\">\n              Toggl Engine\n            </span>\n          </div>\n          <p class=\"text-muted-foreground text-sm\">{{ subtitle() }}</p>\n        </div>\n\n        <div class=\"flex items-center gap-2.5\">\n          <button ui-button aria-label=\"Download attachment\" variant=\"outline\" size=\"sm\" class=\"h-9 gap-1.5 shadow-xs\">\n            <lucide-icon [img]=\"Download\" class=\"size-4\" />\n            Export CSV\n          </button>\n          <button ui-button size=\"sm\" class=\"h-9 gap-1.5 shadow-xs\">\n            <lucide-icon [img]=\"Plus\" class=\"size-4\" />\n            Manual Entry\n          </button>\n        </div>\n      </div>\n\n      <!-- Live Running Stopwatch Hero Bar -->\n      <div ui-card class=\"border-border bg-card overflow-hidden shadow-xs\">\n        <div ui-card-content class=\"p-4 sm:p-5\">\n          <div class=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between\">\n            <!-- Task description & Project Tag Select -->\n            <div class=\"flex flex-1 flex-col gap-3 sm:flex-row sm:items-center\">\n              <div class=\"relative flex-1\">\n                <ui-input\n                  [value]=\"activeTask()\"\n                  (valueChange)=\"activeTask.set($event)\"\n                  type=\"text\"\n                  placeholder=\"What are you working on?\"\n                  class=\"h-10 text-sm font-medium focus-visible:ring-2\"\n                />\n              </div>\n\n              <div class=\"w-full shrink-0 sm:w-64\">\n                <ui-select [value]=\"selectedProjectId()\" (valueChange)=\"selectedProjectId.set($event)\">\n                  <button ui-select-trigger class=\"h-10 text-xs sm:text-sm\">\n                    <ui-select-value placeholder=\"Select client/project\" />\n                  </button>\n                  <ui-select-content>\n                    @for (proj of projectOptions; track proj.id) {\n                      <ui-select-item [value]=\"proj.id\">\n                        <div class=\"flex items-center gap-2\">\n                          <span [class]=\"cn('size-2 shrink-0 rounded-full', proj.dotClass)\"></span>\n                          <span class=\"text-foreground font-medium\">{{ proj.name }}</span>\n                          <span class=\"text-muted-foreground text-xs\">· {{ proj.rateLabel }}</span>\n                        </div>\n                      </ui-select-item>\n                    }\n                  </ui-select-content>\n                </ui-select>\n              </div>\n            </div>\n\n            <!-- Controls: Billable Switch, Live Stopwatch, Primary Action Button -->\n            <div\n              class=\"border-border/60 flex flex-wrap items-center justify-between gap-3 border-t pt-3 sm:border-t-0 sm:pt-0 lg:justify-end\"\n            >\n              <div class=\"flex items-center gap-2\">\n                <button\n                  ui-switch\n                  id=\"billable-toggle-angular\"\n                  size=\"sm\"\n                  [checked]=\"isBillable()\"\n                  (checkedChange)=\"isBillable.set($event)\"\n                  aria-label=\"Billable toggle\"\n                ></button>\n                <label\n                  for=\"billable-toggle-angular\"\n                  class=\"flex cursor-pointer items-center gap-1.5 text-xs font-medium select-none\"\n                >\n                  <lucide-icon\n                    [img]=\"DollarSign\"\n                    [class]=\"cn('size-3.5', isBillable() ? 'text-success font-semibold' : 'text-muted-foreground')\"\n                  />\n                  <span [class]=\"isBillable() ? 'text-foreground font-medium' : 'text-muted-foreground'\">Billable</span>\n                </label>\n              </div>\n\n              <ui-separator orientation=\"vertical\" class=\"hidden h-7 sm:block\" />\n\n              <!-- Live Running Stopwatch Timer -->\n              <div class=\"flex items-center gap-2.5\">\n                <div\n                  class=\"border-border/80 bg-muted/50 dark:bg-muted/30 flex items-center gap-2.5 rounded-lg border px-3.5 py-1.5 shadow-xs\"\n                >\n                  <div class=\"relative flex size-2.5 items-center justify-center\">\n                    @if (isRunning()) {\n                      <span class=\"bg-success absolute inline-flex h-full w-full rounded-full opacity-75\"></span>\n                    }\n                    <span\n                      [class]=\"\n                        cn(\n                          'relative inline-flex size-2.5 rounded-full',\n                          isRunning() ? 'bg-success' : 'bg-muted-foreground/40'\n                        )\n                      \"\n                    ></span>\n                  </div>\n                  <span class=\"text-foreground font-mono text-xl font-bold tracking-tight tabular-nums sm:text-2xl\">\n                    {{ formattedTimer() }}\n                  </span>\n                </div>\n\n                <button\n                  ui-button\n                  [variant]=\"isRunning() ? 'destructive' : 'default'\"\n                  size=\"default\"\n                  class=\"h-10 gap-2 px-4 font-medium shadow-xs\"\n                  (click)=\"toggleTimer()\"\n                >\n                  @if (isRunning()) {\n                    <lucide-icon [img]=\"Square\" class=\"size-4 fill-current\" />\n                  } @else {\n                    <lucide-icon [img]=\"Play\" class=\"size-4 fill-current\" />\n                  }\n                  <span>{{ isRunning() ? 'Stop' : 'Start' }}</span>\n                </button>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n\n      <!-- 4 Timesheet Summary Cards -->\n      <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n        <div ui-card class=\"border-border shadow-xs\">\n          <div ui-card-content class=\"space-y-2 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">Total Hours This Week</p>\n              <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-lg\">\n                <lucide-icon [img]=\"Clock\" class=\"size-4\" />\n              </div>\n            </div>\n            <div class=\"flex items-baseline gap-2\">\n              <p class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">38.5 hrs</p>\n              <span class=\"text-muted-foreground text-xs tabular-nums\">/ 40.0 target</span>\n            </div>\n            <div class=\"text-muted-foreground flex items-center gap-1.5 text-xs\">\n              <span class=\"text-success inline-flex items-center font-medium\">\n                <lucide-icon [img]=\"TrendingUp\" class=\"mr-0.5 size-3.5\" />\n                96%\n              </span>\n              <span>of weekly target logged</span>\n            </div>\n          </div>\n        </div>\n\n        <div ui-card class=\"border-border shadow-xs\">\n          <div ui-card-content class=\"space-y-2 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">Billable Hours</p>\n              <div class=\"bg-success/10 text-success flex size-8 items-center justify-center rounded-lg\">\n                <lucide-icon [img]=\"CircleDollarSign\" class=\"size-4\" />\n              </div>\n            </div>\n            <p class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">32.0 hrs</p>\n            <div class=\"text-muted-foreground flex items-center justify-between text-xs\">\n              <span class=\"text-success font-medium\">83% billable ratio</span>\n              <span>6.5 hrs internal</span>\n            </div>\n          </div>\n        </div>\n\n        <div ui-card class=\"border-border shadow-xs\">\n          <div ui-card-content class=\"space-y-2 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">Total Earnings</p>\n              <div class=\"bg-chart-2/10 text-chart-2 flex size-8 items-center justify-center rounded-lg\">\n                <lucide-icon [img]=\"DollarSign\" class=\"size-4\" />\n              </div>\n            </div>\n            <p class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">$4,800.00</p>\n            <p class=\"text-muted-foreground text-xs\">Avg $150.00/hr effective rate</p>\n          </div>\n        </div>\n\n        <div ui-card class=\"border-border shadow-xs\">\n          <div ui-card-content class=\"space-y-2 p-5\">\n            <div class=\"flex items-center justify-between\">\n              <p class=\"text-muted-foreground text-sm font-medium\">Active Clients</p>\n              <div class=\"bg-info/10 text-info flex size-8 items-center justify-center rounded-lg\">\n                <lucide-icon [img]=\"Briefcase\" class=\"size-4\" />\n              </div>\n            </div>\n            <p class=\"text-foreground text-2xl font-bold tracking-tight tabular-nums\">3 active clients</p>\n            <p class=\"text-muted-foreground truncate text-xs\">Acme Corp · Stripe · Linear</p>\n          </div>\n        </div>\n      </div>\n\n      <!-- Weekly Timesheet & Daily Activity Table -->\n      <div ui-card class=\"border-border bg-card shadow-xs\">\n        <div ui-card-header class=\"border-border/60 border-b pb-3\">\n          <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n            <div>\n              <h3 ui-card-title class=\"text-base font-semibold\">Weekly Timesheet & Daily Activity</h3>\n              <p ui-card-description class=\"text-xs\">\n                Detailed time entries for the current billing cycle (Aug 17 – Aug 23, 2026).\n              </p>\n            </div>\n            <div class=\"flex items-center gap-2\">\n              <span ui-badge variant=\"outline\" class=\"border-border font-mono text-xs font-medium\">\n                Week 34 · 38h 30m · $4,800.00\n              </span>\n            </div>\n          </div>\n        </div>\n\n        <div ui-card-content class=\"p-0\">\n          <div class=\"overflow-x-auto\">\n            <table ui-table>\n              <thead ui-table-header class=\"bg-muted/40\">\n                <tr ui-table-row>\n                  <th ui-table-head class=\"min-w-[160px]\">Project & Client</th>\n                  <th ui-table-head class=\"min-w-[280px]\">Task Description</th>\n                  <th ui-table-head class=\"min-w-[130px]\">Billable</th>\n                  <th ui-table-head class=\"min-w-[130px]\">Time Range</th>\n                  <th ui-table-head class=\"min-w-[110px] text-right\">Duration</th>\n                  <th ui-table-head class=\"min-w-[110px] text-right\">Amount</th>\n                  <th ui-table-head class=\"w-24 text-right\">\n                    <span class=\"sr-only\">Actions</span>\n                  </th>\n                </tr>\n              </thead>\n              <tbody ui-table-body>\n                @for (group of groupedEntries(); track group.date) {\n                  <tr ui-table-row class=\"border-border bg-muted/20 hover:bg-muted/30 border-y font-medium\">\n                    <td ui-table-cell colspan=\"4\" class=\"text-foreground py-2.5 text-xs font-semibold\">\n                      <div class=\"flex items-center gap-2\">\n                        <lucide-icon [img]=\"Calendar\" class=\"text-muted-foreground size-3.5\" />\n                        <span>{{ group.label }}</span>\n                      </div>\n                    </td>\n                    <td\n                      ui-table-cell\n                      class=\"text-foreground py-2.5 text-right font-mono text-xs font-semibold tabular-nums\"\n                    >\n                      {{ formatDuration(group.totalSeconds) }}\n                    </td>\n                    <td\n                      ui-table-cell\n                      class=\"text-foreground py-2.5 text-right font-mono text-xs font-semibold tabular-nums\"\n                    >\n                      {{ formatCurrency(group.totalAmount) }}\n                    </td>\n                    <td ui-table-cell class=\"py-2.5\"></td>\n                  </tr>\n\n                  @for (entry of group.entries; track entry.id) {\n                    <tr ui-table-row class=\"hover:bg-muted/50 transition-colors\">\n                      <td ui-table-cell>\n                        <div class=\"flex flex-col gap-1\">\n                          <span\n                            ui-badge\n                            variant=\"outline\"\n                            [class]=\"cn('w-fit text-xs font-normal', entry.projectBadgeClass)\"\n                          >\n                            {{ entry.projectName }}\n                          </span>\n                          <span class=\"text-muted-foreground text-xs\">{{ entry.clientName }}</span>\n                        </div>\n                      </td>\n                      <td ui-table-cell>\n                        <p class=\"text-foreground text-sm font-medium\">{{ entry.task }}</p>\n                      </td>\n                      <td ui-table-cell>\n                        <div class=\"flex items-center gap-1.5\">\n                          @if (entry.isBillable) {\n                            <span\n                              ui-badge\n                              variant=\"outline\"\n                              class=\"border-success/30 bg-success/10 text-success text-xs font-medium\"\n                            >\n                              <lucide-icon [img]=\"DollarSign\" class=\"mr-0.5 size-3\" />\n                              Billable ({{ '$' + entry.rate }}/h)\n                            </span>\n                          } @else {\n                            <span\n                              ui-badge\n                              variant=\"outline\"\n                              class=\"border-border bg-muted/60 text-muted-foreground text-xs font-normal\"\n                            >\n                              Non-billable\n                            </span>\n                          }\n                        </div>\n                      </td>\n                      <td ui-table-cell class=\"text-muted-foreground font-mono text-xs whitespace-nowrap tabular-nums\">\n                        {{ entry.startTime }} – {{ entry.endTime }}\n                      </td>\n                      <td\n                        ui-table-cell\n                        class=\"text-foreground text-right font-mono text-sm font-medium whitespace-nowrap tabular-nums\"\n                      >\n                        {{ formatDuration(entry.durationSeconds) }}\n                      </td>\n                      <td\n                        ui-table-cell\n                        class=\"text-right font-mono text-sm font-semibold whitespace-nowrap tabular-nums\"\n                      >\n                        <span [class]=\"entry.isBillable ? 'text-foreground' : 'text-muted-foreground'\">\n                          {{ formatCurrency(entry.amount) }}\n                        </span>\n                      </td>\n                      <td ui-table-cell class=\"text-right\">\n                        <div class=\"flex items-center justify-end gap-1\">\n                          <button\n                            ui-button\n                            variant=\"ghost\"\n                            size=\"icon-sm\"\n                            class=\"text-muted-foreground hover:text-foreground size-8\"\n                            title=\"Resume Timer\"\n                            [attr.aria-label]=\"'Resume timer for ' + entry.task\"\n                            (click)=\"resumeEntry(entry)\"\n                          >\n                            <lucide-icon [img]=\"Play\" class=\"size-3.5 fill-current\" />\n                          </button>\n                          <button\n                            ui-button\n                            variant=\"ghost\"\n                            size=\"icon-sm\"\n                            class=\"text-muted-foreground hover:text-destructive size-8\"\n                            title=\"Delete Entry\"\n                            aria-label=\"Delete entry\"\n                            (click)=\"deleteEntry(entry.id)\"\n                          >\n                            <lucide-icon [img]=\"Trash2\" class=\"size-3.5\" />\n                          </button>\n                        </div>\n                      </td>\n                    </tr>\n                  }\n                }\n\n                @if (entries().length === 0) {\n                  <tr ui-table-row>\n                    <td ui-table-cell [attr.colspan]=\"7\" class=\"h-32 text-center\">\n                      <div class=\"text-muted-foreground flex flex-col items-center justify-center gap-1.5\">\n                        <lucide-icon [img]=\"Clock\" class=\"text-muted-foreground/60 size-6\" />\n                        <p class=\"text-foreground text-sm font-medium\">No time entries recorded for this week</p>\n                        <p class=\"text-xs\">Start the live timer above to log your hours.</p>\n                      </div>\n                    </td>\n                  </tr>\n                }\n              </tbody>\n            </table>\n          </div>\n\n          <!-- Timesheet Table Footer Strip -->\n          <div\n            class=\"border-border bg-card text-muted-foreground flex flex-col items-center justify-between gap-3 border-t px-4 py-3 text-xs sm:flex-row\"\n          >\n            <p class=\"tabular-nums\">\n              Showing <span class=\"text-foreground font-medium\">{{ entries().length }}</span> time entries across\n              <span class=\"text-foreground font-medium\">{{ groupedEntries().length }}</span> days\n            </p>\n            <div class=\"flex items-center gap-3\">\n              <span class=\"text-foreground font-mono text-xs font-medium tabular-nums\">\n                Total: {{ formatDuration(totalWeeklySeconds()) }} ({{ formatCurrency(totalWeeklyAmount()) }})\n              </span>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiTimeTrackerTimesheetComponent {\n  protected readonly Briefcase = Briefcase\n  protected readonly Calendar = Calendar\n  protected readonly CircleDollarSign = CircleDollarSign\n  protected readonly Clock = Clock\n  protected readonly DollarSign = DollarSign\n  protected readonly Download = Download\n  protected readonly Play = Play\n  protected readonly Plus = Plus\n  protected readonly Square = Square\n  protected readonly Trash2 = Trash2\n  protected readonly TrendingUp = TrendingUp\n\n  protected readonly projectOptions = projects\n  protected readonly cn = cn\n\n  readonly title = signal('Time Tracker & Timesheet')\n  readonly subtitle = signal('Track live project hours, calculate client billing rates, and review weekly activity.')\n\n  @Input('title') set heading(v: string | undefined) {\n    if (v !== undefined) this.title.set(v)\n  }\n  @Input('subtitle') set subheading(v: string | undefined) {\n    if (v !== undefined) this.subtitle.set(v)\n  }\n  @Input() set initialEntries(v: TimeEntry[] | undefined) {\n    if (v !== undefined) this.entries.set([...v])\n  }\n  @Input() set initialRunning(v: boolean) {\n    this.isRunning.set(!!v)\n  }\n  @Input('class') set classNameInput(v: string | undefined) {\n    this.className.set(v ?? '')\n  }\n\n  readonly className = signal('')\n\n  readonly entries = signal<TimeEntry[]>([...initialEntries])\n  readonly activeTask = signal('Refactoring OKLCH Color Engine & AST Parser')\n  readonly selectedProjectId = signal('acme')\n  readonly isBillable = signal(true)\n  readonly isRunning = signal(true)\n  readonly timerSeconds = signal(6138) // 01:42:18\n\n  constructor() {\n    const timer = setInterval(() => {\n      if (this.isRunning()) this.timerSeconds.update((v) => v + 1)\n    }, 1000)\n    // Keep the suite hermetic under fake timers: unref when available (node/jsdom).\n    if (typeof (timer as unknown as { unref?: () => void }).unref === 'function') {\n      ;(timer as unknown as { unref: () => void }).unref()\n    }\n    inject(DestroyRef).onDestroy(() => clearInterval(timer))\n  }\n\n  readonly formattedTimer = computed(() => formatDuration(this.timerSeconds()))\n\n  readonly groupedEntries = computed(() => {\n    const groups: Record<\n      string,\n      { label: string; date: string; entries: TimeEntry[]; totalSeconds: number; totalAmount: number }\n    > = {}\n    for (const entry of this.entries()) {\n      if (!groups[entry.date]) {\n        groups[entry.date] = { label: entry.dateLabel, date: entry.date, entries: [], totalSeconds: 0, totalAmount: 0 }\n      }\n      groups[entry.date].entries.push(entry)\n      groups[entry.date].totalSeconds += entry.durationSeconds\n      groups[entry.date].totalAmount += entry.amount\n    }\n    return Object.values(groups)\n  })\n\n  readonly totalWeeklySeconds = computed(() => this.entries().reduce((acc, e) => acc + e.durationSeconds, 0))\n  readonly totalWeeklyAmount = computed(() => this.entries().reduce((acc, e) => acc + e.amount, 0))\n\n  get rootClass(): string {\n    return cn('w-full space-y-6', this.className())\n  }\n\n  formatDuration(totalSeconds: number): string {\n    return formatDuration(totalSeconds)\n  }\n\n  formatCurrency(val: number): string {\n    return formatCurrency(val)\n  }\n\n  toggleTimer(): void {\n    this.isRunning.update((v) => !v)\n  }\n\n  resumeEntry(entry: TimeEntry): void {\n    this.activeTask.set(entry.task)\n    this.selectedProjectId.set(entry.projectId)\n    this.isBillable.set(entry.isBillable)\n    this.isRunning.set(true)\n  }\n\n  deleteEntry(id: string): void {\n    this.entries.update((prev) => prev.filter((e) => e.id !== id))\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/time-tracker-timesheet/time-tracker-timesheet.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/time-tracker-timesheet/index.ts",
      "content": "export {\n  UiTimeTrackerTimesheetComponent,\n  formatCurrency,\n  formatDuration,\n  type ProjectOption,\n  type TimeEntry,\n} from './time-tracker-timesheet.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/time-tracker-timesheet/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/select.json",
    "https://uipkge.dev/r/angular/separator.json",
    "https://uipkge.dev/r/angular/switch.json",
    "https://uipkge.dev/r/angular/table.json"
  ],
  "description": "Toggl/Harvest-style live time tracker with active running stopwatch, project rate selection, billable status toggle, 4 weekly summary metric cards, and daily grouped timesheet activity ledger with resume actions.",
  "categories": [
    "productivity",
    "app",
    "billing"
  ]
}