@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.
- package/LICENSE +201 -0
- package/README.md +7 -0
- package/package.json +58 -0
- package/src/index.d.ts +35 -0
- package/src/index.js +35 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/campaign-design-create-widget.d.ts +21 -0
- package/src/lib/components/campaign-design-create-widget.js +61 -0
- package/src/lib/components/campaign-design-create-widget.js.map +1 -0
- package/src/lib/components/campaign-sender-editor-widget.d.ts +18 -0
- package/src/lib/components/campaign-sender-editor-widget.js +39 -0
- package/src/lib/components/campaign-sender-editor-widget.js.map +1 -0
- package/src/lib/components/campaign-topic-options-widget.d.ts +19 -0
- package/src/lib/components/campaign-topic-options-widget.js +48 -0
- package/src/lib/components/campaign-topic-options-widget.js.map +1 -0
- package/src/lib/components/campaign-topic-select.d.ts +32 -0
- package/src/lib/components/campaign-topic-select.js +87 -0
- package/src/lib/components/campaign-topic-select.js.map +1 -0
- package/src/lib/components/dynamic-list-rule-fields.d.ts +173 -0
- package/src/lib/components/dynamic-list-rule-fields.js +1473 -0
- package/src/lib/components/dynamic-list-rule-fields.js.map +1 -0
- package/src/lib/components/email-blocks.d.ts +111 -0
- package/src/lib/components/email-blocks.js +875 -0
- package/src/lib/components/email-blocks.js.map +1 -0
- package/src/lib/components/email-design-preview.d.ts +62 -0
- package/src/lib/components/email-design-preview.js +174 -0
- package/src/lib/components/email-design-preview.js.map +1 -0
- package/src/lib/components/email-screens-card.d.ts +42 -0
- package/src/lib/components/email-screens-card.js +277 -0
- package/src/lib/components/email-screens-card.js.map +1 -0
- package/src/lib/components/email-template-detail.d.ts +48 -0
- package/src/lib/components/email-template-detail.js +681 -0
- package/src/lib/components/email-template-detail.js.map +1 -0
- package/src/lib/components/email-topic-detail.d.ts +32 -0
- package/src/lib/components/email-topic-detail.js +293 -0
- package/src/lib/components/email-topic-detail.js.map +1 -0
- package/src/lib/components/email-topics-card.d.ts +46 -0
- package/src/lib/components/email-topics-card.js +327 -0
- package/src/lib/components/email-topics-card.js.map +1 -0
- package/src/lib/components/email-zones.d.ts +28 -0
- package/src/lib/components/email-zones.js +20 -0
- package/src/lib/components/email-zones.js.map +1 -0
- package/src/lib/components/emails-console-page.d.ts +32 -0
- package/src/lib/components/emails-console-page.js +229 -0
- package/src/lib/components/emails-console-page.js.map +1 -0
- package/src/lib/components/emails-console-sections.d.ts +36 -0
- package/src/lib/components/emails-console-sections.js +108 -0
- package/src/lib/components/emails-console-sections.js.map +1 -0
- package/src/lib/components/list-detail-card.d.ts +47 -0
- package/src/lib/components/list-detail-card.js +273 -0
- package/src/lib/components/list-detail-card.js.map +1 -0
- package/src/lib/components/list-edit-card.d.ts +11 -0
- package/src/lib/components/list-edit-card.js +287 -0
- package/src/lib/components/list-edit-card.js.map +1 -0
- package/src/lib/components/list-import-drawer.d.ts +22 -0
- package/src/lib/components/list-import-drawer.js +662 -0
- package/src/lib/components/list-import-drawer.js.map +1 -0
- package/src/lib/components/list-members-panel.d.ts +94 -0
- package/src/lib/components/list-members-panel.js +686 -0
- package/src/lib/components/list-members-panel.js.map +1 -0
- package/src/lib/components/lists-card.d.ts +28 -0
- package/src/lib/components/lists-card.js +377 -0
- package/src/lib/components/lists-card.js.map +1 -0
- package/src/lib/components/sending-domain-detail.d.ts +26 -0
- package/src/lib/components/sending-domain-detail.js +496 -0
- package/src/lib/components/sending-domain-detail.js.map +1 -0
- package/src/lib/components/sending-domains-card.d.ts +33 -0
- package/src/lib/components/sending-domains-card.js +962 -0
- package/src/lib/components/sending-domains-card.js.map +1 -0
- package/src/lib/components/sending-sender-drawer.d.ts +94 -0
- package/src/lib/components/sending-sender-drawer.js +543 -0
- package/src/lib/components/sending-sender-drawer.js.map +1 -0
- package/src/lib/components/suppressions-card.d.ts +49 -0
- package/src/lib/components/suppressions-card.js +639 -0
- package/src/lib/components/suppressions-card.js.map +1 -0
- package/src/lib/components/use-org-email-topics.d.ts +79 -0
- package/src/lib/components/use-org-email-topics.js +111 -0
- package/src/lib/components/use-org-email-topics.js.map +1 -0
- package/src/lib/constants/bundle-common.d.ts +18 -0
- package/src/lib/constants/bundle-common.js +18 -0
- package/src/lib/constants/bundle-common.js.map +1 -0
- package/src/lib/hooks/use-org-company-options.d.ts +20 -0
- package/src/lib/hooks/use-org-company-options.js +138 -0
- package/src/lib/hooks/use-org-company-options.js.map +1 -0
- package/src/lib/hooks/use-org-contact-fields.d.ts +40 -0
- package/src/lib/hooks/use-org-contact-fields.js +91 -0
- package/src/lib/hooks/use-org-contact-fields.js.map +1 -0
- package/src/lib/hooks/use-org-contact-segments.d.ts +16 -0
- package/src/lib/hooks/use-org-contact-segments.js +55 -0
- package/src/lib/hooks/use-org-contact-segments.js.map +1 -0
- package/src/lib/hooks/use-org-crm-views.d.ts +8 -0
- package/src/lib/hooks/use-org-crm-views.js +74 -0
- package/src/lib/hooks/use-org-crm-views.js.map +1 -0
- package/src/lib/hooks/use-org-lists.d.ts +8 -0
- package/src/lib/hooks/use-org-lists.js +47 -0
- package/src/lib/hooks/use-org-lists.js.map +1 -0
- package/src/lib/model/email-design-document.d.ts +52 -0
- package/src/lib/model/email-design-document.js +62 -0
- package/src/lib/model/email-design-document.js.map +1 -0
- package/src/lib/model/index.d.ts +64 -0
- package/src/lib/model/index.js +71 -0
- package/src/lib/model/index.js.map +1 -0
- package/src/lib/model/sending-domain-status.d.ts +99 -0
- package/src/lib/model/sending-domain-status.js +196 -0
- package/src/lib/model/sending-domain-status.js.map +1 -0
- package/src/lib/model/template-provenance.d.ts +113 -0
- package/src/lib/model/template-provenance.js +107 -0
- package/src/lib/model/template-provenance.js.map +1 -0
- package/src/lib/model/template-report.d.ts +158 -0
- package/src/lib/model/template-report.js +249 -0
- package/src/lib/model/template-report.js.map +1 -0
- package/src/lib/plugin.d.ts +27 -0
- package/src/lib/plugin.js +163 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/server-console.d.ts +116 -0
- package/src/lib/server-console.js +422 -0
- package/src/lib/server-console.js.map +1 -0
- package/src/lib/server-email-drafts.d.ts +104 -0
- package/src/lib/server-email-drafts.js +381 -0
- package/src/lib/server-email-drafts.js.map +1 -0
- package/src/lib/server-list-gate.d.ts +183 -0
- package/src/lib/server-list-gate.js +365 -0
- package/src/lib/server-list-gate.js.map +1 -0
- package/src/lib/server-list-import.d.ts +199 -0
- package/src/lib/server-list-import.js +632 -0
- package/src/lib/server-list-import.js.map +1 -0
- package/src/lib/server-suppressions.d.ts +135 -0
- package/src/lib/server-suppressions.js +295 -0
- package/src/lib/server-suppressions.js.map +1 -0
- package/src/lib/server.d.ts +19 -0
- package/src/lib/server.js +834 -0
- package/src/lib/server.js.map +1 -0
- package/src/lib/site.d.ts +26 -0
- package/src/lib/site.js +81 -0
- package/src/lib/site.js.map +1 -0
- package/src/lib/unsubscribe-link.d.ts +311 -0
- package/src/lib/unsubscribe-link.js +398 -0
- package/src/lib/unsubscribe-link.js.map +1 -0
- package/src/lib/utils/create-email-screen.d.ts +59 -0
- package/src/lib/utils/create-email-screen.js +59 -0
- package/src/lib/utils/create-email-screen.js.map +1 -0
- package/src/lib/utils/generate-preset-id.d.ts +19 -0
- package/src/lib/utils/generate-preset-id.js +25 -0
- package/src/lib/utils/generate-preset-id.js.map +1 -0
|
@@ -0,0 +1,91 @@
|
|
|
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 { _ as _extends } from "@swc/helpers/_/_extends";
|
|
18
|
+
/**
|
|
19
|
+
* The org's custom contact field DEFINITIONS, for the rule editor to offer
|
|
20
|
+
* (AGL-2603).
|
|
21
|
+
*
|
|
22
|
+
* A twin of `use-org-lists.ts`: a LOOKUP that fills one control's options,
|
|
23
|
+
* bounded so an org cannot turn the dropdown into an unbounded read. The
|
|
24
|
+
* definitions live in `orgs/{orgId}/contactFields` and the values a rule
|
|
25
|
+
* compares against live under each contact facet's `custom`, keyed by the
|
|
26
|
+
* definition's `key` — so the picker offers keys, and the clause it authors
|
|
27
|
+
* names one.
|
|
28
|
+
*
|
|
29
|
+
* ## Scoped like the contacts themselves
|
|
30
|
+
*
|
|
31
|
+
* The rules gate the collection on the contacts predicate, per document, so
|
|
32
|
+
* a collaborator scoped to one site reading it unfiltered is refused rather
|
|
33
|
+
* than shown less. The viewer's scope tokens narrow the query the way every
|
|
34
|
+
* CRM read is narrowed; an org-wide member reads without a filter, which is
|
|
35
|
+
* what keeps a definition the backfill never stamped from vanishing for them.
|
|
36
|
+
*
|
|
37
|
+
* ## A retired field is offered nowhere new
|
|
38
|
+
*
|
|
39
|
+
* `retiredAt` is a retirement, not a deletion: values written under the key
|
|
40
|
+
* survive and an export still reads them back. A rule written before the
|
|
41
|
+
* retirement keeps its clause — the editor shows the key it names — but the
|
|
42
|
+
* picker stops offering the field, because a merchant building a new
|
|
43
|
+
* audience on a field their team has retired is building it on a value
|
|
44
|
+
* nobody is maintaining.
|
|
45
|
+
*/ import { CRM_COLLECTIONS } from "@aglyn/aglyn";
|
|
46
|
+
import { collection, limit, query, where } from "firebase/firestore";
|
|
47
|
+
import { useMemo } from "react";
|
|
48
|
+
import { useFirestore, useFirestoreCollection, useScopeTokens } from "@aglyn/tenant-feature-instance";
|
|
49
|
+
/** The picker's window. Matches the list and segment pickers beside it. */ export const CONTACT_FIELD_PICKER_LIMIT = 50;
|
|
50
|
+
export function useOrgContactFields(scope) {
|
|
51
|
+
const firestore = useFirestore();
|
|
52
|
+
const { tokens, orgWide, loaded } = useScopeTokens(scope[1]);
|
|
53
|
+
const { data, status } = useFirestoreCollection(()=>// Not until the viewer's reach is known: `useScopeTokens` reports
|
|
54
|
+
// org-wide while loading, and a scoped member's first render would
|
|
55
|
+
// send an unfiltered query the rules deny per document.
|
|
56
|
+
loaded ? query(collection(firestore, scope[0], scope[1], CRM_COLLECTIONS.contactFields), ...orgWide ? [] : [
|
|
57
|
+
where('visibleTo', 'array-contains-any', tokens)
|
|
58
|
+
], limit(CONTACT_FIELD_PICKER_LIMIT)) : null, [
|
|
59
|
+
firestore,
|
|
60
|
+
scope[0],
|
|
61
|
+
scope[1],
|
|
62
|
+
loaded,
|
|
63
|
+
orgWide,
|
|
64
|
+
tokens
|
|
65
|
+
], {
|
|
66
|
+
idField: '$id'
|
|
67
|
+
});
|
|
68
|
+
return useMemo(()=>({
|
|
69
|
+
fields: (data != null ? data : []).filter((field)=>!field.retiredAt && typeof field.key === 'string').sort((a, b)=>{
|
|
70
|
+
var _a_order, _b_order;
|
|
71
|
+
return ((_a_order = a.order) != null ? _a_order : 0) - ((_b_order = b.order) != null ? _b_order : 0);
|
|
72
|
+
}).map(({ $id, key, label, type, options, order })=>_extends({
|
|
73
|
+
$id,
|
|
74
|
+
key,
|
|
75
|
+
label: label || key,
|
|
76
|
+
type
|
|
77
|
+
}, options ? {
|
|
78
|
+
options
|
|
79
|
+
} : {}, {
|
|
80
|
+
order: order != null ? order : 0
|
|
81
|
+
})),
|
|
82
|
+
ready: loaded && status !== 'loading'
|
|
83
|
+
}), [
|
|
84
|
+
data,
|
|
85
|
+
loaded,
|
|
86
|
+
status
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
export default useOrgContactFields;
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=use-org-contact-fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/hooks/use-org-contact-fields.ts"],"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\n/**\n * The org's custom contact field DEFINITIONS, for the rule editor to offer\n * (AGL-2603).\n *\n * A twin of `use-org-lists.ts`: a LOOKUP that fills one control's options,\n * bounded so an org cannot turn the dropdown into an unbounded read. The\n * definitions live in `orgs/{orgId}/contactFields` and the values a rule\n * compares against live under each contact facet's `custom`, keyed by the\n * definition's `key` — so the picker offers keys, and the clause it authors\n * names one.\n *\n * ## Scoped like the contacts themselves\n *\n * The rules gate the collection on the contacts predicate, per document, so\n * a collaborator scoped to one site reading it unfiltered is refused rather\n * than shown less. The viewer's scope tokens narrow the query the way every\n * CRM read is narrowed; an org-wide member reads without a filter, which is\n * what keeps a definition the backfill never stamped from vanishing for them.\n *\n * ## A retired field is offered nowhere new\n *\n * `retiredAt` is a retirement, not a deletion: values written under the key\n * survive and an export still reads them back. A rule written before the\n * retirement keeps its clause — the editor shows the key it names — but the\n * picker stops offering the field, because a merchant building a new\n * audience on a field their team has retired is building it on a value\n * nobody is maintaining.\n */\n\nimport { CRM_COLLECTIONS, type ContactFieldDefinition } from '@aglyn/aglyn'\nimport { collection, limit, query, where } from 'firebase/firestore'\nimport { useMemo } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useScopeTokens,\n} from '@aglyn/tenant-feature-instance'\n\n/** One definition, as the picker and the value control need it. */\nexport type OrgContactFieldOption = Pick<\n ContactFieldDefinition,\n 'key' | 'label' | 'type' | 'options' | 'order'\n> & { $id: string }\n\n/** The picker's window. Matches the list and segment pickers beside it. */\nexport const CONTACT_FIELD_PICKER_LIMIT = 50\n\nexport function useOrgContactFields(\n scope: readonly [string, string],\n): { fields: OrgContactFieldOption[]; ready: boolean } {\n const firestore = useFirestore()\n const { tokens, orgWide, loaded } = useScopeTokens(scope[1])\n const { data, status } = useFirestoreCollection<\n OrgContactFieldOption & { retiredAt?: number | null }\n >(\n () =>\n // Not until the viewer's reach is known: `useScopeTokens` reports\n // org-wide while loading, and a scoped member's first render would\n // send an unfiltered query the rules deny per document.\n loaded\n ? query(\n collection(\n firestore,\n scope[0],\n scope[1],\n CRM_COLLECTIONS.contactFields,\n ),\n ...(orgWide ? [] : [where('visibleTo', 'array-contains-any', tokens)]),\n limit(CONTACT_FIELD_PICKER_LIMIT),\n )\n : null,\n [firestore, scope[0], scope[1], loaded, orgWide, tokens],\n { idField: '$id' },\n )\n return useMemo(\n () => ({\n fields: (data ?? [])\n .filter((field) => !field.retiredAt && typeof field.key === 'string')\n .sort((a, b) => (a.order ?? 0) - (b.order ?? 0))\n .map(({ $id, key, label, type, options, order }) => ({\n $id,\n key,\n label: label || key,\n type,\n ...(options ? { options } : {}),\n order: order ?? 0,\n })),\n ready: loaded && status !== 'loading',\n }),\n [data, loaded, status],\n )\n}\n\nexport default useOrgContactFields\n"],"names":["CRM_COLLECTIONS","collection","limit","query","where","useMemo","useFirestore","useFirestoreCollection","useScopeTokens","CONTACT_FIELD_PICKER_LIMIT","useOrgContactFields","scope","firestore","tokens","orgWide","loaded","data","status","contactFields","idField","fields","filter","field","retiredAt","key","sort","a","b","order","map","$id","label","type","options","ready"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GAED,SAASA,eAAe,QAAqC,eAAc;AAC3E,SAASC,UAAU,EAAEC,KAAK,EAAEC,KAAK,EAAEC,KAAK,QAAQ,qBAAoB;AACpE,SAASC,OAAO,QAAQ,QAAO;AAC/B,SACEC,YAAY,EACZC,sBAAsB,EACtBC,cAAc,QACT,iCAAgC;AAQvC,yEAAyE,GACzE,OAAO,MAAMC,6BAA6B,GAAE;AAE5C,OAAO,SAASC,oBACdC,KAAgC;IAEhC,MAAMC,YAAYN;IAClB,MAAM,EAAEO,MAAM,EAAEC,OAAO,EAAEC,MAAM,EAAE,GAAGP,eAAeG,KAAK,CAAC,EAAE;IAC3D,MAAM,EAAEK,IAAI,EAAEC,MAAM,EAAE,GAAGV,uBAGvB,IACE,kEAAkE;QAClE,mEAAmE;QACnE,wDAAwD;QACxDQ,SACIZ,MACEF,WACEW,WACAD,KAAK,CAAC,EAAE,EACRA,KAAK,CAAC,EAAE,EACRX,gBAAgBkB,aAAa,MAE3BJ,UAAU,EAAE,GAAG;YAACV,MAAM,aAAa,sBAAsBS;SAAQ,EACrEX,MAAMO,+BAER,MACN;QAACG;QAAWD,KAAK,CAAC,EAAE;QAAEA,KAAK,CAAC,EAAE;QAAEI;QAAQD;QAASD;KAAO,EACxD;QAAEM,SAAS;IAAM;IAEnB,OAAOd,QACL,IAAO,CAAA;YACLe,QAAQ,CAACJ,eAAAA,OAAQ,EAAE,EAChBK,MAAM,CAAC,CAACC,QAAU,CAACA,MAAMC,SAAS,IAAI,OAAOD,MAAME,GAAG,KAAK,UAC3DC,IAAI,CAAC,CAACC,GAAGC;oBAAOD,UAAiBC;uBAAlB,EAACD,WAAAA,EAAEE,KAAK,YAAPF,WAAW,OAAMC,WAAAA,EAAEC,KAAK,YAAPD,WAAW;eAC5CE,GAAG,CAAC,CAAC,EAAEC,GAAG,EAAEN,GAAG,EAAEO,KAAK,EAAEC,IAAI,EAAEC,OAAO,EAAEL,KAAK,EAAE,GAAM;oBACnDE;oBACAN;oBACAO,OAAOA,SAASP;oBAChBQ;mBACIC,UAAU;oBAAEA;gBAAQ,IAAI,CAAC;oBAC7BL,KAAK,EAAEA,gBAAAA,QAAS;;YAEpBM,OAAOnB,UAAUE,WAAW;QAC9B,CAAA,GACA;QAACD;QAAMD;QAAQE;KAAO;AAE1B;AAEA,eAAeP,oBAAmB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** One saved segment, as the picker needs it. */
|
|
2
|
+
export interface OrgContactSegment {
|
|
3
|
+
$id: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* The picker's window.
|
|
8
|
+
*
|
|
9
|
+
* Bounded rather than paged — see the module note. A rule that names a segment
|
|
10
|
+
* beyond this window still shows its own reference: the picker adds the stored
|
|
11
|
+
* id as an option of its own, because a `Select` whose value is absent from
|
|
12
|
+
* its options renders empty, and saving from that screen would erase the
|
|
13
|
+
* reference the merchant could not see.
|
|
14
|
+
*/
|
|
15
|
+
export declare const SEGMENT_PICKER_LIMIT = 50;
|
|
16
|
+
export declare function useOrgContactSegments(scope: readonly [string, string]): OrgContactSegment[];
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
/**
|
|
18
|
+
* The org's saved contact segments, for a picker to offer.
|
|
19
|
+
*
|
|
20
|
+
* A LOOKUP, not a list. Nothing pages this: it fills the options of one
|
|
21
|
+
* `Select`, a reader opens it rather than walking it, and the window is
|
|
22
|
+
* bounded so an org with a thousand segments cannot turn one dropdown into an
|
|
23
|
+
* unbounded read. That is a different question from the one a paged table
|
|
24
|
+
* answers, and keeping the read in its own module is what stops the form that
|
|
25
|
+
* renders the dropdown reading like a list surface that forgot its footer.
|
|
26
|
+
*
|
|
27
|
+
* `orderBy('name')` is safe here, which is a claim about the writer rather
|
|
28
|
+
* than a preference: `handleSaveSegment` in the contacts console is the only
|
|
29
|
+
* thing that creates a segment and it refuses an empty name. `orderBy` filters
|
|
30
|
+
* as well as sorts, so a segment written without one would be missing from its
|
|
31
|
+
* own picker rather than merely out of order.
|
|
32
|
+
*/ import { collection, limit, orderBy, query } from "firebase/firestore";
|
|
33
|
+
import { useFirestore, useFirestoreCollection } from "@aglyn/tenant-feature-instance";
|
|
34
|
+
/**
|
|
35
|
+
* The picker's window.
|
|
36
|
+
*
|
|
37
|
+
* Bounded rather than paged — see the module note. A rule that names a segment
|
|
38
|
+
* beyond this window still shows its own reference: the picker adds the stored
|
|
39
|
+
* id as an option of its own, because a `Select` whose value is absent from
|
|
40
|
+
* its options renders empty, and saving from that screen would erase the
|
|
41
|
+
* reference the merchant could not see.
|
|
42
|
+
*/ export const SEGMENT_PICKER_LIMIT = 50;
|
|
43
|
+
export function useOrgContactSegments(scope) {
|
|
44
|
+
const firestore = useFirestore();
|
|
45
|
+
const { data } = useFirestoreCollection(()=>query(collection(firestore, scope[0], scope[1], 'contactSegments'), orderBy('name'), limit(SEGMENT_PICKER_LIMIT)), [
|
|
46
|
+
firestore,
|
|
47
|
+
scope[0],
|
|
48
|
+
scope[1]
|
|
49
|
+
], {
|
|
50
|
+
idField: '$id'
|
|
51
|
+
});
|
|
52
|
+
return data != null ? data : [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=use-org-contact-segments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/hooks/use-org-contact-segments.ts"],"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\n/**\n * The org's saved contact segments, for a picker to offer.\n *\n * A LOOKUP, not a list. Nothing pages this: it fills the options of one\n * `Select`, a reader opens it rather than walking it, and the window is\n * bounded so an org with a thousand segments cannot turn one dropdown into an\n * unbounded read. That is a different question from the one a paged table\n * answers, and keeping the read in its own module is what stops the form that\n * renders the dropdown reading like a list surface that forgot its footer.\n *\n * `orderBy('name')` is safe here, which is a claim about the writer rather\n * than a preference: `handleSaveSegment` in the contacts console is the only\n * thing that creates a segment and it refuses an empty name. `orderBy` filters\n * as well as sorts, so a segment written without one would be missing from its\n * own picker rather than merely out of order.\n */\n\nimport { collection, limit, orderBy, query } from 'firebase/firestore'\nimport { useFirestore, useFirestoreCollection } from '@aglyn/tenant-feature-instance'\n\n/** One saved segment, as the picker needs it. */\nexport interface OrgContactSegment {\n $id: string\n name?: string\n}\n\n/**\n * The picker's window.\n *\n * Bounded rather than paged — see the module note. A rule that names a segment\n * beyond this window still shows its own reference: the picker adds the stored\n * id as an option of its own, because a `Select` whose value is absent from\n * its options renders empty, and saving from that screen would erase the\n * reference the merchant could not see.\n */\nexport const SEGMENT_PICKER_LIMIT = 50\n\nexport function useOrgContactSegments(\n scope: readonly [string, string],\n): OrgContactSegment[] {\n const firestore = useFirestore()\n const { data } = useFirestoreCollection<OrgContactSegment>(\n () =>\n query(\n collection(firestore, scope[0], scope[1], 'contactSegments'),\n orderBy('name'),\n limit(SEGMENT_PICKER_LIMIT),\n ),\n [firestore, scope[0], scope[1]],\n { idField: '$id' },\n )\n return data ?? []\n}\n"],"names":["collection","limit","orderBy","query","useFirestore","useFirestoreCollection","SEGMENT_PICKER_LIMIT","useOrgContactSegments","scope","firestore","data","idField"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAEA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,UAAU,EAAEC,KAAK,EAAEC,OAAO,EAAEC,KAAK,QAAQ,qBAAoB;AACtE,SAASC,YAAY,EAAEC,sBAAsB,QAAQ,iCAAgC;AAQrF;;;;;;;;CAQC,GACD,OAAO,MAAMC,uBAAuB,GAAE;AAEtC,OAAO,SAASC,sBACdC,KAAgC;IAEhC,MAAMC,YAAYL;IAClB,MAAM,EAAEM,IAAI,EAAE,GAAGL,uBACf,IACEF,MACEH,WAAWS,WAAWD,KAAK,CAAC,EAAE,EAAEA,KAAK,CAAC,EAAE,EAAE,oBAC1CN,QAAQ,SACRD,MAAMK,wBAEV;QAACG;QAAWD,KAAK,CAAC,EAAE;QAAEA,KAAK,CAAC,EAAE;KAAC,EAC/B;QAAEG,SAAS;IAAM;IAEnB,OAAOD,eAAAA,OAAQ,EAAE;AACnB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** One saved view, as the picker needs it. */
|
|
2
|
+
export interface OrgCrmViewOption {
|
|
3
|
+
$id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const CRM_VIEW_PICKER_LIMIT = 50;
|
|
7
|
+
export declare function useOrgCrmViews(scope: readonly [string, string]): OrgCrmViewOption[];
|
|
8
|
+
export default useOrgCrmViews;
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|
+
/**
|
|
18
|
+
* The org's saved Contacts views that can be an audience, for the rule
|
|
19
|
+
* editor's picker (AGL-2617).
|
|
20
|
+
*
|
|
21
|
+
* A LOOKUP like the segment picker's, and bounded for the same reason. Two
|
|
22
|
+
* things narrow it past the segments'. It lists only the views this reader
|
|
23
|
+
* would see on the Contacts list — their own and the shared ones — because
|
|
24
|
+
* a private view is one person's working arrangement and offering it as a
|
|
25
|
+
* colleague's audience would put it to a use its owner never saw. And it
|
|
26
|
+
* lists only views that TRANSLATE WHOLE into the rule language: a view
|
|
27
|
+
* carrying a name prefix or an `updatedAt` window has no audience meaning,
|
|
28
|
+
* and the sweep would refuse it by narrowing to nobody — so the picker
|
|
29
|
+
* does not offer what the sweep cannot honor.
|
|
30
|
+
*
|
|
31
|
+
* Scoped like the field picker: the reader's own tokens filter the query
|
|
32
|
+
* for a scoped member, and an org-wide member reads the section unfiltered
|
|
33
|
+
* on the `(section, name)` index.
|
|
34
|
+
*/ import { CRM_COLLECTIONS, crmViewIsListed, dynamicListDimensionsForCrmView, normalizeCrmViewFilters } from "@aglyn/aglyn";
|
|
35
|
+
import { collection, limit, orderBy, query, where } from "firebase/firestore";
|
|
36
|
+
import { useMemo } from "react";
|
|
37
|
+
import { useFirestore, useFirestoreCollection, useScopeTokens, useUser } from "@aglyn/tenant-feature-instance";
|
|
38
|
+
export const CRM_VIEW_PICKER_LIMIT = 50;
|
|
39
|
+
export function useOrgCrmViews(scope) {
|
|
40
|
+
var _ref;
|
|
41
|
+
const firestore = useFirestore();
|
|
42
|
+
const { data: user } = useUser();
|
|
43
|
+
const uid = (_ref = user == null ? void 0 : user.uid) != null ? _ref : null;
|
|
44
|
+
const { tokens, orgWide, loaded } = useScopeTokens(scope[1]);
|
|
45
|
+
const { data } = useFirestoreCollection(()=>// Not until the viewer's reach is known — see the field picker.
|
|
46
|
+
loaded ? query(collection(firestore, scope[0], scope[1], CRM_COLLECTIONS.views), ...orgWide ? [] : [
|
|
47
|
+
where('visibleTo', 'array-contains-any', tokens)
|
|
48
|
+
], where('section', '==', 'contacts'), orderBy('name'), limit(CRM_VIEW_PICKER_LIMIT)) : null, [
|
|
49
|
+
firestore,
|
|
50
|
+
scope[0],
|
|
51
|
+
scope[1],
|
|
52
|
+
loaded,
|
|
53
|
+
orgWide,
|
|
54
|
+
tokens
|
|
55
|
+
], {
|
|
56
|
+
idField: '$id'
|
|
57
|
+
});
|
|
58
|
+
return useMemo(()=>(data != null ? data : []).filter((row)=>{
|
|
59
|
+
var _row_ownerUid;
|
|
60
|
+
return typeof row['name'] === 'string' && row['name'] && crmViewIsListed({
|
|
61
|
+
shared: row['shared'] === true,
|
|
62
|
+
ownerUid: String((_row_ownerUid = row['ownerUid']) != null ? _row_ownerUid : '')
|
|
63
|
+
}, uid) && dynamicListDimensionsForCrmView(normalizeCrmViewFilters(row['filters'])).unsupported.length === 0;
|
|
64
|
+
}).map((row)=>({
|
|
65
|
+
$id: String(row['$id']),
|
|
66
|
+
name: String(row['name'])
|
|
67
|
+
})), [
|
|
68
|
+
data,
|
|
69
|
+
uid
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
export default useOrgCrmViews;
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=use-org-crm-views.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/hooks/use-org-crm-views.ts"],"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\n/**\n * The org's saved Contacts views that can be an audience, for the rule\n * editor's picker (AGL-2617).\n *\n * A LOOKUP like the segment picker's, and bounded for the same reason. Two\n * things narrow it past the segments'. It lists only the views this reader\n * would see on the Contacts list — their own and the shared ones — because\n * a private view is one person's working arrangement and offering it as a\n * colleague's audience would put it to a use its owner never saw. And it\n * lists only views that TRANSLATE WHOLE into the rule language: a view\n * carrying a name prefix or an `updatedAt` window has no audience meaning,\n * and the sweep would refuse it by narrowing to nobody — so the picker\n * does not offer what the sweep cannot honor.\n *\n * Scoped like the field picker: the reader's own tokens filter the query\n * for a scoped member, and an org-wide member reads the section unfiltered\n * on the `(section, name)` index.\n */\n\nimport {\n CRM_COLLECTIONS,\n crmViewIsListed,\n dynamicListDimensionsForCrmView,\n normalizeCrmViewFilters,\n} from '@aglyn/aglyn'\nimport { collection, limit, orderBy, query, where } from 'firebase/firestore'\nimport { useMemo } from 'react'\nimport {\n useFirestore,\n useFirestoreCollection,\n useScopeTokens,\n useUser,\n} from '@aglyn/tenant-feature-instance'\n\n/** One saved view, as the picker needs it. */\nexport interface OrgCrmViewOption {\n $id: string\n name: string\n}\n\nexport const CRM_VIEW_PICKER_LIMIT = 50\n\nexport function useOrgCrmViews(\n scope: readonly [string, string],\n): OrgCrmViewOption[] {\n const firestore = useFirestore()\n const { data: user } = useUser()\n const uid = (user as { uid?: string } | null | undefined)?.uid ?? null\n const { tokens, orgWide, loaded } = useScopeTokens(scope[1])\n const { data } = useFirestoreCollection<Record<string, unknown>>(\n () =>\n // Not until the viewer's reach is known — see the field picker.\n loaded\n ? query(\n collection(firestore, scope[0], scope[1], CRM_COLLECTIONS.views),\n ...(orgWide ? [] : [where('visibleTo', 'array-contains-any', tokens)]),\n where('section', '==', 'contacts'),\n orderBy('name'),\n limit(CRM_VIEW_PICKER_LIMIT),\n )\n : null,\n [firestore, scope[0], scope[1], loaded, orgWide, tokens],\n { idField: '$id' },\n )\n return useMemo(\n () =>\n (data ?? [])\n .filter(\n (row) =>\n typeof row['name'] === 'string' &&\n row['name'] &&\n crmViewIsListed(\n {\n shared: row['shared'] === true,\n ownerUid: String(row['ownerUid'] ?? ''),\n },\n uid,\n ) &&\n dynamicListDimensionsForCrmView(normalizeCrmViewFilters(row['filters']))\n .unsupported.length === 0,\n )\n .map((row) => ({ $id: String(row['$id']), name: String(row['name']) })),\n [data, uid],\n )\n}\n\nexport default useOrgCrmViews\n"],"names":["CRM_COLLECTIONS","crmViewIsListed","dynamicListDimensionsForCrmView","normalizeCrmViewFilters","collection","limit","orderBy","query","where","useMemo","useFirestore","useFirestoreCollection","useScopeTokens","useUser","CRM_VIEW_PICKER_LIMIT","useOrgCrmViews","scope","firestore","data","user","uid","tokens","orgWide","loaded","views","idField","filter","row","shared","ownerUid","String","unsupported","length","map","$id","name"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAEA;;;;;;;;;;;;;;;;;CAiBC,GAED,SACEA,eAAe,EACfC,eAAe,EACfC,+BAA+B,EAC/BC,uBAAuB,QAClB,eAAc;AACrB,SAASC,UAAU,EAAEC,KAAK,EAAEC,OAAO,EAAEC,KAAK,EAAEC,KAAK,QAAQ,qBAAoB;AAC7E,SAASC,OAAO,QAAQ,QAAO;AAC/B,SACEC,YAAY,EACZC,sBAAsB,EACtBC,cAAc,EACdC,OAAO,QACF,iCAAgC;AAQvC,OAAO,MAAMC,wBAAwB,GAAE;AAEvC,OAAO,SAASC,eACdC,KAAgC;;IAEhC,MAAMC,YAAYP;IAClB,MAAM,EAAEQ,MAAMC,IAAI,EAAE,GAAGN;IACvB,MAAMO,cAAOD,wBAAD,AAACA,KAA8CC,GAAG,mBAAI;IAClE,MAAM,EAAEC,MAAM,EAAEC,OAAO,EAAEC,MAAM,EAAE,GAAGX,eAAeI,KAAK,CAAC,EAAE;IAC3D,MAAM,EAAEE,IAAI,EAAE,GAAGP,uBACf,IACE,gEAAgE;QAChEY,SACIhB,MACEH,WAAWa,WAAWD,KAAK,CAAC,EAAE,EAAEA,KAAK,CAAC,EAAE,EAAEhB,gBAAgBwB,KAAK,MAC3DF,UAAU,EAAE,GAAG;YAACd,MAAM,aAAa,sBAAsBa;SAAQ,EACrEb,MAAM,WAAW,MAAM,aACvBF,QAAQ,SACRD,MAAMS,0BAER,MACN;QAACG;QAAWD,KAAK,CAAC,EAAE;QAAEA,KAAK,CAAC,EAAE;QAAEO;QAAQD;QAASD;KAAO,EACxD;QAAEI,SAAS;IAAM;IAEnB,OAAOhB,QACL,IACE,CAACS,eAAAA,OAAQ,EAAE,EACRQ,MAAM,CACL,CAACC;gBAMsBA;mBALrB,OAAOA,GAAG,CAAC,OAAO,KAAK,YACvBA,GAAG,CAAC,OAAO,IACX1B,gBACE;gBACE2B,QAAQD,GAAG,CAAC,SAAS,KAAK;gBAC1BE,UAAUC,QAAOH,gBAAAA,GAAG,CAAC,WAAW,YAAfA,gBAAmB;YACtC,GACAP,QAEFlB,gCAAgCC,wBAAwBwB,GAAG,CAAC,UAAU,GACnEI,WAAW,CAACC,MAAM,KAAK;WAE7BC,GAAG,CAAC,CAACN,MAAS,CAAA;gBAAEO,KAAKJ,OAAOH,GAAG,CAAC,MAAM;gBAAGQ,MAAML,OAAOH,GAAG,CAAC,OAAO;YAAE,CAAA,IACxE;QAACT;QAAME;KAAI;AAEf;AAEA,eAAeL,eAAc"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** One audience, as the picker needs it. */
|
|
2
|
+
export interface OrgListOption {
|
|
3
|
+
$id: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
}
|
|
6
|
+
/** The picker's window. Matches the segment picker beside it. */
|
|
7
|
+
export declare const LIST_PICKER_LIMIT = 50;
|
|
8
|
+
export declare function useOrgLists(scope: readonly [string, string]): OrgListOption[];
|
|
@@ -0,0 +1,47 @@
|
|
|
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
|
+
/**
|
|
18
|
+
* The org's audiences, as the options of a picker.
|
|
19
|
+
*
|
|
20
|
+
* The sibling of `use-org-contact-segments.ts` and deliberately its twin: a
|
|
21
|
+
* LOOKUP rather than a list. Nothing pages this — it fills the options of one
|
|
22
|
+
* `Select` that a reader opens rather than walks, and the window is bounded so
|
|
23
|
+
* an org with a thousand audiences cannot turn a dropdown into an unbounded
|
|
24
|
+
* read. The paged surface that answers the other question already exists in
|
|
25
|
+
* `lists-card.tsx`.
|
|
26
|
+
*
|
|
27
|
+
* `orderBy('name')` is safe here, and that is a claim about the writer rather
|
|
28
|
+
* than a preference: the create drawer in `lists-card.tsx` is the only thing
|
|
29
|
+
* that creates a list and it refuses an empty name. `orderBy` filters as well
|
|
30
|
+
* as sorts, so a list written without one would be missing from its own picker
|
|
31
|
+
* rather than merely out of order.
|
|
32
|
+
*/ import { collection, limit, orderBy, query } from "firebase/firestore";
|
|
33
|
+
import { useFirestore, useFirestoreCollection } from "@aglyn/tenant-feature-instance";
|
|
34
|
+
/** The picker's window. Matches the segment picker beside it. */ export const LIST_PICKER_LIMIT = 50;
|
|
35
|
+
export function useOrgLists(scope) {
|
|
36
|
+
const firestore = useFirestore();
|
|
37
|
+
const { data } = useFirestoreCollection(()=>query(collection(firestore, scope[0], scope[1], 'lists'), orderBy('name'), limit(LIST_PICKER_LIMIT)), [
|
|
38
|
+
firestore,
|
|
39
|
+
scope[0],
|
|
40
|
+
scope[1]
|
|
41
|
+
], {
|
|
42
|
+
idField: '$id'
|
|
43
|
+
});
|
|
44
|
+
return data != null ? data : [];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=use-org-lists.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/hooks/use-org-lists.ts"],"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\n/**\n * The org's audiences, as the options of a picker.\n *\n * The sibling of `use-org-contact-segments.ts` and deliberately its twin: a\n * LOOKUP rather than a list. Nothing pages this — it fills the options of one\n * `Select` that a reader opens rather than walks, and the window is bounded so\n * an org with a thousand audiences cannot turn a dropdown into an unbounded\n * read. The paged surface that answers the other question already exists in\n * `lists-card.tsx`.\n *\n * `orderBy('name')` is safe here, and that is a claim about the writer rather\n * than a preference: the create drawer in `lists-card.tsx` is the only thing\n * that creates a list and it refuses an empty name. `orderBy` filters as well\n * as sorts, so a list written without one would be missing from its own picker\n * rather than merely out of order.\n */\n\nimport { collection, limit, orderBy, query } from 'firebase/firestore'\nimport { useFirestore, useFirestoreCollection } from '@aglyn/tenant-feature-instance'\n\n/** One audience, as the picker needs it. */\nexport interface OrgListOption {\n $id: string\n name?: string\n}\n\n/** The picker's window. Matches the segment picker beside it. */\nexport const LIST_PICKER_LIMIT = 50\n\nexport function useOrgLists(\n scope: readonly [string, string],\n): OrgListOption[] {\n const firestore = useFirestore()\n const { data } = useFirestoreCollection<OrgListOption>(\n () =>\n query(\n collection(firestore, scope[0], scope[1], 'lists'),\n orderBy('name'),\n limit(LIST_PICKER_LIMIT),\n ),\n [firestore, scope[0], scope[1]],\n { idField: '$id' },\n )\n return data ?? []\n}\n"],"names":["collection","limit","orderBy","query","useFirestore","useFirestoreCollection","LIST_PICKER_LIMIT","useOrgLists","scope","firestore","data","idField"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;AAEA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,UAAU,EAAEC,KAAK,EAAEC,OAAO,EAAEC,KAAK,QAAQ,qBAAoB;AACtE,SAASC,YAAY,EAAEC,sBAAsB,QAAQ,iCAAgC;AAQrF,+DAA+D,GAC/D,OAAO,MAAMC,oBAAoB,GAAE;AAEnC,OAAO,SAASC,YACdC,KAAgC;IAEhC,MAAMC,YAAYL;IAClB,MAAM,EAAEM,IAAI,EAAE,GAAGL,uBACf,IACEF,MACEH,WAAWS,WAAWD,KAAK,CAAC,EAAE,EAAEA,KAAK,CAAC,EAAE,EAAE,UAC1CN,QAAQ,SACRD,MAAMK,qBAEV;QAACG;QAAWD,KAAK,CAAC,EAAE;QAAEA,KAAK,CAAC,EAAE;KAAC,EAC/B;QAAEG,SAAS;IAAM;IAEnB,OAAOD,eAAAA,OAAQ,EAAE;AACnB"}
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
*/
|
|
17
|
+
import { SCREEN_KIND_EMAIL } from '@aglyn/aglyn/app-utils/screen-route';
|
|
18
|
+
/**
|
|
19
|
+
* THE TWO DOCUMENTS AN EMAIL DESIGN IS: a screen with `kind: 'email'`, and its
|
|
20
|
+
* first version holding the node map.
|
|
21
|
+
*
|
|
22
|
+
* Leaf modules only, so the console's create (a client component) and the
|
|
23
|
+
* server's draft writer build the same pair from one function: a design made
|
|
24
|
+
* on either side opens in the same besigner, lists on the same Templates tab,
|
|
25
|
+
* and is picked up by the same campaign composer.
|
|
26
|
+
*/
|
|
27
|
+
/** The screen document of a new email design. */
|
|
28
|
+
export interface EmailDesignScreenDocument {
|
|
29
|
+
displayName: string;
|
|
30
|
+
kind: typeof SCREEN_KIND_EMAIL;
|
|
31
|
+
versionId: string;
|
|
32
|
+
}
|
|
33
|
+
/** The first version of a new email design. */
|
|
34
|
+
export interface EmailDesignVersionDocument {
|
|
35
|
+
screenId: string;
|
|
36
|
+
nodes: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
/** A new design's first canvas: one email section holding a greeting. */
|
|
39
|
+
export declare function emailDesignStarterNodes(ids: {
|
|
40
|
+
sectionId: string;
|
|
41
|
+
textId: string;
|
|
42
|
+
}): Record<string, unknown>;
|
|
43
|
+
/** The screen and first version a design with these ids and this canvas is stored as. */
|
|
44
|
+
export declare function emailDesignDocuments(input: {
|
|
45
|
+
screenId: string;
|
|
46
|
+
versionId: string;
|
|
47
|
+
displayName: string;
|
|
48
|
+
nodes: Record<string, unknown>;
|
|
49
|
+
}): {
|
|
50
|
+
screen: EmailDesignScreenDocument;
|
|
51
|
+
version: EmailDesignVersionDocument;
|
|
52
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
*/ import { SCREEN_KIND_EMAIL } from "@aglyn/aglyn/app-utils/screen-route";
|
|
17
|
+
import { CANVAS_ROOT_ELEMENT_ID } from "@aglyn/aglyn/foundation/constants/canvas";
|
|
18
|
+
/** A new design's first canvas: one email section holding a greeting. */ export function emailDesignStarterNodes(ids) {
|
|
19
|
+
return {
|
|
20
|
+
[CANVAS_ROOT_ELEMENT_ID]: {
|
|
21
|
+
$id: CANVAS_ROOT_ELEMENT_ID,
|
|
22
|
+
componentId: 'div',
|
|
23
|
+
nodes: [
|
|
24
|
+
ids.sectionId
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
[ids.sectionId]: {
|
|
28
|
+
$id: ids.sectionId,
|
|
29
|
+
componentId: 'emailSection',
|
|
30
|
+
pluginId: 'email',
|
|
31
|
+
parentId: CANVAS_ROOT_ELEMENT_ID,
|
|
32
|
+
nodes: [
|
|
33
|
+
ids.textId
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
[ids.textId]: {
|
|
37
|
+
$id: ids.textId,
|
|
38
|
+
componentId: 'emailText',
|
|
39
|
+
pluginId: 'email',
|
|
40
|
+
parentId: ids.sectionId,
|
|
41
|
+
props: {
|
|
42
|
+
children: 'Hello {{contact.firstName}},',
|
|
43
|
+
variant: 'body'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** The screen and first version a design with these ids and this canvas is stored as. */ export function emailDesignDocuments(input) {
|
|
49
|
+
return {
|
|
50
|
+
screen: {
|
|
51
|
+
displayName: input.displayName,
|
|
52
|
+
kind: SCREEN_KIND_EMAIL,
|
|
53
|
+
versionId: input.versionId
|
|
54
|
+
},
|
|
55
|
+
version: {
|
|
56
|
+
screenId: input.screenId,
|
|
57
|
+
nodes: input.nodes
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=email-design-document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/model/email-design-document.ts"],"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\nimport { SCREEN_KIND_EMAIL } from '@aglyn/aglyn/app-utils/screen-route'\nimport { CANVAS_ROOT_ELEMENT_ID } from '@aglyn/aglyn/foundation/constants/canvas'\n\n/**\n * THE TWO DOCUMENTS AN EMAIL DESIGN IS: a screen with `kind: 'email'`, and its\n * first version holding the node map.\n *\n * Leaf modules only, so the console's create (a client component) and the\n * server's draft writer build the same pair from one function: a design made\n * on either side opens in the same besigner, lists on the same Templates tab,\n * and is picked up by the same campaign composer.\n */\n\n/** The screen document of a new email design. */\nexport interface EmailDesignScreenDocument {\n displayName: string\n kind: typeof SCREEN_KIND_EMAIL\n versionId: string\n}\n\n/** The first version of a new email design. */\nexport interface EmailDesignVersionDocument {\n screenId: string\n nodes: Record<string, unknown>\n}\n\n/** A new design's first canvas: one email section holding a greeting. */\nexport function emailDesignStarterNodes(ids: {\n sectionId: string\n textId: string\n}): Record<string, unknown> {\n return {\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'div',\n nodes: [ids.sectionId],\n },\n [ids.sectionId]: {\n $id: ids.sectionId,\n componentId: 'emailSection',\n pluginId: 'email',\n parentId: CANVAS_ROOT_ELEMENT_ID,\n nodes: [ids.textId],\n },\n [ids.textId]: {\n $id: ids.textId,\n componentId: 'emailText',\n pluginId: 'email',\n parentId: ids.sectionId,\n props: {\n children: 'Hello {{contact.firstName}},',\n variant: 'body',\n },\n },\n }\n}\n\n/** The screen and first version a design with these ids and this canvas is stored as. */\nexport function emailDesignDocuments(input: {\n screenId: string\n versionId: string\n displayName: string\n nodes: Record<string, unknown>\n}): { screen: EmailDesignScreenDocument; version: EmailDesignVersionDocument } {\n return {\n screen: {\n displayName: input.displayName,\n kind: SCREEN_KIND_EMAIL,\n versionId: input.versionId,\n },\n version: { screenId: input.screenId, nodes: input.nodes },\n }\n}\n"],"names":["SCREEN_KIND_EMAIL","CANVAS_ROOT_ELEMENT_ID","emailDesignStarterNodes","ids","$id","componentId","nodes","sectionId","pluginId","parentId","textId","props","children","variant","emailDesignDocuments","input","screen","displayName","kind","versionId","version","screenId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,iBAAiB,QAAQ,sCAAqC;AACvE,SAASC,sBAAsB,QAAQ,2CAA0C;AAyBjF,uEAAuE,GACvE,OAAO,SAASC,wBAAwBC,GAGvC;IACC,OAAO;QACL,CAACF,uBAAuB,EAAE;YACxBG,KAAKH;YACLI,aAAa;YACbC,OAAO;gBAACH,IAAII,SAAS;aAAC;QACxB;QACA,CAACJ,IAAII,SAAS,CAAC,EAAE;YACfH,KAAKD,IAAII,SAAS;YAClBF,aAAa;YACbG,UAAU;YACVC,UAAUR;YACVK,OAAO;gBAACH,IAAIO,MAAM;aAAC;QACrB;QACA,CAACP,IAAIO,MAAM,CAAC,EAAE;YACZN,KAAKD,IAAIO,MAAM;YACfL,aAAa;YACbG,UAAU;YACVC,UAAUN,IAAII,SAAS;YACvBI,OAAO;gBACLC,UAAU;gBACVC,SAAS;YACX;QACF;IACF;AACF;AAEA,uFAAuF,GACvF,OAAO,SAASC,qBAAqBC,KAKpC;IACC,OAAO;QACLC,QAAQ;YACNC,aAAaF,MAAME,WAAW;YAC9BC,MAAMlB;YACNmB,WAAWJ,MAAMI,SAAS;QAC5B;QACAC,SAAS;YAAEC,UAAUN,MAAMM,QAAQ;YAAEf,OAAOS,MAAMT,KAAK;QAAC;IAC1D;AACF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Email rendering model (AGL-412): besigner-built email HTML rendering + merge tags, relocated from core app-utils.
|
|
19
|
+
* Context-free — importable by client components, /server handlers, and
|
|
20
|
+
* other plugins/apps via `@aglyn/plugins-email/model`.
|
|
21
|
+
*/
|
|
22
|
+
export { renderEmailHtml, substituteMergeTokens, resolveMergeTags, type EmailRenderOptions, type EmailRenderProduct, type RenderedEmail, } from '@aglyn/shared-util-email';
|
|
23
|
+
/**
|
|
24
|
+
* Campaign reporting: the rate math, the populations the send recorded, and
|
|
25
|
+
* the link rollup — pure, so every denominator is named once and provable
|
|
26
|
+
* rather than chosen in JSX.
|
|
27
|
+
*/
|
|
28
|
+
export * from '@aglyn/shared-ui-email-campaigns/model/campaign-report';
|
|
29
|
+
/**
|
|
30
|
+
* What a campaign EARNED: the last-click window, the gross/refunded pair and
|
|
31
|
+
* the per-currency buckets that are never added together. Separate from the
|
|
32
|
+
* rate math because it reads a different document and answers the merchant's
|
|
33
|
+
* second question rather than their first.
|
|
34
|
+
*/
|
|
35
|
+
export * from '@aglyn/shared-ui-email-campaigns/model/campaign-revenue';
|
|
36
|
+
/**
|
|
37
|
+
* The same math taken across every message sent from one TEMPLATE. Separate
|
|
38
|
+
* from `campaign-report` because a sum has a membership: which messages went
|
|
39
|
+
* into it, and therefore which population the rate describes.
|
|
40
|
+
*/
|
|
41
|
+
export * from './template-report';
|
|
42
|
+
/** Reading one message record — its state, and when it went out. */
|
|
43
|
+
export * from '@aglyn/shared-ui-email-campaigns/model/email-record';
|
|
44
|
+
/**
|
|
45
|
+
* Whose template this is: authored here, or installed from a marketplace
|
|
46
|
+
* listing and versioned by somebody else.
|
|
47
|
+
*/
|
|
48
|
+
export * from './template-provenance';
|
|
49
|
+
/**
|
|
50
|
+
* One campaign message, rendered. Shared by the send loop, the composer's
|
|
51
|
+
* pre-send preview and any other surface that has to show what will be
|
|
52
|
+
* mailed — a preview that renders the message a second way previews
|
|
53
|
+
* something else.
|
|
54
|
+
*/
|
|
55
|
+
/**
|
|
56
|
+
* The campaign CONTAINER — its window, its lists, and the arithmetic that
|
|
57
|
+
* rolls its sends into one set of figures.
|
|
58
|
+
*/
|
|
59
|
+
export * from '@aglyn/shared-ui-email-campaigns/model/campaign-container';
|
|
60
|
+
/**
|
|
61
|
+
* How each sending-domain state reads, said once for the list and the
|
|
62
|
+
* domain's own page — including why `inconclusive` is neither of them.
|
|
63
|
+
*/
|
|
64
|
+
export * from './sending-domain-status';
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
*/ /**
|
|
17
|
+
* Email rendering model (AGL-412): besigner-built email HTML rendering + merge tags, relocated from core app-utils.
|
|
18
|
+
* Context-free — importable by client components, /server handlers, and
|
|
19
|
+
* other plugins/apps via `@aglyn/plugins-email/model`.
|
|
20
|
+
*/ // The renderer and merge-tag helpers moved to `@aglyn/shared-util-email`
|
|
21
|
+
// (AGL-750): they are pure functions over a node map with no imports at all,
|
|
22
|
+
// and the console's own API routes need them to render system email
|
|
23
|
+
// templates. Apps are forbidden to import feature plugins (the `scope:app`
|
|
24
|
+
// → `aglyn:addons` boundary), so leaving them here made them unreachable
|
|
25
|
+
// from exactly the code that has to send the mail.
|
|
26
|
+
//
|
|
27
|
+
// Re-exported so `@aglyn/plugins-email/model` keeps working unchanged.
|
|
28
|
+
export { renderEmailHtml, substituteMergeTokens, resolveMergeTags } from "@aglyn/shared-util-email";
|
|
29
|
+
// The campaign document model and its arithmetic live in
|
|
30
|
+
// `@aglyn/shared-ui-email-campaigns`. Marketing owns the campaign console
|
|
31
|
+
// section and Email owns the message surfaces; both are independently
|
|
32
|
+
// toggleable plugins, so a shape either one reads has to sit in a lib they
|
|
33
|
+
// depend on as peers rather than inside one of them. Re-exported here so
|
|
34
|
+
// `@aglyn/plugins-email/model` remains one barrel for this plugin's own
|
|
35
|
+
// callers.
|
|
36
|
+
/**
|
|
37
|
+
* Campaign reporting: the rate math, the populations the send recorded, and
|
|
38
|
+
* the link rollup — pure, so every denominator is named once and provable
|
|
39
|
+
* rather than chosen in JSX.
|
|
40
|
+
*/ export * from "@aglyn/shared-ui-email-campaigns/model/campaign-report";
|
|
41
|
+
/**
|
|
42
|
+
* What a campaign EARNED: the last-click window, the gross/refunded pair and
|
|
43
|
+
* the per-currency buckets that are never added together. Separate from the
|
|
44
|
+
* rate math because it reads a different document and answers the merchant's
|
|
45
|
+
* second question rather than their first.
|
|
46
|
+
*/ export * from "@aglyn/shared-ui-email-campaigns/model/campaign-revenue";
|
|
47
|
+
/**
|
|
48
|
+
* The same math taken across every message sent from one TEMPLATE. Separate
|
|
49
|
+
* from `campaign-report` because a sum has a membership: which messages went
|
|
50
|
+
* into it, and therefore which population the rate describes.
|
|
51
|
+
*/ export * from "./template-report.js";
|
|
52
|
+
/** Reading one message record — its state, and when it went out. */ export * from "@aglyn/shared-ui-email-campaigns/model/email-record";
|
|
53
|
+
/**
|
|
54
|
+
* Whose template this is: authored here, or installed from a marketplace
|
|
55
|
+
* listing and versioned by somebody else.
|
|
56
|
+
*/ export * from "./template-provenance.js";
|
|
57
|
+
/**
|
|
58
|
+
* One campaign message, rendered. Shared by the send loop, the composer's
|
|
59
|
+
* pre-send preview and any other surface that has to show what will be
|
|
60
|
+
* mailed — a preview that renders the message a second way previews
|
|
61
|
+
* something else.
|
|
62
|
+
*/ /**
|
|
63
|
+
* The campaign CONTAINER — its window, its lists, and the arithmetic that
|
|
64
|
+
* rolls its sends into one set of figures.
|
|
65
|
+
*/ export * from "@aglyn/shared-ui-email-campaigns/model/campaign-container";
|
|
66
|
+
/**
|
|
67
|
+
* How each sending-domain state reads, said once for the list and the
|
|
68
|
+
* domain's own page — including why `inconclusive` is neither of them.
|
|
69
|
+
*/ export * from "./sending-domain-status.js";
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/model/index.ts"],"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\n/**\n * Email rendering model (AGL-412): besigner-built email HTML rendering + merge tags, relocated from core app-utils.\n * Context-free — importable by client components, /server handlers, and\n * other plugins/apps via `@aglyn/plugins-email/model`.\n */\n// The renderer and merge-tag helpers moved to `@aglyn/shared-util-email`\n// (AGL-750): they are pure functions over a node map with no imports at all,\n// and the console's own API routes need them to render system email\n// templates. Apps are forbidden to import feature plugins (the `scope:app`\n// → `aglyn:addons` boundary), so leaving them here made them unreachable\n// from exactly the code that has to send the mail.\n//\n// Re-exported so `@aglyn/plugins-email/model` keeps working unchanged.\nexport {\n renderEmailHtml,\n substituteMergeTokens,\n resolveMergeTags,\n type EmailRenderOptions,\n type EmailRenderProduct,\n type RenderedEmail,\n} from '@aglyn/shared-util-email'\n\n// The campaign document model and its arithmetic live in\n// `@aglyn/shared-ui-email-campaigns`. Marketing owns the campaign console\n// section and Email owns the message surfaces; both are independently\n// toggleable plugins, so a shape either one reads has to sit in a lib they\n// depend on as peers rather than inside one of them. Re-exported here so\n// `@aglyn/plugins-email/model` remains one barrel for this plugin's own\n// callers.\n\n/**\n * Campaign reporting: the rate math, the populations the send recorded, and\n * the link rollup — pure, so every denominator is named once and provable\n * rather than chosen in JSX.\n */\nexport * from '@aglyn/shared-ui-email-campaigns/model/campaign-report'\n\n/**\n * What a campaign EARNED: the last-click window, the gross/refunded pair and\n * the per-currency buckets that are never added together. Separate from the\n * rate math because it reads a different document and answers the merchant's\n * second question rather than their first.\n */\nexport * from '@aglyn/shared-ui-email-campaigns/model/campaign-revenue'\n\n/**\n * The same math taken across every message sent from one TEMPLATE. Separate\n * from `campaign-report` because a sum has a membership: which messages went\n * into it, and therefore which population the rate describes.\n */\nexport * from './template-report'\n\n/** Reading one message record — its state, and when it went out. */\nexport * from '@aglyn/shared-ui-email-campaigns/model/email-record'\n\n/**\n * Whose template this is: authored here, or installed from a marketplace\n * listing and versioned by somebody else.\n */\nexport * from './template-provenance'\n\n/**\n * One campaign message, rendered. Shared by the send loop, the composer's\n * pre-send preview and any other surface that has to show what will be\n * mailed — a preview that renders the message a second way previews\n * something else.\n */\n\n/**\n * The campaign CONTAINER — its window, its lists, and the arithmetic that\n * rolls its sends into one set of figures.\n */\nexport * from '@aglyn/shared-ui-email-campaigns/model/campaign-container'\n\n/**\n * How each sending-domain state reads, said once for the list and the\n * domain's own page — including why `inconclusive` is neither of them.\n */\nexport * from './sending-domain-status'\n"],"names":["renderEmailHtml","substituteMergeTokens","resolveMergeTags"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;CAIC,GACD,yEAAyE;AACzE,6EAA6E;AAC7E,oEAAoE;AACpE,2EAA2E;AAC3E,yEAAyE;AACzE,mDAAmD;AACnD,EAAE;AACF,uEAAuE;AACvE,SACEA,eAAe,EACfC,qBAAqB,EACrBC,gBAAgB,QAIX,2BAA0B;AAEjC,yDAAyD;AACzD,0EAA0E;AAC1E,sEAAsE;AACtE,2EAA2E;AAC3E,yEAAyE;AACzE,wEAAwE;AACxE,WAAW;AAEX;;;;CAIC,GACD,cAAc,yDAAwD;AAEtE;;;;;CAKC,GACD,cAAc,0DAAyD;AAEvE;;;;CAIC,GACD,cAAc,uBAAmB;AAEjC,kEAAkE,GAClE,cAAc,sDAAqD;AAEnE;;;CAGC,GACD,cAAc,2BAAuB;AAErC;;;;;CAKC,GAED;;;CAGC,GACD,cAAc,4DAA2D;AAEzE;;;CAGC,GACD,cAAc,6BAAyB"}
|