@hortiview/shared-components 2.12.1 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [2.13.0](https://dev.azure.com/sdundc/HV%20Platform/_git/HortiView-Frontend-Shared/compare/v2.12.2...v2.13.0) (2026-02-06)
2
+
3
+ ### Features
4
+
5
+ * add isDeletePending prop to DeleteModal for loading state ([d513159](https://dev.azure.com/sdundc/HV%20Platform/_git/HortiView-Frontend-Shared/commit/d51315909b7de18462ca79da88fd36260f621780))
6
+
7
+ ## [2.12.2](https://dev.azure.com/sdundc/HV%20Platform/_git/HortiView-Frontend-Shared/compare/v2.12.1...v2.12.2) (2026-02-03)
8
+
9
+ ### Code Refactoring
10
+
11
+ * return permissions instead of roles ([2e41e46](https://dev.azure.com/sdundc/HV%20Platform/_git/HortiView-Frontend-Shared/commit/2e41e4668b3cf24cd4c5c2481f0d1871d753b236))
12
+
1
13
  ## [2.12.1](https://dev.azure.com/sdundc/HV%20Platform/_git/HortiView-Frontend-Shared/compare/v2.12.0...v2.12.1) (2026-02-03)
2
14
 
3
15
  ### Code Refactoring
package/README.md CHANGED
@@ -205,6 +205,12 @@ const actions = [
205
205
 
206
206
  A modal to confirm a deletion.
207
207
 
208
+ It provides a loading state for the delete button to prevent multiple deletion attempts in a row. In this case, the button is disabled and a spinner is shown in the button.
209
+
210
+ If a deletion is not possible, the button is also disabled and the specified `impossibleDeleteHeader` is shown.
211
+
212
+ The button is also disabled when the user is offline, indicated by the `isOnline` prop. In this case, the [OfflineView](#offlineview) is shown.
213
+
208
214
  ```jsx
209
215
  import { DeleteModal } from '@hortiview/shared-components';
210
216
 
@@ -222,9 +228,11 @@ const onDelete = () => {
222
228
  confirmButtonLabel={'Remove'}
223
229
  cancelButtonLabel={'Cancel'}
224
230
  deleteHeader={'Delete my block'}
225
- impossibleDeleteHeader={'block cant be deleted'}
231
+ impossibleDeleteHeader={'Block cannot be deleted'}
226
232
  deleteBody={['block 1', 'block 2', 'block 3']}
227
233
  isDeletePossible
234
+ isDeletePending={isDeleteRequestPending}
235
+ isOnline={isOnline}
228
236
  />;
229
237
  ```
230
238
 
@@ -60,6 +60,10 @@ type DeleteModalProps = {
60
60
  * Default is _true_.
61
61
  */
62
62
  isDeletePossible?: boolean;
63
+ /**
64
+ * Whether the delete action is pending; shows a loading state on the confirm button when true.
65
+ */
66
+ isDeletePending?: boolean;
63
67
  /**
64
68
  * Whether the user is online; shows OfflineView when false.
65
69
  */
@@ -85,9 +89,10 @@ type DeleteModalProps = {
85
89
  * @prop {function} onDelete - The function to call when the confirm button is clicked.
86
90
  * @prop {function} onCancel - A optional function to call when the cancel button is clicked.
87
91
  * @prop {boolean} isDeletePossible - If true, the delete button will be enabled. If false, the delete button will be disabled and the {@link impossibleDeleteHeader} will be displayed. Default is _true_.
92
+ * @prop {boolean} isDeletePending - Whether the delete action is pending; shows a loading state on the confirm button when true.
88
93
  * @prop {boolean} isOnline - Whether the user is online; shows OfflineView when false.
89
94
  * @prop {Partial} offlineViewProps - Props forwarded to OfflineView when offline.
90
95
  * @returns {ReactElement} A modal component for deleting items.
91
96
  */
92
- export declare const DeleteModal: ({ title, confirmButtonLabel, cancelButtonLabel, deleteHeader, deleteBody, deleteText, icon, isIconCrossedOut, impossibleDeleteHeader, open, setOpen, onDelete, onCancel, isDeletePossible, isOnline, offlineViewProps, }: DeleteModalProps) => import("react/jsx-runtime").JSX.Element;
97
+ export declare const DeleteModal: ({ title, confirmButtonLabel, cancelButtonLabel, deleteHeader, deleteBody, deleteText, icon, isIconCrossedOut, impossibleDeleteHeader, open, setOpen, onDelete, onCancel, isDeletePossible, isDeletePending, isOnline, offlineViewProps, }: DeleteModalProps) => import("react/jsx-runtime").JSX.Element;
93
98
  export {};
@@ -1,112 +1,127 @@
1
- import { jsx as o, jsxs as f, Fragment as c } from "react/jsx-runtime";
2
- import { B as p } from "../../index.es-B8p8KCdj.js";
3
- import { G as i } from "../../index.es-oxil0uFe.js";
4
- import { I } from "../../index.es-Dgxrk6xW.js";
1
+ import { jsx as o, jsxs as b, Fragment as c } from "react/jsx-runtime";
2
+ import { B as f } from "../../index.es-B8p8KCdj.js";
3
+ import { G as m } from "../../index.es-oxil0uFe.js";
4
+ import { I as S } from "../../index.es-Dgxrk6xW.js";
5
5
  import { T as l } from "../../index.es-SjZ_-d7U.js";
6
- import { u as N } from "../../uniqueId-BNVTeImh.js";
7
- import { AvailableCustomIcons as S } from "../../enums/AvailableCustomIcons.js";
8
- import { u as x } from "../../useBreakpoints-MzTZ0tCT.js";
9
- import { Iconify as B } from "../Iconify/Iconify.js";
6
+ import { u as x } from "../../uniqueId-BNVTeImh.js";
7
+ import { AvailableCustomIcons as N } from "../../enums/AvailableCustomIcons.js";
8
+ import { u as O } from "../../useBreakpoints-MzTZ0tCT.js";
9
+ import { Iconify as z } from "../Iconify/Iconify.js";
10
+ import "react";
11
+ import "../../react-tooltip.min-CVsI--2Y.js";
12
+ import "../../orderBy-DLBJlgdL.js";
13
+ import { LoadingSpinner as B } from "../LoadingSpinner/Default/LoadingSpinner.js";
10
14
  import { Modal as M } from "../Modal/Modal.js";
11
- import '../../assets/DeleteModal.css';const O = "_bulletPoint_bd412_1", j = "_modal_bd412_7", z = "_colorDanger_bd412_11", P = "_crossedOut_bd412_15", t = {
12
- bulletPoint: O,
13
- modal: j,
14
- colorDanger: z,
15
- crossedOut: P
16
- }, V = ({
15
+ import "react-hook-form";
16
+ import "../../get-D8IXqiys.js";
17
+ import "../../isArray-BjSPDQ4v.js";
18
+ import "../../isString-DVA49FEo.js";
19
+ import "../../omit-CYY_mIYv.js";
20
+ import "../../types/Time.js";
21
+ import "../../index-CuHybtft.js";
22
+ import "../SharedComponentsPermissionProvider/PermissionContext.js";
23
+ import "../../react.esm-Bm0cAgpZ.js";
24
+ import '../../assets/DeleteModal.css';const j = "_bulletPoint_bd412_1", A = "_modal_bd412_7", G = "_colorDanger_bd412_11", q = "_crossedOut_bd412_15", e = {
25
+ bulletPoint: j,
26
+ modal: A,
27
+ colorDanger: G,
28
+ crossedOut: q
29
+ }, co = ({
17
30
  title: r,
18
- confirmButtonLabel: e,
31
+ confirmButtonLabel: t,
19
32
  cancelButtonLabel: n,
20
33
  deleteHeader: a,
21
- deleteBody: s,
22
- deleteText: b,
23
- icon: g,
24
- isIconCrossedOut: h,
25
- impossibleDeleteHeader: v,
34
+ deleteBody: i,
35
+ deleteText: g,
36
+ icon: h,
37
+ isIconCrossedOut: v,
38
+ impossibleDeleteHeader: y,
26
39
  open: _,
27
40
  setOpen: d,
28
- onDelete: y,
29
- onCancel: C,
30
- isDeletePossible: m = !0,
31
- isOnline: u = !0,
32
- offlineViewProps: D
41
+ onDelete: C,
42
+ onCancel: I,
43
+ isDeletePossible: u = !0,
44
+ isDeletePending: s = !1,
45
+ isOnline: p = !0,
46
+ offlineViewProps: k
33
47
  }) => {
34
- const { isDesktop: k } = x();
48
+ const { isDesktop: D } = O();
35
49
  return /* @__PURE__ */ o(
36
50
  M,
37
51
  {
38
- isOnline: u,
39
- offlineViewProps: D,
40
- className: `${t.modal}`,
41
- modalSize: k ? "small" : "fullscreen",
52
+ isOnline: p,
53
+ offlineViewProps: k,
54
+ className: `${e.modal}`,
55
+ modalSize: D ? "small" : "fullscreen",
42
56
  open: _,
43
57
  title: r,
44
58
  onClose: () => d(!1),
45
59
  actionButton: /* @__PURE__ */ o(
46
- p,
60
+ f,
47
61
  {
48
62
  "data-testid": "delete-button",
49
63
  variant: "danger",
50
- label: e,
51
- onClick: y,
52
- disabled: !m || !u
64
+ label: s ? "" : t,
65
+ onClick: C,
66
+ disabled: !u || s || !p,
67
+ trailingIcon: s ? /* @__PURE__ */ o(B, { size: "sm", text: "", spinnerOnly: !0 }) : null
53
68
  }
54
69
  ),
55
70
  footerSupplemental: /* @__PURE__ */ o(
56
- p,
71
+ f,
57
72
  {
58
73
  "data-testid": "cancel-button",
59
74
  variant: "text",
60
75
  label: n,
61
76
  onClick: () => {
62
- C?.(), d(!1);
77
+ I?.(), d(!1);
63
78
  }
64
79
  }
65
80
  ),
66
- children: /* @__PURE__ */ f(i, { direction: "vertical", secondaryAlign: "center", children: [
67
- /* @__PURE__ */ o(A, { element: /* @__PURE__ */ o(G, { icon: g }), isCrossedOut: h }),
68
- /* @__PURE__ */ o(i, { direction: "vertical", children: m ? /* @__PURE__ */ o(
69
- q,
81
+ children: /* @__PURE__ */ b(m, { direction: "vertical", secondaryAlign: "center", children: [
82
+ /* @__PURE__ */ o(F, { element: /* @__PURE__ */ o(H, { icon: h }), isCrossedOut: v }),
83
+ /* @__PURE__ */ o(m, { direction: "vertical", children: u ? /* @__PURE__ */ o(
84
+ L,
70
85
  {
71
86
  deleteHeader: a,
72
- deleteText: b,
73
- deleteBody: s
87
+ deleteText: g,
88
+ deleteBody: i
74
89
  }
75
- ) : /* @__PURE__ */ o(F, { impossibleDeleteHeader: v }) })
90
+ ) : /* @__PURE__ */ o(P, { impossibleDeleteHeader: y }) })
76
91
  ] })
77
92
  }
78
93
  );
79
- }, A = ({
94
+ }, F = ({
80
95
  element: r,
81
- isCrossedOut: e = !1
82
- }) => e ? /* @__PURE__ */ o("span", { className: t.crossedOut, children: r }) : r, G = ({ icon: r }) => r ? typeof r != "string" ? r : Object.values(S).includes(r) ? /* @__PURE__ */ o(
83
- B,
96
+ isCrossedOut: t = !1
97
+ }) => t ? /* @__PURE__ */ o("span", { className: e.crossedOut, children: r }) : r, H = ({ icon: r }) => r ? typeof r != "string" ? r : Object.values(N).includes(r) ? /* @__PURE__ */ o(
98
+ z,
84
99
  {
85
100
  icon: r,
86
101
  iconSize: "xlarge",
87
- className: t.colorDanger
102
+ className: e.colorDanger
88
103
  }
89
- ) : /* @__PURE__ */ o(I, { icon: r, iconSize: "xlarge", className: t.colorDanger }) : /* @__PURE__ */ o(c, {}), q = ({
104
+ ) : /* @__PURE__ */ o(S, { icon: r, iconSize: "xlarge", className: e.colorDanger }) : /* @__PURE__ */ o(c, {}), L = ({
90
105
  deleteHeader: r,
91
- deleteText: e,
106
+ deleteText: t,
92
107
  deleteBody: n
93
- }) => /* @__PURE__ */ f(c, { children: [
108
+ }) => /* @__PURE__ */ b(c, { children: [
94
109
  /* @__PURE__ */ o(l, { level: 1, themeColor: "text-primary-on-background", children: r }),
95
- e && typeof e == "string" && /* @__PURE__ */ o(l, { level: 2, themeColor: "text-secondary-on-background", children: e }),
96
- e && typeof e != "string" && /* @__PURE__ */ o(c, { children: e }),
97
- n && e === void 0 && /* @__PURE__ */ o(i, { direction: "vertical", gap: "none", children: n.map((a, s) => /* @__PURE__ */ o(
110
+ t && typeof t == "string" && /* @__PURE__ */ o(l, { level: 2, themeColor: "text-secondary-on-background", children: t }),
111
+ t && typeof t != "string" && /* @__PURE__ */ o(c, { children: t }),
112
+ n && t === void 0 && /* @__PURE__ */ o(m, { direction: "vertical", gap: "none", children: n.map((a, i) => /* @__PURE__ */ o(
98
113
  l,
99
114
  {
100
115
  level: 2,
101
- className: t.bulletPoint,
116
+ className: e.bulletPoint,
102
117
  themeColor: "text-secondary-on-background",
103
118
  children: a
104
119
  },
105
- N(s.toString())
120
+ x(i.toString())
106
121
  )) })
107
- ] }), F = ({
122
+ ] }), P = ({
108
123
  impossibleDeleteHeader: r
109
124
  }) => /* @__PURE__ */ o(l, { level: 1, themeColor: "text-primary-on-background", children: r });
110
125
  export {
111
- V as DeleteModal
126
+ co as DeleteModal
112
127
  };
@@ -1,12 +1,12 @@
1
1
  import { jsx as n } from "react/jsx-runtime";
2
- import { a as c, s as e } from "../../react.esm-Bm0cAgpZ.js";
3
- import { DeleteModal as a } from "./DeleteModal.js";
4
- import { d, t as b, a as o, g as t } from "../../vi.bdSIJ99Y-B308Q-4w.js";
5
- d("DeleteModal Test", () => {
6
- b("should render the DeleteModal properly", () => {
2
+ import { a as c, s as t } from "../../react.esm-Bm0cAgpZ.js";
3
+ import { DeleteModal as b } from "./DeleteModal.js";
4
+ import { d as a, t as s, a as o, g as e } from "../../vi.bdSIJ99Y-B308Q-4w.js";
5
+ a("DeleteModal Test", () => {
6
+ s("should render the DeleteModal properly", () => {
7
7
  c(
8
8
  /* @__PURE__ */ n(
9
- a,
9
+ b,
10
10
  {
11
11
  icon: "block",
12
12
  open: !0,
@@ -21,11 +21,11 @@ d("DeleteModal Test", () => {
21
21
  isDeletePossible: !0
22
22
  }
23
23
  )
24
- ), t(e.getByText("DELETE")).toBeInTheDocument(), t(e.getByTestId("block")).toBeInTheDocument(), t(e.getByText("block 1")).toBeInTheDocument(), t(e.getByText("block 2")).toBeInTheDocument(), t(e.getByText("block 3")).toBeInTheDocument(), t(e.getByText("Remove")).toBeInTheDocument(), t(e.getByText("Cancel")).toBeInTheDocument(), t(e.queryByText("block cant be deleted")).not.toBeInTheDocument();
25
- }), b("should render the DeleteModal properly when isDeletePossible = false", () => {
24
+ ), e(t.getByText("DELETE")).toBeInTheDocument(), e(t.getByTestId("block")).toBeInTheDocument(), e(t.getByText("block 1")).toBeInTheDocument(), e(t.getByText("block 2")).toBeInTheDocument(), e(t.getByText("block 3")).toBeInTheDocument(), e(t.getByText("Remove")).toBeInTheDocument(), e(t.getByText("Cancel")).toBeInTheDocument(), e(t.queryByText("block cant be deleted")).not.toBeInTheDocument();
25
+ }), s("should render the DeleteModal properly when isDeletePossible = false", () => {
26
26
  c(
27
27
  /* @__PURE__ */ n(
28
- a,
28
+ b,
29
29
  {
30
30
  icon: "block",
31
31
  open: !0,
@@ -40,12 +40,12 @@ d("DeleteModal Test", () => {
40
40
  isDeletePossible: !1
41
41
  }
42
42
  )
43
- ), t(e.getByText("DELETE")).toBeInTheDocument(), t(e.getByTestId("block")).toBeInTheDocument(), t(e.queryByText("block 1")).not.toBeInTheDocument(), t(e.queryByText("block 2")).not.toBeInTheDocument(), t(e.queryByText("block 3")).not.toBeInTheDocument(), t(e.getByText("Remove")).toBeInTheDocument(), t(e.getByText("Remove").parentElement).toBeDisabled(), t(e.getByText("Cancel")).toBeInTheDocument(), t(e.queryByText("block cant be deleted")).toBeInTheDocument();
44
- }), b("should call setOpen with false when clicking on cancel button", () => {
43
+ ), e(t.getByText("DELETE")).toBeInTheDocument(), e(t.getByTestId("block")).toBeInTheDocument(), e(t.queryByText("block 1")).not.toBeInTheDocument(), e(t.queryByText("block 2")).not.toBeInTheDocument(), e(t.queryByText("block 3")).not.toBeInTheDocument(), e(t.getByText("Remove")).toBeInTheDocument(), e(t.getByText("Remove").parentElement).toBeDisabled(), e(t.getByText("Cancel")).toBeInTheDocument(), e(t.queryByText("block cant be deleted")).toBeInTheDocument();
44
+ }), s("should call setOpen with false when clicking on cancel button", () => {
45
45
  const l = o.fn();
46
46
  c(
47
47
  /* @__PURE__ */ n(
48
- a,
48
+ b,
49
49
  {
50
50
  icon: "block",
51
51
  open: !0,
@@ -60,12 +60,12 @@ d("DeleteModal Test", () => {
60
60
  isDeletePossible: !0
61
61
  }
62
62
  )
63
- ), e.getByText("Cancel").click(), t(l).toHaveBeenCalledWith(!1);
64
- }), b("should call onDelete when clicking on delete button", () => {
63
+ ), t.getByText("Cancel").click(), e(l).toHaveBeenCalledWith(!1);
64
+ }), s("should call onDelete when clicking on delete button", () => {
65
65
  const l = o.fn();
66
66
  c(
67
67
  /* @__PURE__ */ n(
68
- a,
68
+ b,
69
69
  {
70
70
  icon: "block",
71
71
  open: !0,
@@ -80,12 +80,12 @@ d("DeleteModal Test", () => {
80
80
  isDeletePossible: !0
81
81
  }
82
82
  )
83
- ), e.getByText("Remove").click(), t(l).toHaveBeenCalled();
84
- }), b("render DeleteModal with OfflineView", () => {
83
+ ), t.getByText("Remove").click(), e(l).toHaveBeenCalled();
84
+ }), s("render DeleteModal with OfflineView", () => {
85
85
  const l = o.fn();
86
86
  c(
87
87
  /* @__PURE__ */ n(
88
- a,
88
+ b,
89
89
  {
90
90
  icon: "block",
91
91
  open: !0,
@@ -104,6 +104,29 @@ d("DeleteModal Test", () => {
104
104
  }
105
105
  }
106
106
  )
107
- ), e.getByText("Remove").click(), t(l).not.toHaveBeenCalled(), t(e.getByTestId("offline-test")).toBeInTheDocument();
107
+ ), t.getByText("Remove").click(), e(l).not.toHaveBeenCalled(), e(t.getByTestId("offline-test")).toBeInTheDocument();
108
+ }), s("should disable delete button when isDeletePending is true", () => {
109
+ const l = o.fn();
110
+ c(
111
+ /* @__PURE__ */ n(
112
+ b,
113
+ {
114
+ icon: "block",
115
+ open: !0,
116
+ setOpen: o.fn(),
117
+ onDelete: l,
118
+ title: "DELETE",
119
+ confirmButtonLabel: "Remove",
120
+ cancelButtonLabel: "Cancel",
121
+ deleteHeader: "Delete my block",
122
+ impossibleDeleteHeader: "block cant be deleted",
123
+ deleteBody: ["block 1", "block 2", "block 3"],
124
+ isDeletePossible: !0,
125
+ isDeletePending: !0
126
+ }
127
+ )
128
+ );
129
+ const d = t.getByTestId("delete-button");
130
+ e(d).toBeDisabled(), e(t.getByTestId("loading-spinner")).toBeInTheDocument(), d.click(), e(l).not.toHaveBeenCalled();
108
131
  });
109
132
  });
@@ -1,17 +1,17 @@
1
- import { j as u } from "../../index-CuHybtft.js";
1
+ import { j as l } from "../../index-CuHybtft.js";
2
2
  import { useCallback as a } from "react";
3
3
  import { usePermissionContext as c } from "../SharedComponentsPermissionProvider/PermissionContext.js";
4
- const p = () => {
4
+ const y = () => {
5
5
  const { userPermissions: s, activeOrganizationId: e } = c();
6
6
  return a(
7
- (t, o, l = !1, m) => {
7
+ (t, o, m = !1, u) => {
8
8
  if (t.length === 0) return !0;
9
- const r = m ?? s, n = [
9
+ const r = u ?? s, n = [
10
10
  ...r?.[o ?? e ?? ""] ?? [],
11
11
  ...r?.General ?? [],
12
12
  ...r?.Plattform ?? []
13
13
  ];
14
- return n ? l ? t.every((i) => n.includes(i)) : t.some((i) => n.includes(i)) : !1;
14
+ return n ? m ? t.every((i) => n.includes(i)) : t.some((i) => n.includes(i)) : !1;
15
15
  },
16
16
  [e, s]
17
17
  );
@@ -19,10 +19,10 @@ const p = () => {
19
19
  permissionToken: s
20
20
  }) => {
21
21
  if (!s || s === "") return;
22
- const e = u(s), t = e.ModuleRolesAndPermissions.FarmOrganizationId, o = e.ModuleRolesAndPermissions.RoleNames;
22
+ const e = l(s), t = e.ModuleRolesAndPermissions.FarmOrganizationId, o = e.ModuleRolesAndPermissions.ExecuteEndpointPermissionNames;
23
23
  return { [t]: [...o] };
24
24
  };
25
25
  export {
26
26
  I as getPermissionsFromModulePermissionToken,
27
- p as useIsAllowed
27
+ y as useIsAllowed
28
28
  };
@@ -66,7 +66,7 @@ eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJNb2R1bGVSb2xlc0FuZFBlcm1pc3Npb25zIjp7IkZ
66
66
  permissionToken: n
67
67
  });
68
68
  o(e).toEqual({
69
- "2fd2c0b0-5061-4d45-ac25-d1ca4acd7338": ["Farm Manager", "Farm Worker"]
69
+ "2fd2c0b0-5061-4d45-ac25-d1ca4acd7338": ["blocks-read", "blocks-update", "farms-read"]
70
70
  });
71
71
  }), t("should return undefined when permission token is empty", () => {
72
72
  const e = a({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hortiview/shared-components",
3
3
  "description": "This is a shared component library. It should used in the HortiView platform and its modules.",
4
- "version": "2.12.1",
4
+ "version": "2.13.0",
5
5
  "type": "module",
6
6
  "repository": "https://dev.azure.com/sdundc/HV%20Platform/_git/HortiView-Frontend-Shared",
7
7
  "author": "Falk Menge <falk.menge.ext@bayer.com>",