{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "project-roadmap",
  "title": "Project Roadmap",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/project-roadmap/project-roadmap.component.ts",
      "content": "// Full interactive project roadmap and deliverables surface: Gantt timeline, work\n// breakdown task tree, overall progress metrics, search + status/assignee filters,\n// task detail slide-over sheet, and an add-task modal dialog. Port of the Vue block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport { Download, LucideAngularModule, Plus, Search } from 'lucide-angular'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiGanttComponent,\n  UiGanttHeaderComponent,\n  UiGanttTimelineComponent,\n  UiGanttTreeComponent,\n} from '@/ui/gantt/gantt.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 type { GanttScale, GanttTask, GanttTaskPriority, GanttTaskStatus } from '@/ui/gantt/types'\nimport { UiRoadmapAddTaskDialogComponent } from './roadmap-add-task-dialog.component'\nimport { UiRoadmapMetricsComponent } from './roadmap-metrics.component'\nimport { UiRoadmapTaskDetailComponent } from './roadmap-task-detail.component'\n\nconst defaultTasks: GanttTask[] = [\n  {\n    id: 'task-1',\n    name: 'System Architecture & Database Schema',\n    startDate: '2026-08-01',\n    endDate: '2026-08-08',\n    progress: 100,\n    status: 'done',\n    priority: 'high',\n    assignee: { name: 'Sarah Connor', initials: 'SC' },\n  },\n  {\n    id: 'task-2',\n    name: 'Auth, RBAC & Multi-Tenant Session Engine',\n    startDate: '2026-08-06',\n    endDate: '2026-08-16',\n    progress: 85,\n    status: 'in-progress',\n    priority: 'urgent',\n    assignee: { name: 'Marcus Rivera', initials: 'MR' },\n  },\n  {\n    id: 'task-3',\n    name: 'Real-time WebSocket Notification Bus',\n    startDate: '2026-08-12',\n    endDate: '2026-08-22',\n    progress: 50,\n    status: 'in-progress',\n    priority: 'high',\n    assignee: { name: 'Priya Nair', initials: 'PN' },\n  },\n  {\n    id: 'task-4',\n    name: 'Alpha Core API Release Milestone',\n    startDate: '2026-08-23',\n    endDate: '2026-08-23',\n    isMilestone: true,\n    status: 'todo',\n    priority: 'urgent',\n  },\n  {\n    id: 'task-5',\n    name: 'Stripe Billing & Invoicing Integration',\n    startDate: '2026-08-24',\n    endDate: '2026-09-04',\n    progress: 15,\n    status: 'todo',\n    priority: 'medium',\n    assignee: { name: 'Sundar Krishnan', initials: 'SK' },\n  },\n  {\n    id: 'task-6',\n    name: 'End-to-End Playwright Automated Testing',\n    startDate: '2026-08-28',\n    endDate: '2026-09-08',\n    progress: 0,\n    status: 'todo',\n    priority: 'low',\n    assignee: { name: 'Diane Cho', initials: 'DC' },\n  },\n  {\n    id: 'task-7',\n    name: 'Production Cloudflare Pages Deployment',\n    startDate: '2026-09-10',\n    endDate: '2026-09-10',\n    isMilestone: true,\n    status: 'todo',\n  },\n]\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-project-roadmap, [ui-project-roadmap]',\n  standalone: true,\n  // Vue 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    UiButtonComponent,\n    UiGanttComponent,\n    UiGanttHeaderComponent,\n    UiGanttTimelineComponent,\n    UiGanttTreeComponent,\n    UiInputComponent,\n    UiRoadmapAddTaskDialogComponent,\n    UiRoadmapMetricsComponent,\n    UiRoadmapTaskDetailComponent,\n    UiSelectComponent,\n    UiSelectContentComponent,\n    UiSelectItemComponent,\n    UiSelectTriggerComponent,\n    UiSelectValueComponent,\n  ],\n  template: `\n    <div data-uipkge data-slot=\"project-roadmap\" [class]=\"rootClass\">\n      <!-- Section Top Header -->\n      <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div>\n          <h2 class=\"text-foreground text-xl font-bold tracking-tight sm:text-2xl\">{{ title }}</h2>\n          <p class=\"text-muted-foreground mt-0.5 text-sm\">{{ description }}</p>\n        </div>\n\n        <div class=\"flex items-center gap-2\">\n          <button ui-button aria-label=\"Download attachment\" variant=\"outline\" size=\"sm\">\n            <lucide-icon [img]=\"Download\" class=\"mr-1 size-3.5\" />\n            Export\n          </button>\n          <button ui-button size=\"sm\" (click)=\"addDialogOpen.set(true)\">\n            <lucide-icon [img]=\"Plus\" class=\"mr-1 size-3.5\" />\n            Add Task\n          </button>\n        </div>\n      </div>\n\n      <!-- Summary Metrics -->\n      <ui-roadmap-metrics [tasks]=\"tasks()\" />\n\n      <!-- Filters & Action Bar -->\n      <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"flex max-w-lg flex-1 flex-wrap items-center gap-2\">\n          <div class=\"relative w-56\">\n            <lucide-icon\n              [img]=\"Search\"\n              class=\"text-muted-foreground pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2\"\n            />\n            <ui-input\n              [value]=\"searchQuery()\"\n              (valueChange)=\"searchQuery.set($event)\"\n              placeholder=\"Filter tasks by name...\"\n              class=\"h-8 pl-8 text-xs\"\n            />\n          </div>\n\n          <ui-select [value]=\"statusFilter()\" (valueChange)=\"statusFilter.set($event)\">\n            <button ui-select-trigger class=\"h-8 w-32 text-xs\">\n              <ui-select-value placeholder=\"All Status\" />\n            </button>\n            <ui-select-content>\n              <ui-select-item value=\"all\">All Status</ui-select-item>\n              <ui-select-item value=\"done\">Completed</ui-select-item>\n              <ui-select-item value=\"in-progress\">In Progress</ui-select-item>\n              <ui-select-item value=\"todo\">To Do</ui-select-item>\n            </ui-select-content>\n          </ui-select>\n\n          <ui-select [value]=\"assigneeFilter()\" (valueChange)=\"assigneeFilter.set($event)\">\n            <button ui-select-trigger class=\"h-8 w-36 text-xs\">\n              <ui-select-value placeholder=\"All Assignees\" />\n            </button>\n            <ui-select-content>\n              <ui-select-item value=\"all\">All Assignees</ui-select-item>\n              <ui-select-item value=\"Sarah Connor\">Sarah Connor</ui-select-item>\n              <ui-select-item value=\"Marcus Rivera\">Marcus Rivera</ui-select-item>\n              <ui-select-item value=\"Priya Nair\">Priya Nair</ui-select-item>\n              <ui-select-item value=\"Sundar Krishnan\">Sundar Krishnan</ui-select-item>\n              <ui-select-item value=\"Diane Cho\">Diane Cho</ui-select-item>\n            </ui-select-content>\n          </ui-select>\n        </div>\n\n        <p class=\"text-muted-foreground font-mono text-xs\">\n          Showing <span class=\"text-foreground font-bold\">{{ filteredTasks().length }}</span> of\n          {{ tasks().length }} deliverables\n          <span class=\"text-muted-foreground/60 ml-2\">(Right-click task for quick actions)</span>\n        </p>\n      </div>\n\n      <!-- Main Interactive Gantt Workspace with Right-Click Context Actions -->\n      <ui-gantt [tasks]=\"filteredTasks()\" [scale]=\"scale()\" (scaleChange)=\"scale.set($event)\" class=\"h-[480px]\">\n        <ui-gantt-header [title]=\"title\" />\n        <div class=\"flex min-h-0 flex-1 overflow-hidden\">\n          <ui-gantt-tree (taskClick)=\"handleTaskClick($event)\" />\n          <ui-gantt-timeline (taskClick)=\"handleTaskClick($event)\" />\n        </div>\n      </ui-gantt>\n\n      <!-- Task Detail Drawer -->\n      <ui-roadmap-task-detail [task]=\"selectedTask()\" [open]=\"sheetOpen()\" (openChange)=\"sheetOpen.set($event)\" />\n\n      <!-- Add Task Modal Dialog -->\n      <ui-roadmap-add-task-dialog\n        [open]=\"addDialogOpen()\"\n        (openChange)=\"addDialogOpen.set($event)\"\n        (addTask)=\"handleAddTask($event)\"\n      />\n    </div>\n  `,\n})\nexport class UiProjectRoadmapComponent {\n  protected readonly Download = Download\n  protected readonly Plus = Plus\n  protected readonly Search = Search\n\n  @Input() title = 'Engineering Q3 Deliverables'\n  @Input() description = 'Track roadmap work breakdown, sprint milestones, and cross-team dependencies.'\n  @Input() set initialTasks(v: GanttTask[] | undefined) {\n    this.tasks.set((v ?? defaultTasks).map((t) => ({ ...t })))\n  }\n  @Input('class') className?: string\n\n  readonly tasks = signal<GanttTask[]>(defaultTasks.map((t) => ({ ...t })))\n  readonly searchQuery = signal('')\n  readonly statusFilter = signal('all')\n  readonly assigneeFilter = signal('all')\n  readonly scale = signal<GanttScale>('day')\n\n  readonly selectedTask = signal<GanttTask | null>(null)\n  readonly sheetOpen = signal(false)\n  readonly addDialogOpen = signal(false)\n\n  readonly filteredTasks = computed(() => {\n    const q = this.searchQuery().toLowerCase()\n    const status = this.statusFilter()\n    const assignee = this.assigneeFilter()\n    return this.tasks().filter((t) => {\n      const matchesSearch = !q || t.name.toLowerCase().includes(q)\n      const matchesStatus = status === 'all' || t.status === status\n      const matchesAssignee = assignee === 'all' || t.assignee?.name === assignee\n      return matchesSearch && matchesStatus && matchesAssignee\n    })\n  })\n\n  get rootClass(): string {\n    return ['space-y-6', this.className].filter(Boolean).join(' ')\n  }\n\n  handleTaskClick(task: GanttTask): void {\n    this.selectedTask.set(task)\n    this.sheetOpen.set(true)\n  }\n\n  handleAddTask(newTask: GanttTask): void {\n    this.tasks.update((prev) => [...prev, newTask])\n  }\n\n  handleStatusChange(task: GanttTask, status: GanttTaskStatus): void {\n    this.tasks.update((prev) =>\n      prev.map((t) => (t.id === task.id ? { ...t, status, progress: status === 'done' ? 100 : t.progress } : t)),\n    )\n  }\n\n  handlePriorityChange(task: GanttTask, priority: GanttTaskPriority): void {\n    this.tasks.update((prev) => prev.map((t) => (t.id === task.id ? { ...t, priority } : t)))\n  }\n\n  handleDuplicate(task: GanttTask): void {\n    const cloned: GanttTask = { ...task, id: `task-${Date.now()}`, name: `${task.name} (Copy)` }\n    this.tasks.update((prev) => [...prev, cloned])\n  }\n\n  handleDelete(task: GanttTask): void {\n    this.tasks.update((prev) => prev.filter((t) => t.id !== task.id))\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/project-roadmap/project-roadmap.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/project-roadmap/roadmap-metrics.component.ts",
      "content": "// Summary metric cards for the project roadmap: overall progress, in-progress count,\n// completed count, and milestone count. Port of the Vue RoadmapMetrics block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport { AlertCircle, CheckCircle2, Clock, LucideAngularModule, Milestone } from 'lucide-angular'\nimport { UiCardComponent, UiCardContentComponent } from '@/ui/card/card.component'\nimport type { GanttTask } from '@/ui/gantt/types'\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-roadmap-metrics, [ui-roadmap-metrics]',\n  standalone: true,\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [LucideAngularModule, UiCardComponent, UiCardContentComponent],\n  template: `\n    <div data-slot=\"roadmap-metrics\" class=\"grid grid-cols-2 gap-3 sm:grid-cols-4\">\n      <div ui-card class=\"bg-card/50 border-border/80 shadow-none\">\n        <div ui-card-content class=\"flex items-center justify-between p-3.5\">\n          <div>\n            <p class=\"text-muted-foreground text-xs font-medium\">Overall Progress</p>\n            <p class=\"text-foreground mt-0.5 font-mono text-xl font-bold\">{{ progress() }}%</p>\n          </div>\n          <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-full\">\n            <lucide-icon [img]=\"Clock\" class=\"size-4\" />\n          </div>\n        </div>\n      </div>\n\n      <div ui-card class=\"bg-card/50 border-border/80 shadow-none\">\n        <div ui-card-content class=\"flex items-center justify-between p-3.5\">\n          <div>\n            <p class=\"text-muted-foreground text-xs font-medium\">In Progress</p>\n            <p class=\"text-foreground mt-0.5 font-mono text-xl font-bold\">{{ inProgress() }}</p>\n          </div>\n          <div class=\"bg-info/10 text-info flex size-8 items-center justify-center rounded-full\">\n            <lucide-icon [img]=\"AlertCircle\" class=\"size-4\" />\n          </div>\n        </div>\n      </div>\n\n      <div ui-card class=\"bg-card/50 border-border/80 shadow-none\">\n        <div ui-card-content class=\"flex items-center justify-between p-3.5\">\n          <div>\n            <p class=\"text-muted-foreground text-xs font-medium\">Completed</p>\n            <p class=\"text-foreground mt-0.5 font-mono text-xl font-bold\">{{ completed() }}</p>\n          </div>\n          <div class=\"bg-success/10 text-success flex size-8 items-center justify-center rounded-full\">\n            <lucide-icon [img]=\"CheckCircle2\" class=\"size-4\" />\n          </div>\n        </div>\n      </div>\n\n      <div ui-card class=\"bg-card/50 border-border/80 shadow-none\">\n        <div ui-card-content class=\"flex items-center justify-between p-3.5\">\n          <div>\n            <p class=\"text-muted-foreground text-xs font-medium\">Milestones</p>\n            <p class=\"text-foreground mt-0.5 font-mono text-xl font-bold\">{{ milestones() }}</p>\n          </div>\n          <div class=\"bg-warning/10 text-warning flex size-8 items-center justify-center rounded-full\">\n            <lucide-icon [img]=\"Milestone\" class=\"size-4\" />\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiRoadmapMetricsComponent {\n  protected readonly AlertCircle = AlertCircle\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Clock = Clock\n  protected readonly Milestone = Milestone\n\n  @Input() set tasks(v: GanttTask[] | undefined) {\n    this._tasks.set(v ?? [])\n  }\n  private readonly _tasks = signal<GanttTask[]>([])\n\n  readonly total = computed(() => this._tasks().length)\n  readonly completed = computed(() => this._tasks().filter((t) => t.status === 'done').length)\n  readonly inProgress = computed(() => this._tasks().filter((t) => t.status === 'in-progress').length)\n  readonly milestones = computed(() => this._tasks().filter((t) => t.isMilestone).length)\n  readonly progress = computed(() => {\n    const total = this.total()\n    if (total === 0) return 0\n    const sum = this._tasks().reduce((acc, t) => acc + (t.progress ?? 0), 0)\n    return Math.round(sum / total)\n  })\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/project-roadmap/roadmap-metrics.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/project-roadmap/roadmap-task-detail.component.ts",
      "content": "// Slide-over detail drawer for the selected roadmap task: status + milestone badges,\n// schedule dates, completion bar, and owner. Port of the Vue RoadmapTaskDetail block 1:1.\nimport { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, computed } from '@angular/core'\nimport { Calendar, LucideAngularModule } from 'lucide-angular'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport {\n  UiSheetComponent,\n  UiSheetContentComponent,\n  UiSheetDescriptionComponent,\n  UiSheetHeaderComponent,\n  UiSheetTitleComponent,\n} from '@/ui/sheet/sheet.component'\nimport type { GanttTask } from '@/ui/gantt/types'\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-roadmap-task-detail, [ui-roadmap-task-detail]',\n  standalone: true,\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiSheetComponent,\n    UiSheetContentComponent,\n    UiSheetDescriptionComponent,\n    UiSheetHeaderComponent,\n    UiSheetTitleComponent,\n  ],\n  template: `\n    <div ui-sheet data-slot=\"project-roadmap\" [open]=\"open\" (openChange)=\"openChange.emit($event)\">\n      @if (task) {\n        <div ui-sheet-content class=\"sm:max-w-md\">\n          <div ui-sheet-header class=\"border-border space-y-2 border-b pb-4\">\n            <div class=\"flex items-center gap-2\">\n              <span ui-badge [variant]=\"statusBadge().variant\" [class]=\"statusBadge().class\">\n                {{ statusBadge().label }}\n              </span>\n              @if (task.isMilestone) {\n                <span ui-badge variant=\"outline\" class=\"border-warning/30 text-warning\"> Milestone </span>\n              }\n            </div>\n            <h2 ui-sheet-title class=\"text-foreground text-lg font-semibold\">\n              {{ task.name }}\n            </h2>\n            <p ui-sheet-description class=\"text-muted-foreground text-xs\">\n              Task ID: <span class=\"font-mono\">{{ task.id }}</span>\n            </p>\n          </div>\n\n          <div class=\"space-y-5 py-5 text-sm\">\n            <!-- Date Schedule -->\n            <div class=\"space-y-1.5\">\n              <span class=\"text-muted-foreground text-xs font-medium tracking-wider uppercase\">Schedule</span>\n              <div class=\"text-foreground flex items-center gap-2 font-mono text-xs\">\n                <lucide-icon [img]=\"Calendar\" class=\"text-muted-foreground size-4\" />\n                <span>{{ task.startDate }}</span>\n                <span>&rarr;</span>\n                <span>{{ task.endDate }}</span>\n              </div>\n            </div>\n\n            <!-- Progress -->\n            @if (task.progress != null) {\n              <div class=\"space-y-1.5\">\n                <div class=\"flex items-center justify-between text-xs\">\n                  <span class=\"text-muted-foreground font-medium tracking-wider uppercase\">Completion</span>\n                  <span class=\"font-mono font-bold\">{{ task.progress }}%</span>\n                </div>\n                <div class=\"bg-muted h-2 w-full overflow-hidden rounded-full\">\n                  <div\n                    [style.width.%]=\"task.progress\"\n                    class=\"bg-primary h-full rounded-full transition-[width] duration-300\"\n                  ></div>\n                </div>\n              </div>\n            }\n\n            <!-- Assignee -->\n            @if (task.assignee) {\n              <div class=\"space-y-1.5\">\n                <span class=\"text-muted-foreground text-xs font-medium tracking-wider uppercase\">Owner</span>\n                <div class=\"flex items-center gap-2.5\">\n                  <div\n                    class=\"bg-primary/10 text-primary flex size-7 items-center justify-center rounded-full text-xs font-medium\"\n                  >\n                    {{ task.assignee.initials || task.assignee.name.charAt(0) }}\n                  </div>\n                  <span class=\"text-foreground text-sm font-medium\">{{ task.assignee.name }}</span>\n                </div>\n              </div>\n            }\n          </div>\n        </div>\n      }\n    </div>\n  `,\n})\nexport class UiRoadmapTaskDetailComponent {\n  protected readonly Calendar = Calendar\n\n  @Input() task: GanttTask | null = null\n  @Input() open = false\n  @Output() openChange = new EventEmitter<boolean>()\n\n  readonly statusBadge = computed(() => {\n    const status = this.task?.status\n    if (!status) return { label: 'Active', variant: 'secondary' as const, class: undefined as string | undefined }\n    switch (status) {\n      case 'done':\n        return { label: 'Completed', variant: 'outline' as const, class: 'text-success border-success/30' }\n      case 'in-progress':\n        return { label: 'In Progress', variant: 'default' as const, class: undefined as string | undefined }\n      case 'blocked':\n        return { label: 'Blocked', variant: 'destructive' as const, class: undefined as string | undefined }\n      default:\n        return { label: 'To Do', variant: 'secondary' as const, class: undefined as string | undefined }\n    }\n  })\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/project-roadmap/roadmap-task-detail.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/project-roadmap/roadmap-add-task-dialog.component.ts",
      "content": "// Add-task modal dialog for the project roadmap: name, date range, milestone toggle,\n// initial status, and assignee. Emits `addTask`. Port of the Vue RoadmapAddTaskDialog 1:1.\nimport { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, signal } from '@angular/core'\nimport { LucideAngularModule, Plus } from 'lucide-angular'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport { UiCheckboxComponent } from '@/ui/checkbox/checkbox.component'\nimport {\n  UiDialogComponent,\n  UiDialogContentComponent,\n  UiDialogDescriptionComponent,\n  UiDialogFooterComponent,\n  UiDialogHeaderComponent,\n  UiDialogTitleComponent,\n} from '@/ui/dialog/dialog.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport { UiLabelComponent } from '@/ui/label/label.component'\nimport {\n  UiSelectComponent,\n  UiSelectContentComponent,\n  UiSelectItemComponent,\n  UiSelectTriggerComponent,\n  UiSelectValueComponent,\n} from '@/ui/select/select.component'\nimport type { GanttTask } from '@/ui/gantt/types'\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-roadmap-add-task-dialog, [ui-roadmap-add-task-dialog]',\n  standalone: true,\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiButtonComponent,\n    UiCheckboxComponent,\n    UiDialogComponent,\n    UiDialogContentComponent,\n    UiDialogDescriptionComponent,\n    UiDialogFooterComponent,\n    UiDialogHeaderComponent,\n    UiDialogTitleComponent,\n    UiInputComponent,\n    UiLabelComponent,\n    UiSelectComponent,\n    UiSelectContentComponent,\n    UiSelectItemComponent,\n    UiSelectTriggerComponent,\n    UiSelectValueComponent,\n  ],\n  template: `\n    <ui-dialog data-slot=\"project-roadmap\" [open]=\"open\" (openChange)=\"openChange.emit($event)\">\n      <ui-dialog-content class=\"max-w-[480px] sm:max-w-full\">\n        <ui-dialog-header>\n          <h2 ui-dialog-title>Add Roadmap Task</h2>\n          <p ui-dialog-description>\n            Create a new scheduled deliverable or critical milestone in the project Gantt timeline.\n          </p>\n        </ui-dialog-header>\n\n        <form (submit)=\"handleSubmit($event)\" class=\"space-y-4 py-2\">\n          <!-- Task Name -->\n          <div class=\"space-y-1.5\">\n            <label ui-label for=\"roadmap-task-name\">Task Name</label>\n            <ui-input\n              id=\"roadmap-task-name\"\n              [value]=\"name()\"\n              (valueChange)=\"name.set($event)\"\n              placeholder=\"e.g. Design Token Hierarchy & Specs\"\n            />\n          </div>\n\n          <!-- Date Range -->\n          <div class=\"grid grid-cols-2 gap-3\">\n            <div class=\"space-y-1.5\">\n              <label ui-label for=\"roadmap-start-date\">Start Date</label>\n              <ui-input\n                id=\"roadmap-start-date\"\n                [value]=\"startDate()\"\n                (valueChange)=\"startDate.set($event)\"\n                type=\"date\"\n              />\n            </div>\n            <div class=\"space-y-1.5\">\n              <label ui-label for=\"roadmap-end-date\">End Date</label>\n              <ui-input\n                id=\"roadmap-end-date\"\n                [value]=\"endDate()\"\n                (valueChange)=\"endDate.set($event)\"\n                type=\"date\"\n                [disabled]=\"isMilestone()\"\n              />\n            </div>\n          </div>\n\n          <!-- Milestone toggle -->\n          <div class=\"border-border/80 bg-muted/20 flex items-center space-x-2 rounded-md border p-3\">\n            <ui-checkbox\n              id=\"roadmap-milestone-toggle\"\n              [checked]=\"isMilestone()\"\n              (checkedChange)=\"isMilestone.set(!!$event)\"\n            />\n            <div class=\"grid gap-0.5 leading-none\">\n              <label\n                for=\"roadmap-milestone-toggle\"\n                class=\"text-foreground cursor-pointer text-xs font-semibold select-none\"\n              >\n                Key Project Milestone\n              </label>\n              <p class=\"text-muted-foreground text-xs\">\n                Marks a zero-duration target deliverable (e.g. Beta Release, Audit sign-off)\n              </p>\n            </div>\n          </div>\n\n          <!-- Status & Assignee -->\n          <div class=\"grid grid-cols-2 gap-3\">\n            <div class=\"space-y-1.5\">\n              <label ui-label>Initial Status</label>\n              <ui-select [value]=\"status()\" (valueChange)=\"status.set($any($event))\">\n                <button ui-select-trigger>\n                  <ui-select-value placeholder=\"Status\" />\n                </button>\n                <ui-select-content>\n                  <ui-select-item value=\"todo\">To Do</ui-select-item>\n                  <ui-select-item value=\"in-progress\">In Progress</ui-select-item>\n                  <ui-select-item value=\"done\">Completed</ui-select-item>\n                </ui-select-content>\n              </ui-select>\n            </div>\n\n            <div class=\"space-y-1.5\">\n              <label ui-label>Assignee</label>\n              <ui-select [value]=\"assigneeName()\" (valueChange)=\"assigneeName.set($event)\">\n                <button ui-select-trigger>\n                  <ui-select-value placeholder=\"Assignee\" />\n                </button>\n                <ui-select-content>\n                  <ui-select-item value=\"Marcus Rivera\">Marcus Rivera</ui-select-item>\n                  <ui-select-item value=\"Sarah Connor\">Sarah Connor</ui-select-item>\n                  <ui-select-item value=\"Priya Nair\">Priya Nair</ui-select-item>\n                  <ui-select-item value=\"Sundar Krishnan\">Sundar Krishnan</ui-select-item>\n                  <ui-select-item value=\"Diane Cho\">Diane Cho</ui-select-item>\n                </ui-select-content>\n              </ui-select>\n            </div>\n          </div>\n\n          <div ui-dialog-footer class=\"pt-4\">\n            <button ui-button variant=\"outline\" type=\"button\" (click)=\"openChange.emit(false)\">Cancel</button>\n            <button ui-button type=\"submit\">\n              <lucide-icon [img]=\"Plus\" class=\"mr-1.5 size-3.5\" />\n              Create Task\n            </button>\n          </div>\n        </form>\n      </ui-dialog-content>\n    </ui-dialog>\n  `,\n})\nexport class UiRoadmapAddTaskDialogComponent {\n  protected readonly Plus = Plus\n\n  @Input() open = false\n  @Output() openChange = new EventEmitter<boolean>()\n  @Output() addTask = new EventEmitter<GanttTask>()\n\n  readonly name = signal('')\n  readonly startDate = signal('2026-08-20')\n  readonly endDate = signal('2026-08-28')\n  readonly status = signal<'todo' | 'in-progress' | 'done'>('todo')\n  readonly progress = signal(0)\n  readonly isMilestone = signal(false)\n  readonly assigneeName = signal('Marcus Rivera')\n\n  handleSubmit(event: Event): void {\n    event.preventDefault()\n    const trimmed = this.name().trim()\n    if (!trimmed) return\n\n    const assignee = this.assigneeName()\n    const newTask: GanttTask = {\n      id: `task-${Date.now()}`,\n      name: trimmed,\n      startDate: this.startDate(),\n      endDate: this.isMilestone() ? this.startDate() : this.endDate(),\n      status: this.status(),\n      progress: this.isMilestone() ? 100 : Number(this.progress()) || 0,\n      isMilestone: this.isMilestone(),\n      assignee: {\n        name: assignee,\n        initials: assignee\n          .split(' ')\n          .map((p) => p[0])\n          .join(''),\n      },\n    }\n\n    this.addTask.emit(newTask)\n    this.openChange.emit(false)\n\n    // Reset form\n    this.name.set('')\n    this.progress.set(0)\n    this.isMilestone.set(false)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/project-roadmap/roadmap-add-task-dialog.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/project-roadmap/types.ts",
      "content": "import type { GanttTask } from '@/ui/gantt/types'\n\nexport interface RoadmapProject {\n  id: string\n  name: string\n  description?: string\n  tasks: GanttTask[]\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/project-roadmap/types.ts"
    },
    {
      "path": "packages/registry-angular/blocks/project-roadmap/index.ts",
      "content": "export { UiProjectRoadmapComponent } from './project-roadmap.component'\nexport { UiRoadmapAddTaskDialogComponent } from './roadmap-add-task-dialog.component'\nexport { UiRoadmapMetricsComponent } from './roadmap-metrics.component'\nexport { UiRoadmapTaskDetailComponent } from './roadmap-task-detail.component'\nexport type { RoadmapProject } from './types'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/project-roadmap/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/gantt.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/select.json",
    "https://uipkge.dev/r/angular/dialog.json",
    "https://uipkge.dev/r/angular/sheet.json",
    "https://uipkge.dev/r/angular/checkbox.json",
    "https://uipkge.dev/r/angular/label.json"
  ],
  "description": "Full interactive project roadmap and deliverables surface: Gantt timeline with multi-scale zooming, work breakdown task tree, overall progress metrics, search and status/assignee filters, task detail slide-over Sheet, and Add Task modal dialog.",
  "categories": [
    "productivity",
    "dashboard",
    "data"
  ]
}