@almadar/ui 3.9.1 → 4.0.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/avl/index.js CHANGED
@@ -10883,13 +10883,13 @@ var init_MapView = __esm({
10883
10883
  shadowSize: [41, 41]
10884
10884
  });
10885
10885
  L.Marker.prototype.options.icon = defaultIcon;
10886
- const { useEffect: useEffect88, useRef: useRef87, useCallback: useCallback125, useState: useState126 } = React126__default;
10886
+ const { useEffect: useEffect87, useRef: useRef87, useCallback: useCallback125, useState: useState125 } = React126__default;
10887
10887
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
10888
10888
  const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
10889
10889
  function MapUpdater({ centerLat, centerLng, zoom }) {
10890
10890
  const map = useMap();
10891
10891
  const prevRef = useRef87({ centerLat, centerLng, zoom });
10892
- useEffect88(() => {
10892
+ useEffect87(() => {
10893
10893
  const prev = prevRef.current;
10894
10894
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
10895
10895
  map.setView([centerLat, centerLng], zoom);
@@ -10900,7 +10900,7 @@ var init_MapView = __esm({
10900
10900
  }
10901
10901
  function MapClickHandler({ onMapClick }) {
10902
10902
  const map = useMap();
10903
- useEffect88(() => {
10903
+ useEffect87(() => {
10904
10904
  if (!onMapClick) return;
10905
10905
  const handler = (e) => {
10906
10906
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -10927,7 +10927,7 @@ var init_MapView = __esm({
10927
10927
  showAttribution = true
10928
10928
  }) {
10929
10929
  const eventBus = useEventBus3();
10930
- const [clickedPosition, setClickedPosition] = useState126(null);
10930
+ const [clickedPosition, setClickedPosition] = useState125(null);
10931
10931
  const handleMapClick = useCallback125((lat, lng) => {
10932
10932
  if (showClickedPin) {
10933
10933
  setClickedPosition({ lat, lng });
@@ -21611,7 +21611,290 @@ function formatValue(value, format) {
21611
21611
  return String(value);
21612
21612
  }
21613
21613
  }
21614
- var gapStyles6, DataGrid;
21614
+ function DataGrid({
21615
+ entity,
21616
+ fields: fieldsProp,
21617
+ columns: columnsProp,
21618
+ itemActions,
21619
+ cols,
21620
+ gap = "md",
21621
+ minCardWidth = 280,
21622
+ className,
21623
+ isLoading = false,
21624
+ error = null,
21625
+ imageField,
21626
+ selectable = false,
21627
+ selectionEvent,
21628
+ infiniteScroll,
21629
+ loadMoreEvent,
21630
+ hasMore,
21631
+ children,
21632
+ pageSize = 0
21633
+ }) {
21634
+ const eventBus = useEventBus();
21635
+ const { t } = useTranslate();
21636
+ const [selectedIds, setSelectedIds] = useState(/* @__PURE__ */ new Set());
21637
+ const [visibleCount, setVisibleCount] = useState(pageSize || Infinity);
21638
+ const fields = fieldsProp ?? columnsProp ?? [];
21639
+ const allData = Array.isArray(entity) ? entity : entity ? [entity] : [];
21640
+ const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
21641
+ const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
21642
+ const toggleSelection = useCallback((id) => {
21643
+ setSelectedIds((prev) => {
21644
+ const next = new Set(prev);
21645
+ if (next.has(id)) next.delete(id);
21646
+ else next.add(id);
21647
+ if (selectionEvent) {
21648
+ const payload = { selectedIds: Array.from(next) };
21649
+ eventBus.emit(`UI:${selectionEvent}`, payload);
21650
+ }
21651
+ return next;
21652
+ });
21653
+ }, [selectionEvent, eventBus]);
21654
+ const toggleAll = useCallback(() => {
21655
+ setSelectedIds((prev) => {
21656
+ const allIds2 = data.map((item, i) => item.id || String(i));
21657
+ const allSelected2 = allIds2.length > 0 && allIds2.every((id) => prev.has(id));
21658
+ const next = allSelected2 ? /* @__PURE__ */ new Set() : new Set(allIds2);
21659
+ if (selectionEvent) {
21660
+ const payload = { selectedIds: Array.from(next) };
21661
+ eventBus.emit(`UI:${selectionEvent}`, payload);
21662
+ }
21663
+ return next;
21664
+ });
21665
+ }, [data, selectionEvent, eventBus]);
21666
+ const titleField = fields.find((f3) => f3.variant === "h3" || f3.variant === "h4") ?? fields[0];
21667
+ const badgeFields = fields.filter((f3) => f3.variant === "badge" && f3 !== titleField);
21668
+ const bodyFields = fields.filter((f3) => f3 !== titleField && !badgeFields.includes(f3));
21669
+ const primaryActions = itemActions?.filter((a) => a.variant !== "danger") ?? [];
21670
+ const dangerActions = itemActions?.filter((a) => a.variant === "danger") ?? [];
21671
+ const handleActionClick = (action, itemData) => (e) => {
21672
+ e.stopPropagation();
21673
+ const payload = {
21674
+ id: itemData.id,
21675
+ row: itemData
21676
+ };
21677
+ eventBus.emit(`UI:${action.event}`, payload);
21678
+ };
21679
+ const gridTemplateColumns = cols ? void 0 : `repeat(auto-fit, minmax(min(${minCardWidth}px, 100%), 1fr))`;
21680
+ const colsClass = cols ? {
21681
+ 1: "grid-cols-1",
21682
+ 2: "sm:grid-cols-2",
21683
+ 3: "sm:grid-cols-2 lg:grid-cols-3",
21684
+ 4: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
21685
+ 5: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5",
21686
+ 6: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6"
21687
+ }[cols] : void 0;
21688
+ if (isLoading) {
21689
+ return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") || "Loading..." }) });
21690
+ }
21691
+ if (error) {
21692
+ return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
21693
+ }
21694
+ if (data.length === 0) {
21695
+ return /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("empty.noItems") || "No items found" }) });
21696
+ }
21697
+ const hasRenderProp = typeof children === "function";
21698
+ const allIds = data.map((item, i) => item.id || String(i));
21699
+ const allSelected = allIds.length > 0 && allIds.every((id) => selectedIds.has(id));
21700
+ const someSelected = selectedIds.size > 0;
21701
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
21702
+ selectable && someSelected && /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center px-2 py-2 bg-muted rounded-sm", children: [
21703
+ /* @__PURE__ */ jsx(
21704
+ "input",
21705
+ {
21706
+ type: "checkbox",
21707
+ checked: allSelected,
21708
+ onChange: toggleAll,
21709
+ className: "w-4 h-4 accent-primary",
21710
+ "aria-label": "Select all"
21711
+ }
21712
+ ),
21713
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "font-semibold", children: [
21714
+ selectedIds.size,
21715
+ " ",
21716
+ t("common.selected") || "selected"
21717
+ ] })
21718
+ ] }),
21719
+ /* @__PURE__ */ jsx(
21720
+ Box,
21721
+ {
21722
+ className: cn("grid", gapStyles6[gap], colsClass, className),
21723
+ style: gridTemplateColumns ? { gridTemplateColumns } : void 0,
21724
+ children: data.map((item, index) => {
21725
+ const itemData = item;
21726
+ const id = itemData.id || String(index);
21727
+ const isSelected = selectedIds.has(id);
21728
+ if (hasRenderProp) {
21729
+ return /* @__PURE__ */ jsx(
21730
+ Box,
21731
+ {
21732
+ "data-entity-row": true,
21733
+ "data-entity-id": id,
21734
+ className: cn(
21735
+ "bg-card rounded-lg",
21736
+ "border border-border",
21737
+ "shadow-sm hover:shadow-lg",
21738
+ "hover:border-primary transition-all",
21739
+ "p-4",
21740
+ isSelected && "ring-2 ring-primary border-primary"
21741
+ ),
21742
+ children: children(itemData, index)
21743
+ },
21744
+ id
21745
+ );
21746
+ }
21747
+ const titleValue = getNestedValue(itemData, titleField?.name ?? "");
21748
+ return /* @__PURE__ */ jsxs(
21749
+ Box,
21750
+ {
21751
+ "data-entity-row": true,
21752
+ "data-entity-id": id,
21753
+ className: cn(
21754
+ "bg-card rounded-lg",
21755
+ "border border-border",
21756
+ "shadow-sm hover:shadow-lg",
21757
+ "hover:border-primary transition-all",
21758
+ "flex flex-col",
21759
+ isSelected && "ring-2 ring-primary border-primary"
21760
+ ),
21761
+ children: [
21762
+ imageField && (() => {
21763
+ const imgUrl = getNestedValue(itemData, imageField);
21764
+ if (!imgUrl || typeof imgUrl !== "string") return null;
21765
+ return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
21766
+ "img",
21767
+ {
21768
+ src: imgUrl,
21769
+ alt: titleValue !== void 0 ? String(titleValue) : "",
21770
+ className: "w-full h-full object-cover",
21771
+ loading: "lazy"
21772
+ }
21773
+ ) });
21774
+ })(),
21775
+ /* @__PURE__ */ jsx(Box, { className: "p-4 pb-0", children: /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
21776
+ selectable && /* @__PURE__ */ jsx(
21777
+ "input",
21778
+ {
21779
+ type: "checkbox",
21780
+ checked: isSelected,
21781
+ onChange: () => toggleSelection(id),
21782
+ onClick: (e) => e.stopPropagation(),
21783
+ className: "w-4 h-4 mt-1 flex-shrink-0 accent-primary",
21784
+ "aria-label": `Select ${titleValue !== void 0 ? String(titleValue) : "item"}`
21785
+ }
21786
+ ),
21787
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
21788
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
21789
+ titleField?.icon && /* @__PURE__ */ jsx(Icon, { name: titleField.icon, size: "sm", className: "text-primary flex-shrink-0" }),
21790
+ /* @__PURE__ */ jsx(
21791
+ Typography,
21792
+ {
21793
+ variant: titleField?.variant === "h3" ? "h3" : "h4",
21794
+ className: "font-semibold truncate",
21795
+ children: String(titleValue)
21796
+ }
21797
+ )
21798
+ ] }),
21799
+ badgeFields.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap", children: badgeFields.map((field) => {
21800
+ const val = getNestedValue(itemData, field.name);
21801
+ if (val === void 0 || val === null) return null;
21802
+ return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
21803
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs" }),
21804
+ /* @__PURE__ */ jsx(Badge, { variant: statusVariant2(String(val)), children: String(val) })
21805
+ ] }, field.name);
21806
+ }) })
21807
+ ] }),
21808
+ dangerActions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-shrink-0", children: dangerActions.map((action, idx) => /* @__PURE__ */ jsxs(
21809
+ Button,
21810
+ {
21811
+ variant: "ghost",
21812
+ size: "sm",
21813
+ onClick: handleActionClick(action, itemData),
21814
+ "data-testid": `action-${action.event}`,
21815
+ className: "text-error hover:bg-error/10 px-2",
21816
+ children: [
21817
+ action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs" }),
21818
+ action.label
21819
+ ]
21820
+ },
21821
+ idx
21822
+ )) })
21823
+ ] }) }),
21824
+ bodyFields.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 flex-1", children: /* @__PURE__ */ jsx(VStack, { gap: "xs", children: bodyFields.map((field) => {
21825
+ const value = getNestedValue(itemData, field.name);
21826
+ if (value === void 0 || value === null || value === "") return null;
21827
+ if (field.format === "boolean") {
21828
+ return /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-center", children: [
21829
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
21830
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-muted-foreground" }),
21831
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel2(field.name) })
21832
+ ] }),
21833
+ /* @__PURE__ */ jsx(Badge, { variant: value ? "success" : "neutral", children: value ? t("common.yes") || "Yes" : t("common.no") || "No" })
21834
+ ] }, field.name);
21835
+ }
21836
+ return /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-center", children: [
21837
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
21838
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-muted-foreground" }),
21839
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel2(field.name) })
21840
+ ] }),
21841
+ /* @__PURE__ */ jsx(
21842
+ Typography,
21843
+ {
21844
+ variant: field.variant === "caption" ? "caption" : "small",
21845
+ className: "text-right truncate max-w-[60%]",
21846
+ children: formatValue(value, field.format)
21847
+ }
21848
+ )
21849
+ ] }, field.name);
21850
+ }) }) }),
21851
+ primaryActions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsx(HStack, { gap: "sm", className: "justify-end", children: primaryActions.map((action, idx) => /* @__PURE__ */ jsxs(
21852
+ Button,
21853
+ {
21854
+ variant: action.variant === "primary" ? "primary" : "ghost",
21855
+ size: "sm",
21856
+ onClick: handleActionClick(action, itemData),
21857
+ "data-testid": `action-${action.event}`,
21858
+ children: [
21859
+ action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
21860
+ action.label
21861
+ ]
21862
+ },
21863
+ idx
21864
+ )) }) })
21865
+ ]
21866
+ },
21867
+ id
21868
+ );
21869
+ })
21870
+ }
21871
+ ),
21872
+ hasMoreLocal && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(
21873
+ Button,
21874
+ {
21875
+ variant: "ghost",
21876
+ size: "sm",
21877
+ onClick: () => setVisibleCount((prev) => prev + (pageSize || 5)),
21878
+ children: [
21879
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
21880
+ t("common.showMore"),
21881
+ " (",
21882
+ allData.length - visibleCount,
21883
+ " remaining)"
21884
+ ]
21885
+ }
21886
+ ) }),
21887
+ infiniteScroll && loadMoreEvent && /* @__PURE__ */ jsx(
21888
+ InfiniteScrollSentinel,
21889
+ {
21890
+ loadMoreEvent,
21891
+ isLoading,
21892
+ hasMore
21893
+ }
21894
+ )
21895
+ ] });
21896
+ }
21897
+ var gapStyles6;
21615
21898
  var init_DataGrid = __esm({
21616
21899
  "components/molecules/DataGrid.tsx"() {
21617
21900
  "use client";
@@ -21633,289 +21916,6 @@ var init_DataGrid = __esm({
21633
21916
  lg: "gap-6",
21634
21917
  xl: "gap-8"
21635
21918
  };
21636
- DataGrid = ({
21637
- entity,
21638
- fields: fieldsProp,
21639
- columns: columnsProp,
21640
- itemActions,
21641
- cols,
21642
- gap = "md",
21643
- minCardWidth = 280,
21644
- className,
21645
- isLoading = false,
21646
- error = null,
21647
- imageField,
21648
- selectable = false,
21649
- selectionEvent,
21650
- infiniteScroll,
21651
- loadMoreEvent,
21652
- hasMore,
21653
- children,
21654
- pageSize = 0
21655
- }) => {
21656
- const eventBus = useEventBus();
21657
- const { t } = useTranslate();
21658
- const [selectedIds, setSelectedIds] = useState(/* @__PURE__ */ new Set());
21659
- const [visibleCount, setVisibleCount] = useState(pageSize || Infinity);
21660
- const fields = fieldsProp ?? columnsProp ?? [];
21661
- const allData = Array.isArray(entity) ? entity : entity ? [entity] : [];
21662
- const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
21663
- const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
21664
- const toggleSelection = useCallback((id) => {
21665
- setSelectedIds((prev) => {
21666
- const next = new Set(prev);
21667
- if (next.has(id)) next.delete(id);
21668
- else next.add(id);
21669
- if (selectionEvent) {
21670
- const payload = { selectedIds: Array.from(next) };
21671
- eventBus.emit(`UI:${selectionEvent}`, payload);
21672
- }
21673
- return next;
21674
- });
21675
- }, [selectionEvent, eventBus]);
21676
- const toggleAll = useCallback(() => {
21677
- setSelectedIds((prev) => {
21678
- const allIds2 = data.map((item, i) => item.id || String(i));
21679
- const allSelected2 = allIds2.length > 0 && allIds2.every((id) => prev.has(id));
21680
- const next = allSelected2 ? /* @__PURE__ */ new Set() : new Set(allIds2);
21681
- if (selectionEvent) {
21682
- const payload = { selectedIds: Array.from(next) };
21683
- eventBus.emit(`UI:${selectionEvent}`, payload);
21684
- }
21685
- return next;
21686
- });
21687
- }, [data, selectionEvent, eventBus]);
21688
- const titleField = fields.find((f3) => f3.variant === "h3" || f3.variant === "h4") ?? fields[0];
21689
- const badgeFields = fields.filter((f3) => f3.variant === "badge" && f3 !== titleField);
21690
- const bodyFields = fields.filter((f3) => f3 !== titleField && !badgeFields.includes(f3));
21691
- const primaryActions = itemActions?.filter((a) => a.variant !== "danger") ?? [];
21692
- const dangerActions = itemActions?.filter((a) => a.variant === "danger") ?? [];
21693
- const handleActionClick = (action, itemData) => (e) => {
21694
- e.stopPropagation();
21695
- const payload = {
21696
- id: itemData.id,
21697
- row: itemData
21698
- };
21699
- eventBus.emit(`UI:${action.event}`, payload);
21700
- };
21701
- const gridTemplateColumns = cols ? void 0 : `repeat(auto-fit, minmax(min(${minCardWidth}px, 100%), 1fr))`;
21702
- const colsClass = cols ? {
21703
- 1: "grid-cols-1",
21704
- 2: "sm:grid-cols-2",
21705
- 3: "sm:grid-cols-2 lg:grid-cols-3",
21706
- 4: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
21707
- 5: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5",
21708
- 6: "sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-6"
21709
- }[cols] : void 0;
21710
- if (isLoading) {
21711
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") || "Loading..." }) });
21712
- }
21713
- if (error) {
21714
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
21715
- }
21716
- if (data.length === 0) {
21717
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("empty.noItems") || "No items found" }) });
21718
- }
21719
- const hasRenderProp = typeof children === "function";
21720
- const allIds = data.map((item, i) => item.id || String(i));
21721
- const allSelected = allIds.length > 0 && allIds.every((id) => selectedIds.has(id));
21722
- const someSelected = selectedIds.size > 0;
21723
- return /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
21724
- selectable && someSelected && /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center px-2 py-2 bg-muted rounded-sm", children: [
21725
- /* @__PURE__ */ jsx(
21726
- "input",
21727
- {
21728
- type: "checkbox",
21729
- checked: allSelected,
21730
- onChange: toggleAll,
21731
- className: "w-4 h-4 accent-primary",
21732
- "aria-label": "Select all"
21733
- }
21734
- ),
21735
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "font-semibold", children: [
21736
- selectedIds.size,
21737
- " ",
21738
- t("common.selected") || "selected"
21739
- ] })
21740
- ] }),
21741
- /* @__PURE__ */ jsx(
21742
- Box,
21743
- {
21744
- className: cn("grid", gapStyles6[gap], colsClass, className),
21745
- style: gridTemplateColumns ? { gridTemplateColumns } : void 0,
21746
- children: data.map((item, index) => {
21747
- const itemData = item;
21748
- const id = itemData.id || String(index);
21749
- const isSelected = selectedIds.has(id);
21750
- if (hasRenderProp) {
21751
- return /* @__PURE__ */ jsx(
21752
- Box,
21753
- {
21754
- "data-entity-row": true,
21755
- "data-entity-id": id,
21756
- className: cn(
21757
- "bg-card rounded-lg",
21758
- "border border-border",
21759
- "shadow-sm hover:shadow-lg",
21760
- "hover:border-primary transition-all",
21761
- "p-4",
21762
- isSelected && "ring-2 ring-primary border-primary"
21763
- ),
21764
- children: children(itemData, index)
21765
- },
21766
- id
21767
- );
21768
- }
21769
- const titleValue = getNestedValue(itemData, titleField?.name ?? "");
21770
- return /* @__PURE__ */ jsxs(
21771
- Box,
21772
- {
21773
- "data-entity-row": true,
21774
- "data-entity-id": id,
21775
- className: cn(
21776
- "bg-card rounded-lg",
21777
- "border border-border",
21778
- "shadow-sm hover:shadow-lg",
21779
- "hover:border-primary transition-all",
21780
- "flex flex-col",
21781
- isSelected && "ring-2 ring-primary border-primary"
21782
- ),
21783
- children: [
21784
- imageField && (() => {
21785
- const imgUrl = getNestedValue(itemData, imageField);
21786
- if (!imgUrl || typeof imgUrl !== "string") return null;
21787
- return /* @__PURE__ */ jsx(Box, { className: "w-full aspect-video overflow-hidden rounded-t-lg", children: /* @__PURE__ */ jsx(
21788
- "img",
21789
- {
21790
- src: imgUrl,
21791
- alt: titleValue !== void 0 ? String(titleValue) : "",
21792
- className: "w-full h-full object-cover",
21793
- loading: "lazy"
21794
- }
21795
- ) });
21796
- })(),
21797
- /* @__PURE__ */ jsx(Box, { className: "p-4 pb-0", children: /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-start", children: [
21798
- selectable && /* @__PURE__ */ jsx(
21799
- "input",
21800
- {
21801
- type: "checkbox",
21802
- checked: isSelected,
21803
- onChange: () => toggleSelection(id),
21804
- onClick: (e) => e.stopPropagation(),
21805
- className: "w-4 h-4 mt-1 flex-shrink-0 accent-primary",
21806
- "aria-label": `Select ${titleValue !== void 0 ? String(titleValue) : "item"}`
21807
- }
21808
- ),
21809
- /* @__PURE__ */ jsxs(VStack, { gap: "xs", className: "flex-1 min-w-0", children: [
21810
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
21811
- titleField?.icon && /* @__PURE__ */ jsx(Icon, { name: titleField.icon, size: "sm", className: "text-primary flex-shrink-0" }),
21812
- /* @__PURE__ */ jsx(
21813
- Typography,
21814
- {
21815
- variant: titleField?.variant === "h3" ? "h3" : "h4",
21816
- className: "font-semibold truncate",
21817
- children: String(titleValue)
21818
- }
21819
- )
21820
- ] }),
21821
- badgeFields.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap", children: badgeFields.map((field) => {
21822
- const val = getNestedValue(itemData, field.name);
21823
- if (val === void 0 || val === null) return null;
21824
- return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
21825
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs" }),
21826
- /* @__PURE__ */ jsx(Badge, { variant: statusVariant2(String(val)), children: String(val) })
21827
- ] }, field.name);
21828
- }) })
21829
- ] }),
21830
- dangerActions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-shrink-0", children: dangerActions.map((action, idx) => /* @__PURE__ */ jsxs(
21831
- Button,
21832
- {
21833
- variant: "ghost",
21834
- size: "sm",
21835
- onClick: handleActionClick(action, itemData),
21836
- "data-testid": `action-${action.event}`,
21837
- className: "text-error hover:bg-error/10 px-2",
21838
- children: [
21839
- action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs" }),
21840
- action.label
21841
- ]
21842
- },
21843
- idx
21844
- )) })
21845
- ] }) }),
21846
- bodyFields.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 flex-1", children: /* @__PURE__ */ jsx(VStack, { gap: "xs", children: bodyFields.map((field) => {
21847
- const value = getNestedValue(itemData, field.name);
21848
- if (value === void 0 || value === null || value === "") return null;
21849
- if (field.format === "boolean") {
21850
- return /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-center", children: [
21851
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
21852
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-muted-foreground" }),
21853
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel2(field.name) })
21854
- ] }),
21855
- /* @__PURE__ */ jsx(Badge, { variant: value ? "success" : "neutral", children: value ? t("common.yes") || "Yes" : t("common.no") || "No" })
21856
- ] }, field.name);
21857
- }
21858
- return /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "justify-between items-center", children: [
21859
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
21860
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-muted-foreground" }),
21861
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel2(field.name) })
21862
- ] }),
21863
- /* @__PURE__ */ jsx(
21864
- Typography,
21865
- {
21866
- variant: field.variant === "caption" ? "caption" : "small",
21867
- className: "text-right truncate max-w-[60%]",
21868
- children: formatValue(value, field.format)
21869
- }
21870
- )
21871
- ] }, field.name);
21872
- }) }) }),
21873
- primaryActions.length > 0 && /* @__PURE__ */ jsx(Box, { className: "px-4 py-3 mt-auto border-t border-border", children: /* @__PURE__ */ jsx(HStack, { gap: "sm", className: "justify-end", children: primaryActions.map((action, idx) => /* @__PURE__ */ jsxs(
21874
- Button,
21875
- {
21876
- variant: action.variant === "primary" ? "primary" : "ghost",
21877
- size: "sm",
21878
- onClick: handleActionClick(action, itemData),
21879
- "data-testid": `action-${action.event}`,
21880
- children: [
21881
- action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
21882
- action.label
21883
- ]
21884
- },
21885
- idx
21886
- )) }) })
21887
- ]
21888
- },
21889
- id
21890
- );
21891
- })
21892
- }
21893
- ),
21894
- hasMoreLocal && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(
21895
- Button,
21896
- {
21897
- variant: "ghost",
21898
- size: "sm",
21899
- onClick: () => setVisibleCount((prev) => prev + (pageSize || 5)),
21900
- children: [
21901
- /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
21902
- t("common.showMore"),
21903
- " (",
21904
- allData.length - visibleCount,
21905
- " remaining)"
21906
- ]
21907
- }
21908
- ) }),
21909
- infiniteScroll && loadMoreEvent && /* @__PURE__ */ jsx(
21910
- InfiniteScrollSentinel,
21911
- {
21912
- loadMoreEvent,
21913
- isLoading,
21914
- hasMore
21915
- }
21916
- )
21917
- ] });
21918
- };
21919
21919
  DataGrid.displayName = "DataGrid";
21920
21920
  }
21921
21921
  });
@@ -21963,318 +21963,317 @@ function groupData(items, field) {
21963
21963
  }
21964
21964
  return Array.from(groups.entries()).map(([label, groupItems]) => ({ label, items: groupItems }));
21965
21965
  }
21966
- var DataList;
21967
- var init_DataList = __esm({
21968
- "components/molecules/DataList.tsx"() {
21969
- "use client";
21970
- init_cn();
21971
- init_getNestedValue();
21972
- init_useEventBus();
21973
- init_useTranslate();
21974
- init_Box();
21975
- init_Stack();
21976
- init_Typography();
21977
- init_Badge();
21978
- init_Button();
21979
- init_Icon();
21980
- init_ProgressBar();
21981
- init_Divider();
21982
- init_InfiniteScrollSentinel();
21983
- DataList = ({
21984
- entity,
21985
- fields: fieldsProp,
21986
- columns: columnsProp,
21987
- itemActions,
21988
- gap = "none",
21989
- variant = "default",
21990
- groupBy,
21991
- senderField,
21992
- currentUser,
21993
- className,
21994
- isLoading = false,
21995
- error = null,
21996
- // Gesture props: reorderable, swipeLeftEvent, swipeRightEvent, longPressEvent
21997
- // are consumed by the compiler to wrap items in SwipeableRow/SortableList.
21998
- // DataList destructures them here to prevent DOM passthrough.
21999
- reorderable: _reorderable,
22000
- reorderEvent: _reorderEvent,
22001
- swipeLeftEvent: _swipeLeftEvent,
22002
- swipeLeftActions: _swipeLeftActions,
22003
- swipeRightEvent: _swipeRightEvent,
22004
- swipeRightActions: _swipeRightActions,
22005
- longPressEvent: _longPressEvent,
22006
- infiniteScroll,
22007
- loadMoreEvent,
22008
- hasMore,
22009
- children,
22010
- pageSize = 5
22011
- }) => {
22012
- const eventBus = useEventBus();
22013
- const { t } = useTranslate();
22014
- const [visibleCount, setVisibleCount] = React126__default.useState(pageSize || Infinity);
22015
- const fields = fieldsProp ?? columnsProp ?? [];
22016
- const allData = Array.isArray(entity) ? entity : entity ? [entity] : [];
22017
- const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
22018
- const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
22019
- const titleField = fields.find((f3) => f3.variant === "h3" || f3.variant === "h4") ?? fields[0];
22020
- const badgeFields = fields.filter((f3) => f3.variant === "badge" && f3 !== titleField);
22021
- const progressFields = fields.filter((f3) => f3.variant === "progress");
22022
- const bodyFields = fields.filter(
22023
- (f3) => f3 !== titleField && !badgeFields.includes(f3) && !progressFields.includes(f3)
22024
- );
22025
- const handleActionClick = (action, itemData) => (e) => {
22026
- e.stopPropagation();
22027
- const payload = {
22028
- id: itemData.id,
22029
- row: itemData
22030
- };
22031
- eventBus.emit(`UI:${action.event}`, payload);
22032
- };
22033
- if (isLoading) {
22034
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") || "Loading..." }) });
22035
- }
22036
- if (error) {
22037
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
22038
- }
22039
- if (data.length === 0) {
22040
- return /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("empty.noItems") || "No items found" }) });
22041
- }
22042
- const gapClass = {
22043
- none: "",
22044
- sm: "gap-1",
22045
- md: "gap-2",
22046
- lg: "gap-4"
22047
- }[gap];
22048
- const isCard = variant === "card";
22049
- const isCompact = variant === "compact";
22050
- const isMessage = variant === "message";
22051
- if (isMessage) {
22052
- const items2 = data.map((item) => item);
22053
- const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
22054
- const contentField = titleField?.name ?? fields[0]?.name ?? "";
22055
- return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React126__default.Fragment, { children: [
22056
- group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
22057
- group.items.map((itemData, index) => {
22058
- const id = itemData.id || `${gi}-${index}`;
22059
- const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
22060
- const isSent = Boolean(currentUser && sender === currentUser);
22061
- const content = getNestedValue(itemData, contentField);
22062
- const timestampField = fields.find((f3) => f3.format === "date");
22063
- const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
22064
- return /* @__PURE__ */ jsx(
22065
- Box,
22066
- {
22067
- className: cn(
22068
- "flex px-4",
22069
- isSent ? "justify-end" : "justify-start"
22070
- ),
22071
- children: /* @__PURE__ */ jsxs(
22072
- Box,
22073
- {
22074
- className: cn(
22075
- "max-w-[75%] px-4 py-2",
22076
- isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
22077
- ),
22078
- children: [
22079
- !isSent && senderField && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-semibold mb-0.5", children: sender }),
22080
- /* @__PURE__ */ jsx(Typography, { variant: "body", children: content !== void 0 && content !== null ? String(content) : "" }),
22081
- timestamp != null ? /* @__PURE__ */ jsx(
22082
- Typography,
22083
- {
22084
- variant: "caption",
22085
- className: cn(
22086
- "mt-1 text-[0.65rem]",
22087
- isSent ? "opacity-70" : "text-muted-foreground"
22088
- ),
22089
- children: formatDate3(timestamp)
22090
- }
22091
- ) : null
22092
- ]
22093
- }
22094
- )
22095
- },
22096
- id
22097
- );
22098
- })
22099
- ] }, gi)) });
22100
- }
22101
- const hasRenderProp = typeof children === "function";
22102
- const items = data.map((item) => item);
22103
- const groups = groupBy ? groupData(items, groupBy) : [{ label: "", items }];
22104
- const renderItem = (itemData, index, isLast) => {
22105
- if (hasRenderProp) {
22106
- const id2 = itemData.id || String(index);
22107
- return /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id2, children: [
22108
- /* @__PURE__ */ jsxs(
21966
+ function DataList({
21967
+ entity,
21968
+ fields: fieldsProp,
21969
+ columns: columnsProp,
21970
+ itemActions,
21971
+ gap = "none",
21972
+ variant = "default",
21973
+ groupBy,
21974
+ senderField,
21975
+ currentUser,
21976
+ className,
21977
+ isLoading = false,
21978
+ error = null,
21979
+ // Gesture props: reorderable, swipeLeftEvent, swipeRightEvent, longPressEvent
21980
+ // are consumed by the compiler to wrap items in SwipeableRow/SortableList.
21981
+ // DataList destructures them here to prevent DOM passthrough.
21982
+ reorderable: _reorderable,
21983
+ reorderEvent: _reorderEvent,
21984
+ swipeLeftEvent: _swipeLeftEvent,
21985
+ swipeLeftActions: _swipeLeftActions,
21986
+ swipeRightEvent: _swipeRightEvent,
21987
+ swipeRightActions: _swipeRightActions,
21988
+ longPressEvent: _longPressEvent,
21989
+ infiniteScroll,
21990
+ loadMoreEvent,
21991
+ hasMore,
21992
+ children,
21993
+ pageSize = 5
21994
+ }) {
21995
+ const eventBus = useEventBus();
21996
+ const { t } = useTranslate();
21997
+ const [visibleCount, setVisibleCount] = React126__default.useState(pageSize || Infinity);
21998
+ const fields = fieldsProp ?? columnsProp ?? [];
21999
+ const allData = Array.isArray(entity) ? entity : entity ? [entity] : [];
22000
+ const data = pageSize > 0 ? allData.slice(0, visibleCount) : allData;
22001
+ const hasMoreLocal = pageSize > 0 && visibleCount < allData.length;
22002
+ const titleField = fields.find((f3) => f3.variant === "h3" || f3.variant === "h4") ?? fields[0];
22003
+ const badgeFields = fields.filter((f3) => f3.variant === "badge" && f3 !== titleField);
22004
+ const progressFields = fields.filter((f3) => f3.variant === "progress");
22005
+ const bodyFields = fields.filter(
22006
+ (f3) => f3 !== titleField && !badgeFields.includes(f3) && !progressFields.includes(f3)
22007
+ );
22008
+ const handleActionClick = (action, itemData) => (e) => {
22009
+ e.stopPropagation();
22010
+ const payload = {
22011
+ id: itemData.id,
22012
+ row: itemData
22013
+ };
22014
+ eventBus.emit(`UI:${action.event}`, payload);
22015
+ };
22016
+ if (isLoading) {
22017
+ return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("loading.items") || "Loading..." }) });
22018
+ }
22019
+ if (error) {
22020
+ return /* @__PURE__ */ jsx(Box, { className: "text-center py-8", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "error", children: error.message }) });
22021
+ }
22022
+ if (data.length === 0) {
22023
+ return /* @__PURE__ */ jsx(Box, { className: "text-center py-12", children: /* @__PURE__ */ jsx(Typography, { variant: "body", color: "secondary", children: t("empty.noItems") || "No items found" }) });
22024
+ }
22025
+ const gapClass = {
22026
+ none: "",
22027
+ sm: "gap-1",
22028
+ md: "gap-2",
22029
+ lg: "gap-4"
22030
+ }[gap];
22031
+ const isCard = variant === "card";
22032
+ const isCompact = variant === "compact";
22033
+ const isMessage = variant === "message";
22034
+ if (isMessage) {
22035
+ const items2 = data.map((item) => item);
22036
+ const groups2 = groupBy ? groupData(items2, groupBy) : [{ label: "", items: items2 }];
22037
+ const contentField = titleField?.name ?? fields[0]?.name ?? "";
22038
+ return /* @__PURE__ */ jsx(VStack, { gap: "sm", className: cn("py-2", className), children: groups2.map((group, gi) => /* @__PURE__ */ jsxs(React126__default.Fragment, { children: [
22039
+ group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: "my-2" }),
22040
+ group.items.map((itemData, index) => {
22041
+ const id = itemData.id || `${gi}-${index}`;
22042
+ const sender = senderField ? String(getNestedValue(itemData, senderField) ?? "") : "";
22043
+ const isSent = Boolean(currentUser && sender === currentUser);
22044
+ const content = getNestedValue(itemData, contentField);
22045
+ const timestampField = fields.find((f3) => f3.format === "date");
22046
+ const timestamp = timestampField ? getNestedValue(itemData, timestampField.name) : null;
22047
+ return /* @__PURE__ */ jsx(
22048
+ Box,
22049
+ {
22050
+ className: cn(
22051
+ "flex px-4",
22052
+ isSent ? "justify-end" : "justify-start"
22053
+ ),
22054
+ children: /* @__PURE__ */ jsxs(
22109
22055
  Box,
22110
22056
  {
22111
22057
  className: cn(
22112
- "group flex items-center gap-4 transition-all duration-200",
22113
- isCompact ? "px-4 py-2" : "px-6 py-4",
22114
- "hover:bg-muted/80",
22115
- !isCard && !isCompact && "rounded-lg border border-transparent hover:border-border"
22058
+ "max-w-[75%] px-4 py-2",
22059
+ isSent ? "bg-primary text-primary-foreground rounded-2xl rounded-br-sm" : "bg-muted text-foreground rounded-2xl rounded-bl-sm"
22116
22060
  ),
22117
22061
  children: [
22118
- /* @__PURE__ */ jsx(Box, { className: "flex-1 min-w-0", children: children(itemData, index) }),
22119
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsx(
22120
- HStack,
22062
+ !isSent && senderField && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: "font-semibold mb-0.5", children: sender }),
22063
+ /* @__PURE__ */ jsx(Typography, { variant: "body", children: content !== void 0 && content !== null ? String(content) : "" }),
22064
+ timestamp != null ? /* @__PURE__ */ jsx(
22065
+ Typography,
22121
22066
  {
22122
- gap: "xs",
22123
- className: "flex-shrink-0",
22124
- children: itemActions.map((action, idx) => /* @__PURE__ */ jsxs(
22125
- Button,
22126
- {
22127
- variant: action.variant ?? "ghost",
22128
- size: "sm",
22129
- onClick: handleActionClick(action, itemData),
22130
- "data-testid": `action-${action.event}`,
22131
- className: cn(
22132
- action.variant === "danger" && "text-error hover:bg-error/10"
22133
- ),
22134
- children: [
22135
- action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
22136
- action.label
22137
- ]
22138
- },
22139
- idx
22140
- ))
22067
+ variant: "caption",
22068
+ className: cn(
22069
+ "mt-1 text-[0.65rem]",
22070
+ isSent ? "opacity-70" : "text-muted-foreground"
22071
+ ),
22072
+ children: formatDate3(timestamp)
22141
22073
  }
22142
- )
22074
+ ) : null
22143
22075
  ]
22144
22076
  }
22077
+ )
22078
+ },
22079
+ id
22080
+ );
22081
+ })
22082
+ ] }, gi)) });
22083
+ }
22084
+ const hasRenderProp = typeof children === "function";
22085
+ const items = data.map((item) => item);
22086
+ const groups = groupBy ? groupData(items, groupBy) : [{ label: "", items }];
22087
+ const renderItem = (itemData, index, isLast) => {
22088
+ if (hasRenderProp) {
22089
+ const id2 = itemData.id || String(index);
22090
+ return /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id2, children: [
22091
+ /* @__PURE__ */ jsxs(
22092
+ Box,
22093
+ {
22094
+ className: cn(
22095
+ "group flex items-center gap-4 transition-all duration-200",
22096
+ isCompact ? "px-4 py-2" : "px-6 py-4",
22097
+ "hover:bg-muted/80",
22098
+ !isCard && !isCompact && "rounded-lg border border-transparent hover:border-border"
22145
22099
  ),
22146
- isCard && !isLast && /* @__PURE__ */ jsx(Box, { className: "mx-6 border-b border-border/40" })
22147
- ] }, id2);
22148
- }
22149
- const id = itemData.id || String(index);
22150
- const titleValue = getNestedValue(itemData, titleField?.name ?? "");
22151
- return /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id, children: [
22152
- /* @__PURE__ */ jsxs(
22153
- Box,
22154
- {
22155
- className: cn(
22156
- "group flex items-center gap-4 transition-all duration-200",
22157
- isCompact ? "px-4 py-2" : "px-6 py-4",
22158
- "hover:bg-muted/80",
22159
- !isCard && !isCompact && "rounded-lg border border-transparent hover:border-border"
22160
- ),
22161
- children: [
22162
- /* @__PURE__ */ jsxs(Box, { className: "flex-1 min-w-0", children: [
22163
- /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
22164
- titleField?.icon && /* @__PURE__ */ jsx(
22165
- Icon,
22166
- {
22167
- name: titleField.icon,
22168
- size: isCompact ? "xs" : "sm",
22169
- className: "text-primary flex-shrink-0"
22170
- }
22171
- ),
22172
- titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsx(
22173
- Typography,
22174
- {
22175
- variant: titleField?.variant === "h3" ? "h3" : "h4",
22176
- className: cn("font-semibold truncate flex-1", isCompact && "text-sm"),
22177
- children: String(titleValue)
22178
- }
22179
- ),
22180
- badgeFields.map((field) => {
22181
- const val = getNestedValue(itemData, field.name);
22182
- if (val === void 0 || val === null) return null;
22183
- return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center flex-shrink-0", children: [
22184
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs" }),
22185
- /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(val)), children: String(val) })
22186
- ] }, field.name);
22187
- })
22188
- ] }),
22189
- bodyFields.length > 0 && !isCompact && /* @__PURE__ */ jsx(HStack, { gap: "md", className: "mt-1.5 flex-wrap", children: bodyFields.map((field) => {
22190
- const value = getNestedValue(itemData, field.name);
22191
- if (value === void 0 || value === null || value === "") return null;
22192
- return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
22193
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-muted-foreground" }),
22194
- /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", children: [
22195
- field.label ?? fieldLabel3(field.name),
22196
- ":"
22197
- ] }),
22198
- /* @__PURE__ */ jsx(Typography, { variant: "small", children: formatValue2(value, field.format) })
22199
- ] }, field.name);
22200
- }) }),
22201
- progressFields.map((field) => {
22202
- const value = getNestedValue(itemData, field.name);
22203
- if (typeof value !== "number") return null;
22204
- return /* @__PURE__ */ jsxs(Box, { className: "mt-2 max-w-xs", children: [
22205
- /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center mb-1", children: [
22206
- field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-muted-foreground" }),
22207
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel3(field.name) })
22208
- ] }),
22209
- /* @__PURE__ */ jsx(ProgressBar, { value, max: 100 })
22210
- ] }, field.name);
22211
- })
22212
- ] }),
22213
- itemActions && itemActions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-shrink-0", children: itemActions.map((action, idx) => /* @__PURE__ */ jsxs(
22214
- Button,
22215
- {
22216
- variant: action.variant ?? "ghost",
22217
- size: "sm",
22218
- onClick: handleActionClick(action, itemData),
22219
- "data-testid": `action-${action.event}`,
22220
- className: cn(
22221
- action.variant === "danger" && "text-error hover:bg-error/10"
22222
- ),
22223
- children: [
22224
- action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
22225
- action.label
22226
- ]
22227
- },
22228
- idx
22229
- )) })
22230
- ]
22231
- }
22232
- ),
22233
- isCard && !isLast && /* @__PURE__ */ jsx(Box, { className: "mx-6 border-b border-border/40" })
22234
- ] }, id);
22235
- };
22236
- return /* @__PURE__ */ jsxs(
22100
+ children: [
22101
+ /* @__PURE__ */ jsx(Box, { className: "flex-1 min-w-0", children: children(itemData, index) }),
22102
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsx(
22103
+ HStack,
22104
+ {
22105
+ gap: "xs",
22106
+ className: "flex-shrink-0",
22107
+ children: itemActions.map((action, idx) => /* @__PURE__ */ jsxs(
22108
+ Button,
22109
+ {
22110
+ variant: action.variant ?? "ghost",
22111
+ size: "sm",
22112
+ onClick: handleActionClick(action, itemData),
22113
+ "data-testid": `action-${action.event}`,
22114
+ className: cn(
22115
+ action.variant === "danger" && "text-error hover:bg-error/10"
22116
+ ),
22117
+ children: [
22118
+ action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
22119
+ action.label
22120
+ ]
22121
+ },
22122
+ idx
22123
+ ))
22124
+ }
22125
+ )
22126
+ ]
22127
+ }
22128
+ ),
22129
+ isCard && !isLast && /* @__PURE__ */ jsx(Box, { className: "mx-6 border-b border-border/40" })
22130
+ ] }, id2);
22131
+ }
22132
+ const id = itemData.id || String(index);
22133
+ const titleValue = getNestedValue(itemData, titleField?.name ?? "");
22134
+ return /* @__PURE__ */ jsxs(Box, { "data-entity-row": true, "data-entity-id": id, children: [
22135
+ /* @__PURE__ */ jsxs(
22237
22136
  Box,
22238
22137
  {
22239
22138
  className: cn(
22240
- isCard && "bg-card rounded-xl border border-border shadow-lg overflow-hidden",
22241
- !isCard && gapClass,
22242
- className
22139
+ "group flex items-center gap-4 transition-all duration-200",
22140
+ isCompact ? "px-4 py-2" : "px-6 py-4",
22141
+ "hover:bg-muted/80",
22142
+ !isCard && !isCompact && "rounded-lg border border-transparent hover:border-border"
22243
22143
  ),
22244
22144
  children: [
22245
- groups.map((group, gi) => /* @__PURE__ */ jsxs(React126__default.Fragment, { children: [
22246
- group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-4" : "mt-0" }),
22247
- group.items.map(
22248
- (itemData, index) => renderItem(itemData, index, gi === groups.length - 1 && index === group.items.length - 1)
22249
- )
22250
- ] }, gi)),
22251
- hasMoreLocal && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(
22145
+ /* @__PURE__ */ jsxs(Box, { className: "flex-1 min-w-0", children: [
22146
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
22147
+ titleField?.icon && /* @__PURE__ */ jsx(
22148
+ Icon,
22149
+ {
22150
+ name: titleField.icon,
22151
+ size: isCompact ? "xs" : "sm",
22152
+ className: "text-primary flex-shrink-0"
22153
+ }
22154
+ ),
22155
+ titleValue !== void 0 && titleValue !== null && /* @__PURE__ */ jsx(
22156
+ Typography,
22157
+ {
22158
+ variant: titleField?.variant === "h3" ? "h3" : "h4",
22159
+ className: cn("font-semibold truncate flex-1", isCompact && "text-sm"),
22160
+ children: String(titleValue)
22161
+ }
22162
+ ),
22163
+ badgeFields.map((field) => {
22164
+ const val = getNestedValue(itemData, field.name);
22165
+ if (val === void 0 || val === null) return null;
22166
+ return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center flex-shrink-0", children: [
22167
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs" }),
22168
+ /* @__PURE__ */ jsx(Badge, { variant: statusVariant3(String(val)), children: String(val) })
22169
+ ] }, field.name);
22170
+ })
22171
+ ] }),
22172
+ bodyFields.length > 0 && !isCompact && /* @__PURE__ */ jsx(HStack, { gap: "md", className: "mt-1.5 flex-wrap", children: bodyFields.map((field) => {
22173
+ const value = getNestedValue(itemData, field.name);
22174
+ if (value === void 0 || value === null || value === "") return null;
22175
+ return /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center", children: [
22176
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-muted-foreground" }),
22177
+ /* @__PURE__ */ jsxs(Typography, { variant: "caption", color: "secondary", children: [
22178
+ field.label ?? fieldLabel3(field.name),
22179
+ ":"
22180
+ ] }),
22181
+ /* @__PURE__ */ jsx(Typography, { variant: "small", children: formatValue2(value, field.format) })
22182
+ ] }, field.name);
22183
+ }) }),
22184
+ progressFields.map((field) => {
22185
+ const value = getNestedValue(itemData, field.name);
22186
+ if (typeof value !== "number") return null;
22187
+ return /* @__PURE__ */ jsxs(Box, { className: "mt-2 max-w-xs", children: [
22188
+ /* @__PURE__ */ jsxs(HStack, { gap: "xs", className: "items-center mb-1", children: [
22189
+ field.icon && /* @__PURE__ */ jsx(Icon, { name: field.icon, size: "xs", className: "text-muted-foreground" }),
22190
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: field.label ?? fieldLabel3(field.name) })
22191
+ ] }),
22192
+ /* @__PURE__ */ jsx(ProgressBar, { value, max: 100 })
22193
+ ] }, field.name);
22194
+ })
22195
+ ] }),
22196
+ itemActions && itemActions.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-shrink-0", children: itemActions.map((action, idx) => /* @__PURE__ */ jsxs(
22252
22197
  Button,
22253
22198
  {
22254
- variant: "ghost",
22199
+ variant: action.variant ?? "ghost",
22255
22200
  size: "sm",
22256
- onClick: () => setVisibleCount((prev) => prev + (pageSize || 5)),
22201
+ onClick: handleActionClick(action, itemData),
22202
+ "data-testid": `action-${action.event}`,
22203
+ className: cn(
22204
+ action.variant === "danger" && "text-error hover:bg-error/10"
22205
+ ),
22257
22206
  children: [
22258
- /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
22259
- t("common.showMore"),
22260
- " (",
22261
- allData.length - visibleCount,
22262
- " remaining)"
22207
+ action.icon && /* @__PURE__ */ jsx(Icon, { name: action.icon, size: "xs", className: "mr-1" }),
22208
+ action.label
22263
22209
  ]
22264
- }
22265
- ) }),
22266
- infiniteScroll && loadMoreEvent && /* @__PURE__ */ jsx(
22267
- InfiniteScrollSentinel,
22268
- {
22269
- loadMoreEvent,
22270
- isLoading,
22271
- hasMore
22272
- }
22273
- )
22210
+ },
22211
+ idx
22212
+ )) })
22274
22213
  ]
22275
22214
  }
22276
- );
22277
- };
22215
+ ),
22216
+ isCard && !isLast && /* @__PURE__ */ jsx(Box, { className: "mx-6 border-b border-border/40" })
22217
+ ] }, id);
22218
+ };
22219
+ return /* @__PURE__ */ jsxs(
22220
+ Box,
22221
+ {
22222
+ className: cn(
22223
+ isCard && "bg-card rounded-xl border border-border shadow-lg overflow-hidden",
22224
+ !isCard && gapClass,
22225
+ className
22226
+ ),
22227
+ children: [
22228
+ groups.map((group, gi) => /* @__PURE__ */ jsxs(React126__default.Fragment, { children: [
22229
+ group.label && /* @__PURE__ */ jsx(Divider, { label: group.label, className: gi > 0 ? "mt-4" : "mt-0" }),
22230
+ group.items.map(
22231
+ (itemData, index) => renderItem(itemData, index, gi === groups.length - 1 && index === group.items.length - 1)
22232
+ )
22233
+ ] }, gi)),
22234
+ hasMoreLocal && /* @__PURE__ */ jsx(Box, { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs(
22235
+ Button,
22236
+ {
22237
+ variant: "ghost",
22238
+ size: "sm",
22239
+ onClick: () => setVisibleCount((prev) => prev + (pageSize || 5)),
22240
+ children: [
22241
+ /* @__PURE__ */ jsx(Icon, { name: "chevron-down", size: "xs", className: "mr-1" }),
22242
+ t("common.showMore"),
22243
+ " (",
22244
+ allData.length - visibleCount,
22245
+ " remaining)"
22246
+ ]
22247
+ }
22248
+ ) }),
22249
+ infiniteScroll && loadMoreEvent && /* @__PURE__ */ jsx(
22250
+ InfiniteScrollSentinel,
22251
+ {
22252
+ loadMoreEvent,
22253
+ isLoading,
22254
+ hasMore
22255
+ }
22256
+ )
22257
+ ]
22258
+ }
22259
+ );
22260
+ }
22261
+ var init_DataList = __esm({
22262
+ "components/molecules/DataList.tsx"() {
22263
+ "use client";
22264
+ init_cn();
22265
+ init_getNestedValue();
22266
+ init_useEventBus();
22267
+ init_useTranslate();
22268
+ init_Box();
22269
+ init_Stack();
22270
+ init_Typography();
22271
+ init_Badge();
22272
+ init_Button();
22273
+ init_Icon();
22274
+ init_ProgressBar();
22275
+ init_Divider();
22276
+ init_InfiniteScrollSentinel();
22278
22277
  DataList.displayName = "DataList";
22279
22278
  }
22280
22279
  });
@@ -23475,7 +23474,10 @@ var init_WizardProgress = __esm({
23475
23474
  stepClickEvent
23476
23475
  }) => {
23477
23476
  const eventBus = useEventBus();
23478
- const totalSteps = steps.length;
23477
+ const normalizedSteps = steps.map(
23478
+ (s, i) => typeof s === "string" ? { id: `step-${i}`, title: s } : s
23479
+ );
23480
+ const totalSteps = normalizedSteps.length;
23479
23481
  const handleStepClick = (index) => {
23480
23482
  const isCompleted = index < currentStep;
23481
23483
  if (isCompleted && allowNavigation) {
@@ -23492,7 +23494,7 @@ var init_WizardProgress = __esm({
23492
23494
  compact ? "px-4 py-2" : "px-6 py-4",
23493
23495
  className
23494
23496
  ),
23495
- children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: steps.map((step, index) => {
23497
+ children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: normalizedSteps.map((step, index) => {
23496
23498
  const isActive = index === currentStep;
23497
23499
  const isCompleted = index < currentStep;
23498
23500
  return /* @__PURE__ */ jsxs(React126__default.Fragment, { children: [
@@ -50986,18 +50988,12 @@ init_useUISlots();
50986
50988
 
50987
50989
  // hooks/useUIEvents.ts
50988
50990
  init_useEventBus();
50989
- createContext(null);
50990
50991
 
50991
50992
  // hooks/index.ts
50992
50993
  init_useQuerySingleton();
50993
50994
 
50994
- // lib/api-client.ts
50995
- typeof process !== "undefined" && process.env?.VITE_API_URL ? process.env.VITE_API_URL : "/api";
50996
-
50997
50995
  // hooks/index.ts
50998
50996
  init_useTranslate();
50999
-
51000
- // hooks/index.ts
51001
50997
  init_useAuthContext();
51002
50998
  init_useSwipeGesture();
51003
50999
  init_useLongPress();
@@ -51568,33 +51564,66 @@ function useTraitStateMachine(traitBindings, slotsActions, options) {
51568
51564
  canHandleEvent
51569
51565
  };
51570
51566
  }
51567
+ var DEFAULT_SOURCE_KEY = "__default__";
51568
+ function slotEntriesInOrder(slot) {
51569
+ if (!slot) return [];
51570
+ const out = [];
51571
+ for (const [sourceKey, entry] of Object.entries(slot)) {
51572
+ if (entry.patterns.length > 0) {
51573
+ out.push({ sourceKey, entry });
51574
+ }
51575
+ }
51576
+ return out;
51577
+ }
51571
51578
  var SlotsStateContext = createContext({});
51572
51579
  var SlotsActionsContext = createContext(null);
51573
51580
  function SlotsProvider({ children }) {
51574
51581
  const [slots, setSlots] = useState({});
51575
51582
  const setSlotPatterns = useCallback((slot, patterns, source) => {
51576
- setSlots((prev) => ({
51577
- ...prev,
51578
- [slot]: { patterns, source }
51579
- }));
51583
+ const sourceKey = source?.trait ?? DEFAULT_SOURCE_KEY;
51584
+ setSlots((prev) => {
51585
+ const prevSlot = prev[slot] ?? {};
51586
+ return {
51587
+ ...prev,
51588
+ [slot]: {
51589
+ ...prevSlot,
51590
+ [sourceKey]: { patterns, source }
51591
+ }
51592
+ };
51593
+ });
51580
51594
  }, []);
51581
51595
  const clearSlot = useCallback((slot) => {
51582
51596
  setSlots((prev) => {
51583
51597
  const existing = prev[slot];
51584
- if (existing && existing.patterns.length === 0 && !existing.source) {
51598
+ if (existing && Object.keys(existing).length === 0) {
51585
51599
  return prev;
51586
51600
  }
51587
- return { ...prev, [slot]: { patterns: [] } };
51601
+ return { ...prev, [slot]: {} };
51602
+ });
51603
+ }, []);
51604
+ const clearSlotForSource = useCallback((slot, sourceTrait) => {
51605
+ setSlots((prev) => {
51606
+ const existing = prev[slot];
51607
+ if (!existing || !(sourceTrait in existing)) return prev;
51608
+ const next = { ...existing };
51609
+ delete next[sourceTrait];
51610
+ return { ...prev, [slot]: next };
51588
51611
  });
51589
51612
  }, []);
51590
51613
  const clearAllSlots = useCallback(() => {
51591
51614
  setSlots({});
51592
51615
  }, []);
51593
- const actionsRef = useRef({ setSlotPatterns, clearSlot, clearAllSlots });
51594
- actionsRef.current = { setSlotPatterns, clearSlot, clearAllSlots };
51616
+ const actionsRef = useRef({
51617
+ setSlotPatterns,
51618
+ clearSlot,
51619
+ clearSlotForSource,
51620
+ clearAllSlots
51621
+ });
51622
+ actionsRef.current = { setSlotPatterns, clearSlot, clearSlotForSource, clearAllSlots };
51595
51623
  const [stableActions] = useState(() => ({
51596
51624
  setSlotPatterns: (...args) => actionsRef.current.setSlotPatterns(...args),
51597
51625
  clearSlot: (...args) => actionsRef.current.clearSlot(...args),
51626
+ clearSlotForSource: (...args) => actionsRef.current.clearSlotForSource(...args),
51598
51627
  clearAllSlots: () => actionsRef.current.clearAllSlots()
51599
51628
  }));
51600
51629
  return /* @__PURE__ */ jsx(SlotsActionsContext.Provider, { value: stableActions, children: /* @__PURE__ */ jsx(SlotsStateContext.Provider, { value: slots, children }) });
@@ -51830,34 +51859,60 @@ function SlotBridge() {
51830
51859
  const { render, clear } = useUISlots();
51831
51860
  useEffect(() => {
51832
51861
  for (const [slotName, slotState] of Object.entries(slots)) {
51833
- if (slotState.patterns.length === 0) {
51862
+ const entries = slotEntriesInOrder(slotState);
51863
+ if (entries.length === 0) {
51834
51864
  clear(slotName);
51835
51865
  continue;
51836
51866
  }
51837
- const entry = slotState.patterns[slotState.patterns.length - 1];
51838
- const patternRecord = entry.pattern;
51839
- const { type: patternType, children, ...inlineProps } = patternRecord;
51840
- const normalizedChildren = Array.isArray(children) ? children.map((c) => normalizeChild(c)) : children;
51841
- render({
51842
- target: slotName,
51843
- pattern: patternType,
51844
- props: {
51867
+ const children = entries.map(({ entry }) => entry.patterns[entry.patterns.length - 1]).filter((p2) => Boolean(p2)).map((entry) => {
51868
+ const record = entry.pattern;
51869
+ const { type: patternType, children: nested, ...inlineProps } = record;
51870
+ const normalizedNested = Array.isArray(nested) ? nested.map((c) => normalizeChild(c)) : nested;
51871
+ return {
51872
+ type: patternType,
51845
51873
  ...inlineProps,
51846
51874
  ...entry.props,
51847
- ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
51848
- },
51849
- sourceTrait: slotState.source?.trait
51875
+ ...normalizedNested !== void 0 ? { children: normalizedNested } : {}
51876
+ };
51850
51877
  });
51878
+ if (children.length === 1) {
51879
+ const only = children[0];
51880
+ const { type, children: nested, ...rest } = only;
51881
+ const lastEntry = entries[entries.length - 1];
51882
+ render({
51883
+ target: slotName,
51884
+ pattern: type,
51885
+ props: {
51886
+ ...rest,
51887
+ ...nested !== void 0 ? { children: nested } : {}
51888
+ },
51889
+ sourceTrait: lastEntry.entry.source?.trait
51890
+ });
51891
+ } else {
51892
+ const lastEntry = entries[entries.length - 1];
51893
+ render({
51894
+ target: slotName,
51895
+ pattern: "stack",
51896
+ props: {
51897
+ direction: "vertical",
51898
+ gap: "lg",
51899
+ children
51900
+ },
51901
+ sourceTrait: lastEntry.entry.source?.trait
51902
+ });
51903
+ }
51851
51904
  }
51852
51905
  }, [slots, render, clear]);
51853
51906
  return null;
51854
51907
  }
51855
51908
  function applyServerEffects(effects, uiSlots, onNavigate) {
51909
+ const perSlotRenders = /* @__PURE__ */ new Map();
51856
51910
  for (const eff of effects) {
51857
51911
  if (eff.type === "render-ui" && eff.slot && eff.pattern) {
51858
51912
  const patternRecord = eff.pattern;
51859
51913
  const { type: patternType, children, ...inlineProps } = patternRecord;
51860
51914
  const normalizedChildren = Array.isArray(children) ? children.map((c) => normalizeChild(c)) : children;
51915
+ const sourceTrait = eff.traitName ?? "server";
51861
51916
  uiSlots.render({
51862
51917
  target: eff.slot,
51863
51918
  pattern: patternType,
@@ -51865,12 +51920,38 @@ function applyServerEffects(effects, uiSlots, onNavigate) {
51865
51920
  ...inlineProps,
51866
51921
  ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
51867
51922
  },
51868
- sourceTrait: eff.traitName ?? "server"
51923
+ sourceTrait
51924
+ });
51925
+ const bucket = perSlotRenders.get(eff.slot) ?? [];
51926
+ bucket.push({
51927
+ sourceTrait,
51928
+ pattern: {
51929
+ type: patternType,
51930
+ ...inlineProps,
51931
+ ...normalizedChildren !== void 0 ? { children: normalizedChildren } : {}
51932
+ }
51869
51933
  });
51934
+ perSlotRenders.set(eff.slot, bucket);
51870
51935
  } else if (eff.type === "navigate" && eff.route && onNavigate) {
51871
51936
  onNavigate(eff.route, eff.params);
51872
51937
  }
51873
51938
  }
51939
+ for (const [slot, bucket] of perSlotRenders) {
51940
+ const distinctSources = new Set(bucket.map((b) => b.sourceTrait));
51941
+ if (distinctSources.size <= 1) continue;
51942
+ uiSlots.render({
51943
+ target: slot,
51944
+ pattern: "stack",
51945
+ props: {
51946
+ direction: "vertical",
51947
+ gap: "lg",
51948
+ children: bucket.map((b) => b.pattern)
51949
+ },
51950
+ // Use a synthetic wrapper source trait; individual traits' frames
51951
+ // already live in the per-trait index from the per-effect loop.
51952
+ sourceTrait: "__multi_source_stack__"
51953
+ });
51954
+ }
51874
51955
  }
51875
51956
  function TraitInitializer({ traits: traits2, orbitalNames, onNavigate, onLocalFallback, persistence }) {
51876
51957
  const slotsActions = useSlotsActions();