@burdenoff/planmagnet-fe-sdk 2026.703.1 → 2026.703.2

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.
package/dist/index.js CHANGED
@@ -1,16 +1,17 @@
1
1
  import { createContext, useMemo, useEffect, useContext, useState, useCallback } from 'react';
2
- import { ApolloProvider, useQuery, useMutation, useApolloClient } from '@apollo/client/react';
2
+ import { ApolloProvider, useApolloClient, useMutation, useQuery } from '@apollo/client/react';
3
3
  import { createHttpLink, ApolloClient, from, InMemoryCache } from '@apollo/client/core';
4
4
  import { setContext } from '@apollo/client/link/context';
5
5
  import { onError } from '@apollo/client/link/error';
6
6
  import { CombinedGraphQLErrors } from '@apollo/client/errors';
7
7
  import { jsx, jsxs } from 'react/jsx-runtime';
8
- import { Card, CardContent } from '@burdenoff/fe-libs/ui';
9
- import { Filter } from 'lucide-react';
10
8
  import { gql } from '@apollo/client';
9
+ import { CircleX, CircleCheckBig, CircleDotDashed, CircleDot, Circle, CircleDashed, User, CalendarDays, ChevronUp, ChevronDown, ChevronLeft, ChevronRight, CalendarRange, List, LayoutGrid, Search, Plus } from 'lucide-react';
11
10
  import { clsx } from 'clsx';
12
11
  import { twMerge } from 'tailwind-merge';
12
+ import { addWeeks, startOfWeek, endOfWeek, eachDayOfInterval, addMonths, startOfMonth, endOfMonth, format, isToday, isSameMonth, startOfDay, parseISO, isValid } from 'date-fns';
13
13
  import { cva } from 'class-variance-authority';
14
+ import { Card, CardContent } from '@burdenoff/fe-libs/ui';
14
15
 
15
16
  var clientInstance = null;
16
17
  function buildCache() {
@@ -412,6 +413,109 @@ var MOVE_WORK_ITEM = gql`
412
413
  function cn(...inputs) {
413
414
  return twMerge(clsx(inputs));
414
415
  }
416
+ var CLOSED_STATUSES = /* @__PURE__ */ new Set(["DONE", "CANCELLED"]);
417
+ function parseDate(iso) {
418
+ if (!iso) return null;
419
+ const d = parseISO(iso);
420
+ return isValid(d) ? d : null;
421
+ }
422
+ function formatDueDate(iso) {
423
+ const d = parseDate(iso);
424
+ if (!d) return null;
425
+ const now = /* @__PURE__ */ new Date();
426
+ return format(
427
+ d,
428
+ d.getFullYear() === now.getFullYear() ? "MMM d" : "MMM d yyyy"
429
+ );
430
+ }
431
+ function isOverdue(iso, status) {
432
+ const d = parseDate(iso);
433
+ if (!d) return false;
434
+ if (status && CLOSED_STATUSES.has(status)) return false;
435
+ return startOfDay(d).getTime() < startOfDay(/* @__PURE__ */ new Date()).getTime();
436
+ }
437
+ var badgeVariants = cva(
438
+ "inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium capitalize transition-colors",
439
+ {
440
+ variants: {
441
+ variant: {
442
+ default: "bg-primary/10 text-primary",
443
+ muted: "bg-muted text-muted-foreground",
444
+ secondary: "bg-secondary text-secondary-foreground",
445
+ accent: "bg-accent text-accent-foreground",
446
+ destructive: "bg-destructive/15 text-destructive"
447
+ }
448
+ },
449
+ defaultVariants: {
450
+ variant: "default"
451
+ }
452
+ }
453
+ );
454
+ function Badge({ className, variant, ...props }) {
455
+ return /* @__PURE__ */ jsx("span", { className: cn(badgeVariants({ variant }), className), ...props });
456
+ }
457
+ var TINTS = [
458
+ "bg-primary/15 text-primary",
459
+ "bg-secondary text-secondary-foreground",
460
+ "bg-accent text-accent-foreground",
461
+ "bg-muted text-muted-foreground",
462
+ "bg-destructive/15 text-destructive"
463
+ ];
464
+ var SIZES = {
465
+ sm: "h-5 w-5 text-[9px]",
466
+ md: "h-6 w-6 text-[10px]",
467
+ lg: "h-8 w-8 text-xs"
468
+ };
469
+ function tintIndex(seed) {
470
+ let h = 0;
471
+ for (let i = 0; i < seed.length; i++) {
472
+ h = h * 31 + seed.charCodeAt(i) >>> 0;
473
+ }
474
+ return h % TINTS.length;
475
+ }
476
+ function initials(name, id) {
477
+ const source = (name ?? "").trim();
478
+ if (source) {
479
+ const parts = source.split(/\s+/);
480
+ const first = parts[0]?.[0] ?? "";
481
+ const second = parts.length > 1 ? parts[parts.length - 1]?.[0] ?? "" : "";
482
+ return (first + second).toUpperCase() || "?";
483
+ }
484
+ const raw = (id ?? "").replace(/[^a-zA-Z0-9]/g, "");
485
+ return raw.slice(0, 2).toUpperCase() || "?";
486
+ }
487
+ function Avatar({ id, name, size = "md", className }) {
488
+ if (!id && !name) {
489
+ return /* @__PURE__ */ jsx(
490
+ "span",
491
+ {
492
+ className: cn(
493
+ "inline-flex items-center justify-center rounded-full border border-dashed border-border text-muted-foreground",
494
+ SIZES[size],
495
+ className
496
+ ),
497
+ title: "Unassigned",
498
+ "aria-label": "Unassigned",
499
+ children: /* @__PURE__ */ jsx(User, { className: "h-1/2 w-1/2", "aria-hidden": "true" })
500
+ }
501
+ );
502
+ }
503
+ const tint = TINTS[tintIndex(name || id || "")];
504
+ return /* @__PURE__ */ jsx(
505
+ "span",
506
+ {
507
+ className: cn(
508
+ "inline-flex items-center justify-center rounded-full font-medium",
509
+ SIZES[size],
510
+ tint,
511
+ className
512
+ ),
513
+ title: name ?? id ?? void 0,
514
+ "aria-label": name ?? id ?? void 0,
515
+ children: initials(name, id)
516
+ }
517
+ );
518
+ }
415
519
 
416
520
  // src/types/index.ts
417
521
  var DEFAULT_BOARD_COLUMNS = [
@@ -448,47 +552,275 @@ var TASK_STATUSES = [
448
552
  function enumLabel(value) {
449
553
  return value.toLowerCase().replace(/_/g, " ");
450
554
  }
451
- var columnAccent = {
452
- BACKLOG: "border-t-muted-foreground/40",
453
- TODO: "border-t-primary/50",
454
- IN_PROGRESS: "border-t-primary",
455
- IN_REVIEW: "border-t-secondary-foreground/50",
456
- TESTING: "border-t-accent-foreground/50",
457
- DONE: "border-t-primary/70",
458
- CANCELLED: "border-t-destructive/60"
555
+ var STATUS_META = {
556
+ BACKLOG: { icon: CircleDashed, tone: "text-muted-foreground/60" },
557
+ TODO: { icon: Circle, tone: "text-muted-foreground" },
558
+ IN_PROGRESS: { icon: CircleDot, tone: "text-primary" },
559
+ IN_REVIEW: { icon: CircleDotDashed, tone: "text-accent-foreground" },
560
+ TESTING: { icon: CircleDotDashed, tone: "text-secondary-foreground" },
561
+ DONE: { icon: CircleCheckBig, tone: "text-primary" },
562
+ CANCELLED: { icon: CircleX, tone: "text-destructive/70" }
459
563
  };
460
- var priorityTone = {
461
- NONE: "bg-muted text-muted-foreground",
462
- LOW: "bg-muted text-muted-foreground",
463
- MEDIUM: "bg-secondary text-secondary-foreground",
464
- HIGH: "bg-accent text-accent-foreground",
465
- CRITICAL: "bg-destructive/15 text-destructive"
564
+ function StatusIcon({ status, className }) {
565
+ const meta = STATUS_META[status] ?? STATUS_META.TODO;
566
+ const Icon = meta.icon;
567
+ return /* @__PURE__ */ jsx(
568
+ Icon,
569
+ {
570
+ className: cn("h-3.5 w-3.5 shrink-0", meta.tone, className),
571
+ "aria-hidden": "true"
572
+ }
573
+ );
574
+ }
575
+ function StatusPill({ status, className }) {
576
+ return /* @__PURE__ */ jsxs(
577
+ "span",
578
+ {
579
+ className: cn(
580
+ "inline-flex items-center gap-1.5 rounded-md border border-border bg-muted/40 px-2 py-0.5 text-xs font-medium capitalize text-foreground",
581
+ className
582
+ ),
583
+ children: [
584
+ /* @__PURE__ */ jsx(StatusIcon, { status }),
585
+ enumLabel(status)
586
+ ]
587
+ }
588
+ );
589
+ }
590
+ var PRIORITY_META = {
591
+ NONE: { level: 0, tone: "text-muted-foreground/50" },
592
+ LOW: { level: 1, tone: "text-muted-foreground" },
593
+ MEDIUM: { level: 2, tone: "text-accent-foreground" },
594
+ HIGH: { level: 3, tone: "text-accent-foreground" },
595
+ CRITICAL: { level: 3, tone: "text-destructive" }
466
596
  };
467
- var typeTone = {
468
- TASK: "bg-secondary text-secondary-foreground",
469
- STORY: "bg-primary/10 text-primary",
470
- BUG: "bg-destructive/15 text-destructive",
471
- EPIC: "bg-accent text-accent-foreground",
472
- SUB_TASK: "bg-muted text-muted-foreground"
597
+ var BAR_HEIGHTS = ["h-1.5", "h-2.5", "h-3.5"];
598
+ function PriorityIndicator({
599
+ priority,
600
+ showLabel = false,
601
+ className
602
+ }) {
603
+ const meta = PRIORITY_META[priority] ?? PRIORITY_META.NONE;
604
+ return /* @__PURE__ */ jsxs(
605
+ "span",
606
+ {
607
+ className: cn("inline-flex items-center gap-1.5", meta.tone, className),
608
+ title: `Priority: ${enumLabel(priority)}`,
609
+ children: [
610
+ /* @__PURE__ */ jsx("span", { className: "flex items-end gap-0.5", "aria-hidden": "true", children: BAR_HEIGHTS.map((h, i) => /* @__PURE__ */ jsx(
611
+ "span",
612
+ {
613
+ className: cn(
614
+ "w-1 rounded-[1px]",
615
+ h,
616
+ i < meta.level ? "bg-current" : "bg-current/25"
617
+ )
618
+ },
619
+ h
620
+ )) }),
621
+ showLabel && /* @__PURE__ */ jsx("span", { className: "text-xs font-medium capitalize", children: enumLabel(priority) })
622
+ ]
623
+ }
624
+ );
625
+ }
626
+ var TYPE_VARIANT = {
627
+ TASK: "secondary",
628
+ STORY: "default",
629
+ BUG: "destructive",
630
+ EPIC: "accent",
631
+ SUB_TASK: "muted"
473
632
  };
633
+ function TypeBadge({ type, className }) {
634
+ return /* @__PURE__ */ jsx(Badge, { variant: TYPE_VARIANT[type], className, children: enumLabel(type) });
635
+ }
636
+ function OverlayShell({
637
+ open,
638
+ onClose,
639
+ children,
640
+ ariaLabel,
641
+ className,
642
+ variant
643
+ }) {
644
+ useEffect(() => {
645
+ if (!open) return;
646
+ const onKeyDown = (e) => {
647
+ if (e.key === "Escape") onClose();
648
+ };
649
+ document.addEventListener("keydown", onKeyDown);
650
+ return () => document.removeEventListener("keydown", onKeyDown);
651
+ }, [open, onClose]);
652
+ if (!open) return null;
653
+ const isModal = variant === "modal";
654
+ return /* @__PURE__ */ jsx(
655
+ "div",
656
+ {
657
+ className: cn(
658
+ "fixed inset-0 z-50 flex bg-foreground/40",
659
+ isModal ? "items-center justify-center p-4" : "justify-end"
660
+ ),
661
+ onClick: onClose,
662
+ children: /* @__PURE__ */ jsxs(
663
+ "div",
664
+ {
665
+ role: "dialog",
666
+ "aria-modal": "true",
667
+ "aria-label": ariaLabel,
668
+ className: cn(
669
+ "relative overflow-y-auto bg-background text-foreground shadow-lg",
670
+ isModal ? "max-h-[90vh] w-full max-w-2xl rounded-lg" : "h-full w-full max-w-lg",
671
+ className
672
+ ),
673
+ onClick: (e) => e.stopPropagation(),
674
+ children: [
675
+ /* @__PURE__ */ jsx(
676
+ "button",
677
+ {
678
+ type: "button",
679
+ "aria-label": "Close",
680
+ onClick: onClose,
681
+ className: "absolute right-3 top-3 z-10 rounded-md bg-muted px-2 py-1 text-sm font-medium text-muted-foreground hover:bg-muted/70",
682
+ children: "\u2715"
683
+ }
684
+ ),
685
+ children
686
+ ]
687
+ }
688
+ )
689
+ }
690
+ );
691
+ }
692
+ function Modal(props) {
693
+ return /* @__PURE__ */ jsx(OverlayShell, { variant: "modal", ...props });
694
+ }
695
+ function Drawer(props) {
696
+ return /* @__PURE__ */ jsx(OverlayShell, { variant: "drawer", ...props });
697
+ }
698
+ var EMPTY = /* @__PURE__ */ new Set();
699
+ function TaskCard({
700
+ task,
701
+ hidden = EMPTY,
702
+ onClick,
703
+ draggable,
704
+ onDragStart,
705
+ onDragEnd,
706
+ dragging,
707
+ resolveAssignee,
708
+ className
709
+ }) {
710
+ const overdue = isOverdue(task.dueDate, task.status);
711
+ const due = formatDueDate(task.dueDate);
712
+ const resolved = task.assigneeId ? resolveAssignee?.(task.assigneeId) : null;
713
+ const showTopRow = !hidden.has("key") || !hidden.has("type");
714
+ const showLabels = !hidden.has("labels") && (task.labels?.length ?? 0) > 0;
715
+ const showPriority = !hidden.has("priority");
716
+ const showPoints = !hidden.has("storyPoints") && task.storyPoints != null;
717
+ const showMetaRow = showPriority || showPoints;
718
+ const showAssignee = !hidden.has("assignee");
719
+ const showDue = !hidden.has("dueDate") && Boolean(due);
720
+ const showPeopleRow = showAssignee || showDue;
721
+ const interactive = Boolean(onClick);
722
+ const handleKey = onClick ? (e) => {
723
+ if (e.key === "Enter" || e.key === " ") {
724
+ e.preventDefault();
725
+ onClick(task);
726
+ }
727
+ } : void 0;
728
+ return /* @__PURE__ */ jsxs(
729
+ "div",
730
+ {
731
+ role: interactive ? "button" : void 0,
732
+ tabIndex: interactive ? 0 : void 0,
733
+ draggable,
734
+ onDragStart: draggable ? (e) => onDragStart?.(task, e) : void 0,
735
+ onDragEnd: draggable ? (e) => onDragEnd?.(task, e) : void 0,
736
+ onClick: onClick ? () => onClick(task) : void 0,
737
+ onKeyDown: handleKey,
738
+ className: cn(
739
+ "group rounded-lg border border-border bg-card p-2.5 shadow-sm transition",
740
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
741
+ interactive && "cursor-pointer hover:border-primary/50 hover:shadow-md",
742
+ draggable && "active:cursor-grabbing",
743
+ dragging && "opacity-50 ring-2 ring-primary/40",
744
+ className
745
+ ),
746
+ children: [
747
+ showTopRow && /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [
748
+ !hidden.has("key") ? /* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] uppercase tracking-wide text-muted-foreground", children: task.key }) : /* @__PURE__ */ jsx("span", {}),
749
+ !hidden.has("type") && /* @__PURE__ */ jsx(TypeBadge, { type: task.type })
750
+ ] }),
751
+ /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-sm font-medium leading-snug text-foreground", children: task.title }),
752
+ showLabels && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex flex-wrap gap-1", children: [
753
+ task.labels.slice(0, 3).map((l) => /* @__PURE__ */ jsx(
754
+ "span",
755
+ {
756
+ className: "rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground",
757
+ children: l
758
+ },
759
+ l
760
+ )),
761
+ task.labels.length > 3 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
762
+ "+",
763
+ task.labels.length - 3
764
+ ] })
765
+ ] }),
766
+ showMetaRow && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-between", children: [
767
+ showPriority ? /* @__PURE__ */ jsx(PriorityIndicator, { priority: task.priority, showLabel: true }) : /* @__PURE__ */ jsx("span", {}),
768
+ showPoints && /* @__PURE__ */ jsx(
769
+ "span",
770
+ {
771
+ className: "flex h-5 min-w-5 items-center justify-center rounded-full bg-primary/10 px-1 text-[10px] font-semibold text-primary",
772
+ title: `${task.storyPoints} story points`,
773
+ children: task.storyPoints
774
+ }
775
+ )
776
+ ] }),
777
+ showPeopleRow && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-between gap-2", children: [
778
+ showAssignee ? /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
779
+ /* @__PURE__ */ jsx(Avatar, { id: task.assigneeId, name: resolved?.name, size: "sm" }),
780
+ resolved?.name && /* @__PURE__ */ jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: resolved.name })
781
+ ] }) : /* @__PURE__ */ jsx("span", {}),
782
+ showDue && /* @__PURE__ */ jsxs(
783
+ "span",
784
+ {
785
+ className: cn(
786
+ "flex shrink-0 items-center gap-1 text-[11px]",
787
+ overdue ? "font-medium text-destructive" : "text-muted-foreground"
788
+ ),
789
+ title: overdue ? "Overdue" : "Due date",
790
+ children: [
791
+ /* @__PURE__ */ jsx(CalendarDays, { className: "h-3 w-3", "aria-hidden": "true" }),
792
+ due
793
+ ]
794
+ }
795
+ )
796
+ ] })
797
+ ]
798
+ }
799
+ );
800
+ }
474
801
  function TaskBoardCore({
475
802
  projectId,
476
803
  columns = DEFAULT_BOARD_COLUMNS,
477
- readonly: _readonly,
804
+ readonly,
478
805
  hideHeader,
479
- hideFilters,
806
+ priority = "ALL",
807
+ type = "ALL",
808
+ search = "",
809
+ assigneeId,
480
810
  hiddenFields,
811
+ resolveAssignee,
481
812
  onTaskClick,
482
813
  className,
483
814
  workspaceId: workspaceIdProp
484
815
  }) {
485
816
  const ctx = usePlanMagnetContextSafe();
486
817
  const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
487
- const [priorityFilter, setPriorityFilter] = useState(
488
- "ALL"
489
- );
490
- const [typeFilter, setTypeFilter] = useState("ALL");
818
+ const client = useApolloClient();
491
819
  const hidden = useMemo(() => new Set(hiddenFields ?? []), [hiddenFields]);
820
+ const [draggingId, setDraggingId] = useState(null);
821
+ const [dragOver, setDragOver] = useState(null);
822
+ const [moveError, setMoveError] = useState(null);
823
+ const [updateItem] = useMutation(UPDATE_WORK_ITEM);
492
824
  const { data, loading, error } = useQuery(
493
825
  LIST_WORK_ITEMS,
494
826
  {
@@ -500,20 +832,28 @@ function TaskBoardCore({
500
832
  skip: !workspaceId
501
833
  }
502
834
  );
835
+ const moveTask = useCallback(
836
+ (task, toStatus) => {
837
+ const from2 = task.status;
838
+ if (from2 === toStatus) return;
839
+ const id = client.cache.identify({ __typename: "WorkItem", id: task.id });
840
+ if (!id) return;
841
+ setMoveError(null);
842
+ client.cache.modify({ id, fields: { status: () => toStatus } });
843
+ updateItem({
844
+ variables: { id: task.id, input: { status: toStatus } }
845
+ }).catch(() => {
846
+ client.cache.modify({ id, fields: { status: () => from2 } });
847
+ setMoveError("Couldn't move the task. Please try again.");
848
+ });
849
+ },
850
+ [client, updateItem]
851
+ );
503
852
  if (!workspaceId) {
504
853
  return /* @__PURE__ */ jsx("div", { className: cn("p-6 text-sm text-destructive", className), children: "TaskBoard: no workspaceId provided (prop or PlanMagnetProvider)." });
505
854
  }
506
855
  if (loading && !data) {
507
- return /* @__PURE__ */ jsx(
508
- "div",
509
- {
510
- className: cn(
511
- "flex items-center justify-center p-12",
512
- className
513
- ),
514
- children: /* @__PURE__ */ jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-2 border-primary border-t-transparent" })
515
- }
516
- );
856
+ return /* @__PURE__ */ jsx("div", { className: cn("flex items-center justify-center p-12", className), children: /* @__PURE__ */ jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-2 border-primary border-t-transparent" }) });
517
857
  }
518
858
  if (error && !data) {
519
859
  return /* @__PURE__ */ jsxs("div", { className: cn("p-6 text-sm text-destructive", className), children: [
@@ -522,150 +862,90 @@ function TaskBoardCore({
522
862
  ] });
523
863
  }
524
864
  const tasks = data?.planmagnetWorkItems?.items ?? [];
525
- const filtered = tasks.filter(
526
- (t) => (priorityFilter === "ALL" || t.priority === priorityFilter) && (typeFilter === "ALL" || t.type === typeFilter)
527
- );
528
- const gridCols = columns.length <= 4 ? "md:grid-cols-4" : columns.length === 5 ? "md:grid-cols-5" : "md:grid-cols-6";
529
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4 p-6", className), children: [
530
- !hideHeader && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs("div", { children: [
531
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold text-foreground", children: "Task Board" }),
532
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
533
- tasks.length,
865
+ const query = search.trim().toLowerCase();
866
+ const filtered = tasks.filter((t) => {
867
+ if (priority !== "ALL" && t.priority !== priority) return false;
868
+ if (type !== "ALL" && t.type !== type) return false;
869
+ if (assigneeId && t.assigneeId !== assigneeId) return false;
870
+ if (query) {
871
+ const haystack = [t.title, t.key, ...t.labels ?? []].join(" ").toLowerCase();
872
+ if (!haystack.includes(query)) return false;
873
+ }
874
+ return true;
875
+ });
876
+ const canDrag = !readonly;
877
+ const onColumnDragOver = (status) => (e) => {
878
+ if (!canDrag || !draggingId) return;
879
+ e.preventDefault();
880
+ if (dragOver !== status) setDragOver(status);
881
+ };
882
+ const onColumnDrop = (status) => (e) => {
883
+ if (!canDrag) return;
884
+ e.preventDefault();
885
+ const task = tasks.find((t) => t.id === draggingId);
886
+ setDragOver(null);
887
+ setDraggingId(null);
888
+ if (task) moveTask(task, status);
889
+ };
890
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-3", className), children: [
891
+ !hideHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
892
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-foreground", children: "Board" }),
893
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
894
+ filtered.length,
534
895
  " ",
535
- tasks.length === 1 ? "task" : "tasks"
536
- ] })
537
- ] }) }),
538
- !hideFilters && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-4", children: [
539
- /* @__PURE__ */ jsx(Filter, { className: "h-4 w-4 text-muted-foreground" }),
540
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1", children: [
541
- /* @__PURE__ */ jsx(
542
- FilterChip,
543
- {
544
- active: priorityFilter === "ALL",
545
- label: "all",
546
- onClick: () => setPriorityFilter("ALL")
547
- }
548
- ),
549
- TASK_PRIORITIES.map((p) => /* @__PURE__ */ jsx(
550
- FilterChip,
551
- {
552
- active: priorityFilter === p,
553
- label: p.toLowerCase(),
554
- onClick: () => setPriorityFilter(p)
555
- },
556
- p
557
- ))
558
- ] }),
559
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1", children: [
560
- /* @__PURE__ */ jsx(
561
- FilterChip,
562
- {
563
- active: typeFilter === "ALL",
564
- label: "all",
565
- onClick: () => setTypeFilter("ALL")
566
- }
567
- ),
568
- TASK_TYPES.map((t) => /* @__PURE__ */ jsx(
569
- FilterChip,
570
- {
571
- active: typeFilter === t,
572
- label: t.toLowerCase().replace("_", " "),
573
- onClick: () => setTypeFilter(t)
574
- },
575
- t
576
- ))
896
+ filtered.length === 1 ? "task" : "tasks"
577
897
  ] })
578
898
  ] }),
579
- /* @__PURE__ */ jsx("div", { className: cn("grid grid-cols-1 gap-3 overflow-x-auto", gridCols), children: columns.map((col) => {
899
+ moveError && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-1.5 text-xs text-destructive", children: moveError }),
900
+ /* @__PURE__ */ jsx("div", { className: "flex gap-3 overflow-x-auto pb-2", children: columns.map((col) => {
580
901
  const colTasks = filtered.filter((t) => t.status === col.status);
581
- return /* @__PURE__ */ jsxs("div", { className: "min-w-[200px]", children: [
582
- /* @__PURE__ */ jsx(
583
- "div",
584
- {
585
- className: cn(
586
- "rounded-t-md border-t-2 bg-muted/50 p-2",
587
- columnAccent[col.status] ?? "border-t-muted-foreground/40"
588
- ),
589
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
590
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: col.label }),
591
- /* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted px-1.5 text-xs text-muted-foreground", children: colTasks.length })
592
- ] })
593
- }
594
- ),
595
- /* @__PURE__ */ jsx("div", { className: "mt-2 space-y-2", children: colTasks.map((task) => /* @__PURE__ */ jsx(
596
- Card,
597
- {
598
- className: cn(
599
- "shadow-sm",
600
- onTaskClick && "cursor-pointer hover:border-primary"
601
- ),
602
- onClick: onTaskClick ? () => onTaskClick(task) : void 0,
603
- children: /* @__PURE__ */ jsxs(CardContent, { className: "space-y-2 p-3", children: [
604
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-tight text-foreground", children: task.title }),
605
- !hidden.has("key") && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: task.key }),
606
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1", children: [
607
- !hidden.has("priority") && /* @__PURE__ */ jsx(
608
- "span",
609
- {
610
- className: cn(
611
- "rounded-full px-1.5 py-0.5 text-[10px] capitalize",
612
- priorityTone[task.priority]
613
- ),
614
- children: task.priority.toLowerCase()
615
- }
616
- ),
617
- !hidden.has("type") && /* @__PURE__ */ jsx(
618
- "span",
619
- {
620
- className: cn(
621
- "rounded-full px-1.5 py-0.5 text-[10px] capitalize",
622
- typeTone[task.type]
623
- ),
624
- children: task.type.toLowerCase().replace("_", " ")
625
- }
626
- )
627
- ] }),
628
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
629
- !hidden.has("labels") && /* @__PURE__ */ jsx("div", { className: "flex gap-1", children: (task.labels ?? []).slice(0, 2).map((l) => /* @__PURE__ */ jsx(
630
- "span",
631
- {
632
- className: "rounded bg-muted px-1 py-0.5 text-[9px] text-muted-foreground",
633
- children: l
634
- },
635
- l
636
- )) }),
637
- !hidden.has("storyPoints") && /* @__PURE__ */ jsx("span", { className: "flex h-5 w-5 items-center justify-center rounded-full bg-primary/10 text-[10px] font-medium text-primary", children: task.storyPoints ?? 0 })
902
+ const isOver = dragOver === col.status;
903
+ return /* @__PURE__ */ jsxs(
904
+ "div",
905
+ {
906
+ onDragOver: onColumnDragOver(col.status),
907
+ onDragLeave: () => dragOver === col.status && setDragOver(null),
908
+ onDrop: onColumnDrop(col.status),
909
+ className: cn(
910
+ "flex w-72 shrink-0 flex-col rounded-lg border transition-colors",
911
+ isOver ? "border-primary/50 bg-primary/5" : "border-transparent bg-muted/40"
912
+ ),
913
+ children: [
914
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2", children: [
915
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
916
+ /* @__PURE__ */ jsx(StatusIcon, { status: col.status }),
917
+ col.label
638
918
  ] }),
639
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between text-[10px] text-muted-foreground", children: [
640
- !hidden.has("assignee") && /* @__PURE__ */ jsx("span", { children: task.assigneeId ?? "Unassigned" }),
641
- !hidden.has("dueDate") && /* @__PURE__ */ jsx("span", { children: task.dueDate ?? "" })
642
- ] })
643
- ] })
644
- },
645
- task.id
646
- )) })
647
- ] }, col.status);
919
+ /* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted px-1.5 text-xs font-medium text-muted-foreground", children: colTasks.length })
920
+ ] }),
921
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 px-2 pb-2", children: colTasks.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed border-border/70 px-3 py-6 text-center text-xs text-muted-foreground", children: isOver ? "Drop here" : "No tasks" }) : colTasks.map((task) => /* @__PURE__ */ jsx(
922
+ TaskCard,
923
+ {
924
+ task,
925
+ hidden,
926
+ onClick: onTaskClick,
927
+ draggable: canDrag,
928
+ dragging: draggingId === task.id,
929
+ resolveAssignee,
930
+ onDragStart: (t, e) => {
931
+ setDraggingId(t.id);
932
+ e.dataTransfer.effectAllowed = "move";
933
+ e.dataTransfer.setData("text/plain", t.id);
934
+ },
935
+ onDragEnd: () => {
936
+ setDraggingId(null);
937
+ setDragOver(null);
938
+ }
939
+ },
940
+ task.id
941
+ )) })
942
+ ]
943
+ },
944
+ col.status
945
+ );
648
946
  }) })
649
947
  ] });
650
948
  }
651
- function FilterChip({
652
- active,
653
- label,
654
- onClick
655
- }) {
656
- return /* @__PURE__ */ jsx(
657
- "button",
658
- {
659
- type: "button",
660
- onClick,
661
- className: cn(
662
- "rounded px-2 py-1 text-xs capitalize",
663
- active ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground hover:bg-muted/70"
664
- ),
665
- children: label
666
- }
667
- );
668
- }
669
949
  function TaskBoard({
670
950
  apiGatewayUrl,
671
951
  authToken,
@@ -979,96 +1259,13 @@ function CreateTask({
979
1259
  }
980
1260
  );
981
1261
  }
982
- var badgeVariants = cva(
983
- "inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium capitalize transition-colors",
984
- {
985
- variants: {
986
- variant: {
987
- default: "bg-primary/10 text-primary",
988
- muted: "bg-muted text-muted-foreground",
989
- secondary: "bg-secondary text-secondary-foreground",
990
- accent: "bg-accent text-accent-foreground",
991
- destructive: "bg-destructive/15 text-destructive"
992
- }
993
- },
994
- defaultVariants: {
995
- variant: "default"
996
- }
997
- }
998
- );
999
- function Badge({ className, variant, ...props }) {
1000
- return /* @__PURE__ */ jsx("span", { className: cn(badgeVariants({ variant }), className), ...props });
1001
- }
1002
- function OverlayShell({
1003
- open,
1004
- onClose,
1005
- children,
1006
- ariaLabel,
1007
- className,
1008
- variant
1009
- }) {
1010
- useEffect(() => {
1011
- if (!open) return;
1012
- const onKeyDown = (e) => {
1013
- if (e.key === "Escape") onClose();
1014
- };
1015
- document.addEventListener("keydown", onKeyDown);
1016
- return () => document.removeEventListener("keydown", onKeyDown);
1017
- }, [open, onClose]);
1018
- if (!open) return null;
1019
- const isModal = variant === "modal";
1020
- return /* @__PURE__ */ jsx(
1021
- "div",
1022
- {
1023
- className: cn(
1024
- "fixed inset-0 z-50 flex bg-foreground/40",
1025
- isModal ? "items-center justify-center p-4" : "justify-end"
1026
- ),
1027
- onClick: onClose,
1028
- children: /* @__PURE__ */ jsxs(
1029
- "div",
1030
- {
1031
- role: "dialog",
1032
- "aria-modal": "true",
1033
- "aria-label": ariaLabel,
1034
- className: cn(
1035
- "relative overflow-y-auto bg-background text-foreground shadow-lg",
1036
- isModal ? "max-h-[90vh] w-full max-w-2xl rounded-lg" : "h-full w-full max-w-lg",
1037
- className
1038
- ),
1039
- onClick: (e) => e.stopPropagation(),
1040
- children: [
1041
- /* @__PURE__ */ jsx(
1042
- "button",
1043
- {
1044
- type: "button",
1045
- "aria-label": "Close",
1046
- onClick: onClose,
1047
- className: "absolute right-3 top-3 z-10 rounded-md bg-muted px-2 py-1 text-sm font-medium text-muted-foreground hover:bg-muted/70",
1048
- children: "\u2715"
1049
- }
1050
- ),
1051
- children
1052
- ]
1053
- }
1054
- )
1055
- }
1056
- );
1057
- }
1058
- function Modal(props) {
1059
- return /* @__PURE__ */ jsx(OverlayShell, { variant: "modal", ...props });
1060
- }
1061
- function Drawer(props) {
1062
- return /* @__PURE__ */ jsx(OverlayShell, { variant: "drawer", ...props });
1063
- }
1064
1262
  var DEFAULT_COLUMNS = [
1065
- "key",
1066
1263
  "title",
1067
- "type",
1068
1264
  "status",
1069
1265
  "priority",
1070
1266
  "assignee",
1071
- "dueDate"
1267
+ "dueDate",
1268
+ "storyPoints"
1072
1269
  ];
1073
1270
  var priorityRank = {
1074
1271
  CRITICAL: 0,
@@ -1086,24 +1283,25 @@ var statusRank = {
1086
1283
  DONE: 5,
1087
1284
  CANCELLED: 6
1088
1285
  };
1089
- var priorityVariant = {
1090
- NONE: "muted",
1091
- LOW: "muted",
1092
- MEDIUM: "secondary",
1093
- HIGH: "accent",
1094
- CRITICAL: "destructive"
1095
- };
1096
1286
  var COLUMN_LABELS = {
1097
1287
  key: "Key",
1098
- title: "Title",
1288
+ title: "Task",
1099
1289
  type: "Type",
1100
1290
  status: "Status",
1101
1291
  priority: "Priority",
1102
1292
  assignee: "Assignee",
1103
- dueDate: "Due",
1293
+ dueDate: "Due Date",
1104
1294
  storyPoints: "Points",
1105
1295
  labels: "Labels"
1106
1296
  };
1297
+ var COLUMN_SORT = {
1298
+ title: "title",
1299
+ type: "type",
1300
+ status: "status",
1301
+ priority: "priority",
1302
+ dueDate: "dueDate",
1303
+ storyPoints: "storyPoints"
1304
+ };
1107
1305
  function TaskListCore({
1108
1306
  projectId,
1109
1307
  status,
@@ -1116,12 +1314,17 @@ function TaskListCore({
1116
1314
  sortDir = "asc",
1117
1315
  hideHeader,
1118
1316
  onSelect,
1317
+ resolveAssignee,
1119
1318
  limit = 200,
1120
1319
  className,
1121
1320
  workspaceId: workspaceIdProp
1122
1321
  }) {
1123
1322
  const ctx = usePlanMagnetContextSafe();
1124
1323
  const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
1324
+ const [sort, setSort] = useState({
1325
+ by: sortBy ?? null,
1326
+ dir: sortDir
1327
+ });
1125
1328
  const filter = useMemo(() => {
1126
1329
  const f = {};
1127
1330
  if (projectId) f.projectId = projectId;
@@ -1145,10 +1348,18 @@ function TaskListCore({
1145
1348
  );
1146
1349
  const rows = useMemo(() => {
1147
1350
  const items = data?.planmagnetWorkItems?.items ?? [];
1148
- if (!sortBy) return items;
1149
- const dir = sortDir === "desc" ? -1 : 1;
1150
- return [...items].sort((a, b) => dir * compareTasks(a, b, sortBy));
1151
- }, [data, sortBy, sortDir]);
1351
+ if (!sort.by) return items;
1352
+ const dir = sort.dir === "desc" ? -1 : 1;
1353
+ const key = sort.by;
1354
+ return [...items].sort((a, b) => dir * compareTasks(a, b, key));
1355
+ }, [data, sort]);
1356
+ const toggleSort = (col) => {
1357
+ const key = COLUMN_SORT[col];
1358
+ if (!key) return;
1359
+ setSort(
1360
+ (prev) => prev.by === key ? { by: key, dir: prev.dir === "asc" ? "desc" : "asc" } : { by: key, dir: "asc" }
1361
+ );
1362
+ };
1152
1363
  if (!workspaceId) {
1153
1364
  return /* @__PURE__ */ jsx("div", { className: cn("p-6 text-sm text-destructive", className), children: "TaskList: no workspaceId provided (prop or PlanMagnetProvider)." });
1154
1365
  }
@@ -1161,36 +1372,41 @@ function TaskListCore({
1161
1372
  error.message
1162
1373
  ] });
1163
1374
  }
1164
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4 p-6", className), children: [
1165
- !hideHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1166
- /* @__PURE__ */ jsxs("div", { children: [
1167
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold text-foreground", children: "Tasks" }),
1168
- /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
1169
- rows.length,
1170
- " ",
1171
- rows.length === 1 ? "task" : "tasks"
1172
- ] })
1173
- ] }),
1174
- (status || type || priority || search) && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
1175
- /* @__PURE__ */ jsx(Filter, { className: "h-3.5 w-3.5" }),
1176
- " filtered"
1375
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-3", className), children: [
1376
+ !hideHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
1377
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-foreground", children: "List" }),
1378
+ /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
1379
+ rows.length,
1380
+ " ",
1381
+ rows.length === 1 ? "task" : "tasks"
1177
1382
  ] })
1178
1383
  ] }),
1179
- /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-sm", children: [
1180
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border bg-muted/50 text-left", children: columns.map((col) => /* @__PURE__ */ jsx(
1181
- "th",
1182
- {
1183
- className: "px-3 py-2 font-medium text-muted-foreground",
1184
- children: COLUMN_LABELS[col]
1185
- },
1186
- col
1187
- )) }) }),
1384
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border border-border", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-sm", children: [
1385
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border bg-muted/50 text-left", children: columns.map((col) => {
1386
+ const sortable = Boolean(COLUMN_SORT[col]);
1387
+ const active = sort.by === COLUMN_SORT[col];
1388
+ return /* @__PURE__ */ jsx(
1389
+ "th",
1390
+ {
1391
+ onClick: sortable ? () => toggleSort(col) : void 0,
1392
+ className: cn(
1393
+ "px-3 py-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground",
1394
+ sortable && "cursor-pointer select-none hover:text-foreground"
1395
+ ),
1396
+ children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
1397
+ COLUMN_LABELS[col],
1398
+ active && (sort.dir === "asc" ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-3 w-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3", "aria-hidden": "true" }))
1399
+ ] })
1400
+ },
1401
+ col
1402
+ );
1403
+ }) }) }),
1188
1404
  /* @__PURE__ */ jsxs("tbody", { children: [
1189
1405
  rows.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1190
1406
  "td",
1191
1407
  {
1192
1408
  colSpan: columns.length,
1193
- className: "px-3 py-6 text-center text-muted-foreground",
1409
+ className: "px-3 py-8 text-center text-muted-foreground",
1194
1410
  children: "No tasks found."
1195
1411
  }
1196
1412
  ) }),
@@ -1202,7 +1418,14 @@ function TaskListCore({
1202
1418
  "border-b border-border last:border-0",
1203
1419
  onSelect && "cursor-pointer hover:bg-muted/40"
1204
1420
  ),
1205
- children: columns.map((col) => /* @__PURE__ */ jsx("td", { className: "px-3 py-2 text-foreground", children: renderCell(col, task) }, col))
1421
+ children: columns.map((col) => /* @__PURE__ */ jsx(
1422
+ "td",
1423
+ {
1424
+ className: "px-3 py-2 align-middle text-foreground",
1425
+ children: renderCell(col, task, resolveAssignee)
1426
+ },
1427
+ col
1428
+ ))
1206
1429
  },
1207
1430
  task.id
1208
1431
  ))
@@ -1210,29 +1433,49 @@ function TaskListCore({
1210
1433
  ] }) })
1211
1434
  ] });
1212
1435
  }
1213
- function renderCell(col, task) {
1436
+ function renderCell(col, task, resolveAssignee) {
1214
1437
  switch (col) {
1215
1438
  case "key":
1216
- return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: task.key });
1439
+ return /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-muted-foreground", children: task.key });
1217
1440
  case "title":
1218
- return /* @__PURE__ */ jsx("span", { className: "font-medium", children: task.title });
1441
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
1442
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: task.title }),
1443
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] uppercase text-muted-foreground", children: task.key })
1444
+ ] });
1219
1445
  case "type":
1220
- return /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: enumLabel(task.type) });
1446
+ return /* @__PURE__ */ jsx(TypeBadge, { type: task.type });
1221
1447
  case "status":
1222
- return /* @__PURE__ */ jsx("span", { className: "capitalize text-muted-foreground", children: enumLabel(task.status) });
1448
+ return /* @__PURE__ */ jsx(StatusPill, { status: task.status });
1223
1449
  case "priority":
1224
- return /* @__PURE__ */ jsx(Badge, { variant: priorityVariant[task.priority], children: enumLabel(task.priority) });
1225
- case "assignee":
1226
- return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: task.assigneeId ?? "Unassigned" });
1227
- case "dueDate":
1228
- return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: task.dueDate ?? "\u2014" });
1450
+ return /* @__PURE__ */ jsx(PriorityIndicator, { priority: task.priority, showLabel: true });
1451
+ case "assignee": {
1452
+ const resolved = task.assigneeId ? resolveAssignee?.(task.assigneeId) : null;
1453
+ return /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
1454
+ /* @__PURE__ */ jsx(Avatar, { id: task.assigneeId, name: resolved?.name, size: "sm" }),
1455
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: resolved?.name ?? (task.assigneeId ? "" : "Unassigned") })
1456
+ ] });
1457
+ }
1458
+ case "dueDate": {
1459
+ const due = formatDueDate(task.dueDate);
1460
+ if (!due) return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
1461
+ return /* @__PURE__ */ jsx(
1462
+ "span",
1463
+ {
1464
+ className: cn(
1465
+ "text-xs",
1466
+ isOverdue(task.dueDate, task.status) ? "font-medium text-destructive" : "text-muted-foreground"
1467
+ ),
1468
+ children: due
1469
+ }
1470
+ );
1471
+ }
1229
1472
  case "storyPoints":
1230
- return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: task.storyPoints ?? 0 });
1473
+ return task.storyPoints != null ? /* @__PURE__ */ jsx("span", { className: "flex h-5 min-w-5 items-center justify-center rounded-full bg-primary/10 px-1 text-[10px] font-semibold text-primary", children: task.storyPoints }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
1231
1474
  case "labels":
1232
1475
  return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: (task.labels ?? []).slice(0, 3).map((l) => /* @__PURE__ */ jsx(
1233
1476
  "span",
1234
1477
  {
1235
- className: "rounded bg-muted px-1 py-0.5 text-[10px] text-muted-foreground",
1478
+ className: "rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground",
1236
1479
  children: l
1237
1480
  },
1238
1481
  l
@@ -1287,9 +1530,448 @@ function TaskList({
1287
1530
  }
1288
1531
  );
1289
1532
  }
1533
+ var WEEK_OPTS = { weekStartsOn: 1 };
1534
+ function dayKey(d) {
1535
+ return format(d, "yyyy-MM-dd");
1536
+ }
1537
+ function TaskTimelineCore({
1538
+ projectId,
1539
+ priority = "ALL",
1540
+ type = "ALL",
1541
+ search = "",
1542
+ assigneeId,
1543
+ defaultMode = "week",
1544
+ onSelect,
1545
+ resolveAssignee,
1546
+ className,
1547
+ workspaceId: workspaceIdProp
1548
+ }) {
1549
+ const ctx = usePlanMagnetContextSafe();
1550
+ const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
1551
+ const [mode, setMode] = useState(defaultMode);
1552
+ const [offset, setOffset] = useState(0);
1553
+ const { data, loading, error } = useQuery(
1554
+ LIST_WORK_ITEMS,
1555
+ {
1556
+ variables: {
1557
+ workspaceId,
1558
+ ...projectId ? { filter: { projectId } } : {},
1559
+ pagination: { first: 200 }
1560
+ },
1561
+ skip: !workspaceId
1562
+ }
1563
+ );
1564
+ const tasks = useMemo(() => {
1565
+ const all = data?.planmagnetWorkItems?.items ?? [];
1566
+ const query = search.trim().toLowerCase();
1567
+ return all.filter((t) => {
1568
+ if (priority !== "ALL" && t.priority !== priority) return false;
1569
+ if (type !== "ALL" && t.type !== type) return false;
1570
+ if (assigneeId && t.assigneeId !== assigneeId) return false;
1571
+ if (query) {
1572
+ const hay = [t.title, t.key, ...t.labels ?? []].join(" ").toLowerCase();
1573
+ if (!hay.includes(query)) return false;
1574
+ }
1575
+ return true;
1576
+ });
1577
+ }, [data, priority, type, search, assigneeId]);
1578
+ const { anchor, rangeStart, rangeEnd, days } = useMemo(() => {
1579
+ const base = /* @__PURE__ */ new Date();
1580
+ if (mode === "week") {
1581
+ const a2 = addWeeks(base, offset);
1582
+ const s2 = startOfWeek(a2, WEEK_OPTS);
1583
+ const e2 = endOfWeek(a2, WEEK_OPTS);
1584
+ return {
1585
+ anchor: a2,
1586
+ rangeStart: s2,
1587
+ rangeEnd: e2,
1588
+ days: eachDayOfInterval({ start: s2, end: e2 })
1589
+ };
1590
+ }
1591
+ const a = addMonths(base, offset);
1592
+ const s = startOfWeek(startOfMonth(a), WEEK_OPTS);
1593
+ const e = endOfWeek(endOfMonth(a), WEEK_OPTS);
1594
+ return {
1595
+ anchor: a,
1596
+ rangeStart: s,
1597
+ rangeEnd: e,
1598
+ days: eachDayOfInterval({ start: s, end: e })
1599
+ };
1600
+ }, [mode, offset]);
1601
+ const { byDay, overdue, undated } = useMemo(() => {
1602
+ const map = /* @__PURE__ */ new Map();
1603
+ const od = [];
1604
+ const ud = [];
1605
+ for (const t of tasks) {
1606
+ const d = parseDate(t.dueDate);
1607
+ if (!d) {
1608
+ ud.push(t);
1609
+ continue;
1610
+ }
1611
+ if (isOverdue(t.dueDate, t.status)) {
1612
+ od.push(t);
1613
+ continue;
1614
+ }
1615
+ const k = dayKey(d);
1616
+ const arr = map.get(k);
1617
+ if (arr) arr.push(t);
1618
+ else map.set(k, [t]);
1619
+ }
1620
+ return { byDay: map, overdue: od, undated: ud };
1621
+ }, [tasks]);
1622
+ if (!workspaceId) {
1623
+ return /* @__PURE__ */ jsx("div", { className: cn("p-6 text-sm text-destructive", className), children: "Timeline: no workspaceId provided (prop or PlanMagnetProvider)." });
1624
+ }
1625
+ if (loading && !data) {
1626
+ return /* @__PURE__ */ jsx("div", { className: cn("flex items-center justify-center p-12", className), children: /* @__PURE__ */ jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-2 border-primary border-t-transparent" }) });
1627
+ }
1628
+ if (error && !data) {
1629
+ return /* @__PURE__ */ jsxs("div", { className: cn("p-6 text-sm text-destructive", className), children: [
1630
+ "Error loading timeline: ",
1631
+ error.message
1632
+ ] });
1633
+ }
1634
+ const rangeLabel = mode === "week" ? `${format(rangeStart, "MMM d")} \u2013 ${format(rangeEnd, "MMM d")}` : format(anchor, "MMMM yyyy");
1635
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-3", className), children: [
1636
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
1637
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1638
+ /* @__PURE__ */ jsx(
1639
+ "button",
1640
+ {
1641
+ type: "button",
1642
+ onClick: () => setOffset((o) => o - 1),
1643
+ "aria-label": "Previous",
1644
+ className: "flex h-8 w-8 items-center justify-center rounded-md border border-border text-muted-foreground hover:bg-muted/50",
1645
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
1646
+ }
1647
+ ),
1648
+ /* @__PURE__ */ jsx(
1649
+ "button",
1650
+ {
1651
+ type: "button",
1652
+ onClick: () => setOffset(0),
1653
+ className: "h-8 rounded-md border border-border px-3 text-xs font-medium text-foreground hover:bg-muted/50",
1654
+ children: "Today"
1655
+ }
1656
+ ),
1657
+ /* @__PURE__ */ jsx(
1658
+ "button",
1659
+ {
1660
+ type: "button",
1661
+ onClick: () => setOffset((o) => o + 1),
1662
+ "aria-label": "Next",
1663
+ className: "flex h-8 w-8 items-center justify-center rounded-md border border-border text-muted-foreground hover:bg-muted/50",
1664
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
1665
+ }
1666
+ ),
1667
+ /* @__PURE__ */ jsx("span", { className: "ml-2 text-sm font-medium text-foreground", children: rangeLabel })
1668
+ ] }),
1669
+ /* @__PURE__ */ jsx("div", { className: "inline-flex rounded-md border border-border p-0.5", children: ["week", "month"].map((m) => /* @__PURE__ */ jsx(
1670
+ "button",
1671
+ {
1672
+ type: "button",
1673
+ onClick: () => {
1674
+ setMode(m);
1675
+ setOffset(0);
1676
+ },
1677
+ className: cn(
1678
+ "rounded px-3 py-1 text-xs font-medium capitalize transition-colors",
1679
+ mode === m ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"
1680
+ ),
1681
+ children: m
1682
+ },
1683
+ m
1684
+ )) })
1685
+ ] }),
1686
+ overdue.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2", children: [
1687
+ /* @__PURE__ */ jsxs("p", { className: "mb-1 text-xs font-semibold text-destructive", children: [
1688
+ overdue.length,
1689
+ " overdue"
1690
+ ] }),
1691
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: overdue.map((t) => /* @__PURE__ */ jsx(
1692
+ TaskChip,
1693
+ {
1694
+ task: t,
1695
+ onSelect,
1696
+ resolveAssignee,
1697
+ tone: "destructive"
1698
+ },
1699
+ t.id
1700
+ )) })
1701
+ ] }),
1702
+ mode === "week" ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: days.map((day) => {
1703
+ const items = byDay.get(dayKey(day)) ?? [];
1704
+ const today = isToday(day);
1705
+ return /* @__PURE__ */ jsxs(
1706
+ "div",
1707
+ {
1708
+ className: cn(
1709
+ "flex min-h-32 flex-col rounded-lg border p-2",
1710
+ today ? "border-primary/50 bg-primary/5" : "border-border bg-muted/20"
1711
+ ),
1712
+ children: [
1713
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-baseline justify-between", children: [
1714
+ /* @__PURE__ */ jsx(
1715
+ "span",
1716
+ {
1717
+ className: cn(
1718
+ "text-xs font-semibold uppercase",
1719
+ today ? "text-primary" : "text-muted-foreground"
1720
+ ),
1721
+ children: format(day, "EEE")
1722
+ }
1723
+ ),
1724
+ /* @__PURE__ */ jsx(
1725
+ "span",
1726
+ {
1727
+ className: cn(
1728
+ "text-sm font-semibold",
1729
+ today ? "text-primary" : "text-foreground"
1730
+ ),
1731
+ children: format(day, "d")
1732
+ }
1733
+ )
1734
+ ] }),
1735
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1.5", children: items.map((t) => /* @__PURE__ */ jsx(
1736
+ TaskChip,
1737
+ {
1738
+ task: t,
1739
+ onSelect,
1740
+ resolveAssignee
1741
+ },
1742
+ t.id
1743
+ )) })
1744
+ ]
1745
+ },
1746
+ dayKey(day)
1747
+ );
1748
+ }) }) : /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-7 gap-1", children: [
1749
+ ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].map((d) => /* @__PURE__ */ jsx(
1750
+ "div",
1751
+ {
1752
+ className: "px-1 pb-1 text-center text-[10px] font-semibold uppercase text-muted-foreground",
1753
+ children: d
1754
+ },
1755
+ d
1756
+ )),
1757
+ days.map((day) => {
1758
+ const items = byDay.get(dayKey(day)) ?? [];
1759
+ const today = isToday(day);
1760
+ const inMonth = isSameMonth(day, anchor);
1761
+ return /* @__PURE__ */ jsxs(
1762
+ "div",
1763
+ {
1764
+ className: cn(
1765
+ "flex min-h-20 flex-col gap-1 rounded-md border p-1",
1766
+ today ? "border-primary/50 bg-primary/5" : "border-border/60 bg-muted/10",
1767
+ !inMonth && "opacity-40"
1768
+ ),
1769
+ children: [
1770
+ /* @__PURE__ */ jsx(
1771
+ "span",
1772
+ {
1773
+ className: cn(
1774
+ "text-[10px] font-semibold",
1775
+ today ? "text-primary" : "text-muted-foreground"
1776
+ ),
1777
+ children: format(day, "d")
1778
+ }
1779
+ ),
1780
+ items.slice(0, 3).map((t) => /* @__PURE__ */ jsx(
1781
+ TaskChip,
1782
+ {
1783
+ task: t,
1784
+ onSelect,
1785
+ resolveAssignee,
1786
+ compact: true
1787
+ },
1788
+ t.id
1789
+ )),
1790
+ items.length > 3 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
1791
+ "+",
1792
+ items.length - 3,
1793
+ " more"
1794
+ ] })
1795
+ ]
1796
+ },
1797
+ dayKey(day)
1798
+ );
1799
+ })
1800
+ ] }),
1801
+ undated.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-muted/20 px-3 py-2", children: [
1802
+ /* @__PURE__ */ jsxs("p", { className: "mb-1 text-xs font-semibold text-muted-foreground", children: [
1803
+ "No due date (",
1804
+ undated.length,
1805
+ ")"
1806
+ ] }),
1807
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: undated.map((t) => /* @__PURE__ */ jsx(
1808
+ TaskChip,
1809
+ {
1810
+ task: t,
1811
+ onSelect,
1812
+ resolveAssignee
1813
+ },
1814
+ t.id
1815
+ )) })
1816
+ ] })
1817
+ ] });
1818
+ }
1819
+ function TaskChip({
1820
+ task,
1821
+ onSelect,
1822
+ resolveAssignee,
1823
+ tone,
1824
+ compact
1825
+ }) {
1826
+ const resolved = task.assigneeId ? resolveAssignee?.(task.assigneeId) : null;
1827
+ const interactive = Boolean(onSelect);
1828
+ return /* @__PURE__ */ jsxs(
1829
+ "button",
1830
+ {
1831
+ type: "button",
1832
+ disabled: !interactive,
1833
+ onClick: interactive ? () => onSelect?.(task.id) : void 0,
1834
+ title: task.title,
1835
+ className: cn(
1836
+ "flex w-full items-center gap-1.5 rounded-md border px-1.5 py-1 text-left transition-colors",
1837
+ tone === "destructive" ? "border-destructive/40 bg-card hover:bg-destructive/5" : "border-border bg-card hover:border-primary/50",
1838
+ interactive ? "cursor-pointer" : "cursor-default"
1839
+ ),
1840
+ children: [
1841
+ /* @__PURE__ */ jsx(PriorityIndicator, { priority: task.priority }),
1842
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-[11px] font-medium text-foreground", children: task.title }),
1843
+ !compact && /* @__PURE__ */ jsx(Avatar, { id: task.assigneeId, name: resolved?.name, size: "sm" })
1844
+ ]
1845
+ }
1846
+ );
1847
+ }
1848
+ var VIEW_META = {
1849
+ board: { label: "Board", icon: LayoutGrid },
1850
+ list: { label: "List", icon: List },
1851
+ timeline: { label: "Timeline", icon: CalendarRange }
1852
+ };
1853
+ var SELECT_CLASS = "h-8 rounded-md border border-border bg-background px-2 text-xs font-medium text-foreground capitalize transition-colors hover:bg-muted/50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring";
1854
+ function TaskToolbar({
1855
+ view,
1856
+ onViewChange,
1857
+ views = ["board", "list", "timeline"],
1858
+ search,
1859
+ onSearchChange,
1860
+ priority,
1861
+ onPriorityChange,
1862
+ type,
1863
+ onTypeChange,
1864
+ onNewTask,
1865
+ canCreate,
1866
+ className
1867
+ }) {
1868
+ return /* @__PURE__ */ jsxs(
1869
+ "div",
1870
+ {
1871
+ className: cn(
1872
+ "flex flex-wrap items-center justify-between gap-3 border-b border-border pb-2",
1873
+ className
1874
+ ),
1875
+ children: [
1876
+ /* @__PURE__ */ jsx(
1877
+ "div",
1878
+ {
1879
+ className: "flex items-center gap-1",
1880
+ role: "tablist",
1881
+ "aria-label": "Task views",
1882
+ children: views.map((v) => {
1883
+ const meta = VIEW_META[v];
1884
+ const Icon = meta.icon;
1885
+ const active = v === view;
1886
+ return /* @__PURE__ */ jsxs(
1887
+ "button",
1888
+ {
1889
+ type: "button",
1890
+ role: "tab",
1891
+ "aria-selected": active,
1892
+ onClick: () => onViewChange(v),
1893
+ className: cn(
1894
+ "relative flex items-center gap-1.5 rounded-t-md px-2.5 py-1.5 text-sm font-medium transition-colors",
1895
+ active ? "text-foreground" : "text-muted-foreground hover:text-foreground"
1896
+ ),
1897
+ children: [
1898
+ /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4", "aria-hidden": "true" }),
1899
+ meta.label,
1900
+ active && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-1 -bottom-[9px] h-0.5 rounded-full bg-primary" })
1901
+ ]
1902
+ },
1903
+ v
1904
+ );
1905
+ })
1906
+ }
1907
+ ),
1908
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
1909
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1910
+ /* @__PURE__ */ jsx(
1911
+ Search,
1912
+ {
1913
+ className: "pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground",
1914
+ "aria-hidden": "true"
1915
+ }
1916
+ ),
1917
+ /* @__PURE__ */ jsx(
1918
+ "input",
1919
+ {
1920
+ type: "search",
1921
+ value: search,
1922
+ onChange: (e) => onSearchChange(e.target.value),
1923
+ placeholder: "Search tasks\u2026",
1924
+ "aria-label": "Search tasks",
1925
+ className: "h-8 w-40 rounded-md border border-border bg-background pl-7 pr-2 text-xs text-foreground placeholder:text-muted-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
1926
+ }
1927
+ )
1928
+ ] }),
1929
+ /* @__PURE__ */ jsxs(
1930
+ "select",
1931
+ {
1932
+ value: priority,
1933
+ onChange: (e) => onPriorityChange(e.target.value),
1934
+ "aria-label": "Filter by priority",
1935
+ className: SELECT_CLASS,
1936
+ children: [
1937
+ /* @__PURE__ */ jsx("option", { value: "ALL", children: "All priorities" }),
1938
+ TASK_PRIORITIES.map((p) => /* @__PURE__ */ jsx("option", { value: p, children: enumLabel(p) }, p))
1939
+ ]
1940
+ }
1941
+ ),
1942
+ /* @__PURE__ */ jsxs(
1943
+ "select",
1944
+ {
1945
+ value: type,
1946
+ onChange: (e) => onTypeChange(e.target.value),
1947
+ "aria-label": "Filter by type",
1948
+ className: SELECT_CLASS,
1949
+ children: [
1950
+ /* @__PURE__ */ jsx("option", { value: "ALL", children: "All types" }),
1951
+ TASK_TYPES.map((t) => /* @__PURE__ */ jsx("option", { value: t, children: enumLabel(t) }, t))
1952
+ ]
1953
+ }
1954
+ ),
1955
+ canCreate && onNewTask && /* @__PURE__ */ jsxs(
1956
+ "button",
1957
+ {
1958
+ type: "button",
1959
+ onClick: onNewTask,
1960
+ className: "flex h-8 items-center gap-1.5 rounded-md bg-primary px-3 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90",
1961
+ children: [
1962
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4", "aria-hidden": "true" }),
1963
+ "New task"
1964
+ ]
1965
+ }
1966
+ )
1967
+ ] })
1968
+ ]
1969
+ }
1970
+ );
1971
+ }
1290
1972
  var fieldClass2 = "w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring";
1291
1973
  var labelClass2 = "text-xs font-medium uppercase tracking-wide text-muted-foreground";
1292
- var priorityVariant2 = {
1974
+ var priorityVariant = {
1293
1975
  NONE: "muted",
1294
1976
  LOW: "muted",
1295
1977
  MEDIUM: "secondary",
@@ -1412,7 +2094,7 @@ function TaskDetailCore({
1412
2094
  !editing ? /* @__PURE__ */ jsxs("dl", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
1413
2095
  !hidden.has("type") && /* @__PURE__ */ jsx(Field, { label: "Type", children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: enumLabel(item.type) }) }),
1414
2096
  !hidden.has("status") && /* @__PURE__ */ jsx(Field, { label: "Status", children: /* @__PURE__ */ jsx("span", { className: "capitalize text-foreground", children: enumLabel(item.status) }) }),
1415
- !hidden.has("priority") && /* @__PURE__ */ jsx(Field, { label: "Priority", children: /* @__PURE__ */ jsx(Badge, { variant: priorityVariant2[item.priority], children: enumLabel(item.priority) }) }),
2097
+ !hidden.has("priority") && /* @__PURE__ */ jsx(Field, { label: "Priority", children: /* @__PURE__ */ jsx(Badge, { variant: priorityVariant[item.priority], children: enumLabel(item.priority) }) }),
1416
2098
  !hidden.has("assignee") && /* @__PURE__ */ jsx(Field, { label: "Assignee", children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: item.assigneeId ?? "Unassigned" }) }),
1417
2099
  !hidden.has("dueDate") && /* @__PURE__ */ jsx(Field, { label: "Due date", children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: item.dueDate ?? "\u2014" }) }),
1418
2100
  !hidden.has("storyPoints") && /* @__PURE__ */ jsx(Field, { label: "Story points", children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: item.storyPoints ?? 0 }) }),
@@ -1622,7 +2304,9 @@ function TasksPageCore({
1622
2304
  columns,
1623
2305
  readonly,
1624
2306
  hiddenFields,
1625
- hideFilters,
2307
+ views,
2308
+ defaultView = "board",
2309
+ resolveAssignee,
1626
2310
  header,
1627
2311
  onTaskSaved,
1628
2312
  createSurface = "modal",
@@ -1632,6 +2316,10 @@ function TasksPageCore({
1632
2316
  const ctx = usePlanMagnetContextSafe();
1633
2317
  const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
1634
2318
  const client = useApolloClient();
2319
+ const [view, setView] = useState(defaultView);
2320
+ const [search, setSearch] = useState("");
2321
+ const [priority, setPriority] = useState("ALL");
2322
+ const [type, setType] = useState("ALL");
1635
2323
  const [creating, setCreating] = useState(false);
1636
2324
  const [selectedId, setSelectedId] = useState(null);
1637
2325
  const refetchBoard = useCallback(() => {
@@ -1656,6 +2344,7 @@ function TasksPageCore({
1656
2344
  const handleTaskClick = useCallback((task) => {
1657
2345
  setSelectedId(task.id);
1658
2346
  }, []);
2347
+ const handleSelect = useCallback((id) => setSelectedId(id), []);
1659
2348
  if (!workspaceId) {
1660
2349
  return /* @__PURE__ */ jsx("div", { className: cn("p-6 text-sm text-destructive", className), children: "TasksPage: no workspaceId provided (prop or PlanMagnetProvider)." });
1661
2350
  }
@@ -1681,34 +2370,69 @@ function TasksPageCore({
1681
2370
  onUpdated: handleUpdated
1682
2371
  }
1683
2372
  ) : null;
1684
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
1685
- (header || canCreate) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4 px-6 pt-6", children: [
1686
- /* @__PURE__ */ jsx("div", { className: "min-w-0", children: typeof header === "string" ? /* @__PURE__ */ jsx("h1", { className: "truncate text-2xl font-semibold text-foreground", children: header }) : header }),
1687
- canCreate && /* @__PURE__ */ jsx(
1688
- "button",
1689
- {
1690
- type: "button",
1691
- onClick: () => setCreating(true),
1692
- className: "shrink-0 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90",
1693
- children: "New task"
1694
- }
1695
- )
1696
- ] }),
1697
- showInlineCreate && /* @__PURE__ */ jsx("div", { className: "px-6", children: createForm }),
2373
+ const listPriority = priority === "ALL" ? void 0 : priority;
2374
+ const listType = type === "ALL" ? void 0 : type;
2375
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4 p-6", className), children: [
2376
+ header && /* @__PURE__ */ jsx("div", { className: "min-w-0", children: typeof header === "string" ? /* @__PURE__ */ jsx("h1", { className: "truncate text-2xl font-semibold text-foreground", children: header }) : header }),
1698
2377
  /* @__PURE__ */ jsx(
2378
+ TaskToolbar,
2379
+ {
2380
+ view,
2381
+ onViewChange: setView,
2382
+ views,
2383
+ search,
2384
+ onSearchChange: setSearch,
2385
+ priority,
2386
+ onPriorityChange: setPriority,
2387
+ type,
2388
+ onTypeChange: setType,
2389
+ onNewTask: () => setCreating(true),
2390
+ canCreate
2391
+ }
2392
+ ),
2393
+ showInlineCreate && /* @__PURE__ */ jsx("div", { children: createForm }),
2394
+ view === "board" && /* @__PURE__ */ jsx(
1699
2395
  TaskBoardCore,
1700
2396
  {
1701
2397
  workspaceId,
1702
2398
  projectId: boardProjectId,
1703
2399
  columns,
1704
2400
  readonly,
1705
- hideHeader: Boolean(header),
1706
- hideFilters,
2401
+ hideHeader: true,
2402
+ priority,
2403
+ type,
2404
+ search,
1707
2405
  hiddenFields,
2406
+ resolveAssignee,
1708
2407
  onTaskClick: handleTaskClick
1709
2408
  }
1710
2409
  ),
1711
- showInlineDetail && /* @__PURE__ */ jsx("div", { className: "px-6 pb-6", children: detailView }),
2410
+ view === "list" && /* @__PURE__ */ jsx(
2411
+ TaskListCore,
2412
+ {
2413
+ workspaceId,
2414
+ projectId: boardProjectId,
2415
+ priority: listPriority,
2416
+ type: listType,
2417
+ search: search || void 0,
2418
+ hideHeader: true,
2419
+ resolveAssignee,
2420
+ onSelect: handleSelect
2421
+ }
2422
+ ),
2423
+ view === "timeline" && /* @__PURE__ */ jsx(
2424
+ TaskTimelineCore,
2425
+ {
2426
+ workspaceId,
2427
+ projectId: boardProjectId,
2428
+ priority,
2429
+ type,
2430
+ search,
2431
+ resolveAssignee,
2432
+ onSelect: handleSelect
2433
+ }
2434
+ ),
2435
+ showInlineDetail && /* @__PURE__ */ jsx("div", { className: "pb-6", children: detailView }),
1712
2436
  createSurface === "modal" && /* @__PURE__ */ jsx(
1713
2437
  Modal,
1714
2438
  {
@@ -1759,6 +2483,6 @@ function TasksPage({
1759
2483
  );
1760
2484
  }
1761
2485
 
1762
- export { Badge, CREATE_WORK_ITEM, CreateTask, CreateTaskCore, DEFAULT_BOARD_COLUMNS, Drawer, GET_PROJECTS, GET_WORK_ITEM, LIST_WORK_ITEMS, MOVE_WORK_ITEM, Modal, PROJECT_SUMMARY_FIELDS, PlanMagnetProvider, TASK_PRIORITIES, TASK_STATUSES, TASK_TYPES, TaskBoard, TaskBoardCore, TaskDetail, TaskDetailCore, TaskList, TaskListCore, TasksPage, TasksPageCore, UPDATE_WORK_ITEM, WORK_ITEM_FIELDS, WORK_ITEM_SUMMARY_FIELDS, badgeVariants, cn, config, createPlanMagnetClient, enumLabel, getGraphQLEndpoint, getPlanMagnetClient, getPlanMagnetClientSafe, getPlanMagnetConfig, isPlanMagnetClientReady, resetPlanMagnetClient, resetPlanMagnetConfig, setPlanMagnetClient, setPlanMagnetConfig, usePlanMagnetContext, usePlanMagnetContextSafe, validateTrustedOrigin, waitForPlanMagnetClient };
2486
+ export { Avatar, Badge, CREATE_WORK_ITEM, CreateTask, CreateTaskCore, DEFAULT_BOARD_COLUMNS, Drawer, GET_PROJECTS, GET_WORK_ITEM, LIST_WORK_ITEMS, MOVE_WORK_ITEM, Modal, PRIORITY_META, PROJECT_SUMMARY_FIELDS, PlanMagnetProvider, PriorityIndicator, STATUS_META, StatusIcon, StatusPill, TASK_PRIORITIES, TASK_STATUSES, TASK_TYPES, TaskBoard, TaskBoardCore, TaskCard, TaskDetail, TaskDetailCore, TaskList, TaskListCore, TaskTimelineCore, TaskToolbar, TasksPage, TasksPageCore, TypeBadge, UPDATE_WORK_ITEM, WORK_ITEM_FIELDS, WORK_ITEM_SUMMARY_FIELDS, badgeVariants, cn, config, createPlanMagnetClient, enumLabel, getGraphQLEndpoint, getPlanMagnetClient, getPlanMagnetClientSafe, getPlanMagnetConfig, isPlanMagnetClientReady, resetPlanMagnetClient, resetPlanMagnetConfig, setPlanMagnetClient, setPlanMagnetConfig, usePlanMagnetContext, usePlanMagnetContextSafe, validateTrustedOrigin, waitForPlanMagnetClient };
1763
2487
  //# sourceMappingURL=index.js.map
1764
2488
  //# sourceMappingURL=index.js.map