@j3m-quantum/ui 1.9.1 → 1.11.1
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.cjs +1916 -192
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +261 -14
- package/dist/index.d.ts +261 -14
- package/dist/index.js +1887 -196
- package/dist/index.js.map +1 -1
- package/package.json +19 -10
package/dist/index.cjs
CHANGED
|
@@ -43,6 +43,15 @@ var ResizablePrimitive = require('react-resizable-panels');
|
|
|
43
43
|
var reactLeaflet = require('react-leaflet');
|
|
44
44
|
var reactTable = require('@tanstack/react-table');
|
|
45
45
|
var dateFns = require('date-fns');
|
|
46
|
+
var core = require('@dnd-kit/core');
|
|
47
|
+
var modifiers = require('@dnd-kit/modifiers');
|
|
48
|
+
var usehooks = require('@uidotdev/usehooks');
|
|
49
|
+
var jotai = require('jotai');
|
|
50
|
+
var throttle = require('lodash.throttle');
|
|
51
|
+
var sortable = require('@dnd-kit/sortable');
|
|
52
|
+
var utilities = require('@dnd-kit/utilities');
|
|
53
|
+
var reactDom = require('react-dom');
|
|
54
|
+
var tunnel = require('tunnel-rat');
|
|
46
55
|
|
|
47
56
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
48
57
|
|
|
@@ -93,6 +102,8 @@ var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespace(PopoverPrimitiv
|
|
|
93
102
|
var HoverCardPrimitive__namespace = /*#__PURE__*/_interopNamespace(HoverCardPrimitive);
|
|
94
103
|
var CollapsiblePrimitive__namespace = /*#__PURE__*/_interopNamespace(CollapsiblePrimitive);
|
|
95
104
|
var ResizablePrimitive__namespace = /*#__PURE__*/_interopNamespace(ResizablePrimitive);
|
|
105
|
+
var throttle__default = /*#__PURE__*/_interopDefault(throttle);
|
|
106
|
+
var tunnel__default = /*#__PURE__*/_interopDefault(tunnel);
|
|
96
107
|
|
|
97
108
|
// src/hooks/use-mobile.ts
|
|
98
109
|
var MOBILE_BREAKPOINT = 768;
|
|
@@ -6828,7 +6839,7 @@ function PlanningWeekCommentPopover({
|
|
|
6828
6839
|
onCommentClick(comment);
|
|
6829
6840
|
}
|
|
6830
6841
|
};
|
|
6831
|
-
const
|
|
6842
|
+
const formatDate3 = (date) => {
|
|
6832
6843
|
return new Intl.DateTimeFormat("en-US", {
|
|
6833
6844
|
month: "short",
|
|
6834
6845
|
day: "numeric",
|
|
@@ -6908,7 +6919,7 @@ function PlanningWeekCommentPopover({
|
|
|
6908
6919
|
] }),
|
|
6909
6920
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
6910
6921
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium", children: comment.author }),
|
|
6911
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children:
|
|
6922
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children: formatDate3(comment.createdAt) })
|
|
6912
6923
|
] }),
|
|
6913
6924
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-foreground", children: comment.text })
|
|
6914
6925
|
]
|
|
@@ -7435,7 +7446,7 @@ function DeliveryCommentPopover({
|
|
|
7435
7446
|
setNewCommentText("");
|
|
7436
7447
|
}
|
|
7437
7448
|
};
|
|
7438
|
-
const
|
|
7449
|
+
const formatDate3 = (date) => {
|
|
7439
7450
|
return new Intl.DateTimeFormat("en-US", {
|
|
7440
7451
|
month: "short",
|
|
7441
7452
|
day: "numeric",
|
|
@@ -7481,7 +7492,7 @@ function DeliveryCommentPopover({
|
|
|
7481
7492
|
/* @__PURE__ */ jsxRuntime.jsx(CollapsibleContent2, { className: "space-y-2 pt-2", children: comments.length > 0 ? comments.map((comment) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg bg-muted/50 p-3 space-y-2", children: [
|
|
7482
7493
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
7483
7494
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium", children: comment.author }),
|
|
7484
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children:
|
|
7495
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children: formatDate3(comment.createdAt) })
|
|
7485
7496
|
] }),
|
|
7486
7497
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-foreground", children: comment.text })
|
|
7487
7498
|
] }, comment.id)) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground text-center py-2", children: "No comments yet" }) })
|
|
@@ -7575,7 +7586,7 @@ function ProductionCommentSection({
|
|
|
7575
7586
|
setNewComment("");
|
|
7576
7587
|
}
|
|
7577
7588
|
};
|
|
7578
|
-
const
|
|
7589
|
+
const formatDate3 = (date) => {
|
|
7579
7590
|
return new Intl.DateTimeFormat("en-US", {
|
|
7580
7591
|
month: "short",
|
|
7581
7592
|
day: "numeric",
|
|
@@ -7587,7 +7598,7 @@ function ProductionCommentSection({
|
|
|
7587
7598
|
comments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: comments.map((comment) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg bg-muted/50 p-2.5 space-y-1", children: [
|
|
7588
7599
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
7589
7600
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium", children: comment.author }),
|
|
7590
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children:
|
|
7601
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children: formatDate3(comment.createdAt) })
|
|
7591
7602
|
] }),
|
|
7592
7603
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-foreground", children: comment.text })
|
|
7593
7604
|
] }, comment.id)) }),
|
|
@@ -8531,7 +8542,7 @@ function CommentPopover({
|
|
|
8531
8542
|
setNewCommentText("");
|
|
8532
8543
|
}
|
|
8533
8544
|
};
|
|
8534
|
-
const
|
|
8545
|
+
const formatDate3 = (date) => {
|
|
8535
8546
|
return new Intl.DateTimeFormat("en-US", {
|
|
8536
8547
|
month: "short",
|
|
8537
8548
|
day: "numeric",
|
|
@@ -8599,7 +8610,7 @@ function CommentPopover({
|
|
|
8599
8610
|
),
|
|
8600
8611
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
8601
8612
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium", children: comment.author }),
|
|
8602
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children:
|
|
8613
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children: formatDate3(comment.createdAt) })
|
|
8603
8614
|
] }),
|
|
8604
8615
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-foreground", children: comment.text })
|
|
8605
8616
|
]
|
|
@@ -9318,7 +9329,7 @@ function generateLoadingWeek(date) {
|
|
|
9318
9329
|
const today = /* @__PURE__ */ new Date();
|
|
9319
9330
|
today.setHours(0, 0, 0, 0);
|
|
9320
9331
|
const isCurrentWeek = monday <= today && today <= friday;
|
|
9321
|
-
const
|
|
9332
|
+
const formatDate3 = (d) => {
|
|
9322
9333
|
return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
|
|
9323
9334
|
};
|
|
9324
9335
|
return {
|
|
@@ -9326,7 +9337,7 @@ function generateLoadingWeek(date) {
|
|
|
9326
9337
|
label: `W${getLoadingISOWeek(monday).toString().padStart(2, "0")}`,
|
|
9327
9338
|
startDate: monday,
|
|
9328
9339
|
endDate: friday,
|
|
9329
|
-
dateRange: `${
|
|
9340
|
+
dateRange: `${formatDate3(monday)} - ${formatDate3(friday)}`,
|
|
9330
9341
|
isCurrentWeek
|
|
9331
9342
|
};
|
|
9332
9343
|
}
|
|
@@ -9418,36 +9429,41 @@ function getLeftStrokeStyles(state, hasRisk, isHovered) {
|
|
|
9418
9429
|
stroke: "border-l-[3px] border-l-red-500",
|
|
9419
9430
|
content: "text-foreground",
|
|
9420
9431
|
progressBg: "bg-red-500",
|
|
9421
|
-
iconColor: "text-red-600 dark:text-red-400"
|
|
9432
|
+
iconColor: "text-red-600 dark:text-red-400",
|
|
9433
|
+
cardBg: "bg-background"
|
|
9422
9434
|
};
|
|
9423
9435
|
}
|
|
9424
9436
|
switch (state) {
|
|
9425
9437
|
case "sent":
|
|
9426
9438
|
return {
|
|
9427
|
-
stroke: "border-l-[3px] border-l-green-500/
|
|
9428
|
-
content: "text-muted-foreground/
|
|
9429
|
-
progressBg: "bg-green-500",
|
|
9430
|
-
iconColor: "text-green-600 dark:text-green-400"
|
|
9439
|
+
stroke: "border-l-[3px] border-l-green-500/40",
|
|
9440
|
+
content: "text-muted-foreground/50",
|
|
9441
|
+
progressBg: "bg-green-500/50",
|
|
9442
|
+
iconColor: "text-green-600/50 dark:text-green-400/50",
|
|
9443
|
+
cardBg: "bg-muted/30"
|
|
9431
9444
|
};
|
|
9432
9445
|
case "ready":
|
|
9433
9446
|
return {
|
|
9434
9447
|
stroke: "border-l-[3px] border-l-green-500",
|
|
9435
9448
|
content: "text-foreground",
|
|
9436
9449
|
progressBg: "bg-green-500",
|
|
9437
|
-
iconColor: "text-green-600 dark:text-green-400"
|
|
9450
|
+
iconColor: "text-green-600 dark:text-green-400",
|
|
9451
|
+
cardBg: "bg-background"
|
|
9438
9452
|
};
|
|
9439
9453
|
default:
|
|
9440
9454
|
return {
|
|
9441
9455
|
stroke: isHovered ? "border-l-[3px] border-l-primary/50" : "border-l-[3px] border-l-border",
|
|
9442
9456
|
content: "text-foreground",
|
|
9443
9457
|
progressBg: "bg-primary",
|
|
9444
|
-
iconColor: "text-muted-foreground"
|
|
9458
|
+
iconColor: "text-muted-foreground",
|
|
9459
|
+
cardBg: "bg-background"
|
|
9445
9460
|
};
|
|
9446
9461
|
}
|
|
9447
9462
|
}
|
|
9448
9463
|
function DeliveryBadge({
|
|
9449
9464
|
delivery,
|
|
9450
9465
|
onClick,
|
|
9466
|
+
onCommentClick,
|
|
9451
9467
|
className
|
|
9452
9468
|
}) {
|
|
9453
9469
|
const [isHovered, setIsHovered] = React27__namespace.useState(false);
|
|
@@ -9510,70 +9526,125 @@ function DeliveryBadge({
|
|
|
9510
9526
|
onClick?.();
|
|
9511
9527
|
}
|
|
9512
9528
|
};
|
|
9529
|
+
const handleCommentClick = (e) => {
|
|
9530
|
+
e.stopPropagation();
|
|
9531
|
+
onCommentClick?.();
|
|
9532
|
+
};
|
|
9533
|
+
const amountColorClass = React27__namespace.useMemo(() => {
|
|
9534
|
+
if (visualState === "sent") {
|
|
9535
|
+
return "text-muted-foreground/40";
|
|
9536
|
+
}
|
|
9537
|
+
if (delivery.isReadyToUnload) {
|
|
9538
|
+
return "text-green-600 dark:text-green-400";
|
|
9539
|
+
}
|
|
9540
|
+
if (delivery.hasProductionRisk) {
|
|
9541
|
+
return "text-red-600 dark:text-red-400";
|
|
9542
|
+
}
|
|
9543
|
+
return "text-muted-foreground";
|
|
9544
|
+
}, [visualState, delivery.isReadyToUnload, delivery.hasProductionRisk]);
|
|
9513
9545
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9514
|
-
"
|
|
9546
|
+
"div",
|
|
9515
9547
|
{
|
|
9516
|
-
type: "button",
|
|
9517
|
-
onClick: handleClick,
|
|
9518
|
-
onKeyDown: handleKeyDown,
|
|
9519
|
-
onMouseEnter: () => setIsHovered(true),
|
|
9520
|
-
onMouseLeave: () => setIsHovered(false),
|
|
9521
9548
|
className: cn(
|
|
9522
|
-
// Position relative for comment
|
|
9549
|
+
// Position relative for comment button
|
|
9523
9550
|
"relative",
|
|
9524
9551
|
// Full-width in cell, 90° corners
|
|
9525
9552
|
"w-full rounded-none",
|
|
9526
9553
|
// Sizing using Quantum tokens:
|
|
9527
|
-
// - h-[
|
|
9528
|
-
// -
|
|
9529
|
-
// -
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
"
|
|
9554
|
+
// - min-h-[100px] card min-height
|
|
9555
|
+
// - pt-4 pb-3 = vertical padding (j3m.spacing.m / j3m.spacing.s)
|
|
9556
|
+
// - pl-4 = left padding (j3m.spacing.m)
|
|
9557
|
+
// - pr-2 = minimal right padding (progress bar extends further)
|
|
9558
|
+
"min-h-[100px] pt-4 pb-3 pl-4 pr-2",
|
|
9559
|
+
// Card base: dynamic background based on state, complete border
|
|
9560
|
+
styles.cardBg,
|
|
9561
|
+
"border border-border",
|
|
9535
9562
|
// Left stroke for status
|
|
9536
9563
|
styles.stroke,
|
|
9537
|
-
// Interactive states
|
|
9538
|
-
"
|
|
9539
|
-
"hover:-translate-y-0.5 hover:shadow-[var(--j3m-shadow-md)]",
|
|
9540
|
-
"active:translate-y-0 active:shadow-sm",
|
|
9541
|
-
// Focus state
|
|
9542
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1",
|
|
9543
|
-
// Greyed out for sent state
|
|
9544
|
-
visualState === "sent" && "opacity-60",
|
|
9564
|
+
// Interactive states (reduced for sent state)
|
|
9565
|
+
"transition-all duration-200 ease-out",
|
|
9566
|
+
visualState !== "sent" && "hover:-translate-y-0.5 hover:shadow-[var(--j3m-shadow-md)]",
|
|
9545
9567
|
className
|
|
9546
9568
|
),
|
|
9547
9569
|
children: [
|
|
9548
|
-
|
|
9549
|
-
|
|
9570
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9571
|
+
Button,
|
|
9550
9572
|
{
|
|
9551
|
-
|
|
9552
|
-
|
|
9573
|
+
type: "button",
|
|
9574
|
+
variant: "ghost",
|
|
9575
|
+
size: "icon",
|
|
9576
|
+
className: cn(
|
|
9577
|
+
"absolute top-2 right-2",
|
|
9578
|
+
// 44px touch target for accessibility
|
|
9579
|
+
"h-11 w-11",
|
|
9580
|
+
"rounded-full",
|
|
9581
|
+
hasComments && "text-primary"
|
|
9582
|
+
),
|
|
9583
|
+
onClick: handleCommentClick,
|
|
9584
|
+
"aria-label": hasComments ? `${delivery.comments.length} comments` : "Add comment",
|
|
9585
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
9586
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-5 w-5" }),
|
|
9587
|
+
hasComments && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9588
|
+
"span",
|
|
9589
|
+
{
|
|
9590
|
+
className: "absolute -top-1 -right-1 h-2.5 w-2.5 rounded-full bg-primary border-2 border-background",
|
|
9591
|
+
"aria-hidden": "true"
|
|
9592
|
+
}
|
|
9593
|
+
)
|
|
9594
|
+
] })
|
|
9553
9595
|
}
|
|
9554
9596
|
),
|
|
9555
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
"
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
"
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9597
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9598
|
+
"button",
|
|
9599
|
+
{
|
|
9600
|
+
type: "button",
|
|
9601
|
+
onClick: handleClick,
|
|
9602
|
+
onKeyDown: handleKeyDown,
|
|
9603
|
+
onMouseEnter: () => setIsHovered(true),
|
|
9604
|
+
onMouseLeave: () => setIsHovered(false),
|
|
9605
|
+
className: cn(
|
|
9606
|
+
// Full width, no background (inherits from parent)
|
|
9607
|
+
"w-full bg-transparent text-left",
|
|
9608
|
+
// Layout - vertical stack
|
|
9609
|
+
"flex flex-col gap-2",
|
|
9610
|
+
// Interactive states
|
|
9611
|
+
"cursor-pointer",
|
|
9612
|
+
"active:translate-y-0 active:shadow-sm",
|
|
9613
|
+
// Focus state
|
|
9614
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-1"
|
|
9615
|
+
),
|
|
9616
|
+
children: [
|
|
9617
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 overflow-hidden pr-12", children: [
|
|
9618
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-sm font-semibold truncate", styles.content), children: prefixTitle }),
|
|
9619
|
+
delivery.hasProductionRisk && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-4 w-4 text-red-500 shrink-0" }),
|
|
9620
|
+
delivery.supplierName && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
|
|
9621
|
+
"text-xs truncate ml-auto",
|
|
9622
|
+
visualState === "sent" ? "text-muted-foreground/40" : "text-muted-foreground"
|
|
9623
|
+
), children: delivery.supplierName })
|
|
9624
|
+
] }),
|
|
9625
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pr-14", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 bg-black/10 dark:bg-white/10 rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9626
|
+
"div",
|
|
9627
|
+
{
|
|
9628
|
+
className: cn("h-full rounded-full transition-all", styles.progressBg),
|
|
9629
|
+
style: { width: `${productionProgress}%` }
|
|
9630
|
+
}
|
|
9631
|
+
) }) }),
|
|
9632
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
9633
|
+
productionDisplay && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
9634
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Factory, { className: cn("h-3 w-3 shrink-0", styles.iconColor) }),
|
|
9635
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
|
|
9636
|
+
"text-[11px] tabular-nums font-medium",
|
|
9637
|
+
amountColorClass
|
|
9638
|
+
), children: productionDisplay })
|
|
9639
|
+
] }),
|
|
9640
|
+
visualState === "sent" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 ml-auto", children: [
|
|
9641
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3.5 w-3.5 text-green-600/60 dark:text-green-400/60 shrink-0" }),
|
|
9642
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium text-muted-foreground/50 uppercase tracking-wide", children: "Sent" })
|
|
9643
|
+
] })
|
|
9644
|
+
] })
|
|
9645
|
+
]
|
|
9646
|
+
}
|
|
9647
|
+
)
|
|
9577
9648
|
]
|
|
9578
9649
|
}
|
|
9579
9650
|
);
|
|
@@ -9583,7 +9654,7 @@ function WeeklyLoadingView({
|
|
|
9583
9654
|
deliveries,
|
|
9584
9655
|
onDeliveryClick,
|
|
9585
9656
|
onWeekChange,
|
|
9586
|
-
|
|
9657
|
+
onDeliveryCommentClick,
|
|
9587
9658
|
showNavigation = true,
|
|
9588
9659
|
className
|
|
9589
9660
|
}) {
|
|
@@ -9614,20 +9685,6 @@ function WeeklyLoadingView({
|
|
|
9614
9685
|
}
|
|
9615
9686
|
return grouped;
|
|
9616
9687
|
}, [deliveries]);
|
|
9617
|
-
const commentCountByDay = React27__namespace.useMemo(() => {
|
|
9618
|
-
const counts = /* @__PURE__ */ new Map();
|
|
9619
|
-
for (let i = 1; i <= 5; i++) {
|
|
9620
|
-
counts.set(i, 0);
|
|
9621
|
-
}
|
|
9622
|
-
for (const delivery of deliveries) {
|
|
9623
|
-
const dayOfWeek = delivery.date.getDay();
|
|
9624
|
-
if (dayOfWeek >= 1 && dayOfWeek <= 5) {
|
|
9625
|
-
const current = counts.get(dayOfWeek) ?? 0;
|
|
9626
|
-
counts.set(dayOfWeek, current + delivery.comments.length);
|
|
9627
|
-
}
|
|
9628
|
-
}
|
|
9629
|
-
return counts;
|
|
9630
|
-
}, [deliveries]);
|
|
9631
9688
|
const totalDeliveries = deliveries.length;
|
|
9632
9689
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col", className), children: [
|
|
9633
9690
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 border-b border-border p-4 lg:flex-row lg:items-center lg:justify-between", children: [
|
|
@@ -9692,54 +9749,27 @@ function WeeklyLoadingView({
|
|
|
9692
9749
|
] }),
|
|
9693
9750
|
/* @__PURE__ */ jsxRuntime.jsxs(ScrollArea, { className: "flex-1", children: [
|
|
9694
9751
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "hidden sm:block", children: [
|
|
9695
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-5 border-b border-border bg-muted/30", children: weekDays.map(({ date, dayOfWeek, isToday: dayIsToday }) =>
|
|
9696
|
-
|
|
9697
|
-
|
|
9698
|
-
|
|
9699
|
-
|
|
9700
|
-
|
|
9701
|
-
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
children:
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9717
|
-
Button,
|
|
9718
|
-
{
|
|
9719
|
-
variant: "ghost",
|
|
9720
|
-
size: "icon",
|
|
9721
|
-
className: cn(
|
|
9722
|
-
"absolute top-1 right-1 h-7 w-7",
|
|
9723
|
-
dayCommentCount > 0 && "text-primary"
|
|
9724
|
-
),
|
|
9725
|
-
onClick: (e) => {
|
|
9726
|
-
e.stopPropagation();
|
|
9727
|
-
onDayCommentClick?.(dayOfWeek, date);
|
|
9728
|
-
},
|
|
9729
|
-
children: [
|
|
9730
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
9731
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-4 w-4" }),
|
|
9732
|
-
dayCommentCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 h-2 w-2 rounded-full bg-primary" })
|
|
9733
|
-
] }),
|
|
9734
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: dayCommentCount > 0 ? `${dayCommentCount} comments on ${getShortDayLabel(dayOfWeek)}` : `Add comment for ${getShortDayLabel(dayOfWeek)}` })
|
|
9735
|
-
]
|
|
9736
|
-
}
|
|
9737
|
-
)
|
|
9738
|
-
]
|
|
9739
|
-
},
|
|
9740
|
-
dayOfWeek
|
|
9741
|
-
);
|
|
9742
|
-
}) }),
|
|
9752
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-5 border-b border-border bg-muted/30", children: weekDays.map(({ date, dayOfWeek, isToday: dayIsToday }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9753
|
+
"div",
|
|
9754
|
+
{
|
|
9755
|
+
className: cn(
|
|
9756
|
+
// Compact padding
|
|
9757
|
+
"flex flex-col items-center justify-center py-2 px-2",
|
|
9758
|
+
dayIsToday && "bg-primary/5"
|
|
9759
|
+
),
|
|
9760
|
+
children: [
|
|
9761
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
|
|
9762
|
+
"text-[11px] font-semibold uppercase tracking-wide leading-none",
|
|
9763
|
+
dayIsToday ? "text-primary" : "text-muted-foreground"
|
|
9764
|
+
), children: getShortDayLabel(dayOfWeek) }),
|
|
9765
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
|
|
9766
|
+
"text-lg font-medium tabular-nums leading-tight mt-0.5",
|
|
9767
|
+
dayIsToday ? "text-primary" : "text-foreground"
|
|
9768
|
+
), children: date.getDate() })
|
|
9769
|
+
]
|
|
9770
|
+
},
|
|
9771
|
+
dayOfWeek
|
|
9772
|
+
)) }),
|
|
9743
9773
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-5", children: weekDays.map(({ dayOfWeek, isToday: dayIsToday }) => {
|
|
9744
9774
|
const dayDeliveries = deliveriesByDay.get(dayOfWeek) ?? [];
|
|
9745
9775
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -9754,7 +9784,8 @@ function WeeklyLoadingView({
|
|
|
9754
9784
|
DeliveryBadge,
|
|
9755
9785
|
{
|
|
9756
9786
|
delivery,
|
|
9757
|
-
onClick: () => onDeliveryClick?.(delivery)
|
|
9787
|
+
onClick: () => onDeliveryClick?.(delivery),
|
|
9788
|
+
onCommentClick: () => onDeliveryCommentClick?.(delivery)
|
|
9758
9789
|
},
|
|
9759
9790
|
delivery.id
|
|
9760
9791
|
)) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-6 text-sm text-muted-foreground/40", children: "\u2014" }) })
|
|
@@ -9765,7 +9796,6 @@ function WeeklyLoadingView({
|
|
|
9765
9796
|
] }),
|
|
9766
9797
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden divide-y divide-border", children: weekDays.map(({ date, dayOfWeek, isToday: dayIsToday }) => {
|
|
9767
9798
|
const dayDeliveries = deliveriesByDay.get(dayOfWeek) ?? [];
|
|
9768
|
-
const dayCommentCount = commentCountByDay.get(dayOfWeek) ?? 0;
|
|
9769
9799
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9770
9800
|
"div",
|
|
9771
9801
|
{
|
|
@@ -9774,52 +9804,31 @@ function WeeklyLoadingView({
|
|
|
9774
9804
|
),
|
|
9775
9805
|
children: [
|
|
9776
9806
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-2", children: [
|
|
9777
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-
|
|
9778
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9779
|
-
|
|
9780
|
-
|
|
9781
|
-
|
|
9782
|
-
|
|
9783
|
-
|
|
9784
|
-
|
|
9785
|
-
|
|
9786
|
-
|
|
9787
|
-
] }),
|
|
9788
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
9789
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
9790
|
-
dayDeliveries.length,
|
|
9791
|
-
" ",
|
|
9792
|
-
dayDeliveries.length === 1 ? "delivery" : "deliveries"
|
|
9807
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
9808
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center min-w-[40px]", children: [
|
|
9809
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
|
|
9810
|
+
"text-[11px] font-semibold uppercase tracking-wide leading-none",
|
|
9811
|
+
dayIsToday ? "text-primary" : "text-muted-foreground"
|
|
9812
|
+
), children: getShortDayLabel(dayOfWeek) }),
|
|
9813
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn(
|
|
9814
|
+
"text-lg font-medium tabular-nums leading-tight mt-0.5",
|
|
9815
|
+
dayIsToday ? "text-primary" : "text-foreground"
|
|
9816
|
+
), children: date.getDate() })
|
|
9793
9817
|
] }),
|
|
9794
|
-
/* @__PURE__ */ jsxRuntime.
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
"h-7 w-7",
|
|
9801
|
-
dayCommentCount > 0 && "text-primary"
|
|
9802
|
-
),
|
|
9803
|
-
onClick: (e) => {
|
|
9804
|
-
e.stopPropagation();
|
|
9805
|
-
onDayCommentClick?.(dayOfWeek, date);
|
|
9806
|
-
},
|
|
9807
|
-
children: [
|
|
9808
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
9809
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-4 w-4" }),
|
|
9810
|
-
dayCommentCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 h-2 w-2 rounded-full bg-primary" })
|
|
9811
|
-
] }),
|
|
9812
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Comments" })
|
|
9813
|
-
]
|
|
9814
|
-
}
|
|
9815
|
-
)
|
|
9818
|
+
dayIsToday && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-primary font-medium bg-primary/10 px-2 py-0.5 rounded", children: "Today" })
|
|
9819
|
+
] }),
|
|
9820
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
9821
|
+
dayDeliveries.length,
|
|
9822
|
+
" ",
|
|
9823
|
+
dayDeliveries.length === 1 ? "delivery" : "deliveries"
|
|
9816
9824
|
] })
|
|
9817
9825
|
] }),
|
|
9818
9826
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-3 p-3 pt-0", children: dayDeliveries.length > 0 ? dayDeliveries.map((delivery) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
9819
9827
|
DeliveryBadge,
|
|
9820
9828
|
{
|
|
9821
9829
|
delivery,
|
|
9822
|
-
onClick: () => onDeliveryClick?.(delivery)
|
|
9830
|
+
onClick: () => onDeliveryClick?.(delivery),
|
|
9831
|
+
onCommentClick: () => onDeliveryCommentClick?.(delivery)
|
|
9823
9832
|
},
|
|
9824
9833
|
delivery.id
|
|
9825
9834
|
)) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-muted-foreground/40 py-4 text-center", children: "\u2014" }) })
|
|
@@ -9929,7 +9938,7 @@ function AddCommentDialog({
|
|
|
9929
9938
|
}, [open]);
|
|
9930
9939
|
return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "sm:max-w-md", children: [
|
|
9931
9940
|
/* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
|
|
9932
|
-
/* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Add pre-
|
|
9941
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Add pre-loading note" }),
|
|
9933
9942
|
/* @__PURE__ */ jsxRuntime.jsxs(DialogDescription, { children: [
|
|
9934
9943
|
"Add a note for ",
|
|
9935
9944
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: delivery.label }),
|
|
@@ -9945,7 +9954,7 @@ function AddCommentDialog({
|
|
|
9945
9954
|
Textarea,
|
|
9946
9955
|
{
|
|
9947
9956
|
id: "comment-text",
|
|
9948
|
-
placeholder: "Add a note before
|
|
9957
|
+
placeholder: "Add a note before loading...",
|
|
9949
9958
|
value: commentText,
|
|
9950
9959
|
onChange: (e) => setCommentText(e.target.value),
|
|
9951
9960
|
onKeyDown: handleKeyDown,
|
|
@@ -9987,7 +9996,7 @@ function CommentsSection({
|
|
|
9987
9996
|
}) {
|
|
9988
9997
|
const [viewCommentsOpen, setViewCommentsOpen] = React27__namespace.useState(true);
|
|
9989
9998
|
const [addDialogOpen, setAddDialogOpen] = React27__namespace.useState(false);
|
|
9990
|
-
const
|
|
9999
|
+
const formatDate3 = (date) => {
|
|
9991
10000
|
return new Intl.DateTimeFormat("en-US", {
|
|
9992
10001
|
month: "short",
|
|
9993
10002
|
day: "numeric",
|
|
@@ -10000,7 +10009,7 @@ function CommentsSection({
|
|
|
10000
10009
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
10001
10010
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
10002
10011
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquare, { className: "h-4 w-4 text-muted-foreground" }),
|
|
10003
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: "Notes before
|
|
10012
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: "Notes before loading" }),
|
|
10004
10013
|
comments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", className: "text-[10px] h-5", children: comments.length })
|
|
10005
10014
|
] }),
|
|
10006
10015
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -10031,11 +10040,11 @@ function CommentsSection({
|
|
|
10031
10040
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium", children: comment.author }),
|
|
10032
10041
|
comment.supplierName && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "outline", className: "text-[9px] px-1.5 py-0 h-4", children: comment.prefixName ? `${comment.supplierName} - ${comment.prefixName}` : comment.supplierName })
|
|
10033
10042
|
] }),
|
|
10034
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children:
|
|
10043
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children: formatDate3(comment.createdAt) })
|
|
10035
10044
|
] }),
|
|
10036
10045
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", children: comment.text })
|
|
10037
10046
|
] }, comment.id)) })
|
|
10038
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-dashed p-4 text-center text-sm text-muted-foreground", children: "No pre-
|
|
10047
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-dashed p-4 text-center text-sm text-muted-foreground", children: "No pre-loading notes yet. Add notes before confirming the load." })
|
|
10039
10048
|
] }),
|
|
10040
10049
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10041
10050
|
AddCommentDialog,
|
|
@@ -10098,7 +10107,7 @@ function DeliveryDetailPage({
|
|
|
10098
10107
|
const addons = delivery.elements.filter((e) => e.status === "addon");
|
|
10099
10108
|
return { loaded, missing, moved, addons };
|
|
10100
10109
|
}, [delivery.elements]);
|
|
10101
|
-
const
|
|
10110
|
+
const preLoadingComments = delivery.comments.filter((c) => c.context === "pre_unloading");
|
|
10102
10111
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full", children: [
|
|
10103
10112
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 px-4 py-3 border-b bg-background sticky top-0 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10104
10113
|
Button,
|
|
@@ -10147,7 +10156,7 @@ function DeliveryDetailPage({
|
|
|
10147
10156
|
{
|
|
10148
10157
|
variant: "outline",
|
|
10149
10158
|
className: "bg-green-100 dark:bg-green-900/50 border-green-300 dark:border-green-700 text-green-700 dark:text-green-300",
|
|
10150
|
-
children: "Ready to
|
|
10159
|
+
children: "Ready to load"
|
|
10151
10160
|
}
|
|
10152
10161
|
)
|
|
10153
10162
|
] })
|
|
@@ -10238,13 +10247,13 @@ function DeliveryDetailPage({
|
|
|
10238
10247
|
] })
|
|
10239
10248
|
] })
|
|
10240
10249
|
] }),
|
|
10241
|
-
|
|
10250
|
+
(elementsByStatus.loaded.length > 0 || elementsByStatus.missing.length > 0 || elementsByStatus.moved.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
|
|
10242
10251
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
10243
10252
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Package, { className: "h-4 w-4 text-muted-foreground" }),
|
|
10244
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: "
|
|
10253
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: "What should be packed" }),
|
|
10245
10254
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground ml-auto", children: [
|
|
10246
|
-
|
|
10247
|
-
"
|
|
10255
|
+
elementsByStatus.loaded.length + elementsByStatus.missing.length + elementsByStatus.moved.length,
|
|
10256
|
+
" items"
|
|
10248
10257
|
] })
|
|
10249
10258
|
] }),
|
|
10250
10259
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs(Table, { children: [
|
|
@@ -10255,7 +10264,7 @@ function DeliveryDetailPage({
|
|
|
10255
10264
|
/* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "font-semibold text-right", children: "Size (m\xB2)" }),
|
|
10256
10265
|
/* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "font-semibold text-center", children: "Status" })
|
|
10257
10266
|
] }) }),
|
|
10258
|
-
/* @__PURE__ */ jsxRuntime.jsx(TableBody, { children:
|
|
10267
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableBody, { children: [...elementsByStatus.loaded, ...elementsByStatus.missing, ...elementsByStatus.moved].map((element) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10259
10268
|
TableRow,
|
|
10260
10269
|
{
|
|
10261
10270
|
className: getElementRowBg(element.status),
|
|
@@ -10283,10 +10292,6 @@ function DeliveryDetailPage({
|
|
|
10283
10292
|
element.status === "moved" && element.actualDeliveryLabel && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[9px] text-blue-600 dark:text-blue-400", children: [
|
|
10284
10293
|
"\u2192 ",
|
|
10285
10294
|
element.actualDeliveryLabel
|
|
10286
|
-
] }),
|
|
10287
|
-
element.status === "addon" && element.originalDeliveryLabel && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[9px] text-purple-600 dark:text-purple-400", children: [
|
|
10288
|
-
"from ",
|
|
10289
|
-
element.originalDeliveryLabel
|
|
10290
10295
|
] })
|
|
10291
10296
|
] }) })
|
|
10292
10297
|
]
|
|
@@ -10295,10 +10300,48 @@ function DeliveryDetailPage({
|
|
|
10295
10300
|
)) })
|
|
10296
10301
|
] }) })
|
|
10297
10302
|
] }),
|
|
10303
|
+
elementsByStatus.addons.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-3", children: [
|
|
10304
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
10305
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4 text-purple-600 dark:text-purple-400" }),
|
|
10306
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: "Extra items (moved to this delivery)" }),
|
|
10307
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground ml-auto", children: [
|
|
10308
|
+
elementsByStatus.addons.length,
|
|
10309
|
+
" items"
|
|
10310
|
+
] })
|
|
10311
|
+
] }),
|
|
10312
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "These items were originally planned for other deliveries but have been moved to this one." }),
|
|
10313
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-purple-200 dark:border-purple-800 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsxs(Table, { children: [
|
|
10314
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs(TableRow, { className: "bg-purple-50/50 dark:bg-purple-950/30 hover:bg-purple-50/50 dark:hover:bg-purple-950/30", children: [
|
|
10315
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "font-semibold", children: "Prefix" }),
|
|
10316
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "font-semibold", children: "Type" }),
|
|
10317
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "font-semibold text-right", children: "Weight" }),
|
|
10318
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "font-semibold text-right", children: "Size (m\xB2)" }),
|
|
10319
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "font-semibold", children: "Moved from" })
|
|
10320
|
+
] }) }),
|
|
10321
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableBody, { children: elementsByStatus.addons.map((element) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
10322
|
+
TableRow,
|
|
10323
|
+
{
|
|
10324
|
+
className: "bg-purple-50/30 dark:bg-purple-950/10",
|
|
10325
|
+
children: [
|
|
10326
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableCell, { className: "font-medium", children: element.prefix }),
|
|
10327
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableCell, { children: element.type }),
|
|
10328
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableCell, { className: "text-right tabular-nums", children: element.weight ? /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
10329
|
+
element.weight,
|
|
10330
|
+
" ",
|
|
10331
|
+
element.weightUnit || "kg"
|
|
10332
|
+
] }) : "\u2014" }),
|
|
10333
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableCell, { className: "text-right tabular-nums", children: element.sizeSqm ?? "\u2014" }),
|
|
10334
|
+
/* @__PURE__ */ jsxRuntime.jsx(TableCell, { children: element.originalDeliveryLabel ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-purple-600 dark:text-purple-400", children: element.originalDeliveryLabel }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "\u2014" }) })
|
|
10335
|
+
]
|
|
10336
|
+
},
|
|
10337
|
+
element.id
|
|
10338
|
+
)) })
|
|
10339
|
+
] }) })
|
|
10340
|
+
] }),
|
|
10298
10341
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10299
10342
|
CommentsSection,
|
|
10300
10343
|
{
|
|
10301
|
-
comments:
|
|
10344
|
+
comments: preLoadingComments,
|
|
10302
10345
|
delivery,
|
|
10303
10346
|
weekId: week.weekKey,
|
|
10304
10347
|
onAddComment
|
|
@@ -10335,6 +10378,9 @@ function SupplierWeeklyLoading({
|
|
|
10335
10378
|
}) {
|
|
10336
10379
|
const [selectedDelivery, setSelectedDelivery] = React27__namespace.useState(null);
|
|
10337
10380
|
const [sheetOpen, setSheetOpen] = React27__namespace.useState(false);
|
|
10381
|
+
const [commentDelivery, setCommentDelivery] = React27__namespace.useState(null);
|
|
10382
|
+
const [commentDialogOpen, setCommentDialogOpen] = React27__namespace.useState(false);
|
|
10383
|
+
const [commentText, setCommentText] = React27__namespace.useState("");
|
|
10338
10384
|
const handleDeliveryClick = (delivery) => {
|
|
10339
10385
|
setSelectedDelivery(delivery);
|
|
10340
10386
|
setSheetOpen(true);
|
|
@@ -10345,6 +10391,38 @@ function SupplierWeeklyLoading({
|
|
|
10345
10391
|
setTimeout(() => setSelectedDelivery(null), 200);
|
|
10346
10392
|
onBack?.();
|
|
10347
10393
|
};
|
|
10394
|
+
const handleDeliveryCommentClick = (delivery) => {
|
|
10395
|
+
setCommentDelivery(delivery);
|
|
10396
|
+
setCommentDialogOpen(true);
|
|
10397
|
+
};
|
|
10398
|
+
const handleCommentDialogClose = () => {
|
|
10399
|
+
setCommentDialogOpen(false);
|
|
10400
|
+
setCommentText("");
|
|
10401
|
+
setTimeout(() => setCommentDelivery(null), 200);
|
|
10402
|
+
};
|
|
10403
|
+
const handleCommentSubmit = () => {
|
|
10404
|
+
if (commentText.trim() && commentDelivery && onAddComment) {
|
|
10405
|
+
onAddComment({
|
|
10406
|
+
author: "Current User",
|
|
10407
|
+
// Would come from auth context in real app
|
|
10408
|
+
text: commentText.trim(),
|
|
10409
|
+
context: "pre_unloading",
|
|
10410
|
+
weekId: week.weekKey,
|
|
10411
|
+
deliveryId: commentDelivery.id,
|
|
10412
|
+
supplierId: commentDelivery.supplierId,
|
|
10413
|
+
supplierName: commentDelivery.supplierName,
|
|
10414
|
+
prefixId: commentDelivery.prefixScope,
|
|
10415
|
+
prefixName: commentDelivery.prefixScope
|
|
10416
|
+
});
|
|
10417
|
+
handleCommentDialogClose();
|
|
10418
|
+
}
|
|
10419
|
+
};
|
|
10420
|
+
const handleCommentKeyDown = (e) => {
|
|
10421
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
10422
|
+
e.preventDefault();
|
|
10423
|
+
handleCommentSubmit();
|
|
10424
|
+
}
|
|
10425
|
+
};
|
|
10348
10426
|
const Wrapper = bordered ? Card : "div";
|
|
10349
10427
|
const Content14 = bordered ? CardContent : "div";
|
|
10350
10428
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -10363,6 +10441,7 @@ function SupplierWeeklyLoading({
|
|
|
10363
10441
|
week,
|
|
10364
10442
|
deliveries,
|
|
10365
10443
|
onDeliveryClick: handleDeliveryClick,
|
|
10444
|
+
onDeliveryCommentClick: handleDeliveryCommentClick,
|
|
10366
10445
|
onWeekChange,
|
|
10367
10446
|
showNavigation
|
|
10368
10447
|
}
|
|
@@ -10391,7 +10470,58 @@ function SupplierWeeklyLoading({
|
|
|
10391
10470
|
)
|
|
10392
10471
|
]
|
|
10393
10472
|
}
|
|
10394
|
-
) })
|
|
10473
|
+
) }),
|
|
10474
|
+
/* @__PURE__ */ jsxRuntime.jsx(Dialog, { open: commentDialogOpen, onOpenChange: (open) => !open && handleCommentDialogClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "sm:max-w-md", children: [
|
|
10475
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
|
|
10476
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Add pre-loading note" }),
|
|
10477
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogDescription, { children: [
|
|
10478
|
+
"Add a note for ",
|
|
10479
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: commentDelivery?.label }),
|
|
10480
|
+
" (",
|
|
10481
|
+
commentDelivery?.supplierName,
|
|
10482
|
+
commentDelivery?.prefixScope && ` \u2022 ${commentDelivery.prefixScope}`,
|
|
10483
|
+
")."
|
|
10484
|
+
] })
|
|
10485
|
+
] }),
|
|
10486
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
10487
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label2, { htmlFor: "card-comment-text", className: "text-sm font-medium", children: "Note" }),
|
|
10488
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10489
|
+
Textarea,
|
|
10490
|
+
{
|
|
10491
|
+
id: "card-comment-text",
|
|
10492
|
+
placeholder: "Add a note before loading...",
|
|
10493
|
+
value: commentText,
|
|
10494
|
+
onChange: (e) => setCommentText(e.target.value),
|
|
10495
|
+
onKeyDown: handleCommentKeyDown,
|
|
10496
|
+
className: "min-h-[120px] text-base resize-none",
|
|
10497
|
+
autoFocus: true
|
|
10498
|
+
}
|
|
10499
|
+
),
|
|
10500
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "\u2318+Enter to save" })
|
|
10501
|
+
] }) }),
|
|
10502
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { className: "gap-2 sm:gap-0", children: [
|
|
10503
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
10504
|
+
Button,
|
|
10505
|
+
{
|
|
10506
|
+
variant: "ghost",
|
|
10507
|
+
onClick: handleCommentDialogClose,
|
|
10508
|
+
children: "Cancel"
|
|
10509
|
+
}
|
|
10510
|
+
),
|
|
10511
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10512
|
+
Button,
|
|
10513
|
+
{
|
|
10514
|
+
onClick: handleCommentSubmit,
|
|
10515
|
+
disabled: !commentText.trim(),
|
|
10516
|
+
className: "gap-1.5",
|
|
10517
|
+
children: [
|
|
10518
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "h-4 w-4" }),
|
|
10519
|
+
"Save note"
|
|
10520
|
+
]
|
|
10521
|
+
}
|
|
10522
|
+
)
|
|
10523
|
+
] })
|
|
10524
|
+
] }) })
|
|
10395
10525
|
] });
|
|
10396
10526
|
}
|
|
10397
10527
|
function getStatusBadgeVariant3(status) {
|
|
@@ -10902,11 +11032,11 @@ function getVisibleHours(visibleHours, singleDayEvents) {
|
|
|
10902
11032
|
function getCalendarCells(selectedDate) {
|
|
10903
11033
|
const currentYear = selectedDate.getFullYear();
|
|
10904
11034
|
const currentMonth = selectedDate.getMonth();
|
|
10905
|
-
const
|
|
11035
|
+
const getDaysInMonth2 = (year, month) => new Date(year, month + 1, 0).getDate();
|
|
10906
11036
|
const getFirstDayOfMonth = (year, month) => new Date(year, month, 1).getDay();
|
|
10907
|
-
const daysInMonth =
|
|
11037
|
+
const daysInMonth = getDaysInMonth2(currentYear, currentMonth);
|
|
10908
11038
|
const firstDayOfMonth = getFirstDayOfMonth(currentYear, currentMonth);
|
|
10909
|
-
const daysInPrevMonth =
|
|
11039
|
+
const daysInPrevMonth = getDaysInMonth2(currentYear, currentMonth - 1);
|
|
10910
11040
|
const totalDays = firstDayOfMonth + daysInMonth;
|
|
10911
11041
|
const prevMonthCells = Array.from({ length: firstDayOfMonth }, (_, i) => ({
|
|
10912
11042
|
day: daysInPrevMonth - firstDayOfMonth + i + 1,
|
|
@@ -13627,6 +13757,1569 @@ function CalendarView({
|
|
|
13627
13757
|
return null;
|
|
13628
13758
|
}
|
|
13629
13759
|
}
|
|
13760
|
+
var draggingAtom = jotai.atom(false);
|
|
13761
|
+
var scrollXAtom = jotai.atom(0);
|
|
13762
|
+
var useGanttDragging = () => jotai.useAtom(draggingAtom);
|
|
13763
|
+
var useGanttScrollX = () => jotai.useAtom(scrollXAtom);
|
|
13764
|
+
var getDifferenceIn = (range) => {
|
|
13765
|
+
if (range === "weekly") {
|
|
13766
|
+
return dateFns.differenceInWeeks;
|
|
13767
|
+
}
|
|
13768
|
+
return dateFns.differenceInMonths;
|
|
13769
|
+
};
|
|
13770
|
+
var getInnerDifferenceIn = (range) => {
|
|
13771
|
+
if (range === "weekly") {
|
|
13772
|
+
return dateFns.differenceInDays;
|
|
13773
|
+
}
|
|
13774
|
+
return dateFns.differenceInDays;
|
|
13775
|
+
};
|
|
13776
|
+
var getStartOf = (range) => {
|
|
13777
|
+
if (range === "weekly") {
|
|
13778
|
+
return (date) => dateFns.startOfWeek(date, { weekStartsOn: 1 });
|
|
13779
|
+
}
|
|
13780
|
+
return dateFns.startOfMonth;
|
|
13781
|
+
};
|
|
13782
|
+
var getEndOf = (range) => {
|
|
13783
|
+
if (range === "weekly") {
|
|
13784
|
+
return (date) => dateFns.endOfWeek(date, { weekStartsOn: 1 });
|
|
13785
|
+
}
|
|
13786
|
+
return dateFns.endOfMonth;
|
|
13787
|
+
};
|
|
13788
|
+
var getAddRange = (range) => {
|
|
13789
|
+
if (range === "weekly") {
|
|
13790
|
+
return dateFns.addWeeks;
|
|
13791
|
+
}
|
|
13792
|
+
return dateFns.addMonths;
|
|
13793
|
+
};
|
|
13794
|
+
var getDateByMousePosition = (context, mouseX) => {
|
|
13795
|
+
const columnWidth = context.columnWidth * context.zoom / 100;
|
|
13796
|
+
const offset = Math.floor(mouseX / columnWidth);
|
|
13797
|
+
const addRange = getAddRange(context.range);
|
|
13798
|
+
if (context.range === "weekly") {
|
|
13799
|
+
const firstYear = context.timelineData[0].year;
|
|
13800
|
+
const weekStart = getWeekStartDate(firstYear, 1);
|
|
13801
|
+
const targetWeekStart = addRange(weekStart, offset);
|
|
13802
|
+
const pixelsPerDay2 = columnWidth / 7;
|
|
13803
|
+
const dayOffset2 = Math.floor(mouseX % columnWidth / pixelsPerDay2);
|
|
13804
|
+
return dateFns.addDays(targetWeekStart, dayOffset2);
|
|
13805
|
+
}
|
|
13806
|
+
const timelineStartDate = new Date(context.timelineData[0].year, 0, 1);
|
|
13807
|
+
const month = addRange(timelineStartDate, offset);
|
|
13808
|
+
const daysInMonth = dateFns.getDaysInMonth(month);
|
|
13809
|
+
const pixelsPerDay = Math.round(columnWidth / daysInMonth);
|
|
13810
|
+
const dayOffset = Math.floor(mouseX % columnWidth / pixelsPerDay);
|
|
13811
|
+
return dateFns.addDays(month, dayOffset);
|
|
13812
|
+
};
|
|
13813
|
+
var createInitialTimelineData = (today) => {
|
|
13814
|
+
const data = [];
|
|
13815
|
+
data.push(
|
|
13816
|
+
{ year: today.getFullYear() - 1, quarters: new Array(4).fill(null) },
|
|
13817
|
+
{ year: today.getFullYear(), quarters: new Array(4).fill(null) },
|
|
13818
|
+
{ year: today.getFullYear() + 1, quarters: new Array(4).fill(null) }
|
|
13819
|
+
);
|
|
13820
|
+
for (const yearObj of data) {
|
|
13821
|
+
yearObj.quarters = new Array(4).fill(null).map((_, quarterIndex) => ({
|
|
13822
|
+
months: new Array(3).fill(null).map((_2, monthIndex) => {
|
|
13823
|
+
const month = quarterIndex * 3 + monthIndex;
|
|
13824
|
+
return {
|
|
13825
|
+
days: dateFns.getDaysInMonth(new Date(yearObj.year, month, 1))
|
|
13826
|
+
};
|
|
13827
|
+
})
|
|
13828
|
+
}));
|
|
13829
|
+
}
|
|
13830
|
+
return data;
|
|
13831
|
+
};
|
|
13832
|
+
var getOffset = (date, timelineStartDate, context) => {
|
|
13833
|
+
const parsedColumnWidth = context.columnWidth * context.zoom / 100;
|
|
13834
|
+
const differenceIn = getDifferenceIn(context.range);
|
|
13835
|
+
const startOf = getStartOf(context.range);
|
|
13836
|
+
const fullColumns = differenceIn(startOf(date), startOf(timelineStartDate));
|
|
13837
|
+
if (context.range === "weekly") {
|
|
13838
|
+
const weekStart = startOf(date);
|
|
13839
|
+
const dayOfWeek = dateFns.differenceInDays(date, weekStart);
|
|
13840
|
+
const pixelsPerDay2 = parsedColumnWidth / 7;
|
|
13841
|
+
return fullColumns * parsedColumnWidth + dayOfWeek * pixelsPerDay2;
|
|
13842
|
+
}
|
|
13843
|
+
const partialColumns = date.getDate();
|
|
13844
|
+
const daysInMonth = dateFns.getDaysInMonth(date);
|
|
13845
|
+
const pixelsPerDay = parsedColumnWidth / daysInMonth;
|
|
13846
|
+
return fullColumns * parsedColumnWidth + partialColumns * pixelsPerDay;
|
|
13847
|
+
};
|
|
13848
|
+
var getWidth = (startAt, endAt, context) => {
|
|
13849
|
+
const parsedColumnWidth = context.columnWidth * context.zoom / 100;
|
|
13850
|
+
if (!endAt) {
|
|
13851
|
+
return parsedColumnWidth * 2;
|
|
13852
|
+
}
|
|
13853
|
+
const startOf = getStartOf(context.range);
|
|
13854
|
+
if (context.range === "weekly") {
|
|
13855
|
+
const pixelsPerDay = parsedColumnWidth / 7;
|
|
13856
|
+
const daysDiff = dateFns.differenceInDays(endAt, startAt);
|
|
13857
|
+
return Math.max(pixelsPerDay, daysDiff * pixelsPerDay);
|
|
13858
|
+
}
|
|
13859
|
+
const differenceIn = getDifferenceIn(context.range);
|
|
13860
|
+
const daysInStartMonth = dateFns.getDaysInMonth(startAt);
|
|
13861
|
+
const pixelsPerDayInStartMonth = parsedColumnWidth / daysInStartMonth;
|
|
13862
|
+
if (dateFns.isSameDay(startAt, endAt)) {
|
|
13863
|
+
return pixelsPerDayInStartMonth;
|
|
13864
|
+
}
|
|
13865
|
+
const innerDifferenceIn = getInnerDifferenceIn(context.range);
|
|
13866
|
+
if (dateFns.isSameDay(startOf(startAt), startOf(endAt))) {
|
|
13867
|
+
return innerDifferenceIn(endAt, startAt) * pixelsPerDayInStartMonth;
|
|
13868
|
+
}
|
|
13869
|
+
const startRangeOffset = daysInStartMonth - dateFns.getDate(startAt);
|
|
13870
|
+
const endRangeOffset = dateFns.getDate(endAt);
|
|
13871
|
+
const fullRangeOffset = differenceIn(startOf(endAt), startOf(startAt));
|
|
13872
|
+
const daysInEndMonth = dateFns.getDaysInMonth(endAt);
|
|
13873
|
+
const pixelsPerDayInEndMonth = parsedColumnWidth / daysInEndMonth;
|
|
13874
|
+
return (fullRangeOffset - 1) * parsedColumnWidth + startRangeOffset * pixelsPerDayInStartMonth + endRangeOffset * pixelsPerDayInEndMonth;
|
|
13875
|
+
};
|
|
13876
|
+
var calculateInnerOffset = (date, range, columnWidth) => {
|
|
13877
|
+
const startOf = getStartOf(range);
|
|
13878
|
+
const endOf = getEndOf(range);
|
|
13879
|
+
const startOfRange = startOf(date);
|
|
13880
|
+
const endOfRange = endOf(date);
|
|
13881
|
+
if (range === "weekly") {
|
|
13882
|
+
const dayOfWeek = dateFns.differenceInDays(date, startOfRange);
|
|
13883
|
+
return dayOfWeek / 7 * columnWidth;
|
|
13884
|
+
}
|
|
13885
|
+
const totalRangeDays = dateFns.differenceInDays(endOfRange, startOfRange);
|
|
13886
|
+
const dayOfMonth = date.getDate();
|
|
13887
|
+
return dayOfMonth / totalRangeDays * columnWidth;
|
|
13888
|
+
};
|
|
13889
|
+
var GanttContext = React27.createContext({
|
|
13890
|
+
zoom: 100,
|
|
13891
|
+
range: "monthly",
|
|
13892
|
+
columnWidth: 50,
|
|
13893
|
+
headerHeight: 60,
|
|
13894
|
+
sidebarWidth: 300,
|
|
13895
|
+
rowHeight: 36,
|
|
13896
|
+
onAddItem: void 0,
|
|
13897
|
+
placeholderLength: 2,
|
|
13898
|
+
timelineData: [],
|
|
13899
|
+
ref: null,
|
|
13900
|
+
scrollToFeature: void 0,
|
|
13901
|
+
expandedGroups: {},
|
|
13902
|
+
setGroupExpanded: () => {
|
|
13903
|
+
}
|
|
13904
|
+
});
|
|
13905
|
+
var GanttContentHeader = ({
|
|
13906
|
+
title,
|
|
13907
|
+
columns,
|
|
13908
|
+
renderHeaderItem
|
|
13909
|
+
}) => {
|
|
13910
|
+
const id = React27.useId();
|
|
13911
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13912
|
+
"div",
|
|
13913
|
+
{
|
|
13914
|
+
className: "sticky top-0 z-20 grid w-full shrink-0 bg-background/95 backdrop-blur-sm",
|
|
13915
|
+
style: { height: "var(--gantt-header-height)" },
|
|
13916
|
+
children: [
|
|
13917
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
13918
|
+
"div",
|
|
13919
|
+
{
|
|
13920
|
+
className: "sticky inline-flex whitespace-nowrap px-3 py-2 text-muted-foreground text-xs",
|
|
13921
|
+
style: {
|
|
13922
|
+
left: "var(--gantt-sidebar-width)"
|
|
13923
|
+
},
|
|
13924
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: title })
|
|
13925
|
+
}
|
|
13926
|
+
) }),
|
|
13927
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13928
|
+
"div",
|
|
13929
|
+
{
|
|
13930
|
+
className: "grid w-full",
|
|
13931
|
+
style: {
|
|
13932
|
+
gridTemplateColumns: `repeat(${columns}, var(--gantt-column-width))`
|
|
13933
|
+
},
|
|
13934
|
+
children: Array.from({ length: columns }).map((_, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
13935
|
+
"div",
|
|
13936
|
+
{
|
|
13937
|
+
className: "shrink-0 border-border/50 border-b py-1 text-center text-xs",
|
|
13938
|
+
children: renderHeaderItem(index)
|
|
13939
|
+
},
|
|
13940
|
+
`${id}-${index}`
|
|
13941
|
+
))
|
|
13942
|
+
}
|
|
13943
|
+
)
|
|
13944
|
+
]
|
|
13945
|
+
}
|
|
13946
|
+
);
|
|
13947
|
+
};
|
|
13948
|
+
var getWeeksInYear = (year) => {
|
|
13949
|
+
const dec28 = new Date(year, 11, 28);
|
|
13950
|
+
return dateFns.getISOWeek(dec28);
|
|
13951
|
+
};
|
|
13952
|
+
var getWeekStartDate = (year, weekNumber) => {
|
|
13953
|
+
const jan4 = new Date(year, 0, 4);
|
|
13954
|
+
const week1Start = dateFns.startOfWeek(jan4, { weekStartsOn: 1 });
|
|
13955
|
+
return dateFns.addWeeks(week1Start, weekNumber - 1);
|
|
13956
|
+
};
|
|
13957
|
+
var WeeklyHeader = () => {
|
|
13958
|
+
const gantt = React27.useContext(GanttContext);
|
|
13959
|
+
const weeklyData = React27.useMemo(() => {
|
|
13960
|
+
const result = [];
|
|
13961
|
+
for (const yearData of gantt.timelineData) {
|
|
13962
|
+
const year = yearData.year;
|
|
13963
|
+
const weeksInYear = getWeeksInYear(year);
|
|
13964
|
+
const weeks = [];
|
|
13965
|
+
for (let w = 1; w <= weeksInYear; w++) {
|
|
13966
|
+
weeks.push({
|
|
13967
|
+
weekNumber: w,
|
|
13968
|
+
startDate: getWeekStartDate(year, w)
|
|
13969
|
+
});
|
|
13970
|
+
}
|
|
13971
|
+
result.push({ year, weeks });
|
|
13972
|
+
}
|
|
13973
|
+
return result;
|
|
13974
|
+
}, [gantt.timelineData]);
|
|
13975
|
+
return weeklyData.map((yearData) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-col", children: [
|
|
13976
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13977
|
+
GanttContentHeader,
|
|
13978
|
+
{
|
|
13979
|
+
columns: yearData.weeks.length,
|
|
13980
|
+
renderHeaderItem: (weekIndex) => {
|
|
13981
|
+
const week = yearData.weeks[weekIndex];
|
|
13982
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium", children: [
|
|
13983
|
+
"W",
|
|
13984
|
+
week.weekNumber.toString().padStart(2, "0")
|
|
13985
|
+
] }) });
|
|
13986
|
+
},
|
|
13987
|
+
title: `${yearData.year}`
|
|
13988
|
+
}
|
|
13989
|
+
),
|
|
13990
|
+
/* @__PURE__ */ jsxRuntime.jsx(GanttColumns, { columns: yearData.weeks.length })
|
|
13991
|
+
] }, yearData.year));
|
|
13992
|
+
};
|
|
13993
|
+
var MonthlyHeader = () => {
|
|
13994
|
+
const gantt = React27.useContext(GanttContext);
|
|
13995
|
+
return gantt.timelineData.map((year) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex flex-col", children: [
|
|
13996
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13997
|
+
GanttContentHeader,
|
|
13998
|
+
{
|
|
13999
|
+
columns: year.quarters.flatMap((quarter) => quarter.months).length,
|
|
14000
|
+
renderHeaderItem: (item) => /* @__PURE__ */ jsxRuntime.jsx("p", { children: dateFns.format(new Date(year.year, item, 1), "MMM") }),
|
|
14001
|
+
title: `${year.year}`
|
|
14002
|
+
}
|
|
14003
|
+
),
|
|
14004
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14005
|
+
GanttColumns,
|
|
14006
|
+
{
|
|
14007
|
+
columns: year.quarters.flatMap((quarter) => quarter.months).length
|
|
14008
|
+
}
|
|
14009
|
+
)
|
|
14010
|
+
] }, year.year));
|
|
14011
|
+
};
|
|
14012
|
+
var QuarterlyHeader = () => {
|
|
14013
|
+
const gantt = React27.useContext(GanttContext);
|
|
14014
|
+
return gantt.timelineData.map(
|
|
14015
|
+
(year) => year.quarters.map((quarter, quarterIndex) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14016
|
+
"div",
|
|
14017
|
+
{
|
|
14018
|
+
className: "relative flex flex-col",
|
|
14019
|
+
children: [
|
|
14020
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14021
|
+
GanttContentHeader,
|
|
14022
|
+
{
|
|
14023
|
+
columns: quarter.months.length,
|
|
14024
|
+
renderHeaderItem: (item) => /* @__PURE__ */ jsxRuntime.jsx("p", { children: dateFns.format(new Date(year.year, quarterIndex * 3 + item, 1), "MMM") }),
|
|
14025
|
+
title: `Q${quarterIndex + 1} ${year.year}`
|
|
14026
|
+
}
|
|
14027
|
+
),
|
|
14028
|
+
/* @__PURE__ */ jsxRuntime.jsx(GanttColumns, { columns: quarter.months.length })
|
|
14029
|
+
]
|
|
14030
|
+
},
|
|
14031
|
+
`${year.year}-${quarterIndex}`
|
|
14032
|
+
))
|
|
14033
|
+
);
|
|
14034
|
+
};
|
|
14035
|
+
var headers = {
|
|
14036
|
+
weekly: WeeklyHeader,
|
|
14037
|
+
monthly: MonthlyHeader,
|
|
14038
|
+
quarterly: QuarterlyHeader
|
|
14039
|
+
};
|
|
14040
|
+
var GanttHeader = ({ className }) => {
|
|
14041
|
+
const gantt = React27.useContext(GanttContext);
|
|
14042
|
+
const Header2 = headers[gantt.range];
|
|
14043
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14044
|
+
"div",
|
|
14045
|
+
{
|
|
14046
|
+
className: cn(
|
|
14047
|
+
"-space-x-px flex h-full w-max divide-x-2 divide-border",
|
|
14048
|
+
className
|
|
14049
|
+
),
|
|
14050
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Header2, {})
|
|
14051
|
+
}
|
|
14052
|
+
);
|
|
14053
|
+
};
|
|
14054
|
+
var GanttSidebarItem = ({
|
|
14055
|
+
feature,
|
|
14056
|
+
onSelectItem,
|
|
14057
|
+
className
|
|
14058
|
+
}) => {
|
|
14059
|
+
const gantt = React27.useContext(GanttContext);
|
|
14060
|
+
const tempEndAt = feature.endAt && dateFns.isSameDay(feature.startAt, feature.endAt) ? dateFns.addDays(feature.endAt, 1) : feature.endAt;
|
|
14061
|
+
const duration = tempEndAt ? dateFns.formatDistance(feature.startAt, tempEndAt) : `${dateFns.formatDistance(feature.startAt, /* @__PURE__ */ new Date())} so far`;
|
|
14062
|
+
const handleClick = (event) => {
|
|
14063
|
+
if (event.target === event.currentTarget) {
|
|
14064
|
+
gantt.scrollToFeature?.(feature);
|
|
14065
|
+
onSelectItem?.(feature.id);
|
|
14066
|
+
}
|
|
14067
|
+
};
|
|
14068
|
+
const handleKeyDown = (event) => {
|
|
14069
|
+
if (event.key === "Enter") {
|
|
14070
|
+
gantt.scrollToFeature?.(feature);
|
|
14071
|
+
onSelectItem?.(feature.id);
|
|
14072
|
+
}
|
|
14073
|
+
};
|
|
14074
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14075
|
+
"div",
|
|
14076
|
+
{
|
|
14077
|
+
className: cn(
|
|
14078
|
+
"relative flex items-center gap-2.5 px-2.5 py-1.5 text-xs hover:bg-muted/50 border-b border-border/30",
|
|
14079
|
+
className
|
|
14080
|
+
),
|
|
14081
|
+
onClick: handleClick,
|
|
14082
|
+
onKeyDown: handleKeyDown,
|
|
14083
|
+
role: "button",
|
|
14084
|
+
style: {
|
|
14085
|
+
height: "var(--gantt-row-height)"
|
|
14086
|
+
},
|
|
14087
|
+
tabIndex: 0,
|
|
14088
|
+
children: [
|
|
14089
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14090
|
+
"div",
|
|
14091
|
+
{
|
|
14092
|
+
className: "pointer-events-none h-2 w-2 shrink-0 rounded-full",
|
|
14093
|
+
style: {
|
|
14094
|
+
backgroundColor: feature.status.color
|
|
14095
|
+
}
|
|
14096
|
+
}
|
|
14097
|
+
),
|
|
14098
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "pointer-events-none flex-1 truncate text-left font-medium", children: feature.name }),
|
|
14099
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "pointer-events-none text-muted-foreground text-[11px]", children: duration })
|
|
14100
|
+
]
|
|
14101
|
+
},
|
|
14102
|
+
feature.id
|
|
14103
|
+
);
|
|
14104
|
+
};
|
|
14105
|
+
var GanttSidebarHeader = ({
|
|
14106
|
+
title = "Tasks",
|
|
14107
|
+
durationLabel = "Duration"
|
|
14108
|
+
}) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14109
|
+
"div",
|
|
14110
|
+
{
|
|
14111
|
+
className: "sticky top-0 z-10 flex shrink-0 items-end justify-between gap-2.5 border-border/50 border-b bg-background/95 px-2.5 py-2 font-medium text-muted-foreground text-xs backdrop-blur-sm",
|
|
14112
|
+
style: { height: "var(--gantt-header-height)" },
|
|
14113
|
+
children: [
|
|
14114
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "flex-1 truncate text-left", children: title }),
|
|
14115
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0", children: durationLabel })
|
|
14116
|
+
]
|
|
14117
|
+
}
|
|
14118
|
+
);
|
|
14119
|
+
function computeGroupRange(features) {
|
|
14120
|
+
if (features.length === 0) {
|
|
14121
|
+
return { start: null, end: null };
|
|
14122
|
+
}
|
|
14123
|
+
let minStart = features[0].startAt;
|
|
14124
|
+
let maxEnd = features[0].endAt;
|
|
14125
|
+
for (const feature of features) {
|
|
14126
|
+
if (feature.startAt < minStart) {
|
|
14127
|
+
minStart = feature.startAt;
|
|
14128
|
+
}
|
|
14129
|
+
if (feature.endAt > maxEnd) {
|
|
14130
|
+
maxEnd = feature.endAt;
|
|
14131
|
+
}
|
|
14132
|
+
}
|
|
14133
|
+
return { start: minStart, end: maxEnd };
|
|
14134
|
+
}
|
|
14135
|
+
var GanttGroupSummaryBar = React27.memo(({
|
|
14136
|
+
group,
|
|
14137
|
+
className
|
|
14138
|
+
}) => {
|
|
14139
|
+
const gantt = React27.useContext(GanttContext);
|
|
14140
|
+
const timelineStartDate = React27.useMemo(
|
|
14141
|
+
() => new Date(gantt.timelineData.at(0)?.year ?? 0, 0, 1),
|
|
14142
|
+
[gantt.timelineData]
|
|
14143
|
+
);
|
|
14144
|
+
const { start: groupStart, end: groupEnd } = React27.useMemo(
|
|
14145
|
+
() => computeGroupRange(group.features),
|
|
14146
|
+
[group.features]
|
|
14147
|
+
);
|
|
14148
|
+
const offset = React27.useMemo(() => {
|
|
14149
|
+
if (!groupStart) return 0;
|
|
14150
|
+
return Math.round(getOffset(groupStart, timelineStartDate, gantt));
|
|
14151
|
+
}, [groupStart, timelineStartDate, gantt]);
|
|
14152
|
+
const width = React27.useMemo(() => {
|
|
14153
|
+
if (!groupStart || !groupEnd) return 0;
|
|
14154
|
+
return Math.round(getWidth(groupStart, groupEnd, gantt));
|
|
14155
|
+
}, [groupStart, groupEnd, gantt]);
|
|
14156
|
+
if (!groupStart || !groupEnd || width === 0) {
|
|
14157
|
+
return null;
|
|
14158
|
+
}
|
|
14159
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14160
|
+
"div",
|
|
14161
|
+
{
|
|
14162
|
+
className: cn(
|
|
14163
|
+
"absolute pointer-events-none",
|
|
14164
|
+
className
|
|
14165
|
+
),
|
|
14166
|
+
style: {
|
|
14167
|
+
height: "calc(var(--gantt-row-height) - 12px)",
|
|
14168
|
+
top: "6px",
|
|
14169
|
+
width,
|
|
14170
|
+
left: offset
|
|
14171
|
+
},
|
|
14172
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14173
|
+
"div",
|
|
14174
|
+
{
|
|
14175
|
+
className: "h-full w-full rounded-sm border border-border/60 bg-muted/40 shadow-sm",
|
|
14176
|
+
style: {
|
|
14177
|
+
// Subtle gradient for depth
|
|
14178
|
+
background: "linear-gradient(180deg, hsl(var(--muted) / 0.5) 0%, hsl(var(--muted) / 0.3) 100%)"
|
|
14179
|
+
}
|
|
14180
|
+
}
|
|
14181
|
+
)
|
|
14182
|
+
}
|
|
14183
|
+
);
|
|
14184
|
+
});
|
|
14185
|
+
GanttGroupSummaryBar.displayName = "GanttGroupSummaryBar";
|
|
14186
|
+
var GanttCollapsibleSidebarGroup = ({
|
|
14187
|
+
group,
|
|
14188
|
+
children,
|
|
14189
|
+
className
|
|
14190
|
+
}) => {
|
|
14191
|
+
const gantt = React27.useContext(GanttContext);
|
|
14192
|
+
const isExpanded = gantt.expandedGroups[group.id] ?? true;
|
|
14193
|
+
const { start: groupStart, end: groupEnd } = React27.useMemo(
|
|
14194
|
+
() => computeGroupRange(group.features),
|
|
14195
|
+
[group.features]
|
|
14196
|
+
);
|
|
14197
|
+
const duration = React27.useMemo(() => {
|
|
14198
|
+
if (!groupStart || !groupEnd) return null;
|
|
14199
|
+
return dateFns.formatDistance(groupStart, groupEnd);
|
|
14200
|
+
}, [groupStart, groupEnd]);
|
|
14201
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14202
|
+
Collapsible,
|
|
14203
|
+
{
|
|
14204
|
+
open: isExpanded,
|
|
14205
|
+
onOpenChange: (open) => gantt.setGroupExpanded(group.id, open),
|
|
14206
|
+
className,
|
|
14207
|
+
children: [
|
|
14208
|
+
/* @__PURE__ */ jsxRuntime.jsx(CollapsibleTrigger2, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14209
|
+
"button",
|
|
14210
|
+
{
|
|
14211
|
+
className: "flex w-full items-center gap-2 px-2.5 py-2 text-left font-medium text-xs hover:bg-muted/30 border-b border-border/50 bg-background",
|
|
14212
|
+
style: { height: "var(--gantt-row-height)" },
|
|
14213
|
+
type: "button",
|
|
14214
|
+
children: [
|
|
14215
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14216
|
+
lucideReact.ChevronRightIcon,
|
|
14217
|
+
{
|
|
14218
|
+
className: cn(
|
|
14219
|
+
"h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200",
|
|
14220
|
+
isExpanded && "rotate-90"
|
|
14221
|
+
)
|
|
14222
|
+
}
|
|
14223
|
+
),
|
|
14224
|
+
group.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: group.icon }),
|
|
14225
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate", children: group.title }),
|
|
14226
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] text-muted-foreground", children: group.features.length }),
|
|
14227
|
+
duration && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: duration })
|
|
14228
|
+
]
|
|
14229
|
+
}
|
|
14230
|
+
) }),
|
|
14231
|
+
/* @__PURE__ */ jsxRuntime.jsx(CollapsibleContent2, { className: "[&[data-state]]:!opacity-100", children })
|
|
14232
|
+
]
|
|
14233
|
+
}
|
|
14234
|
+
);
|
|
14235
|
+
};
|
|
14236
|
+
var GanttCollapsibleTimelineGroup = ({
|
|
14237
|
+
group,
|
|
14238
|
+
children,
|
|
14239
|
+
className
|
|
14240
|
+
}) => {
|
|
14241
|
+
const gantt = React27.useContext(GanttContext);
|
|
14242
|
+
const isExpanded = gantt.expandedGroups[group.id] ?? true;
|
|
14243
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14244
|
+
Collapsible,
|
|
14245
|
+
{
|
|
14246
|
+
open: isExpanded,
|
|
14247
|
+
onOpenChange: (open) => gantt.setGroupExpanded(group.id, open),
|
|
14248
|
+
className,
|
|
14249
|
+
children: [
|
|
14250
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14251
|
+
"div",
|
|
14252
|
+
{
|
|
14253
|
+
className: "relative w-full border-b border-border/50",
|
|
14254
|
+
style: { height: "var(--gantt-row-height)" },
|
|
14255
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(GanttGroupSummaryBar, { group })
|
|
14256
|
+
}
|
|
14257
|
+
),
|
|
14258
|
+
/* @__PURE__ */ jsxRuntime.jsx(CollapsibleContent2, { className: "[&[data-state]]:!opacity-100", children })
|
|
14259
|
+
]
|
|
14260
|
+
}
|
|
14261
|
+
);
|
|
14262
|
+
};
|
|
14263
|
+
var GanttSidebarGroup = ({
|
|
14264
|
+
children,
|
|
14265
|
+
name,
|
|
14266
|
+
className
|
|
14267
|
+
}) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
|
|
14268
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14269
|
+
"p",
|
|
14270
|
+
{
|
|
14271
|
+
className: "w-full truncate px-2.5 py-2 text-left font-medium text-muted-foreground text-xs border-b border-border/50 bg-background",
|
|
14272
|
+
style: { height: "var(--gantt-row-height)" },
|
|
14273
|
+
children: name
|
|
14274
|
+
}
|
|
14275
|
+
),
|
|
14276
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children })
|
|
14277
|
+
] });
|
|
14278
|
+
var GanttSidebar = ({
|
|
14279
|
+
children,
|
|
14280
|
+
className
|
|
14281
|
+
}) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14282
|
+
"div",
|
|
14283
|
+
{
|
|
14284
|
+
className: cn(
|
|
14285
|
+
"sticky left-0 z-30 h-max min-h-full border-border/50 border-r bg-background",
|
|
14286
|
+
className
|
|
14287
|
+
),
|
|
14288
|
+
style: { width: "var(--gantt-sidebar-width)" },
|
|
14289
|
+
"data-roadmap-ui": "gantt-sidebar",
|
|
14290
|
+
children: [
|
|
14291
|
+
/* @__PURE__ */ jsxRuntime.jsx(GanttSidebarHeader, {}),
|
|
14292
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children })
|
|
14293
|
+
]
|
|
14294
|
+
}
|
|
14295
|
+
);
|
|
14296
|
+
var GanttAddFeatureHelper = ({
|
|
14297
|
+
top,
|
|
14298
|
+
className
|
|
14299
|
+
}) => {
|
|
14300
|
+
const [scrollX] = useGanttScrollX();
|
|
14301
|
+
const gantt = React27.useContext(GanttContext);
|
|
14302
|
+
const [mousePosition, mouseRef] = usehooks.useMouse();
|
|
14303
|
+
const handleClick = () => {
|
|
14304
|
+
const ganttRect = gantt.ref?.current?.getBoundingClientRect();
|
|
14305
|
+
const x = mousePosition.x - (ganttRect?.left ?? 0) + scrollX - gantt.sidebarWidth;
|
|
14306
|
+
const currentDate = getDateByMousePosition(gantt, x);
|
|
14307
|
+
gantt.onAddItem?.(currentDate);
|
|
14308
|
+
};
|
|
14309
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14310
|
+
"div",
|
|
14311
|
+
{
|
|
14312
|
+
className: cn("absolute top-0 w-full px-0.5", className),
|
|
14313
|
+
ref: mouseRef,
|
|
14314
|
+
style: {
|
|
14315
|
+
marginTop: -gantt.rowHeight / 2,
|
|
14316
|
+
transform: `translateY(${top}px)`
|
|
14317
|
+
},
|
|
14318
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14319
|
+
"button",
|
|
14320
|
+
{
|
|
14321
|
+
className: "flex h-full w-full items-center justify-center rounded-sm border border-dashed p-2",
|
|
14322
|
+
onClick: handleClick,
|
|
14323
|
+
type: "button",
|
|
14324
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14325
|
+
lucideReact.PlusIcon,
|
|
14326
|
+
{
|
|
14327
|
+
className: "pointer-events-none select-none text-muted-foreground",
|
|
14328
|
+
size: 16
|
|
14329
|
+
}
|
|
14330
|
+
)
|
|
14331
|
+
}
|
|
14332
|
+
)
|
|
14333
|
+
}
|
|
14334
|
+
);
|
|
14335
|
+
};
|
|
14336
|
+
var GanttColumn = ({
|
|
14337
|
+
index,
|
|
14338
|
+
isColumnSecondary
|
|
14339
|
+
}) => {
|
|
14340
|
+
const gantt = React27.useContext(GanttContext);
|
|
14341
|
+
const [dragging] = useGanttDragging();
|
|
14342
|
+
const [mousePosition, mouseRef] = usehooks.useMouse();
|
|
14343
|
+
const [hovering, setHovering] = React27.useState(false);
|
|
14344
|
+
const [windowScroll] = usehooks.useWindowScroll();
|
|
14345
|
+
const handleMouseEnter = () => setHovering(true);
|
|
14346
|
+
const handleMouseLeave = () => setHovering(false);
|
|
14347
|
+
const top = usehooks.useThrottle(
|
|
14348
|
+
mousePosition.y - (mouseRef.current?.getBoundingClientRect().y ?? 0) - (windowScroll.y ?? 0),
|
|
14349
|
+
10
|
|
14350
|
+
);
|
|
14351
|
+
return (
|
|
14352
|
+
// biome-ignore lint/a11y/noStaticElementInteractions: "This is a clickable column"
|
|
14353
|
+
// biome-ignore lint/nursery/noNoninteractiveElementInteractions: "This is a clickable column"
|
|
14354
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14355
|
+
"div",
|
|
14356
|
+
{
|
|
14357
|
+
className: cn(
|
|
14358
|
+
"group relative h-full overflow-hidden",
|
|
14359
|
+
isColumnSecondary?.(index) ? "bg-muted/30" : ""
|
|
14360
|
+
),
|
|
14361
|
+
onMouseEnter: handleMouseEnter,
|
|
14362
|
+
onMouseLeave: handleMouseLeave,
|
|
14363
|
+
ref: mouseRef,
|
|
14364
|
+
children: !dragging && hovering && gantt.onAddItem ? /* @__PURE__ */ jsxRuntime.jsx(GanttAddFeatureHelper, { top }) : null
|
|
14365
|
+
}
|
|
14366
|
+
)
|
|
14367
|
+
);
|
|
14368
|
+
};
|
|
14369
|
+
var GanttColumns = ({
|
|
14370
|
+
columns,
|
|
14371
|
+
isColumnSecondary
|
|
14372
|
+
}) => {
|
|
14373
|
+
const id = React27.useId();
|
|
14374
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14375
|
+
"div",
|
|
14376
|
+
{
|
|
14377
|
+
className: "divide grid h-full w-full divide-x divide-border",
|
|
14378
|
+
style: {
|
|
14379
|
+
gridTemplateColumns: `repeat(${columns}, var(--gantt-column-width))`
|
|
14380
|
+
},
|
|
14381
|
+
children: Array.from({ length: columns }).map((_, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
14382
|
+
GanttColumn,
|
|
14383
|
+
{
|
|
14384
|
+
index,
|
|
14385
|
+
isColumnSecondary
|
|
14386
|
+
},
|
|
14387
|
+
`${id}-${index}`
|
|
14388
|
+
))
|
|
14389
|
+
}
|
|
14390
|
+
);
|
|
14391
|
+
};
|
|
14392
|
+
var GanttGridLines = React27.memo(({
|
|
14393
|
+
className
|
|
14394
|
+
}) => {
|
|
14395
|
+
const gantt = React27.useContext(GanttContext);
|
|
14396
|
+
const id = React27.useId();
|
|
14397
|
+
const { columns, monthBoundaryIndices } = React27.useMemo(() => {
|
|
14398
|
+
let totalColumns = 0;
|
|
14399
|
+
const monthIndices = [];
|
|
14400
|
+
if (gantt.range === "weekly") {
|
|
14401
|
+
let weekIndex = 0;
|
|
14402
|
+
for (const yearData of gantt.timelineData) {
|
|
14403
|
+
const weeksInYear = getWeeksInYear(yearData.year);
|
|
14404
|
+
let currentMonth = -1;
|
|
14405
|
+
for (let w = 1; w <= weeksInYear; w++) {
|
|
14406
|
+
const weekStart = getWeekStartDate(yearData.year, w);
|
|
14407
|
+
const monthOfWeek = weekStart.getMonth();
|
|
14408
|
+
if (monthOfWeek !== currentMonth) {
|
|
14409
|
+
monthIndices.push(weekIndex);
|
|
14410
|
+
currentMonth = monthOfWeek;
|
|
14411
|
+
}
|
|
14412
|
+
weekIndex++;
|
|
14413
|
+
}
|
|
14414
|
+
totalColumns += weeksInYear;
|
|
14415
|
+
}
|
|
14416
|
+
} else {
|
|
14417
|
+
for (const yearData of gantt.timelineData) {
|
|
14418
|
+
const monthsInYear = yearData.quarters.flatMap((q) => q.months).length;
|
|
14419
|
+
totalColumns += monthsInYear;
|
|
14420
|
+
}
|
|
14421
|
+
}
|
|
14422
|
+
return { columns: totalColumns, monthBoundaryIndices: monthIndices };
|
|
14423
|
+
}, [gantt.timelineData, gantt.range]);
|
|
14424
|
+
const columnWidth = gantt.columnWidth * gantt.zoom / 100;
|
|
14425
|
+
const totalWidth = columns * columnWidth;
|
|
14426
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14427
|
+
"div",
|
|
14428
|
+
{
|
|
14429
|
+
className: cn(
|
|
14430
|
+
"pointer-events-none absolute top-0 left-0 h-full",
|
|
14431
|
+
className
|
|
14432
|
+
),
|
|
14433
|
+
style: {
|
|
14434
|
+
marginTop: "var(--gantt-header-height)",
|
|
14435
|
+
width: totalWidth
|
|
14436
|
+
},
|
|
14437
|
+
children: Array.from({ length: columns + 1 }).map((_, index) => {
|
|
14438
|
+
const isMonthBoundary = gantt.range === "weekly" && monthBoundaryIndices.includes(index);
|
|
14439
|
+
const x = Math.round(index * columnWidth);
|
|
14440
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14441
|
+
"div",
|
|
14442
|
+
{
|
|
14443
|
+
className: "absolute top-0 bottom-0",
|
|
14444
|
+
style: {
|
|
14445
|
+
left: x,
|
|
14446
|
+
width: isMonthBoundary ? "2px" : "1px",
|
|
14447
|
+
backgroundColor: isMonthBoundary ? "hsl(var(--foreground) / 0.25)" : "hsl(var(--border) / 0.8)"
|
|
14448
|
+
}
|
|
14449
|
+
},
|
|
14450
|
+
`${id}-grid-${index}`
|
|
14451
|
+
);
|
|
14452
|
+
})
|
|
14453
|
+
}
|
|
14454
|
+
);
|
|
14455
|
+
});
|
|
14456
|
+
GanttGridLines.displayName = "GanttGridLines";
|
|
14457
|
+
var GanttCreateMarkerTrigger = ({
|
|
14458
|
+
onCreateMarker,
|
|
14459
|
+
className
|
|
14460
|
+
}) => {
|
|
14461
|
+
const gantt = React27.useContext(GanttContext);
|
|
14462
|
+
const [mousePosition, mouseRef] = usehooks.useMouse();
|
|
14463
|
+
const [windowScroll] = usehooks.useWindowScroll();
|
|
14464
|
+
const x = usehooks.useThrottle(
|
|
14465
|
+
mousePosition.x - (mouseRef.current?.getBoundingClientRect().x ?? 0) - (windowScroll.x ?? 0),
|
|
14466
|
+
10
|
|
14467
|
+
);
|
|
14468
|
+
const date = getDateByMousePosition(gantt, x);
|
|
14469
|
+
const handleClick = () => onCreateMarker(date);
|
|
14470
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14471
|
+
"div",
|
|
14472
|
+
{
|
|
14473
|
+
className: cn(
|
|
14474
|
+
"group pointer-events-none absolute top-0 left-0 h-full w-full select-none overflow-visible",
|
|
14475
|
+
className
|
|
14476
|
+
),
|
|
14477
|
+
ref: mouseRef,
|
|
14478
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14479
|
+
"div",
|
|
14480
|
+
{
|
|
14481
|
+
className: "-ml-2 pointer-events-auto sticky top-6 z-20 flex w-4 flex-col items-center justify-center gap-1 overflow-visible opacity-0 group-hover:opacity-100",
|
|
14482
|
+
style: { transform: `translateX(${x}px)` },
|
|
14483
|
+
children: [
|
|
14484
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14485
|
+
"button",
|
|
14486
|
+
{
|
|
14487
|
+
className: "z-50 inline-flex h-4 w-4 items-center justify-center rounded-full bg-card",
|
|
14488
|
+
onClick: handleClick,
|
|
14489
|
+
type: "button",
|
|
14490
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PlusIcon, { className: "text-muted-foreground", size: 12 })
|
|
14491
|
+
}
|
|
14492
|
+
),
|
|
14493
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-nowrap rounded-sm border border-border/50 bg-background/95 px-2 py-1 text-foreground text-xs backdrop-blur-sm", children: dateFns.formatDate(date, "MMM dd, yyyy") })
|
|
14494
|
+
]
|
|
14495
|
+
}
|
|
14496
|
+
)
|
|
14497
|
+
}
|
|
14498
|
+
);
|
|
14499
|
+
};
|
|
14500
|
+
var GanttFeatureDragHelper = ({
|
|
14501
|
+
direction,
|
|
14502
|
+
featureId,
|
|
14503
|
+
date
|
|
14504
|
+
}) => {
|
|
14505
|
+
const [, setDragging] = useGanttDragging();
|
|
14506
|
+
const { attributes, listeners, setNodeRef } = core.useDraggable({
|
|
14507
|
+
id: `feature-drag-helper-${featureId}`
|
|
14508
|
+
});
|
|
14509
|
+
const isPressed = Boolean(attributes["aria-pressed"]);
|
|
14510
|
+
React27.useEffect(() => setDragging(isPressed), [isPressed, setDragging]);
|
|
14511
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14512
|
+
"div",
|
|
14513
|
+
{
|
|
14514
|
+
className: cn(
|
|
14515
|
+
"group -translate-y-1/2 !cursor-col-resize absolute top-1/2 z-[3] h-full w-6 rounded-sm outline-none",
|
|
14516
|
+
direction === "left" ? "-left-2.5" : "-right-2.5"
|
|
14517
|
+
),
|
|
14518
|
+
ref: setNodeRef,
|
|
14519
|
+
...attributes,
|
|
14520
|
+
...listeners,
|
|
14521
|
+
children: [
|
|
14522
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
14523
|
+
"div",
|
|
14524
|
+
{
|
|
14525
|
+
className: cn(
|
|
14526
|
+
"-translate-y-1/2 absolute top-1/2 h-[80%] w-1 rounded-sm bg-muted-foreground opacity-0 transition-all",
|
|
14527
|
+
direction === "left" ? "left-2.5" : "right-2.5",
|
|
14528
|
+
direction === "left" ? "group-hover:left-0" : "group-hover:right-0",
|
|
14529
|
+
isPressed && (direction === "left" ? "left-0" : "right-0"),
|
|
14530
|
+
"group-hover:opacity-100",
|
|
14531
|
+
isPressed && "opacity-100"
|
|
14532
|
+
)
|
|
14533
|
+
}
|
|
14534
|
+
),
|
|
14535
|
+
date && /* @__PURE__ */ jsxRuntime.jsx(
|
|
14536
|
+
"div",
|
|
14537
|
+
{
|
|
14538
|
+
className: cn(
|
|
14539
|
+
"-translate-x-1/2 absolute top-10 hidden whitespace-nowrap rounded-sm border border-border/50 bg-background/95 px-2 py-1 text-foreground text-xs backdrop-blur-sm group-hover:block",
|
|
14540
|
+
isPressed && "block"
|
|
14541
|
+
),
|
|
14542
|
+
children: dateFns.format(date, "MMM dd, yyyy")
|
|
14543
|
+
}
|
|
14544
|
+
)
|
|
14545
|
+
]
|
|
14546
|
+
}
|
|
14547
|
+
);
|
|
14548
|
+
};
|
|
14549
|
+
var GanttFeatureItemCard = ({
|
|
14550
|
+
id,
|
|
14551
|
+
children
|
|
14552
|
+
}) => {
|
|
14553
|
+
const [, setDragging] = useGanttDragging();
|
|
14554
|
+
const { attributes, listeners, setNodeRef } = core.useDraggable({ id });
|
|
14555
|
+
const isPressed = Boolean(attributes["aria-pressed"]);
|
|
14556
|
+
React27.useEffect(() => setDragging(isPressed), [isPressed, setDragging]);
|
|
14557
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "h-full w-full rounded-sm border bg-background px-2 py-1.5 text-xs shadow-sm overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14558
|
+
"div",
|
|
14559
|
+
{
|
|
14560
|
+
className: cn(
|
|
14561
|
+
"flex h-full w-full items-center justify-between gap-2 text-left",
|
|
14562
|
+
isPressed && "cursor-grabbing"
|
|
14563
|
+
),
|
|
14564
|
+
...attributes,
|
|
14565
|
+
...listeners,
|
|
14566
|
+
ref: setNodeRef,
|
|
14567
|
+
children
|
|
14568
|
+
}
|
|
14569
|
+
) });
|
|
14570
|
+
};
|
|
14571
|
+
var GanttFeatureItem = ({
|
|
14572
|
+
onMove,
|
|
14573
|
+
contextMenuActions,
|
|
14574
|
+
onEdit,
|
|
14575
|
+
onDelete,
|
|
14576
|
+
onDuplicate,
|
|
14577
|
+
children,
|
|
14578
|
+
className,
|
|
14579
|
+
...feature
|
|
14580
|
+
}) => {
|
|
14581
|
+
const [scrollX] = useGanttScrollX();
|
|
14582
|
+
const gantt = React27.useContext(GanttContext);
|
|
14583
|
+
const timelineStartDate = React27.useMemo(
|
|
14584
|
+
() => new Date(gantt.timelineData.at(0)?.year ?? 0, 0, 1),
|
|
14585
|
+
[gantt.timelineData]
|
|
14586
|
+
);
|
|
14587
|
+
const [startAt, setStartAt] = React27.useState(feature.startAt);
|
|
14588
|
+
const [endAt, setEndAt] = React27.useState(feature.endAt);
|
|
14589
|
+
const width = React27.useMemo(
|
|
14590
|
+
() => Math.round(getWidth(startAt, endAt, gantt)),
|
|
14591
|
+
[startAt, endAt, gantt]
|
|
14592
|
+
);
|
|
14593
|
+
const offset = React27.useMemo(
|
|
14594
|
+
() => Math.round(getOffset(startAt, timelineStartDate, gantt)),
|
|
14595
|
+
[startAt, timelineStartDate, gantt]
|
|
14596
|
+
);
|
|
14597
|
+
const addRange = React27.useMemo(() => getAddRange(gantt.range), [gantt.range]);
|
|
14598
|
+
const [mousePosition] = usehooks.useMouse();
|
|
14599
|
+
const [previousMouseX, setPreviousMouseX] = React27.useState(0);
|
|
14600
|
+
const [previousStartAt, setPreviousStartAt] = React27.useState(startAt);
|
|
14601
|
+
const [previousEndAt, setPreviousEndAt] = React27.useState(endAt);
|
|
14602
|
+
const mouseSensor = core.useSensor(core.MouseSensor, {
|
|
14603
|
+
activationConstraint: {
|
|
14604
|
+
distance: 10
|
|
14605
|
+
}
|
|
14606
|
+
});
|
|
14607
|
+
const handleItemDragStart = React27.useCallback(() => {
|
|
14608
|
+
setPreviousMouseX(mousePosition.x);
|
|
14609
|
+
setPreviousStartAt(startAt);
|
|
14610
|
+
setPreviousEndAt(endAt);
|
|
14611
|
+
}, [mousePosition.x, startAt, endAt]);
|
|
14612
|
+
const handleItemDragMove = React27.useCallback(() => {
|
|
14613
|
+
const currentDate = getDateByMousePosition(gantt, mousePosition.x);
|
|
14614
|
+
const originalDate = getDateByMousePosition(gantt, previousMouseX);
|
|
14615
|
+
const delta = gantt.range === "weekly" ? dateFns.differenceInDays(currentDate, originalDate) : getInnerDifferenceIn(gantt.range)(currentDate, originalDate);
|
|
14616
|
+
const newStartDate = dateFns.addDays(previousStartAt, delta);
|
|
14617
|
+
const newEndDate = previousEndAt ? dateFns.addDays(previousEndAt, delta) : null;
|
|
14618
|
+
setStartAt(newStartDate);
|
|
14619
|
+
setEndAt(newEndDate);
|
|
14620
|
+
}, [gantt, mousePosition.x, previousMouseX, previousStartAt, previousEndAt]);
|
|
14621
|
+
const onDragEnd = React27.useCallback(
|
|
14622
|
+
() => onMove?.(feature.id, startAt, endAt),
|
|
14623
|
+
[onMove, feature.id, startAt, endAt]
|
|
14624
|
+
);
|
|
14625
|
+
const handleLeftDragMove = React27.useCallback(() => {
|
|
14626
|
+
const ganttRect = gantt.ref?.current?.getBoundingClientRect();
|
|
14627
|
+
const x = mousePosition.x - (ganttRect?.left ?? 0) + scrollX - gantt.sidebarWidth;
|
|
14628
|
+
const newStartAt = getDateByMousePosition(gantt, x);
|
|
14629
|
+
setStartAt(newStartAt);
|
|
14630
|
+
}, [gantt, mousePosition.x, scrollX]);
|
|
14631
|
+
const handleRightDragMove = React27.useCallback(() => {
|
|
14632
|
+
const ganttRect = gantt.ref?.current?.getBoundingClientRect();
|
|
14633
|
+
const x = mousePosition.x - (ganttRect?.left ?? 0) + scrollX - gantt.sidebarWidth;
|
|
14634
|
+
const newEndAt = getDateByMousePosition(gantt, x);
|
|
14635
|
+
setEndAt(newEndAt);
|
|
14636
|
+
}, [gantt, mousePosition.x, scrollX]);
|
|
14637
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14638
|
+
"div",
|
|
14639
|
+
{
|
|
14640
|
+
className: cn("relative flex w-max min-w-full", className),
|
|
14641
|
+
style: { height: "var(--gantt-row-height)" },
|
|
14642
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14643
|
+
"div",
|
|
14644
|
+
{
|
|
14645
|
+
className: "pointer-events-auto absolute",
|
|
14646
|
+
style: {
|
|
14647
|
+
height: "calc(var(--gantt-row-height) - 8px)",
|
|
14648
|
+
top: "4px",
|
|
14649
|
+
width,
|
|
14650
|
+
left: offset
|
|
14651
|
+
},
|
|
14652
|
+
children: [
|
|
14653
|
+
onMove && /* @__PURE__ */ jsxRuntime.jsx(
|
|
14654
|
+
core.DndContext,
|
|
14655
|
+
{
|
|
14656
|
+
modifiers: [modifiers.restrictToHorizontalAxis],
|
|
14657
|
+
onDragEnd,
|
|
14658
|
+
onDragMove: handleLeftDragMove,
|
|
14659
|
+
sensors: [mouseSensor],
|
|
14660
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14661
|
+
GanttFeatureDragHelper,
|
|
14662
|
+
{
|
|
14663
|
+
date: startAt,
|
|
14664
|
+
direction: "left",
|
|
14665
|
+
featureId: feature.id
|
|
14666
|
+
}
|
|
14667
|
+
)
|
|
14668
|
+
}
|
|
14669
|
+
),
|
|
14670
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ContextMenu, { children: [
|
|
14671
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14672
|
+
core.DndContext,
|
|
14673
|
+
{
|
|
14674
|
+
modifiers: [modifiers.restrictToHorizontalAxis],
|
|
14675
|
+
onDragEnd,
|
|
14676
|
+
onDragMove: handleItemDragMove,
|
|
14677
|
+
onDragStart: handleItemDragStart,
|
|
14678
|
+
sensors: [mouseSensor],
|
|
14679
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(GanttFeatureItemCard, { id: feature.id, children: children ?? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "flex-1 truncate text-xs", children: feature.name }) })
|
|
14680
|
+
}
|
|
14681
|
+
) }) }),
|
|
14682
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ContextMenuContent, { children: [
|
|
14683
|
+
onEdit && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14684
|
+
ContextMenuItem,
|
|
14685
|
+
{
|
|
14686
|
+
className: "flex items-center gap-2",
|
|
14687
|
+
onClick: () => onEdit(feature),
|
|
14688
|
+
children: [
|
|
14689
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.PencilIcon, { size: 14 }),
|
|
14690
|
+
"Edit"
|
|
14691
|
+
]
|
|
14692
|
+
}
|
|
14693
|
+
),
|
|
14694
|
+
onDuplicate && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14695
|
+
ContextMenuItem,
|
|
14696
|
+
{
|
|
14697
|
+
className: "flex items-center gap-2",
|
|
14698
|
+
onClick: () => onDuplicate(feature),
|
|
14699
|
+
children: [
|
|
14700
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CopyIcon, { size: 14 }),
|
|
14701
|
+
"Duplicate"
|
|
14702
|
+
]
|
|
14703
|
+
}
|
|
14704
|
+
),
|
|
14705
|
+
contextMenuActions?.map((action, index) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14706
|
+
ContextMenuItem,
|
|
14707
|
+
{
|
|
14708
|
+
className: cn(
|
|
14709
|
+
"flex items-center gap-2",
|
|
14710
|
+
action.destructive && "text-destructive"
|
|
14711
|
+
),
|
|
14712
|
+
onClick: () => action.onClick(feature),
|
|
14713
|
+
disabled: action.disabled,
|
|
14714
|
+
children: [
|
|
14715
|
+
action.icon,
|
|
14716
|
+
action.label
|
|
14717
|
+
]
|
|
14718
|
+
},
|
|
14719
|
+
index
|
|
14720
|
+
)),
|
|
14721
|
+
onDelete && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14722
|
+
ContextMenuItem,
|
|
14723
|
+
{
|
|
14724
|
+
className: "flex items-center gap-2 text-destructive",
|
|
14725
|
+
onClick: () => onDelete(feature),
|
|
14726
|
+
children: [
|
|
14727
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrashIcon, { size: 14 }),
|
|
14728
|
+
"Delete"
|
|
14729
|
+
]
|
|
14730
|
+
}
|
|
14731
|
+
)
|
|
14732
|
+
] })
|
|
14733
|
+
] }),
|
|
14734
|
+
onMove && /* @__PURE__ */ jsxRuntime.jsx(
|
|
14735
|
+
core.DndContext,
|
|
14736
|
+
{
|
|
14737
|
+
modifiers: [modifiers.restrictToHorizontalAxis],
|
|
14738
|
+
onDragEnd,
|
|
14739
|
+
onDragMove: handleRightDragMove,
|
|
14740
|
+
sensors: [mouseSensor],
|
|
14741
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
14742
|
+
GanttFeatureDragHelper,
|
|
14743
|
+
{
|
|
14744
|
+
date: endAt ?? addRange(startAt, 2),
|
|
14745
|
+
direction: "right",
|
|
14746
|
+
featureId: feature.id
|
|
14747
|
+
}
|
|
14748
|
+
)
|
|
14749
|
+
}
|
|
14750
|
+
)
|
|
14751
|
+
]
|
|
14752
|
+
}
|
|
14753
|
+
)
|
|
14754
|
+
}
|
|
14755
|
+
);
|
|
14756
|
+
};
|
|
14757
|
+
var GanttFeatureListGroup = ({
|
|
14758
|
+
children,
|
|
14759
|
+
className
|
|
14760
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
14761
|
+
"div",
|
|
14762
|
+
{
|
|
14763
|
+
className,
|
|
14764
|
+
style: { paddingTop: "var(--gantt-row-height)" },
|
|
14765
|
+
children
|
|
14766
|
+
}
|
|
14767
|
+
);
|
|
14768
|
+
var GanttFeatureRow = ({
|
|
14769
|
+
features,
|
|
14770
|
+
onMove,
|
|
14771
|
+
children,
|
|
14772
|
+
className
|
|
14773
|
+
}) => {
|
|
14774
|
+
const sortedFeatures = [...features].sort(
|
|
14775
|
+
(a, b) => a.startAt.getTime() - b.startAt.getTime()
|
|
14776
|
+
);
|
|
14777
|
+
const featureWithPositions = [];
|
|
14778
|
+
const subRowEndTimes = [];
|
|
14779
|
+
for (const feature of sortedFeatures) {
|
|
14780
|
+
let subRow = 0;
|
|
14781
|
+
while (subRow < subRowEndTimes.length && subRowEndTimes[subRow] > feature.startAt) {
|
|
14782
|
+
subRow++;
|
|
14783
|
+
}
|
|
14784
|
+
if (subRow === subRowEndTimes.length) {
|
|
14785
|
+
subRowEndTimes.push(feature.endAt);
|
|
14786
|
+
} else {
|
|
14787
|
+
subRowEndTimes[subRow] = feature.endAt;
|
|
14788
|
+
}
|
|
14789
|
+
featureWithPositions.push({ ...feature, subRow });
|
|
14790
|
+
}
|
|
14791
|
+
const maxSubRows = Math.max(1, subRowEndTimes.length);
|
|
14792
|
+
const subRowHeight = 36;
|
|
14793
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
14794
|
+
"div",
|
|
14795
|
+
{
|
|
14796
|
+
className: cn("relative", className),
|
|
14797
|
+
style: {
|
|
14798
|
+
height: `${maxSubRows * subRowHeight}px`,
|
|
14799
|
+
minHeight: "var(--gantt-row-height)"
|
|
14800
|
+
},
|
|
14801
|
+
children: featureWithPositions.map((feature) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
14802
|
+
"div",
|
|
14803
|
+
{
|
|
14804
|
+
className: "absolute w-full",
|
|
14805
|
+
style: {
|
|
14806
|
+
top: `${feature.subRow * subRowHeight}px`,
|
|
14807
|
+
height: `${subRowHeight}px`
|
|
14808
|
+
},
|
|
14809
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(GanttFeatureItem, { ...feature, onMove, children: children ? children(feature) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "flex-1 truncate text-xs", children: feature.name }) })
|
|
14810
|
+
},
|
|
14811
|
+
feature.id
|
|
14812
|
+
))
|
|
14813
|
+
}
|
|
14814
|
+
);
|
|
14815
|
+
};
|
|
14816
|
+
var GanttFeatureList = ({
|
|
14817
|
+
className,
|
|
14818
|
+
children
|
|
14819
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
14820
|
+
"div",
|
|
14821
|
+
{
|
|
14822
|
+
className: cn("absolute top-0 left-0 h-max w-max min-w-full", className),
|
|
14823
|
+
style: { marginTop: "var(--gantt-header-height)" },
|
|
14824
|
+
children
|
|
14825
|
+
}
|
|
14826
|
+
);
|
|
14827
|
+
var GanttMarker = React27.memo(({ label, date, id, onRemove, className }) => {
|
|
14828
|
+
const gantt = React27.useContext(GanttContext);
|
|
14829
|
+
const differenceIn = React27.useMemo(
|
|
14830
|
+
() => getDifferenceIn(gantt.range),
|
|
14831
|
+
[gantt.range]
|
|
14832
|
+
);
|
|
14833
|
+
const timelineStartDate = React27.useMemo(
|
|
14834
|
+
() => new Date(gantt.timelineData.at(0)?.year ?? 0, 0, 1),
|
|
14835
|
+
[gantt.timelineData]
|
|
14836
|
+
);
|
|
14837
|
+
const offset = React27.useMemo(
|
|
14838
|
+
() => differenceIn(date, timelineStartDate),
|
|
14839
|
+
[differenceIn, date, timelineStartDate]
|
|
14840
|
+
);
|
|
14841
|
+
const innerOffset = React27.useMemo(
|
|
14842
|
+
() => calculateInnerOffset(
|
|
14843
|
+
date,
|
|
14844
|
+
gantt.range,
|
|
14845
|
+
gantt.columnWidth * gantt.zoom / 100
|
|
14846
|
+
),
|
|
14847
|
+
[date, gantt.range, gantt.columnWidth, gantt.zoom]
|
|
14848
|
+
);
|
|
14849
|
+
const handleRemove = React27.useCallback(() => onRemove?.(id), [onRemove, id]);
|
|
14850
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14851
|
+
"div",
|
|
14852
|
+
{
|
|
14853
|
+
className: "pointer-events-none absolute top-0 left-0 z-20 flex h-full select-none flex-col items-center justify-center overflow-visible",
|
|
14854
|
+
style: {
|
|
14855
|
+
width: 0,
|
|
14856
|
+
transform: `translateX(calc(var(--gantt-column-width) * ${offset} + ${Math.round(innerOffset)}px))`
|
|
14857
|
+
},
|
|
14858
|
+
children: [
|
|
14859
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ContextMenu, { children: [
|
|
14860
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14861
|
+
"div",
|
|
14862
|
+
{
|
|
14863
|
+
className: cn(
|
|
14864
|
+
"group pointer-events-auto sticky top-0 flex select-auto flex-col flex-nowrap items-center justify-center whitespace-nowrap rounded-sm bg-card px-2 py-1 text-foreground text-xs shadow-sm",
|
|
14865
|
+
className
|
|
14866
|
+
),
|
|
14867
|
+
children: [
|
|
14868
|
+
label,
|
|
14869
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-h-[0] overflow-hidden opacity-80 transition-all group-hover:max-h-[2rem]", children: dateFns.formatDate(date, "MMM dd, yyyy") })
|
|
14870
|
+
]
|
|
14871
|
+
}
|
|
14872
|
+
) }),
|
|
14873
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextMenuContent, { children: onRemove ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
14874
|
+
ContextMenuItem,
|
|
14875
|
+
{
|
|
14876
|
+
className: "flex items-center gap-2 text-destructive",
|
|
14877
|
+
onClick: handleRemove,
|
|
14878
|
+
children: [
|
|
14879
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrashIcon, { size: 16 }),
|
|
14880
|
+
"Remove marker"
|
|
14881
|
+
]
|
|
14882
|
+
}
|
|
14883
|
+
) : null })
|
|
14884
|
+
] }),
|
|
14885
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("h-full w-px bg-card", className) })
|
|
14886
|
+
]
|
|
14887
|
+
}
|
|
14888
|
+
);
|
|
14889
|
+
});
|
|
14890
|
+
GanttMarker.displayName = "GanttMarker";
|
|
14891
|
+
var GanttProvider = ({
|
|
14892
|
+
zoom = 100,
|
|
14893
|
+
range = "monthly",
|
|
14894
|
+
onAddItem,
|
|
14895
|
+
children,
|
|
14896
|
+
className,
|
|
14897
|
+
defaultExpandedGroups = {}
|
|
14898
|
+
}) => {
|
|
14899
|
+
const scrollRef = React27.useRef(null);
|
|
14900
|
+
const [timelineData, setTimelineData] = React27.useState(
|
|
14901
|
+
createInitialTimelineData(/* @__PURE__ */ new Date())
|
|
14902
|
+
);
|
|
14903
|
+
const [, setScrollX] = useGanttScrollX();
|
|
14904
|
+
const sidebarWidth = 280;
|
|
14905
|
+
const [expandedGroups, setExpandedGroups] = React27.useState(defaultExpandedGroups);
|
|
14906
|
+
const setGroupExpanded = React27.useCallback((groupId, expanded) => {
|
|
14907
|
+
setExpandedGroups((prev) => ({ ...prev, [groupId]: expanded }));
|
|
14908
|
+
}, []);
|
|
14909
|
+
const headerHeight = 60;
|
|
14910
|
+
const rowHeight = 36;
|
|
14911
|
+
let columnWidth = 50;
|
|
14912
|
+
if (range === "monthly") {
|
|
14913
|
+
columnWidth = 150;
|
|
14914
|
+
} else if (range === "quarterly") {
|
|
14915
|
+
columnWidth = 100;
|
|
14916
|
+
}
|
|
14917
|
+
const cssVariables = React27.useMemo(
|
|
14918
|
+
() => ({
|
|
14919
|
+
"--gantt-zoom": `${zoom}`,
|
|
14920
|
+
"--gantt-column-width": `${zoom / 100 * columnWidth}px`,
|
|
14921
|
+
"--gantt-header-height": `${headerHeight}px`,
|
|
14922
|
+
"--gantt-row-height": `${rowHeight}px`,
|
|
14923
|
+
"--gantt-sidebar-width": `${sidebarWidth}px`
|
|
14924
|
+
}),
|
|
14925
|
+
[zoom, columnWidth, sidebarWidth]
|
|
14926
|
+
);
|
|
14927
|
+
React27.useEffect(() => {
|
|
14928
|
+
if (scrollRef.current) {
|
|
14929
|
+
scrollRef.current.scrollLeft = scrollRef.current.scrollWidth / 2 - scrollRef.current.clientWidth / 2;
|
|
14930
|
+
setScrollX(scrollRef.current.scrollLeft);
|
|
14931
|
+
}
|
|
14932
|
+
}, [setScrollX]);
|
|
14933
|
+
const handleScroll = React27.useCallback(
|
|
14934
|
+
throttle__default.default(() => {
|
|
14935
|
+
const scrollElement = scrollRef.current;
|
|
14936
|
+
if (!scrollElement) {
|
|
14937
|
+
return;
|
|
14938
|
+
}
|
|
14939
|
+
const { scrollLeft, scrollWidth, clientWidth } = scrollElement;
|
|
14940
|
+
setScrollX(scrollLeft);
|
|
14941
|
+
if (scrollLeft === 0) {
|
|
14942
|
+
const firstYear = timelineData[0]?.year;
|
|
14943
|
+
if (!firstYear) {
|
|
14944
|
+
return;
|
|
14945
|
+
}
|
|
14946
|
+
const newTimelineData = [...timelineData];
|
|
14947
|
+
newTimelineData.unshift({
|
|
14948
|
+
year: firstYear - 1,
|
|
14949
|
+
quarters: new Array(4).fill(null).map((_, quarterIndex) => ({
|
|
14950
|
+
months: new Array(3).fill(null).map((_2, monthIndex) => {
|
|
14951
|
+
const month = quarterIndex * 3 + monthIndex;
|
|
14952
|
+
return {
|
|
14953
|
+
days: dateFns.getDaysInMonth(new Date(firstYear, month, 1))
|
|
14954
|
+
};
|
|
14955
|
+
})
|
|
14956
|
+
}))
|
|
14957
|
+
});
|
|
14958
|
+
setTimelineData(newTimelineData);
|
|
14959
|
+
scrollElement.scrollLeft = scrollElement.clientWidth;
|
|
14960
|
+
setScrollX(scrollElement.scrollLeft);
|
|
14961
|
+
} else if (scrollLeft + clientWidth >= scrollWidth) {
|
|
14962
|
+
const lastYear = timelineData.at(-1)?.year;
|
|
14963
|
+
if (!lastYear) {
|
|
14964
|
+
return;
|
|
14965
|
+
}
|
|
14966
|
+
const newTimelineData = [...timelineData];
|
|
14967
|
+
newTimelineData.push({
|
|
14968
|
+
year: lastYear + 1,
|
|
14969
|
+
quarters: new Array(4).fill(null).map((_, quarterIndex) => ({
|
|
14970
|
+
months: new Array(3).fill(null).map((_2, monthIndex) => {
|
|
14971
|
+
const month = quarterIndex * 3 + monthIndex;
|
|
14972
|
+
return {
|
|
14973
|
+
days: dateFns.getDaysInMonth(new Date(lastYear, month, 1))
|
|
14974
|
+
};
|
|
14975
|
+
})
|
|
14976
|
+
}))
|
|
14977
|
+
});
|
|
14978
|
+
setTimelineData(newTimelineData);
|
|
14979
|
+
scrollElement.scrollLeft = scrollElement.scrollWidth - scrollElement.clientWidth;
|
|
14980
|
+
setScrollX(scrollElement.scrollLeft);
|
|
14981
|
+
}
|
|
14982
|
+
}, 100),
|
|
14983
|
+
[]
|
|
14984
|
+
);
|
|
14985
|
+
React27.useEffect(() => {
|
|
14986
|
+
const scrollElement = scrollRef.current;
|
|
14987
|
+
if (scrollElement) {
|
|
14988
|
+
scrollElement.addEventListener("scroll", handleScroll);
|
|
14989
|
+
}
|
|
14990
|
+
return () => {
|
|
14991
|
+
if (scrollElement) {
|
|
14992
|
+
scrollElement.removeEventListener("scroll", handleScroll);
|
|
14993
|
+
}
|
|
14994
|
+
};
|
|
14995
|
+
}, [handleScroll]);
|
|
14996
|
+
const scrollToFeature = React27.useCallback(
|
|
14997
|
+
(feature) => {
|
|
14998
|
+
const scrollElement = scrollRef.current;
|
|
14999
|
+
if (!scrollElement) {
|
|
15000
|
+
return;
|
|
15001
|
+
}
|
|
15002
|
+
const timelineStartDate = new Date(timelineData[0].year, 0, 1);
|
|
15003
|
+
const offset = getOffset(feature.startAt, timelineStartDate, {
|
|
15004
|
+
zoom,
|
|
15005
|
+
range,
|
|
15006
|
+
columnWidth});
|
|
15007
|
+
const targetScrollLeft = Math.max(0, offset);
|
|
15008
|
+
scrollElement.scrollTo({
|
|
15009
|
+
left: targetScrollLeft,
|
|
15010
|
+
behavior: "smooth"
|
|
15011
|
+
});
|
|
15012
|
+
},
|
|
15013
|
+
[timelineData, zoom, range, columnWidth, sidebarWidth, onAddItem, expandedGroups, setGroupExpanded]
|
|
15014
|
+
);
|
|
15015
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15016
|
+
GanttContext.Provider,
|
|
15017
|
+
{
|
|
15018
|
+
value: {
|
|
15019
|
+
zoom,
|
|
15020
|
+
range,
|
|
15021
|
+
headerHeight,
|
|
15022
|
+
columnWidth,
|
|
15023
|
+
sidebarWidth,
|
|
15024
|
+
rowHeight,
|
|
15025
|
+
onAddItem,
|
|
15026
|
+
timelineData,
|
|
15027
|
+
placeholderLength: 2,
|
|
15028
|
+
ref: scrollRef,
|
|
15029
|
+
scrollToFeature,
|
|
15030
|
+
expandedGroups,
|
|
15031
|
+
setGroupExpanded
|
|
15032
|
+
},
|
|
15033
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15034
|
+
"div",
|
|
15035
|
+
{
|
|
15036
|
+
className: cn(
|
|
15037
|
+
"gantt relative isolate grid h-full w-full flex-none select-none rounded-sm border bg-background",
|
|
15038
|
+
range,
|
|
15039
|
+
className
|
|
15040
|
+
),
|
|
15041
|
+
style: {
|
|
15042
|
+
...cssVariables,
|
|
15043
|
+
gridTemplateColumns: "var(--gantt-sidebar-width) 1fr"
|
|
15044
|
+
},
|
|
15045
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15046
|
+
"div",
|
|
15047
|
+
{
|
|
15048
|
+
className: "col-span-2 grid overflow-auto",
|
|
15049
|
+
ref: scrollRef,
|
|
15050
|
+
style: {
|
|
15051
|
+
gridTemplateColumns: "var(--gantt-sidebar-width) 1fr"
|
|
15052
|
+
},
|
|
15053
|
+
children
|
|
15054
|
+
}
|
|
15055
|
+
)
|
|
15056
|
+
}
|
|
15057
|
+
)
|
|
15058
|
+
}
|
|
15059
|
+
);
|
|
15060
|
+
};
|
|
15061
|
+
var GanttTimeline = ({
|
|
15062
|
+
children,
|
|
15063
|
+
className
|
|
15064
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
15065
|
+
"div",
|
|
15066
|
+
{
|
|
15067
|
+
className: cn(
|
|
15068
|
+
"relative flex h-max min-h-full w-max flex-none",
|
|
15069
|
+
className
|
|
15070
|
+
),
|
|
15071
|
+
children
|
|
15072
|
+
}
|
|
15073
|
+
);
|
|
15074
|
+
var GanttToday = ({ className }) => {
|
|
15075
|
+
const label = "Today";
|
|
15076
|
+
const date = React27.useMemo(() => /* @__PURE__ */ new Date(), []);
|
|
15077
|
+
const gantt = React27.useContext(GanttContext);
|
|
15078
|
+
const differenceIn = React27.useMemo(
|
|
15079
|
+
() => getDifferenceIn(gantt.range),
|
|
15080
|
+
[gantt.range]
|
|
15081
|
+
);
|
|
15082
|
+
const timelineStartDate = React27.useMemo(
|
|
15083
|
+
() => new Date(gantt.timelineData.at(0)?.year ?? 0, 0, 1),
|
|
15084
|
+
[gantt.timelineData]
|
|
15085
|
+
);
|
|
15086
|
+
const offset = React27.useMemo(
|
|
15087
|
+
() => differenceIn(date, timelineStartDate),
|
|
15088
|
+
[differenceIn, date, timelineStartDate]
|
|
15089
|
+
);
|
|
15090
|
+
const innerOffset = React27.useMemo(
|
|
15091
|
+
() => Math.round(calculateInnerOffset(
|
|
15092
|
+
date,
|
|
15093
|
+
gantt.range,
|
|
15094
|
+
gantt.columnWidth * gantt.zoom / 100
|
|
15095
|
+
)),
|
|
15096
|
+
[date, gantt.range, gantt.columnWidth, gantt.zoom]
|
|
15097
|
+
);
|
|
15098
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15099
|
+
"div",
|
|
15100
|
+
{
|
|
15101
|
+
className: "pointer-events-none absolute top-0 left-0 z-20 flex h-full select-none flex-col items-center justify-center overflow-visible",
|
|
15102
|
+
style: {
|
|
15103
|
+
width: 0,
|
|
15104
|
+
transform: `translateX(calc(var(--gantt-column-width) * ${offset} + ${innerOffset}px))`
|
|
15105
|
+
},
|
|
15106
|
+
children: [
|
|
15107
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
15108
|
+
"div",
|
|
15109
|
+
{
|
|
15110
|
+
className: cn(
|
|
15111
|
+
"group pointer-events-auto sticky top-0 flex select-auto flex-col flex-nowrap items-center justify-center whitespace-nowrap rounded-sm bg-primary px-2 py-1 text-primary-foreground text-xs shadow-sm",
|
|
15112
|
+
className
|
|
15113
|
+
),
|
|
15114
|
+
children: [
|
|
15115
|
+
label,
|
|
15116
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-h-[0] overflow-hidden opacity-80 transition-all group-hover:max-h-[2rem]", children: dateFns.formatDate(date, "MMM dd, yyyy") })
|
|
15117
|
+
]
|
|
15118
|
+
}
|
|
15119
|
+
),
|
|
15120
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("h-full w-px bg-primary", className) })
|
|
15121
|
+
]
|
|
15122
|
+
}
|
|
15123
|
+
);
|
|
15124
|
+
};
|
|
15125
|
+
var t = tunnel__default.default();
|
|
15126
|
+
var KanbanContext = React27.createContext({
|
|
15127
|
+
columns: [],
|
|
15128
|
+
data: [],
|
|
15129
|
+
activeCardId: null
|
|
15130
|
+
});
|
|
15131
|
+
var KanbanBoard = ({ id, children, className }) => {
|
|
15132
|
+
const { isOver, setNodeRef } = core.useDroppable({
|
|
15133
|
+
id
|
|
15134
|
+
});
|
|
15135
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15136
|
+
"div",
|
|
15137
|
+
{
|
|
15138
|
+
className: cn(
|
|
15139
|
+
"flex size-full min-h-40 flex-col divide-y overflow-hidden rounded-md border bg-secondary text-xs shadow-sm ring-2 transition-all",
|
|
15140
|
+
isOver ? "ring-primary" : "ring-transparent",
|
|
15141
|
+
className
|
|
15142
|
+
),
|
|
15143
|
+
ref: setNodeRef,
|
|
15144
|
+
children
|
|
15145
|
+
}
|
|
15146
|
+
);
|
|
15147
|
+
};
|
|
15148
|
+
var KanbanCard = ({
|
|
15149
|
+
id,
|
|
15150
|
+
name,
|
|
15151
|
+
children,
|
|
15152
|
+
className
|
|
15153
|
+
}) => {
|
|
15154
|
+
const {
|
|
15155
|
+
attributes,
|
|
15156
|
+
listeners,
|
|
15157
|
+
setNodeRef,
|
|
15158
|
+
transition,
|
|
15159
|
+
transform,
|
|
15160
|
+
isDragging
|
|
15161
|
+
} = sortable.useSortable({
|
|
15162
|
+
id
|
|
15163
|
+
});
|
|
15164
|
+
const { activeCardId } = React27.useContext(KanbanContext);
|
|
15165
|
+
const style = {
|
|
15166
|
+
transition,
|
|
15167
|
+
transform: utilities.CSS.Transform.toString(transform)
|
|
15168
|
+
};
|
|
15169
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
15170
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style, ...listeners, ...attributes, ref: setNodeRef, className: "min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15171
|
+
Card,
|
|
15172
|
+
{
|
|
15173
|
+
className: cn(
|
|
15174
|
+
"cursor-grab gap-4 rounded-md p-2 shadow-sm overflow-hidden",
|
|
15175
|
+
isDragging && "pointer-events-none cursor-grabbing opacity-30",
|
|
15176
|
+
className
|
|
15177
|
+
),
|
|
15178
|
+
children: children ?? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 font-medium text-sm", children: name })
|
|
15179
|
+
}
|
|
15180
|
+
) }),
|
|
15181
|
+
activeCardId === id && /* @__PURE__ */ jsxRuntime.jsx(t.In, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15182
|
+
Card,
|
|
15183
|
+
{
|
|
15184
|
+
className: cn(
|
|
15185
|
+
"cursor-grab gap-4 rounded-md p-2 shadow-sm ring-2 ring-primary overflow-hidden",
|
|
15186
|
+
isDragging && "cursor-grabbing",
|
|
15187
|
+
className
|
|
15188
|
+
),
|
|
15189
|
+
children: children ?? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "m-0 font-medium text-sm", children: name })
|
|
15190
|
+
}
|
|
15191
|
+
) })
|
|
15192
|
+
] });
|
|
15193
|
+
};
|
|
15194
|
+
var KanbanCards = ({
|
|
15195
|
+
children,
|
|
15196
|
+
className,
|
|
15197
|
+
...props
|
|
15198
|
+
}) => {
|
|
15199
|
+
const { data } = React27.useContext(KanbanContext);
|
|
15200
|
+
const filteredData = data.filter((item) => item.column === props.id);
|
|
15201
|
+
const items = filteredData.map((item) => item.id);
|
|
15202
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ScrollArea, { className: "overflow-hidden", children: [
|
|
15203
|
+
/* @__PURE__ */ jsxRuntime.jsx(sortable.SortableContext, { items, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15204
|
+
"div",
|
|
15205
|
+
{
|
|
15206
|
+
className: cn("flex flex-grow flex-col gap-2 p-2", className),
|
|
15207
|
+
...props,
|
|
15208
|
+
children: filteredData.map(children)
|
|
15209
|
+
}
|
|
15210
|
+
) }),
|
|
15211
|
+
/* @__PURE__ */ jsxRuntime.jsx(ScrollBar, { orientation: "vertical" })
|
|
15212
|
+
] });
|
|
15213
|
+
};
|
|
15214
|
+
var KanbanHeader = ({ className, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("m-0 p-2 font-semibold text-sm", className), ...props });
|
|
15215
|
+
var KanbanProvider = ({
|
|
15216
|
+
children,
|
|
15217
|
+
onDragStart,
|
|
15218
|
+
onDragEnd,
|
|
15219
|
+
onDragOver,
|
|
15220
|
+
className,
|
|
15221
|
+
columns,
|
|
15222
|
+
data,
|
|
15223
|
+
onDataChange,
|
|
15224
|
+
...props
|
|
15225
|
+
}) => {
|
|
15226
|
+
const [activeCardId, setActiveCardId] = React27.useState(null);
|
|
15227
|
+
const sensors = core.useSensors(
|
|
15228
|
+
core.useSensor(core.MouseSensor),
|
|
15229
|
+
core.useSensor(core.TouchSensor),
|
|
15230
|
+
core.useSensor(core.KeyboardSensor)
|
|
15231
|
+
);
|
|
15232
|
+
const handleDragStart = (event) => {
|
|
15233
|
+
const card = data.find((item) => item.id === event.active.id);
|
|
15234
|
+
if (card) {
|
|
15235
|
+
setActiveCardId(event.active.id);
|
|
15236
|
+
}
|
|
15237
|
+
onDragStart?.(event);
|
|
15238
|
+
};
|
|
15239
|
+
const handleDragOver = (event) => {
|
|
15240
|
+
const { active, over } = event;
|
|
15241
|
+
if (!over) {
|
|
15242
|
+
return;
|
|
15243
|
+
}
|
|
15244
|
+
const activeItem = data.find((item) => item.id === active.id);
|
|
15245
|
+
const overItem = data.find((item) => item.id === over.id);
|
|
15246
|
+
if (!activeItem) {
|
|
15247
|
+
return;
|
|
15248
|
+
}
|
|
15249
|
+
const activeColumn = activeItem.column;
|
|
15250
|
+
const overColumn = overItem?.column || columns.find((col) => col.id === over.id)?.id || columns[0]?.id;
|
|
15251
|
+
if (activeColumn !== overColumn) {
|
|
15252
|
+
let newData = [...data];
|
|
15253
|
+
const activeIndex = newData.findIndex((item) => item.id === active.id);
|
|
15254
|
+
const overIndex = newData.findIndex((item) => item.id === over.id);
|
|
15255
|
+
newData[activeIndex].column = overColumn;
|
|
15256
|
+
newData = sortable.arrayMove(newData, activeIndex, overIndex);
|
|
15257
|
+
onDataChange?.(newData);
|
|
15258
|
+
}
|
|
15259
|
+
onDragOver?.(event);
|
|
15260
|
+
};
|
|
15261
|
+
const handleDragEnd = (event) => {
|
|
15262
|
+
setActiveCardId(null);
|
|
15263
|
+
onDragEnd?.(event);
|
|
15264
|
+
const { active, over } = event;
|
|
15265
|
+
if (!over || active.id === over.id) {
|
|
15266
|
+
return;
|
|
15267
|
+
}
|
|
15268
|
+
let newData = [...data];
|
|
15269
|
+
const oldIndex = newData.findIndex((item) => item.id === active.id);
|
|
15270
|
+
const newIndex = newData.findIndex((item) => item.id === over.id);
|
|
15271
|
+
newData = sortable.arrayMove(newData, oldIndex, newIndex);
|
|
15272
|
+
onDataChange?.(newData);
|
|
15273
|
+
};
|
|
15274
|
+
const announcements = {
|
|
15275
|
+
onDragStart({ active }) {
|
|
15276
|
+
const { name, column } = data.find((item) => item.id === active.id) ?? {};
|
|
15277
|
+
return `Picked up the card "${name}" from the "${column}" column`;
|
|
15278
|
+
},
|
|
15279
|
+
onDragOver({ active, over }) {
|
|
15280
|
+
const { name } = data.find((item) => item.id === active.id) ?? {};
|
|
15281
|
+
const newColumn = columns.find((column) => column.id === over?.id)?.name;
|
|
15282
|
+
return `Dragged the card "${name}" over the "${newColumn}" column`;
|
|
15283
|
+
},
|
|
15284
|
+
onDragEnd({ active, over }) {
|
|
15285
|
+
const { name } = data.find((item) => item.id === active.id) ?? {};
|
|
15286
|
+
const newColumn = columns.find((column) => column.id === over?.id)?.name;
|
|
15287
|
+
return `Dropped the card "${name}" into the "${newColumn}" column`;
|
|
15288
|
+
},
|
|
15289
|
+
onDragCancel({ active }) {
|
|
15290
|
+
const { name } = data.find((item) => item.id === active.id) ?? {};
|
|
15291
|
+
return `Cancelled dragging the card "${name}"`;
|
|
15292
|
+
}
|
|
15293
|
+
};
|
|
15294
|
+
return /* @__PURE__ */ jsxRuntime.jsx(KanbanContext.Provider, { value: { columns, data, activeCardId }, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15295
|
+
core.DndContext,
|
|
15296
|
+
{
|
|
15297
|
+
accessibility: { announcements },
|
|
15298
|
+
collisionDetection: core.closestCenter,
|
|
15299
|
+
onDragEnd: handleDragEnd,
|
|
15300
|
+
onDragOver: handleDragOver,
|
|
15301
|
+
onDragStart: handleDragStart,
|
|
15302
|
+
sensors,
|
|
15303
|
+
...props,
|
|
15304
|
+
children: [
|
|
15305
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
15306
|
+
"div",
|
|
15307
|
+
{
|
|
15308
|
+
className: cn(
|
|
15309
|
+
"grid size-full auto-cols-fr grid-flow-col gap-4",
|
|
15310
|
+
className
|
|
15311
|
+
),
|
|
15312
|
+
children: columns.map((column) => children(column))
|
|
15313
|
+
}
|
|
15314
|
+
),
|
|
15315
|
+
typeof window !== "undefined" && reactDom.createPortal(
|
|
15316
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.DragOverlay, { children: /* @__PURE__ */ jsxRuntime.jsx(t.Out, {}) }),
|
|
15317
|
+
document.body
|
|
15318
|
+
)
|
|
15319
|
+
]
|
|
15320
|
+
}
|
|
15321
|
+
) });
|
|
15322
|
+
};
|
|
13630
15323
|
|
|
13631
15324
|
Object.defineProperty(exports, "flexRender", {
|
|
13632
15325
|
enumerable: true,
|
|
@@ -13858,6 +15551,30 @@ exports.FormField = FormField;
|
|
|
13858
15551
|
exports.FormItem = FormItem;
|
|
13859
15552
|
exports.FormLabel = FormLabel;
|
|
13860
15553
|
exports.FormMessage = FormMessage;
|
|
15554
|
+
exports.GanttAddFeatureHelper = GanttAddFeatureHelper;
|
|
15555
|
+
exports.GanttCollapsibleSidebarGroup = GanttCollapsibleSidebarGroup;
|
|
15556
|
+
exports.GanttCollapsibleTimelineGroup = GanttCollapsibleTimelineGroup;
|
|
15557
|
+
exports.GanttColumn = GanttColumn;
|
|
15558
|
+
exports.GanttColumns = GanttColumns;
|
|
15559
|
+
exports.GanttContentHeader = GanttContentHeader;
|
|
15560
|
+
exports.GanttCreateMarkerTrigger = GanttCreateMarkerTrigger;
|
|
15561
|
+
exports.GanttFeatureDragHelper = GanttFeatureDragHelper;
|
|
15562
|
+
exports.GanttFeatureItem = GanttFeatureItem;
|
|
15563
|
+
exports.GanttFeatureItemCard = GanttFeatureItemCard;
|
|
15564
|
+
exports.GanttFeatureList = GanttFeatureList;
|
|
15565
|
+
exports.GanttFeatureListGroup = GanttFeatureListGroup;
|
|
15566
|
+
exports.GanttFeatureRow = GanttFeatureRow;
|
|
15567
|
+
exports.GanttGridLines = GanttGridLines;
|
|
15568
|
+
exports.GanttGroupSummaryBar = GanttGroupSummaryBar;
|
|
15569
|
+
exports.GanttHeader = GanttHeader;
|
|
15570
|
+
exports.GanttMarker = GanttMarker;
|
|
15571
|
+
exports.GanttProvider = GanttProvider;
|
|
15572
|
+
exports.GanttSidebar = GanttSidebar;
|
|
15573
|
+
exports.GanttSidebarGroup = GanttSidebarGroup;
|
|
15574
|
+
exports.GanttSidebarHeader = GanttSidebarHeader;
|
|
15575
|
+
exports.GanttSidebarItem = GanttSidebarItem;
|
|
15576
|
+
exports.GanttTimeline = GanttTimeline;
|
|
15577
|
+
exports.GanttToday = GanttToday;
|
|
13861
15578
|
exports.HoverCard = HoverCard;
|
|
13862
15579
|
exports.HoverCardContent = HoverCardContent;
|
|
13863
15580
|
exports.HoverCardTrigger = HoverCardTrigger;
|
|
@@ -13878,6 +15595,11 @@ exports.ItemHeader = ItemHeader;
|
|
|
13878
15595
|
exports.ItemMedia = ItemMedia;
|
|
13879
15596
|
exports.ItemSeparator = ItemSeparator;
|
|
13880
15597
|
exports.ItemTitle = ItemTitle;
|
|
15598
|
+
exports.KanbanBoard = KanbanBoard;
|
|
15599
|
+
exports.KanbanCard = KanbanCard;
|
|
15600
|
+
exports.KanbanCards = KanbanCards;
|
|
15601
|
+
exports.KanbanHeader = KanbanHeader;
|
|
15602
|
+
exports.KanbanProvider = KanbanProvider;
|
|
13881
15603
|
exports.Kbd = Kbd;
|
|
13882
15604
|
exports.KbdGroup = KbdGroup;
|
|
13883
15605
|
exports.Label = Label2;
|
|
@@ -14125,6 +15847,8 @@ exports.useEventCalendar = useEventCalendar;
|
|
|
14125
15847
|
exports.useEventsInRange = useEventsInRange;
|
|
14126
15848
|
exports.useFilteredEvents = useFilteredEvents;
|
|
14127
15849
|
exports.useFormField = useFormField;
|
|
15850
|
+
exports.useGanttDragging = useGanttDragging;
|
|
15851
|
+
exports.useGanttScrollX = useGanttScrollX;
|
|
14128
15852
|
exports.useIsMobile = useIsMobile;
|
|
14129
15853
|
exports.useSearchShortcut = useSearchShortcut;
|
|
14130
15854
|
exports.useSidebar = useSidebar;
|