@almadar/ui 5.128.0 → 5.129.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.
@@ -27388,12 +27388,12 @@ var init_MapView = __esm({
27388
27388
  shadowSize: [41, 41]
27389
27389
  });
27390
27390
  L.Marker.prototype.options.icon = defaultIcon;
27391
- const { useEffect: useEffect62, useRef: useRef59, useCallback: useCallback93, useState: useState91 } = React84__namespace.default;
27391
+ const { useEffect: useEffect62, useRef: useRef60, useCallback: useCallback93, useState: useState91 } = React84__namespace.default;
27392
27392
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
27393
27393
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
27394
27394
  function MapUpdater({ centerLat, centerLng, zoom }) {
27395
27395
  const map = useMap();
27396
- const prevRef = useRef59({ centerLat, centerLng, zoom });
27396
+ const prevRef = useRef60({ centerLat, centerLng, zoom });
27397
27397
  useEffect62(() => {
27398
27398
  const prev = prevRef.current;
27399
27399
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
@@ -36583,6 +36583,290 @@ var init_GraphCanvas = __esm({
36583
36583
  GraphCanvas.displayName = "GraphCanvas";
36584
36584
  }
36585
36585
  });
36586
+ var ImportSourcePicker;
36587
+ var init_ImportSourcePicker = __esm({
36588
+ "components/core/molecules/import/ImportSourcePicker.tsx"() {
36589
+ "use client";
36590
+ init_Box();
36591
+ init_Icon();
36592
+ init_Typography();
36593
+ init_cn();
36594
+ ImportSourcePicker = ({
36595
+ sources,
36596
+ onSelect,
36597
+ onFilesSelected,
36598
+ title,
36599
+ moreSources,
36600
+ className
36601
+ }) => {
36602
+ const fileInputRef = React84.useRef(null);
36603
+ const handlePick = (source) => {
36604
+ if (source.disabled) return;
36605
+ if (source.kind === "file") {
36606
+ const input = fileInputRef.current;
36607
+ if (!input) return;
36608
+ input.accept = source.accept ?? "";
36609
+ input.multiple = source.multiple ?? false;
36610
+ input.click();
36611
+ return;
36612
+ }
36613
+ onSelect?.(source.id);
36614
+ };
36615
+ const handleFiles = (event) => {
36616
+ const files = event.target.files ? Array.from(event.target.files) : [];
36617
+ event.target.value = "";
36618
+ if (files.length > 0) onFilesSelected?.(files);
36619
+ };
36620
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("flex flex-col gap-2", className), children: [
36621
+ title ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title }) : null,
36622
+ sources.map((source) => /* @__PURE__ */ jsxRuntime.jsxs(
36623
+ Box,
36624
+ {
36625
+ role: "button",
36626
+ tabIndex: source.disabled ? -1 : 0,
36627
+ "aria-disabled": source.disabled,
36628
+ onClick: () => handlePick(source),
36629
+ onKeyDown: (event) => {
36630
+ if (event.key === "Enter" || event.key === " ") {
36631
+ event.preventDefault();
36632
+ handlePick(source);
36633
+ }
36634
+ },
36635
+ className: cn(
36636
+ "flex items-center gap-3 rounded-md border border-border bg-card px-4 py-3 text-left",
36637
+ source.disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer hover:bg-accent hover:text-accent-foreground"
36638
+ ),
36639
+ children: [
36640
+ source.icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: source.icon, size: "md" }) : null,
36641
+ /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex flex-col", children: [
36642
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "label", children: source.label }),
36643
+ source.description ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: source.description }) : null
36644
+ ] })
36645
+ ]
36646
+ },
36647
+ source.id
36648
+ )),
36649
+ moreSources,
36650
+ /* @__PURE__ */ jsxRuntime.jsx(
36651
+ "input",
36652
+ {
36653
+ ref: fileInputRef,
36654
+ type: "file",
36655
+ className: "hidden",
36656
+ onChange: handleFiles,
36657
+ "data-testid": "import-source-file-input"
36658
+ }
36659
+ )
36660
+ ] });
36661
+ };
36662
+ }
36663
+ });
36664
+ function formatFieldValue(value) {
36665
+ if (value === null) return "\u2014";
36666
+ if (value instanceof Date) return value.toISOString();
36667
+ if (Array.isArray(value)) return value.map(formatFieldValue).join(", ");
36668
+ if (typeof value === "object") return JSON.stringify(value);
36669
+ return String(value);
36670
+ }
36671
+ function unitTitle(unit) {
36672
+ for (const key of TITLE_KEYS) {
36673
+ const value = unit.fields[key];
36674
+ if (typeof value === "string" && value.length > 0) return value;
36675
+ }
36676
+ return unit.ref;
36677
+ }
36678
+ function fieldSummary(unit) {
36679
+ return Object.entries(unit.fields).filter(([key]) => !TITLE_KEYS.includes(key)).map(([key, value]) => `${key}: ${formatFieldValue(value)}`).join(" \xB7 ");
36680
+ }
36681
+ var TITLE_KEYS, ImportPreviewTree;
36682
+ var init_ImportPreviewTree = __esm({
36683
+ "components/core/molecules/import/ImportPreviewTree.tsx"() {
36684
+ "use client";
36685
+ init_Box();
36686
+ init_Badge();
36687
+ init_Button();
36688
+ init_Icon();
36689
+ init_Typography();
36690
+ init_cn();
36691
+ TITLE_KEYS = ["title", "name", "label"];
36692
+ ImportPreviewTree = ({
36693
+ units,
36694
+ skipped = [],
36695
+ entityDisplay,
36696
+ onConfirm,
36697
+ onCancel,
36698
+ confirmLabel = "Confirm import",
36699
+ cancelLabel = "Cancel",
36700
+ indent = 16,
36701
+ className
36702
+ }) => {
36703
+ const groups = /* @__PURE__ */ new Map();
36704
+ for (const unit of units) {
36705
+ const group = groups.get(unit.targetEntity);
36706
+ if (group) group.push(unit);
36707
+ else groups.set(unit.targetEntity, [unit]);
36708
+ }
36709
+ const renderUnit = (unit, childrenByParent, depth) => {
36710
+ const summary = fieldSummary(unit);
36711
+ const children = childrenByParent.get(unit.ref) ?? [];
36712
+ return /* @__PURE__ */ jsxRuntime.jsxs(React84__namespace.default.Fragment, { children: [
36713
+ /* @__PURE__ */ jsxRuntime.jsxs(
36714
+ Box,
36715
+ {
36716
+ className: "flex flex-col py-1",
36717
+ style: { paddingLeft: depth * indent },
36718
+ "data-testid": `import-preview-unit-${unit.ref}`,
36719
+ children: [
36720
+ /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-2", children: [
36721
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: "file-text", size: "xs", className: "text-muted-foreground" }),
36722
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", children: unitTitle(unit) })
36723
+ ] }),
36724
+ summary ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: summary }) : null
36725
+ ]
36726
+ }
36727
+ ),
36728
+ children.map((child) => renderUnit(child, childrenByParent, depth + 1))
36729
+ ] }, unit.ref);
36730
+ };
36731
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("flex flex-col gap-4", className), children: [
36732
+ units.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: "No units staged." }) : null,
36733
+ Array.from(groups.entries()).map(([entity, groupUnits]) => {
36734
+ const refs = new Set(groupUnits.map((unit) => unit.ref));
36735
+ const childrenByParent = /* @__PURE__ */ new Map();
36736
+ const roots = [];
36737
+ for (const unit of groupUnits) {
36738
+ if (unit.parentRef && refs.has(unit.parentRef)) {
36739
+ const siblings = childrenByParent.get(unit.parentRef);
36740
+ if (siblings) siblings.push(unit);
36741
+ else childrenByParent.set(unit.parentRef, [unit]);
36742
+ } else {
36743
+ roots.push(unit);
36744
+ }
36745
+ }
36746
+ const label = entityDisplay[entity]?.plural ?? entity;
36747
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { border: true, className: "rounded-md border-border bg-card p-3", children: [
36748
+ /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-2 pb-2", children: [
36749
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "label", children: label }),
36750
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { amount: groupUnits.length })
36751
+ ] }),
36752
+ roots.map((unit) => renderUnit(unit, childrenByParent, 0))
36753
+ ] }, entity);
36754
+ }),
36755
+ skipped.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(Box, { border: true, className: "rounded-md border-border bg-card p-3", children: [
36756
+ /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-2 pb-2", children: [
36757
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "label", children: "Skipped" }),
36758
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { amount: skipped.length })
36759
+ ] }),
36760
+ skipped.map((element) => /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-baseline gap-2 py-1", children: [
36761
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", children: element.ref }),
36762
+ /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: element.reason })
36763
+ ] }, element.ref))
36764
+ ] }) : null,
36765
+ onConfirm || onCancel ? /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center justify-end gap-2", children: [
36766
+ onCancel ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "default", label: cancelLabel, onClick: onCancel }) : null,
36767
+ onConfirm ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", label: confirmLabel, onClick: onConfirm }) : null
36768
+ ] }) : null
36769
+ ] });
36770
+ };
36771
+ }
36772
+ });
36773
+ var PIPELINE, DEFAULT_LABELS, ImportProgress;
36774
+ var init_ImportProgress = __esm({
36775
+ "components/core/molecules/import/ImportProgress.tsx"() {
36776
+ "use client";
36777
+ init_Box();
36778
+ init_Badge();
36779
+ init_Icon();
36780
+ init_Typography();
36781
+ init_cn();
36782
+ PIPELINE = [
36783
+ "fetching",
36784
+ "mapping",
36785
+ "reviewing",
36786
+ "committing"
36787
+ ];
36788
+ DEFAULT_LABELS = {
36789
+ fetching: "Fetching",
36790
+ mapping: "Mapping",
36791
+ reviewing: "Reviewing",
36792
+ committing: "Committing",
36793
+ done: "Done",
36794
+ failed: "Failed"
36795
+ };
36796
+ ImportProgress = ({
36797
+ step,
36798
+ counts,
36799
+ labels,
36800
+ className
36801
+ }) => {
36802
+ const label = (key) => labels?.[key] ?? DEFAULT_LABELS[key];
36803
+ const currentIndex = step === "done" || step === "failed" ? PIPELINE.length : PIPELINE.indexOf(step);
36804
+ return /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: cn("flex flex-col gap-3", className), children: [
36805
+ /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-2", children: [
36806
+ PIPELINE.map((key, index) => {
36807
+ const isComplete = index < currentIndex;
36808
+ const isActive = index === currentIndex;
36809
+ return /* @__PURE__ */ jsxRuntime.jsxs(React84__namespace.default.Fragment, { children: [
36810
+ index > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "h-px w-4 bg-border" }) : null,
36811
+ /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-1", "data-testid": `import-progress-step-${key}`, children: [
36812
+ /* @__PURE__ */ jsxRuntime.jsx(
36813
+ Icon,
36814
+ {
36815
+ icon: isComplete ? "check" : isActive ? "loader" : "circle",
36816
+ size: "sm",
36817
+ className: cn(
36818
+ isComplete ? "text-success" : isActive ? "text-primary" : "text-muted-foreground"
36819
+ )
36820
+ }
36821
+ ),
36822
+ /* @__PURE__ */ jsxRuntime.jsx(
36823
+ Typography,
36824
+ {
36825
+ variant: "caption",
36826
+ className: cn(isActive ? "text-foreground" : "text-muted-foreground"),
36827
+ children: label(key)
36828
+ }
36829
+ )
36830
+ ] })
36831
+ ] }, key);
36832
+ }),
36833
+ step === "done" || step === "failed" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
36834
+ /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "h-px w-4 bg-border" }),
36835
+ /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-1", "data-testid": `import-progress-step-${step}`, children: [
36836
+ /* @__PURE__ */ jsxRuntime.jsx(
36837
+ Icon,
36838
+ {
36839
+ icon: step === "done" ? "check" : "x",
36840
+ size: "sm",
36841
+ className: step === "done" ? "text-success" : "text-error"
36842
+ }
36843
+ ),
36844
+ /* @__PURE__ */ jsxRuntime.jsx(
36845
+ Typography,
36846
+ {
36847
+ variant: "caption",
36848
+ className: step === "done" ? "text-success" : "text-error",
36849
+ children: label(step)
36850
+ }
36851
+ )
36852
+ ] })
36853
+ ] }) : null
36854
+ ] }),
36855
+ counts ? /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-2", children: [
36856
+ counts.staged !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { label: `Staged ${counts.staged}` }) : null,
36857
+ counts.committed !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "success", label: `Committed ${counts.committed}` }) : null,
36858
+ counts.failed !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "danger", label: `Failed ${counts.failed}` }) : null
36859
+ ] }) : null
36860
+ ] });
36861
+ };
36862
+ }
36863
+ });
36864
+
36865
+ // components/core/molecules/import/index.ts
36866
+ var init_import = __esm({
36867
+ "components/core/molecules/import/index.ts"() {
36868
+ }
36869
+ });
36586
36870
  var ReflectionBlock;
36587
36871
  var init_ReflectionBlock = __esm({
36588
36872
  "components/core/molecules/ReflectionBlock.tsx"() {
@@ -36657,6 +36941,7 @@ var init_molecules2 = __esm({
36657
36941
  init_EmptyState();
36658
36942
  init_Pagination();
36659
36943
  init_molecules();
36944
+ init_import();
36660
36945
  }
36661
36946
  });
36662
36947
 
@@ -37137,7 +37422,7 @@ function getBadgeVariant(fieldName, value) {
37137
37422
  function formatFieldLabel(fieldName) {
37138
37423
  return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
37139
37424
  }
37140
- function formatFieldValue(value, fieldName) {
37425
+ function formatFieldValue2(value, fieldName) {
37141
37426
  if (typeof value === "number") {
37142
37427
  if (fieldName.toLowerCase().includes("progress") || fieldName.toLowerCase().includes("percent")) {
37143
37428
  return `${value}%`;
@@ -37232,7 +37517,7 @@ function renderRichFieldValue(value, fieldName, fieldType) {
37232
37517
  return str;
37233
37518
  }
37234
37519
  default:
37235
- return formatFieldValue(value, fieldName);
37520
+ return formatFieldValue2(value, fieldName);
37236
37521
  }
37237
37522
  }
37238
37523
  function normalizeFieldDefs(fields) {
@@ -37343,7 +37628,7 @@ var init_DetailPanel = __esm({
37343
37628
  const value = getNestedValue(normalizedData, field);
37344
37629
  return {
37345
37630
  label: labelFor(field),
37346
- value: formatFieldValue(value, field),
37631
+ value: formatFieldValue2(value, field),
37347
37632
  icon: getFieldIcon(field)
37348
37633
  };
37349
37634
  }
@@ -43355,6 +43640,9 @@ var init_component_registry_generated = __esm({
43355
43640
  init_HeroOrganism();
43356
43641
  init_HeroSection();
43357
43642
  init_Icon();
43643
+ init_ImportPreviewTree();
43644
+ init_ImportProgress();
43645
+ init_ImportSourcePicker();
43358
43646
  init_InfiniteScrollSentinel();
43359
43647
  init_Input();
43360
43648
  init_InputGroup();
@@ -43618,6 +43906,9 @@ var init_component_registry_generated = __esm({
43618
43906
  "HeroOrganism": HeroOrganism,
43619
43907
  "HeroSection": HeroSection,
43620
43908
  "Icon": Icon,
43909
+ "ImportPreviewTree": ImportPreviewTree,
43910
+ "ImportProgress": ImportProgress,
43911
+ "ImportSourcePicker": ImportSourcePicker,
43621
43912
  "InfiniteScrollSentinel": InfiniteScrollSentinel,
43622
43913
  "Input": Input,
43623
43914
  "InputGroup": InputGroup,