@aglyn/plugins-email 1.0.0-beta.143

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 (144) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +7 -0
  3. package/package.json +58 -0
  4. package/src/index.d.ts +35 -0
  5. package/src/index.js +35 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/campaign-design-create-widget.d.ts +21 -0
  8. package/src/lib/components/campaign-design-create-widget.js +61 -0
  9. package/src/lib/components/campaign-design-create-widget.js.map +1 -0
  10. package/src/lib/components/campaign-sender-editor-widget.d.ts +18 -0
  11. package/src/lib/components/campaign-sender-editor-widget.js +39 -0
  12. package/src/lib/components/campaign-sender-editor-widget.js.map +1 -0
  13. package/src/lib/components/campaign-topic-options-widget.d.ts +19 -0
  14. package/src/lib/components/campaign-topic-options-widget.js +48 -0
  15. package/src/lib/components/campaign-topic-options-widget.js.map +1 -0
  16. package/src/lib/components/campaign-topic-select.d.ts +32 -0
  17. package/src/lib/components/campaign-topic-select.js +87 -0
  18. package/src/lib/components/campaign-topic-select.js.map +1 -0
  19. package/src/lib/components/dynamic-list-rule-fields.d.ts +173 -0
  20. package/src/lib/components/dynamic-list-rule-fields.js +1473 -0
  21. package/src/lib/components/dynamic-list-rule-fields.js.map +1 -0
  22. package/src/lib/components/email-blocks.d.ts +111 -0
  23. package/src/lib/components/email-blocks.js +875 -0
  24. package/src/lib/components/email-blocks.js.map +1 -0
  25. package/src/lib/components/email-design-preview.d.ts +62 -0
  26. package/src/lib/components/email-design-preview.js +174 -0
  27. package/src/lib/components/email-design-preview.js.map +1 -0
  28. package/src/lib/components/email-screens-card.d.ts +42 -0
  29. package/src/lib/components/email-screens-card.js +277 -0
  30. package/src/lib/components/email-screens-card.js.map +1 -0
  31. package/src/lib/components/email-template-detail.d.ts +48 -0
  32. package/src/lib/components/email-template-detail.js +681 -0
  33. package/src/lib/components/email-template-detail.js.map +1 -0
  34. package/src/lib/components/email-topic-detail.d.ts +32 -0
  35. package/src/lib/components/email-topic-detail.js +293 -0
  36. package/src/lib/components/email-topic-detail.js.map +1 -0
  37. package/src/lib/components/email-topics-card.d.ts +46 -0
  38. package/src/lib/components/email-topics-card.js +327 -0
  39. package/src/lib/components/email-topics-card.js.map +1 -0
  40. package/src/lib/components/email-zones.d.ts +28 -0
  41. package/src/lib/components/email-zones.js +20 -0
  42. package/src/lib/components/email-zones.js.map +1 -0
  43. package/src/lib/components/emails-console-page.d.ts +32 -0
  44. package/src/lib/components/emails-console-page.js +229 -0
  45. package/src/lib/components/emails-console-page.js.map +1 -0
  46. package/src/lib/components/emails-console-sections.d.ts +36 -0
  47. package/src/lib/components/emails-console-sections.js +108 -0
  48. package/src/lib/components/emails-console-sections.js.map +1 -0
  49. package/src/lib/components/list-detail-card.d.ts +47 -0
  50. package/src/lib/components/list-detail-card.js +273 -0
  51. package/src/lib/components/list-detail-card.js.map +1 -0
  52. package/src/lib/components/list-edit-card.d.ts +11 -0
  53. package/src/lib/components/list-edit-card.js +287 -0
  54. package/src/lib/components/list-edit-card.js.map +1 -0
  55. package/src/lib/components/list-import-drawer.d.ts +22 -0
  56. package/src/lib/components/list-import-drawer.js +662 -0
  57. package/src/lib/components/list-import-drawer.js.map +1 -0
  58. package/src/lib/components/list-members-panel.d.ts +94 -0
  59. package/src/lib/components/list-members-panel.js +686 -0
  60. package/src/lib/components/list-members-panel.js.map +1 -0
  61. package/src/lib/components/lists-card.d.ts +28 -0
  62. package/src/lib/components/lists-card.js +377 -0
  63. package/src/lib/components/lists-card.js.map +1 -0
  64. package/src/lib/components/sending-domain-detail.d.ts +26 -0
  65. package/src/lib/components/sending-domain-detail.js +496 -0
  66. package/src/lib/components/sending-domain-detail.js.map +1 -0
  67. package/src/lib/components/sending-domains-card.d.ts +33 -0
  68. package/src/lib/components/sending-domains-card.js +962 -0
  69. package/src/lib/components/sending-domains-card.js.map +1 -0
  70. package/src/lib/components/sending-sender-drawer.d.ts +94 -0
  71. package/src/lib/components/sending-sender-drawer.js +543 -0
  72. package/src/lib/components/sending-sender-drawer.js.map +1 -0
  73. package/src/lib/components/suppressions-card.d.ts +49 -0
  74. package/src/lib/components/suppressions-card.js +639 -0
  75. package/src/lib/components/suppressions-card.js.map +1 -0
  76. package/src/lib/components/use-org-email-topics.d.ts +79 -0
  77. package/src/lib/components/use-org-email-topics.js +111 -0
  78. package/src/lib/components/use-org-email-topics.js.map +1 -0
  79. package/src/lib/constants/bundle-common.d.ts +18 -0
  80. package/src/lib/constants/bundle-common.js +18 -0
  81. package/src/lib/constants/bundle-common.js.map +1 -0
  82. package/src/lib/hooks/use-org-company-options.d.ts +20 -0
  83. package/src/lib/hooks/use-org-company-options.js +138 -0
  84. package/src/lib/hooks/use-org-company-options.js.map +1 -0
  85. package/src/lib/hooks/use-org-contact-fields.d.ts +40 -0
  86. package/src/lib/hooks/use-org-contact-fields.js +91 -0
  87. package/src/lib/hooks/use-org-contact-fields.js.map +1 -0
  88. package/src/lib/hooks/use-org-contact-segments.d.ts +16 -0
  89. package/src/lib/hooks/use-org-contact-segments.js +55 -0
  90. package/src/lib/hooks/use-org-contact-segments.js.map +1 -0
  91. package/src/lib/hooks/use-org-crm-views.d.ts +8 -0
  92. package/src/lib/hooks/use-org-crm-views.js +74 -0
  93. package/src/lib/hooks/use-org-crm-views.js.map +1 -0
  94. package/src/lib/hooks/use-org-lists.d.ts +8 -0
  95. package/src/lib/hooks/use-org-lists.js +47 -0
  96. package/src/lib/hooks/use-org-lists.js.map +1 -0
  97. package/src/lib/model/email-design-document.d.ts +52 -0
  98. package/src/lib/model/email-design-document.js +62 -0
  99. package/src/lib/model/email-design-document.js.map +1 -0
  100. package/src/lib/model/index.d.ts +64 -0
  101. package/src/lib/model/index.js +71 -0
  102. package/src/lib/model/index.js.map +1 -0
  103. package/src/lib/model/sending-domain-status.d.ts +99 -0
  104. package/src/lib/model/sending-domain-status.js +196 -0
  105. package/src/lib/model/sending-domain-status.js.map +1 -0
  106. package/src/lib/model/template-provenance.d.ts +113 -0
  107. package/src/lib/model/template-provenance.js +107 -0
  108. package/src/lib/model/template-provenance.js.map +1 -0
  109. package/src/lib/model/template-report.d.ts +158 -0
  110. package/src/lib/model/template-report.js +249 -0
  111. package/src/lib/model/template-report.js.map +1 -0
  112. package/src/lib/plugin.d.ts +27 -0
  113. package/src/lib/plugin.js +163 -0
  114. package/src/lib/plugin.js.map +1 -0
  115. package/src/lib/server-console.d.ts +116 -0
  116. package/src/lib/server-console.js +422 -0
  117. package/src/lib/server-console.js.map +1 -0
  118. package/src/lib/server-email-drafts.d.ts +104 -0
  119. package/src/lib/server-email-drafts.js +381 -0
  120. package/src/lib/server-email-drafts.js.map +1 -0
  121. package/src/lib/server-list-gate.d.ts +183 -0
  122. package/src/lib/server-list-gate.js +365 -0
  123. package/src/lib/server-list-gate.js.map +1 -0
  124. package/src/lib/server-list-import.d.ts +199 -0
  125. package/src/lib/server-list-import.js +632 -0
  126. package/src/lib/server-list-import.js.map +1 -0
  127. package/src/lib/server-suppressions.d.ts +135 -0
  128. package/src/lib/server-suppressions.js +295 -0
  129. package/src/lib/server-suppressions.js.map +1 -0
  130. package/src/lib/server.d.ts +19 -0
  131. package/src/lib/server.js +834 -0
  132. package/src/lib/server.js.map +1 -0
  133. package/src/lib/site.d.ts +26 -0
  134. package/src/lib/site.js +81 -0
  135. package/src/lib/site.js.map +1 -0
  136. package/src/lib/unsubscribe-link.d.ts +311 -0
  137. package/src/lib/unsubscribe-link.js +398 -0
  138. package/src/lib/unsubscribe-link.js.map +1 -0
  139. package/src/lib/utils/create-email-screen.d.ts +59 -0
  140. package/src/lib/utils/create-email-screen.js +59 -0
  141. package/src/lib/utils/create-email-screen.js.map +1 -0
  142. package/src/lib/utils/generate-preset-id.d.ts +19 -0
  143. package/src/lib/utils/generate-preset-id.js +25 -0
  144. package/src/lib/utils/generate-preset-id.js.map +1 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { jsx as _jsx } from "react/jsx-runtime";
18
+ import { activeEmailTopics, DEFAULT_CAMPAIGN_TOPIC_ID } from "@aglyn/aglyn";
19
+ import { MenuItem, TextField } from "@mui/material";
20
+ import { useEffect } from "react";
21
+ import { useOrgEmailTopics } from "./use-org-email-topics.js";
22
+ /**
23
+ * WHICH STREAM this campaign belongs to, chosen before it is sent.
24
+ *
25
+ * The topic decides three things at once: which recipients the send skips
26
+ * (anyone who has left this stream), what the preference page linked from the
27
+ * footer highlights as "this email", and which stream a resulting unsubscribe
28
+ * is recorded against. So it is a composer field rather than a setting — it is
29
+ * a property of the message, and the same site sends promotions and a
30
+ * newsletter in the same week.
31
+ *
32
+ * ## Why this is its own component
33
+ *
34
+ * The composer (`campaign-composer.tsx`) is a large component under concurrent
35
+ * edit. Keeping the picker separate means it can be dropped into the
36
+ * composer's field stack as one line and carries its own read of the org
37
+ * catalog, rather than threading a fifth collection listener through a
38
+ * component that already holds four.
39
+ *
40
+ * Retired topics are not offered. A campaign may not be composed under a
41
+ * stream nobody can unsubscribe from — but a campaign already SENT under one
42
+ * keeps resolving, which is why retiring is not deleting. See
43
+ * `email-topics-card.tsx`.
44
+ */ export function CampaignTopicSelect(props) {
45
+ const { hostId, value, onChange, disabled } = props;
46
+ const { topics } = useOrgEmailTopics(hostId);
47
+ const options = activeEmailTopics(topics);
48
+ /*
49
+ * SETTLE ON A REAL OPTION, rather than submitting an empty topic.
50
+ *
51
+ * The send resolves an absent topic to the default anyway, so an empty value
52
+ * would still produce a correct send — but it would produce one whose topic
53
+ * the composer never showed the author. A merchant is entitled to see which
54
+ * stream they are about to mail before they press send, and a select that
55
+ * reads blank while the server has already decided is the shape of a field
56
+ * that lies.
57
+ *
58
+ * It also re-settles when the chosen topic leaves the list, which is what a
59
+ * topic retired in another tab looks like from here.
60
+ */ useEffect(()=>{
61
+ var _options_find;
62
+ if (!options.length) return;
63
+ if (options.some((topic)=>topic.id === value)) return;
64
+ const fallback = (_options_find = options.find((topic)=>topic.id === DEFAULT_CAMPAIGN_TOPIC_ID)) != null ? _options_find : options[0];
65
+ onChange(fallback.id);
66
+ }, [
67
+ options,
68
+ value,
69
+ onChange
70
+ ]);
71
+ return /*#__PURE__*/ _jsx(TextField, {
72
+ select: true,
73
+ size: "small",
74
+ label: "Topic",
75
+ value: options.some((topic)=>topic.id === value) ? value : '',
76
+ onChange: (event)=>onChange(event.target.value),
77
+ disabled: disabled || !options.length,
78
+ helperText: 'Recipients who have left this stream are skipped, and the ' + 'unsubscribe link in this email offers to stop it on its own.',
79
+ children: options.map((topic)=>/*#__PURE__*/ _jsx(MenuItem, {
80
+ value: topic.id,
81
+ children: topic.name
82
+ }, topic.id))
83
+ });
84
+ }
85
+ export default CampaignTopicSelect;
86
+
87
+ //# sourceMappingURL=campaign-topic-select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/campaign-topic-select.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { activeEmailTopics, DEFAULT_CAMPAIGN_TOPIC_ID } from '@aglyn/aglyn'\nimport { MenuItem, TextField } from '@mui/material'\nimport { useEffect } from 'react'\nimport { useOrgEmailTopics } from './use-org-email-topics'\n\nexport interface CampaignTopicSelectProps {\n hostId: string\n /** The chosen topic id; empty until the effect below settles it. */\n value: string\n onChange: (topicId: string) => void\n disabled?: boolean\n}\n\n/**\n * WHICH STREAM this campaign belongs to, chosen before it is sent.\n *\n * The topic decides three things at once: which recipients the send skips\n * (anyone who has left this stream), what the preference page linked from the\n * footer highlights as \"this email\", and which stream a resulting unsubscribe\n * is recorded against. So it is a composer field rather than a setting — it is\n * a property of the message, and the same site sends promotions and a\n * newsletter in the same week.\n *\n * ## Why this is its own component\n *\n * The composer (`campaign-composer.tsx`) is a large component under concurrent\n * edit. Keeping the picker separate means it can be dropped into the\n * composer's field stack as one line and carries its own read of the org\n * catalog, rather than threading a fifth collection listener through a\n * component that already holds four.\n *\n * Retired topics are not offered. A campaign may not be composed under a\n * stream nobody can unsubscribe from — but a campaign already SENT under one\n * keeps resolving, which is why retiring is not deleting. See\n * `email-topics-card.tsx`.\n */\nexport function CampaignTopicSelect(props: CampaignTopicSelectProps) {\n const { hostId, value, onChange, disabled } = props\n const { topics } = useOrgEmailTopics(hostId)\n const options = activeEmailTopics(topics)\n\n /*\n * SETTLE ON A REAL OPTION, rather than submitting an empty topic.\n *\n * The send resolves an absent topic to the default anyway, so an empty value\n * would still produce a correct send — but it would produce one whose topic\n * the composer never showed the author. A merchant is entitled to see which\n * stream they are about to mail before they press send, and a select that\n * reads blank while the server has already decided is the shape of a field\n * that lies.\n *\n * It also re-settles when the chosen topic leaves the list, which is what a\n * topic retired in another tab looks like from here.\n */\n useEffect(() => {\n if (!options.length) return\n if (options.some((topic) => topic.id === value)) return\n const fallback =\n options.find((topic) => topic.id === DEFAULT_CAMPAIGN_TOPIC_ID) ??\n options[0]\n onChange(fallback.id)\n }, [options, value, onChange])\n\n return (\n <TextField\n select\n size=\"small\"\n label=\"Topic\"\n value={options.some((topic) => topic.id === value) ? value : ''}\n onChange={(event) => onChange(event.target.value)}\n disabled={disabled || !options.length}\n helperText={\n 'Recipients who have left this stream are skipped, and the ' +\n 'unsubscribe link in this email offers to stop it on its own.'\n }\n >\n {options.map((topic) => (\n <MenuItem key={topic.id} value={topic.id}>\n {topic.name}\n </MenuItem>\n ))}\n </TextField>\n )\n}\n\nexport default CampaignTopicSelect\n"],"names":["activeEmailTopics","DEFAULT_CAMPAIGN_TOPIC_ID","MenuItem","TextField","useEffect","useOrgEmailTopics","CampaignTopicSelect","props","hostId","value","onChange","disabled","topics","options","length","some","topic","id","fallback","find","select","size","label","event","target","helperText","map","name"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA,SAASA,iBAAiB,EAAEC,yBAAyB,QAAQ,eAAc;AAC3E,SAASC,QAAQ,EAAEC,SAAS,QAAQ,gBAAe;AACnD,SAASC,SAAS,QAAQ,QAAO;AACjC,SAASC,iBAAiB,QAAQ,4BAAwB;AAU1D;;;;;;;;;;;;;;;;;;;;;;CAsBC,GACD,OAAO,SAASC,oBAAoBC,KAA+B;IACjE,MAAM,EAAEC,MAAM,EAAEC,KAAK,EAAEC,QAAQ,EAAEC,QAAQ,EAAE,GAAGJ;IAC9C,MAAM,EAAEK,MAAM,EAAE,GAAGP,kBAAkBG;IACrC,MAAMK,UAAUb,kBAAkBY;IAElC;;;;;;;;;;;;GAYC,GACDR,UAAU;YAINS;QAHF,IAAI,CAACA,QAAQC,MAAM,EAAE;QACrB,IAAID,QAAQE,IAAI,CAAC,CAACC,QAAUA,MAAMC,EAAE,KAAKR,QAAQ;QACjD,MAAMS,YACJL,gBAAAA,QAAQM,IAAI,CAAC,CAACH,QAAUA,MAAMC,EAAE,KAAKhB,sCAArCY,gBACAA,OAAO,CAAC,EAAE;QACZH,SAASQ,SAASD,EAAE;IACtB,GAAG;QAACJ;QAASJ;QAAOC;KAAS;IAE7B,qBACE,KAACP;QACCiB,MAAM;QACNC,MAAK;QACLC,OAAM;QACNb,OAAOI,QAAQE,IAAI,CAAC,CAACC,QAAUA,MAAMC,EAAE,KAAKR,SAASA,QAAQ;QAC7DC,UAAU,CAACa,QAAUb,SAASa,MAAMC,MAAM,CAACf,KAAK;QAChDE,UAAUA,YAAY,CAACE,QAAQC,MAAM;QACrCW,YACE,+DACA;kBAGDZ,QAAQa,GAAG,CAAC,CAACV,sBACZ,KAACd;gBAAwBO,OAAOO,MAAMC,EAAE;0BACrCD,MAAMW,IAAI;eADEX,MAAMC,EAAE;;AAM/B;AAEA,eAAeX,oBAAmB"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Every field of `DynamicListRule`, with a control for each.
3
+ *
4
+ * ## Why the whole rule, and why in one place
5
+ *
6
+ * Every dimension the matcher reads has a control here, including the two
7
+ * that are answered by a keyed lookup rather than by the person's own record
8
+ * — engagement, and membership of another audience — and the combinator that
9
+ * decides whether the filters are ANDed, ORed or inverted. Needing to
10
+ * hand-write a stored structure to reach a shipped engine is a missing
11
+ * picker, not a power-user affordance, which is the standard this form is
12
+ * held to rather than a count of fields.
13
+ *
14
+ * One component rather than a form on the create card and a second on the edit
15
+ * page: the two would drift, and the half that drifts is whichever the
16
+ * merchant is not looking at. A rule authored at creation and the same rule
17
+ * reopened for editing are the same questions.
18
+ *
19
+ * ## The draft is text, the value is a rule
20
+ *
21
+ * The controls hold what was TYPED — `vip, ` is a legitimate intermediate
22
+ * state of a tag list, and a component that round-tripped every keystroke
23
+ * through `normalizeDynamicListRule` would delete the comma the moment it was
24
+ * typed. So the draft is strings, `draftToRule` is the one place it becomes a
25
+ * rule, and the parent never sees anything else.
26
+ *
27
+ * ## Money is entered the way a merchant says it
28
+ *
29
+ * `ltvCentsAtLeast` is stored in cents. A field labeled for the stored unit
30
+ * turns "customers who spent over 500" into customers who spent over five
31
+ * dollars, silently, and the audience looks plausible either way. The control
32
+ * takes whole currency units and this file owns the only multiplication.
33
+ *
34
+ * ## It reads the segments, and only when it is on screen
35
+ *
36
+ * The segment picker needs the org's saved segments, through
37
+ * `useOrgContactSegments`. That listen belongs to this component so it opens
38
+ * when a rule is actually being authored — the audiences section's cost is a
39
+ * list of lists, and a segment read on arrival would be a read for a form
40
+ * nobody has opened.
41
+ */
42
+ import { type ContactLifecycleStage, type ContactSource, type DynamicListCustomClause, type DynamicListRule, type DynamicListSource } from '@aglyn/aglyn';
43
+ /**
44
+ * The rule, in sentences a merchant can read back to themselves.
45
+ *
46
+ * Every clause names a dimension that is actually SET, so a rule with four
47
+ * dimensions reads as four clauses and a rule with one reads as one — an
48
+ * explanation that also lists the fields a rule does not use is an explanation
49
+ * the reader has to subtract from. Rendered live beside the controls, because
50
+ * the question a filter form has to answer continuously is "who does this
51
+ * describe", and a form of eleven boxes does not answer it on its own.
52
+ */
53
+ /** Names for the ids a rule stores, so a clause can say "Customers", not a uid. */
54
+ export interface DynamicListRuleNames {
55
+ /** List id → its name. */
56
+ lists?: Record<string, string>;
57
+ /** Segment id → its name. */
58
+ segments?: Record<string, string>;
59
+ /** Saved Contacts view id → its name. */
60
+ views?: Record<string, string>;
61
+ /** Campaign id → its name. */
62
+ campaigns?: Record<string, string>;
63
+ /** Team member uid → how they read on the roster. */
64
+ members?: Record<string, string>;
65
+ /** Company id → its name. */
66
+ companies?: Record<string, string>;
67
+ /** Custom field key → the definition's label. */
68
+ fields?: Record<string, string>;
69
+ }
70
+ export declare function describeDynamicListRule(rule: DynamicListRule, names?: DynamicListRuleNames): string[];
71
+ /**
72
+ * How the filters below combine.
73
+ *
74
+ * The three shapes this form authors, mapped onto the rule's two operators:
75
+ * `all` is the plain AND block, `none` is that block with `negate`, and `any`
76
+ * puts each filter into its own OR branch. A saved segment is outside all
77
+ * three — it is resolved once from `segmentId` and always applies, which the
78
+ * control says on screen rather than leaving to be discovered.
79
+ */
80
+ export type DynamicListRuleMatch = 'all' | 'any' | 'none';
81
+ /** The rule as the controls hold it: what was typed, not what it means. */
82
+ export interface DynamicListRuleDraft {
83
+ sources: DynamicListSource[];
84
+ match: DynamicListRuleMatch;
85
+ segmentId: string;
86
+ /** A saved Contacts view's id, resolved by the sweep beside the segment (AGL-2617). */
87
+ viewId: string;
88
+ tags: string;
89
+ captureSources: ContactSource[];
90
+ formNames: string;
91
+ /** Campaign ids, from the site's own campaigns picker. */
92
+ campaignIds: string[];
93
+ /** `yyyy-mm-dd`, read as UTC — the same instant `Date.parse` gives it. */
94
+ createdAfter: string;
95
+ createdBefore: string;
96
+ ordersCountAtLeast: string;
97
+ /** WHOLE currency units. `draftToRule` is the only place this becomes cents. */
98
+ ltvAtLeast: string;
99
+ lastPurchaseWithinDays: string;
100
+ noPurchaseForDays: string;
101
+ openedWithinDays: string;
102
+ clickedWithinDays: string;
103
+ notOpenedForDays: string;
104
+ notClickedForDays: string;
105
+ inListIds: string[];
106
+ notInListIds: string[];
107
+ ownerUids: string[];
108
+ lifecycleStages: ContactLifecycleStage[];
109
+ companyIds: string[];
110
+ custom: DynamicListCustomClause[];
111
+ /**
112
+ * The re-engagement window (AGL-2616), typed like the purchase windows
113
+ * — days, as text, so an empty box is not a zero-day window.
114
+ */
115
+ engagedWithinDays: string;
116
+ }
117
+ export declare const EMPTY_RULE_DRAFT: DynamicListRuleDraft;
118
+ /** Comma-separated free text → the trimmed, non-empty values. */
119
+ export declare const splitRuleList: (value: string) => string[];
120
+ /**
121
+ * A stored rule, as the controls should show it.
122
+ *
123
+ * ⚠️ The three shapes {@link DynamicListRuleMatch} names are the shapes this
124
+ * form AUTHORS. A rule written through the REST API can hold branches the
125
+ * controls have no place for — a branch with its own `negate`, or top-level
126
+ * filters alongside branches — and this reads such a rule back as the union
127
+ * of every filter in it, in `any` mode. It is not a silent rewrite: the
128
+ * sentences above the controls are computed from `draftToRule(draft)`, so
129
+ * what a save would store is on screen before the reader saves it.
130
+ */
131
+ export declare function ruleToDraft(rule: DynamicListRule): DynamicListRuleDraft;
132
+ /** What the controls mean, coerced by the function the matcher reads through. */
133
+ export declare function draftToRule(draft: DynamicListRuleDraft): DynamicListRule;
134
+ export interface DynamicListRuleFieldsProps {
135
+ /** `['orgs', orgId]` — the resolved org scope the caller already holds. */
136
+ scope: readonly [string, string];
137
+ /**
138
+ * The site whose campaigns the campaign picker offers.
139
+ *
140
+ * A list is org-owned and its rule is materialized against ONE site's silos,
141
+ * which is the same site whose campaigns a form or a screen can be filed
142
+ * under. Offering the org's every campaign would offer ids the sweep can
143
+ * never match, because the membership it reads was written by that site.
144
+ */
145
+ hostId: string;
146
+ draft: DynamicListRuleDraft;
147
+ onChange: (draft: DynamicListRuleDraft) => void;
148
+ /**
149
+ * The audience being edited, when there is one.
150
+ *
151
+ * Kept out of its own list pickers. A rule that referred to the list it
152
+ * fills would oscillate — see `dynamicListRuleWithoutListReference`, which
153
+ * strips it at the sweep as well, because a rule outlives any one form.
154
+ * Offering it here and stripping it there would show a merchant a filter
155
+ * that quietly does nothing.
156
+ */
157
+ listId?: string;
158
+ }
159
+ export declare function DynamicListRuleFields(props: DynamicListRuleFieldsProps): import("react").JSX.Element;
160
+ export declare namespace DynamicListRuleFields {
161
+ var displayName: string;
162
+ }
163
+ /**
164
+ * A draft seeded from a rule that arrives after the form does.
165
+ *
166
+ * The edit page mounts before its list document lands, so the controls are
167
+ * built from an empty rule and filled in once. Seeding ONCE per subject is the
168
+ * whole point: the document is a live listen, and re-seeding on every snapshot
169
+ * would overwrite whatever the reader had typed each time anything on the list
170
+ * changed — including the snapshot the reader's own save produces.
171
+ */
172
+ export declare function useRuleDraft(rule: DynamicListRule | undefined, resetKey: string): [DynamicListRuleDraft, (draft: DynamicListRuleDraft) => void];
173
+ export default DynamicListRuleFields;