@hartl-services/medusa-base 2.7.0 → 2.9.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.
Files changed (54) hide show
  1. package/.medusa/server/src/admin/admin-components/features/plugin-features/components/PluginSettingsForm.js +12 -9
  2. package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatureEnabled.d.ts +5 -1
  3. package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginFeatures.d.ts +20 -4
  4. package/.medusa/server/src/admin/admin-components/features/plugin-features/hooks/usePluginSettingsForm.d.ts +5 -1
  5. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-features-admin-client.d.ts +20 -4
  6. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-view.d.ts +0 -5
  7. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/plugin-view.js +0 -5
  8. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.d.ts +4 -0
  9. package/.medusa/server/src/admin/admin-components/features/plugin-features/lib/settings-layout.js +4 -0
  10. package/.medusa/server/src/admin/admin-components/i18n/texts.d.ts +1 -2
  11. package/.medusa/server/src/admin/admin-components/i18n/texts.js +1 -2
  12. package/.medusa/server/src/admin/index.js +142 -87
  13. package/.medusa/server/src/admin/index.mjs +144 -89
  14. package/.medusa/server/src/api/admin/email/templates/[kind]/send-test/route.js +7 -1
  15. package/.medusa/server/src/customer-auth/plugin-features.js +10 -10
  16. package/.medusa/server/src/email/application/warn-credential-expiry.js +77 -0
  17. package/.medusa/server/src/email/config/index.js +8 -2
  18. package/.medusa/server/src/email/config/notification-module.js +28 -2
  19. package/.medusa/server/src/email/domain/business-keys.js +5 -2
  20. package/.medusa/server/src/email/domain/failure-classification.js +9 -1
  21. package/.medusa/server/src/email/plugin-features.js +123 -1
  22. package/.medusa/server/src/email/rendering/default-templates.js +16 -1
  23. package/.medusa/server/src/email/rendering/render-email.js +2 -1
  24. package/.medusa/server/src/email/types/email-kinds.js +2 -1
  25. package/.medusa/server/src/email/types/template-data.js +8 -1
  26. package/.medusa/server/src/jobs/email-credential-expiry.js +27 -0
  27. package/.medusa/server/src/modules/email/domain/message-state.js +3 -1
  28. package/.medusa/server/src/modules/email/migrations/Migration20261011120000.js +20 -0
  29. package/.medusa/server/src/modules/plugin-features/domain/plugin-settings.js +27 -6
  30. package/.medusa/server/src/modules/plugin-features/service.js +4 -3
  31. package/.medusa/server/src/providers/smtp/delivery-settings.js +75 -0
  32. package/.medusa/server/src/providers/smtp/graph-transport.js +150 -0
  33. package/.medusa/server/src/providers/smtp/service.js +105 -37
  34. package/.medusa/server/src/providers/smtp/types.js +7 -1
  35. package/.medusa/server/src/public/descriptor.js +24 -3
  36. package/.medusa/server/src/shared/lib/dates.js +8 -2
  37. package/.medusa/server/src/shop-config/build-food-shop-config.js +3 -5
  38. package/README.md +100 -65
  39. package/dist/customer-auth/plugin-features.js +9 -9
  40. package/dist/email/config/index.d.ts +6 -1
  41. package/dist/email/config/index.js +7 -1
  42. package/dist/email/config/notification-module.d.ts +25 -0
  43. package/dist/email/config/notification-module.js +27 -1
  44. package/dist/email/types/email-kinds.d.ts +2 -1
  45. package/dist/email/types/email-kinds.js +1 -0
  46. package/dist/email/types/template-data.d.ts +10 -0
  47. package/dist/email/types/template-data.js +7 -0
  48. package/dist/public/descriptor.js +23 -2
  49. package/dist/public/types.d.ts +14 -4
  50. package/dist/shared/lib/dates.d.ts +2 -0
  51. package/dist/shared/lib/dates.js +7 -1
  52. package/dist/shop-config/build-food-shop-config.d.ts +1 -1
  53. package/dist/shop-config/build-food-shop-config.js +2 -4
  54. package/package.json +2 -2
@@ -4,9 +4,11 @@ import { texts } from "../../../i18n/texts";
4
4
  import { AdminSection } from "../../../shared/ui/AdminSection";
5
5
  import { InfoTooltip } from "../../../shared/ui/InfoTooltip";
6
6
  import { SwitchRow } from "../../../shared/ui/SwitchRow";
7
- import { invalidatedByLabels, invalidatesLabels, isGroupDimmed, settingInfoText, showsBoundsLine, showsSourceHint, } from "../lib/plugin-view";
7
+ import { invalidatedByLabels, isGroupDimmed, settingInfoText, showsBoundsLine, showsSourceHint, } from "../lib/plugin-view";
8
8
  import { isHexColor, normalizeColorInput } from "../lib/settings-draft";
9
- import { buildSettingGroups } from "../lib/settings-layout";
9
+ import { dateFromInputValue, inputValueFromDate } from "../../../shared/lib/date-input";
10
+ import { AdminDatePicker } from "../../../shared/ui/AdminDatePicker";
11
+ import { buildSettingGroups, isGroupVisible, } from "../lib/settings-layout";
10
12
  import { usePluginSettingsForm } from "../hooks/usePluginSettingsForm";
11
13
  import { ImageSettingInput } from "./ImageSettingInput";
12
14
  const fieldId = (pluginKey, key) => `plugin-setting-${pluginKey}-${key}`;
@@ -34,8 +36,9 @@ const ColorSettingInput = ({ id, hintId, label, value, invalid, required, disabl
34
36
  className: `h-8 w-10 shrink-0 cursor-pointer rounded-md border border-ui-border-base bg-ui-bg-field p-1 disabled:cursor-not-allowed ${hasColor ? "" : "opacity-30"}`, value: hasColor ? value.trim() : "#000000", disabled: disabled, onChange: (event) => onChange(normalizeColorInput(event.target.value)) })] }));
35
37
  };
36
38
  /** A non-boolean setting: label (with source hint), control and hints. */
37
- const SettingField = ({ pluginKey, setting, invalidatedBy, invalidates, value, invalid, disabled, onChange, }) => {
39
+ const SettingField = ({ pluginKey, setting, invalidatedBy, value, invalid, disabled, onChange, }) => {
38
40
  const id = fieldId(pluginKey, setting.key);
41
+ const labelId = `${id}-label`;
39
42
  const hintId = `${id}-hint`;
40
43
  const clearPending = setting.type === "secret" && value === null;
41
44
  const bounds = setting.type === "number" ? boundsText(setting) : undefined;
@@ -46,30 +49,30 @@ const SettingField = ({ pluginKey, setting, invalidatedBy, invalidates, value, i
46
49
  const hasHints = Boolean(hint) ||
47
50
  clearPending ||
48
51
  invalidatedBy.length > 0 ||
49
- invalidates.length > 0 ||
50
52
  showBounds ||
51
53
  invalid;
52
54
  const describedBy = hasHints ? hintId : undefined;
53
55
  const placeholder = setting.placeholder ?? undefined;
54
- return (_jsxs("div", { className: "flex flex-col gap-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-x-1", children: [_jsx(Label, { htmlFor: id, size: "small", weight: "plus", children: labelText(setting) }), info && (_jsx(InfoTooltip, { label: texts.info.label(setting.label), children: info }))] }), showsSourceHint(setting.source) && (_jsx(Text, { size: "xsmall", className: "text-ui-fg-muted shrink-0", children: texts.settings.source[setting.source] }))] }), setting.type === "secret" ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { id: id, "aria-describedby": describedBy, "aria-required": required || undefined, className: "flex-1", type: "password", autoComplete: "new-password", placeholder: setting.is_set ? texts.settings.secretPlaceholder : placeholder, value: typeof value === "string" ? value : "", disabled: disabled || clearPending, onChange: (event) => onChange(event.target.value) }), setting.source === "stored" && (_jsx(Button, { type: "button", size: "small", variant: "secondary", disabled: disabled, "aria-label": clearPending
56
+ return (_jsxs("div", { className: "flex flex-col gap-y-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-x-1", children: [_jsx(Label, { id: labelId, htmlFor: id, size: "small", weight: "plus", children: labelText(setting) }), info && (_jsx(InfoTooltip, { label: texts.info.label(setting.label), children: info }))] }), showsSourceHint(setting.source) && (_jsx(Text, { size: "xsmall", className: "text-ui-fg-muted shrink-0", children: texts.settings.source[setting.source] }))] }), setting.type === "secret" ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { id: id, "aria-describedby": describedBy, "aria-required": required || undefined, className: "flex-1", type: "password", autoComplete: "new-password", placeholder: setting.is_set ? texts.settings.secretPlaceholder : placeholder, value: typeof value === "string" ? value : "", disabled: disabled || clearPending, onChange: (event) => onChange(event.target.value) }), setting.source === "stored" && (_jsx(Button, { type: "button", size: "small", variant: "secondary", disabled: disabled, "aria-label": clearPending
55
57
  ? texts.settings.secretClearUndoLabel(setting.label)
56
58
  : texts.settings.secretClearLabel(setting.label), onClick: () => onChange(clearPending ? "" : null), children: clearPending
57
59
  ? texts.settings.secretClearUndo
58
- : texts.settings.secretClear }))] })) : setting.type === "select" ? (_jsxs(Select, { value: typeof value === "string" ? value : "", disabled: disabled, onValueChange: (next) => onChange(next), children: [_jsx(Select.Trigger, { id: id, "aria-describedby": describedBy, "aria-required": required || undefined, children: _jsx(Select.Value, { placeholder: texts.settings.selectPlaceholder }) }), _jsx(Select.Content, { children: setting.options.map((option) => (_jsx(Select.Item, { value: option.value, children: option.label }, option.value))) })] })) : setting.type === "image" ? (_jsx(ImageSettingInput, { id: id, describedBy: describedBy, label: setting.label, value: typeof value === "string" ? value : "", disabled: disabled, onChange: (url) => onChange(url) })) : setting.type === "color" ? (_jsx(ColorSettingInput, { id: id, hintId: describedBy, label: setting.label, value: typeof value === "string" ? value : "", invalid: invalid, required: required, disabled: disabled, onChange: (next) => onChange(next) })) : setting.type === "number" ? (_jsx(Input, { id: id, "aria-describedby": describedBy, "aria-invalid": invalid, "aria-required": required || undefined, type: "number", step: setting.integer ? 1 : "any", min: setting.min ?? undefined, max: setting.max ?? undefined, placeholder: placeholder, value: typeof value === "string" ? value : "", disabled: disabled, onChange: (event) => onChange(event.target.value, event.target.validity.badInput) })) : (_jsx(Input, { id: id, "aria-describedby": describedBy, "aria-invalid": invalid, "aria-required": required || undefined, type: setting.type === "url" ? "url" : "text", placeholder: placeholder, value: typeof value === "string" ? value : "", disabled: disabled, onChange: (event) => onChange(event.target.value) })), hasHints && (_jsxs("div", { id: hintId, className: "flex flex-col gap-y-1", children: [hint && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: hint })), clearPending && (_jsx(Hint, { variant: "info", children: texts.settings.secretClearPending })), invalidatedBy.length > 0 && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: texts.settings.invalidatedBy(invalidatedBy) })), invalidates.length > 0 && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: texts.settings.invalidates(invalidates.join(", ")) })), showBounds && !invalid && (_jsx(Text, { size: "small", className: "text-ui-fg-muted", children: bounds })), invalid && (_jsx(Hint, { variant: "error", children: setting.type === "color"
60
+ : texts.settings.secretClear }))] })) : setting.type === "select" ? (_jsxs(Select, { value: typeof value === "string" ? value : "", disabled: disabled, onValueChange: (next) => onChange(next), children: [_jsx(Select.Trigger, { id: id, "aria-describedby": describedBy, "aria-required": required || undefined, children: _jsx(Select.Value, { placeholder: texts.settings.selectPlaceholder }) }), _jsx(Select.Content, { children: setting.options.map((option) => (_jsx(Select.Item, { value: option.value, children: option.label }, option.value))) })] })) : setting.type === "image" ? (_jsx(ImageSettingInput, { id: id, describedBy: describedBy, label: setting.label, value: typeof value === "string" ? value : "", disabled: disabled, onChange: (url) => onChange(url) })) : setting.type === "color" ? (_jsx(ColorSettingInput, { id: id, hintId: describedBy, label: setting.label, value: typeof value === "string" ? value : "", invalid: invalid, required: required, disabled: disabled, onChange: (next) => onChange(next) })) : setting.type === "date" ? (_jsx(AdminDatePicker, { id: id, "aria-labelledby": labelId, "aria-describedby": describedBy, isDisabled: disabled, value: dateFromInputValue(typeof value === "string" ? value : ""), onChange: (date) => onChange(inputValueFromDate(date)) })) : setting.type === "number" ? (_jsx(Input, { id: id, "aria-describedby": describedBy, "aria-invalid": invalid, "aria-required": required || undefined, type: "number", step: setting.integer ? 1 : "any", min: setting.min ?? undefined, max: setting.max ?? undefined, placeholder: placeholder, value: typeof value === "string" ? value : "", disabled: disabled, onChange: (event) => onChange(event.target.value, event.target.validity.badInput) })) : (_jsx(Input, { id: id, "aria-describedby": describedBy, "aria-invalid": invalid, "aria-required": required || undefined, type: setting.type === "url" ? "url" : "text", placeholder: placeholder, value: typeof value === "string" ? value : "", disabled: disabled, onChange: (event) => onChange(event.target.value) })), hasHints && (_jsxs("div", { id: hintId, className: "flex flex-col gap-y-1", children: [hint && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: hint })), clearPending && (_jsx(Hint, { variant: "info", children: texts.settings.secretClearPending })), invalidatedBy.length > 0 && (_jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: texts.settings.invalidatedBy(invalidatedBy) })), showBounds && !invalid && (_jsx(Text, { size: "small", className: "text-ui-fg-muted", children: bounds })), invalid && (_jsx(Hint, { variant: "error", children: setting.type === "color"
59
61
  ? texts.settings.invalidColor
60
62
  : (bounds ?? texts.settings.invalidNumber) }))] }))] }));
61
63
  };
62
64
  export const PluginSettingsForm = ({ plugin }) => {
63
65
  const form = usePluginSettingsForm(plugin);
64
66
  const pending = form.mutation.isPending;
65
- const groups = buildSettingGroups(plugin.settings, plugin.setting_groups, texts.sections.settings);
67
+ const groups = buildSettingGroups(plugin.settings, plugin.setting_groups, texts.sections.settings).filter((group) => isGroupVisible(group, form.draft));
66
68
  const hasTextFields = plugin.settings.some((setting) => setting.type === "string" ||
67
69
  setting.type === "url" ||
68
70
  setting.type === "number" ||
69
- setting.type === "color");
71
+ setting.type === "color" ||
72
+ setting.type === "date");
70
73
  const renderField = (setting) => (_jsx(SettingField, { pluginKey: plugin.plugin_key, setting: setting, invalidatedBy: setting.type === "secret"
71
74
  ? invalidatedByLabels(plugin.settings, setting.key)
72
- : [], invalidates: invalidatesLabels(plugin.settings, setting), value: form.draft[setting.key], invalid: form.invalid.includes(setting.key), disabled: pending, onChange: (value, badInput) => form.setValue(setting.key, value, badInput) }, setting.key));
75
+ : [], value: form.draft[setting.key], invalid: form.invalid.includes(setting.key), disabled: pending, onChange: (value, badInput) => form.setValue(setting.key, value, badInput) }, setting.key));
73
76
  // `dimmed`: the group's required feature is off; the rows stay editable.
74
77
  const renderBlock = (block, dimmed) => {
75
78
  const dim = dimmed ? "opacity-50 transition-opacity" : "transition-opacity";
@@ -90,7 +90,7 @@ export declare const pluginFeatureEnabledQueryOptions: (pluginKey: string, featu
90
90
  width: "full" | "half";
91
91
  section: string | null;
92
92
  } | {
93
- type: "string" | "boolean" | "color" | "image" | "url";
93
+ type: "string" | "boolean" | "color" | "image" | "url" | "date";
94
94
  default: string | number | boolean | null;
95
95
  value: string | number | boolean | null;
96
96
  key: string;
@@ -130,6 +130,10 @@ export declare const pluginFeatureEnabledQueryOptions: (pluginKey: string, featu
130
130
  description: string | null;
131
131
  note: string | null;
132
132
  requires_feature: string | null;
133
+ visible_when: {
134
+ setting: string;
135
+ equals: string | boolean;
136
+ } | null;
133
137
  }[];
134
138
  }[];
135
139
  }>;
@@ -70,7 +70,7 @@ export declare function usePluginFeatures(): import("@tanstack/react-query").Use
70
70
  width: "full" | "half";
71
71
  section: string | null;
72
72
  } | {
73
- type: "string" | "boolean" | "color" | "image" | "url";
73
+ type: "string" | "boolean" | "color" | "image" | "url" | "date";
74
74
  default: string | number | boolean | null;
75
75
  value: string | number | boolean | null;
76
76
  key: string;
@@ -110,6 +110,10 @@ export declare function usePluginFeatures(): import("@tanstack/react-query").Use
110
110
  description: string | null;
111
111
  note: string | null;
112
112
  requires_feature: string | null;
113
+ visible_when: {
114
+ setting: string;
115
+ equals: string | boolean;
116
+ } | null;
113
117
  }[];
114
118
  }[];
115
119
  }, Error>;
@@ -192,7 +196,7 @@ export declare function usePluginFeature(pluginKey: string | undefined): import(
192
196
  width: "full" | "half";
193
197
  section: string | null;
194
198
  } | {
195
- type: "string" | "boolean" | "color" | "image" | "url";
199
+ type: "string" | "boolean" | "color" | "image" | "url" | "date";
196
200
  default: string | number | boolean | null;
197
201
  value: string | number | boolean | null;
198
202
  key: string;
@@ -232,6 +236,10 @@ export declare function usePluginFeature(pluginKey: string | undefined): import(
232
236
  description: string | null;
233
237
  note: string | null;
234
238
  requires_feature: string | null;
239
+ visible_when: {
240
+ setting: string;
241
+ equals: string | boolean;
242
+ } | null;
235
243
  }[];
236
244
  };
237
245
  }, Error>;
@@ -308,7 +316,7 @@ export declare function useUpdatePluginFeatures(): import("@tanstack/react-query
308
316
  width: "full" | "half";
309
317
  section: string | null;
310
318
  } | {
311
- type: "string" | "boolean" | "color" | "image" | "url";
319
+ type: "string" | "boolean" | "color" | "image" | "url" | "date";
312
320
  default: string | number | boolean | null;
313
321
  value: string | number | boolean | null;
314
322
  key: string;
@@ -348,6 +356,10 @@ export declare function useUpdatePluginFeatures(): import("@tanstack/react-query
348
356
  description: string | null;
349
357
  note: string | null;
350
358
  requires_feature: string | null;
359
+ visible_when: {
360
+ setting: string;
361
+ equals: string | boolean;
362
+ } | null;
351
363
  }[];
352
364
  };
353
365
  }, Error, {
@@ -428,7 +440,7 @@ export declare function useUpdatePluginSettings(pluginKey: string): import("@tan
428
440
  width: "full" | "half";
429
441
  section: string | null;
430
442
  } | {
431
- type: "string" | "boolean" | "color" | "image" | "url";
443
+ type: "string" | "boolean" | "color" | "image" | "url" | "date";
432
444
  default: string | number | boolean | null;
433
445
  value: string | number | boolean | null;
434
446
  key: string;
@@ -468,6 +480,10 @@ export declare function useUpdatePluginSettings(pluginKey: string): import("@tan
468
480
  description: string | null;
469
481
  note: string | null;
470
482
  requires_feature: string | null;
483
+ visible_when: {
484
+ setting: string;
485
+ equals: string | boolean;
486
+ } | null;
471
487
  }[];
472
488
  };
473
489
  }, Error, {
@@ -79,7 +79,7 @@ export declare function usePluginSettingsForm(plugin: AdminPluginFeatures): {
79
79
  width: "full" | "half";
80
80
  section: string | null;
81
81
  } | {
82
- type: "string" | "boolean" | "color" | "image" | "url";
82
+ type: "string" | "boolean" | "color" | "image" | "url" | "date";
83
83
  default: string | number | boolean | null;
84
84
  value: string | number | boolean | null;
85
85
  key: string;
@@ -119,6 +119,10 @@ export declare function usePluginSettingsForm(plugin: AdminPluginFeatures): {
119
119
  description: string | null;
120
120
  note: string | null;
121
121
  requires_feature: string | null;
122
+ visible_when: {
123
+ setting: string;
124
+ equals: string | boolean;
125
+ } | null;
122
126
  }[];
123
127
  };
124
128
  }, Error, {
@@ -71,7 +71,7 @@ export declare const listPluginFeatures: () => Promise<{
71
71
  width: "full" | "half";
72
72
  section: string | null;
73
73
  } | {
74
- type: "string" | "boolean" | "color" | "image" | "url";
74
+ type: "string" | "boolean" | "color" | "image" | "url" | "date";
75
75
  default: string | number | boolean | null;
76
76
  value: string | number | boolean | null;
77
77
  key: string;
@@ -111,6 +111,10 @@ export declare const listPluginFeatures: () => Promise<{
111
111
  description: string | null;
112
112
  note: string | null;
113
113
  requires_feature: string | null;
114
+ visible_when: {
115
+ setting: string;
116
+ equals: string | boolean;
117
+ } | null;
114
118
  }[];
115
119
  }[];
116
120
  }>;
@@ -186,7 +190,7 @@ export declare const retrievePluginFeatures: (pluginKey: string) => Promise<{
186
190
  width: "full" | "half";
187
191
  section: string | null;
188
192
  } | {
189
- type: "string" | "boolean" | "color" | "image" | "url";
193
+ type: "string" | "boolean" | "color" | "image" | "url" | "date";
190
194
  default: string | number | boolean | null;
191
195
  value: string | number | boolean | null;
192
196
  key: string;
@@ -226,6 +230,10 @@ export declare const retrievePluginFeatures: (pluginKey: string) => Promise<{
226
230
  description: string | null;
227
231
  note: string | null;
228
232
  requires_feature: string | null;
233
+ visible_when: {
234
+ setting: string;
235
+ equals: string | boolean;
236
+ } | null;
229
237
  }[];
230
238
  };
231
239
  }>;
@@ -301,7 +309,7 @@ export declare const updatePluginFeatures: (payload: UpdatePluginFeaturesPayload
301
309
  width: "full" | "half";
302
310
  section: string | null;
303
311
  } | {
304
- type: "string" | "boolean" | "color" | "image" | "url";
312
+ type: "string" | "boolean" | "color" | "image" | "url" | "date";
305
313
  default: string | number | boolean | null;
306
314
  value: string | number | boolean | null;
307
315
  key: string;
@@ -341,6 +349,10 @@ export declare const updatePluginFeatures: (payload: UpdatePluginFeaturesPayload
341
349
  description: string | null;
342
350
  note: string | null;
343
351
  requires_feature: string | null;
352
+ visible_when: {
353
+ setting: string;
354
+ equals: string | boolean;
355
+ } | null;
344
356
  }[];
345
357
  };
346
358
  }>;
@@ -416,7 +428,7 @@ export declare const updatePluginSettings: (pluginKey: string, payload: UpdatePl
416
428
  width: "full" | "half";
417
429
  section: string | null;
418
430
  } | {
419
- type: "string" | "boolean" | "color" | "image" | "url";
431
+ type: "string" | "boolean" | "color" | "image" | "url" | "date";
420
432
  default: string | number | boolean | null;
421
433
  value: string | number | boolean | null;
422
434
  key: string;
@@ -456,6 +468,10 @@ export declare const updatePluginSettings: (pluginKey: string, payload: UpdatePl
456
468
  description: string | null;
457
469
  note: string | null;
458
470
  requires_feature: string | null;
471
+ visible_when: {
472
+ setting: string;
473
+ equals: string | boolean;
474
+ } | null;
459
475
  }[];
460
476
  };
461
477
  }>;
@@ -27,11 +27,6 @@ export declare const disabledParent: (features: readonly AdminPluginFeature[], f
27
27
  export declare const invalidatedByLabels: (settings: readonly AdminPluginSetting[], secretKey: string) => string[];
28
28
  /** Whether a settings group is dimmed: its required feature exists and is not effective. */
29
29
  export declare const isGroupDimmed: (features: readonly AdminPluginFeature[], requiresFeature: string | null) => boolean;
30
- /**
31
- * Labels of the secrets that a change of `setting` deletes (forward
32
- * `invalidates`), in the setting's order; unknown keys fall back to the key.
33
- */
34
- export declare const invalidatesLabels: (settings: readonly AdminPluginSetting[], setting: Pick<AdminPluginSetting, "invalidates">) => string[];
35
30
  /**
36
31
  * The description a setting shows in its info tooltip: only when a `hint`
37
32
  * takes the place under the field and the description says something else.
@@ -41,11 +41,6 @@ export const invalidatedByLabels = (settings, secretKey) => settings
41
41
  export const isGroupDimmed = (features, requiresFeature) => requiresFeature !== null &&
42
42
  features.find((feature) => feature.key === requiresFeature)
43
43
  ?.effective_enabled === false;
44
- /**
45
- * Labels of the secrets that a change of `setting` deletes (forward
46
- * `invalidates`), in the setting's order; unknown keys fall back to the key.
47
- */
48
- export const invalidatesLabels = (settings, setting) => setting.invalidates.map((key) => settings.find((candidate) => candidate.key === key)?.label ?? key);
49
44
  /**
50
45
  * The description a setting shows in its info tooltip: only when a `hint`
51
46
  * takes the place under the field and the description says something else.
@@ -30,6 +30,8 @@ export type SettingGroupView = {
30
30
  description: string | null;
31
31
  note: string | null;
32
32
  requiresFeature: string | null;
33
+ /** The group applies only while this setting's draft value equals `equals`. */
34
+ visibleWhen: AdminPluginSettingGroup["visible_when"];
33
35
  settings: AdminPluginSetting[];
34
36
  blocks: SettingBlock[];
35
37
  };
@@ -41,3 +43,5 @@ export declare const buildSettingBlocks: (settings: readonly AdminPluginSetting[
41
43
  * `groups` supplies the description, note and required feature per group.
42
44
  */
43
45
  export declare const buildSettingGroups: (settings: readonly AdminPluginSetting[], groups: readonly AdminPluginSettingGroup[], ungroupedTitle: string) => SettingGroupView[];
46
+ /** Whether a group's `visible_when` holds for the current form values. */
47
+ export declare const isGroupVisible: (group: Pick<SettingGroupView, "visibleWhen">, draft: Readonly<Record<string, string | boolean | null>>) => boolean;
@@ -59,8 +59,12 @@ export const buildSettingGroups = (settings, groups, ungroupedTitle) => {
59
59
  description: meta?.description ?? null,
60
60
  note: meta?.note ?? null,
61
61
  requiresFeature: meta?.requires_feature ?? null,
62
+ visibleWhen: meta?.visible_when ?? null,
62
63
  settings: groupSettings,
63
64
  blocks: buildSettingBlocks(groupSettings),
64
65
  };
65
66
  });
66
67
  };
68
+ /** Whether a group's `visible_when` holds for the current form values. */
69
+ export const isGroupVisible = (group, draft) => group.visibleWhen === null ||
70
+ draft[group.visibleWhen.setting] === group.visibleWhen.equals;
@@ -5,6 +5,7 @@ export declare const texts: {
5
5
  breadcrumbs: {
6
6
  hub: string;
7
7
  emailAttachments: string;
8
+ emailDelivery: string;
8
9
  };
9
10
  page: {
10
11
  title: string;
@@ -70,8 +71,6 @@ export declare const texts: {
70
71
  secretClearUndoLabel: (label: string) => string;
71
72
  /** Shown on a secret: the settings whose change deletes it ("A", "A oder B"). */
72
73
  invalidatedBy: (labels: readonly string[]) => string;
73
- /** Shown on the changing setting: the secrets its change deletes. */
74
- invalidates: (labels: string) => string;
75
74
  invalidNumber: string;
76
75
  invalidColor: string;
77
76
  colorPlaceholder: string;
@@ -5,6 +5,7 @@ export const texts = {
5
5
  breadcrumbs: {
6
6
  hub: "Plugins",
7
7
  emailAttachments: "Anhänge",
8
+ emailDelivery: "Versand",
8
9
  },
9
10
  page: {
10
11
  title: "Hartl Services Plugins",
@@ -72,8 +73,6 @@ export const texts = {
72
73
  invalidatedBy: (labels) => `Wird gelöscht, wenn ${labels.length > 1
73
74
  ? `${labels.slice(0, -1).join(", ")} oder ${labels[labels.length - 1]}`
74
75
  : labels.join("")} sich ändert.`,
75
- /** Shown on the changing setting: the secrets its change deletes. */
76
- invalidates: (labels) => `Beim Ändern muss ${labels} neu eingegeben werden.`,
77
76
  invalidNumber: "Bitte eine gültige Zahl eingeben.",
78
77
  invalidColor: "Bitte eine Hex-Farbe wie #1a2b3c eingeben.",
79
78
  colorPlaceholder: "Standard",