@almadar/ui 6.25.0 → 6.27.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.
@@ -31971,6 +31971,247 @@ var init_WizardNavigation = __esm({
31971
31971
  WizardNavigation.displayName = "WizardNavigation";
31972
31972
  }
31973
31973
  });
31974
+ function parseDay(value) {
31975
+ if (value === void 0 || value === null || value === "") return null;
31976
+ const d = value instanceof Date ? new Date(value.getTime()) : new Date(value);
31977
+ if (Number.isNaN(d.getTime())) return null;
31978
+ d.setHours(0, 0, 0, 0);
31979
+ return d;
31980
+ }
31981
+ function Gantt({
31982
+ tasks = [],
31983
+ links = [],
31984
+ titleField = "title",
31985
+ startField = "start",
31986
+ endField = "end",
31987
+ durationField,
31988
+ statusField = "status",
31989
+ groupField = "",
31990
+ rangeStart,
31991
+ rangeEnd,
31992
+ showToday = true,
31993
+ dayWidth = 28,
31994
+ barClickEvent,
31995
+ className,
31996
+ isLoading = false,
31997
+ error = null
31998
+ }) {
31999
+ const { t } = useTranslate();
32000
+ const placed = useMemo(() => {
32001
+ const rows = Array.isArray(tasks) ? tasks : tasks ? [tasks] : [];
32002
+ const out = [];
32003
+ rows.forEach((row, idx) => {
32004
+ const start = parseDay(getNestedValue(row, startField));
32005
+ if (!start) return;
32006
+ let end = parseDay(getNestedValue(row, endField));
32007
+ if (!end && durationField) {
32008
+ const days2 = Number(getNestedValue(row, durationField));
32009
+ if (Number.isFinite(days2) && days2 > 0) {
32010
+ end = new Date(start.getTime() + days2 * DAY_MS);
32011
+ }
32012
+ }
32013
+ if (!end || end.getTime() < start.getTime()) end = new Date(start.getTime() + DAY_MS);
32014
+ out.push({
32015
+ row,
32016
+ id: String(row.id ?? idx),
32017
+ label: String(getNestedValue(row, titleField) ?? ""),
32018
+ status: String(getNestedValue(row, statusField) ?? "").toLowerCase(),
32019
+ group: groupField ? String(getNestedValue(row, groupField) ?? "") : "",
32020
+ start,
32021
+ end
32022
+ });
32023
+ });
32024
+ return out;
32025
+ }, [tasks, titleField, startField, endField, durationField, statusField, groupField]);
32026
+ const [axisStart, axisEnd] = useMemo(() => {
32027
+ 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)));
32028
+ 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));
32029
+ return hi.getTime() > lo.getTime() ? [lo, hi] : [lo, new Date(lo.getTime() + DAY_MS)];
32030
+ }, [rangeStart, rangeEnd, placed]);
32031
+ const totalDays = Math.round((axisEnd.getTime() - axisStart.getTime()) / DAY_MS);
32032
+ const chartWidth = totalDays * dayWidth;
32033
+ const dayOffset = (d) => (d.getTime() - axisStart.getTime()) / DAY_MS * dayWidth;
32034
+ const displayItems = useMemo(() => {
32035
+ if (!groupField) return placed.map((task) => ({ kind: "task", task }));
32036
+ const items = [];
32037
+ const seen = /* @__PURE__ */ new Set();
32038
+ for (const task of placed) {
32039
+ if (!seen.has(task.group)) {
32040
+ seen.add(task.group);
32041
+ items.push({ kind: "group", label: task.group || "\u2014" });
32042
+ }
32043
+ items.push({ kind: "task", task });
32044
+ }
32045
+ return items;
32046
+ }, [placed, groupField]);
32047
+ const barGeometry = useMemo(() => {
32048
+ const offset = (d) => (d.getTime() - axisStart.getTime()) / DAY_MS * dayWidth;
32049
+ const map = /* @__PURE__ */ new Map();
32050
+ displayItems.forEach((item, idx) => {
32051
+ if (item.kind !== "task") return;
32052
+ const x0 = offset(item.task.start);
32053
+ const x1 = Math.max(offset(item.task.end), x0 + dayWidth / 2);
32054
+ map.set(item.task.id, { x0, x1, y: HEADER_HEIGHT + idx * ROW_HEIGHT + ROW_HEIGHT / 2 });
32055
+ });
32056
+ return map;
32057
+ }, [displayItems, axisStart, dayWidth]);
32058
+ const days = useMemo(() => {
32059
+ const out = [];
32060
+ for (let i = 0; i < totalDays; i++) out.push(new Date(axisStart.getTime() + i * DAY_MS));
32061
+ return out;
32062
+ }, [axisStart, totalDays]);
32063
+ const todayOffset = useMemo(() => {
32064
+ const today = parseDay(/* @__PURE__ */ new Date());
32065
+ if (!today || today < axisStart || today > axisEnd) return null;
32066
+ return (today.getTime() - axisStart.getTime()) / DAY_MS * dayWidth;
32067
+ }, [axisStart, axisEnd, dayWidth]);
32068
+ if (isLoading) {
32069
+ return /* @__PURE__ */ jsx(LoadingState, { message: t("common.loading"), className });
32070
+ }
32071
+ if (error) {
32072
+ return /* @__PURE__ */ jsx(Box, { className: cn("p-4", className), children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
32073
+ }
32074
+ if (placed.length === 0) {
32075
+ return /* @__PURE__ */ jsx(
32076
+ EmptyState,
32077
+ {
32078
+ title: t("empty.noData"),
32079
+ className
32080
+ }
32081
+ );
32082
+ }
32083
+ return /* @__PURE__ */ jsx(
32084
+ Box,
32085
+ {
32086
+ className: cn("w-full overflow-auto rounded-md border border-border bg-card", className),
32087
+ children: /* @__PURE__ */ jsxs(Box, { className: "relative", style: { width: LABEL_WIDTH + chartWidth, minWidth: "100%" }, children: [
32088
+ /* @__PURE__ */ jsxs(HStack, { gap: "none", className: "sticky top-0 z-20 bg-card border-b border-border", style: { height: HEADER_HEIGHT }, children: [
32089
+ /* @__PURE__ */ jsx(Box, { className: "sticky left-0 z-10 shrink-0 bg-card border-r border-border", style: { width: LABEL_WIDTH, height: HEADER_HEIGHT } }),
32090
+ /* @__PURE__ */ jsx(Box, { className: "relative", style: { width: chartWidth, height: HEADER_HEIGHT }, children: days.map((day, i) => /* @__PURE__ */ jsx(
32091
+ Box,
32092
+ {
32093
+ className: cn(
32094
+ "absolute top-0 bottom-0 border-l border-border/50 flex items-end justify-center pb-1",
32095
+ day.getDay() === 0 || day.getDay() === 6 ? "bg-muted/40" : void 0
32096
+ ),
32097
+ style: { left: i * dayWidth, width: dayWidth },
32098
+ children: dayWidth >= 20 && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: day.getDate() })
32099
+ },
32100
+ i
32101
+ )) })
32102
+ ] }),
32103
+ /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "relative", children: [
32104
+ displayItems.map(
32105
+ (item, idx) => item.kind === "group" ? /* @__PURE__ */ jsxs(
32106
+ HStack,
32107
+ {
32108
+ gap: "none",
32109
+ className: "border-b border-border bg-muted/30",
32110
+ style: { height: ROW_HEIGHT },
32111
+ children: [
32112
+ /* @__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 }) }),
32113
+ /* @__PURE__ */ jsx(Box, { style: { width: chartWidth, height: ROW_HEIGHT } })
32114
+ ]
32115
+ },
32116
+ `g-${idx}`
32117
+ ) : /* @__PURE__ */ jsxs(
32118
+ HStack,
32119
+ {
32120
+ gap: "none",
32121
+ className: "border-b border-border/50",
32122
+ style: { height: ROW_HEIGHT },
32123
+ children: [
32124
+ /* @__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 }) }),
32125
+ /* @__PURE__ */ jsx(Box, { className: "relative", style: { width: chartWidth, height: ROW_HEIGHT }, children: /* @__PURE__ */ jsx(
32126
+ Box,
32127
+ {
32128
+ className: cn(
32129
+ "absolute top-1/2 -translate-y-1/2 h-4 rounded-sm transition-colors",
32130
+ STATUS_BAR[item.task.status] ?? "bg-primary/80 hover:bg-primary",
32131
+ barClickEvent ? "cursor-pointer" : void 0
32132
+ ),
32133
+ style: {
32134
+ left: dayOffset(item.task.start),
32135
+ width: Math.max(dayOffset(item.task.end) - dayOffset(item.task.start), dayWidth / 2)
32136
+ },
32137
+ action: barClickEvent,
32138
+ actionPayload: { id: item.task.id }
32139
+ }
32140
+ ) })
32141
+ ]
32142
+ },
32143
+ item.task.id
32144
+ )
32145
+ ),
32146
+ links.length > 0 && /* @__PURE__ */ jsxs(
32147
+ "svg",
32148
+ {
32149
+ className: "absolute pointer-events-none",
32150
+ style: { left: LABEL_WIDTH, top: 0 },
32151
+ width: chartWidth,
32152
+ height: HEADER_HEIGHT + displayItems.length * ROW_HEIGHT,
32153
+ children: [
32154
+ /* @__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)" }) }) }),
32155
+ links.map((link, i) => {
32156
+ const from = barGeometry.get(link.from);
32157
+ const to = barGeometry.get(link.to);
32158
+ if (!from || !to) return null;
32159
+ const midX = from.x1 + Math.max(8, (to.x0 - from.x1) / 2);
32160
+ return /* @__PURE__ */ jsx(
32161
+ "path",
32162
+ {
32163
+ d: `M ${from.x1} ${from.y} L ${midX} ${from.y} L ${midX} ${to.y} L ${to.x0} ${to.y}`,
32164
+ fill: "none",
32165
+ stroke: "var(--muted-foreground, currentColor)",
32166
+ strokeWidth: 1.5,
32167
+ markerEnd: "url(#gantt-arrow)"
32168
+ },
32169
+ i
32170
+ );
32171
+ })
32172
+ ]
32173
+ }
32174
+ ),
32175
+ showToday && todayOffset !== null && /* @__PURE__ */ jsx(
32176
+ Box,
32177
+ {
32178
+ className: "absolute top-0 bottom-0 w-0.5 bg-error/70 pointer-events-none",
32179
+ style: { left: LABEL_WIDTH + todayOffset }
32180
+ }
32181
+ )
32182
+ ] })
32183
+ ] })
32184
+ }
32185
+ );
32186
+ }
32187
+ var DAY_MS, ROW_HEIGHT, HEADER_HEIGHT, LABEL_WIDTH, STATUS_BAR;
32188
+ var init_Gantt = __esm({
32189
+ "components/core/molecules/Gantt.tsx"() {
32190
+ "use client";
32191
+ init_cn();
32192
+ init_getNestedValue();
32193
+ init_Box();
32194
+ init_Stack();
32195
+ init_Typography();
32196
+ init_LoadingState();
32197
+ init_EmptyState();
32198
+ DAY_MS = 24 * 60 * 60 * 1e3;
32199
+ ROW_HEIGHT = 36;
32200
+ HEADER_HEIGHT = 44;
32201
+ LABEL_WIDTH = 192;
32202
+ STATUS_BAR = {
32203
+ complete: "bg-success/80 hover:bg-success",
32204
+ done: "bg-success/80 hover:bg-success",
32205
+ active: "bg-primary/80 hover:bg-primary",
32206
+ "in-progress": "bg-primary/80 hover:bg-primary",
32207
+ blocked: "bg-error/80 hover:bg-error",
32208
+ error: "bg-error/80 hover:bg-error",
32209
+ "at-risk": "bg-warning/80 hover:bg-warning",
32210
+ pending: "bg-muted-foreground/50 hover:bg-muted-foreground/70"
32211
+ };
32212
+ Gantt.displayName = "Gantt";
32213
+ }
32214
+ });
31974
32215
  var RepeatableFormSection;
31975
32216
  var init_RepeatableFormSection = __esm({
31976
32217
  "components/core/molecules/RepeatableFormSection.tsx"() {
@@ -51399,6 +51640,7 @@ var init_component_registry_generated = __esm({
51399
51640
  init_GameIcon();
51400
51641
  init_GameMenu();
51401
51642
  init_GameShell();
51643
+ init_Gantt();
51402
51644
  init_GenericAppTemplate();
51403
51645
  init_GeometricPattern();
51404
51646
  init_GradientDivider();
@@ -51678,6 +51920,7 @@ var init_component_registry_generated = __esm({
51678
51920
  "GameIcon": GameIcon,
51679
51921
  "GameMenu": GameMenu,
51680
51922
  "GameShell": GameShell,
51923
+ "Gantt": Gantt,
51681
51924
  "GenericAppTemplate": GenericAppTemplate,
51682
51925
  "GeometricPattern": GeometricPattern,
51683
51926
  "GradientDivider": GradientDivider,
@@ -54751,6 +54994,10 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
54751
54994
  () => new Set(traits2.map((b) => b.trait.name).filter((n) => !!n)),
54752
54995
  [traits2]
54753
54996
  );
54997
+ const activeTraitNamesRef = useRef(activeTraitNames);
54998
+ useEffect(() => {
54999
+ activeTraitNamesRef.current = activeTraitNames;
55000
+ }, [activeTraitNames]);
54754
55001
  const withActiveTraits = useCallback(
54755
55002
  (payload) => {
54756
55003
  if (!serverActiveTraits || serverActiveTraits.size === 0) return payload;
@@ -54775,7 +55022,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
54775
55022
  }
54776
55023
  void bridge.sendEvent(name, event, withActiveTraits(payload), void 0, void 0, locallyEmitted, results, entityByTrait).then(({ effects, meta }) => {
54777
55024
  recordServerResponse(name, event, { ...meta, effectResults: effectResultsToTraces(meta.effectResults) });
54778
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
55025
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNamesRef.current, onNavigateBack);
54779
55026
  });
54780
55027
  }
54781
55028
  }, [bridge.connected, bridge.sendEvent, orbitalNames, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits]);
@@ -54838,7 +55085,7 @@ function TraitInitializer({ traits: traits2, routeParams, orbitalNames, onNaviga
54838
55085
  effects: effectTraces,
54839
55086
  timestamp: Date.now()
54840
55087
  });
54841
- applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNames, onNavigateBack);
55088
+ applyServerEffects(effects, uiSlots, onNavigate, embeddedTraits, activeTraitNamesRef.current, onNavigateBack);
54842
55089
  }
54843
55090
  })();
54844
55091
  }, [bridge.connected, orbitalNames, bridge.sendEvent, uiSlots, onNavigate, onNavigateBack, embeddedTraits, activeTraitNames, withActiveTraits, routeParams]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/ui",
3
- "version": "6.25.0",
3
+ "version": "6.27.0",
4
4
  "description": "React UI components, hooks, and providers for Almadar",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -118,11 +118,11 @@
118
118
  "access": "public"
119
119
  },
120
120
  "dependencies": {
121
- "@almadar/core": "^10.92.0",
122
- "@almadar/evaluator": "^2.45.0",
121
+ "@almadar/core": "^10.93.0",
122
+ "@almadar/evaluator": "^2.46.0",
123
123
  "@almadar/logger": "^1.12.0",
124
- "@almadar/runtime": "^6.79.0",
125
- "@almadar/std": "^16.217.0",
124
+ "@almadar/runtime": "^6.81.0",
125
+ "@almadar/std": "^16.219.0",
126
126
  "@almadar/syntax": "^1.17.0",
127
127
  "@dnd-kit/core": "^6.3.1",
128
128
  "@dnd-kit/sortable": "^10.0.0",