@burdenoff/planmagnet-fe-sdk 2026.706.5 → 2026.706.7
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 +77 -4
- package/dist/index.js +678 -86
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,12 +6,13 @@ 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
8
|
import { gql } from '@apollo/client';
|
|
9
|
-
import { CircleX, CircleCheckBig, CircleDotDashed, CircleDot, Circle, CircleDashed, User, Link2, CalendarDays, ChevronUp, ChevronDown, ChevronLeft, ChevronRight, Flag, CalendarRange, List, LayoutGrid, Search,
|
|
9
|
+
import { CircleX, CircleCheckBig, CircleDotDashed, CircleDot, Circle, CircleDashed, User, Link2, CalendarDays, Trash2, ChevronUp, ChevronDown, ChevronLeft, ChevronRight, Flag, Plus, CalendarRange, List, LayoutGrid, Search, Pencil, SmilePlus } from 'lucide-react';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
11
|
import { twMerge } from 'tailwind-merge';
|
|
12
|
-
import {
|
|
12
|
+
import { startOfWeek, format, addDays, addWeeks, endOfWeek, eachDayOfInterval, addMonths, startOfMonth, endOfMonth, isToday, isSameMonth, startOfDay, parseISO, isValid } from 'date-fns';
|
|
13
13
|
import { cva } from 'class-variance-authority';
|
|
14
14
|
import { Card, CardContent } from '@burdenoff/fe-libs/ui';
|
|
15
|
+
import { enUS, ta, ar, es, zhCN, hi } from 'date-fns/locale';
|
|
15
16
|
|
|
16
17
|
var clientInstance = null;
|
|
17
18
|
function buildCache() {
|
|
@@ -507,11 +508,29 @@ var LIST_MILESTONES = gql`
|
|
|
507
508
|
}
|
|
508
509
|
${MILESTONE_FIELDS}
|
|
509
510
|
`;
|
|
511
|
+
var CREATE_MILESTONE = gql`
|
|
512
|
+
mutation CreateMilestone($input: PlanmagnetCreateMilestoneInput!) {
|
|
513
|
+
planmagnetCreateMilestone(input: $input) {
|
|
514
|
+
...MilestoneFields
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
${MILESTONE_FIELDS}
|
|
518
|
+
`;
|
|
510
519
|
var DELETE_WORK_ITEM = gql`
|
|
511
520
|
mutation DeleteWorkItem($id: ID!) {
|
|
512
521
|
planmagnetDeleteWorkItem(id: $id)
|
|
513
522
|
}
|
|
514
523
|
`;
|
|
524
|
+
var BULK_UPDATE_WORK_ITEMS = gql`
|
|
525
|
+
mutation BulkUpdateWorkItems($input: PlanmagnetBulkUpdateWorkItemsInput!) {
|
|
526
|
+
planmagnetBulkUpdateWorkItems(input: $input) {
|
|
527
|
+
id
|
|
528
|
+
status
|
|
529
|
+
priority
|
|
530
|
+
assigneeId
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
`;
|
|
515
534
|
var ENTITY_COMMENT_FIELDS = gql`
|
|
516
535
|
fragment EntityCommentFields on EntityComment {
|
|
517
536
|
id
|
|
@@ -530,6 +549,11 @@ var ENTITY_COMMENT_FIELDS = gql`
|
|
|
530
549
|
mentions {
|
|
531
550
|
mentionedUserId
|
|
532
551
|
}
|
|
552
|
+
reactionSummary {
|
|
553
|
+
emoji
|
|
554
|
+
count
|
|
555
|
+
userIds
|
|
556
|
+
}
|
|
533
557
|
}
|
|
534
558
|
`;
|
|
535
559
|
var GET_ENTITY_COMMENTS = gql`
|
|
@@ -556,6 +580,29 @@ var DELETE_ENTITY_COMMENT = gql`
|
|
|
556
580
|
deleteEntityComment(input: $input)
|
|
557
581
|
}
|
|
558
582
|
`;
|
|
583
|
+
var UPDATE_ENTITY_COMMENT = gql`
|
|
584
|
+
mutation UpdateEntityComment($input: UpdateEntityCommentInput!) {
|
|
585
|
+
updateEntityComment(input: $input) {
|
|
586
|
+
...EntityCommentFields
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
${ENTITY_COMMENT_FIELDS}
|
|
590
|
+
`;
|
|
591
|
+
var ADD_ENTITY_COMMENT_REACTION = gql`
|
|
592
|
+
mutation AddEntityCommentReaction($input: AddReactionInput!) {
|
|
593
|
+
addEntityCommentReaction(input: $input) {
|
|
594
|
+
id
|
|
595
|
+
commentId
|
|
596
|
+
emoji
|
|
597
|
+
userId
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
`;
|
|
601
|
+
var REMOVE_ENTITY_COMMENT_REACTION = gql`
|
|
602
|
+
mutation RemoveEntityCommentReaction($input: RemoveReactionInput!) {
|
|
603
|
+
removeEntityCommentReaction(input: $input)
|
|
604
|
+
}
|
|
605
|
+
`;
|
|
559
606
|
function cn(...inputs) {
|
|
560
607
|
return twMerge(clsx(inputs));
|
|
561
608
|
}
|
|
@@ -772,11 +819,21 @@ var DEFAULT_TASK_LABELS = {
|
|
|
772
819
|
overdue: "{n} overdue",
|
|
773
820
|
noDueDate: "No due date ({n})",
|
|
774
821
|
milestones: "Milestones",
|
|
822
|
+
addMilestone: "Add milestone",
|
|
823
|
+
milestoneNamePlaceholder: "Milestone name",
|
|
824
|
+
failedToCreateMilestone: "Failed to create milestone",
|
|
775
825
|
today: "Today",
|
|
776
826
|
week: "Week",
|
|
777
827
|
month: "Month",
|
|
778
828
|
tasksSingular: "task",
|
|
779
829
|
tasksPlural: "tasks",
|
|
830
|
+
bulkSelected: "{n} selected",
|
|
831
|
+
bulkSetStatus: "Set status",
|
|
832
|
+
bulkSetPriority: "Set priority",
|
|
833
|
+
bulkAssign: "Assign",
|
|
834
|
+
bulkDelete: "Delete",
|
|
835
|
+
bulkClear: "Clear",
|
|
836
|
+
bulkApplying: "Applying\u2026",
|
|
780
837
|
colTask: "Task",
|
|
781
838
|
colStatus: "Status",
|
|
782
839
|
colPriority: "Priority",
|
|
@@ -815,8 +872,13 @@ var DEFAULT_TASK_LABELS = {
|
|
|
815
872
|
noComments: "No comments yet",
|
|
816
873
|
you: "You",
|
|
817
874
|
editedTag: "(edited)",
|
|
875
|
+
editComment: "Edit",
|
|
876
|
+
saveComment: "Save",
|
|
877
|
+
addReaction: "React",
|
|
818
878
|
failedToComment: "Failed to post comment",
|
|
819
879
|
failedToDeleteComment: "Failed to delete comment",
|
|
880
|
+
failedToEditComment: "Failed to edit comment",
|
|
881
|
+
failedToReact: "Failed to update reaction",
|
|
820
882
|
reply: "Reply",
|
|
821
883
|
replyPlaceholder: "Write a reply\u2026",
|
|
822
884
|
repliesCount: "{n} replies",
|
|
@@ -1712,6 +1774,27 @@ var statusRank = {
|
|
|
1712
1774
|
DONE: 5,
|
|
1713
1775
|
CANCELLED: 6
|
|
1714
1776
|
};
|
|
1777
|
+
var bulkFieldClass = "rounded-md border border-input bg-background px-2 py-1 text-xs text-foreground focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50";
|
|
1778
|
+
function BulkCheckbox({
|
|
1779
|
+
checked,
|
|
1780
|
+
indeterminate = false,
|
|
1781
|
+
...props
|
|
1782
|
+
}) {
|
|
1783
|
+
const ref = useRef(null);
|
|
1784
|
+
useEffect(() => {
|
|
1785
|
+
if (ref.current) ref.current.indeterminate = indeterminate;
|
|
1786
|
+
}, [indeterminate]);
|
|
1787
|
+
return /* @__PURE__ */ jsx(
|
|
1788
|
+
"input",
|
|
1789
|
+
{
|
|
1790
|
+
ref,
|
|
1791
|
+
type: "checkbox",
|
|
1792
|
+
checked,
|
|
1793
|
+
className: "h-4 w-4 cursor-pointer accent-primary disabled:cursor-not-allowed disabled:opacity-50",
|
|
1794
|
+
...props
|
|
1795
|
+
}
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1715
1798
|
function columnLabel(col, L) {
|
|
1716
1799
|
switch (col) {
|
|
1717
1800
|
case "key":
|
|
@@ -1762,6 +1845,8 @@ function TaskListCore({
|
|
|
1762
1845
|
linkSourceProduct,
|
|
1763
1846
|
renderLinkIcon,
|
|
1764
1847
|
limit = 200,
|
|
1848
|
+
selectable = false,
|
|
1849
|
+
assigneeOptions,
|
|
1765
1850
|
className,
|
|
1766
1851
|
workspaceId: workspaceIdProp
|
|
1767
1852
|
}) {
|
|
@@ -1783,7 +1868,7 @@ function TaskListCore({
|
|
|
1783
1868
|
if (search) f.search = search;
|
|
1784
1869
|
return f;
|
|
1785
1870
|
}, [projectId, status, type, priority, assigneeId, search]);
|
|
1786
|
-
const { data, loading, error } = useQuery(
|
|
1871
|
+
const { data, loading, error, refetch } = useQuery(
|
|
1787
1872
|
LIST_WORK_ITEMS,
|
|
1788
1873
|
{
|
|
1789
1874
|
variables: {
|
|
@@ -1801,6 +1886,66 @@ function TaskListCore({
|
|
|
1801
1886
|
const key = sort.by;
|
|
1802
1887
|
return [...items].sort((a, b) => dir * compareTasks(a, b, key));
|
|
1803
1888
|
}, [data, sort]);
|
|
1889
|
+
const [selected, setSelected] = useState(
|
|
1890
|
+
() => /* @__PURE__ */ new Set()
|
|
1891
|
+
);
|
|
1892
|
+
const rowsKey = useMemo(() => rows.map((r) => r.id).join(","), [rows]);
|
|
1893
|
+
useEffect(() => {
|
|
1894
|
+
setSelected((prev) => prev.size ? /* @__PURE__ */ new Set() : prev);
|
|
1895
|
+
}, [rowsKey]);
|
|
1896
|
+
const [bulkUpdate, { loading: bulkUpdating }] = useMutation(
|
|
1897
|
+
BULK_UPDATE_WORK_ITEMS
|
|
1898
|
+
);
|
|
1899
|
+
const [bulkDelete, { loading: bulkDeleting }] = useMutation(DELETE_WORK_ITEM);
|
|
1900
|
+
const applying = bulkUpdating || bulkDeleting;
|
|
1901
|
+
const [bulkError, setBulkError] = useState(null);
|
|
1902
|
+
const allSelected = rows.length > 0 && rows.every((r) => selected.has(r.id));
|
|
1903
|
+
const someSelected = selected.size > 0 && !allSelected;
|
|
1904
|
+
const toggleRow = (id) => {
|
|
1905
|
+
setSelected((prev) => {
|
|
1906
|
+
const next = new Set(prev);
|
|
1907
|
+
if (next.has(id)) next.delete(id);
|
|
1908
|
+
else next.add(id);
|
|
1909
|
+
return next;
|
|
1910
|
+
});
|
|
1911
|
+
};
|
|
1912
|
+
const toggleAll = () => {
|
|
1913
|
+
setSelected(
|
|
1914
|
+
(prev) => prev.size === rows.length ? /* @__PURE__ */ new Set() : new Set(rows.map((r) => r.id))
|
|
1915
|
+
);
|
|
1916
|
+
};
|
|
1917
|
+
const clearSelection = () => {
|
|
1918
|
+
setSelected(/* @__PURE__ */ new Set());
|
|
1919
|
+
setBulkError(null);
|
|
1920
|
+
};
|
|
1921
|
+
const applyBulk = async (patch) => {
|
|
1922
|
+
if (selected.size === 0 || applying) return;
|
|
1923
|
+
setBulkError(null);
|
|
1924
|
+
try {
|
|
1925
|
+
await bulkUpdate({
|
|
1926
|
+
variables: { input: { ids: [...selected], ...patch } }
|
|
1927
|
+
});
|
|
1928
|
+
} catch {
|
|
1929
|
+
setBulkError(L.failedToSave);
|
|
1930
|
+
return;
|
|
1931
|
+
}
|
|
1932
|
+
await refetch().catch(() => {
|
|
1933
|
+
});
|
|
1934
|
+
clearSelection();
|
|
1935
|
+
};
|
|
1936
|
+
const applyBulkDelete = async () => {
|
|
1937
|
+
if (selected.size === 0 || applying) return;
|
|
1938
|
+
setBulkError(null);
|
|
1939
|
+
const results = await Promise.allSettled(
|
|
1940
|
+
[...selected].map((id) => bulkDelete({ variables: { id } }))
|
|
1941
|
+
);
|
|
1942
|
+
await refetch().catch(() => {
|
|
1943
|
+
});
|
|
1944
|
+
clearSelection();
|
|
1945
|
+
if (results.some((r) => r.status === "rejected")) {
|
|
1946
|
+
setBulkError(L.failedToDelete);
|
|
1947
|
+
}
|
|
1948
|
+
};
|
|
1804
1949
|
const toggleSort = (col) => {
|
|
1805
1950
|
const key = COLUMN_SORT[col];
|
|
1806
1951
|
if (!key) return;
|
|
@@ -1821,6 +1966,7 @@ function TaskListCore({
|
|
|
1821
1966
|
error.message
|
|
1822
1967
|
] });
|
|
1823
1968
|
}
|
|
1969
|
+
const colCount = columns.length + (selectable ? 1 : 0);
|
|
1824
1970
|
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-3", className), children: [
|
|
1825
1971
|
!hideHeader && /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
|
|
1826
1972
|
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-foreground", children: L.list }),
|
|
@@ -1830,56 +1976,160 @@ function TaskListCore({
|
|
|
1830
1976
|
rows.length === 1 ? L.tasksSingular : L.tasksPlural
|
|
1831
1977
|
] })
|
|
1832
1978
|
] }),
|
|
1979
|
+
selectable && selected.size > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 rounded-lg border border-border bg-muted/50 px-3 py-2 text-sm", children: [
|
|
1980
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: L.bulkSelected.replace("{n}", String(selected.size)) }),
|
|
1981
|
+
/* @__PURE__ */ jsxs(
|
|
1982
|
+
"select",
|
|
1983
|
+
{
|
|
1984
|
+
"aria-label": L.bulkSetStatus,
|
|
1985
|
+
disabled: applying,
|
|
1986
|
+
value: "",
|
|
1987
|
+
onChange: (e) => {
|
|
1988
|
+
const v = e.target.value;
|
|
1989
|
+
if (v) void applyBulk({ status: v });
|
|
1990
|
+
},
|
|
1991
|
+
className: bulkFieldClass,
|
|
1992
|
+
children: [
|
|
1993
|
+
/* @__PURE__ */ jsx("option", { value: "", children: L.bulkSetStatus }),
|
|
1994
|
+
TASK_STATUSES.map((s) => /* @__PURE__ */ jsx("option", { value: s, children: L.status[s] }, s))
|
|
1995
|
+
]
|
|
1996
|
+
}
|
|
1997
|
+
),
|
|
1998
|
+
/* @__PURE__ */ jsxs(
|
|
1999
|
+
"select",
|
|
2000
|
+
{
|
|
2001
|
+
"aria-label": L.bulkSetPriority,
|
|
2002
|
+
disabled: applying,
|
|
2003
|
+
value: "",
|
|
2004
|
+
onChange: (e) => {
|
|
2005
|
+
const v = e.target.value;
|
|
2006
|
+
if (v) void applyBulk({ priority: v });
|
|
2007
|
+
},
|
|
2008
|
+
className: bulkFieldClass,
|
|
2009
|
+
children: [
|
|
2010
|
+
/* @__PURE__ */ jsx("option", { value: "", children: L.bulkSetPriority }),
|
|
2011
|
+
TASK_PRIORITIES.map((p) => /* @__PURE__ */ jsx("option", { value: p, children: L.priority[p] }, p))
|
|
2012
|
+
]
|
|
2013
|
+
}
|
|
2014
|
+
),
|
|
2015
|
+
assigneeOptions && assigneeOptions.length > 0 && /* @__PURE__ */ jsxs(
|
|
2016
|
+
"select",
|
|
2017
|
+
{
|
|
2018
|
+
"aria-label": L.bulkAssign,
|
|
2019
|
+
disabled: applying,
|
|
2020
|
+
value: "",
|
|
2021
|
+
onChange: (e) => {
|
|
2022
|
+
const v = e.target.value;
|
|
2023
|
+
if (v) void applyBulk({ assigneeId: v });
|
|
2024
|
+
},
|
|
2025
|
+
className: bulkFieldClass,
|
|
2026
|
+
children: [
|
|
2027
|
+
/* @__PURE__ */ jsx("option", { value: "", children: L.bulkAssign }),
|
|
2028
|
+
assigneeOptions.map((o) => /* @__PURE__ */ jsx("option", { value: o.id, children: o.name }, o.id))
|
|
2029
|
+
]
|
|
2030
|
+
}
|
|
2031
|
+
),
|
|
2032
|
+
/* @__PURE__ */ jsxs(
|
|
2033
|
+
"button",
|
|
2034
|
+
{
|
|
2035
|
+
type: "button",
|
|
2036
|
+
disabled: applying,
|
|
2037
|
+
onClick: () => void applyBulkDelete(),
|
|
2038
|
+
className: "inline-flex items-center gap-1 rounded-md px-2 py-1 text-destructive hover:bg-destructive/10 disabled:opacity-50",
|
|
2039
|
+
children: [
|
|
2040
|
+
/* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2041
|
+
L.bulkDelete
|
|
2042
|
+
]
|
|
2043
|
+
}
|
|
2044
|
+
),
|
|
2045
|
+
/* @__PURE__ */ jsx(
|
|
2046
|
+
"button",
|
|
2047
|
+
{
|
|
2048
|
+
type: "button",
|
|
2049
|
+
disabled: applying,
|
|
2050
|
+
onClick: clearSelection,
|
|
2051
|
+
className: "rounded-md px-2 py-1 text-muted-foreground hover:bg-muted disabled:opacity-50",
|
|
2052
|
+
children: L.bulkClear
|
|
2053
|
+
}
|
|
2054
|
+
),
|
|
2055
|
+
applying && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: L.bulkApplying }),
|
|
2056
|
+
bulkError && /* @__PURE__ */ jsx("span", { className: "text-xs text-destructive", children: bulkError })
|
|
2057
|
+
] }),
|
|
1833
2058
|
/* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border border-border", children: /* @__PURE__ */ jsxs("table", { className: "w-full border-collapse text-sm", children: [
|
|
1834
|
-
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
return /* @__PURE__ */ jsx(
|
|
1838
|
-
"th",
|
|
2059
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted/50 text-left", children: [
|
|
2060
|
+
selectable && /* @__PURE__ */ jsx("th", { className: "w-9 px-3 py-2", children: /* @__PURE__ */ jsx(
|
|
2061
|
+
BulkCheckbox,
|
|
1839
2062
|
{
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
2063
|
+
checked: allSelected,
|
|
2064
|
+
indeterminate: someSelected,
|
|
2065
|
+
disabled: rows.length === 0 || applying,
|
|
2066
|
+
"aria-label": L.bulkSelected.replace("{n}", "all"),
|
|
2067
|
+
onChange: toggleAll
|
|
2068
|
+
}
|
|
2069
|
+
) }),
|
|
2070
|
+
columns.map((col) => {
|
|
2071
|
+
const sortable = Boolean(COLUMN_SORT[col]);
|
|
2072
|
+
const active = sort.by === COLUMN_SORT[col];
|
|
2073
|
+
return /* @__PURE__ */ jsx(
|
|
2074
|
+
"th",
|
|
2075
|
+
{
|
|
2076
|
+
onClick: sortable ? () => toggleSort(col) : void 0,
|
|
2077
|
+
className: cn(
|
|
2078
|
+
"px-3 py-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground",
|
|
2079
|
+
sortable && "cursor-pointer select-none hover:text-foreground"
|
|
2080
|
+
),
|
|
2081
|
+
children: /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
2082
|
+
columnLabel(col, L),
|
|
2083
|
+
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" }))
|
|
2084
|
+
] })
|
|
2085
|
+
},
|
|
2086
|
+
col
|
|
2087
|
+
);
|
|
2088
|
+
})
|
|
2089
|
+
] }) }),
|
|
1853
2090
|
/* @__PURE__ */ jsxs("tbody", { children: [
|
|
1854
2091
|
rows.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
|
|
1855
2092
|
"td",
|
|
1856
2093
|
{
|
|
1857
|
-
colSpan:
|
|
2094
|
+
colSpan: colCount,
|
|
1858
2095
|
className: "px-3 py-8 text-center text-muted-foreground",
|
|
1859
2096
|
children: L.noTasksFound
|
|
1860
2097
|
}
|
|
1861
2098
|
) }),
|
|
1862
|
-
rows.map((task) => /* @__PURE__ */
|
|
2099
|
+
rows.map((task) => /* @__PURE__ */ jsxs(
|
|
1863
2100
|
"tr",
|
|
1864
2101
|
{
|
|
1865
2102
|
onClick: onSelect ? () => onSelect(task.id) : void 0,
|
|
1866
2103
|
className: cn(
|
|
1867
2104
|
"border-b border-border last:border-0",
|
|
1868
|
-
onSelect && "cursor-pointer hover:bg-muted/40"
|
|
2105
|
+
onSelect && "cursor-pointer hover:bg-muted/40",
|
|
2106
|
+
selectable && selected.has(task.id) && "bg-primary/5"
|
|
1869
2107
|
),
|
|
1870
|
-
children:
|
|
1871
|
-
"td",
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
}
|
|
1880
|
-
},
|
|
1881
|
-
col
|
|
1882
|
-
|
|
2108
|
+
children: [
|
|
2109
|
+
selectable && /* @__PURE__ */ jsx("td", { className: "w-9 px-3 py-2 align-middle", children: /* @__PURE__ */ jsx(
|
|
2110
|
+
BulkCheckbox,
|
|
2111
|
+
{
|
|
2112
|
+
checked: selected.has(task.id),
|
|
2113
|
+
disabled: applying,
|
|
2114
|
+
"aria-label": task.title,
|
|
2115
|
+
onClick: (e) => e.stopPropagation(),
|
|
2116
|
+
onChange: () => toggleRow(task.id)
|
|
2117
|
+
}
|
|
2118
|
+
) }),
|
|
2119
|
+
columns.map((col) => /* @__PURE__ */ jsx(
|
|
2120
|
+
"td",
|
|
2121
|
+
{
|
|
2122
|
+
className: "px-3 py-2 align-middle text-foreground",
|
|
2123
|
+
children: renderCell(col, task, {
|
|
2124
|
+
resolveAssignee,
|
|
2125
|
+
links: linksByItem.get(task.id),
|
|
2126
|
+
renderLinkIcon,
|
|
2127
|
+
unassigned: L.unassigned
|
|
2128
|
+
})
|
|
2129
|
+
},
|
|
2130
|
+
col
|
|
2131
|
+
))
|
|
2132
|
+
]
|
|
1883
2133
|
},
|
|
1884
2134
|
task.id
|
|
1885
2135
|
))
|
|
@@ -1996,17 +2246,52 @@ function TaskList({
|
|
|
1996
2246
|
}
|
|
1997
2247
|
);
|
|
1998
2248
|
}
|
|
2249
|
+
function dayToLocalNoonIso(day) {
|
|
2250
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(day);
|
|
2251
|
+
if (!m) return void 0;
|
|
2252
|
+
return new Date(
|
|
2253
|
+
Number(m[1]),
|
|
2254
|
+
Number(m[2]) - 1,
|
|
2255
|
+
Number(m[3]),
|
|
2256
|
+
12,
|
|
2257
|
+
0,
|
|
2258
|
+
0
|
|
2259
|
+
).toISOString();
|
|
2260
|
+
}
|
|
1999
2261
|
function useMilestones(workspaceId, opts = {}) {
|
|
2000
2262
|
const { projectId, enabled = true, limit = 100 } = opts;
|
|
2001
|
-
const { data, loading } = useQuery(
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2263
|
+
const { data, loading, refetch } = useQuery(
|
|
2264
|
+
LIST_MILESTONES,
|
|
2265
|
+
{
|
|
2266
|
+
variables: {
|
|
2267
|
+
workspaceId,
|
|
2268
|
+
...projectId ? { filter: { projectId } } : {},
|
|
2269
|
+
pagination: { first: limit }
|
|
2270
|
+
},
|
|
2271
|
+
skip: !workspaceId || !enabled,
|
|
2272
|
+
errorPolicy: "all"
|
|
2273
|
+
}
|
|
2274
|
+
);
|
|
2275
|
+
const [createMutation, { loading: creating }] = useMutation(CREATE_MILESTONE);
|
|
2276
|
+
const createMilestone = useCallback(
|
|
2277
|
+
async (args) => {
|
|
2278
|
+
const iso = args.dueDate ? dayToLocalNoonIso(args.dueDate) : void 0;
|
|
2279
|
+
const res = await createMutation({
|
|
2280
|
+
variables: {
|
|
2281
|
+
input: {
|
|
2282
|
+
projectId: args.projectId,
|
|
2283
|
+
name: args.name,
|
|
2284
|
+
...iso ? { dueDate: iso } : {},
|
|
2285
|
+
...args.status ? { status: args.status } : {},
|
|
2286
|
+
...args.description ? { description: args.description } : {}
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
});
|
|
2290
|
+
await refetch();
|
|
2291
|
+
return res.data?.planmagnetCreateMilestone ?? null;
|
|
2006
2292
|
},
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
});
|
|
2293
|
+
[createMutation, refetch]
|
|
2294
|
+
);
|
|
2010
2295
|
const milestones = useMemo(
|
|
2011
2296
|
() => data?.planmagnetMilestones?.items ?? [],
|
|
2012
2297
|
[data]
|
|
@@ -2023,7 +2308,20 @@ function useMilestones(workspaceId, opts = {}) {
|
|
|
2023
2308
|
}
|
|
2024
2309
|
return map;
|
|
2025
2310
|
}, [milestones]);
|
|
2026
|
-
return { milestones, byDay, loading };
|
|
2311
|
+
return { milestones, byDay, loading, creating, createMilestone };
|
|
2312
|
+
}
|
|
2313
|
+
var LOCALE_MAP = {
|
|
2314
|
+
en: enUS,
|
|
2315
|
+
hi,
|
|
2316
|
+
zh: zhCN,
|
|
2317
|
+
es,
|
|
2318
|
+
ar,
|
|
2319
|
+
ta
|
|
2320
|
+
};
|
|
2321
|
+
function resolveDateLocale(lang) {
|
|
2322
|
+
if (!lang) return enUS;
|
|
2323
|
+
const primary = lang.toLowerCase().split(/[-_]/)[0];
|
|
2324
|
+
return LOCALE_MAP[primary] ?? enUS;
|
|
2027
2325
|
}
|
|
2028
2326
|
var WEEK_OPTS = { weekStartsOn: 1 };
|
|
2029
2327
|
function dayKey(d) {
|
|
@@ -2039,18 +2337,58 @@ function TaskTimelineCore({
|
|
|
2039
2337
|
onSelect,
|
|
2040
2338
|
resolveAssignee,
|
|
2041
2339
|
showMilestones = true,
|
|
2340
|
+
canCreateMilestone = false,
|
|
2341
|
+
milestoneProjectId,
|
|
2342
|
+
locale,
|
|
2042
2343
|
className,
|
|
2043
2344
|
workspaceId: workspaceIdProp
|
|
2044
2345
|
}) {
|
|
2045
2346
|
const ctx = usePlanMagnetContextSafe();
|
|
2046
2347
|
const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
|
|
2047
2348
|
const L = useTaskLabels();
|
|
2048
|
-
const
|
|
2349
|
+
const dfLocale = resolveDateLocale(locale);
|
|
2350
|
+
const weekdayHeaders = useMemo(() => {
|
|
2351
|
+
const weekStart = startOfWeek(new Date(2026, 0, 5), WEEK_OPTS);
|
|
2352
|
+
return Array.from(
|
|
2353
|
+
{ length: 7 },
|
|
2354
|
+
(_, i) => format(addDays(weekStart, i), "EEE", { locale: dfLocale })
|
|
2355
|
+
);
|
|
2356
|
+
}, [dfLocale]);
|
|
2357
|
+
const {
|
|
2358
|
+
milestones,
|
|
2359
|
+
byDay: milestonesByDay,
|
|
2360
|
+
creating: creatingMilestone,
|
|
2361
|
+
createMilestone
|
|
2362
|
+
} = useMilestones(workspaceId, {
|
|
2049
2363
|
projectId,
|
|
2050
2364
|
enabled: showMilestones
|
|
2051
2365
|
});
|
|
2052
2366
|
const [mode, setMode] = useState(defaultMode);
|
|
2053
2367
|
const [offset, setOffset] = useState(0);
|
|
2368
|
+
const milestoneProject = milestoneProjectId ?? projectId;
|
|
2369
|
+
const canAddMilestone = canCreateMilestone && Boolean(milestoneProject);
|
|
2370
|
+
const [showMilestoneForm, setShowMilestoneForm] = useState(false);
|
|
2371
|
+
const [milestoneName, setMilestoneName] = useState("");
|
|
2372
|
+
const [milestoneDue, setMilestoneDue] = useState("");
|
|
2373
|
+
const [milestoneError, setMilestoneError] = useState(null);
|
|
2374
|
+
const submitMilestone = async (e) => {
|
|
2375
|
+
e.preventDefault();
|
|
2376
|
+
const name = milestoneName.trim();
|
|
2377
|
+
if (!name || !milestoneProject) return;
|
|
2378
|
+
setMilestoneError(null);
|
|
2379
|
+
try {
|
|
2380
|
+
await createMilestone({
|
|
2381
|
+
projectId: milestoneProject,
|
|
2382
|
+
name,
|
|
2383
|
+
dueDate: milestoneDue || void 0
|
|
2384
|
+
});
|
|
2385
|
+
setMilestoneName("");
|
|
2386
|
+
setMilestoneDue("");
|
|
2387
|
+
setShowMilestoneForm(false);
|
|
2388
|
+
} catch {
|
|
2389
|
+
setMilestoneError(L.failedToCreateMilestone);
|
|
2390
|
+
}
|
|
2391
|
+
};
|
|
2054
2392
|
const { data, loading, error } = useQuery(
|
|
2055
2393
|
LIST_WORK_ITEMS,
|
|
2056
2394
|
{
|
|
@@ -2133,7 +2471,7 @@ function TaskTimelineCore({
|
|
|
2133
2471
|
error.message
|
|
2134
2472
|
] });
|
|
2135
2473
|
}
|
|
2136
|
-
const rangeLabel = mode === "week" ? `${format(rangeStart, "MMM d")} \u2013 ${format(rangeEnd, "MMM d")}` : format(anchor, "MMMM yyyy");
|
|
2474
|
+
const rangeLabel = mode === "week" ? `${format(rangeStart, "MMM d", { locale: dfLocale })} \u2013 ${format(rangeEnd, "MMM d", { locale: dfLocale })}` : format(anchor, "MMMM yyyy", { locale: dfLocale });
|
|
2137
2475
|
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-3", className), children: [
|
|
2138
2476
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
2139
2477
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
@@ -2198,12 +2536,82 @@ function TaskTimelineCore({
|
|
|
2198
2536
|
t.id
|
|
2199
2537
|
)) })
|
|
2200
2538
|
] }),
|
|
2201
|
-
milestones.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-primary/30 bg-primary/5 px-3 py-2", children: [
|
|
2202
|
-
/* @__PURE__ */ jsxs("
|
|
2203
|
-
/* @__PURE__ */
|
|
2204
|
-
|
|
2539
|
+
(milestones.length > 0 || canAddMilestone) && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-primary/30 bg-primary/5 px-3 py-2", children: [
|
|
2540
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [
|
|
2541
|
+
/* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1 text-xs font-semibold text-primary", children: [
|
|
2542
|
+
/* @__PURE__ */ jsx(Flag, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2543
|
+
L.milestones
|
|
2544
|
+
] }),
|
|
2545
|
+
canAddMilestone && /* @__PURE__ */ jsx(
|
|
2546
|
+
"button",
|
|
2547
|
+
{
|
|
2548
|
+
type: "button",
|
|
2549
|
+
onClick: () => {
|
|
2550
|
+
setShowMilestoneForm((v) => !v);
|
|
2551
|
+
setMilestoneError(null);
|
|
2552
|
+
},
|
|
2553
|
+
"aria-label": L.addMilestone,
|
|
2554
|
+
"aria-expanded": showMilestoneForm,
|
|
2555
|
+
title: L.addMilestone,
|
|
2556
|
+
className: "flex h-6 w-6 items-center justify-center rounded-md border border-primary/40 text-primary hover:bg-primary/10",
|
|
2557
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
|
|
2558
|
+
}
|
|
2559
|
+
)
|
|
2205
2560
|
] }),
|
|
2206
|
-
|
|
2561
|
+
showMilestoneForm && canAddMilestone && /* @__PURE__ */ jsxs(
|
|
2562
|
+
"form",
|
|
2563
|
+
{
|
|
2564
|
+
onSubmit: submitMilestone,
|
|
2565
|
+
className: "mb-2 flex flex-wrap items-center gap-2",
|
|
2566
|
+
children: [
|
|
2567
|
+
/* @__PURE__ */ jsx(
|
|
2568
|
+
"input",
|
|
2569
|
+
{
|
|
2570
|
+
type: "text",
|
|
2571
|
+
value: milestoneName,
|
|
2572
|
+
onChange: (e) => setMilestoneName(e.target.value),
|
|
2573
|
+
placeholder: L.milestoneNamePlaceholder,
|
|
2574
|
+
"aria-label": L.milestoneNamePlaceholder,
|
|
2575
|
+
autoFocus: true,
|
|
2576
|
+
className: "h-8 min-w-40 flex-1 rounded-md border border-border bg-background px-2 text-xs text-foreground"
|
|
2577
|
+
}
|
|
2578
|
+
),
|
|
2579
|
+
/* @__PURE__ */ jsx(
|
|
2580
|
+
"input",
|
|
2581
|
+
{
|
|
2582
|
+
type: "date",
|
|
2583
|
+
value: milestoneDue,
|
|
2584
|
+
onChange: (e) => setMilestoneDue(e.target.value),
|
|
2585
|
+
"aria-label": L.dueDateLabel,
|
|
2586
|
+
className: "h-8 rounded-md border border-border bg-background px-2 text-xs text-foreground"
|
|
2587
|
+
}
|
|
2588
|
+
),
|
|
2589
|
+
/* @__PURE__ */ jsx(
|
|
2590
|
+
"button",
|
|
2591
|
+
{
|
|
2592
|
+
type: "submit",
|
|
2593
|
+
disabled: !milestoneName.trim() || creatingMilestone,
|
|
2594
|
+
className: "h-8 rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
|
|
2595
|
+
children: creatingMilestone ? L.creating : L.addMilestone
|
|
2596
|
+
}
|
|
2597
|
+
),
|
|
2598
|
+
/* @__PURE__ */ jsx(
|
|
2599
|
+
"button",
|
|
2600
|
+
{
|
|
2601
|
+
type: "button",
|
|
2602
|
+
onClick: () => {
|
|
2603
|
+
setShowMilestoneForm(false);
|
|
2604
|
+
setMilestoneError(null);
|
|
2605
|
+
},
|
|
2606
|
+
className: "h-8 rounded-md border border-border px-3 text-xs font-medium text-foreground hover:bg-muted/50",
|
|
2607
|
+
children: L.cancel
|
|
2608
|
+
}
|
|
2609
|
+
)
|
|
2610
|
+
]
|
|
2611
|
+
}
|
|
2612
|
+
),
|
|
2613
|
+
milestoneError && /* @__PURE__ */ jsx("p", { className: "mb-1 text-[11px] text-destructive", children: milestoneError }),
|
|
2614
|
+
milestones.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: milestones.map((m) => /* @__PURE__ */ jsx(MilestoneFlag, { milestone: m }, m.id)) })
|
|
2207
2615
|
] }),
|
|
2208
2616
|
mode === "week" ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: days.map((day) => {
|
|
2209
2617
|
const items = byDay.get(dayKey(day)) ?? [];
|
|
@@ -2225,7 +2633,7 @@ function TaskTimelineCore({
|
|
|
2225
2633
|
"text-xs font-semibold uppercase",
|
|
2226
2634
|
today ? "text-primary" : "text-muted-foreground"
|
|
2227
2635
|
),
|
|
2228
|
-
children: format(day, "EEE")
|
|
2636
|
+
children: format(day, "EEE", { locale: dfLocale })
|
|
2229
2637
|
}
|
|
2230
2638
|
),
|
|
2231
2639
|
/* @__PURE__ */ jsx(
|
|
@@ -2235,7 +2643,7 @@ function TaskTimelineCore({
|
|
|
2235
2643
|
"text-sm font-semibold",
|
|
2236
2644
|
today ? "text-primary" : "text-foreground"
|
|
2237
2645
|
),
|
|
2238
|
-
children: format(day, "d")
|
|
2646
|
+
children: format(day, "d", { locale: dfLocale })
|
|
2239
2647
|
}
|
|
2240
2648
|
)
|
|
2241
2649
|
] }),
|
|
@@ -2256,13 +2664,13 @@ function TaskTimelineCore({
|
|
|
2256
2664
|
dayKey(day)
|
|
2257
2665
|
);
|
|
2258
2666
|
}) }) : /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-7 gap-1", children: [
|
|
2259
|
-
|
|
2667
|
+
weekdayHeaders.map((d, i) => /* @__PURE__ */ jsx(
|
|
2260
2668
|
"div",
|
|
2261
2669
|
{
|
|
2262
2670
|
className: "px-1 pb-1 text-center text-[10px] font-semibold uppercase text-muted-foreground",
|
|
2263
2671
|
children: d
|
|
2264
2672
|
},
|
|
2265
|
-
|
|
2673
|
+
i
|
|
2266
2674
|
)),
|
|
2267
2675
|
days.map((day) => {
|
|
2268
2676
|
const items = byDay.get(dayKey(day)) ?? [];
|
|
@@ -2284,7 +2692,7 @@ function TaskTimelineCore({
|
|
|
2284
2692
|
"text-[10px] font-semibold",
|
|
2285
2693
|
today ? "text-primary" : "text-muted-foreground"
|
|
2286
2694
|
),
|
|
2287
|
-
children: format(day, "d")
|
|
2695
|
+
children: format(day, "d", { locale: dfLocale })
|
|
2288
2696
|
}
|
|
2289
2697
|
),
|
|
2290
2698
|
items.slice(0, 3).map((t) => /* @__PURE__ */ jsx(
|
|
@@ -2627,6 +3035,7 @@ function CommentComposer({
|
|
|
2627
3035
|
) })
|
|
2628
3036
|
] });
|
|
2629
3037
|
}
|
|
3038
|
+
var REACTION_EMOJIS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F389}", "\u{1F440}"];
|
|
2630
3039
|
var labelClass2 = "text-xs font-medium uppercase tracking-wide text-muted-foreground";
|
|
2631
3040
|
var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2632
3041
|
function TaskCommentsCore({
|
|
@@ -2642,6 +3051,11 @@ function TaskCommentsCore({
|
|
|
2642
3051
|
const L = useTaskLabels();
|
|
2643
3052
|
const [error, setError] = useState(null);
|
|
2644
3053
|
const [replyingTo, setReplyingTo] = useState(null);
|
|
3054
|
+
const [editingId, setEditingId] = useState(null);
|
|
3055
|
+
const [editText, setEditText] = useState("");
|
|
3056
|
+
const [savingEdit, setSavingEdit] = useState(false);
|
|
3057
|
+
const [pickerOpenId, setPickerOpenId] = useState(null);
|
|
3058
|
+
const [reactingId, setReactingId] = useState(null);
|
|
2645
3059
|
const { data, loading, refetch } = useQuery(
|
|
2646
3060
|
GET_ENTITY_COMMENTS,
|
|
2647
3061
|
{
|
|
@@ -2684,6 +3098,13 @@ function TaskCommentsCore({
|
|
|
2684
3098
|
const [deleteComment] = useMutation(
|
|
2685
3099
|
DELETE_ENTITY_COMMENT
|
|
2686
3100
|
);
|
|
3101
|
+
const [updateComment] = useMutation(
|
|
3102
|
+
UPDATE_ENTITY_COMMENT
|
|
3103
|
+
);
|
|
3104
|
+
const [addReaction] = useMutation(
|
|
3105
|
+
ADD_ENTITY_COMMENT_REACTION
|
|
3106
|
+
);
|
|
3107
|
+
const [removeReaction] = useMutation(REMOVE_ENTITY_COMMENT_REACTION);
|
|
2687
3108
|
const authorName = (id) => id === currentActorId ? L.you : resolveAssignee?.(id)?.name ?? void 0;
|
|
2688
3109
|
const post = async ({ text, mentionUserIds }, parentCommentId) => {
|
|
2689
3110
|
setError(null);
|
|
@@ -2724,6 +3145,57 @@ function TaskCommentsCore({
|
|
|
2724
3145
|
setError(L.failedToDeleteComment);
|
|
2725
3146
|
}
|
|
2726
3147
|
};
|
|
3148
|
+
const startEdit = (c) => {
|
|
3149
|
+
setError(null);
|
|
3150
|
+
setEditingId(c.id);
|
|
3151
|
+
setEditText(c.content?.text ?? "");
|
|
3152
|
+
};
|
|
3153
|
+
const cancelEdit = () => {
|
|
3154
|
+
setEditingId(null);
|
|
3155
|
+
setEditText("");
|
|
3156
|
+
};
|
|
3157
|
+
const saveEdit = async (id) => {
|
|
3158
|
+
const text = editText.trim();
|
|
3159
|
+
if (!text || savingEdit) return;
|
|
3160
|
+
setError(null);
|
|
3161
|
+
setSavingEdit(true);
|
|
3162
|
+
try {
|
|
3163
|
+
await updateComment({
|
|
3164
|
+
variables: { input: { id, content: { text }, contentType: "TEXT" } }
|
|
3165
|
+
});
|
|
3166
|
+
await refetch();
|
|
3167
|
+
setEditingId(null);
|
|
3168
|
+
setEditText("");
|
|
3169
|
+
} catch {
|
|
3170
|
+
setError(L.failedToEditComment);
|
|
3171
|
+
} finally {
|
|
3172
|
+
setSavingEdit(false);
|
|
3173
|
+
}
|
|
3174
|
+
};
|
|
3175
|
+
const hasReacted = (c, emoji) => {
|
|
3176
|
+
if (!currentActorId) return false;
|
|
3177
|
+
const row = (c.reactionSummary ?? []).find((r) => r.emoji === emoji);
|
|
3178
|
+
return !!row?.userIds.includes(currentActorId);
|
|
3179
|
+
};
|
|
3180
|
+
const toggleReaction = async (c, emoji) => {
|
|
3181
|
+
if (!currentActorId || reactingId === c.id) return;
|
|
3182
|
+
setError(null);
|
|
3183
|
+
setPickerOpenId(null);
|
|
3184
|
+
setReactingId(c.id);
|
|
3185
|
+
const input = { commentId: c.id, emoji, userId: currentActorId };
|
|
3186
|
+
try {
|
|
3187
|
+
if (hasReacted(c, emoji)) {
|
|
3188
|
+
await removeReaction({ variables: { input } });
|
|
3189
|
+
} else {
|
|
3190
|
+
await addReaction({ variables: { input } });
|
|
3191
|
+
}
|
|
3192
|
+
await refetch();
|
|
3193
|
+
} catch {
|
|
3194
|
+
setError(L.failedToReact);
|
|
3195
|
+
} finally {
|
|
3196
|
+
setReactingId(null);
|
|
3197
|
+
}
|
|
3198
|
+
};
|
|
2727
3199
|
const renderBody = (c) => {
|
|
2728
3200
|
const text = c.content?.text ?? "";
|
|
2729
3201
|
const names = (members ?? []).map((m) => m.name).filter((n) => text.includes(`@${n}`));
|
|
@@ -2741,37 +3213,150 @@ function TaskCommentsCore({
|
|
|
2741
3213
|
) : /* @__PURE__ */ jsx("span", { children: part }, i)
|
|
2742
3214
|
);
|
|
2743
3215
|
};
|
|
2744
|
-
const
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
3216
|
+
const reactionBar = (c) => {
|
|
3217
|
+
const summary = (c.reactionSummary ?? []).filter((r) => r.count > 0);
|
|
3218
|
+
const canReact = !readonly && !!currentActorId;
|
|
3219
|
+
if (summary.length === 0 && !canReact) return null;
|
|
3220
|
+
return /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex flex-wrap items-center gap-1", children: [
|
|
3221
|
+
summary.map((r) => {
|
|
3222
|
+
const mine = hasReacted(c, r.emoji);
|
|
3223
|
+
return /* @__PURE__ */ jsxs(
|
|
2752
3224
|
"button",
|
|
2753
3225
|
{
|
|
2754
3226
|
type: "button",
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
3227
|
+
disabled: !canReact || reactingId === c.id,
|
|
3228
|
+
onClick: () => toggleReaction(c, r.emoji),
|
|
3229
|
+
"aria-label": `${r.emoji} ${r.count}`,
|
|
3230
|
+
"aria-pressed": mine,
|
|
3231
|
+
className: cn(
|
|
3232
|
+
"inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-xs",
|
|
3233
|
+
mine ? "border-primary bg-primary/10 text-primary" : "border-border text-muted-foreground",
|
|
3234
|
+
canReact ? "hover:border-primary" : "cursor-default"
|
|
3235
|
+
),
|
|
3236
|
+
children: [
|
|
3237
|
+
/* @__PURE__ */ jsx("span", { children: r.emoji }),
|
|
3238
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: r.count })
|
|
3239
|
+
]
|
|
3240
|
+
},
|
|
3241
|
+
r.emoji
|
|
3242
|
+
);
|
|
3243
|
+
}),
|
|
3244
|
+
canReact && /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
3245
|
+
/* @__PURE__ */ jsx(
|
|
3246
|
+
"button",
|
|
3247
|
+
{
|
|
3248
|
+
type: "button",
|
|
3249
|
+
onClick: () => setPickerOpenId((prev) => prev === c.id ? null : c.id),
|
|
3250
|
+
"aria-label": L.addReaction,
|
|
3251
|
+
"aria-expanded": pickerOpenId === c.id,
|
|
3252
|
+
title: L.addReaction,
|
|
3253
|
+
className: "inline-flex items-center rounded-full border border-border p-1 text-muted-foreground hover:border-primary hover:text-primary",
|
|
3254
|
+
children: /* @__PURE__ */ jsx(SmilePlus, { className: "h-3.5 w-3.5" })
|
|
3255
|
+
}
|
|
3256
|
+
),
|
|
3257
|
+
pickerOpenId === c.id && /* @__PURE__ */ jsx("div", { className: "absolute left-0 top-full z-20 mt-1 flex gap-1 rounded-md border border-border bg-popover p-1 shadow-md", children: REACTION_EMOJIS.map((emoji) => /* @__PURE__ */ jsx(
|
|
3258
|
+
"button",
|
|
3259
|
+
{
|
|
3260
|
+
type: "button",
|
|
3261
|
+
disabled: reactingId === c.id,
|
|
3262
|
+
onClick: () => toggleReaction(c, emoji),
|
|
3263
|
+
"aria-label": emoji,
|
|
3264
|
+
className: cn(
|
|
3265
|
+
"rounded px-1 py-0.5 text-sm hover:bg-muted",
|
|
3266
|
+
hasReacted(c, emoji) && "bg-primary/10"
|
|
3267
|
+
),
|
|
3268
|
+
children: emoji
|
|
3269
|
+
},
|
|
3270
|
+
emoji
|
|
3271
|
+
)) })
|
|
3272
|
+
] })
|
|
3273
|
+
] });
|
|
3274
|
+
};
|
|
3275
|
+
const commentBlock = (c, isReply) => {
|
|
3276
|
+
const isMine = c.authorId === currentActorId;
|
|
3277
|
+
const isEditing = editingId === c.id;
|
|
3278
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
3279
|
+
/* @__PURE__ */ jsx(Avatar, { id: c.authorId, name: authorName(c.authorId), size: "sm" }),
|
|
3280
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3281
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3282
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: authorName(c.authorId) ?? c.authorId.slice(0, 8) }),
|
|
3283
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: formatDateFull(c.createdAt) }),
|
|
3284
|
+
c.edited && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: L.editedTag }),
|
|
3285
|
+
!readonly && isMine && !isEditing && /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1", children: [
|
|
3286
|
+
/* @__PURE__ */ jsx(
|
|
3287
|
+
"button",
|
|
3288
|
+
{
|
|
3289
|
+
type: "button",
|
|
3290
|
+
onClick: () => startEdit(c),
|
|
3291
|
+
"aria-label": L.editComment,
|
|
3292
|
+
title: L.editComment,
|
|
3293
|
+
className: "rounded p-1 text-muted-foreground hover:text-foreground",
|
|
3294
|
+
children: /* @__PURE__ */ jsx(Pencil, { className: "h-3.5 w-3.5" })
|
|
3295
|
+
}
|
|
3296
|
+
),
|
|
3297
|
+
/* @__PURE__ */ jsx(
|
|
3298
|
+
"button",
|
|
3299
|
+
{
|
|
3300
|
+
type: "button",
|
|
3301
|
+
onClick: () => handleDelete(c.id),
|
|
3302
|
+
"aria-label": L.del,
|
|
3303
|
+
title: L.del,
|
|
3304
|
+
className: "rounded p-1 text-muted-foreground hover:text-destructive",
|
|
3305
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
3306
|
+
}
|
|
3307
|
+
)
|
|
3308
|
+
] })
|
|
3309
|
+
] }),
|
|
3310
|
+
isEditing ? /* @__PURE__ */ jsxs("div", { className: "mt-1 space-y-2", children: [
|
|
3311
|
+
/* @__PURE__ */ jsx(
|
|
3312
|
+
"textarea",
|
|
3313
|
+
{
|
|
3314
|
+
className: "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",
|
|
3315
|
+
rows: 2,
|
|
3316
|
+
value: editText,
|
|
3317
|
+
autoFocus: true,
|
|
3318
|
+
"aria-label": L.editComment,
|
|
3319
|
+
onChange: (e) => setEditText(e.target.value)
|
|
3320
|
+
}
|
|
3321
|
+
),
|
|
3322
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
3323
|
+
/* @__PURE__ */ jsx(
|
|
3324
|
+
"button",
|
|
3325
|
+
{
|
|
3326
|
+
type: "button",
|
|
3327
|
+
onClick: cancelEdit,
|
|
3328
|
+
className: "rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground hover:text-foreground",
|
|
3329
|
+
children: L.cancel
|
|
3330
|
+
}
|
|
3331
|
+
),
|
|
3332
|
+
/* @__PURE__ */ jsx(
|
|
3333
|
+
"button",
|
|
3334
|
+
{
|
|
3335
|
+
type: "button",
|
|
3336
|
+
onClick: () => saveEdit(c.id),
|
|
3337
|
+
disabled: savingEdit || !editText.trim(),
|
|
3338
|
+
className: cn(
|
|
3339
|
+
"rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground",
|
|
3340
|
+
(savingEdit || !editText.trim()) && "cursor-not-allowed opacity-50"
|
|
3341
|
+
),
|
|
3342
|
+
children: savingEdit ? L.saving : L.saveComment
|
|
3343
|
+
}
|
|
3344
|
+
)
|
|
3345
|
+
] })
|
|
3346
|
+
] }) : /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap break-words text-sm text-foreground", children: renderBody(c) }),
|
|
3347
|
+
!isEditing && reactionBar(c),
|
|
3348
|
+
!isReply && !readonly && !isEditing && /* @__PURE__ */ jsx(
|
|
3349
|
+
"button",
|
|
3350
|
+
{
|
|
3351
|
+
type: "button",
|
|
3352
|
+
onClick: () => setReplyingTo((prev) => prev === c.id ? null : c.id),
|
|
3353
|
+
className: "mt-1 text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
3354
|
+
children: L.reply
|
|
2760
3355
|
}
|
|
2761
3356
|
)
|
|
2762
|
-
] })
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
"button",
|
|
2766
|
-
{
|
|
2767
|
-
type: "button",
|
|
2768
|
-
onClick: () => setReplyingTo((prev) => prev === c.id ? null : c.id),
|
|
2769
|
-
className: "mt-1 text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
2770
|
-
children: L.reply
|
|
2771
|
-
}
|
|
2772
|
-
)
|
|
2773
|
-
] })
|
|
2774
|
-
] });
|
|
3357
|
+
] })
|
|
3358
|
+
] });
|
|
3359
|
+
};
|
|
2775
3360
|
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
|
|
2776
3361
|
/* @__PURE__ */ jsxs("div", { className: labelClass2, children: [
|
|
2777
3362
|
L.commentsLabel,
|
|
@@ -3342,6 +3927,8 @@ function TasksPageCore({
|
|
|
3342
3927
|
boardProjectId,
|
|
3343
3928
|
columns,
|
|
3344
3929
|
readonly,
|
|
3930
|
+
canCreateMilestone,
|
|
3931
|
+
locale,
|
|
3345
3932
|
hiddenFields,
|
|
3346
3933
|
views,
|
|
3347
3934
|
defaultView = "board",
|
|
@@ -3522,6 +4109,8 @@ function TasksPageCore({
|
|
|
3522
4109
|
search: search || void 0,
|
|
3523
4110
|
assigneeId: mineAssignee,
|
|
3524
4111
|
hideHeader: true,
|
|
4112
|
+
selectable: !readonly,
|
|
4113
|
+
assigneeOptions,
|
|
3525
4114
|
resolveAssignee,
|
|
3526
4115
|
linkSourceProduct,
|
|
3527
4116
|
renderLinkIcon,
|
|
@@ -3539,7 +4128,10 @@ function TasksPageCore({
|
|
|
3539
4128
|
search,
|
|
3540
4129
|
assigneeId: mineAssignee,
|
|
3541
4130
|
resolveAssignee,
|
|
3542
|
-
onSelect: handleSelect
|
|
4131
|
+
onSelect: handleSelect,
|
|
4132
|
+
canCreateMilestone: canCreateMilestone ?? !readonly,
|
|
4133
|
+
milestoneProjectId: boardProjectId ?? projectId,
|
|
4134
|
+
locale
|
|
3543
4135
|
}
|
|
3544
4136
|
),
|
|
3545
4137
|
showInlineDetail && /* @__PURE__ */ jsx("div", { className: "pb-6", children: detailView }),
|
|
@@ -3593,6 +4185,6 @@ function TasksPage({
|
|
|
3593
4185
|
);
|
|
3594
4186
|
}
|
|
3595
4187
|
|
|
3596
|
-
export { Avatar, Badge, CREATE_WORK_ITEM, CREATE_WORK_ITEM_LINK, CreateTask, CreateTaskCore, DEFAULT_BOARD_COLUMNS, DEFAULT_TASK_LABELS, DELETE_WORK_ITEM, DELETE_WORK_ITEM_LINK, Drawer, GET_PROJECTS, GET_WORK_ITEM, LIST_MILESTONES, LIST_WORK_ITEMS, LIST_WORK_ITEM_LINKS, LinkChip, MILESTONE_FIELDS, MOVE_WORK_ITEM, Modal, PRIORITY_META, PROJECT_SUMMARY_FIELDS, PlanMagnetProvider, PriorityIndicator, STATUS_META, StatusIcon, StatusPill, TASK_PRIORITIES, TASK_STATUSES, TASK_TYPES, TaskBoard, TaskBoardCore, TaskCard, TaskCommentsCore, TaskDetail, TaskDetailCore, TaskLabelsProvider, 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, mergeTaskLabels, resetPlanMagnetClient, resetPlanMagnetConfig, setPlanMagnetClient, setPlanMagnetConfig, useMilestones, usePlanMagnetContext, usePlanMagnetContextSafe, useTaskLabels, useWorkItemLinks, validateTrustedOrigin, waitForPlanMagnetClient };
|
|
4188
|
+
export { Avatar, BULK_UPDATE_WORK_ITEMS, Badge, CREATE_WORK_ITEM, CREATE_WORK_ITEM_LINK, CreateTask, CreateTaskCore, DEFAULT_BOARD_COLUMNS, DEFAULT_TASK_LABELS, DELETE_WORK_ITEM, DELETE_WORK_ITEM_LINK, Drawer, GET_PROJECTS, GET_WORK_ITEM, LIST_MILESTONES, LIST_WORK_ITEMS, LIST_WORK_ITEM_LINKS, LinkChip, MILESTONE_FIELDS, MOVE_WORK_ITEM, Modal, PRIORITY_META, PROJECT_SUMMARY_FIELDS, PlanMagnetProvider, PriorityIndicator, STATUS_META, StatusIcon, StatusPill, TASK_PRIORITIES, TASK_STATUSES, TASK_TYPES, TaskBoard, TaskBoardCore, TaskCard, TaskCommentsCore, TaskDetail, TaskDetailCore, TaskLabelsProvider, 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, mergeTaskLabels, resetPlanMagnetClient, resetPlanMagnetConfig, setPlanMagnetClient, setPlanMagnetConfig, useMilestones, usePlanMagnetContext, usePlanMagnetContextSafe, useTaskLabels, useWorkItemLinks, validateTrustedOrigin, waitForPlanMagnetClient };
|
|
3597
4189
|
//# sourceMappingURL=index.js.map
|
|
3598
4190
|
//# sourceMappingURL=index.js.map
|