@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.d.ts +214 -36
- package/dist/index.js +1054 -330
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { createContext, useMemo, useEffect, useContext, useState, useCallback } from 'react';
|
|
2
|
-
import { ApolloProvider,
|
|
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
|
|
452
|
-
BACKLOG: "
|
|
453
|
-
TODO: "
|
|
454
|
-
IN_PROGRESS: "
|
|
455
|
-
IN_REVIEW: "
|
|
456
|
-
TESTING: "
|
|
457
|
-
DONE: "
|
|
458
|
-
CANCELLED: "
|
|
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
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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
|
|
804
|
+
readonly,
|
|
478
805
|
hideHeader,
|
|
479
|
-
|
|
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
|
|
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
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
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__ */
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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: "
|
|
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 (!
|
|
1149
|
-
const dir =
|
|
1150
|
-
|
|
1151
|
-
|
|
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-
|
|
1165
|
-
!hideHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-
|
|
1166
|
-
/* @__PURE__ */
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
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-
|
|
1180
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border bg-muted/50 text-left", children: columns.map((col) =>
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
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-
|
|
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(
|
|
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__ */
|
|
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(
|
|
1446
|
+
return /* @__PURE__ */ jsx(TypeBadge, { type: task.type });
|
|
1221
1447
|
case "status":
|
|
1222
|
-
return /* @__PURE__ */ jsx(
|
|
1448
|
+
return /* @__PURE__ */ jsx(StatusPill, { status: task.status });
|
|
1223
1449
|
case "priority":
|
|
1224
|
-
return /* @__PURE__ */ jsx(
|
|
1225
|
-
case "assignee":
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
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-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
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:
|
|
1706
|
-
|
|
2401
|
+
hideHeader: true,
|
|
2402
|
+
priority,
|
|
2403
|
+
type,
|
|
2404
|
+
search,
|
|
1707
2405
|
hiddenFields,
|
|
2406
|
+
resolveAssignee,
|
|
1708
2407
|
onTaskClick: handleTaskClick
|
|
1709
2408
|
}
|
|
1710
2409
|
),
|
|
1711
|
-
|
|
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
|