@aglyn/plugins-outreach 1.0.0-beta.231 → 1.0.0-beta.233
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/package.json +14 -14
- package/src/lib/components/curated-step-editor.js +1 -0
- package/src/lib/components/curated-step-editor.js.map +1 -1
- package/src/lib/components/do-not-contact-domains.js +23 -8
- package/src/lib/components/do-not-contact-domains.js.map +1 -1
- package/src/lib/components/enroll-dialog.js +6 -0
- package/src/lib/components/enroll-dialog.js.map +1 -1
- package/src/lib/components/enrollment-detail.js +3 -0
- package/src/lib/components/enrollment-detail.js.map +1 -1
- package/src/lib/components/enrollments-table.js +94 -48
- package/src/lib/components/enrollments-table.js.map +1 -1
- package/src/lib/components/link-domains-card.js +2 -0
- package/src/lib/components/link-domains-card.js.map +1 -1
- package/src/lib/components/mailboxes-section.js +1 -0
- package/src/lib/components/mailboxes-section.js.map +1 -1
- package/src/lib/components/sequence-detail.js +1 -0
- package/src/lib/components/sequence-detail.js.map +1 -1
- package/src/lib/components/sequence-report-card.js +1 -0
- package/src/lib/components/sequence-report-card.js.map +1 -1
- package/src/lib/components/sequences-section.js +47 -15
- package/src/lib/components/sequences-section.js.map +1 -1
- package/src/lib/model/do-not-contact-domain-list-query.js +60 -2
- package/src/lib/model/do-not-contact-domain-list-query.js.map +1 -1
- package/src/lib/model/enrollment-list-query.js +55 -2
- package/src/lib/model/enrollment-list-query.js.map +1 -1
- package/src/lib/model/sequence-list-query.js +36 -2
- package/src/lib/model/sequence-list-query.js.map +1 -1
|
@@ -73,10 +73,68 @@ export const OUTREACH_DO_NOT_CONTACT_DOMAIN_LIST_QUERY = {
|
|
|
73
73
|
]
|
|
74
74
|
}
|
|
75
75
|
],
|
|
76
|
-
|
|
76
|
+
/*
|
|
77
|
+
* The headers (AGL-3680). A domain IS its document id, so Domain orders by
|
|
78
|
+
* the id, alphabetical first as the list always was. Why, Added and Detail
|
|
79
|
+
* are on every entry — the one writer stamps `reason`, `addedAtMs` and
|
|
80
|
+
* `detail` (null when none) — and order the query `alone`: on this
|
|
81
|
+
* unscoped collection that costs no composite. Added newest first is also
|
|
82
|
+
* the order its date range imposes.
|
|
83
|
+
*/ sorts: [
|
|
77
84
|
{
|
|
78
85
|
path: LIST_QUERY_ID_PATH,
|
|
79
|
-
direction: 'asc'
|
|
86
|
+
direction: 'asc',
|
|
87
|
+
column: 'domain',
|
|
88
|
+
label: 'Domain'
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
path: LIST_QUERY_ID_PATH,
|
|
92
|
+
direction: 'desc',
|
|
93
|
+
column: 'domain',
|
|
94
|
+
label: 'Domain',
|
|
95
|
+
alone: true
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
path: 'reason',
|
|
99
|
+
direction: 'asc',
|
|
100
|
+
column: 'reason',
|
|
101
|
+
label: 'Why',
|
|
102
|
+
alone: true
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
path: 'reason',
|
|
106
|
+
direction: 'desc',
|
|
107
|
+
column: 'reason',
|
|
108
|
+
label: 'Why',
|
|
109
|
+
alone: true
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
path: 'addedAtMs',
|
|
113
|
+
direction: 'desc',
|
|
114
|
+
column: 'addedAtMs',
|
|
115
|
+
label: 'Added',
|
|
116
|
+
alone: true
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
path: 'addedAtMs',
|
|
120
|
+
direction: 'asc',
|
|
121
|
+
column: 'addedAtMs',
|
|
122
|
+
label: 'Added',
|
|
123
|
+
alone: true
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
path: 'detail',
|
|
127
|
+
direction: 'asc',
|
|
128
|
+
column: 'detail',
|
|
129
|
+
label: 'Detail',
|
|
130
|
+
alone: true
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
path: 'detail',
|
|
134
|
+
direction: 'desc',
|
|
135
|
+
column: 'detail',
|
|
136
|
+
label: 'Detail',
|
|
137
|
+
alone: true
|
|
80
138
|
}
|
|
81
139
|
],
|
|
82
140
|
search: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/model/do-not-contact-domain-list-query.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 { nameSearchTokens } from '@aglyn/aglyn/app-utils/name-search'\nimport {\n LIST_QUERY_ID_PATH,\n type ListQueryDeclaration,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport type { OutreachDoNotContactDomainEntry } from './outreach.types'\n\n/*\n * WHAT THE DO-NOT-CONTACT DOMAINS LIST ASKS FIRESTORE (AGL-3321).\n *\n * One query over `orgs/{orgId}/outreachDoNotContactDomains`, alphabetical —\n * the document id IS the domain — paged by that query:\n *\n * domain `equals` one domain, on the `domain` field every entry carries\n * beside its id, lower-cased the way every domain is written; a\n * prefix is the search box's job, which reads every label;\n * reason the stored reason, which every entry carries;\n * added the list's ONE range: when the entry was added, which every\n * entry carries; a range leads the order, so while it applies\n * the list is newest added first;\n * search `searchTokens`: the domain, each shorter domain inside it and\n * each of its labels, and the words of its detail, stamped when\n * the entry is created.\n *\n * Without the range every shape is equalities and the one array clause,\n * ordered by the document id, which Firestore serves by merging\n * single-field indexes. The range's order needs one composite per\n * equality field beside it: `reason` and `searchTokens`.\n */\n\n/** The array the search box reads, stamped by the one writer that adds a domain. */\nexport const OUTREACH_DOMAIN_SEARCH_TOKENS = 'searchTokens'\n\nexport const OUTREACH_DO_NOT_CONTACT_DOMAIN_LIST_QUERY: ListQueryDeclaration = {\n fields: [\n { column: 'domain', kind: 'text', path: 'domain', lowerPath: 'domain', operators: ['equals'] },\n { column: 'reason', kind: 'exact', path: 'reason', presence: 'always', operators: ['equals', 'isAnyOf'] },\n {\n column: 'addedAtMs',\n kind: 'date',\n path: 'addedAtMs',\n storedAs: 'millis',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n ],\n sorts: [{ path: LIST_QUERY_ID_PATH, direction: 'asc' }],\n search: { tokensPath: OUTREACH_DOMAIN_SEARCH_TOKENS },\n}\n\n/**\n * What the domains list searches an entry by: every prefix of the domain\n * whole and of each shorter domain inside it, of each of its labels, and of\n * each word of its detail — so \"acme\", \"acme.com\" and \"mail.ac\" all find\n * `mail.acme.com`, and \"barracuda\" finds the domain a gateway block filed.\n * An entry is created once and never edited, so its one writer stamps this\n * and `tools/scripts/backfill-outreach-list-search.mjs` restates it for the\n * entries made before it; both are held to\n * `tools/scripts/lib/outreach-search-tokens.fixtures.json`.\n */\nexport function outreachDomainSearchTokens(\n entry: Pick<OutreachDoNotContactDomainEntry, 'domain' | 'detail'>,\n): string[] {\n const domain = String(entry.domain ?? '')\n const labels = domain.split('.')\n const within = labels.map((_, at) => labels.slice(at).join('.'))\n return nameSearchTokens([...within, ...domain.split(/[.-]+/), entry.detail ?? ''].join(' '))\n}\n"],"names":["nameSearchTokens","LIST_QUERY_ID_PATH","OUTREACH_DOMAIN_SEARCH_TOKENS","OUTREACH_DO_NOT_CONTACT_DOMAIN_LIST_QUERY","fields","column","kind","path","lowerPath","operators","presence","storedAs","sorts","direction","search","tokensPath","outreachDomainSearchTokens","entry","domain","String","labels","split","within","map","_","at","slice","join","detail"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,gBAAgB,QAAQ,qCAAoC;AACrE,SACEC,kBAAkB,QAEb,6CAA4C;AAGnD;;;;;;;;;;;;;;;;;;;;;CAqBC,GAED,kFAAkF,GAClF,OAAO,MAAMC,gCAAgC,eAAc;AAE3D,OAAO,MAAMC,4CAAkE;IAC7EC,QAAQ;QACN;YAAEC,QAAQ;YAAUC,MAAM;YAAQC,MAAM;YAAUC,WAAW;YAAUC,WAAW;gBAAC;aAAS;QAAC;QAC7F;YAAEJ,QAAQ;YAAUC,MAAM;YAASC,MAAM;YAAUG,UAAU;YAAUD,WAAW;gBAAC;gBAAU;aAAU;QAAC;QACxG;YACEJ,QAAQ;YACRC,MAAM;YACNC,MAAM;YACNI,UAAU;YACVD,UAAU;YACVD,WAAW;gBAAC;gBAAM;gBAAS;gBAAa;gBAAU;aAAa;QACjE;KACD;
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/model/do-not-contact-domain-list-query.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 { nameSearchTokens } from '@aglyn/aglyn/app-utils/name-search'\nimport {\n LIST_QUERY_ID_PATH,\n type ListQueryDeclaration,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\nimport type { OutreachDoNotContactDomainEntry } from './outreach.types'\n\n/*\n * WHAT THE DO-NOT-CONTACT DOMAINS LIST ASKS FIRESTORE (AGL-3321).\n *\n * One query over `orgs/{orgId}/outreachDoNotContactDomains`, alphabetical —\n * the document id IS the domain — paged by that query:\n *\n * domain `equals` one domain, on the `domain` field every entry carries\n * beside its id, lower-cased the way every domain is written; a\n * prefix is the search box's job, which reads every label;\n * reason the stored reason, which every entry carries;\n * added the list's ONE range: when the entry was added, which every\n * entry carries; a range leads the order, so while it applies\n * the list is newest added first;\n * search `searchTokens`: the domain, each shorter domain inside it and\n * each of its labels, and the words of its detail, stamped when\n * the entry is created.\n *\n * Without the range every shape is equalities and the one array clause,\n * ordered by the document id, which Firestore serves by merging\n * single-field indexes. The range's order needs one composite per\n * equality field beside it: `reason` and `searchTokens`.\n */\n\n/** The array the search box reads, stamped by the one writer that adds a domain. */\nexport const OUTREACH_DOMAIN_SEARCH_TOKENS = 'searchTokens'\n\nexport const OUTREACH_DO_NOT_CONTACT_DOMAIN_LIST_QUERY: ListQueryDeclaration = {\n fields: [\n { column: 'domain', kind: 'text', path: 'domain', lowerPath: 'domain', operators: ['equals'] },\n { column: 'reason', kind: 'exact', path: 'reason', presence: 'always', operators: ['equals', 'isAnyOf'] },\n {\n column: 'addedAtMs',\n kind: 'date',\n path: 'addedAtMs',\n storedAs: 'millis',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n ],\n /*\n * The headers (AGL-3680). A domain IS its document id, so Domain orders by\n * the id, alphabetical first as the list always was. Why, Added and Detail\n * are on every entry — the one writer stamps `reason`, `addedAtMs` and\n * `detail` (null when none) — and order the query `alone`: on this\n * unscoped collection that costs no composite. Added newest first is also\n * the order its date range imposes.\n */\n sorts: [\n { path: LIST_QUERY_ID_PATH, direction: 'asc', column: 'domain', label: 'Domain' },\n { path: LIST_QUERY_ID_PATH, direction: 'desc', column: 'domain', label: 'Domain', alone: true },\n { path: 'reason', direction: 'asc', column: 'reason', label: 'Why', alone: true },\n { path: 'reason', direction: 'desc', column: 'reason', label: 'Why', alone: true },\n { path: 'addedAtMs', direction: 'desc', column: 'addedAtMs', label: 'Added', alone: true },\n { path: 'addedAtMs', direction: 'asc', column: 'addedAtMs', label: 'Added', alone: true },\n { path: 'detail', direction: 'asc', column: 'detail', label: 'Detail', alone: true },\n { path: 'detail', direction: 'desc', column: 'detail', label: 'Detail', alone: true },\n ],\n search: { tokensPath: OUTREACH_DOMAIN_SEARCH_TOKENS },\n}\n\n/**\n * What the domains list searches an entry by: every prefix of the domain\n * whole and of each shorter domain inside it, of each of its labels, and of\n * each word of its detail — so \"acme\", \"acme.com\" and \"mail.ac\" all find\n * `mail.acme.com`, and \"barracuda\" finds the domain a gateway block filed.\n * An entry is created once and never edited, so its one writer stamps this\n * and `tools/scripts/backfill-outreach-list-search.mjs` restates it for the\n * entries made before it; both are held to\n * `tools/scripts/lib/outreach-search-tokens.fixtures.json`.\n */\nexport function outreachDomainSearchTokens(\n entry: Pick<OutreachDoNotContactDomainEntry, 'domain' | 'detail'>,\n): string[] {\n const domain = String(entry.domain ?? '')\n const labels = domain.split('.')\n const within = labels.map((_, at) => labels.slice(at).join('.'))\n return nameSearchTokens([...within, ...domain.split(/[.-]+/), entry.detail ?? ''].join(' '))\n}\n"],"names":["nameSearchTokens","LIST_QUERY_ID_PATH","OUTREACH_DOMAIN_SEARCH_TOKENS","OUTREACH_DO_NOT_CONTACT_DOMAIN_LIST_QUERY","fields","column","kind","path","lowerPath","operators","presence","storedAs","sorts","direction","label","alone","search","tokensPath","outreachDomainSearchTokens","entry","domain","String","labels","split","within","map","_","at","slice","join","detail"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,gBAAgB,QAAQ,qCAAoC;AACrE,SACEC,kBAAkB,QAEb,6CAA4C;AAGnD;;;;;;;;;;;;;;;;;;;;;CAqBC,GAED,kFAAkF,GAClF,OAAO,MAAMC,gCAAgC,eAAc;AAE3D,OAAO,MAAMC,4CAAkE;IAC7EC,QAAQ;QACN;YAAEC,QAAQ;YAAUC,MAAM;YAAQC,MAAM;YAAUC,WAAW;YAAUC,WAAW;gBAAC;aAAS;QAAC;QAC7F;YAAEJ,QAAQ;YAAUC,MAAM;YAASC,MAAM;YAAUG,UAAU;YAAUD,WAAW;gBAAC;gBAAU;aAAU;QAAC;QACxG;YACEJ,QAAQ;YACRC,MAAM;YACNC,MAAM;YACNI,UAAU;YACVD,UAAU;YACVD,WAAW;gBAAC;gBAAM;gBAAS;gBAAa;gBAAU;aAAa;QACjE;KACD;IACD;;;;;;;GAOC,GACDG,OAAO;QACL;YAAEL,MAAMN;YAAoBY,WAAW;YAAOR,QAAQ;YAAUS,OAAO;QAAS;QAChF;YAAEP,MAAMN;YAAoBY,WAAW;YAAQR,QAAQ;YAAUS,OAAO;YAAUC,OAAO;QAAK;QAC9F;YAAER,MAAM;YAAUM,WAAW;YAAOR,QAAQ;YAAUS,OAAO;YAAOC,OAAO;QAAK;QAChF;YAAER,MAAM;YAAUM,WAAW;YAAQR,QAAQ;YAAUS,OAAO;YAAOC,OAAO;QAAK;QACjF;YAAER,MAAM;YAAaM,WAAW;YAAQR,QAAQ;YAAaS,OAAO;YAASC,OAAO;QAAK;QACzF;YAAER,MAAM;YAAaM,WAAW;YAAOR,QAAQ;YAAaS,OAAO;YAASC,OAAO;QAAK;QACxF;YAAER,MAAM;YAAUM,WAAW;YAAOR,QAAQ;YAAUS,OAAO;YAAUC,OAAO;QAAK;QACnF;YAAER,MAAM;YAAUM,WAAW;YAAQR,QAAQ;YAAUS,OAAO;YAAUC,OAAO;QAAK;KACrF;IACDC,QAAQ;QAAEC,YAAYf;IAA8B;AACtD,EAAC;AAED;;;;;;;;;CASC,GACD,OAAO,SAASgB,2BACdC,KAAiE;QAE3CA,eAGwCA;IAH9D,MAAMC,SAASC,QAAOF,gBAAAA,MAAMC,MAAM,YAAZD,gBAAgB;IACtC,MAAMG,SAASF,OAAOG,KAAK,CAAC;IAC5B,MAAMC,SAASF,OAAOG,GAAG,CAAC,CAACC,GAAGC,KAAOL,OAAOM,KAAK,CAACD,IAAIE,IAAI,CAAC;IAC3D,OAAO7B,iBAAiB;WAAIwB;WAAWJ,OAAOG,KAAK,CAAC;SAAUJ,gBAAAA,MAAMW,MAAM,YAAZX,gBAAgB;KAAG,CAACU,IAAI,CAAC;AACzF"}
|
|
@@ -128,10 +128,63 @@ export const OUTREACH_ENROLLMENT_LIST_QUERY = {
|
|
|
128
128
|
]
|
|
129
129
|
}
|
|
130
130
|
],
|
|
131
|
-
|
|
131
|
+
/*
|
|
132
|
+
* The headers (AGL-3680). `buildOutreachEnrollment` stores every one of
|
|
133
|
+
* these on every enrollment from its first write — `stopReason` and
|
|
134
|
+
* `nextDueAtMs` as null until they apply — so each orders the query,
|
|
135
|
+
* `alone`: beside the `sequenceId` base that is one composite per order.
|
|
136
|
+
* Each costs a composite, so only Person (the stored lower-cased address)
|
|
137
|
+
* sorts both ways; Status and Stop reason group, Current step reads from
|
|
138
|
+
* the first step and Next send from the soonest, one direction each.
|
|
139
|
+
* Last activity and the engagement figures are derived from
|
|
140
|
+
* `engagement.*`, which is absent until the first event, and sort the page.
|
|
141
|
+
*/ sorts: [
|
|
132
142
|
{
|
|
133
143
|
path: 'createdAtMs',
|
|
134
|
-
direction: 'desc'
|
|
144
|
+
direction: 'desc',
|
|
145
|
+
label: 'newest enrolled first'
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
path: 'email',
|
|
149
|
+
direction: 'asc',
|
|
150
|
+
column: 'person',
|
|
151
|
+
label: 'Person',
|
|
152
|
+
alone: true
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
path: 'email',
|
|
156
|
+
direction: 'desc',
|
|
157
|
+
column: 'person',
|
|
158
|
+
label: 'Person',
|
|
159
|
+
alone: true
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
path: 'status',
|
|
163
|
+
direction: 'asc',
|
|
164
|
+
column: 'status',
|
|
165
|
+
label: 'Status',
|
|
166
|
+
alone: true
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
path: 'stepIndex',
|
|
170
|
+
direction: 'asc',
|
|
171
|
+
column: 'step',
|
|
172
|
+
label: 'Current step',
|
|
173
|
+
alone: true
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
path: 'nextDueAtMs',
|
|
177
|
+
direction: 'asc',
|
|
178
|
+
column: 'nextSend',
|
|
179
|
+
label: 'Next send',
|
|
180
|
+
alone: true
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
path: 'stopReason',
|
|
184
|
+
direction: 'asc',
|
|
185
|
+
column: 'stopReason',
|
|
186
|
+
label: 'Stop reason',
|
|
187
|
+
alone: true
|
|
135
188
|
}
|
|
136
189
|
],
|
|
137
190
|
search: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/model/enrollment-list-query.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 type {\n ListQueryDeclaration,\n ListQueryFilter,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\n\n/*\n * WHAT A SEQUENCE'S ENROLLMENTS TABLE ASKS FIRESTORE (AGL-3321).\n *\n * One query over `orgs/{orgId}/outreachEnrollments`, narrowed to the\n * sequence (`sequenceId ==`, the list's base) and ordered newest enrolled\n * first, paged by that query. Every clause the Filters panel offers and the\n * search box are predicates on it:\n *\n * status the stored status, on every enrollment;\n * target `contact` or `lead`, on every enrollment (the ones made\n * before leads could be sequenced gain `contact` from the\n * backfill);\n * stopReason the stored reason code — the column shows its label and the\n * stop detail, the query asks by code;\n * email the whole address, stored normalized, so `equals` is exact;\n * enrolled the table's ONE range: when the person was enrolled, the\n * field the table is already ordered by, so it costs no index\n * of its own;\n * search `searchTokens`: every prefix of the person's name and of\n * their address and its parts, stamped at enrollment by the\n * enroll route and by `tools/scripts/backfill-outreach-list-search.mjs`\n * for the enrollments made before it;\n * clicked (AGL-3332) whether the person clicked a link: `clicked`,\n * `false` at enrollment and set by the click route's\n * transaction on a person's click (never a scanner's), and\n * stamped from `engagement.clicks` by the same backfill;\n * link (AGL-3332) a destination they followed, whole:\n * `engagement.links`, the capped array the click route keeps,\n * asked with `array-contains-any` — the query's one array\n * clause, so it and the search cannot stand together and the\n * plan refuses the pair by name. The backfill seeds it with an\n * earlier click's `lastClickUrl`, the one destination such a\n * click is known by.\n *\n * The step, the next send and the last activity are not offered: each is\n * derived from the sequence's steps or from several fields, and a query can\n * ask none of them.\n */\n\n/** The array the search box reads, stamped by the enroll route. */\nexport const OUTREACH_ENROLLMENT_SEARCH_TOKENS = 'searchTokens'\n\n/** Whether the person clicked (AGL-3332): what the \"Clicked\" filter asks. */\nexport const OUTREACH_ENROLLMENT_CLICKED = 'clicked'\n\n/** The destinations followed (AGL-3332): what the \"Link followed\" filter asks. */\nexport const OUTREACH_ENROLLMENT_LINKS_PATH = 'engagement.links'\n\n/** The fields the table offers only for a sequence that counts clicks. */\nexport const OUTREACH_ENROLLMENT_CLICK_FIELDS = ['clicked', 'link'] as const\n\nexport const OUTREACH_ENROLLMENT_LIST_QUERY: ListQueryDeclaration = {\n fields: [\n { column: 'status', kind: 'exact', path: 'status', presence: 'always', operators: ['equals', 'isAnyOf'] },\n { column: 'target', kind: 'exact', path: 'target', presence: 'always', operators: ['equals'] },\n { column: 'stopReason', kind: 'exact', path: 'stopReason', operators: ['equals', 'isAnyOf'] },\n { column: 'email', kind: 'text', path: 'email', lowerPath: 'email', operators: ['equals'] },\n {\n column: 'enrolled',\n kind: 'date',\n path: 'createdAtMs',\n storedAs: 'millis',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n { column: 'clicked', kind: 'boolean', path: OUTREACH_ENROLLMENT_CLICKED, operators: ['equals'] },\n {\n column: 'link',\n kind: 'exact',\n path: OUTREACH_ENROLLMENT_LINKS_PATH,\n tokensPath: OUTREACH_ENROLLMENT_LINKS_PATH,\n operators: ['isAnyOf'],\n },\n ],\n sorts: [{ path: 'createdAtMs', direction: 'desc' }],\n search: { tokensPath: OUTREACH_ENROLLMENT_SEARCH_TOKENS },\n}\n\n/** The scope every page of the table carries: this sequence's people. */\nexport function outreachEnrollmentListBase(sequenceId: string): ListQueryFilter[] {\n return [{ path: 'sequenceId', op: '==', value: sequenceId }]\n}\n"],"names":["OUTREACH_ENROLLMENT_SEARCH_TOKENS","OUTREACH_ENROLLMENT_CLICKED","OUTREACH_ENROLLMENT_LINKS_PATH","OUTREACH_ENROLLMENT_CLICK_FIELDS","OUTREACH_ENROLLMENT_LIST_QUERY","fields","column","kind","path","presence","operators","lowerPath","storedAs","tokensPath","sorts","direction","search","outreachEnrollmentListBase","sequenceId","op","value"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCC,GAED,iEAAiE,GACjE,OAAO,MAAMA,oCAAoC,eAAc;AAE/D,2EAA2E,GAC3E,OAAO,MAAMC,8BAA8B,UAAS;AAEpD,gFAAgF,GAChF,OAAO,MAAMC,iCAAiC,mBAAkB;AAEhE,wEAAwE,GACxE,OAAO,MAAMC,mCAAmC;IAAC;IAAW;CAAO,CAAS;AAE5E,OAAO,MAAMC,iCAAuD;IAClEC,QAAQ;QACN;YAAEC,QAAQ;YAAUC,MAAM;YAASC,MAAM;YAAUC,UAAU;YAAUC,WAAW;gBAAC;gBAAU;aAAU;QAAC;QACxG;YAAEJ,QAAQ;YAAUC,MAAM;YAASC,MAAM;YAAUC,UAAU;YAAUC,WAAW;gBAAC;aAAS;QAAC;QAC7F;YAAEJ,QAAQ;YAAcC,MAAM;YAASC,MAAM;YAAcE,WAAW;gBAAC;gBAAU;aAAU;QAAC;QAC5F;YAAEJ,QAAQ;YAASC,MAAM;YAAQC,MAAM;YAASG,WAAW;YAASD,WAAW;gBAAC;aAAS;QAAC;QAC1F;YACEJ,QAAQ;YACRC,MAAM;YACNC,MAAM;YACNI,UAAU;YACVH,UAAU;YACVC,WAAW;gBAAC;gBAAM;gBAAS;gBAAa;gBAAU;aAAa;QACjE;QACA;YAAEJ,QAAQ;YAAWC,MAAM;YAAWC,MAAMP;YAA6BS,WAAW;gBAAC;aAAS;QAAC;QAC/F;YACEJ,QAAQ;YACRC,MAAM;YACNC,MAAMN;YACNW,YAAYX;YACZQ,WAAW;gBAAC;aAAU;QACxB;KACD;
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/model/enrollment-list-query.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 type {\n ListQueryDeclaration,\n ListQueryFilter,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\n\n/*\n * WHAT A SEQUENCE'S ENROLLMENTS TABLE ASKS FIRESTORE (AGL-3321).\n *\n * One query over `orgs/{orgId}/outreachEnrollments`, narrowed to the\n * sequence (`sequenceId ==`, the list's base) and ordered newest enrolled\n * first, paged by that query. Every clause the Filters panel offers and the\n * search box are predicates on it:\n *\n * status the stored status, on every enrollment;\n * target `contact` or `lead`, on every enrollment (the ones made\n * before leads could be sequenced gain `contact` from the\n * backfill);\n * stopReason the stored reason code — the column shows its label and the\n * stop detail, the query asks by code;\n * email the whole address, stored normalized, so `equals` is exact;\n * enrolled the table's ONE range: when the person was enrolled, the\n * field the table is already ordered by, so it costs no index\n * of its own;\n * search `searchTokens`: every prefix of the person's name and of\n * their address and its parts, stamped at enrollment by the\n * enroll route and by `tools/scripts/backfill-outreach-list-search.mjs`\n * for the enrollments made before it;\n * clicked (AGL-3332) whether the person clicked a link: `clicked`,\n * `false` at enrollment and set by the click route's\n * transaction on a person's click (never a scanner's), and\n * stamped from `engagement.clicks` by the same backfill;\n * link (AGL-3332) a destination they followed, whole:\n * `engagement.links`, the capped array the click route keeps,\n * asked with `array-contains-any` — the query's one array\n * clause, so it and the search cannot stand together and the\n * plan refuses the pair by name. The backfill seeds it with an\n * earlier click's `lastClickUrl`, the one destination such a\n * click is known by.\n *\n * The step, the next send and the last activity are not offered: each is\n * derived from the sequence's steps or from several fields, and a query can\n * ask none of them.\n */\n\n/** The array the search box reads, stamped by the enroll route. */\nexport const OUTREACH_ENROLLMENT_SEARCH_TOKENS = 'searchTokens'\n\n/** Whether the person clicked (AGL-3332): what the \"Clicked\" filter asks. */\nexport const OUTREACH_ENROLLMENT_CLICKED = 'clicked'\n\n/** The destinations followed (AGL-3332): what the \"Link followed\" filter asks. */\nexport const OUTREACH_ENROLLMENT_LINKS_PATH = 'engagement.links'\n\n/** The fields the table offers only for a sequence that counts clicks. */\nexport const OUTREACH_ENROLLMENT_CLICK_FIELDS = ['clicked', 'link'] as const\n\nexport const OUTREACH_ENROLLMENT_LIST_QUERY: ListQueryDeclaration = {\n fields: [\n { column: 'status', kind: 'exact', path: 'status', presence: 'always', operators: ['equals', 'isAnyOf'] },\n { column: 'target', kind: 'exact', path: 'target', presence: 'always', operators: ['equals'] },\n { column: 'stopReason', kind: 'exact', path: 'stopReason', operators: ['equals', 'isAnyOf'] },\n { column: 'email', kind: 'text', path: 'email', lowerPath: 'email', operators: ['equals'] },\n {\n column: 'enrolled',\n kind: 'date',\n path: 'createdAtMs',\n storedAs: 'millis',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n { column: 'clicked', kind: 'boolean', path: OUTREACH_ENROLLMENT_CLICKED, operators: ['equals'] },\n {\n column: 'link',\n kind: 'exact',\n path: OUTREACH_ENROLLMENT_LINKS_PATH,\n tokensPath: OUTREACH_ENROLLMENT_LINKS_PATH,\n operators: ['isAnyOf'],\n },\n ],\n /*\n * The headers (AGL-3680). `buildOutreachEnrollment` stores every one of\n * these on every enrollment from its first write — `stopReason` and\n * `nextDueAtMs` as null until they apply — so each orders the query,\n * `alone`: beside the `sequenceId` base that is one composite per order.\n * Each costs a composite, so only Person (the stored lower-cased address)\n * sorts both ways; Status and Stop reason group, Current step reads from\n * the first step and Next send from the soonest, one direction each.\n * Last activity and the engagement figures are derived from\n * `engagement.*`, which is absent until the first event, and sort the page.\n */\n sorts: [\n { path: 'createdAtMs', direction: 'desc', label: 'newest enrolled first' },\n { path: 'email', direction: 'asc', column: 'person', label: 'Person', alone: true },\n { path: 'email', direction: 'desc', column: 'person', label: 'Person', alone: true },\n { path: 'status', direction: 'asc', column: 'status', label: 'Status', alone: true },\n { path: 'stepIndex', direction: 'asc', column: 'step', label: 'Current step', alone: true },\n { path: 'nextDueAtMs', direction: 'asc', column: 'nextSend', label: 'Next send', alone: true },\n { path: 'stopReason', direction: 'asc', column: 'stopReason', label: 'Stop reason', alone: true },\n ],\n search: { tokensPath: OUTREACH_ENROLLMENT_SEARCH_TOKENS },\n}\n\n/** The scope every page of the table carries: this sequence's people. */\nexport function outreachEnrollmentListBase(sequenceId: string): ListQueryFilter[] {\n return [{ path: 'sequenceId', op: '==', value: sequenceId }]\n}\n"],"names":["OUTREACH_ENROLLMENT_SEARCH_TOKENS","OUTREACH_ENROLLMENT_CLICKED","OUTREACH_ENROLLMENT_LINKS_PATH","OUTREACH_ENROLLMENT_CLICK_FIELDS","OUTREACH_ENROLLMENT_LIST_QUERY","fields","column","kind","path","presence","operators","lowerPath","storedAs","tokensPath","sorts","direction","label","alone","search","outreachEnrollmentListBase","sequenceId","op","value"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAOD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCC,GAED,iEAAiE,GACjE,OAAO,MAAMA,oCAAoC,eAAc;AAE/D,2EAA2E,GAC3E,OAAO,MAAMC,8BAA8B,UAAS;AAEpD,gFAAgF,GAChF,OAAO,MAAMC,iCAAiC,mBAAkB;AAEhE,wEAAwE,GACxE,OAAO,MAAMC,mCAAmC;IAAC;IAAW;CAAO,CAAS;AAE5E,OAAO,MAAMC,iCAAuD;IAClEC,QAAQ;QACN;YAAEC,QAAQ;YAAUC,MAAM;YAASC,MAAM;YAAUC,UAAU;YAAUC,WAAW;gBAAC;gBAAU;aAAU;QAAC;QACxG;YAAEJ,QAAQ;YAAUC,MAAM;YAASC,MAAM;YAAUC,UAAU;YAAUC,WAAW;gBAAC;aAAS;QAAC;QAC7F;YAAEJ,QAAQ;YAAcC,MAAM;YAASC,MAAM;YAAcE,WAAW;gBAAC;gBAAU;aAAU;QAAC;QAC5F;YAAEJ,QAAQ;YAASC,MAAM;YAAQC,MAAM;YAASG,WAAW;YAASD,WAAW;gBAAC;aAAS;QAAC;QAC1F;YACEJ,QAAQ;YACRC,MAAM;YACNC,MAAM;YACNI,UAAU;YACVH,UAAU;YACVC,WAAW;gBAAC;gBAAM;gBAAS;gBAAa;gBAAU;aAAa;QACjE;QACA;YAAEJ,QAAQ;YAAWC,MAAM;YAAWC,MAAMP;YAA6BS,WAAW;gBAAC;aAAS;QAAC;QAC/F;YACEJ,QAAQ;YACRC,MAAM;YACNC,MAAMN;YACNW,YAAYX;YACZQ,WAAW;gBAAC;aAAU;QACxB;KACD;IACD;;;;;;;;;;GAUC,GACDI,OAAO;QACL;YAAEN,MAAM;YAAeO,WAAW;YAAQC,OAAO;QAAwB;QACzE;YAAER,MAAM;YAASO,WAAW;YAAOT,QAAQ;YAAUU,OAAO;YAAUC,OAAO;QAAK;QAClF;YAAET,MAAM;YAASO,WAAW;YAAQT,QAAQ;YAAUU,OAAO;YAAUC,OAAO;QAAK;QACnF;YAAET,MAAM;YAAUO,WAAW;YAAOT,QAAQ;YAAUU,OAAO;YAAUC,OAAO;QAAK;QACnF;YAAET,MAAM;YAAaO,WAAW;YAAOT,QAAQ;YAAQU,OAAO;YAAgBC,OAAO;QAAK;QAC1F;YAAET,MAAM;YAAeO,WAAW;YAAOT,QAAQ;YAAYU,OAAO;YAAaC,OAAO;QAAK;QAC7F;YAAET,MAAM;YAAcO,WAAW;YAAOT,QAAQ;YAAcU,OAAO;YAAeC,OAAO;QAAK;KACjG;IACDC,QAAQ;QAAEL,YAAYb;IAAkC;AAC1D,EAAC;AAED,uEAAuE,GACvE,OAAO,SAASmB,2BAA2BC,UAAkB;IAC3D,OAAO;QAAC;YAAEZ,MAAM;YAAca,IAAI;YAAMC,OAAOF;QAAW;KAAE;AAC9D"}
|
|
@@ -85,10 +85,44 @@ export const OUTREACH_SEQUENCE_LIST_QUERY = {
|
|
|
85
85
|
]
|
|
86
86
|
}
|
|
87
87
|
],
|
|
88
|
-
|
|
88
|
+
/*
|
|
89
|
+
* The headers (AGL-3680): Name (`nameLower`, stamped by the save route)
|
|
90
|
+
* and Status order the query, `alone` — on this unscoped collection that
|
|
91
|
+
* costs no composite. Mailbox is a label looked up from the mailbox id,
|
|
92
|
+
* and the counts are counted, so both sort the page.
|
|
93
|
+
*/ sorts: [
|
|
89
94
|
{
|
|
90
95
|
path: 'createdAtMs',
|
|
91
|
-
direction: 'desc'
|
|
96
|
+
direction: 'desc',
|
|
97
|
+
label: 'newest first'
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
path: OUTREACH_SEQUENCE_NAME_LOWER,
|
|
101
|
+
direction: 'asc',
|
|
102
|
+
column: 'name',
|
|
103
|
+
label: 'Name',
|
|
104
|
+
alone: true
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
path: OUTREACH_SEQUENCE_NAME_LOWER,
|
|
108
|
+
direction: 'desc',
|
|
109
|
+
column: 'name',
|
|
110
|
+
label: 'Name',
|
|
111
|
+
alone: true
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
path: 'status',
|
|
115
|
+
direction: 'asc',
|
|
116
|
+
column: 'status',
|
|
117
|
+
label: 'Status',
|
|
118
|
+
alone: true
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
path: 'status',
|
|
122
|
+
direction: 'desc',
|
|
123
|
+
column: 'status',
|
|
124
|
+
label: 'Status',
|
|
125
|
+
alone: true
|
|
92
126
|
}
|
|
93
127
|
],
|
|
94
128
|
search: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/model/sequence-list-query.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 type { ListQueryDeclaration } from '@aglyn/shared-ui-jsx/const/list-query-plan'\n\n/*\n * WHAT THE SEQUENCES LIST ASKS FIRESTORE (AGL-3321).\n *\n * Every clause the list's Filters panel offers, and its search box, is a\n * predicate on ONE query over `orgs/{orgId}/outreachSequences`, newest\n * first, paged by that query — never a match over the sequences a read\n * happened to hold. Each field is here because a query can serve it:\n *\n * name `contains` a word (the `nameTokens` array) or `equals` the\n * whole name (`nameLower`), both stamped by the save route —\n * the one write that sets a name — through `nameSearchFields`,\n * and by `tools/scripts/backfill-outreach-list-search.mjs` for\n * the sequences saved before them;\n * mailbox the sequence's `mailboxId`, picked from the org's mailboxes —\n * the column shows the address, the query asks by id;\n * status the stored status, which every sequence carries;\n * created the list's ONE range: when the sequence was first saved.\n *\n * The one order is `createdAtMs` descending, which the save route stamps on\n * every sequence, and the range is over that same field — a range leads the\n * order, and here it already does, so it costs no index of its own. A range\n * on any other field would reorder the list by it and need a composite per\n * field above under that order as well.\n */\n\n/** The array `contains` and the search box read: every word's prefixes. */\nexport const OUTREACH_SEQUENCE_NAME_TOKENS = 'nameTokens'\n/** The lower-cased whole name `equals` reads. */\nexport const OUTREACH_SEQUENCE_NAME_LOWER = 'nameLower'\n\nexport const OUTREACH_SEQUENCE_LIST_QUERY: ListQueryDeclaration = {\n fields: [\n {\n column: 'name',\n kind: 'text',\n path: 'name',\n lowerPath: OUTREACH_SEQUENCE_NAME_LOWER,\n tokensPath: OUTREACH_SEQUENCE_NAME_TOKENS,\n operators: ['contains', 'equals'],\n },\n { column: 'mailbox', kind: 'exact', path: 'mailboxId', operators: ['equals', 'isAnyOf'] },\n { column: 'status', kind: 'exact', path: 'status', presence: 'always', operators: ['equals', 'isAnyOf'] },\n {\n column: 'created',\n kind: 'date',\n path: 'createdAtMs',\n storedAs: 'millis',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n ],\n sorts: [{ path: 'createdAtMs', direction: 'desc' }],\n search: { tokensPath: OUTREACH_SEQUENCE_NAME_TOKENS },\n}\n"],"names":["OUTREACH_SEQUENCE_NAME_TOKENS","OUTREACH_SEQUENCE_NAME_LOWER","OUTREACH_SEQUENCE_LIST_QUERY","fields","column","kind","path","lowerPath","tokensPath","operators","presence","storedAs","sorts","direction","search"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID;;;;;;;;;;;;;;;;;;;;;;;CAuBC,GAED,yEAAyE,GACzE,OAAO,MAAMA,gCAAgC,aAAY;AACzD,+CAA+C,GAC/C,OAAO,MAAMC,+BAA+B,YAAW;AAEvD,OAAO,MAAMC,+BAAqD;IAChEC,QAAQ;QACN;YACEC,QAAQ;YACRC,MAAM;YACNC,MAAM;YACNC,WAAWN;YACXO,YAAYR;YACZS,WAAW;gBAAC;gBAAY;aAAS;QACnC;QACA;YAAEL,QAAQ;YAAWC,MAAM;YAASC,MAAM;YAAaG,WAAW;gBAAC;gBAAU;aAAU;QAAC;QACxF;YAAEL,QAAQ;YAAUC,MAAM;YAASC,MAAM;YAAUI,UAAU;YAAUD,WAAW;gBAAC;gBAAU;aAAU;QAAC;QACxG;YACEL,QAAQ;YACRC,MAAM;YACNC,MAAM;YACNK,UAAU;YACVD,UAAU;YACVD,WAAW;gBAAC;gBAAM;gBAAS;gBAAa;gBAAU;aAAa;QACjE;KACD;
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/outreach/src/lib/model/sequence-list-query.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 type { ListQueryDeclaration } from '@aglyn/shared-ui-jsx/const/list-query-plan'\n\n/*\n * WHAT THE SEQUENCES LIST ASKS FIRESTORE (AGL-3321).\n *\n * Every clause the list's Filters panel offers, and its search box, is a\n * predicate on ONE query over `orgs/{orgId}/outreachSequences`, newest\n * first, paged by that query — never a match over the sequences a read\n * happened to hold. Each field is here because a query can serve it:\n *\n * name `contains` a word (the `nameTokens` array) or `equals` the\n * whole name (`nameLower`), both stamped by the save route —\n * the one write that sets a name — through `nameSearchFields`,\n * and by `tools/scripts/backfill-outreach-list-search.mjs` for\n * the sequences saved before them;\n * mailbox the sequence's `mailboxId`, picked from the org's mailboxes —\n * the column shows the address, the query asks by id;\n * status the stored status, which every sequence carries;\n * created the list's ONE range: when the sequence was first saved.\n *\n * The one order is `createdAtMs` descending, which the save route stamps on\n * every sequence, and the range is over that same field — a range leads the\n * order, and here it already does, so it costs no index of its own. A range\n * on any other field would reorder the list by it and need a composite per\n * field above under that order as well.\n */\n\n/** The array `contains` and the search box read: every word's prefixes. */\nexport const OUTREACH_SEQUENCE_NAME_TOKENS = 'nameTokens'\n/** The lower-cased whole name `equals` reads. */\nexport const OUTREACH_SEQUENCE_NAME_LOWER = 'nameLower'\n\nexport const OUTREACH_SEQUENCE_LIST_QUERY: ListQueryDeclaration = {\n fields: [\n {\n column: 'name',\n kind: 'text',\n path: 'name',\n lowerPath: OUTREACH_SEQUENCE_NAME_LOWER,\n tokensPath: OUTREACH_SEQUENCE_NAME_TOKENS,\n operators: ['contains', 'equals'],\n },\n { column: 'mailbox', kind: 'exact', path: 'mailboxId', operators: ['equals', 'isAnyOf'] },\n { column: 'status', kind: 'exact', path: 'status', presence: 'always', operators: ['equals', 'isAnyOf'] },\n {\n column: 'created',\n kind: 'date',\n path: 'createdAtMs',\n storedAs: 'millis',\n presence: 'always',\n operators: ['is', 'after', 'onOrAfter', 'before', 'onOrBefore'],\n },\n ],\n /*\n * The headers (AGL-3680): Name (`nameLower`, stamped by the save route)\n * and Status order the query, `alone` — on this unscoped collection that\n * costs no composite. Mailbox is a label looked up from the mailbox id,\n * and the counts are counted, so both sort the page.\n */\n sorts: [\n { path: 'createdAtMs', direction: 'desc', label: 'newest first' },\n { path: OUTREACH_SEQUENCE_NAME_LOWER, direction: 'asc', column: 'name', label: 'Name', alone: true },\n { path: OUTREACH_SEQUENCE_NAME_LOWER, direction: 'desc', column: 'name', label: 'Name', alone: true },\n { path: 'status', direction: 'asc', column: 'status', label: 'Status', alone: true },\n { path: 'status', direction: 'desc', column: 'status', label: 'Status', alone: true },\n ],\n search: { tokensPath: OUTREACH_SEQUENCE_NAME_TOKENS },\n}\n"],"names":["OUTREACH_SEQUENCE_NAME_TOKENS","OUTREACH_SEQUENCE_NAME_LOWER","OUTREACH_SEQUENCE_LIST_QUERY","fields","column","kind","path","lowerPath","tokensPath","operators","presence","storedAs","sorts","direction","label","alone","search"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID;;;;;;;;;;;;;;;;;;;;;;;CAuBC,GAED,yEAAyE,GACzE,OAAO,MAAMA,gCAAgC,aAAY;AACzD,+CAA+C,GAC/C,OAAO,MAAMC,+BAA+B,YAAW;AAEvD,OAAO,MAAMC,+BAAqD;IAChEC,QAAQ;QACN;YACEC,QAAQ;YACRC,MAAM;YACNC,MAAM;YACNC,WAAWN;YACXO,YAAYR;YACZS,WAAW;gBAAC;gBAAY;aAAS;QACnC;QACA;YAAEL,QAAQ;YAAWC,MAAM;YAASC,MAAM;YAAaG,WAAW;gBAAC;gBAAU;aAAU;QAAC;QACxF;YAAEL,QAAQ;YAAUC,MAAM;YAASC,MAAM;YAAUI,UAAU;YAAUD,WAAW;gBAAC;gBAAU;aAAU;QAAC;QACxG;YACEL,QAAQ;YACRC,MAAM;YACNC,MAAM;YACNK,UAAU;YACVD,UAAU;YACVD,WAAW;gBAAC;gBAAM;gBAAS;gBAAa;gBAAU;aAAa;QACjE;KACD;IACD;;;;;GAKC,GACDG,OAAO;QACL;YAAEN,MAAM;YAAeO,WAAW;YAAQC,OAAO;QAAe;QAChE;YAAER,MAAML;YAA8BY,WAAW;YAAOT,QAAQ;YAAQU,OAAO;YAAQC,OAAO;QAAK;QACnG;YAAET,MAAML;YAA8BY,WAAW;YAAQT,QAAQ;YAAQU,OAAO;YAAQC,OAAO;QAAK;QACpG;YAAET,MAAM;YAAUO,WAAW;YAAOT,QAAQ;YAAUU,OAAO;YAAUC,OAAO;QAAK;QACnF;YAAET,MAAM;YAAUO,WAAW;YAAQT,QAAQ;YAAUU,OAAO;YAAUC,OAAO;QAAK;KACrF;IACDC,QAAQ;QAAER,YAAYR;IAA8B;AACtD,EAAC"}
|