@industry-theme/backlogmd-kanban-panel 1.0.25 → 1.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3740,37 +3740,29 @@ const createLucideIcon = (iconName, iconNode) => {
3740
3740
  * This source code is licensed under the ISC license.
3741
3741
  * See the LICENSE file in the root directory of this source tree.
3742
3742
  */
3743
- const __iconNode$o = [
3743
+ const __iconNode$n = [
3744
3744
  ["path", { d: "M8 2v4", key: "1cmpym" }],
3745
3745
  ["path", { d: "M16 2v4", key: "4m81vk" }],
3746
3746
  ["rect", { width: "18", height: "18", x: "3", y: "4", rx: "2", key: "1hopcy" }],
3747
3747
  ["path", { d: "M3 10h18", key: "8toen8" }]
3748
3748
  ];
3749
- const Calendar = createLucideIcon("calendar", __iconNode$o);
3749
+ const Calendar = createLucideIcon("calendar", __iconNode$n);
3750
3750
  /**
3751
3751
  * @license lucide-react v0.552.0 - ISC
3752
3752
  *
3753
3753
  * This source code is licensed under the ISC license.
3754
3754
  * See the LICENSE file in the root directory of this source tree.
3755
3755
  */
3756
- const __iconNode$n = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
3757
- const Check = createLucideIcon("check", __iconNode$n);
3756
+ const __iconNode$m = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
3757
+ const Check = createLucideIcon("check", __iconNode$m);
3758
3758
  /**
3759
3759
  * @license lucide-react v0.552.0 - ISC
3760
3760
  *
3761
3761
  * This source code is licensed under the ISC license.
3762
3762
  * See the LICENSE file in the root directory of this source tree.
3763
3763
  */
3764
- const __iconNode$m = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
3765
- const ChevronDown = createLucideIcon("chevron-down", __iconNode$m);
3766
- /**
3767
- * @license lucide-react v0.552.0 - ISC
3768
- *
3769
- * This source code is licensed under the ISC license.
3770
- * See the LICENSE file in the root directory of this source tree.
3771
- */
3772
- const __iconNode$l = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
3773
- const ChevronRight = createLucideIcon("chevron-right", __iconNode$l);
3764
+ const __iconNode$l = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
3765
+ const ChevronDown = createLucideIcon("chevron-down", __iconNode$l);
3774
3766
  /**
3775
3767
  * @license lucide-react v0.552.0 - ISC
3776
3768
  *
@@ -6149,7 +6141,8 @@ function useKanbanData(options) {
6149
6141
  const TaskCard = ({
6150
6142
  task,
6151
6143
  onClick,
6152
- isDragOverlay = false
6144
+ isDragOverlay = false,
6145
+ isSelected = false
6153
6146
  }) => {
6154
6147
  const { theme: theme2 } = useTheme();
6155
6148
  const {
@@ -6176,10 +6169,10 @@ const TaskCard = ({
6176
6169
  };
6177
6170
  const style2 = {
6178
6171
  flexShrink: 0,
6179
- background: theme2.colors.surface,
6172
+ background: isSelected ? `${theme2.colors.primary}10` : theme2.colors.surface,
6180
6173
  borderRadius: theme2.radii[2],
6181
6174
  padding: "12px",
6182
- border: `1px solid ${theme2.colors.border}`,
6175
+ border: `1px solid ${isSelected ? theme2.colors.primary : theme2.colors.border}`,
6183
6176
  borderLeft: `4px solid ${getPriorityColor(task.priority)}`,
6184
6177
  cursor: isDragOverlay ? "grabbing" : "grab",
6185
6178
  transition: isDragging ? "none" : "all 0.2s ease",
@@ -6189,6 +6182,10 @@ const TaskCard = ({
6189
6182
  // When dragging, the original card stays in place but becomes a placeholder
6190
6183
  // The DragOverlay handles the visual movement
6191
6184
  opacity: isDragging ? 0.4 : 1,
6185
+ // Selected card styling
6186
+ ...isSelected && !isDragOverlay && {
6187
+ boxShadow: `0 0 0 1px ${theme2.colors.primary}`
6188
+ },
6192
6189
  // Overlay card styling
6193
6190
  ...isDragOverlay && {
6194
6191
  boxShadow: `0 8px 16px rgba(0, 0, 0, 0.15)`,
@@ -6328,7 +6325,8 @@ const KanbanColumn = ({
6328
6325
  isLoadingMore = false,
6329
6326
  onLoadMore,
6330
6327
  onTaskClick,
6331
- fullWidth = false
6328
+ fullWidth = false,
6329
+ selectedTaskId
6332
6330
  }) => {
6333
6331
  const { theme: theme2 } = useTheme();
6334
6332
  const { setNodeRef, isOver } = useDroppable({
@@ -6417,7 +6415,8 @@ const KanbanColumn = ({
6417
6415
  TaskCard,
6418
6416
  {
6419
6417
  task,
6420
- onClick: onTaskClick
6418
+ onClick: onTaskClick,
6419
+ isSelected: selectedTaskId === task.id
6421
6420
  },
6422
6421
  task.id
6423
6422
  )),
@@ -6741,6 +6740,7 @@ const TaskModal = ({
6741
6740
  onSave,
6742
6741
  task,
6743
6742
  defaultStatus = "To Do",
6743
+ defaultMilestone = "",
6744
6744
  availableStatuses = ["To Do", "In Progress", "Done"],
6745
6745
  availableMilestones = []
6746
6746
  }) => {
@@ -6773,11 +6773,11 @@ const TaskModal = ({
6773
6773
  setPriority("medium");
6774
6774
  setLabels("");
6775
6775
  setAssignee("");
6776
- setMilestone("");
6776
+ setMilestone(defaultMilestone);
6777
6777
  }
6778
6778
  setError(null);
6779
6779
  }
6780
- }, [isOpen, task, defaultStatus]);
6780
+ }, [isOpen, task, defaultStatus, defaultMilestone]);
6781
6781
  const handleSubmit = async (e) => {
6782
6782
  e.preventDefault();
6783
6783
  if (!title.trim()) {
@@ -7154,7 +7154,7 @@ const TaskModal = ({
7154
7154
  },
7155
7155
  children: [
7156
7156
  /* @__PURE__ */ jsx("option", { value: "", children: "None" }),
7157
- availableMilestones.map((m) => /* @__PURE__ */ jsx("option", { value: m, children: m }, m))
7157
+ availableMilestones.map((m) => /* @__PURE__ */ jsx("option", { value: m.id, children: m.title }, m.id))
7158
7158
  ]
7159
7159
  }
7160
7160
  )
@@ -7315,6 +7315,11 @@ function useMilestoneData(options) {
7315
7315
  try {
7316
7316
  const files = fileTreeSlice.data.allFiles;
7317
7317
  const filePaths = files.map((f) => f.path);
7318
+ const taskPaths = filePaths.filter((p2) => p2.includes("backlog/tasks/"));
7319
+ console.log(`[useMilestoneData] File paths: ${filePaths.length} total, ${taskPaths.length} task files`);
7320
+ if (taskPaths.length > 0) {
7321
+ console.log(`[useMilestoneData] Sample task paths:`, taskPaths.slice(0, 3));
7322
+ }
7318
7323
  const fs = new PanelFileSystemAdapter({
7319
7324
  fetchFile: fetchFileContent,
7320
7325
  filePaths,
@@ -7337,6 +7342,11 @@ function useMilestoneData(options) {
7337
7342
  setCanWrite(fs.canWrite);
7338
7343
  await core2.initializeLazy(filePaths);
7339
7344
  coreRef.current = core2;
7345
+ const taskIndex = core2.taskIndex;
7346
+ if (taskIndex) {
7347
+ const indexedIds = Array.from(taskIndex.keys());
7348
+ console.log(`[useMilestoneData] Core taskIndex has ${indexedIds.length} tasks:`, indexedIds.slice(0, 5));
7349
+ }
7340
7350
  console.log("[useMilestoneData] Core instance:", core2);
7341
7351
  console.log("[useMilestoneData] Core.listMilestones:", typeof core2.listMilestones);
7342
7352
  console.log("[useMilestoneData] Core prototype:", Object.getPrototypeOf(core2));
@@ -7350,7 +7360,7 @@ function useMilestoneData(options) {
7350
7360
  console.log(`[useMilestoneData] Loaded ${milestoneList.length} milestones`);
7351
7361
  const allTaskIds = milestoneList.flatMap((m) => m.tasks);
7352
7362
  const uniqueTaskIds = [...new Set(allTaskIds)];
7353
- console.log(`[useMilestoneData] Task IDs to load:`, uniqueTaskIds.slice(0, 10), `(${uniqueTaskIds.length} total)`);
7363
+ console.log(`[useMilestoneData] Milestone task IDs to load:`, uniqueTaskIds.slice(0, 5), `(${uniqueTaskIds.length} total)`);
7354
7364
  let allTasks = [];
7355
7365
  if (uniqueTaskIds.length > 0) {
7356
7366
  try {
@@ -7468,326 +7478,6 @@ function useMilestoneData(options) {
7468
7478
  core: coreRef.current
7469
7479
  };
7470
7480
  }
7471
- const MilestoneCard = ({
7472
- milestoneState,
7473
- onToggle,
7474
- onTaskClick
7475
- }) => {
7476
- const { theme: theme2 } = useTheme();
7477
- const { milestone, tasks, isLoading, isExpanded } = milestoneState;
7478
- const totalTasks = milestone.tasks.length;
7479
- const doneTasks = tasks.filter(
7480
- (t) => {
7481
- var _a, _b;
7482
- return ((_a = t.status) == null ? void 0 : _a.toLowerCase().includes("done")) || ((_b = t.status) == null ? void 0 : _b.toLowerCase().includes("complete"));
7483
- }
7484
- ).length;
7485
- const progress = totalTasks > 0 ? Math.round(doneTasks / totalTasks * 100) : 0;
7486
- const showProgress = tasks.length > 0 || totalTasks === 0;
7487
- const getPriorityColor = (priority) => {
7488
- switch (priority) {
7489
- case "high":
7490
- return theme2.colors.error;
7491
- case "medium":
7492
- return theme2.colors.warning;
7493
- case "low":
7494
- return theme2.colors.info;
7495
- default:
7496
- return theme2.colors.border;
7497
- }
7498
- };
7499
- return /* @__PURE__ */ jsxs(
7500
- "div",
7501
- {
7502
- style: {
7503
- flexShrink: 0,
7504
- background: theme2.colors.surface,
7505
- borderRadius: theme2.radii[2],
7506
- border: `1px solid ${theme2.colors.border}`,
7507
- overflow: "hidden"
7508
- },
7509
- children: [
7510
- /* @__PURE__ */ jsxs(
7511
- "div",
7512
- {
7513
- onClick: onToggle,
7514
- style: {
7515
- padding: "16px",
7516
- cursor: "pointer",
7517
- display: "flex",
7518
- flexDirection: "column",
7519
- gap: "12px",
7520
- transition: "background 0.2s ease"
7521
- },
7522
- onMouseEnter: (e) => {
7523
- e.currentTarget.style.background = theme2.colors.backgroundSecondary;
7524
- },
7525
- onMouseLeave: (e) => {
7526
- e.currentTarget.style.background = "transparent";
7527
- },
7528
- children: [
7529
- /* @__PURE__ */ jsxs(
7530
- "div",
7531
- {
7532
- style: {
7533
- display: "flex",
7534
- alignItems: "center",
7535
- justifyContent: "space-between",
7536
- gap: "12px"
7537
- },
7538
- children: [
7539
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
7540
- isLoading ? /* @__PURE__ */ jsx(
7541
- LoaderCircle,
7542
- {
7543
- size: 16,
7544
- color: theme2.colors.primary,
7545
- style: { animation: "spin 1s linear infinite" }
7546
- }
7547
- ) : isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { size: 16, color: theme2.colors.textSecondary }) : /* @__PURE__ */ jsx(ChevronRight, { size: 16, color: theme2.colors.textSecondary }),
7548
- /* @__PURE__ */ jsx(
7549
- "h3",
7550
- {
7551
- style: {
7552
- margin: 0,
7553
- fontSize: theme2.fontSizes[3],
7554
- fontWeight: theme2.fontWeights.semibold,
7555
- color: theme2.colors.text
7556
- },
7557
- children: milestone.title
7558
- }
7559
- )
7560
- ] }),
7561
- /* @__PURE__ */ jsxs(
7562
- "span",
7563
- {
7564
- style: {
7565
- fontSize: theme2.fontSizes[1],
7566
- color: theme2.colors.textSecondary,
7567
- background: theme2.colors.backgroundSecondary,
7568
- padding: "4px 10px",
7569
- borderRadius: theme2.radii[1],
7570
- fontWeight: theme2.fontWeights.medium
7571
- },
7572
- children: [
7573
- totalTasks,
7574
- " task",
7575
- totalTasks !== 1 ? "s" : "",
7576
- showProgress && /* @__PURE__ */ jsxs(Fragment, { children: [
7577
- " · ",
7578
- /* @__PURE__ */ jsxs("span", { style: { color: progress === 100 ? theme2.colors.success : theme2.colors.text }, children: [
7579
- progress,
7580
- "%"
7581
- ] })
7582
- ] })
7583
- ]
7584
- }
7585
- )
7586
- ]
7587
- }
7588
- ),
7589
- !isExpanded && milestone.description && /* @__PURE__ */ jsx(
7590
- "p",
7591
- {
7592
- style: {
7593
- margin: 0,
7594
- fontSize: theme2.fontSizes[1],
7595
- color: theme2.colors.textSecondary,
7596
- overflow: "hidden",
7597
- textOverflow: "ellipsis",
7598
- display: "-webkit-box",
7599
- WebkitLineClamp: 2,
7600
- WebkitBoxOrient: "vertical",
7601
- lineHeight: "1.4"
7602
- },
7603
- children: milestone.description
7604
- }
7605
- )
7606
- ]
7607
- }
7608
- ),
7609
- isExpanded && /* @__PURE__ */ jsxs(
7610
- "div",
7611
- {
7612
- style: {
7613
- borderTop: `1px solid ${theme2.colors.border}`,
7614
- padding: "16px",
7615
- display: "flex",
7616
- flexDirection: "column",
7617
- gap: "12px"
7618
- },
7619
- children: [
7620
- milestone.description && /* @__PURE__ */ jsx(
7621
- "p",
7622
- {
7623
- style: {
7624
- margin: 0,
7625
- fontSize: theme2.fontSizes[1],
7626
- color: theme2.colors.textSecondary,
7627
- lineHeight: "1.5"
7628
- },
7629
- children: milestone.description
7630
- }
7631
- ),
7632
- tasks.length > 0 && /* @__PURE__ */ jsxs(
7633
- "div",
7634
- {
7635
- style: {
7636
- display: "flex",
7637
- gap: "12px",
7638
- flexWrap: "wrap",
7639
- fontSize: theme2.fontSizes[1]
7640
- },
7641
- children: [
7642
- /* @__PURE__ */ jsxs("span", { style: { color: theme2.colors.success }, children: [
7643
- doneTasks,
7644
- " done"
7645
- ] }),
7646
- /* @__PURE__ */ jsxs("span", { style: { color: theme2.colors.textSecondary }, children: [
7647
- totalTasks - doneTasks,
7648
- " remaining"
7649
- ] })
7650
- ]
7651
- }
7652
- ),
7653
- isLoading ? /* @__PURE__ */ jsxs(
7654
- "div",
7655
- {
7656
- style: {
7657
- display: "flex",
7658
- alignItems: "center",
7659
- justifyContent: "center",
7660
- padding: "24px",
7661
- color: theme2.colors.textSecondary,
7662
- gap: "8px"
7663
- },
7664
- children: [
7665
- /* @__PURE__ */ jsx(
7666
- LoaderCircle,
7667
- {
7668
- size: 16,
7669
- style: { animation: "spin 1s linear infinite" }
7670
- }
7671
- ),
7672
- "Loading tasks..."
7673
- ]
7674
- }
7675
- ) : tasks.length > 0 ? /* @__PURE__ */ jsx(
7676
- "div",
7677
- {
7678
- style: {
7679
- display: "flex",
7680
- flexDirection: "column",
7681
- gap: "8px",
7682
- maxHeight: "300px",
7683
- overflowY: "auto",
7684
- paddingRight: "4px"
7685
- },
7686
- children: tasks.map((task) => {
7687
- var _a;
7688
- return /* @__PURE__ */ jsxs(
7689
- "div",
7690
- {
7691
- onClick: (e) => {
7692
- e.stopPropagation();
7693
- onTaskClick == null ? void 0 : onTaskClick(task);
7694
- },
7695
- style: {
7696
- background: theme2.colors.background,
7697
- borderRadius: theme2.radii[1],
7698
- padding: "10px 12px",
7699
- border: `1px solid ${theme2.colors.border}`,
7700
- borderLeft: `3px solid ${getPriorityColor(task.priority)}`,
7701
- cursor: onTaskClick ? "pointer" : "default",
7702
- transition: "all 0.2s ease",
7703
- display: "flex",
7704
- alignItems: "center",
7705
- justifyContent: "space-between",
7706
- gap: "12px"
7707
- },
7708
- onMouseEnter: (e) => {
7709
- if (onTaskClick) {
7710
- e.currentTarget.style.background = theme2.colors.backgroundSecondary;
7711
- }
7712
- },
7713
- onMouseLeave: (e) => {
7714
- e.currentTarget.style.background = theme2.colors.background;
7715
- },
7716
- children: [
7717
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", flex: 1, minWidth: 0 }, children: [
7718
- /* @__PURE__ */ jsxs(
7719
- "span",
7720
- {
7721
- style: {
7722
- fontFamily: theme2.fonts.monospace,
7723
- fontSize: theme2.fontSizes[0],
7724
- color: theme2.colors.textMuted,
7725
- flexShrink: 0
7726
- },
7727
- children: [
7728
- "#",
7729
- task.id
7730
- ]
7731
- }
7732
- ),
7733
- /* @__PURE__ */ jsx(
7734
- "span",
7735
- {
7736
- style: {
7737
- fontSize: theme2.fontSizes[1],
7738
- color: theme2.colors.text,
7739
- overflow: "hidden",
7740
- textOverflow: "ellipsis",
7741
- whiteSpace: "nowrap"
7742
- },
7743
- children: task.title
7744
- }
7745
- )
7746
- ] }),
7747
- /* @__PURE__ */ jsx(
7748
- "span",
7749
- {
7750
- style: {
7751
- fontSize: theme2.fontSizes[0],
7752
- color: ((_a = task.status) == null ? void 0 : _a.toLowerCase().includes("done")) ? theme2.colors.success : theme2.colors.textSecondary,
7753
- background: theme2.colors.backgroundSecondary,
7754
- padding: "2px 8px",
7755
- borderRadius: theme2.radii[1],
7756
- flexShrink: 0
7757
- },
7758
- children: task.status
7759
- }
7760
- )
7761
- ]
7762
- },
7763
- task.id
7764
- );
7765
- })
7766
- }
7767
- ) : totalTasks === 0 ? /* @__PURE__ */ jsx(
7768
- "div",
7769
- {
7770
- style: {
7771
- padding: "16px",
7772
- textAlign: "center",
7773
- color: theme2.colors.textMuted,
7774
- fontSize: theme2.fontSizes[1]
7775
- },
7776
- children: "No tasks in this milestone"
7777
- }
7778
- ) : null
7779
- ]
7780
- }
7781
- ),
7782
- /* @__PURE__ */ jsx("style", { children: `
7783
- @keyframes spin {
7784
- to { transform: rotate(360deg); }
7785
- }
7786
- ` })
7787
- ]
7788
- }
7789
- );
7790
- };
7791
7481
  const MilestoneModal = ({
7792
7482
  isOpen,
7793
7483
  onClose,
@@ -8096,7 +7786,7 @@ const KanbanPanel = ({
8096
7786
  }) => {
8097
7787
  var _a, _b;
8098
7788
  const { theme: theme2 } = useTheme();
8099
- const [_selectedTask, setSelectedTask] = useState(null);
7789
+ const [selectedTaskId, setSelectedTaskId] = useState(null);
8100
7790
  const [selectedTab, setSelectedTab] = useState("todo");
8101
7791
  const [isNarrowView, setIsNarrowView] = useState(false);
8102
7792
  const containerRef = useRef(null);
@@ -8107,6 +7797,8 @@ const KanbanPanel = ({
8107
7797
  const [isMilestoneModalOpen, setIsMilestoneModalOpen] = useState(false);
8108
7798
  const [editingMilestone, setEditingMilestone] = useState(void 0);
8109
7799
  const [isRefreshingMilestones, setIsRefreshingMilestones] = useState(false);
7800
+ const [selectedMilestoneId, setSelectedMilestoneId] = useState(null);
7801
+ const [milestoneStatusFilter, setMilestoneStatusFilter] = useState(null);
8110
7802
  const [searchQuery, setSearchQuery] = useState("");
8111
7803
  const sensors = useSensors(
8112
7804
  useSensor(PointerSensor, {
@@ -8132,6 +7824,16 @@ const KanbanPanel = ({
8132
7824
  observer.observe(container);
8133
7825
  return () => observer.disconnect();
8134
7826
  }, []);
7827
+ useEffect(() => {
7828
+ if (!events2) return;
7829
+ const unsubscribe = events2.on("task:selected", (event) => {
7830
+ const payload = event.payload;
7831
+ if (payload == null ? void 0 : payload.taskId) {
7832
+ setSelectedTaskId(payload.taskId);
7833
+ }
7834
+ });
7835
+ return unsubscribe;
7836
+ }, [events2]);
8135
7837
  const {
8136
7838
  statusColumns,
8137
7839
  tasksByStatus,
@@ -8153,7 +7855,6 @@ const KanbanPanel = ({
8153
7855
  milestones,
8154
7856
  isLoading: isMilestonesLoading,
8155
7857
  error: milestonesError,
8156
- toggleMilestone,
8157
7858
  refreshData: refreshMilestones,
8158
7859
  canWrite: canWriteMilestones,
8159
7860
  core: milestoneCore
@@ -8207,7 +7908,7 @@ const KanbanPanel = ({
8207
7908
  }
8208
7909
  }, [getTaskById, moveTaskOptimistic]);
8209
7910
  const handleTaskClick = (task) => {
8210
- setSelectedTask(task);
7911
+ setSelectedTaskId(task.id);
8211
7912
  if (events2) {
8212
7913
  events2.emit({
8213
7914
  type: "task:selected",
@@ -8328,7 +8029,7 @@ const KanbanPanel = ({
8328
8029
  }
8329
8030
  };
8330
8031
  const handleMilestoneTaskClick = (task) => {
8331
- setSelectedTask(task);
8032
+ setSelectedTaskId(task.id);
8332
8033
  if (events2) {
8333
8034
  events2.emit({
8334
8035
  type: "task:selected",
@@ -8559,6 +8260,33 @@ const KanbanPanel = ({
8559
8260
  }
8560
8261
  )
8561
8262
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
8263
+ canWrite && /* @__PURE__ */ jsxs(
8264
+ "button",
8265
+ {
8266
+ onClick: handleOpenNewTask,
8267
+ disabled: !selectedMilestoneId,
8268
+ title: selectedMilestoneId ? "Add task to milestone" : "Select a milestone first",
8269
+ style: {
8270
+ display: "flex",
8271
+ alignItems: "center",
8272
+ gap: "6px",
8273
+ background: selectedMilestoneId ? theme2.colors.primary : theme2.colors.backgroundSecondary,
8274
+ color: selectedMilestoneId ? theme2.colors.textOnPrimary : theme2.colors.textMuted,
8275
+ border: selectedMilestoneId ? "none" : `1px solid ${theme2.colors.border}`,
8276
+ borderRadius: theme2.radii[2],
8277
+ padding: "6px 12px",
8278
+ fontSize: theme2.fontSizes[1],
8279
+ fontWeight: theme2.fontWeights.medium,
8280
+ cursor: selectedMilestoneId ? "pointer" : "not-allowed",
8281
+ opacity: selectedMilestoneId ? 1 : 0.6,
8282
+ transition: "all 0.2s ease"
8283
+ },
8284
+ children: [
8285
+ /* @__PURE__ */ jsx(Plus, { size: 14 }),
8286
+ "Add Task"
8287
+ ]
8288
+ }
8289
+ ),
8562
8290
  canWriteMilestones && /* @__PURE__ */ jsxs(
8563
8291
  "button",
8564
8292
  {
@@ -8752,7 +8480,8 @@ const KanbanPanel = ({
8752
8480
  status: STATUS_DISPLAY_LABELS[status],
8753
8481
  tasks: columnTasks,
8754
8482
  onTaskClick: handleTaskClick,
8755
- fullWidth: isNarrowView
8483
+ fullWidth: isNarrowView,
8484
+ selectedTaskId
8756
8485
  },
8757
8486
  status
8758
8487
  );
@@ -8780,14 +8509,14 @@ const KanbanPanel = ({
8780
8509
  ]
8781
8510
  }
8782
8511
  ) : (
8783
- /* Milestones View */
8512
+ /* Milestones View - Two Column Layout */
8784
8513
  /* @__PURE__ */ jsx(
8785
8514
  "div",
8786
8515
  {
8787
8516
  style: {
8788
8517
  flex: 1,
8789
8518
  display: "flex",
8790
- flexDirection: "column",
8519
+ gap: "16px",
8791
8520
  overflow: "hidden"
8792
8521
  },
8793
8522
  children: isMilestonesLoading ? /* @__PURE__ */ jsx(
@@ -8822,30 +8551,352 @@ const KanbanPanel = ({
8822
8551
  ] })
8823
8552
  ]
8824
8553
  }
8825
- ) : /* @__PURE__ */ jsx(
8826
- "div",
8827
- {
8828
- style: {
8829
- flex: 1,
8830
- overflowY: "auto",
8831
- overflowX: "hidden",
8832
- display: "flex",
8833
- flexDirection: "column",
8834
- gap: "12px",
8835
- paddingRight: "4px",
8836
- marginRight: "-4px"
8837
- },
8838
- children: milestones.map((milestoneState) => /* @__PURE__ */ jsx(
8839
- MilestoneCard,
8840
- {
8841
- milestoneState,
8842
- onToggle: () => toggleMilestone(milestoneState.milestone.id),
8843
- onTaskClick: handleMilestoneTaskClick
8554
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
8555
+ /* @__PURE__ */ jsx(
8556
+ "div",
8557
+ {
8558
+ style: {
8559
+ flex: 1,
8560
+ minWidth: 0,
8561
+ display: "flex",
8562
+ flexDirection: "column",
8563
+ overflow: "hidden"
8844
8564
  },
8845
- milestoneState.milestone.id
8846
- ))
8847
- }
8848
- )
8565
+ children: /* @__PURE__ */ jsx(
8566
+ "div",
8567
+ {
8568
+ style: {
8569
+ flex: 1,
8570
+ overflowY: "auto",
8571
+ display: "flex",
8572
+ flexDirection: "column",
8573
+ gap: "8px",
8574
+ paddingRight: "4px"
8575
+ },
8576
+ children: milestones.map((milestoneState) => {
8577
+ const isSelected = selectedMilestoneId === milestoneState.milestone.id;
8578
+ const totalTasks = milestoneState.milestone.tasks.length;
8579
+ const doneTasks = milestoneState.tasks.filter(
8580
+ (t) => {
8581
+ var _a2, _b2;
8582
+ return ((_a2 = t.status) == null ? void 0 : _a2.toLowerCase().includes("done")) || ((_b2 = t.status) == null ? void 0 : _b2.toLowerCase().includes("complete"));
8583
+ }
8584
+ ).length;
8585
+ const progress = totalTasks > 0 ? Math.round(doneTasks / totalTasks * 100) : 0;
8586
+ const showProgress = milestoneState.tasks.length > 0 || totalTasks === 0;
8587
+ return /* @__PURE__ */ jsxs(
8588
+ "div",
8589
+ {
8590
+ onClick: () => {
8591
+ setSelectedMilestoneId(milestoneState.milestone.id);
8592
+ setMilestoneStatusFilter(null);
8593
+ },
8594
+ style: {
8595
+ padding: "12px 16px",
8596
+ background: isSelected ? theme2.colors.primary + "15" : theme2.colors.surface,
8597
+ border: `1px solid ${isSelected ? theme2.colors.primary : theme2.colors.border}`,
8598
+ borderRadius: theme2.radii[2],
8599
+ cursor: "pointer",
8600
+ transition: "all 0.2s ease"
8601
+ },
8602
+ children: [
8603
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "12px" }, children: [
8604
+ /* @__PURE__ */ jsx(
8605
+ "h3",
8606
+ {
8607
+ style: {
8608
+ margin: 0,
8609
+ fontSize: theme2.fontSizes[2],
8610
+ fontWeight: theme2.fontWeights.semibold,
8611
+ color: theme2.colors.text,
8612
+ overflow: "hidden",
8613
+ textOverflow: "ellipsis",
8614
+ whiteSpace: "nowrap"
8615
+ },
8616
+ children: milestoneState.milestone.title
8617
+ }
8618
+ ),
8619
+ /* @__PURE__ */ jsxs(
8620
+ "span",
8621
+ {
8622
+ style: {
8623
+ fontSize: theme2.fontSizes[1],
8624
+ color: theme2.colors.textSecondary,
8625
+ background: theme2.colors.backgroundSecondary,
8626
+ padding: "2px 8px",
8627
+ borderRadius: theme2.radii[1],
8628
+ flexShrink: 0
8629
+ },
8630
+ children: [
8631
+ totalTasks,
8632
+ " task",
8633
+ totalTasks !== 1 ? "s" : "",
8634
+ showProgress && /* @__PURE__ */ jsxs(Fragment, { children: [
8635
+ " · ",
8636
+ /* @__PURE__ */ jsxs("span", { style: { color: progress === 100 ? theme2.colors.success : theme2.colors.text }, children: [
8637
+ progress,
8638
+ "%"
8639
+ ] })
8640
+ ] })
8641
+ ]
8642
+ }
8643
+ )
8644
+ ] }),
8645
+ isSelected && milestoneState.milestone.description && /* @__PURE__ */ jsx(
8646
+ "p",
8647
+ {
8648
+ style: {
8649
+ margin: "8px 0 0 0",
8650
+ fontSize: theme2.fontSizes[1],
8651
+ color: theme2.colors.textSecondary,
8652
+ lineHeight: "1.4"
8653
+ },
8654
+ children: milestoneState.milestone.description
8655
+ }
8656
+ )
8657
+ ]
8658
+ },
8659
+ milestoneState.milestone.id
8660
+ );
8661
+ })
8662
+ }
8663
+ )
8664
+ }
8665
+ ),
8666
+ /* @__PURE__ */ jsx(
8667
+ "div",
8668
+ {
8669
+ style: {
8670
+ flex: 1,
8671
+ minWidth: 0,
8672
+ display: "flex",
8673
+ flexDirection: "column",
8674
+ background: theme2.colors.surface,
8675
+ borderRadius: theme2.radii[2],
8676
+ border: `1px solid ${theme2.colors.border}`,
8677
+ overflow: "hidden"
8678
+ },
8679
+ children: (() => {
8680
+ const selectedMilestone = milestones.find((m) => m.milestone.id === selectedMilestoneId);
8681
+ if (!selectedMilestone) {
8682
+ return /* @__PURE__ */ jsxs(
8683
+ "div",
8684
+ {
8685
+ style: {
8686
+ flex: 1,
8687
+ display: "flex",
8688
+ flexDirection: "column",
8689
+ alignItems: "center",
8690
+ justifyContent: "center",
8691
+ gap: "8px",
8692
+ color: theme2.colors.textMuted,
8693
+ padding: "24px"
8694
+ },
8695
+ children: [
8696
+ /* @__PURE__ */ jsx(Milestone, { size: 32, color: theme2.colors.border }),
8697
+ /* @__PURE__ */ jsx("span", { style: { fontSize: theme2.fontSizes[1] }, children: "Select a milestone to view tasks" })
8698
+ ]
8699
+ }
8700
+ );
8701
+ }
8702
+ const tasks = selectedMilestone.tasks;
8703
+ const getPriorityColor = (priority) => {
8704
+ switch (priority) {
8705
+ case "high":
8706
+ return theme2.colors.error;
8707
+ case "medium":
8708
+ return theme2.colors.warning;
8709
+ case "low":
8710
+ return theme2.colors.info;
8711
+ default:
8712
+ return theme2.colors.border;
8713
+ }
8714
+ };
8715
+ const todoCount = tasks.filter((t) => t.status === "To Do").length;
8716
+ const inProgressCount = tasks.filter((t) => t.status === "In Progress").length;
8717
+ const doneCount = tasks.filter(
8718
+ (t) => {
8719
+ var _a2, _b2;
8720
+ return ((_a2 = t.status) == null ? void 0 : _a2.toLowerCase().includes("done")) || ((_b2 = t.status) == null ? void 0 : _b2.toLowerCase().includes("complete"));
8721
+ }
8722
+ ).length;
8723
+ const filteredTasks = milestoneStatusFilter ? tasks.filter((t) => {
8724
+ var _a2, _b2;
8725
+ if (milestoneStatusFilter === "Done") {
8726
+ return ((_a2 = t.status) == null ? void 0 : _a2.toLowerCase().includes("done")) || ((_b2 = t.status) == null ? void 0 : _b2.toLowerCase().includes("complete"));
8727
+ }
8728
+ return t.status === milestoneStatusFilter;
8729
+ }) : tasks;
8730
+ const getFilterButtonStyle = (status, count, color2) => {
8731
+ const isActive = milestoneStatusFilter === status;
8732
+ return {
8733
+ flex: 1,
8734
+ padding: "8px 12px",
8735
+ border: "none",
8736
+ borderBottom: isActive ? `2px solid ${color2}` : "2px solid transparent",
8737
+ background: isActive ? `${color2}15` : "transparent",
8738
+ color: isActive ? color2 : theme2.colors.textSecondary,
8739
+ fontSize: theme2.fontSizes[1],
8740
+ fontWeight: isActive ? theme2.fontWeights.medium : theme2.fontWeights.body,
8741
+ cursor: count > 0 ? "pointer" : "default",
8742
+ transition: "all 0.2s ease",
8743
+ textAlign: "center",
8744
+ opacity: count === 0 ? 0.5 : 1
8745
+ };
8746
+ };
8747
+ const handleFilterClick = (status, count) => {
8748
+ if (count === 0) return;
8749
+ setMilestoneStatusFilter(milestoneStatusFilter === status ? null : status);
8750
+ };
8751
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
8752
+ /* @__PURE__ */ jsxs(
8753
+ "div",
8754
+ {
8755
+ style: {
8756
+ borderBottom: `1px solid ${theme2.colors.border}`,
8757
+ background: theme2.colors.backgroundSecondary,
8758
+ display: "flex"
8759
+ },
8760
+ children: [
8761
+ /* @__PURE__ */ jsxs(
8762
+ "button",
8763
+ {
8764
+ onClick: () => handleFilterClick("To Do", todoCount),
8765
+ style: getFilterButtonStyle("To Do", todoCount, theme2.colors.textSecondary),
8766
+ children: [
8767
+ todoCount,
8768
+ " To Do"
8769
+ ]
8770
+ }
8771
+ ),
8772
+ /* @__PURE__ */ jsxs(
8773
+ "button",
8774
+ {
8775
+ onClick: () => handleFilterClick("In Progress", inProgressCount),
8776
+ style: getFilterButtonStyle("In Progress", inProgressCount, theme2.colors.warning),
8777
+ children: [
8778
+ inProgressCount,
8779
+ " In Progress"
8780
+ ]
8781
+ }
8782
+ ),
8783
+ /* @__PURE__ */ jsxs(
8784
+ "button",
8785
+ {
8786
+ onClick: () => handleFilterClick("Done", doneCount),
8787
+ style: getFilterButtonStyle("Done", doneCount, theme2.colors.success),
8788
+ children: [
8789
+ doneCount,
8790
+ " Done"
8791
+ ]
8792
+ }
8793
+ )
8794
+ ]
8795
+ }
8796
+ ),
8797
+ /* @__PURE__ */ jsx(
8798
+ "div",
8799
+ {
8800
+ style: {
8801
+ flex: 1,
8802
+ overflowY: "auto",
8803
+ padding: "12px",
8804
+ display: "flex",
8805
+ flexDirection: "column",
8806
+ gap: "8px"
8807
+ },
8808
+ children: filteredTasks.length === 0 ? /* @__PURE__ */ jsx(
8809
+ "div",
8810
+ {
8811
+ style: {
8812
+ flex: 1,
8813
+ display: "flex",
8814
+ alignItems: "center",
8815
+ justifyContent: "center",
8816
+ color: theme2.colors.textMuted,
8817
+ fontSize: theme2.fontSizes[1]
8818
+ },
8819
+ children: milestoneStatusFilter ? `No ${milestoneStatusFilter.toLowerCase()} tasks` : "No tasks in this milestone"
8820
+ }
8821
+ ) : filteredTasks.map((task) => {
8822
+ var _a2;
8823
+ return /* @__PURE__ */ jsxs(
8824
+ "div",
8825
+ {
8826
+ onClick: () => handleMilestoneTaskClick(task),
8827
+ style: {
8828
+ padding: "10px 12px",
8829
+ background: selectedTaskId === task.id ? `${theme2.colors.primary}10` : theme2.colors.background,
8830
+ border: `1px solid ${selectedTaskId === task.id ? theme2.colors.primary : theme2.colors.border}`,
8831
+ borderLeft: `3px solid ${getPriorityColor(task.priority)}`,
8832
+ borderRadius: theme2.radii[1],
8833
+ cursor: "pointer",
8834
+ transition: "all 0.2s ease",
8835
+ display: "flex",
8836
+ alignItems: "center",
8837
+ justifyContent: "space-between",
8838
+ gap: "12px",
8839
+ ...selectedTaskId === task.id && {
8840
+ boxShadow: `0 0 0 1px ${theme2.colors.primary}`
8841
+ }
8842
+ },
8843
+ children: [
8844
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", flex: 1, minWidth: 0 }, children: [
8845
+ /* @__PURE__ */ jsxs(
8846
+ "span",
8847
+ {
8848
+ style: {
8849
+ fontFamily: theme2.fonts.monospace,
8850
+ fontSize: theme2.fontSizes[0],
8851
+ color: theme2.colors.textMuted,
8852
+ flexShrink: 0
8853
+ },
8854
+ children: [
8855
+ "#",
8856
+ task.id
8857
+ ]
8858
+ }
8859
+ ),
8860
+ /* @__PURE__ */ jsx(
8861
+ "span",
8862
+ {
8863
+ style: {
8864
+ fontSize: theme2.fontSizes[1],
8865
+ color: theme2.colors.text,
8866
+ overflow: "hidden",
8867
+ textOverflow: "ellipsis",
8868
+ whiteSpace: "nowrap"
8869
+ },
8870
+ children: task.title
8871
+ }
8872
+ )
8873
+ ] }),
8874
+ /* @__PURE__ */ jsx(
8875
+ "span",
8876
+ {
8877
+ style: {
8878
+ fontSize: theme2.fontSizes[0],
8879
+ color: ((_a2 = task.status) == null ? void 0 : _a2.toLowerCase().includes("done")) ? theme2.colors.success : theme2.colors.textSecondary,
8880
+ background: theme2.colors.backgroundSecondary,
8881
+ padding: "2px 8px",
8882
+ borderRadius: theme2.radii[1],
8883
+ flexShrink: 0
8884
+ },
8885
+ children: task.status
8886
+ }
8887
+ )
8888
+ ]
8889
+ },
8890
+ task.id
8891
+ );
8892
+ })
8893
+ }
8894
+ )
8895
+ ] });
8896
+ })()
8897
+ }
8898
+ )
8899
+ ] })
8849
8900
  }
8850
8901
  )
8851
8902
  ),
@@ -8857,7 +8908,9 @@ const KanbanPanel = ({
8857
8908
  onSave: handleSaveTask,
8858
8909
  task: editingTask,
8859
8910
  defaultStatus: "To Do",
8860
- availableStatuses
8911
+ defaultMilestone: viewMode === "milestones" ? selectedMilestoneId || "" : "",
8912
+ availableStatuses,
8913
+ availableMilestones: milestones.map((m) => ({ id: m.milestone.id, title: m.milestone.title }))
8861
8914
  }
8862
8915
  ),
8863
8916
  /* @__PURE__ */ jsx(