@vertebr/react 0.3.0 → 0.5.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 (126) hide show
  1. package/dist/components/admin/AdminContent.d.ts +36 -0
  2. package/dist/components/admin/AdminContent.d.ts.map +1 -0
  3. package/dist/components/admin/AdminContent.js +107 -0
  4. package/dist/components/admin/AdminHeader.d.ts +13 -0
  5. package/dist/components/admin/AdminHeader.d.ts.map +1 -0
  6. package/dist/components/admin/AdminHeader.js +32 -0
  7. package/dist/components/admin/AdminSidebar.d.ts +25 -0
  8. package/dist/components/admin/AdminSidebar.d.ts.map +1 -0
  9. package/dist/components/admin/AdminSidebar.js +31 -0
  10. package/dist/components/admin/VertebrAdmin.d.ts +3 -18
  11. package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
  12. package/dist/components/admin/VertebrAdmin.js +130 -120
  13. package/dist/components/admin/sidebar.d.ts +3 -0
  14. package/dist/components/admin/sidebar.d.ts.map +1 -0
  15. package/dist/components/admin/sidebar.js +13 -0
  16. package/dist/components/admin/storage.d.ts +4 -0
  17. package/dist/components/admin/storage.d.ts.map +1 -0
  18. package/dist/components/admin/storage.js +24 -0
  19. package/dist/components/admin/types.d.ts +53 -0
  20. package/dist/components/admin/types.d.ts.map +1 -0
  21. package/dist/components/admin/types.js +1 -0
  22. package/dist/components/config/EnumInput.d.ts +10 -0
  23. package/dist/components/config/EnumInput.d.ts.map +1 -0
  24. package/dist/components/config/EnumInput.js +19 -0
  25. package/dist/components/config/EnumListInput.d.ts +10 -0
  26. package/dist/components/config/EnumListInput.d.ts.map +1 -0
  27. package/dist/components/config/EnumListInput.js +46 -0
  28. package/dist/components/config/VisibilityEye.d.ts +8 -0
  29. package/dist/components/config/VisibilityEye.d.ts.map +1 -0
  30. package/dist/components/config/VisibilityEye.js +18 -0
  31. package/dist/components/config/form/ConfigField.d.ts +2 -1
  32. package/dist/components/config/form/ConfigField.d.ts.map +1 -1
  33. package/dist/components/config/form/ConfigField.js +9 -4
  34. package/dist/components/config/form/ConfigForm.d.ts +1 -1
  35. package/dist/components/config/form/ConfigForm.d.ts.map +1 -1
  36. package/dist/components/config/form/ConfigForm.js +18 -9
  37. package/dist/components/config/form/ConfigFormActions.d.ts +2 -1
  38. package/dist/components/config/form/ConfigFormActions.d.ts.map +1 -1
  39. package/dist/components/config/form/ConfigFormActions.js +2 -2
  40. package/dist/components/config/form/RecordConfigForm.d.ts +3 -1
  41. package/dist/components/config/form/RecordConfigForm.d.ts.map +1 -1
  42. package/dist/components/config/form/RecordConfigForm.js +16 -8
  43. package/dist/components/config/form/SettingsConfigForm.d.ts +3 -1
  44. package/dist/components/config/form/SettingsConfigForm.d.ts.map +1 -1
  45. package/dist/components/config/form/SettingsConfigForm.js +2 -2
  46. package/dist/components/config/form/helpers/fields.d.ts +7 -0
  47. package/dist/components/config/form/helpers/fields.d.ts.map +1 -0
  48. package/dist/components/config/form/helpers/fields.js +31 -0
  49. package/dist/components/config/form/helpers/groups.d.ts +4 -0
  50. package/dist/components/config/form/helpers/groups.d.ts.map +1 -0
  51. package/dist/components/config/form/helpers/groups.js +107 -0
  52. package/dist/components/config/form/helpers/images.d.ts +4 -0
  53. package/dist/components/config/form/helpers/images.d.ts.map +1 -0
  54. package/dist/components/config/form/helpers/images.js +27 -0
  55. package/dist/components/config/form/helpers/index.d.ts +7 -0
  56. package/dist/components/config/form/helpers/index.d.ts.map +1 -0
  57. package/dist/components/config/form/helpers/index.js +6 -0
  58. package/dist/components/config/form/helpers/labels.d.ts +5 -0
  59. package/dist/components/config/form/helpers/labels.d.ts.map +1 -0
  60. package/dist/components/config/form/helpers/labels.js +28 -0
  61. package/dist/components/config/form/helpers/schema.d.ts +8 -0
  62. package/dist/components/config/form/helpers/schema.d.ts.map +1 -0
  63. package/dist/components/config/form/helpers/schema.js +30 -0
  64. package/dist/components/config/form/helpers/visibility.d.ts +13 -0
  65. package/dist/components/config/form/helpers/visibility.d.ts.map +1 -0
  66. package/dist/components/config/form/helpers/visibility.js +86 -0
  67. package/dist/components/config/form/types.d.ts +6 -0
  68. package/dist/components/config/form/types.d.ts.map +1 -1
  69. package/dist/components/emails/EmailRecipientSelect.d.ts +16 -0
  70. package/dist/components/emails/EmailRecipientSelect.d.ts.map +1 -0
  71. package/dist/components/emails/EmailRecipientSelect.js +10 -0
  72. package/dist/components/emails/EmailsActivityPanel.d.ts +11 -0
  73. package/dist/components/emails/EmailsActivityPanel.d.ts.map +1 -0
  74. package/dist/components/emails/EmailsActivityPanel.js +139 -0
  75. package/dist/components/emails/EmailsBoard.d.ts +47 -0
  76. package/dist/components/emails/EmailsBoard.d.ts.map +1 -0
  77. package/dist/components/emails/EmailsBoard.js +594 -0
  78. package/dist/components/emails/EmailsLibraryPanel.d.ts +25 -0
  79. package/dist/components/emails/EmailsLibraryPanel.d.ts.map +1 -0
  80. package/dist/components/emails/EmailsLibraryPanel.js +379 -0
  81. package/dist/components/emails/EmailsNavSelect.d.ts +18 -0
  82. package/dist/components/emails/EmailsNavSelect.d.ts.map +1 -0
  83. package/dist/components/emails/EmailsNavSelect.js +37 -0
  84. package/dist/components/emails/MultiCheckSelect.d.ts +18 -0
  85. package/dist/components/emails/MultiCheckSelect.d.ts.map +1 -0
  86. package/dist/components/emails/MultiCheckSelect.js +74 -0
  87. package/dist/components/metrics/MetricsBoard.d.ts +8 -2
  88. package/dist/components/metrics/MetricsBoard.d.ts.map +1 -1
  89. package/dist/components/metrics/MetricsBoard.js +55 -2
  90. package/dist/components/metrics/charts.d.ts +14 -0
  91. package/dist/components/metrics/charts.d.ts.map +1 -0
  92. package/dist/components/metrics/charts.js +212 -0
  93. package/dist/components/resources/ResourceActionModal.d.ts +14 -0
  94. package/dist/components/resources/ResourceActionModal.d.ts.map +1 -0
  95. package/dist/components/resources/ResourceActionModal.js +64 -0
  96. package/dist/components/resources/ResourceCell.d.ts.map +1 -1
  97. package/dist/components/resources/ResourceCell.js +25 -9
  98. package/dist/components/resources/ResourceCreateModal.d.ts +18 -0
  99. package/dist/components/resources/ResourceCreateModal.d.ts.map +1 -0
  100. package/dist/components/resources/ResourceCreateModal.js +22 -0
  101. package/dist/components/resources/ResourceMailModal.d.ts +12 -0
  102. package/dist/components/resources/ResourceMailModal.d.ts.map +1 -0
  103. package/dist/components/resources/ResourceMailModal.js +196 -0
  104. package/dist/components/resources/ResourceTable.d.ts +9 -3
  105. package/dist/components/resources/ResourceTable.d.ts.map +1 -1
  106. package/dist/components/resources/ResourceTable.js +241 -35
  107. package/dist/index.d.ts +11 -1
  108. package/dist/index.d.ts.map +1 -1
  109. package/dist/index.js +3 -0
  110. package/dist/lib/metricsRanges.d.ts +20 -0
  111. package/dist/lib/metricsRanges.d.ts.map +1 -0
  112. package/dist/lib/metricsRanges.js +14 -0
  113. package/dist/styles/cells.css +42 -0
  114. package/dist/styles/emails.css +632 -0
  115. package/dist/styles/form.css +145 -0
  116. package/dist/styles/index.css +3 -0
  117. package/dist/styles/mail.css +240 -0
  118. package/dist/styles/metrics.css +633 -0
  119. package/dist/styles/modal.css +18 -0
  120. package/dist/styles/nav.css +103 -1
  121. package/dist/styles/panel.css +8 -68
  122. package/dist/styles/table.css +26 -3
  123. package/package.json +2 -2
  124. package/dist/components/config/form/helpers.d.ts +0 -16
  125. package/dist/components/config/form/helpers.d.ts.map +0 -1
  126. package/dist/components/config/form/helpers.js +0 -186
@@ -0,0 +1,86 @@
1
+ import { isSpecsField } from "./fields";
2
+ import { humanizeKey } from "./labels";
3
+ export function showFlagKey(fieldKey) {
4
+ return `show${fieldKey.charAt(0).toUpperCase()}${fieldKey.slice(1)}`;
5
+ }
6
+ /** Ensure each Specs field has a paired `showX` boolean in the editor schema. */
7
+ export function withSpecVisibility(schema) {
8
+ const next = { ...schema };
9
+ for (const [key, field] of Object.entries(schema)) {
10
+ if (field.type === "boolean" || !isSpecsField(key, field))
11
+ continue;
12
+ const flag = showFlagKey(key);
13
+ if (!next[flag]) {
14
+ next[flag] = {
15
+ type: "boolean",
16
+ default: true,
17
+ label: `Show ${field.label ?? humanizeKey(key)}`,
18
+ };
19
+ }
20
+ }
21
+ return next;
22
+ }
23
+ export function withSpecVisibilityValues(schema, value) {
24
+ const next = { ...value };
25
+ for (const [key, field] of Object.entries(schema)) {
26
+ if (field.type === "boolean" || !isSpecsField(key, field))
27
+ continue;
28
+ const flag = showFlagKey(key);
29
+ if (!(flag in next) || next[flag] == null)
30
+ next[flag] = true;
31
+ else
32
+ next[flag] = next[flag] !== false;
33
+ }
34
+ return next;
35
+ }
36
+ function pascalToCamel(value) {
37
+ return value.charAt(0).toLowerCase() + value.slice(1);
38
+ }
39
+ /** `showMaterial` paired with `material`, `showPrice` with `priceCents`. */
40
+ export function visibilityToggleKey(fieldKey, schema) {
41
+ const flag = showFlagKey(fieldKey);
42
+ const candidates = [flag];
43
+ if (fieldKey === "priceCents")
44
+ candidates.push("showPrice");
45
+ for (const key of candidates) {
46
+ if (schema[key]?.type === "boolean")
47
+ return key;
48
+ }
49
+ if (isSpecsField(fieldKey, schema[fieldKey]))
50
+ return flag;
51
+ return null;
52
+ }
53
+ const GROUP_VISIBILITY = {
54
+ Pricing: "showPrice",
55
+ Inventory: "showStock",
56
+ Content: "showDescription",
57
+ };
58
+ function groupVisibilityKeyFromField(key) {
59
+ return Object.keys(GROUP_VISIBILITY).find((group) => GROUP_VISIBILITY[group] === key);
60
+ }
61
+ export function isPairedVisibilityField(key, schema) {
62
+ if (!/^show[A-Z]/.test(key) || schema[key]?.type !== "boolean")
63
+ return false;
64
+ if (groupVisibilityKeyFromField(key))
65
+ return true;
66
+ const target = pascalToCamel(key.slice(4));
67
+ if (schema[target])
68
+ return true;
69
+ return key === "showPrice" && Boolean(schema.priceCents);
70
+ }
71
+ /** Section-level storefront flag, e.g. Pricing → `showPrice`. */
72
+ export function groupVisibilityKey(groupId, schema) {
73
+ const key = GROUP_VISIBILITY[groupId];
74
+ if (key && schema[key]?.type === "boolean")
75
+ return key;
76
+ return null;
77
+ }
78
+ /** Field-level flag, skipped when the section header already owns it. */
79
+ export function fieldVisibilityKey(fieldKey, schema, groupId) {
80
+ const paired = visibilityToggleKey(fieldKey, schema);
81
+ if (!paired)
82
+ return null;
83
+ if (groupId && groupVisibilityKey(groupId, schema) === paired)
84
+ return null;
85
+ return paired;
86
+ }
@@ -4,6 +4,8 @@ export type ConfigFormProps = {
4
4
  schema: Record<string, FieldSchema>;
5
5
  value: Record<string, unknown>;
6
6
  onSave: (value: Record<string, unknown>) => Promise<void>;
7
+ /** Primary submit button label. @default "Save changes" */
8
+ submitLabel?: string;
7
9
  /** `record` uses a dense product-style editor. */
8
10
  variant?: ConfigFormVariant;
9
11
  /** Field-keyed upload capabilities from the host resource. */
@@ -16,6 +18,10 @@ export type ConfigFormProps = {
16
18
  galleryUpload?: Pick<ResourceUploadMeta, "accept" | "maxBytes">;
17
19
  onListGallery?: () => Promise<GalleryItem[]>;
18
20
  onUploadGallery?: (files: File[]) => Promise<GalleryItem[]>;
21
+ /** Live field values (e.g. mail preview). */
22
+ onChange?: (value: Record<string, unknown>) => void;
23
+ /** Hide Save / Discard — parent owns submit. */
24
+ hideActions?: boolean;
19
25
  };
20
26
  export type FieldEntry = [string, FieldSchema];
21
27
  export type FieldGroup = {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEpF,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEpD,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,kDAAkD;IAClD,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,sDAAsD;IACtD,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,aAAa,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;IAChE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;CAC7D,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,eAAe,EACf,eAAe,GAAG,eAAe,GAAG,iBAAiB,CACtD,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEpF,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEpD,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,sDAAsD;IACtD,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,aAAa,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;IAChE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACpD,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,eAAe,EACf,eAAe,GAAG,eAAe,GAAG,iBAAiB,CACtD,CAAC"}
@@ -0,0 +1,16 @@
1
+ export type EmailRecipientOption = {
2
+ email: string;
3
+ id?: string;
4
+ label?: string;
5
+ /** Audience resource this address came from. */
6
+ resourceId?: string;
7
+ };
8
+ export type EmailRecipientSelectProps = {
9
+ options: EmailRecipientOption[];
10
+ value: string[];
11
+ loading?: boolean;
12
+ disabled?: boolean;
13
+ onChange: (emails: string[]) => void;
14
+ };
15
+ export declare function EmailRecipientSelect({ options, value, loading, disabled, onChange, }: EmailRecipientSelectProps): import("react").JSX.Element;
16
+ //# sourceMappingURL=EmailRecipientSelect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailRecipientSelect.d.ts","sourceRoot":"","sources":["../../../src/components/emails/EmailRecipientSelect.tsx"],"names":[],"mappings":"AAGA,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CACtC,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,KAAK,EACL,OAAO,EACP,QAAQ,EACR,QAAQ,GACT,EAAE,yBAAyB,+BAmB3B"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { MultiCheckSelect } from "./MultiCheckSelect";
3
+ export function EmailRecipientSelect({ options, value, loading, disabled, onChange, }) {
4
+ const mapped = options.map((option) => ({
5
+ id: option.email.trim().toLowerCase(),
6
+ label: option.email,
7
+ secondary: option.label,
8
+ }));
9
+ return (_jsx(MultiCheckSelect, { options: mapped, value: value, loading: loading, disabled: disabled, placeholder: "Select emails\u2026", emptyLabel: "No emails on this list", searchPlaceholder: "Search emails\u2026", onChange: onChange }));
10
+ }
@@ -0,0 +1,11 @@
1
+ import type { MailCampaign, MailHistoryEntry } from "@vertebr/client";
2
+ export type EmailsActivityPanelProps = {
3
+ campaigns: MailCampaign[];
4
+ history: MailHistoryEntry[];
5
+ canSchedule?: boolean;
6
+ canHistory?: boolean;
7
+ busy?: boolean;
8
+ onCancel?: (id: string) => Promise<void>;
9
+ };
10
+ export declare function EmailsActivityPanel({ campaigns, history, canSchedule, canHistory, busy, onCancel, }: EmailsActivityPanelProps): import("react").JSX.Element;
11
+ //# sourceMappingURL=EmailsActivityPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailsActivityPanel.d.ts","sourceRoot":"","sources":["../../../src/components/emails/EmailsActivityPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,MAAM,MAAM,wBAAwB,GAAG;IACrC,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC;AAqFF,wBAAgB,mBAAmB,CAAC,EAClC,SAAS,EACT,OAAO,EACP,WAAW,EACX,UAAU,EACV,IAAI,EACJ,QAAQ,GACT,EAAE,wBAAwB,+BA6L1B"}
@@ -0,0 +1,139 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from "react";
3
+ function formatWhen(iso) {
4
+ const ms = Date.parse(iso);
5
+ if (!Number.isFinite(ms))
6
+ return iso;
7
+ return new Intl.DateTimeFormat(undefined, {
8
+ dateStyle: "medium",
9
+ timeStyle: "short",
10
+ }).format(ms);
11
+ }
12
+ function humanize(value) {
13
+ return value
14
+ .replace(/^library:/i, "")
15
+ .replace(/([a-z])([A-Z])/g, "$1 $2")
16
+ .replace(/[_-]+/g, " ")
17
+ .trim();
18
+ }
19
+ function sourceLabel(source) {
20
+ if (source === "row")
21
+ return "Row";
22
+ if (source === "campaign")
23
+ return "Schedule";
24
+ return "Board";
25
+ }
26
+ function confirmationOf(entry) {
27
+ const stored = entry.reference?.trim();
28
+ if (stored && stored.toUpperCase() !== "MM-PENDING")
29
+ return stored;
30
+ const subject = entry.subject ?? "";
31
+ const match = subject.match(/(?:—|-|–)\s*((?:MM|ORD|ORDER)[-_][A-Z0-9-]+|[A-Z]{2,}[-_][A-Z0-9-]{3,})\s*$/i);
32
+ const value = match?.[1]?.trim();
33
+ if (!value || value.toUpperCase() === "MM-PENDING")
34
+ return "";
35
+ return value;
36
+ }
37
+ function subjectOf(entry) {
38
+ const confirmation = confirmationOf(entry);
39
+ const raw = entry.subject?.trim() ?? "";
40
+ if (!raw)
41
+ return entry.templateLabel ?? "Email";
42
+ let next = raw;
43
+ if (confirmation) {
44
+ next = next
45
+ .replace(new RegExp(`\\s*[—\\-–]\\s*${confirmation}\\s*$`, "i"), "")
46
+ .trim();
47
+ }
48
+ return next.replace(/\s*[—\-–]\s*MM-PENDING\s*$/i, "").trim() || raw;
49
+ }
50
+ function recipientOf(entry) {
51
+ if (entry.recipients?.[0]) {
52
+ const extra = Math.max(0, entry.recipientCount - 1);
53
+ return extra > 0
54
+ ? `${entry.recipients[0]} +${extra}`
55
+ : entry.recipients[0];
56
+ }
57
+ if (entry.recipientCount > 1)
58
+ return `${entry.recipientCount} recipients`;
59
+ return "";
60
+ }
61
+ function deliveryOf(entry) {
62
+ if (entry.failed && entry.failed > 0) {
63
+ return `${entry.sent} / ${entry.failed} failed`;
64
+ }
65
+ return `${entry.sent} / ${entry.recipientCount}`;
66
+ }
67
+ function statusBadge(status) {
68
+ if (status === "sent")
69
+ return { label: "Sent", tone: "ok" };
70
+ if (status === "partial")
71
+ return { label: "Partial" };
72
+ if (status === "failed")
73
+ return { label: "Failed" };
74
+ return { label: "Scheduled", tone: "neutral" };
75
+ }
76
+ function CellEmpty() {
77
+ return _jsx("span", { className: "vertebr-cell-empty", children: "\u2014" });
78
+ }
79
+ export function EmailsActivityPanel({ campaigns, history, canSchedule, canHistory, busy, onCancel, }) {
80
+ const [filter, setFilter] = useState("all");
81
+ const upcoming = useMemo(() => campaigns
82
+ .filter((campaign) => campaign.status === "scheduled")
83
+ .slice()
84
+ .sort((a, b) => Date.parse(a.sendAt) - Date.parse(b.sendAt)), [campaigns]);
85
+ const rows = useMemo(() => {
86
+ const scheduled = upcoming.map((campaign) => ({
87
+ kind: "scheduled",
88
+ id: campaign.id,
89
+ campaign,
90
+ }));
91
+ const sent = history.map((entry) => ({
92
+ kind: "history",
93
+ id: entry.id,
94
+ entry,
95
+ }));
96
+ if (filter === "scheduled")
97
+ return scheduled;
98
+ if (filter === "history")
99
+ return sent;
100
+ return [...scheduled, ...sent];
101
+ }, [filter, upcoming, history]);
102
+ const showActions = Boolean(onCancel) && filter !== "history";
103
+ const cancel = async (campaign) => {
104
+ if (!onCancel)
105
+ return;
106
+ if (!window.confirm(`Cancel scheduled email to ${campaign.resourceLabel ?? campaign.resourceId}?`)) {
107
+ return;
108
+ }
109
+ await onCancel(campaign.id);
110
+ };
111
+ const emptyCopy = (() => {
112
+ if (filter === "scheduled") {
113
+ return canSchedule
114
+ ? "No drops scheduled yet."
115
+ : "Host has not enabled scheduling.";
116
+ }
117
+ if (filter === "history") {
118
+ return canHistory
119
+ ? "No sends recorded yet."
120
+ : "Host has not enabled send history.";
121
+ }
122
+ if (!canSchedule && !canHistory) {
123
+ return "Host has not enabled activity.";
124
+ }
125
+ return "No activity yet.";
126
+ })();
127
+ return (_jsxs("div", { className: "vertebr-emails-activity", children: [_jsxs("div", { className: "vertebr-table-toolbar", children: [_jsx("div", { className: "vertebr-table-toolbar-start", children: _jsxs("div", { className: "vertebr-emails-segment", role: "group", "aria-label": "Activity", children: [_jsx("button", { type: "button", "data-active": filter === "all" ? "true" : undefined, onClick: () => setFilter("all"), children: "All" }), _jsx("button", { type: "button", "data-active": filter === "scheduled" ? "true" : undefined, onClick: () => setFilter("scheduled"), children: "Scheduled" }), _jsx("button", { type: "button", "data-active": filter === "history" ? "true" : undefined, onClick: () => setFilter("history"), children: "History" })] }) }), _jsx("span", { className: "vertebr-count", children: rows.length })] }), rows.length === 0 ? (_jsx("div", { className: "vertebr-empty", children: emptyCopy })) : (_jsx("div", { className: "vertebr-table-wrap", children: _jsxs("table", { className: "vertebr-table", children: [_jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { children: "When" }), _jsx("th", { children: "Status" }), _jsx("th", { children: "Subject" }), _jsx("th", { children: "Confirmation" }), _jsx("th", { children: "To" }), _jsx("th", { children: "Audience" }), _jsx("th", { children: "Template" }), _jsx("th", { children: "Via" }), _jsx("th", { children: "Delivery" }), showActions ? _jsx("th", { className: "vertebr-col-actions", children: "Actions" }) : null] }) }), _jsx("tbody", { children: rows.map((row) => {
128
+ if (row.kind === "scheduled") {
129
+ const campaign = row.campaign;
130
+ const badge = statusBadge("scheduled");
131
+ return (_jsxs("tr", { children: [_jsx("td", { children: _jsx("span", { className: "vertebr-cell-datetime", children: formatWhen(campaign.sendAt) }) }), _jsx("td", { children: _jsx("span", { className: "vertebr-badge", "data-tone": badge.tone, children: badge.label }) }), _jsx("td", { children: campaign.subject?.trim() || _jsx(CellEmpty, {}) }), _jsx("td", { children: _jsx(CellEmpty, {}) }), _jsx("td", { children: campaign.recipientCount ?? _jsx(CellEmpty, {}) }), _jsx("td", { children: campaign.resourceLabel ?? campaign.resourceId }), _jsx("td", { children: campaign.templateLabel ?? campaign.templateId }), _jsx("td", { children: "Schedule" }), _jsx("td", { children: _jsx(CellEmpty, {}) }), showActions ? (_jsx("td", { className: "vertebr-col-actions", children: _jsx("button", { type: "button", className: "vertebr-btn vertebr-btn-quiet", disabled: busy, onClick: () => void cancel(campaign), children: "Cancel" }) })) : null] }, `scheduled:${row.id}`));
132
+ }
133
+ const entry = row.entry;
134
+ const badge = statusBadge(entry.status);
135
+ const confirmation = confirmationOf(entry);
136
+ const recipient = recipientOf(entry);
137
+ return (_jsxs("tr", { children: [_jsx("td", { children: _jsx("span", { className: "vertebr-cell-datetime", children: formatWhen(entry.at) }) }), _jsx("td", { children: _jsx("span", { className: "vertebr-badge", "data-tone": badge.tone, children: badge.label }) }), _jsx("td", { children: subjectOf(entry) }), _jsx("td", { children: confirmation || _jsx(CellEmpty, {}) }), _jsx("td", { children: recipient || _jsx(CellEmpty, {}) }), _jsx("td", { children: entry.resourceLabel ?? entry.resourceId }), _jsx("td", { children: entry.templateLabel ?? humanize(entry.templateId) }), _jsx("td", { children: sourceLabel(entry.source) }), _jsx("td", { children: deliveryOf(entry) }), showActions ? (_jsx("td", { className: "vertebr-col-actions" })) : null] }, `history:${row.id}`));
138
+ }) })] }) }))] }));
139
+ }
@@ -0,0 +1,47 @@
1
+ import type { EmailsBoardResponse, MailLibraryTemplate, MailPreviewResponse } from "@vertebr/client";
2
+ import { type EmailRecipientOption } from "./EmailRecipientSelect";
3
+ export type EmailsBoardProps = {
4
+ data: EmailsBoardResponse;
5
+ loading?: boolean;
6
+ /** Controlled section from the admin sidebar (Compose / Templates / Activity). */
7
+ tab?: "compose" | "templates" | "activity";
8
+ onRefresh: () => void | Promise<void>;
9
+ onLoadRecipients: (resourceId: string) => Promise<EmailRecipientOption[]>;
10
+ onPreview: (payload: {
11
+ resourceId: string;
12
+ templateId: string;
13
+ rowId?: string;
14
+ input?: Record<string, unknown>;
15
+ }) => Promise<MailPreviewResponse>;
16
+ onSend: (payload: {
17
+ resourceId: string;
18
+ templateId: string;
19
+ input?: Record<string, unknown>;
20
+ audience: {
21
+ mode: "all";
22
+ } | {
23
+ mode: "emails";
24
+ emails: string[];
25
+ };
26
+ }) => Promise<{
27
+ sent: number;
28
+ failed: number;
29
+ }>;
30
+ onSchedule?: (payload: {
31
+ resourceId: string;
32
+ templateId: string;
33
+ input?: Record<string, unknown>;
34
+ audience: {
35
+ mode: "all";
36
+ } | {
37
+ mode: "emails";
38
+ emails: string[];
39
+ };
40
+ sendAt: string;
41
+ }) => Promise<void>;
42
+ onCancel?: (id: string) => Promise<void>;
43
+ onSaveLibrary?: (template: MailLibraryTemplate) => Promise<void>;
44
+ onDeleteLibrary?: (id: string) => Promise<void>;
45
+ };
46
+ export declare function EmailsBoard({ data, loading, tab: tabProp, onRefresh, onLoadRecipients, onPreview, onSend, onSchedule, onCancel, onSaveLibrary, onDeleteLibrary, }: EmailsBoardProps): import("react").JSX.Element;
47
+ //# sourceMappingURL=EmailsBoard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailsBoard.d.ts","sourceRoot":"","sources":["../../../src/components/emails/EmailsBoard.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EAEnB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,wBAAwB,CAAC;AAKhC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kFAAkF;IAClF,GAAG,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;IAC3C,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,gBAAgB,EAAE,CAChB,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACrC,SAAS,EAAE,CAAC,OAAO,EAAE;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnC,MAAM,EAAE,CAAC,OAAO,EAAE;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,QAAQ,EAAE;YAAE,IAAI,EAAE,KAAK,CAAA;SAAE,GAAG;YAAE,IAAI,EAAE,QAAQ,CAAC;YAAC,MAAM,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;KAClE,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,QAAQ,EAAE;YAAE,IAAI,EAAE,KAAK,CAAA;SAAE,GAAG;YAAE,IAAI,EAAE,QAAQ,CAAC;YAAC,MAAM,EAAE,MAAM,EAAE,CAAA;SAAE,CAAC;QACjE,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjE,eAAe,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD,CAAC;AA+PF,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,OAAO,EACP,GAAG,EAAE,OAAO,EACZ,SAAS,EACT,gBAAgB,EAChB,SAAS,EACT,MAAM,EACN,UAAU,EACV,QAAQ,EACR,aAAa,EACb,eAAe,GAChB,EAAE,gBAAgB,+BAgqBlB"}