@almadar/ui 6.26.0 → 6.28.0

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/avl/index.js CHANGED
@@ -34742,6 +34742,247 @@ var init_WizardNavigation = __esm({
34742
34742
  WizardNavigation.displayName = "WizardNavigation";
34743
34743
  }
34744
34744
  });
34745
+ function parseDay(value) {
34746
+ if (value === void 0 || value === null || value === "") return null;
34747
+ const d = value instanceof Date ? new Date(value.getTime()) : new Date(value);
34748
+ if (Number.isNaN(d.getTime())) return null;
34749
+ d.setHours(0, 0, 0, 0);
34750
+ return d;
34751
+ }
34752
+ function Gantt({
34753
+ tasks = [],
34754
+ links = [],
34755
+ titleField = "title",
34756
+ startField = "start",
34757
+ endField = "end",
34758
+ durationField,
34759
+ statusField = "status",
34760
+ groupField = "",
34761
+ rangeStart,
34762
+ rangeEnd,
34763
+ showToday = true,
34764
+ dayWidth = 28,
34765
+ barClickEvent,
34766
+ className,
34767
+ isLoading = false,
34768
+ error = null
34769
+ }) {
34770
+ const { t } = useTranslate();
34771
+ const placed = useMemo(() => {
34772
+ const rows = Array.isArray(tasks) ? tasks : tasks ? [tasks] : [];
34773
+ const out = [];
34774
+ rows.forEach((row, idx) => {
34775
+ const start = parseDay(getNestedValue(row, startField));
34776
+ if (!start) return;
34777
+ let end = parseDay(getNestedValue(row, endField));
34778
+ if (!end && durationField) {
34779
+ const days2 = Number(getNestedValue(row, durationField));
34780
+ if (Number.isFinite(days2) && days2 > 0) {
34781
+ end = new Date(start.getTime() + days2 * DAY_MS);
34782
+ }
34783
+ }
34784
+ if (!end || end.getTime() < start.getTime()) end = new Date(start.getTime() + DAY_MS);
34785
+ out.push({
34786
+ row,
34787
+ id: String(row.id ?? idx),
34788
+ label: String(getNestedValue(row, titleField) ?? ""),
34789
+ status: String(getNestedValue(row, statusField) ?? "").toLowerCase(),
34790
+ group: groupField ? String(getNestedValue(row, groupField) ?? "") : "",
34791
+ start,
34792
+ end
34793
+ });
34794
+ });
34795
+ return out;
34796
+ }, [tasks, titleField, startField, endField, durationField, statusField, groupField]);
34797
+ const [axisStart, axisEnd] = useMemo(() => {
34798
+ const lo = parseDay(rangeStart) ?? (placed.length ? new Date(Math.min(...placed.map((p) => p.start.getTime())) - 2 * DAY_MS) : new Date((/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0)));
34799
+ const hi = parseDay(rangeEnd) ?? (placed.length ? new Date(Math.max(...placed.map((p) => p.end.getTime())) + 2 * DAY_MS) : new Date(lo.getTime() + 30 * DAY_MS));
34800
+ return hi.getTime() > lo.getTime() ? [lo, hi] : [lo, new Date(lo.getTime() + DAY_MS)];
34801
+ }, [rangeStart, rangeEnd, placed]);
34802
+ const totalDays = Math.round((axisEnd.getTime() - axisStart.getTime()) / DAY_MS);
34803
+ const chartWidth = totalDays * dayWidth;
34804
+ const dayOffset = (d) => (d.getTime() - axisStart.getTime()) / DAY_MS * dayWidth;
34805
+ const displayItems = useMemo(() => {
34806
+ if (!groupField) return placed.map((task) => ({ kind: "task", task }));
34807
+ const items = [];
34808
+ const seen = /* @__PURE__ */ new Set();
34809
+ for (const task of placed) {
34810
+ if (!seen.has(task.group)) {
34811
+ seen.add(task.group);
34812
+ items.push({ kind: "group", label: task.group || "\u2014" });
34813
+ }
34814
+ items.push({ kind: "task", task });
34815
+ }
34816
+ return items;
34817
+ }, [placed, groupField]);
34818
+ const barGeometry = useMemo(() => {
34819
+ const offset = (d) => (d.getTime() - axisStart.getTime()) / DAY_MS * dayWidth;
34820
+ const map = /* @__PURE__ */ new Map();
34821
+ displayItems.forEach((item, idx) => {
34822
+ if (item.kind !== "task") return;
34823
+ const x0 = offset(item.task.start);
34824
+ const x1 = Math.max(offset(item.task.end), x0 + dayWidth / 2);
34825
+ map.set(item.task.id, { x0, x1, y: HEADER_HEIGHT + idx * ROW_HEIGHT + ROW_HEIGHT / 2 });
34826
+ });
34827
+ return map;
34828
+ }, [displayItems, axisStart, dayWidth]);
34829
+ const days = useMemo(() => {
34830
+ const out = [];
34831
+ for (let i = 0; i < totalDays; i++) out.push(new Date(axisStart.getTime() + i * DAY_MS));
34832
+ return out;
34833
+ }, [axisStart, totalDays]);
34834
+ const todayOffset = useMemo(() => {
34835
+ const today = parseDay(/* @__PURE__ */ new Date());
34836
+ if (!today || today < axisStart || today > axisEnd) return null;
34837
+ return (today.getTime() - axisStart.getTime()) / DAY_MS * dayWidth;
34838
+ }, [axisStart, axisEnd, dayWidth]);
34839
+ if (isLoading) {
34840
+ return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
34841
+ }
34842
+ if (error) {
34843
+ return /* @__PURE__ */ jsx(Box, { className: cn("p-4", className), children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
34844
+ }
34845
+ if (placed.length === 0) {
34846
+ return /* @__PURE__ */ jsx(
34847
+ EmptyState,
34848
+ {
34849
+ title: t("empty.noData"),
34850
+ className
34851
+ }
34852
+ );
34853
+ }
34854
+ return /* @__PURE__ */ jsx(
34855
+ Box,
34856
+ {
34857
+ className: cn("w-full overflow-auto rounded-md border border-border bg-card", className),
34858
+ children: /* @__PURE__ */ jsxs(Box, { className: "relative", style: { width: LABEL_WIDTH + chartWidth, minWidth: "100%" }, children: [
34859
+ /* @__PURE__ */ jsxs(HStack, { gap: "none", className: "sticky top-0 z-20 bg-card border-b border-border", style: { height: HEADER_HEIGHT }, children: [
34860
+ /* @__PURE__ */ jsx(Box, { className: "sticky left-0 z-10 shrink-0 bg-card border-r border-border", style: { width: LABEL_WIDTH, height: HEADER_HEIGHT } }),
34861
+ /* @__PURE__ */ jsx(Box, { className: "relative", style: { width: chartWidth, height: HEADER_HEIGHT }, children: days.map((day, i) => /* @__PURE__ */ jsx(
34862
+ Box,
34863
+ {
34864
+ className: cn(
34865
+ "absolute top-0 bottom-0 border-l border-border/50 flex items-end justify-center pb-1",
34866
+ day.getDay() === 0 || day.getDay() === 6 ? "bg-muted/40" : void 0
34867
+ ),
34868
+ style: { left: i * dayWidth, width: dayWidth },
34869
+ children: dayWidth >= 20 && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: day.getDate() })
34870
+ },
34871
+ i
34872
+ )) })
34873
+ ] }),
34874
+ /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "relative", children: [
34875
+ displayItems.map(
34876
+ (item, idx) => item.kind === "group" ? /* @__PURE__ */ jsxs(
34877
+ HStack,
34878
+ {
34879
+ gap: "none",
34880
+ className: "border-b border-border bg-muted/30",
34881
+ style: { height: ROW_HEIGHT },
34882
+ children: [
34883
+ /* @__PURE__ */ jsx(Box, { className: "sticky left-0 z-10 shrink-0 bg-muted/30 px-3 flex items-center border-r border-border", style: { width: LABEL_WIDTH, height: ROW_HEIGHT }, children: /* @__PURE__ */ jsx(Typography, { variant: "caption", weight: "semibold", children: item.label }) }),
34884
+ /* @__PURE__ */ jsx(Box, { style: { width: chartWidth, height: ROW_HEIGHT } })
34885
+ ]
34886
+ },
34887
+ `g-${idx}`
34888
+ ) : /* @__PURE__ */ jsxs(
34889
+ HStack,
34890
+ {
34891
+ gap: "none",
34892
+ className: "border-b border-border/50",
34893
+ style: { height: ROW_HEIGHT },
34894
+ children: [
34895
+ /* @__PURE__ */ jsx(Box, { className: "sticky left-0 z-10 shrink-0 bg-card px-3 flex items-center border-r border-border", style: { width: LABEL_WIDTH, height: ROW_HEIGHT }, children: /* @__PURE__ */ jsx(Typography, { variant: "small", className: "truncate", children: item.task.label }) }),
34896
+ /* @__PURE__ */ jsx(Box, { className: "relative", style: { width: chartWidth, height: ROW_HEIGHT }, children: /* @__PURE__ */ jsx(
34897
+ Box,
34898
+ {
34899
+ className: cn(
34900
+ "absolute top-1/2 -translate-y-1/2 h-4 rounded-sm transition-colors",
34901
+ STATUS_BAR[item.task.status] ?? "bg-primary/80 hover:bg-primary",
34902
+ barClickEvent ? "cursor-pointer" : void 0
34903
+ ),
34904
+ style: {
34905
+ left: dayOffset(item.task.start),
34906
+ width: Math.max(dayOffset(item.task.end) - dayOffset(item.task.start), dayWidth / 2)
34907
+ },
34908
+ action: barClickEvent,
34909
+ actionPayload: { id: item.task.id }
34910
+ }
34911
+ ) })
34912
+ ]
34913
+ },
34914
+ item.task.id
34915
+ )
34916
+ ),
34917
+ links.length > 0 && /* @__PURE__ */ jsxs(
34918
+ "svg",
34919
+ {
34920
+ className: "absolute pointer-events-none",
34921
+ style: { left: LABEL_WIDTH, top: 0 },
34922
+ width: chartWidth,
34923
+ height: HEADER_HEIGHT + displayItems.length * ROW_HEIGHT,
34924
+ children: [
34925
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("marker", { id: "gantt-arrow", markerWidth: "8", markerHeight: "8", refX: "7", refY: "4", orient: "auto", children: /* @__PURE__ */ jsx("path", { d: "M0,0 L8,4 L0,8 z", fill: "var(--muted-foreground, currentColor)" }) }) }),
34926
+ links.map((link, i) => {
34927
+ const from = barGeometry.get(link.from);
34928
+ const to = barGeometry.get(link.to);
34929
+ if (!from || !to) return null;
34930
+ const midX = from.x1 + Math.max(8, (to.x0 - from.x1) / 2);
34931
+ return /* @__PURE__ */ jsx(
34932
+ "path",
34933
+ {
34934
+ d: `M ${from.x1} ${from.y} L ${midX} ${from.y} L ${midX} ${to.y} L ${to.x0} ${to.y}`,
34935
+ fill: "none",
34936
+ stroke: "var(--muted-foreground, currentColor)",
34937
+ strokeWidth: 1.5,
34938
+ markerEnd: "url(#gantt-arrow)"
34939
+ },
34940
+ i
34941
+ );
34942
+ })
34943
+ ]
34944
+ }
34945
+ ),
34946
+ showToday && todayOffset !== null && /* @__PURE__ */ jsx(
34947
+ Box,
34948
+ {
34949
+ className: "absolute top-0 bottom-0 w-0.5 bg-error/70 pointer-events-none",
34950
+ style: { left: LABEL_WIDTH + todayOffset }
34951
+ }
34952
+ )
34953
+ ] })
34954
+ ] })
34955
+ }
34956
+ );
34957
+ }
34958
+ var DAY_MS, ROW_HEIGHT, HEADER_HEIGHT, LABEL_WIDTH, STATUS_BAR;
34959
+ var init_Gantt = __esm({
34960
+ "components/core/molecules/Gantt.tsx"() {
34961
+ "use client";
34962
+ init_cn();
34963
+ init_getNestedValue();
34964
+ init_Box();
34965
+ init_Stack();
34966
+ init_Typography();
34967
+ init_LoadingState();
34968
+ init_EmptyState();
34969
+ DAY_MS = 24 * 60 * 60 * 1e3;
34970
+ ROW_HEIGHT = 36;
34971
+ HEADER_HEIGHT = 44;
34972
+ LABEL_WIDTH = 192;
34973
+ STATUS_BAR = {
34974
+ complete: "bg-success/80 hover:bg-success",
34975
+ done: "bg-success/80 hover:bg-success",
34976
+ active: "bg-primary/80 hover:bg-primary",
34977
+ "in-progress": "bg-primary/80 hover:bg-primary",
34978
+ blocked: "bg-error/80 hover:bg-error",
34979
+ error: "bg-error/80 hover:bg-error",
34980
+ "at-risk": "bg-warning/80 hover:bg-warning",
34981
+ pending: "bg-muted-foreground/50 hover:bg-muted-foreground/70"
34982
+ };
34983
+ Gantt.displayName = "Gantt";
34984
+ }
34985
+ });
34745
34986
  var RepeatableFormSection;
34746
34987
  var init_RepeatableFormSection = __esm({
34747
34988
  "components/core/molecules/RepeatableFormSection.tsx"() {
@@ -53899,6 +54140,7 @@ var init_component_registry_generated = __esm({
53899
54140
  init_GameIcon();
53900
54141
  init_GameMenu();
53901
54142
  init_GameShell();
54143
+ init_Gantt();
53902
54144
  init_GenericAppTemplate();
53903
54145
  init_GeometricPattern();
53904
54146
  init_GradientDivider();
@@ -54178,6 +54420,7 @@ var init_component_registry_generated = __esm({
54178
54420
  "GameIcon": GameIcon,
54179
54421
  "GameMenu": GameMenu,
54180
54422
  "GameShell": GameShell,
54423
+ "Gantt": Gantt,
54181
54424
  "GenericAppTemplate": GenericAppTemplate,
54182
54425
  "GeometricPattern": GeometricPattern,
54183
54426
  "GradientDivider": GradientDivider,
@@ -60516,7 +60759,7 @@ function NavStackRefBridge({ apiRef }) {
60516
60759
  }, [api, apiRef]);
60517
60760
  return null;
60518
60761
  }
60519
- function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNavigate, onNavigateBack, onLocalFallback, localFallbackTimeoutMs, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, callsiteCaptureChildrenByTrait, serverActiveTraits, children }) {
60762
+ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNavigate, onNavigateBack, onLocalFallback, localFallbackTimeoutMs, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, callsiteCaptureChildrenByTrait, serverActiveTraits, user, children }) {
60520
60763
  const bridge = useServerBridge();
60521
60764
  const activeTraitNames = useMemo(
60522
60765
  () => new Set(traits2.map((b) => b.trait.name).filter((n) => !!n)),
@@ -60548,12 +60791,12 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
60548
60791
  void bridge.sendEvent(name, event, withActiveTraits(payload), tick, sourceTrait);
60549
60792
  continue;
60550
60793
  }
60551
- void bridge.sendEvent(name, event, withActiveTraits(payload), void 0, void 0, locallyEmitted, results, entityByTrait).then(({ effects, meta }) => {
60794
+ void bridge.sendEvent(name, event, withActiveTraits(payload), void 0, void 0, locallyEmitted, results, entityByTrait, user ?? void 0).then(({ effects, meta }) => {
60552
60795
  recordServerResponse(name, event, { ...meta, effectResults: effectResultsToTraces(meta.effectResults) });
60553
60796
  applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNamesRef.current, onNavigateBack);
60554
60797
  });
60555
60798
  }
60556
- }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits]);
60799
+ }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits, user]);
60557
60800
  const opts = orbitalNames ? { onEventProcessed, navigate: onNavigate, navigateBack: onNavigateBack, traitConfigsByName, orbitalsByTrait, embeddedTraits, callsiteCaptureChildrenByTrait, initPayload: routeParams } : { navigate: onNavigate, navigateBack: onNavigateBack, persistence, traitConfigsByName, orbitalsByTrait, embeddedTraits, callsiteCaptureChildrenByTrait, initPayload: routeParams };
60558
60801
  const { sendEvent, entityBindingSource } = useTraitStateMachine(traits2, uiSlots, opts);
60559
60802
  const initSentRef = useRef(false);
@@ -60595,7 +60838,17 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
60595
60838
  initSentRef.current = true;
60596
60839
  (async () => {
60597
60840
  for (const name of orbitalNames) {
60598
- const { effects, meta } = await bridge.sendEvent(name, "INIT", withActiveTraits({ ...routeParams ?? {} }));
60841
+ const { effects, meta } = await bridge.sendEvent(
60842
+ name,
60843
+ "INIT",
60844
+ withActiveTraits({ ...routeParams ?? {} }),
60845
+ void 0,
60846
+ void 0,
60847
+ void 0,
60848
+ void 0,
60849
+ void 0,
60850
+ user ?? void 0
60851
+ );
60599
60852
  recordServerResponse(name, "INIT", { ...meta, effectResults: effectResultsToTraces(meta.effectResults) });
60600
60853
  const effectTraces = [
60601
60854
  { type: "fetch", args: [], status: "executed" },
@@ -60616,7 +60869,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
60616
60869
  applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNamesRef.current, onNavigateBack);
60617
60870
  }
60618
60871
  })();
60619
- }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits, routeParams]);
60872
+ }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits, routeParams, user]);
60620
60873
  return /* @__PURE__ */ jsx(EntityBindingContext.Provider, { value: entityBindingSource, children });
60621
60874
  }
60622
60875
  function FitToBox({ children }) {
@@ -60643,7 +60896,7 @@ function FitToBox({ children }) {
60643
60896
  }, []);
60644
60897
  return /* @__PURE__ */ jsx("div", { ref: outerRef, className: "relative h-full w-full overflow-hidden", children: /* @__PURE__ */ jsx("div", { ref: innerRef, style: { transform: `scale(${scale})`, transformOrigin: "top left", width: "fit-content" }, children }) });
60645
60898
  }
60646
- function SchemaRunner({ schema, serverUrl, transport, getAccessToken, mockData, pageName, routeParams, onNavigate, onNavigateBack, onLocalFallback, localFallbackTimeoutMs, persistence }) {
60899
+ function SchemaRunner({ schema, serverUrl, transport, getAccessToken, mockData, pageName, routeParams, onNavigate, onNavigateBack, onLocalFallback, localFallbackTimeoutMs, persistence, user }) {
60647
60900
  const { traits: traits2, allEntities, allTraits, ir } = useResolvedSchema(schema, pageName);
60648
60901
  const allPageTraits = useMemo(() => {
60649
60902
  let base;
@@ -60808,6 +61061,7 @@ function SchemaRunner({ schema, serverUrl, transport, getAccessToken, mockData,
60808
61061
  onLocalFallback,
60809
61062
  localFallbackTimeoutMs,
60810
61063
  persistence,
61064
+ user,
60811
61065
  children: /* @__PURE__ */ jsx(OrbitalThemeProvider, { theme: activeOrbitalTheme, children: /* @__PURE__ */ jsx(Box, { className: "h-full min-h-full overflow-auto p-4", children: /* @__PURE__ */ jsx(UISlotRenderer, { includeHud: true, hudMode: "inline", includeFloating: true }) }) })
60812
61066
  }
60813
61067
  )
@@ -61014,7 +61268,8 @@ function OrbPreview({
61014
61268
  onNavigateBack: handleNavigateBack,
61015
61269
  onLocalFallback: handleLocalFallback,
61016
61270
  localFallbackTimeoutMs,
61017
- persistence
61271
+ persistence,
61272
+ user
61018
61273
  }
61019
61274
  ) }) : /* @__PURE__ */ jsx(
61020
61275
  SchemaRunner,
@@ -61030,7 +61285,8 @@ function OrbPreview({
61030
61285
  onNavigateBack: handleNavigateBack,
61031
61286
  onLocalFallback: handleLocalFallback,
61032
61287
  localFallbackTimeoutMs,
61033
- persistence
61288
+ persistence,
61289
+ user
61034
61290
  }
61035
61291
  ) }) })
61036
61292
  ]
@@ -30823,6 +30823,247 @@ var init_WizardNavigation = __esm({
30823
30823
  exports.WizardNavigation.displayName = "WizardNavigation";
30824
30824
  }
30825
30825
  });
30826
+ function parseDay(value) {
30827
+ if (value === void 0 || value === null || value === "") return null;
30828
+ const d = value instanceof Date ? new Date(value.getTime()) : new Date(value);
30829
+ if (Number.isNaN(d.getTime())) return null;
30830
+ d.setHours(0, 0, 0, 0);
30831
+ return d;
30832
+ }
30833
+ function Gantt({
30834
+ tasks = [],
30835
+ links = [],
30836
+ titleField = "title",
30837
+ startField = "start",
30838
+ endField = "end",
30839
+ durationField,
30840
+ statusField = "status",
30841
+ groupField = "",
30842
+ rangeStart,
30843
+ rangeEnd,
30844
+ showToday = true,
30845
+ dayWidth = 28,
30846
+ barClickEvent,
30847
+ className,
30848
+ isLoading = false,
30849
+ error = null
30850
+ }) {
30851
+ const { t } = hooks.useTranslate();
30852
+ const placed = React79.useMemo(() => {
30853
+ const rows2 = Array.isArray(tasks) ? tasks : tasks ? [tasks] : [];
30854
+ const out = [];
30855
+ rows2.forEach((row, idx) => {
30856
+ const start = parseDay(core.getNestedValue(row, startField));
30857
+ if (!start) return;
30858
+ let end = parseDay(core.getNestedValue(row, endField));
30859
+ if (!end && durationField) {
30860
+ const days2 = Number(core.getNestedValue(row, durationField));
30861
+ if (Number.isFinite(days2) && days2 > 0) {
30862
+ end = new Date(start.getTime() + days2 * DAY_MS);
30863
+ }
30864
+ }
30865
+ if (!end || end.getTime() < start.getTime()) end = new Date(start.getTime() + DAY_MS);
30866
+ out.push({
30867
+ row,
30868
+ id: String(row.id ?? idx),
30869
+ label: String(core.getNestedValue(row, titleField) ?? ""),
30870
+ status: String(core.getNestedValue(row, statusField) ?? "").toLowerCase(),
30871
+ group: groupField ? String(core.getNestedValue(row, groupField) ?? "") : "",
30872
+ start,
30873
+ end
30874
+ });
30875
+ });
30876
+ return out;
30877
+ }, [tasks, titleField, startField, endField, durationField, statusField, groupField]);
30878
+ const [axisStart, axisEnd] = React79.useMemo(() => {
30879
+ const lo = parseDay(rangeStart) ?? (placed.length ? new Date(Math.min(...placed.map((p) => p.start.getTime())) - 2 * DAY_MS) : new Date((/* @__PURE__ */ new Date()).setHours(0, 0, 0, 0)));
30880
+ const hi = parseDay(rangeEnd) ?? (placed.length ? new Date(Math.max(...placed.map((p) => p.end.getTime())) + 2 * DAY_MS) : new Date(lo.getTime() + 30 * DAY_MS));
30881
+ return hi.getTime() > lo.getTime() ? [lo, hi] : [lo, new Date(lo.getTime() + DAY_MS)];
30882
+ }, [rangeStart, rangeEnd, placed]);
30883
+ const totalDays = Math.round((axisEnd.getTime() - axisStart.getTime()) / DAY_MS);
30884
+ const chartWidth = totalDays * dayWidth;
30885
+ const dayOffset = (d) => (d.getTime() - axisStart.getTime()) / DAY_MS * dayWidth;
30886
+ const displayItems = React79.useMemo(() => {
30887
+ if (!groupField) return placed.map((task) => ({ kind: "task", task }));
30888
+ const items = [];
30889
+ const seen = /* @__PURE__ */ new Set();
30890
+ for (const task of placed) {
30891
+ if (!seen.has(task.group)) {
30892
+ seen.add(task.group);
30893
+ items.push({ kind: "group", label: task.group || "\u2014" });
30894
+ }
30895
+ items.push({ kind: "task", task });
30896
+ }
30897
+ return items;
30898
+ }, [placed, groupField]);
30899
+ const barGeometry = React79.useMemo(() => {
30900
+ const offset = (d) => (d.getTime() - axisStart.getTime()) / DAY_MS * dayWidth;
30901
+ const map = /* @__PURE__ */ new Map();
30902
+ displayItems.forEach((item, idx) => {
30903
+ if (item.kind !== "task") return;
30904
+ const x0 = offset(item.task.start);
30905
+ const x1 = Math.max(offset(item.task.end), x0 + dayWidth / 2);
30906
+ map.set(item.task.id, { x0, x1, y: HEADER_HEIGHT + idx * ROW_HEIGHT + ROW_HEIGHT / 2 });
30907
+ });
30908
+ return map;
30909
+ }, [displayItems, axisStart, dayWidth]);
30910
+ const days = React79.useMemo(() => {
30911
+ const out = [];
30912
+ for (let i = 0; i < totalDays; i++) out.push(new Date(axisStart.getTime() + i * DAY_MS));
30913
+ return out;
30914
+ }, [axisStart, totalDays]);
30915
+ const todayOffset = React79.useMemo(() => {
30916
+ const today = parseDay(/* @__PURE__ */ new Date());
30917
+ if (!today || today < axisStart || today > axisEnd) return null;
30918
+ return (today.getTime() - axisStart.getTime()) / DAY_MS * dayWidth;
30919
+ }, [axisStart, axisEnd, dayWidth]);
30920
+ if (isLoading) {
30921
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.LoadingState, { message: t("common.loading"), className });
30922
+ }
30923
+ if (error) {
30924
+ return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("p-4", className), children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", color: "error", children: error.message }) });
30925
+ }
30926
+ if (placed.length === 0) {
30927
+ return /* @__PURE__ */ jsxRuntime.jsx(
30928
+ exports.EmptyState,
30929
+ {
30930
+ title: t("empty.noData"),
30931
+ className
30932
+ }
30933
+ );
30934
+ }
30935
+ return /* @__PURE__ */ jsxRuntime.jsx(
30936
+ exports.Box,
30937
+ {
30938
+ className: cn("w-full overflow-auto rounded-md border border-border bg-card", className),
30939
+ children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "relative", style: { width: LABEL_WIDTH + chartWidth, minWidth: "100%" }, children: [
30940
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "none", className: "sticky top-0 z-20 bg-card border-b border-border", style: { height: HEADER_HEIGHT }, children: [
30941
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "sticky left-0 z-10 shrink-0 bg-card border-r border-border", style: { width: LABEL_WIDTH, height: HEADER_HEIGHT } }),
30942
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "relative", style: { width: chartWidth, height: HEADER_HEIGHT }, children: days.map((day, i) => /* @__PURE__ */ jsxRuntime.jsx(
30943
+ exports.Box,
30944
+ {
30945
+ className: cn(
30946
+ "absolute top-0 bottom-0 border-l border-border/50 flex items-end justify-center pb-1",
30947
+ day.getDay() === 0 || day.getDay() === 6 ? "bg-muted/40" : void 0
30948
+ ),
30949
+ style: { left: i * dayWidth, width: dayWidth },
30950
+ children: dayWidth >= 20 && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: day.getDate() })
30951
+ },
30952
+ i
30953
+ )) })
30954
+ ] }),
30955
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "relative", children: [
30956
+ displayItems.map(
30957
+ (item, idx) => item.kind === "group" ? /* @__PURE__ */ jsxRuntime.jsxs(
30958
+ exports.HStack,
30959
+ {
30960
+ gap: "none",
30961
+ className: "border-b border-border bg-muted/30",
30962
+ style: { height: ROW_HEIGHT },
30963
+ children: [
30964
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "sticky left-0 z-10 shrink-0 bg-muted/30 px-3 flex items-center border-r border-border", style: { width: LABEL_WIDTH, height: ROW_HEIGHT }, children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", weight: "semibold", children: item.label }) }),
30965
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { style: { width: chartWidth, height: ROW_HEIGHT } })
30966
+ ]
30967
+ },
30968
+ `g-${idx}`
30969
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
30970
+ exports.HStack,
30971
+ {
30972
+ gap: "none",
30973
+ className: "border-b border-border/50",
30974
+ style: { height: ROW_HEIGHT },
30975
+ children: [
30976
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "sticky left-0 z-10 shrink-0 bg-card px-3 flex items-center border-r border-border", style: { width: LABEL_WIDTH, height: ROW_HEIGHT }, children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "truncate", children: item.task.label }) }),
30977
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "relative", style: { width: chartWidth, height: ROW_HEIGHT }, children: /* @__PURE__ */ jsxRuntime.jsx(
30978
+ exports.Box,
30979
+ {
30980
+ className: cn(
30981
+ "absolute top-1/2 -translate-y-1/2 h-4 rounded-sm transition-colors",
30982
+ STATUS_BAR[item.task.status] ?? "bg-primary/80 hover:bg-primary",
30983
+ barClickEvent ? "cursor-pointer" : void 0
30984
+ ),
30985
+ style: {
30986
+ left: dayOffset(item.task.start),
30987
+ width: Math.max(dayOffset(item.task.end) - dayOffset(item.task.start), dayWidth / 2)
30988
+ },
30989
+ action: barClickEvent,
30990
+ actionPayload: { id: item.task.id }
30991
+ }
30992
+ ) })
30993
+ ]
30994
+ },
30995
+ item.task.id
30996
+ )
30997
+ ),
30998
+ links.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
30999
+ "svg",
31000
+ {
31001
+ className: "absolute pointer-events-none",
31002
+ style: { left: LABEL_WIDTH, top: 0 },
31003
+ width: chartWidth,
31004
+ height: HEADER_HEIGHT + displayItems.length * ROW_HEIGHT,
31005
+ children: [
31006
+ /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx("marker", { id: "gantt-arrow", markerWidth: "8", markerHeight: "8", refX: "7", refY: "4", orient: "auto", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M0,0 L8,4 L0,8 z", fill: "var(--muted-foreground, currentColor)" }) }) }),
31007
+ links.map((link, i) => {
31008
+ const from = barGeometry.get(link.from);
31009
+ const to = barGeometry.get(link.to);
31010
+ if (!from || !to) return null;
31011
+ const midX = from.x1 + Math.max(8, (to.x0 - from.x1) / 2);
31012
+ return /* @__PURE__ */ jsxRuntime.jsx(
31013
+ "path",
31014
+ {
31015
+ d: `M ${from.x1} ${from.y} L ${midX} ${from.y} L ${midX} ${to.y} L ${to.x0} ${to.y}`,
31016
+ fill: "none",
31017
+ stroke: "var(--muted-foreground, currentColor)",
31018
+ strokeWidth: 1.5,
31019
+ markerEnd: "url(#gantt-arrow)"
31020
+ },
31021
+ i
31022
+ );
31023
+ })
31024
+ ]
31025
+ }
31026
+ ),
31027
+ showToday && todayOffset !== null && /* @__PURE__ */ jsxRuntime.jsx(
31028
+ exports.Box,
31029
+ {
31030
+ className: "absolute top-0 bottom-0 w-0.5 bg-error/70 pointer-events-none",
31031
+ style: { left: LABEL_WIDTH + todayOffset }
31032
+ }
31033
+ )
31034
+ ] })
31035
+ ] })
31036
+ }
31037
+ );
31038
+ }
31039
+ var DAY_MS, ROW_HEIGHT, HEADER_HEIGHT, LABEL_WIDTH, STATUS_BAR;
31040
+ var init_Gantt = __esm({
31041
+ "components/core/molecules/Gantt.tsx"() {
31042
+ "use client";
31043
+ init_cn();
31044
+ init_getNestedValue();
31045
+ init_Box();
31046
+ init_Stack();
31047
+ init_Typography();
31048
+ init_LoadingState();
31049
+ init_EmptyState();
31050
+ DAY_MS = 24 * 60 * 60 * 1e3;
31051
+ ROW_HEIGHT = 36;
31052
+ HEADER_HEIGHT = 44;
31053
+ LABEL_WIDTH = 192;
31054
+ STATUS_BAR = {
31055
+ complete: "bg-success/80 hover:bg-success",
31056
+ done: "bg-success/80 hover:bg-success",
31057
+ active: "bg-primary/80 hover:bg-primary",
31058
+ "in-progress": "bg-primary/80 hover:bg-primary",
31059
+ blocked: "bg-error/80 hover:bg-error",
31060
+ error: "bg-error/80 hover:bg-error",
31061
+ "at-risk": "bg-warning/80 hover:bg-warning",
31062
+ pending: "bg-muted-foreground/50 hover:bg-muted-foreground/70"
31063
+ };
31064
+ Gantt.displayName = "Gantt";
31065
+ }
31066
+ });
30826
31067
  exports.RepeatableFormSection = void 0;
30827
31068
  var init_RepeatableFormSection = __esm({
30828
31069
  "components/core/molecules/RepeatableFormSection.tsx"() {
@@ -45825,6 +46066,7 @@ var init_molecules2 = __esm({
45825
46066
  init_QuizBlock();
45826
46067
  init_ScaledDiagram();
45827
46068
  init_CalendarGrid();
46069
+ init_Gantt();
45828
46070
  init_RepeatableFormSection();
45829
46071
  init_ViolationAlert();
45830
46072
  init_FormSectionHeader();
@@ -53096,6 +53338,7 @@ var init_component_registry_generated = __esm({
53096
53338
  init_GameIcon();
53097
53339
  init_GameMenu();
53098
53340
  init_GameShell();
53341
+ init_Gantt();
53099
53342
  init_GenericAppTemplate();
53100
53343
  init_GeometricPattern();
53101
53344
  init_GradientDivider();
@@ -53375,6 +53618,7 @@ var init_component_registry_generated = __esm({
53375
53618
  "GameIcon": GameIcon,
53376
53619
  "GameMenu": GameMenu,
53377
53620
  "GameShell": exports.GameShell,
53621
+ "Gantt": Gantt,
53378
53622
  "GenericAppTemplate": exports.GenericAppTemplate,
53379
53623
  "GeometricPattern": exports.GeometricPattern,
53380
53624
  "GradientDivider": exports.GradientDivider,
@@ -56930,7 +57174,7 @@ var I18nContext = React79.createContext({
56930
57174
  });
56931
57175
  I18nContext.displayName = "I18nContext";
56932
57176
  var I18nProvider = I18nContext.Provider;
56933
- function useTranslate117() {
57177
+ function useTranslate118() {
56934
57178
  return React79.useContext(I18nContext);
56935
57179
  }
56936
57180
  function createTranslate(messages) {
@@ -57180,6 +57424,7 @@ exports.GameAudioToggle = GameAudioToggle;
57180
57424
  exports.GameHud = GameHud;
57181
57425
  exports.GameIcon = GameIcon;
57182
57426
  exports.GameMenu = GameMenu;
57427
+ exports.Gantt = Gantt;
57183
57428
  exports.HealthBar = HealthBar;
57184
57429
  exports.I18nProvider = I18nProvider;
57185
57430
  exports.LearningScene3D = LearningScene3D;
@@ -57303,7 +57548,7 @@ exports.useSharedEntityStoreContext = useSharedEntityStoreContext;
57303
57548
  exports.useSwipeGesture = useSwipeGesture;
57304
57549
  exports.useTapReveal = useTapReveal;
57305
57550
  exports.useTraitListens = useTraitListens;
57306
- exports.useTranslate = useTranslate117;
57551
+ exports.useTranslate = useTranslate118;
57307
57552
  exports.useUIEvents = useUIEvents;
57308
57553
  exports.useUISlotManager = useUISlotManager;
57309
57554
  exports.useUnitSpriteAtlas = useUnitSpriteAtlas;