@burdenoff/planmagnet-fe-sdk 2026.703.1 → 2026.703.3
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 +362 -67
- package/dist/index.js +1248 -364
- 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, useQuery,
|
|
2
|
+
import { ApolloProvider, useQuery, useApolloClient, useMutation } 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, Link2, 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() {
|
|
@@ -409,9 +410,169 @@ var MOVE_WORK_ITEM = gql`
|
|
|
409
410
|
}
|
|
410
411
|
${WORK_ITEM_FIELDS}
|
|
411
412
|
`;
|
|
413
|
+
var WORK_ITEM_LINK_FIELDS = gql`
|
|
414
|
+
fragment WorkItemLinkFields on WorkItemLink {
|
|
415
|
+
id
|
|
416
|
+
workspaceId
|
|
417
|
+
workItemId
|
|
418
|
+
sourceProduct
|
|
419
|
+
entityType
|
|
420
|
+
entityId
|
|
421
|
+
relationType
|
|
422
|
+
displayNameSnapshot
|
|
423
|
+
deepLinkSnapshot
|
|
424
|
+
metadata
|
|
425
|
+
createdBy
|
|
426
|
+
onBehalfOf
|
|
427
|
+
createdAt
|
|
428
|
+
updatedAt
|
|
429
|
+
}
|
|
430
|
+
`;
|
|
431
|
+
var LIST_WORK_ITEM_LINKS = gql`
|
|
432
|
+
query ListWorkItemLinks(
|
|
433
|
+
$workspaceId: ID!
|
|
434
|
+
$filter: PlanmagnetWorkItemLinkFilter
|
|
435
|
+
$pagination: PlanmagnetPaginationInput
|
|
436
|
+
) {
|
|
437
|
+
planmagnetWorkItemLinks(
|
|
438
|
+
workspaceId: $workspaceId
|
|
439
|
+
filter: $filter
|
|
440
|
+
pagination: $pagination
|
|
441
|
+
) {
|
|
442
|
+
items {
|
|
443
|
+
...WorkItemLinkFields
|
|
444
|
+
}
|
|
445
|
+
pageInfo {
|
|
446
|
+
hasNextPage
|
|
447
|
+
hasPreviousPage
|
|
448
|
+
startCursor
|
|
449
|
+
endCursor
|
|
450
|
+
totalCount
|
|
451
|
+
}
|
|
452
|
+
totalCount
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
${WORK_ITEM_LINK_FIELDS}
|
|
456
|
+
`;
|
|
457
|
+
var CREATE_WORK_ITEM_LINK = gql`
|
|
458
|
+
mutation CreateWorkItemLink($input: PlanmagnetCreateWorkItemLinkInput!) {
|
|
459
|
+
planmagnetLinkWorkItem(input: $input) {
|
|
460
|
+
...WorkItemLinkFields
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
${WORK_ITEM_LINK_FIELDS}
|
|
464
|
+
`;
|
|
465
|
+
var DELETE_WORK_ITEM_LINK = gql`
|
|
466
|
+
mutation UnlinkWorkItem($id: ID!) {
|
|
467
|
+
planmagnetUnlinkWorkItem(id: $id)
|
|
468
|
+
}
|
|
469
|
+
`;
|
|
412
470
|
function cn(...inputs) {
|
|
413
471
|
return twMerge(clsx(inputs));
|
|
414
472
|
}
|
|
473
|
+
var CLOSED_STATUSES = /* @__PURE__ */ new Set(["DONE", "CANCELLED"]);
|
|
474
|
+
function parseDate(iso) {
|
|
475
|
+
if (!iso) return null;
|
|
476
|
+
const d = parseISO(iso);
|
|
477
|
+
return isValid(d) ? d : null;
|
|
478
|
+
}
|
|
479
|
+
function formatDueDate(iso) {
|
|
480
|
+
const d = parseDate(iso);
|
|
481
|
+
if (!d) return null;
|
|
482
|
+
const now = /* @__PURE__ */ new Date();
|
|
483
|
+
return format(
|
|
484
|
+
d,
|
|
485
|
+
d.getFullYear() === now.getFullYear() ? "MMM d" : "MMM d yyyy"
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
function isOverdue(iso, status) {
|
|
489
|
+
const d = parseDate(iso);
|
|
490
|
+
if (!d) return false;
|
|
491
|
+
if (status && CLOSED_STATUSES.has(status)) return false;
|
|
492
|
+
return startOfDay(d).getTime() < startOfDay(/* @__PURE__ */ new Date()).getTime();
|
|
493
|
+
}
|
|
494
|
+
var badgeVariants = cva(
|
|
495
|
+
"inline-flex items-center rounded-full px-2 py-0.5 text-[10px] font-medium capitalize transition-colors",
|
|
496
|
+
{
|
|
497
|
+
variants: {
|
|
498
|
+
variant: {
|
|
499
|
+
default: "bg-primary/10 text-primary",
|
|
500
|
+
muted: "bg-muted text-muted-foreground",
|
|
501
|
+
secondary: "bg-secondary text-secondary-foreground",
|
|
502
|
+
accent: "bg-accent text-accent-foreground",
|
|
503
|
+
destructive: "bg-destructive/15 text-destructive"
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
defaultVariants: {
|
|
507
|
+
variant: "default"
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
);
|
|
511
|
+
function Badge({ className, variant, ...props }) {
|
|
512
|
+
return /* @__PURE__ */ jsx("span", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
513
|
+
}
|
|
514
|
+
var TINTS = [
|
|
515
|
+
"bg-primary/15 text-primary",
|
|
516
|
+
"bg-secondary text-secondary-foreground",
|
|
517
|
+
"bg-accent text-accent-foreground",
|
|
518
|
+
"bg-muted text-muted-foreground",
|
|
519
|
+
"bg-destructive/15 text-destructive"
|
|
520
|
+
];
|
|
521
|
+
var SIZES = {
|
|
522
|
+
sm: "h-5 w-5 text-[9px]",
|
|
523
|
+
md: "h-6 w-6 text-[10px]",
|
|
524
|
+
lg: "h-8 w-8 text-xs"
|
|
525
|
+
};
|
|
526
|
+
function tintIndex(seed) {
|
|
527
|
+
let h = 0;
|
|
528
|
+
for (let i = 0; i < seed.length; i++) {
|
|
529
|
+
h = h * 31 + seed.charCodeAt(i) >>> 0;
|
|
530
|
+
}
|
|
531
|
+
return h % TINTS.length;
|
|
532
|
+
}
|
|
533
|
+
function initials(name, id) {
|
|
534
|
+
const source = (name ?? "").trim();
|
|
535
|
+
if (source) {
|
|
536
|
+
const parts = source.split(/\s+/);
|
|
537
|
+
const first = parts[0]?.[0] ?? "";
|
|
538
|
+
const second = parts.length > 1 ? parts[parts.length - 1]?.[0] ?? "" : "";
|
|
539
|
+
return (first + second).toUpperCase() || "?";
|
|
540
|
+
}
|
|
541
|
+
const raw = (id ?? "").replace(/[^a-zA-Z0-9]/g, "");
|
|
542
|
+
return raw.slice(0, 2).toUpperCase() || "?";
|
|
543
|
+
}
|
|
544
|
+
function Avatar({ id, name, size = "md", className }) {
|
|
545
|
+
if (!id && !name) {
|
|
546
|
+
return /* @__PURE__ */ jsx(
|
|
547
|
+
"span",
|
|
548
|
+
{
|
|
549
|
+
className: cn(
|
|
550
|
+
"inline-flex items-center justify-center rounded-full border border-dashed border-border text-muted-foreground",
|
|
551
|
+
SIZES[size],
|
|
552
|
+
className
|
|
553
|
+
),
|
|
554
|
+
title: "Unassigned",
|
|
555
|
+
"aria-label": "Unassigned",
|
|
556
|
+
children: /* @__PURE__ */ jsx(User, { className: "h-1/2 w-1/2", "aria-hidden": "true" })
|
|
557
|
+
}
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
const tint = TINTS[tintIndex(name || id || "")];
|
|
561
|
+
return /* @__PURE__ */ jsx(
|
|
562
|
+
"span",
|
|
563
|
+
{
|
|
564
|
+
className: cn(
|
|
565
|
+
"inline-flex items-center justify-center rounded-full font-medium",
|
|
566
|
+
SIZES[size],
|
|
567
|
+
tint,
|
|
568
|
+
className
|
|
569
|
+
),
|
|
570
|
+
title: name ?? id ?? void 0,
|
|
571
|
+
"aria-label": name ?? id ?? void 0,
|
|
572
|
+
children: initials(name, id)
|
|
573
|
+
}
|
|
574
|
+
);
|
|
575
|
+
}
|
|
415
576
|
|
|
416
577
|
// src/types/index.ts
|
|
417
578
|
var DEFAULT_BOARD_COLUMNS = [
|
|
@@ -448,47 +609,341 @@ var TASK_STATUSES = [
|
|
|
448
609
|
function enumLabel(value) {
|
|
449
610
|
return value.toLowerCase().replace(/_/g, " ");
|
|
450
611
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
612
|
+
function LinkChip({ link, renderIcon, className }) {
|
|
613
|
+
const label = link.displayNameSnapshot || enumLabel(link.entityType);
|
|
614
|
+
const icon = renderIcon?.(link.entityType) ?? /* @__PURE__ */ jsx(Link2, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" });
|
|
615
|
+
const body = /* @__PURE__ */ jsxs(
|
|
616
|
+
"span",
|
|
617
|
+
{
|
|
618
|
+
className: cn(
|
|
619
|
+
"inline-flex max-w-full items-center gap-1 rounded-full border border-primary/20 bg-primary/5 px-1.5 py-0.5 text-[10px] font-medium text-primary",
|
|
620
|
+
className
|
|
621
|
+
),
|
|
622
|
+
title: `${enumLabel(link.relationType)} \xB7 ${enumLabel(link.entityType)}`,
|
|
623
|
+
children: [
|
|
624
|
+
icon,
|
|
625
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: label })
|
|
626
|
+
]
|
|
627
|
+
}
|
|
628
|
+
);
|
|
629
|
+
if (link.deepLinkSnapshot) {
|
|
630
|
+
return /* @__PURE__ */ jsx(
|
|
631
|
+
"a",
|
|
632
|
+
{
|
|
633
|
+
href: link.deepLinkSnapshot,
|
|
634
|
+
onClick: (e) => e.stopPropagation(),
|
|
635
|
+
className: "inline-flex max-w-full no-underline hover:brightness-95",
|
|
636
|
+
children: body
|
|
637
|
+
}
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
return body;
|
|
641
|
+
}
|
|
642
|
+
var STATUS_META = {
|
|
643
|
+
BACKLOG: { icon: CircleDashed, tone: "text-muted-foreground/60" },
|
|
644
|
+
TODO: { icon: Circle, tone: "text-muted-foreground" },
|
|
645
|
+
IN_PROGRESS: { icon: CircleDot, tone: "text-primary" },
|
|
646
|
+
IN_REVIEW: { icon: CircleDotDashed, tone: "text-accent-foreground" },
|
|
647
|
+
TESTING: { icon: CircleDotDashed, tone: "text-secondary-foreground" },
|
|
648
|
+
DONE: { icon: CircleCheckBig, tone: "text-primary" },
|
|
649
|
+
CANCELLED: { icon: CircleX, tone: "text-destructive/70" }
|
|
459
650
|
};
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
651
|
+
function StatusIcon({ status, className }) {
|
|
652
|
+
const meta = STATUS_META[status] ?? STATUS_META.TODO;
|
|
653
|
+
const Icon = meta.icon;
|
|
654
|
+
return /* @__PURE__ */ jsx(
|
|
655
|
+
Icon,
|
|
656
|
+
{
|
|
657
|
+
className: cn("h-3.5 w-3.5 shrink-0", meta.tone, className),
|
|
658
|
+
"aria-hidden": "true"
|
|
659
|
+
}
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
function StatusPill({ status, className }) {
|
|
663
|
+
return /* @__PURE__ */ jsxs(
|
|
664
|
+
"span",
|
|
665
|
+
{
|
|
666
|
+
className: cn(
|
|
667
|
+
"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",
|
|
668
|
+
className
|
|
669
|
+
),
|
|
670
|
+
children: [
|
|
671
|
+
/* @__PURE__ */ jsx(StatusIcon, { status }),
|
|
672
|
+
enumLabel(status)
|
|
673
|
+
]
|
|
674
|
+
}
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
var PRIORITY_META = {
|
|
678
|
+
NONE: { level: 0, tone: "text-muted-foreground/50" },
|
|
679
|
+
LOW: { level: 1, tone: "text-muted-foreground" },
|
|
680
|
+
MEDIUM: { level: 2, tone: "text-accent-foreground" },
|
|
681
|
+
HIGH: { level: 3, tone: "text-accent-foreground" },
|
|
682
|
+
CRITICAL: { level: 3, tone: "text-destructive" }
|
|
466
683
|
};
|
|
467
|
-
var
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
684
|
+
var BAR_HEIGHTS = ["h-1.5", "h-2.5", "h-3.5"];
|
|
685
|
+
function PriorityIndicator({
|
|
686
|
+
priority,
|
|
687
|
+
showLabel = false,
|
|
688
|
+
className
|
|
689
|
+
}) {
|
|
690
|
+
const meta = PRIORITY_META[priority] ?? PRIORITY_META.NONE;
|
|
691
|
+
return /* @__PURE__ */ jsxs(
|
|
692
|
+
"span",
|
|
693
|
+
{
|
|
694
|
+
className: cn("inline-flex items-center gap-1.5", meta.tone, className),
|
|
695
|
+
title: `Priority: ${enumLabel(priority)}`,
|
|
696
|
+
children: [
|
|
697
|
+
/* @__PURE__ */ jsx("span", { className: "flex items-end gap-0.5", "aria-hidden": "true", children: BAR_HEIGHTS.map((h, i) => /* @__PURE__ */ jsx(
|
|
698
|
+
"span",
|
|
699
|
+
{
|
|
700
|
+
className: cn(
|
|
701
|
+
"w-1 rounded-[1px]",
|
|
702
|
+
h,
|
|
703
|
+
i < meta.level ? "bg-current" : "bg-current/25"
|
|
704
|
+
)
|
|
705
|
+
},
|
|
706
|
+
h
|
|
707
|
+
)) }),
|
|
708
|
+
showLabel && /* @__PURE__ */ jsx("span", { className: "text-xs font-medium capitalize", children: enumLabel(priority) })
|
|
709
|
+
]
|
|
710
|
+
}
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
var TYPE_VARIANT = {
|
|
714
|
+
TASK: "secondary",
|
|
715
|
+
STORY: "default",
|
|
716
|
+
BUG: "destructive",
|
|
717
|
+
EPIC: "accent",
|
|
718
|
+
SUB_TASK: "muted"
|
|
473
719
|
};
|
|
720
|
+
function TypeBadge({ type, className }) {
|
|
721
|
+
return /* @__PURE__ */ jsx(Badge, { variant: TYPE_VARIANT[type], className, children: enumLabel(type) });
|
|
722
|
+
}
|
|
723
|
+
function OverlayShell({
|
|
724
|
+
open,
|
|
725
|
+
onClose,
|
|
726
|
+
children,
|
|
727
|
+
ariaLabel,
|
|
728
|
+
className,
|
|
729
|
+
variant
|
|
730
|
+
}) {
|
|
731
|
+
useEffect(() => {
|
|
732
|
+
if (!open) return;
|
|
733
|
+
const onKeyDown = (e) => {
|
|
734
|
+
if (e.key === "Escape") onClose();
|
|
735
|
+
};
|
|
736
|
+
document.addEventListener("keydown", onKeyDown);
|
|
737
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
738
|
+
}, [open, onClose]);
|
|
739
|
+
if (!open) return null;
|
|
740
|
+
const isModal = variant === "modal";
|
|
741
|
+
return /* @__PURE__ */ jsx(
|
|
742
|
+
"div",
|
|
743
|
+
{
|
|
744
|
+
className: cn(
|
|
745
|
+
"fixed inset-0 z-50 flex bg-foreground/40",
|
|
746
|
+
isModal ? "items-center justify-center p-4" : "justify-end"
|
|
747
|
+
),
|
|
748
|
+
onClick: onClose,
|
|
749
|
+
children: /* @__PURE__ */ jsxs(
|
|
750
|
+
"div",
|
|
751
|
+
{
|
|
752
|
+
role: "dialog",
|
|
753
|
+
"aria-modal": "true",
|
|
754
|
+
"aria-label": ariaLabel,
|
|
755
|
+
className: cn(
|
|
756
|
+
"relative overflow-y-auto bg-background text-foreground shadow-lg",
|
|
757
|
+
isModal ? "max-h-[90vh] w-full max-w-2xl rounded-lg" : "h-full w-full max-w-lg",
|
|
758
|
+
className
|
|
759
|
+
),
|
|
760
|
+
onClick: (e) => e.stopPropagation(),
|
|
761
|
+
children: [
|
|
762
|
+
/* @__PURE__ */ jsx(
|
|
763
|
+
"button",
|
|
764
|
+
{
|
|
765
|
+
type: "button",
|
|
766
|
+
"aria-label": "Close",
|
|
767
|
+
onClick: onClose,
|
|
768
|
+
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",
|
|
769
|
+
children: "\u2715"
|
|
770
|
+
}
|
|
771
|
+
),
|
|
772
|
+
children
|
|
773
|
+
]
|
|
774
|
+
}
|
|
775
|
+
)
|
|
776
|
+
}
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
function Modal(props) {
|
|
780
|
+
return /* @__PURE__ */ jsx(OverlayShell, { variant: "modal", ...props });
|
|
781
|
+
}
|
|
782
|
+
function Drawer(props) {
|
|
783
|
+
return /* @__PURE__ */ jsx(OverlayShell, { variant: "drawer", ...props });
|
|
784
|
+
}
|
|
785
|
+
var EMPTY = /* @__PURE__ */ new Set();
|
|
786
|
+
function TaskCard({
|
|
787
|
+
task,
|
|
788
|
+
hidden = EMPTY,
|
|
789
|
+
onClick,
|
|
790
|
+
draggable,
|
|
791
|
+
onDragStart,
|
|
792
|
+
onDragEnd,
|
|
793
|
+
dragging,
|
|
794
|
+
resolveAssignee,
|
|
795
|
+
links,
|
|
796
|
+
renderLinkIcon,
|
|
797
|
+
className
|
|
798
|
+
}) {
|
|
799
|
+
const overdue = isOverdue(task.dueDate, task.status);
|
|
800
|
+
const due = formatDueDate(task.dueDate);
|
|
801
|
+
const resolved = task.assigneeId ? resolveAssignee?.(task.assigneeId) : null;
|
|
802
|
+
const showTopRow = !hidden.has("key") || !hidden.has("type");
|
|
803
|
+
const showLinks = !hidden.has("links") && (links?.length ?? 0) > 0;
|
|
804
|
+
const showLabels = !hidden.has("labels") && (task.labels?.length ?? 0) > 0;
|
|
805
|
+
const showPriority = !hidden.has("priority");
|
|
806
|
+
const showPoints = !hidden.has("storyPoints") && task.storyPoints != null;
|
|
807
|
+
const showMetaRow = showPriority || showPoints;
|
|
808
|
+
const showAssignee = !hidden.has("assignee");
|
|
809
|
+
const showDue = !hidden.has("dueDate") && Boolean(due);
|
|
810
|
+
const showPeopleRow = showAssignee || showDue;
|
|
811
|
+
const interactive = Boolean(onClick);
|
|
812
|
+
const handleKey = onClick ? (e) => {
|
|
813
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
814
|
+
e.preventDefault();
|
|
815
|
+
onClick(task);
|
|
816
|
+
}
|
|
817
|
+
} : void 0;
|
|
818
|
+
return /* @__PURE__ */ jsxs(
|
|
819
|
+
"div",
|
|
820
|
+
{
|
|
821
|
+
role: interactive ? "button" : void 0,
|
|
822
|
+
tabIndex: interactive ? 0 : void 0,
|
|
823
|
+
draggable,
|
|
824
|
+
onDragStart: draggable ? (e) => onDragStart?.(task, e) : void 0,
|
|
825
|
+
onDragEnd: draggable ? (e) => onDragEnd?.(task, e) : void 0,
|
|
826
|
+
onClick: onClick ? () => onClick(task) : void 0,
|
|
827
|
+
onKeyDown: handleKey,
|
|
828
|
+
className: cn(
|
|
829
|
+
"group rounded-lg border border-border bg-card p-2.5 shadow-sm transition",
|
|
830
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
831
|
+
interactive && "cursor-pointer hover:border-primary/50 hover:shadow-md",
|
|
832
|
+
draggable && "active:cursor-grabbing",
|
|
833
|
+
dragging && "opacity-50 ring-2 ring-primary/40",
|
|
834
|
+
className
|
|
835
|
+
),
|
|
836
|
+
children: [
|
|
837
|
+
showTopRow && /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [
|
|
838
|
+
!hidden.has("key") ? /* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] uppercase tracking-wide text-muted-foreground", children: task.key }) : /* @__PURE__ */ jsx("span", {}),
|
|
839
|
+
!hidden.has("type") && /* @__PURE__ */ jsx(TypeBadge, { type: task.type })
|
|
840
|
+
] }),
|
|
841
|
+
/* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-sm font-medium leading-snug text-foreground", children: task.title }),
|
|
842
|
+
showLinks && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex flex-wrap gap-1", children: [
|
|
843
|
+
links.slice(0, 3).map((link) => /* @__PURE__ */ jsx(LinkChip, { link, renderIcon: renderLinkIcon }, link.id)),
|
|
844
|
+
links.length > 3 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
845
|
+
"+",
|
|
846
|
+
links.length - 3
|
|
847
|
+
] })
|
|
848
|
+
] }),
|
|
849
|
+
showLabels && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex flex-wrap gap-1", children: [
|
|
850
|
+
task.labels.slice(0, 3).map((l) => /* @__PURE__ */ jsx(
|
|
851
|
+
"span",
|
|
852
|
+
{
|
|
853
|
+
className: "rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground",
|
|
854
|
+
children: l
|
|
855
|
+
},
|
|
856
|
+
l
|
|
857
|
+
)),
|
|
858
|
+
task.labels.length > 3 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
859
|
+
"+",
|
|
860
|
+
task.labels.length - 3
|
|
861
|
+
] })
|
|
862
|
+
] }),
|
|
863
|
+
showMetaRow && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-between", children: [
|
|
864
|
+
showPriority ? /* @__PURE__ */ jsx(PriorityIndicator, { priority: task.priority, showLabel: true }) : /* @__PURE__ */ jsx("span", {}),
|
|
865
|
+
showPoints && /* @__PURE__ */ jsx(
|
|
866
|
+
"span",
|
|
867
|
+
{
|
|
868
|
+
className: "flex h-5 min-w-5 items-center justify-center rounded-full bg-primary/10 px-1 text-[10px] font-semibold text-primary",
|
|
869
|
+
title: `${task.storyPoints} story points`,
|
|
870
|
+
children: task.storyPoints
|
|
871
|
+
}
|
|
872
|
+
)
|
|
873
|
+
] }),
|
|
874
|
+
showPeopleRow && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex items-center justify-between gap-2", children: [
|
|
875
|
+
showAssignee ? /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
|
|
876
|
+
/* @__PURE__ */ jsx(Avatar, { id: task.assigneeId, name: resolved?.name, size: "sm" }),
|
|
877
|
+
resolved?.name && /* @__PURE__ */ jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: resolved.name })
|
|
878
|
+
] }) : /* @__PURE__ */ jsx("span", {}),
|
|
879
|
+
showDue && /* @__PURE__ */ jsxs(
|
|
880
|
+
"span",
|
|
881
|
+
{
|
|
882
|
+
className: cn(
|
|
883
|
+
"flex shrink-0 items-center gap-1 text-[11px]",
|
|
884
|
+
overdue ? "font-medium text-destructive" : "text-muted-foreground"
|
|
885
|
+
),
|
|
886
|
+
title: overdue ? "Overdue" : "Due date",
|
|
887
|
+
children: [
|
|
888
|
+
/* @__PURE__ */ jsx(CalendarDays, { className: "h-3 w-3", "aria-hidden": "true" }),
|
|
889
|
+
due
|
|
890
|
+
]
|
|
891
|
+
}
|
|
892
|
+
)
|
|
893
|
+
] })
|
|
894
|
+
]
|
|
895
|
+
}
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
function useWorkItemLinks(workspaceId, sourceProduct, limit = 500) {
|
|
899
|
+
const { data, loading } = useQuery(
|
|
900
|
+
LIST_WORK_ITEM_LINKS,
|
|
901
|
+
{
|
|
902
|
+
variables: {
|
|
903
|
+
workspaceId,
|
|
904
|
+
...sourceProduct ? { filter: { sourceProduct } } : {},
|
|
905
|
+
pagination: { first: limit }
|
|
906
|
+
},
|
|
907
|
+
skip: !workspaceId || !sourceProduct
|
|
908
|
+
}
|
|
909
|
+
);
|
|
910
|
+
const linksByItem = useMemo(() => {
|
|
911
|
+
const map = /* @__PURE__ */ new Map();
|
|
912
|
+
for (const link of data?.planmagnetWorkItemLinks?.items ?? []) {
|
|
913
|
+
const arr = map.get(link.workItemId);
|
|
914
|
+
if (arr) arr.push(link);
|
|
915
|
+
else map.set(link.workItemId, [link]);
|
|
916
|
+
}
|
|
917
|
+
return map;
|
|
918
|
+
}, [data]);
|
|
919
|
+
return { linksByItem, loading };
|
|
920
|
+
}
|
|
474
921
|
function TaskBoardCore({
|
|
475
922
|
projectId,
|
|
476
923
|
columns = DEFAULT_BOARD_COLUMNS,
|
|
477
|
-
readonly
|
|
924
|
+
readonly,
|
|
478
925
|
hideHeader,
|
|
479
|
-
|
|
926
|
+
priority = "ALL",
|
|
927
|
+
type = "ALL",
|
|
928
|
+
search = "",
|
|
929
|
+
assigneeId,
|
|
480
930
|
hiddenFields,
|
|
931
|
+
resolveAssignee,
|
|
932
|
+
linkSourceProduct,
|
|
933
|
+
renderLinkIcon,
|
|
481
934
|
onTaskClick,
|
|
482
935
|
className,
|
|
483
936
|
workspaceId: workspaceIdProp
|
|
484
937
|
}) {
|
|
485
938
|
const ctx = usePlanMagnetContextSafe();
|
|
486
939
|
const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
|
|
487
|
-
const
|
|
488
|
-
"ALL"
|
|
489
|
-
);
|
|
490
|
-
const [typeFilter, setTypeFilter] = useState("ALL");
|
|
940
|
+
const client = useApolloClient();
|
|
491
941
|
const hidden = useMemo(() => new Set(hiddenFields ?? []), [hiddenFields]);
|
|
942
|
+
const { linksByItem } = useWorkItemLinks(workspaceId, linkSourceProduct);
|
|
943
|
+
const [draggingId, setDraggingId] = useState(null);
|
|
944
|
+
const [dragOver, setDragOver] = useState(null);
|
|
945
|
+
const [moveError, setMoveError] = useState(null);
|
|
946
|
+
const [updateItem] = useMutation(UPDATE_WORK_ITEM);
|
|
492
947
|
const { data, loading, error } = useQuery(
|
|
493
948
|
LIST_WORK_ITEMS,
|
|
494
949
|
{
|
|
@@ -500,20 +955,28 @@ function TaskBoardCore({
|
|
|
500
955
|
skip: !workspaceId
|
|
501
956
|
}
|
|
502
957
|
);
|
|
958
|
+
const moveTask = useCallback(
|
|
959
|
+
(task, toStatus) => {
|
|
960
|
+
const from2 = task.status;
|
|
961
|
+
if (from2 === toStatus) return;
|
|
962
|
+
const id = client.cache.identify({ __typename: "WorkItem", id: task.id });
|
|
963
|
+
if (!id) return;
|
|
964
|
+
setMoveError(null);
|
|
965
|
+
client.cache.modify({ id, fields: { status: () => toStatus } });
|
|
966
|
+
updateItem({
|
|
967
|
+
variables: { id: task.id, input: { status: toStatus } }
|
|
968
|
+
}).catch(() => {
|
|
969
|
+
client.cache.modify({ id, fields: { status: () => from2 } });
|
|
970
|
+
setMoveError("Couldn't move the task. Please try again.");
|
|
971
|
+
});
|
|
972
|
+
},
|
|
973
|
+
[client, updateItem]
|
|
974
|
+
);
|
|
503
975
|
if (!workspaceId) {
|
|
504
976
|
return /* @__PURE__ */ jsx("div", { className: cn("p-6 text-sm text-destructive", className), children: "TaskBoard: no workspaceId provided (prop or PlanMagnetProvider)." });
|
|
505
977
|
}
|
|
506
978
|
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
|
-
);
|
|
979
|
+
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
980
|
}
|
|
518
981
|
if (error && !data) {
|
|
519
982
|
return /* @__PURE__ */ jsxs("div", { className: cn("p-6 text-sm text-destructive", className), children: [
|
|
@@ -522,150 +985,92 @@ function TaskBoardCore({
|
|
|
522
985
|
] });
|
|
523
986
|
}
|
|
524
987
|
const tasks = data?.planmagnetWorkItems?.items ?? [];
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
988
|
+
const query = search.trim().toLowerCase();
|
|
989
|
+
const filtered = tasks.filter((t) => {
|
|
990
|
+
if (priority !== "ALL" && t.priority !== priority) return false;
|
|
991
|
+
if (type !== "ALL" && t.type !== type) return false;
|
|
992
|
+
if (assigneeId && t.assigneeId !== assigneeId) return false;
|
|
993
|
+
if (query) {
|
|
994
|
+
const haystack = [t.title, t.key, ...t.labels ?? []].join(" ").toLowerCase();
|
|
995
|
+
if (!haystack.includes(query)) return false;
|
|
996
|
+
}
|
|
997
|
+
return true;
|
|
998
|
+
});
|
|
999
|
+
const canDrag = !readonly;
|
|
1000
|
+
const onColumnDragOver = (status) => (e) => {
|
|
1001
|
+
if (!canDrag || !draggingId) return;
|
|
1002
|
+
e.preventDefault();
|
|
1003
|
+
if (dragOver !== status) setDragOver(status);
|
|
1004
|
+
};
|
|
1005
|
+
const onColumnDrop = (status) => (e) => {
|
|
1006
|
+
if (!canDrag) return;
|
|
1007
|
+
e.preventDefault();
|
|
1008
|
+
const task = tasks.find((t) => t.id === draggingId);
|
|
1009
|
+
setDragOver(null);
|
|
1010
|
+
setDraggingId(null);
|
|
1011
|
+
if (task) moveTask(task, status);
|
|
1012
|
+
};
|
|
1013
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-3", className), children: [
|
|
1014
|
+
!hideHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
|
|
1015
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-foreground", children: "Board" }),
|
|
1016
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
|
|
1017
|
+
filtered.length,
|
|
534
1018
|
" ",
|
|
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
|
-
))
|
|
1019
|
+
filtered.length === 1 ? "task" : "tasks"
|
|
577
1020
|
] })
|
|
578
1021
|
] }),
|
|
579
|
-
/* @__PURE__ */ jsx("div", { className:
|
|
1022
|
+
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 }),
|
|
1023
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-3 overflow-x-auto pb-2", children: columns.map((col) => {
|
|
580
1024
|
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 })
|
|
1025
|
+
const isOver = dragOver === col.status;
|
|
1026
|
+
return /* @__PURE__ */ jsxs(
|
|
1027
|
+
"div",
|
|
1028
|
+
{
|
|
1029
|
+
onDragOver: onColumnDragOver(col.status),
|
|
1030
|
+
onDragLeave: () => dragOver === col.status && setDragOver(null),
|
|
1031
|
+
onDrop: onColumnDrop(col.status),
|
|
1032
|
+
className: cn(
|
|
1033
|
+
"flex w-72 shrink-0 flex-col rounded-lg border transition-colors",
|
|
1034
|
+
isOver ? "border-primary/50 bg-primary/5" : "border-transparent bg-muted/40"
|
|
1035
|
+
),
|
|
1036
|
+
children: [
|
|
1037
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2", children: [
|
|
1038
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium text-foreground", children: [
|
|
1039
|
+
/* @__PURE__ */ jsx(StatusIcon, { status: col.status }),
|
|
1040
|
+
col.label
|
|
638
1041
|
] }),
|
|
639
|
-
/* @__PURE__ */
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
1042
|
+
/* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted px-1.5 text-xs font-medium text-muted-foreground", children: colTasks.length })
|
|
1043
|
+
] }),
|
|
1044
|
+
/* @__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(
|
|
1045
|
+
TaskCard,
|
|
1046
|
+
{
|
|
1047
|
+
task,
|
|
1048
|
+
hidden,
|
|
1049
|
+
onClick: onTaskClick,
|
|
1050
|
+
draggable: canDrag,
|
|
1051
|
+
dragging: draggingId === task.id,
|
|
1052
|
+
resolveAssignee,
|
|
1053
|
+
links: linksByItem.get(task.id),
|
|
1054
|
+
renderLinkIcon,
|
|
1055
|
+
onDragStart: (t, e) => {
|
|
1056
|
+
setDraggingId(t.id);
|
|
1057
|
+
e.dataTransfer.effectAllowed = "move";
|
|
1058
|
+
e.dataTransfer.setData("text/plain", t.id);
|
|
1059
|
+
},
|
|
1060
|
+
onDragEnd: () => {
|
|
1061
|
+
setDraggingId(null);
|
|
1062
|
+
setDragOver(null);
|
|
1063
|
+
}
|
|
1064
|
+
},
|
|
1065
|
+
task.id
|
|
1066
|
+
)) })
|
|
1067
|
+
]
|
|
1068
|
+
},
|
|
1069
|
+
col.status
|
|
1070
|
+
);
|
|
648
1071
|
}) })
|
|
649
1072
|
] });
|
|
650
1073
|
}
|
|
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
1074
|
function TaskBoard({
|
|
670
1075
|
apiGatewayUrl,
|
|
671
1076
|
authToken,
|
|
@@ -937,138 +1342,55 @@ function CreateTaskCore({
|
|
|
937
1342
|
}
|
|
938
1343
|
),
|
|
939
1344
|
/* @__PURE__ */ jsx(
|
|
940
|
-
"button",
|
|
941
|
-
{
|
|
942
|
-
type: "submit",
|
|
943
|
-
disabled: !canSubmit || loading,
|
|
944
|
-
className: cn(
|
|
945
|
-
"rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground",
|
|
946
|
-
(!canSubmit || loading) && "cursor-not-allowed opacity-50"
|
|
947
|
-
),
|
|
948
|
-
children: loading ? "Creating\u2026" : submitLabel
|
|
949
|
-
}
|
|
950
|
-
)
|
|
951
|
-
] })
|
|
952
|
-
] }) }) });
|
|
953
|
-
}
|
|
954
|
-
function CreateTask({
|
|
955
|
-
apiGatewayUrl,
|
|
956
|
-
authToken,
|
|
957
|
-
workspaceToken,
|
|
958
|
-
workspaceId,
|
|
959
|
-
tenantId,
|
|
960
|
-
organizationId,
|
|
961
|
-
trustedOrigins,
|
|
962
|
-
onAuthError,
|
|
963
|
-
workspaceClient,
|
|
964
|
-
...coreProps
|
|
965
|
-
}) {
|
|
966
|
-
return /* @__PURE__ */ jsx(
|
|
967
|
-
PlanMagnetProvider,
|
|
968
|
-
{
|
|
969
|
-
apiGatewayUrl,
|
|
970
|
-
authToken,
|
|
971
|
-
workspaceToken,
|
|
972
|
-
workspaceId,
|
|
973
|
-
tenantId,
|
|
974
|
-
organizationId,
|
|
975
|
-
trustedOrigins,
|
|
976
|
-
onAuthError,
|
|
977
|
-
workspaceClient,
|
|
978
|
-
children: /* @__PURE__ */ jsx(CreateTaskCore, { workspaceId, ...coreProps })
|
|
979
|
-
}
|
|
980
|
-
);
|
|
981
|
-
}
|
|
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",
|
|
1345
|
+
"button",
|
|
1030
1346
|
{
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
"aria-label": ariaLabel,
|
|
1347
|
+
type: "submit",
|
|
1348
|
+
disabled: !canSubmit || loading,
|
|
1034
1349
|
className: cn(
|
|
1035
|
-
"
|
|
1036
|
-
|
|
1037
|
-
className
|
|
1350
|
+
"rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground",
|
|
1351
|
+
(!canSubmit || loading) && "cursor-not-allowed opacity-50"
|
|
1038
1352
|
),
|
|
1039
|
-
|
|
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
|
-
]
|
|
1353
|
+
children: loading ? "Creating\u2026" : submitLabel
|
|
1053
1354
|
}
|
|
1054
1355
|
)
|
|
1356
|
+
] })
|
|
1357
|
+
] }) }) });
|
|
1358
|
+
}
|
|
1359
|
+
function CreateTask({
|
|
1360
|
+
apiGatewayUrl,
|
|
1361
|
+
authToken,
|
|
1362
|
+
workspaceToken,
|
|
1363
|
+
workspaceId,
|
|
1364
|
+
tenantId,
|
|
1365
|
+
organizationId,
|
|
1366
|
+
trustedOrigins,
|
|
1367
|
+
onAuthError,
|
|
1368
|
+
workspaceClient,
|
|
1369
|
+
...coreProps
|
|
1370
|
+
}) {
|
|
1371
|
+
return /* @__PURE__ */ jsx(
|
|
1372
|
+
PlanMagnetProvider,
|
|
1373
|
+
{
|
|
1374
|
+
apiGatewayUrl,
|
|
1375
|
+
authToken,
|
|
1376
|
+
workspaceToken,
|
|
1377
|
+
workspaceId,
|
|
1378
|
+
tenantId,
|
|
1379
|
+
organizationId,
|
|
1380
|
+
trustedOrigins,
|
|
1381
|
+
onAuthError,
|
|
1382
|
+
workspaceClient,
|
|
1383
|
+
children: /* @__PURE__ */ jsx(CreateTaskCore, { workspaceId, ...coreProps })
|
|
1055
1384
|
}
|
|
1056
1385
|
);
|
|
1057
1386
|
}
|
|
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
1387
|
var DEFAULT_COLUMNS = [
|
|
1065
|
-
"key",
|
|
1066
1388
|
"title",
|
|
1067
|
-
"type",
|
|
1068
1389
|
"status",
|
|
1069
1390
|
"priority",
|
|
1070
1391
|
"assignee",
|
|
1071
|
-
"dueDate"
|
|
1392
|
+
"dueDate",
|
|
1393
|
+
"storyPoints"
|
|
1072
1394
|
];
|
|
1073
1395
|
var priorityRank = {
|
|
1074
1396
|
CRITICAL: 0,
|
|
@@ -1086,24 +1408,26 @@ var statusRank = {
|
|
|
1086
1408
|
DONE: 5,
|
|
1087
1409
|
CANCELLED: 6
|
|
1088
1410
|
};
|
|
1089
|
-
var priorityVariant = {
|
|
1090
|
-
NONE: "muted",
|
|
1091
|
-
LOW: "muted",
|
|
1092
|
-
MEDIUM: "secondary",
|
|
1093
|
-
HIGH: "accent",
|
|
1094
|
-
CRITICAL: "destructive"
|
|
1095
|
-
};
|
|
1096
1411
|
var COLUMN_LABELS = {
|
|
1097
1412
|
key: "Key",
|
|
1098
|
-
title: "
|
|
1413
|
+
title: "Task",
|
|
1099
1414
|
type: "Type",
|
|
1100
1415
|
status: "Status",
|
|
1101
1416
|
priority: "Priority",
|
|
1102
1417
|
assignee: "Assignee",
|
|
1103
|
-
dueDate: "Due",
|
|
1418
|
+
dueDate: "Due Date",
|
|
1104
1419
|
storyPoints: "Points",
|
|
1420
|
+
linkedRecord: "Linked Record",
|
|
1105
1421
|
labels: "Labels"
|
|
1106
1422
|
};
|
|
1423
|
+
var COLUMN_SORT = {
|
|
1424
|
+
title: "title",
|
|
1425
|
+
type: "type",
|
|
1426
|
+
status: "status",
|
|
1427
|
+
priority: "priority",
|
|
1428
|
+
dueDate: "dueDate",
|
|
1429
|
+
storyPoints: "storyPoints"
|
|
1430
|
+
};
|
|
1107
1431
|
function TaskListCore({
|
|
1108
1432
|
projectId,
|
|
1109
1433
|
status,
|
|
@@ -1116,12 +1440,20 @@ function TaskListCore({
|
|
|
1116
1440
|
sortDir = "asc",
|
|
1117
1441
|
hideHeader,
|
|
1118
1442
|
onSelect,
|
|
1443
|
+
resolveAssignee,
|
|
1444
|
+
linkSourceProduct,
|
|
1445
|
+
renderLinkIcon,
|
|
1119
1446
|
limit = 200,
|
|
1120
1447
|
className,
|
|
1121
1448
|
workspaceId: workspaceIdProp
|
|
1122
1449
|
}) {
|
|
1123
1450
|
const ctx = usePlanMagnetContextSafe();
|
|
1124
1451
|
const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
|
|
1452
|
+
const { linksByItem } = useWorkItemLinks(workspaceId, linkSourceProduct);
|
|
1453
|
+
const [sort, setSort] = useState({
|
|
1454
|
+
by: sortBy ?? null,
|
|
1455
|
+
dir: sortDir
|
|
1456
|
+
});
|
|
1125
1457
|
const filter = useMemo(() => {
|
|
1126
1458
|
const f = {};
|
|
1127
1459
|
if (projectId) f.projectId = projectId;
|
|
@@ -1145,10 +1477,18 @@ function TaskListCore({
|
|
|
1145
1477
|
);
|
|
1146
1478
|
const rows = useMemo(() => {
|
|
1147
1479
|
const items = data?.planmagnetWorkItems?.items ?? [];
|
|
1148
|
-
if (!
|
|
1149
|
-
const dir =
|
|
1150
|
-
|
|
1151
|
-
|
|
1480
|
+
if (!sort.by) return items;
|
|
1481
|
+
const dir = sort.dir === "desc" ? -1 : 1;
|
|
1482
|
+
const key = sort.by;
|
|
1483
|
+
return [...items].sort((a, b) => dir * compareTasks(a, b, key));
|
|
1484
|
+
}, [data, sort]);
|
|
1485
|
+
const toggleSort = (col) => {
|
|
1486
|
+
const key = COLUMN_SORT[col];
|
|
1487
|
+
if (!key) return;
|
|
1488
|
+
setSort(
|
|
1489
|
+
(prev) => prev.by === key ? { by: key, dir: prev.dir === "asc" ? "desc" : "asc" } : { by: key, dir: "asc" }
|
|
1490
|
+
);
|
|
1491
|
+
};
|
|
1152
1492
|
if (!workspaceId) {
|
|
1153
1493
|
return /* @__PURE__ */ jsx("div", { className: cn("p-6 text-sm text-destructive", className), children: "TaskList: no workspaceId provided (prop or PlanMagnetProvider)." });
|
|
1154
1494
|
}
|
|
@@ -1161,36 +1501,41 @@ function TaskListCore({
|
|
|
1161
1501
|
error.message
|
|
1162
1502
|
] });
|
|
1163
1503
|
}
|
|
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"
|
|
1504
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-3", className), children: [
|
|
1505
|
+
!hideHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
|
|
1506
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-foreground", children: "List" }),
|
|
1507
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
|
|
1508
|
+
rows.length,
|
|
1509
|
+
" ",
|
|
1510
|
+
rows.length === 1 ? "task" : "tasks"
|
|
1177
1511
|
] })
|
|
1178
1512
|
] }),
|
|
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
|
-
|
|
1513
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border border-border", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-sm", children: [
|
|
1514
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border bg-muted/50 text-left", children: columns.map((col) => {
|
|
1515
|
+
const sortable = Boolean(COLUMN_SORT[col]);
|
|
1516
|
+
const active = sort.by === COLUMN_SORT[col];
|
|
1517
|
+
return /* @__PURE__ */ jsx(
|
|
1518
|
+
"th",
|
|
1519
|
+
{
|
|
1520
|
+
onClick: sortable ? () => toggleSort(col) : void 0,
|
|
1521
|
+
className: cn(
|
|
1522
|
+
"px-3 py-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground",
|
|
1523
|
+
sortable && "cursor-pointer select-none hover:text-foreground"
|
|
1524
|
+
),
|
|
1525
|
+
children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
1526
|
+
COLUMN_LABELS[col],
|
|
1527
|
+
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" }))
|
|
1528
|
+
] })
|
|
1529
|
+
},
|
|
1530
|
+
col
|
|
1531
|
+
);
|
|
1532
|
+
}) }) }),
|
|
1188
1533
|
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
1189
1534
|
rows.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
|
|
1190
1535
|
"td",
|
|
1191
1536
|
{
|
|
1192
1537
|
colSpan: columns.length,
|
|
1193
|
-
className: "px-3 py-
|
|
1538
|
+
className: "px-3 py-8 text-center text-muted-foreground",
|
|
1194
1539
|
children: "No tasks found."
|
|
1195
1540
|
}
|
|
1196
1541
|
) }),
|
|
@@ -1202,7 +1547,18 @@ function TaskListCore({
|
|
|
1202
1547
|
"border-b border-border last:border-0",
|
|
1203
1548
|
onSelect && "cursor-pointer hover:bg-muted/40"
|
|
1204
1549
|
),
|
|
1205
|
-
children: columns.map((col) => /* @__PURE__ */ jsx(
|
|
1550
|
+
children: columns.map((col) => /* @__PURE__ */ jsx(
|
|
1551
|
+
"td",
|
|
1552
|
+
{
|
|
1553
|
+
className: "px-3 py-2 align-middle text-foreground",
|
|
1554
|
+
children: renderCell(col, task, {
|
|
1555
|
+
resolveAssignee,
|
|
1556
|
+
links: linksByItem.get(task.id),
|
|
1557
|
+
renderLinkIcon
|
|
1558
|
+
})
|
|
1559
|
+
},
|
|
1560
|
+
col
|
|
1561
|
+
))
|
|
1206
1562
|
},
|
|
1207
1563
|
task.id
|
|
1208
1564
|
))
|
|
@@ -1210,29 +1566,61 @@ function TaskListCore({
|
|
|
1210
1566
|
] }) })
|
|
1211
1567
|
] });
|
|
1212
1568
|
}
|
|
1213
|
-
function renderCell(col, task) {
|
|
1569
|
+
function renderCell(col, task, opts) {
|
|
1570
|
+
const { resolveAssignee, links, renderLinkIcon } = opts;
|
|
1214
1571
|
switch (col) {
|
|
1215
1572
|
case "key":
|
|
1216
|
-
return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: task.key });
|
|
1573
|
+
return /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-muted-foreground", children: task.key });
|
|
1217
1574
|
case "title":
|
|
1218
|
-
return /* @__PURE__ */
|
|
1575
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
1576
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: task.title }),
|
|
1577
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] uppercase text-muted-foreground", children: task.key })
|
|
1578
|
+
] });
|
|
1219
1579
|
case "type":
|
|
1220
|
-
return /* @__PURE__ */ jsx(
|
|
1580
|
+
return /* @__PURE__ */ jsx(TypeBadge, { type: task.type });
|
|
1221
1581
|
case "status":
|
|
1222
|
-
return /* @__PURE__ */ jsx(
|
|
1582
|
+
return /* @__PURE__ */ jsx(StatusPill, { status: task.status });
|
|
1223
1583
|
case "priority":
|
|
1224
|
-
return /* @__PURE__ */ jsx(
|
|
1225
|
-
case "assignee":
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1584
|
+
return /* @__PURE__ */ jsx(PriorityIndicator, { priority: task.priority, showLabel: true });
|
|
1585
|
+
case "assignee": {
|
|
1586
|
+
const resolved = task.assigneeId ? resolveAssignee?.(task.assigneeId) : null;
|
|
1587
|
+
return /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
1588
|
+
/* @__PURE__ */ jsx(Avatar, { id: task.assigneeId, name: resolved?.name, size: "sm" }),
|
|
1589
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: resolved?.name ?? (task.assigneeId ? "" : "Unassigned") })
|
|
1590
|
+
] });
|
|
1591
|
+
}
|
|
1592
|
+
case "dueDate": {
|
|
1593
|
+
const due = formatDueDate(task.dueDate);
|
|
1594
|
+
if (!due) return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
1595
|
+
return /* @__PURE__ */ jsx(
|
|
1596
|
+
"span",
|
|
1597
|
+
{
|
|
1598
|
+
className: cn(
|
|
1599
|
+
"text-xs",
|
|
1600
|
+
isOverdue(task.dueDate, task.status) ? "font-medium text-destructive" : "text-muted-foreground"
|
|
1601
|
+
),
|
|
1602
|
+
children: due
|
|
1603
|
+
}
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1229
1606
|
case "storyPoints":
|
|
1230
|
-
return /* @__PURE__ */ jsx("span", { className: "text-
|
|
1607
|
+
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" });
|
|
1608
|
+
case "linkedRecord":
|
|
1609
|
+
if (!links || links.length === 0) {
|
|
1610
|
+
return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
|
|
1611
|
+
}
|
|
1612
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1", children: [
|
|
1613
|
+
links.slice(0, 2).map((link) => /* @__PURE__ */ jsx(LinkChip, { link, renderIcon: renderLinkIcon }, link.id)),
|
|
1614
|
+
links.length > 2 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
1615
|
+
"+",
|
|
1616
|
+
links.length - 2
|
|
1617
|
+
] })
|
|
1618
|
+
] });
|
|
1231
1619
|
case "labels":
|
|
1232
1620
|
return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: (task.labels ?? []).slice(0, 3).map((l) => /* @__PURE__ */ jsx(
|
|
1233
1621
|
"span",
|
|
1234
1622
|
{
|
|
1235
|
-
className: "rounded bg-muted px-1 py-0.5 text-[10px] text-muted-foreground",
|
|
1623
|
+
className: "rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground",
|
|
1236
1624
|
children: l
|
|
1237
1625
|
},
|
|
1238
1626
|
l
|
|
@@ -1287,9 +1675,448 @@ function TaskList({
|
|
|
1287
1675
|
}
|
|
1288
1676
|
);
|
|
1289
1677
|
}
|
|
1678
|
+
var WEEK_OPTS = { weekStartsOn: 1 };
|
|
1679
|
+
function dayKey(d) {
|
|
1680
|
+
return format(d, "yyyy-MM-dd");
|
|
1681
|
+
}
|
|
1682
|
+
function TaskTimelineCore({
|
|
1683
|
+
projectId,
|
|
1684
|
+
priority = "ALL",
|
|
1685
|
+
type = "ALL",
|
|
1686
|
+
search = "",
|
|
1687
|
+
assigneeId,
|
|
1688
|
+
defaultMode = "week",
|
|
1689
|
+
onSelect,
|
|
1690
|
+
resolveAssignee,
|
|
1691
|
+
className,
|
|
1692
|
+
workspaceId: workspaceIdProp
|
|
1693
|
+
}) {
|
|
1694
|
+
const ctx = usePlanMagnetContextSafe();
|
|
1695
|
+
const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
|
|
1696
|
+
const [mode, setMode] = useState(defaultMode);
|
|
1697
|
+
const [offset, setOffset] = useState(0);
|
|
1698
|
+
const { data, loading, error } = useQuery(
|
|
1699
|
+
LIST_WORK_ITEMS,
|
|
1700
|
+
{
|
|
1701
|
+
variables: {
|
|
1702
|
+
workspaceId,
|
|
1703
|
+
...projectId ? { filter: { projectId } } : {},
|
|
1704
|
+
pagination: { first: 200 }
|
|
1705
|
+
},
|
|
1706
|
+
skip: !workspaceId
|
|
1707
|
+
}
|
|
1708
|
+
);
|
|
1709
|
+
const tasks = useMemo(() => {
|
|
1710
|
+
const all = data?.planmagnetWorkItems?.items ?? [];
|
|
1711
|
+
const query = search.trim().toLowerCase();
|
|
1712
|
+
return all.filter((t) => {
|
|
1713
|
+
if (priority !== "ALL" && t.priority !== priority) return false;
|
|
1714
|
+
if (type !== "ALL" && t.type !== type) return false;
|
|
1715
|
+
if (assigneeId && t.assigneeId !== assigneeId) return false;
|
|
1716
|
+
if (query) {
|
|
1717
|
+
const hay = [t.title, t.key, ...t.labels ?? []].join(" ").toLowerCase();
|
|
1718
|
+
if (!hay.includes(query)) return false;
|
|
1719
|
+
}
|
|
1720
|
+
return true;
|
|
1721
|
+
});
|
|
1722
|
+
}, [data, priority, type, search, assigneeId]);
|
|
1723
|
+
const { anchor, rangeStart, rangeEnd, days } = useMemo(() => {
|
|
1724
|
+
const base = /* @__PURE__ */ new Date();
|
|
1725
|
+
if (mode === "week") {
|
|
1726
|
+
const a2 = addWeeks(base, offset);
|
|
1727
|
+
const s2 = startOfWeek(a2, WEEK_OPTS);
|
|
1728
|
+
const e2 = endOfWeek(a2, WEEK_OPTS);
|
|
1729
|
+
return {
|
|
1730
|
+
anchor: a2,
|
|
1731
|
+
rangeStart: s2,
|
|
1732
|
+
rangeEnd: e2,
|
|
1733
|
+
days: eachDayOfInterval({ start: s2, end: e2 })
|
|
1734
|
+
};
|
|
1735
|
+
}
|
|
1736
|
+
const a = addMonths(base, offset);
|
|
1737
|
+
const s = startOfWeek(startOfMonth(a), WEEK_OPTS);
|
|
1738
|
+
const e = endOfWeek(endOfMonth(a), WEEK_OPTS);
|
|
1739
|
+
return {
|
|
1740
|
+
anchor: a,
|
|
1741
|
+
rangeStart: s,
|
|
1742
|
+
rangeEnd: e,
|
|
1743
|
+
days: eachDayOfInterval({ start: s, end: e })
|
|
1744
|
+
};
|
|
1745
|
+
}, [mode, offset]);
|
|
1746
|
+
const { byDay, overdue, undated } = useMemo(() => {
|
|
1747
|
+
const map = /* @__PURE__ */ new Map();
|
|
1748
|
+
const od = [];
|
|
1749
|
+
const ud = [];
|
|
1750
|
+
for (const t of tasks) {
|
|
1751
|
+
const d = parseDate(t.dueDate);
|
|
1752
|
+
if (!d) {
|
|
1753
|
+
ud.push(t);
|
|
1754
|
+
continue;
|
|
1755
|
+
}
|
|
1756
|
+
if (isOverdue(t.dueDate, t.status)) {
|
|
1757
|
+
od.push(t);
|
|
1758
|
+
continue;
|
|
1759
|
+
}
|
|
1760
|
+
const k = dayKey(d);
|
|
1761
|
+
const arr = map.get(k);
|
|
1762
|
+
if (arr) arr.push(t);
|
|
1763
|
+
else map.set(k, [t]);
|
|
1764
|
+
}
|
|
1765
|
+
return { byDay: map, overdue: od, undated: ud };
|
|
1766
|
+
}, [tasks]);
|
|
1767
|
+
if (!workspaceId) {
|
|
1768
|
+
return /* @__PURE__ */ jsx("div", { className: cn("p-6 text-sm text-destructive", className), children: "Timeline: no workspaceId provided (prop or PlanMagnetProvider)." });
|
|
1769
|
+
}
|
|
1770
|
+
if (loading && !data) {
|
|
1771
|
+
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" }) });
|
|
1772
|
+
}
|
|
1773
|
+
if (error && !data) {
|
|
1774
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("p-6 text-sm text-destructive", className), children: [
|
|
1775
|
+
"Error loading timeline: ",
|
|
1776
|
+
error.message
|
|
1777
|
+
] });
|
|
1778
|
+
}
|
|
1779
|
+
const rangeLabel = mode === "week" ? `${format(rangeStart, "MMM d")} \u2013 ${format(rangeEnd, "MMM d")}` : format(anchor, "MMMM yyyy");
|
|
1780
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-3", className), children: [
|
|
1781
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
1782
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
1783
|
+
/* @__PURE__ */ jsx(
|
|
1784
|
+
"button",
|
|
1785
|
+
{
|
|
1786
|
+
type: "button",
|
|
1787
|
+
onClick: () => setOffset((o) => o - 1),
|
|
1788
|
+
"aria-label": "Previous",
|
|
1789
|
+
className: "flex h-8 w-8 items-center justify-center rounded-md border border-border text-muted-foreground hover:bg-muted/50",
|
|
1790
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
1791
|
+
}
|
|
1792
|
+
),
|
|
1793
|
+
/* @__PURE__ */ jsx(
|
|
1794
|
+
"button",
|
|
1795
|
+
{
|
|
1796
|
+
type: "button",
|
|
1797
|
+
onClick: () => setOffset(0),
|
|
1798
|
+
className: "h-8 rounded-md border border-border px-3 text-xs font-medium text-foreground hover:bg-muted/50",
|
|
1799
|
+
children: "Today"
|
|
1800
|
+
}
|
|
1801
|
+
),
|
|
1802
|
+
/* @__PURE__ */ jsx(
|
|
1803
|
+
"button",
|
|
1804
|
+
{
|
|
1805
|
+
type: "button",
|
|
1806
|
+
onClick: () => setOffset((o) => o + 1),
|
|
1807
|
+
"aria-label": "Next",
|
|
1808
|
+
className: "flex h-8 w-8 items-center justify-center rounded-md border border-border text-muted-foreground hover:bg-muted/50",
|
|
1809
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
1810
|
+
}
|
|
1811
|
+
),
|
|
1812
|
+
/* @__PURE__ */ jsx("span", { className: "ml-2 text-sm font-medium text-foreground", children: rangeLabel })
|
|
1813
|
+
] }),
|
|
1814
|
+
/* @__PURE__ */ jsx("div", { className: "inline-flex rounded-md border border-border p-0.5", children: ["week", "month"].map((m) => /* @__PURE__ */ jsx(
|
|
1815
|
+
"button",
|
|
1816
|
+
{
|
|
1817
|
+
type: "button",
|
|
1818
|
+
onClick: () => {
|
|
1819
|
+
setMode(m);
|
|
1820
|
+
setOffset(0);
|
|
1821
|
+
},
|
|
1822
|
+
className: cn(
|
|
1823
|
+
"rounded px-3 py-1 text-xs font-medium capitalize transition-colors",
|
|
1824
|
+
mode === m ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:text-foreground"
|
|
1825
|
+
),
|
|
1826
|
+
children: m
|
|
1827
|
+
},
|
|
1828
|
+
m
|
|
1829
|
+
)) })
|
|
1830
|
+
] }),
|
|
1831
|
+
overdue.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2", children: [
|
|
1832
|
+
/* @__PURE__ */ jsxs("p", { className: "mb-1 text-xs font-semibold text-destructive", children: [
|
|
1833
|
+
overdue.length,
|
|
1834
|
+
" overdue"
|
|
1835
|
+
] }),
|
|
1836
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: overdue.map((t) => /* @__PURE__ */ jsx(
|
|
1837
|
+
TaskChip,
|
|
1838
|
+
{
|
|
1839
|
+
task: t,
|
|
1840
|
+
onSelect,
|
|
1841
|
+
resolveAssignee,
|
|
1842
|
+
tone: "destructive"
|
|
1843
|
+
},
|
|
1844
|
+
t.id
|
|
1845
|
+
)) })
|
|
1846
|
+
] }),
|
|
1847
|
+
mode === "week" ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: days.map((day) => {
|
|
1848
|
+
const items = byDay.get(dayKey(day)) ?? [];
|
|
1849
|
+
const today = isToday(day);
|
|
1850
|
+
return /* @__PURE__ */ jsxs(
|
|
1851
|
+
"div",
|
|
1852
|
+
{
|
|
1853
|
+
className: cn(
|
|
1854
|
+
"flex min-h-32 flex-col rounded-lg border p-2",
|
|
1855
|
+
today ? "border-primary/50 bg-primary/5" : "border-border bg-muted/20"
|
|
1856
|
+
),
|
|
1857
|
+
children: [
|
|
1858
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-baseline justify-between", children: [
|
|
1859
|
+
/* @__PURE__ */ jsx(
|
|
1860
|
+
"span",
|
|
1861
|
+
{
|
|
1862
|
+
className: cn(
|
|
1863
|
+
"text-xs font-semibold uppercase",
|
|
1864
|
+
today ? "text-primary" : "text-muted-foreground"
|
|
1865
|
+
),
|
|
1866
|
+
children: format(day, "EEE")
|
|
1867
|
+
}
|
|
1868
|
+
),
|
|
1869
|
+
/* @__PURE__ */ jsx(
|
|
1870
|
+
"span",
|
|
1871
|
+
{
|
|
1872
|
+
className: cn(
|
|
1873
|
+
"text-sm font-semibold",
|
|
1874
|
+
today ? "text-primary" : "text-foreground"
|
|
1875
|
+
),
|
|
1876
|
+
children: format(day, "d")
|
|
1877
|
+
}
|
|
1878
|
+
)
|
|
1879
|
+
] }),
|
|
1880
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1.5", children: items.map((t) => /* @__PURE__ */ jsx(
|
|
1881
|
+
TaskChip,
|
|
1882
|
+
{
|
|
1883
|
+
task: t,
|
|
1884
|
+
onSelect,
|
|
1885
|
+
resolveAssignee
|
|
1886
|
+
},
|
|
1887
|
+
t.id
|
|
1888
|
+
)) })
|
|
1889
|
+
]
|
|
1890
|
+
},
|
|
1891
|
+
dayKey(day)
|
|
1892
|
+
);
|
|
1893
|
+
}) }) : /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-7 gap-1", children: [
|
|
1894
|
+
["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].map((d) => /* @__PURE__ */ jsx(
|
|
1895
|
+
"div",
|
|
1896
|
+
{
|
|
1897
|
+
className: "px-1 pb-1 text-center text-[10px] font-semibold uppercase text-muted-foreground",
|
|
1898
|
+
children: d
|
|
1899
|
+
},
|
|
1900
|
+
d
|
|
1901
|
+
)),
|
|
1902
|
+
days.map((day) => {
|
|
1903
|
+
const items = byDay.get(dayKey(day)) ?? [];
|
|
1904
|
+
const today = isToday(day);
|
|
1905
|
+
const inMonth = isSameMonth(day, anchor);
|
|
1906
|
+
return /* @__PURE__ */ jsxs(
|
|
1907
|
+
"div",
|
|
1908
|
+
{
|
|
1909
|
+
className: cn(
|
|
1910
|
+
"flex min-h-20 flex-col gap-1 rounded-md border p-1",
|
|
1911
|
+
today ? "border-primary/50 bg-primary/5" : "border-border/60 bg-muted/10",
|
|
1912
|
+
!inMonth && "opacity-40"
|
|
1913
|
+
),
|
|
1914
|
+
children: [
|
|
1915
|
+
/* @__PURE__ */ jsx(
|
|
1916
|
+
"span",
|
|
1917
|
+
{
|
|
1918
|
+
className: cn(
|
|
1919
|
+
"text-[10px] font-semibold",
|
|
1920
|
+
today ? "text-primary" : "text-muted-foreground"
|
|
1921
|
+
),
|
|
1922
|
+
children: format(day, "d")
|
|
1923
|
+
}
|
|
1924
|
+
),
|
|
1925
|
+
items.slice(0, 3).map((t) => /* @__PURE__ */ jsx(
|
|
1926
|
+
TaskChip,
|
|
1927
|
+
{
|
|
1928
|
+
task: t,
|
|
1929
|
+
onSelect,
|
|
1930
|
+
resolveAssignee,
|
|
1931
|
+
compact: true
|
|
1932
|
+
},
|
|
1933
|
+
t.id
|
|
1934
|
+
)),
|
|
1935
|
+
items.length > 3 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
1936
|
+
"+",
|
|
1937
|
+
items.length - 3,
|
|
1938
|
+
" more"
|
|
1939
|
+
] })
|
|
1940
|
+
]
|
|
1941
|
+
},
|
|
1942
|
+
dayKey(day)
|
|
1943
|
+
);
|
|
1944
|
+
})
|
|
1945
|
+
] }),
|
|
1946
|
+
undated.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-border bg-muted/20 px-3 py-2", children: [
|
|
1947
|
+
/* @__PURE__ */ jsxs("p", { className: "mb-1 text-xs font-semibold text-muted-foreground", children: [
|
|
1948
|
+
"No due date (",
|
|
1949
|
+
undated.length,
|
|
1950
|
+
")"
|
|
1951
|
+
] }),
|
|
1952
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: undated.map((t) => /* @__PURE__ */ jsx(
|
|
1953
|
+
TaskChip,
|
|
1954
|
+
{
|
|
1955
|
+
task: t,
|
|
1956
|
+
onSelect,
|
|
1957
|
+
resolveAssignee
|
|
1958
|
+
},
|
|
1959
|
+
t.id
|
|
1960
|
+
)) })
|
|
1961
|
+
] })
|
|
1962
|
+
] });
|
|
1963
|
+
}
|
|
1964
|
+
function TaskChip({
|
|
1965
|
+
task,
|
|
1966
|
+
onSelect,
|
|
1967
|
+
resolveAssignee,
|
|
1968
|
+
tone,
|
|
1969
|
+
compact
|
|
1970
|
+
}) {
|
|
1971
|
+
const resolved = task.assigneeId ? resolveAssignee?.(task.assigneeId) : null;
|
|
1972
|
+
const interactive = Boolean(onSelect);
|
|
1973
|
+
return /* @__PURE__ */ jsxs(
|
|
1974
|
+
"button",
|
|
1975
|
+
{
|
|
1976
|
+
type: "button",
|
|
1977
|
+
disabled: !interactive,
|
|
1978
|
+
onClick: interactive ? () => onSelect?.(task.id) : void 0,
|
|
1979
|
+
title: task.title,
|
|
1980
|
+
className: cn(
|
|
1981
|
+
"flex w-full items-center gap-1.5 rounded-md border px-1.5 py-1 text-left transition-colors",
|
|
1982
|
+
tone === "destructive" ? "border-destructive/40 bg-card hover:bg-destructive/5" : "border-border bg-card hover:border-primary/50",
|
|
1983
|
+
interactive ? "cursor-pointer" : "cursor-default"
|
|
1984
|
+
),
|
|
1985
|
+
children: [
|
|
1986
|
+
/* @__PURE__ */ jsx(PriorityIndicator, { priority: task.priority }),
|
|
1987
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-[11px] font-medium text-foreground", children: task.title }),
|
|
1988
|
+
!compact && /* @__PURE__ */ jsx(Avatar, { id: task.assigneeId, name: resolved?.name, size: "sm" })
|
|
1989
|
+
]
|
|
1990
|
+
}
|
|
1991
|
+
);
|
|
1992
|
+
}
|
|
1993
|
+
var VIEW_META = {
|
|
1994
|
+
board: { label: "Board", icon: LayoutGrid },
|
|
1995
|
+
list: { label: "List", icon: List },
|
|
1996
|
+
timeline: { label: "Timeline", icon: CalendarRange }
|
|
1997
|
+
};
|
|
1998
|
+
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";
|
|
1999
|
+
function TaskToolbar({
|
|
2000
|
+
view,
|
|
2001
|
+
onViewChange,
|
|
2002
|
+
views = ["board", "list", "timeline"],
|
|
2003
|
+
search,
|
|
2004
|
+
onSearchChange,
|
|
2005
|
+
priority,
|
|
2006
|
+
onPriorityChange,
|
|
2007
|
+
type,
|
|
2008
|
+
onTypeChange,
|
|
2009
|
+
onNewTask,
|
|
2010
|
+
canCreate,
|
|
2011
|
+
className
|
|
2012
|
+
}) {
|
|
2013
|
+
return /* @__PURE__ */ jsxs(
|
|
2014
|
+
"div",
|
|
2015
|
+
{
|
|
2016
|
+
className: cn(
|
|
2017
|
+
"flex flex-wrap items-center justify-between gap-3 border-b border-border pb-2",
|
|
2018
|
+
className
|
|
2019
|
+
),
|
|
2020
|
+
children: [
|
|
2021
|
+
/* @__PURE__ */ jsx(
|
|
2022
|
+
"div",
|
|
2023
|
+
{
|
|
2024
|
+
className: "flex items-center gap-1",
|
|
2025
|
+
role: "tablist",
|
|
2026
|
+
"aria-label": "Task views",
|
|
2027
|
+
children: views.map((v) => {
|
|
2028
|
+
const meta = VIEW_META[v];
|
|
2029
|
+
const Icon = meta.icon;
|
|
2030
|
+
const active = v === view;
|
|
2031
|
+
return /* @__PURE__ */ jsxs(
|
|
2032
|
+
"button",
|
|
2033
|
+
{
|
|
2034
|
+
type: "button",
|
|
2035
|
+
role: "tab",
|
|
2036
|
+
"aria-selected": active,
|
|
2037
|
+
onClick: () => onViewChange(v),
|
|
2038
|
+
className: cn(
|
|
2039
|
+
"relative flex items-center gap-1.5 rounded-t-md px-2.5 py-1.5 text-sm font-medium transition-colors",
|
|
2040
|
+
active ? "text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
2041
|
+
),
|
|
2042
|
+
children: [
|
|
2043
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
2044
|
+
meta.label,
|
|
2045
|
+
active && /* @__PURE__ */ jsx("span", { className: "absolute inset-x-1 -bottom-[9px] h-0.5 rounded-full bg-primary" })
|
|
2046
|
+
]
|
|
2047
|
+
},
|
|
2048
|
+
v
|
|
2049
|
+
);
|
|
2050
|
+
})
|
|
2051
|
+
}
|
|
2052
|
+
),
|
|
2053
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
2054
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
2055
|
+
/* @__PURE__ */ jsx(
|
|
2056
|
+
Search,
|
|
2057
|
+
{
|
|
2058
|
+
className: "pointer-events-none absolute left-2 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground",
|
|
2059
|
+
"aria-hidden": "true"
|
|
2060
|
+
}
|
|
2061
|
+
),
|
|
2062
|
+
/* @__PURE__ */ jsx(
|
|
2063
|
+
"input",
|
|
2064
|
+
{
|
|
2065
|
+
type: "search",
|
|
2066
|
+
value: search,
|
|
2067
|
+
onChange: (e) => onSearchChange(e.target.value),
|
|
2068
|
+
placeholder: "Search tasks\u2026",
|
|
2069
|
+
"aria-label": "Search tasks",
|
|
2070
|
+
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"
|
|
2071
|
+
}
|
|
2072
|
+
)
|
|
2073
|
+
] }),
|
|
2074
|
+
/* @__PURE__ */ jsxs(
|
|
2075
|
+
"select",
|
|
2076
|
+
{
|
|
2077
|
+
value: priority,
|
|
2078
|
+
onChange: (e) => onPriorityChange(e.target.value),
|
|
2079
|
+
"aria-label": "Filter by priority",
|
|
2080
|
+
className: SELECT_CLASS,
|
|
2081
|
+
children: [
|
|
2082
|
+
/* @__PURE__ */ jsx("option", { value: "ALL", children: "All priorities" }),
|
|
2083
|
+
TASK_PRIORITIES.map((p) => /* @__PURE__ */ jsx("option", { value: p, children: enumLabel(p) }, p))
|
|
2084
|
+
]
|
|
2085
|
+
}
|
|
2086
|
+
),
|
|
2087
|
+
/* @__PURE__ */ jsxs(
|
|
2088
|
+
"select",
|
|
2089
|
+
{
|
|
2090
|
+
value: type,
|
|
2091
|
+
onChange: (e) => onTypeChange(e.target.value),
|
|
2092
|
+
"aria-label": "Filter by type",
|
|
2093
|
+
className: SELECT_CLASS,
|
|
2094
|
+
children: [
|
|
2095
|
+
/* @__PURE__ */ jsx("option", { value: "ALL", children: "All types" }),
|
|
2096
|
+
TASK_TYPES.map((t) => /* @__PURE__ */ jsx("option", { value: t, children: enumLabel(t) }, t))
|
|
2097
|
+
]
|
|
2098
|
+
}
|
|
2099
|
+
),
|
|
2100
|
+
canCreate && onNewTask && /* @__PURE__ */ jsxs(
|
|
2101
|
+
"button",
|
|
2102
|
+
{
|
|
2103
|
+
type: "button",
|
|
2104
|
+
onClick: onNewTask,
|
|
2105
|
+
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",
|
|
2106
|
+
children: [
|
|
2107
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
2108
|
+
"New task"
|
|
2109
|
+
]
|
|
2110
|
+
}
|
|
2111
|
+
)
|
|
2112
|
+
] })
|
|
2113
|
+
]
|
|
2114
|
+
}
|
|
2115
|
+
);
|
|
2116
|
+
}
|
|
1290
2117
|
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
2118
|
var labelClass2 = "text-xs font-medium uppercase tracking-wide text-muted-foreground";
|
|
1292
|
-
var
|
|
2119
|
+
var priorityVariant = {
|
|
1293
2120
|
NONE: "muted",
|
|
1294
2121
|
LOW: "muted",
|
|
1295
2122
|
MEDIUM: "secondary",
|
|
@@ -1412,7 +2239,7 @@ function TaskDetailCore({
|
|
|
1412
2239
|
!editing ? /* @__PURE__ */ jsxs("dl", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
|
|
1413
2240
|
!hidden.has("type") && /* @__PURE__ */ jsx(Field, { label: "Type", children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: enumLabel(item.type) }) }),
|
|
1414
2241
|
!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:
|
|
2242
|
+
!hidden.has("priority") && /* @__PURE__ */ jsx(Field, { label: "Priority", children: /* @__PURE__ */ jsx(Badge, { variant: priorityVariant[item.priority], children: enumLabel(item.priority) }) }),
|
|
1416
2243
|
!hidden.has("assignee") && /* @__PURE__ */ jsx(Field, { label: "Assignee", children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: item.assigneeId ?? "Unassigned" }) }),
|
|
1417
2244
|
!hidden.has("dueDate") && /* @__PURE__ */ jsx(Field, { label: "Due date", children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: item.dueDate ?? "\u2014" }) }),
|
|
1418
2245
|
!hidden.has("storyPoints") && /* @__PURE__ */ jsx(Field, { label: "Story points", children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: item.storyPoints ?? 0 }) }),
|
|
@@ -1622,7 +2449,11 @@ function TasksPageCore({
|
|
|
1622
2449
|
columns,
|
|
1623
2450
|
readonly,
|
|
1624
2451
|
hiddenFields,
|
|
1625
|
-
|
|
2452
|
+
views,
|
|
2453
|
+
defaultView = "board",
|
|
2454
|
+
resolveAssignee,
|
|
2455
|
+
linkSourceProduct,
|
|
2456
|
+
renderLinkIcon,
|
|
1626
2457
|
header,
|
|
1627
2458
|
onTaskSaved,
|
|
1628
2459
|
createSurface = "modal",
|
|
@@ -1632,6 +2463,10 @@ function TasksPageCore({
|
|
|
1632
2463
|
const ctx = usePlanMagnetContextSafe();
|
|
1633
2464
|
const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
|
|
1634
2465
|
const client = useApolloClient();
|
|
2466
|
+
const [view, setView] = useState(defaultView);
|
|
2467
|
+
const [search, setSearch] = useState("");
|
|
2468
|
+
const [priority, setPriority] = useState("ALL");
|
|
2469
|
+
const [type, setType] = useState("ALL");
|
|
1635
2470
|
const [creating, setCreating] = useState(false);
|
|
1636
2471
|
const [selectedId, setSelectedId] = useState(null);
|
|
1637
2472
|
const refetchBoard = useCallback(() => {
|
|
@@ -1656,6 +2491,7 @@ function TasksPageCore({
|
|
|
1656
2491
|
const handleTaskClick = useCallback((task) => {
|
|
1657
2492
|
setSelectedId(task.id);
|
|
1658
2493
|
}, []);
|
|
2494
|
+
const handleSelect = useCallback((id) => setSelectedId(id), []);
|
|
1659
2495
|
if (!workspaceId) {
|
|
1660
2496
|
return /* @__PURE__ */ jsx("div", { className: cn("p-6 text-sm text-destructive", className), children: "TasksPage: no workspaceId provided (prop or PlanMagnetProvider)." });
|
|
1661
2497
|
}
|
|
@@ -1681,34 +2517,82 @@ function TasksPageCore({
|
|
|
1681
2517
|
onUpdated: handleUpdated
|
|
1682
2518
|
}
|
|
1683
2519
|
) : null;
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
] }),
|
|
1697
|
-
showInlineCreate && /* @__PURE__ */ jsx("div", { className: "px-6", children: createForm }),
|
|
2520
|
+
const listPriority = priority === "ALL" ? void 0 : priority;
|
|
2521
|
+
const listType = type === "ALL" ? void 0 : type;
|
|
2522
|
+
const listColumns = linkSourceProduct ? [
|
|
2523
|
+
"title",
|
|
2524
|
+
"status",
|
|
2525
|
+
"priority",
|
|
2526
|
+
"assignee",
|
|
2527
|
+
"dueDate",
|
|
2528
|
+
"linkedRecord"
|
|
2529
|
+
] : void 0;
|
|
2530
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4 p-6", className), children: [
|
|
2531
|
+
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
2532
|
/* @__PURE__ */ jsx(
|
|
2533
|
+
TaskToolbar,
|
|
2534
|
+
{
|
|
2535
|
+
view,
|
|
2536
|
+
onViewChange: setView,
|
|
2537
|
+
views,
|
|
2538
|
+
search,
|
|
2539
|
+
onSearchChange: setSearch,
|
|
2540
|
+
priority,
|
|
2541
|
+
onPriorityChange: setPriority,
|
|
2542
|
+
type,
|
|
2543
|
+
onTypeChange: setType,
|
|
2544
|
+
onNewTask: () => setCreating(true),
|
|
2545
|
+
canCreate
|
|
2546
|
+
}
|
|
2547
|
+
),
|
|
2548
|
+
showInlineCreate && /* @__PURE__ */ jsx("div", { children: createForm }),
|
|
2549
|
+
view === "board" && /* @__PURE__ */ jsx(
|
|
1699
2550
|
TaskBoardCore,
|
|
1700
2551
|
{
|
|
1701
2552
|
workspaceId,
|
|
1702
2553
|
projectId: boardProjectId,
|
|
1703
2554
|
columns,
|
|
1704
2555
|
readonly,
|
|
1705
|
-
hideHeader:
|
|
1706
|
-
|
|
2556
|
+
hideHeader: true,
|
|
2557
|
+
priority,
|
|
2558
|
+
type,
|
|
2559
|
+
search,
|
|
1707
2560
|
hiddenFields,
|
|
2561
|
+
resolveAssignee,
|
|
2562
|
+
linkSourceProduct,
|
|
2563
|
+
renderLinkIcon,
|
|
1708
2564
|
onTaskClick: handleTaskClick
|
|
1709
2565
|
}
|
|
1710
2566
|
),
|
|
1711
|
-
|
|
2567
|
+
view === "list" && /* @__PURE__ */ jsx(
|
|
2568
|
+
TaskListCore,
|
|
2569
|
+
{
|
|
2570
|
+
workspaceId,
|
|
2571
|
+
projectId: boardProjectId,
|
|
2572
|
+
priority: listPriority,
|
|
2573
|
+
type: listType,
|
|
2574
|
+
search: search || void 0,
|
|
2575
|
+
hideHeader: true,
|
|
2576
|
+
resolveAssignee,
|
|
2577
|
+
linkSourceProduct,
|
|
2578
|
+
renderLinkIcon,
|
|
2579
|
+
columns: listColumns ? [...listColumns] : void 0,
|
|
2580
|
+
onSelect: handleSelect
|
|
2581
|
+
}
|
|
2582
|
+
),
|
|
2583
|
+
view === "timeline" && /* @__PURE__ */ jsx(
|
|
2584
|
+
TaskTimelineCore,
|
|
2585
|
+
{
|
|
2586
|
+
workspaceId,
|
|
2587
|
+
projectId: boardProjectId,
|
|
2588
|
+
priority,
|
|
2589
|
+
type,
|
|
2590
|
+
search,
|
|
2591
|
+
resolveAssignee,
|
|
2592
|
+
onSelect: handleSelect
|
|
2593
|
+
}
|
|
2594
|
+
),
|
|
2595
|
+
showInlineDetail && /* @__PURE__ */ jsx("div", { className: "pb-6", children: detailView }),
|
|
1712
2596
|
createSurface === "modal" && /* @__PURE__ */ jsx(
|
|
1713
2597
|
Modal,
|
|
1714
2598
|
{
|
|
@@ -1759,6 +2643,6 @@ function TasksPage({
|
|
|
1759
2643
|
);
|
|
1760
2644
|
}
|
|
1761
2645
|
|
|
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 };
|
|
2646
|
+
export { Avatar, Badge, CREATE_WORK_ITEM, CREATE_WORK_ITEM_LINK, CreateTask, CreateTaskCore, DEFAULT_BOARD_COLUMNS, DELETE_WORK_ITEM_LINK, Drawer, GET_PROJECTS, GET_WORK_ITEM, LIST_WORK_ITEMS, LIST_WORK_ITEM_LINKS, LinkChip, 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_LINK_FIELDS, WORK_ITEM_SUMMARY_FIELDS, badgeVariants, cn, config, createPlanMagnetClient, enumLabel, getGraphQLEndpoint, getPlanMagnetClient, getPlanMagnetClientSafe, getPlanMagnetConfig, isPlanMagnetClientReady, resetPlanMagnetClient, resetPlanMagnetConfig, setPlanMagnetClient, setPlanMagnetConfig, usePlanMagnetContext, usePlanMagnetContextSafe, useWorkItemLinks, validateTrustedOrigin, waitForPlanMagnetClient };
|
|
1763
2647
|
//# sourceMappingURL=index.js.map
|
|
1764
2648
|
//# sourceMappingURL=index.js.map
|