@aglyn/plugins-email 1.0.0-beta.195 → 1.0.0-beta.197

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/consent-group-access.d.ts +33 -0
  3. package/src/lib/components/consent-group-access.js +73 -0
  4. package/src/lib/components/consent-group-access.js.map +1 -0
  5. package/src/lib/components/consent-group-change-progress.d.ts +55 -0
  6. package/src/lib/components/consent-group-change-progress.js +282 -0
  7. package/src/lib/components/consent-group-change-progress.js.map +1 -0
  8. package/src/lib/components/consent-group-confirmation-card.d.ts +6 -16
  9. package/src/lib/components/consent-group-confirmation-card.js +7 -50
  10. package/src/lib/components/consent-group-confirmation-card.js.map +1 -1
  11. package/src/lib/components/consent-group-dialog.d.ts +53 -0
  12. package/src/lib/components/consent-group-dialog.js +495 -0
  13. package/src/lib/components/consent-group-dialog.js.map +1 -0
  14. package/src/lib/components/consent-group-editing.d.ts +127 -0
  15. package/src/lib/components/consent-group-editing.js +225 -0
  16. package/src/lib/components/consent-group-editing.js.map +1 -0
  17. package/src/lib/components/consent-group-review.d.ts +62 -0
  18. package/src/lib/components/consent-group-review.js +353 -0
  19. package/src/lib/components/consent-group-review.js.map +1 -0
  20. package/src/lib/components/consent-group-site-picker.d.ts +35 -0
  21. package/src/lib/components/consent-group-site-picker.js +190 -0
  22. package/src/lib/components/consent-group-site-picker.js.map +1 -0
  23. package/src/lib/components/consent-groups-api.d.ts +158 -0
  24. package/src/lib/components/consent-groups-api.js +250 -0
  25. package/src/lib/components/consent-groups-api.js.map +1 -0
  26. package/src/lib/components/consent-groups-card.d.ts +47 -0
  27. package/src/lib/components/consent-groups-card.js +351 -0
  28. package/src/lib/components/consent-groups-card.js.map +1 -0
  29. package/src/lib/components/email-screens-card.js +1 -1
  30. package/src/lib/components/email-screens-card.js.map +1 -1
  31. package/src/lib/components/emails-console-page.d.ts +1 -1
  32. package/src/lib/components/emails-console-page.js +62 -27
  33. package/src/lib/components/emails-console-page.js.map +1 -1
  34. package/src/lib/components/emails-console-sections.d.ts +1 -1
  35. package/src/lib/components/emails-console-sections.js +14 -1
  36. package/src/lib/components/emails-console-sections.js.map +1 -1
  37. package/src/lib/components/org-email-templates-card.js +1 -1
  38. package/src/lib/components/org-email-templates-card.js.map +1 -1
  39. package/src/lib/components/site-consent-group-card.d.ts +23 -0
  40. package/src/lib/components/site-consent-group-card.js +80 -0
  41. package/src/lib/components/site-consent-group-card.js.map +1 -0
  42. package/src/lib/components/suppressions-card.js +28 -7
  43. package/src/lib/components/suppressions-card.js.map +1 -1
  44. package/src/lib/server-console.js +4 -2
  45. package/src/lib/server-console.js.map +1 -1
  46. package/src/lib/server-list-gate.d.ts +9 -0
  47. package/src/lib/server-list-gate.js +23 -9
  48. package/src/lib/server-list-gate.js.map +1 -1
  49. package/src/lib/server-list-import.js +5 -2
  50. package/src/lib/server-list-import.js.map +1 -1
@@ -0,0 +1,353 @@
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
+ * What the admin is told before a consent group change is applied.
18
+ *
19
+ * A change here decides who may email whom, so the review says it in the
20
+ * terms the admin will be held to rather than in the terms the code uses:
21
+ * what signup forms will say, who stops getting mail, who can still be
22
+ * mailed, and what happens to the CRM's records. Every number comes from the
23
+ * route's preview, which counted it without writing anything; a sentence
24
+ * whose number the preview did not supply is said without one rather than
25
+ * with a guess.
26
+ *
27
+ * Pure, so each wording can be asserted against a preview without rendering.
28
+ */ import { draftDisclosure } from "./consent-group-editing.js";
29
+ /*==========================================
30
+ * WORDS
31
+ *=========================================*/ const listFormat = new Intl.ListFormat('en-US', {
32
+ style: 'long',
33
+ type: 'conjunction'
34
+ });
35
+ /** `A`, `A and B`, `A, B, and C`. */ export function joinNames(names) {
36
+ return listFormat.format(names);
37
+ }
38
+ const count = (n)=>n.toLocaleString('en-US');
39
+ const people = (n)=>`${count(n)} ${n === 1 ? 'person' : 'people'}`;
40
+ const sum = (values)=>values.reduce((total, value)=>total + (Number.isFinite(value) ? value : 0), 0);
41
+ const dateFormat = new Intl.DateTimeFormat('en-US', {
42
+ month: 'long',
43
+ day: 'numeric',
44
+ year: 'numeric'
45
+ });
46
+ /** What an estimate means for the person about to wait on it. */ export const CONSENT_GROUP_ESTIMATE_TEXT = {
47
+ instant: 'This takes effect right away.',
48
+ 'under-a-minute': 'This should take less than a minute.',
49
+ minutes: 'This will take a few minutes.',
50
+ long: 'This will take a while — longer for organizations with many contacts.'
51
+ };
52
+ /** What a capture surface is called where a sentence names it. */ const CAPTURE_SURFACE_NAMES = {
53
+ form: 'signup forms',
54
+ newsletter: 'newsletter signups'
55
+ };
56
+ /*==========================================
57
+ * THE PARTICIPANTS' OWN LINES
58
+ *=========================================*/ /**
59
+ * The CRM's counted lines the review writes its own sentences around.
60
+ *
61
+ * The CRM reports what the change does to its records as lines with these
62
+ * ids; the review folds each count into the sentence it belongs to, so the
63
+ * admin reads one account of the change rather than two. A line with any
64
+ * other id is printed as the plugin wrote it.
65
+ */ const FOLDED_LINE_IDS = new Set([
66
+ 'crm.combine',
67
+ 'crm.copy',
68
+ 'crm.figures',
69
+ 'crm.refusals',
70
+ 'crm.grants',
71
+ 'crm.visibility'
72
+ ]);
73
+ function participantLines(preview) {
74
+ var _ref;
75
+ return ((_ref = preview == null ? void 0 : preview.participants) != null ? _ref : []).flatMap((participant)=>Array.isArray(participant == null ? void 0 : participant.lines) ? participant.lines : []);
76
+ }
77
+ /** A folded line's count, or `null` when it was not reported or not counted. */ function lineCount(preview, id) {
78
+ const line = participantLines(preview).find((entry)=>(entry == null ? void 0 : entry.id) === id);
79
+ return line && typeof line.count === 'number' ? line.count : null;
80
+ }
81
+ /** Whether the CRM spoke at all — its sentences are said only if it did. */ function crmReported(preview) {
82
+ return participantLines(preview).some((line)=>{
83
+ var _ref;
84
+ return String((_ref = line == null ? void 0 : line.id) != null ? _ref : '').startsWith('crm.');
85
+ });
86
+ }
87
+ /*==========================================
88
+ * THE REVIEW
89
+ *=========================================*/ function confirmLabel(change) {
90
+ if (change.kind === 'create') return 'Create group';
91
+ if (change.kind === 'dissolve') return 'Dissolve group';
92
+ const joining = change.added.length + change.movedIn.length;
93
+ const leaving = change.removed.length;
94
+ if (change.renamed && !joining && !leaving) return 'Rename group';
95
+ if (!change.renamed && joining && !leaving) {
96
+ return joining === 1 ? 'Add site' : 'Add sites';
97
+ }
98
+ if (!change.renamed && leaving && !joining) {
99
+ return leaving === 1 ? 'Remove site' : 'Remove sites';
100
+ }
101
+ return 'Apply changes';
102
+ }
103
+ function title(change) {
104
+ var _change_previousName;
105
+ if (change.kind === 'create') return `Create “${change.name}”`;
106
+ if (change.kind === 'dissolve') return `Dissolve “${change.name}”`;
107
+ return `Change “${(_change_previousName = change.previousName) != null ? _change_previousName : change.name}”`;
108
+ }
109
+ /** Builds every section of the review, in the order it is read. */ export function describeConsentGroupReview(input) {
110
+ var _ref, _lineCount, _ref1, _ref2, _ref3, _ref4;
111
+ const { change, preview, siteName, awaitsConfirmation } = input;
112
+ const names = (ids)=>joinNames(ids.map(siteName));
113
+ const group = change.name;
114
+ const joining = [
115
+ ...change.added,
116
+ ...change.movedIn.map((move)=>move.hostId)
117
+ ];
118
+ const joins = change.kind === 'create' || joining.length > 0;
119
+ const dissolving = change.kind === 'dissolve';
120
+ const leaving = dissolving ? change.previousHostIds : change.removed;
121
+ const crm = crmReported(preview);
122
+ const summary = [];
123
+ const forms = [];
124
+ const consent = [];
125
+ const optOuts = [];
126
+ const records = [];
127
+ const other = [];
128
+ const say = (into, id, text, tone = 'info')=>into.push({
129
+ id,
130
+ text,
131
+ tone
132
+ });
133
+ /*---- What changes ----*/ if (change.kind === 'create') {
134
+ say(summary, 'summary.create', `Create ${group} with ${names(change.hostIds)}.`);
135
+ }
136
+ if (change.tooFewSites) {
137
+ say(summary, 'summary.too-few', change.unselected.length ? `A consent group needs at least two sites. Removing ${names(change.unselected)} dissolves ${group}.` : `A consent group needs at least two sites, so this dissolves ${group}.`, 'warning');
138
+ } else if (dissolving) {
139
+ say(summary, 'summary.dissolve', `Dissolve ${group}.`);
140
+ }
141
+ if (change.renamed) {
142
+ say(summary, 'summary.rename', `Rename ${change.previousName} to ${group}.`);
143
+ }
144
+ if (change.kind === 'edit' && change.added.length) {
145
+ say(summary, 'summary.add', `Add ${names(change.added)} to ${group}.`);
146
+ }
147
+ for (const move of change.movedIn){
148
+ say(summary, `summary.move.${move.hostId}`, `Move ${siteName(move.hostId)} from ${move.fromName} to ${group}.`);
149
+ }
150
+ if (change.kind === 'edit' && change.removed.length) {
151
+ say(summary, 'summary.remove', `Remove ${names(change.removed)} from ${group}.`);
152
+ }
153
+ for (const donor of change.donors){
154
+ if (donor.absorbed) {
155
+ say(summary, `summary.absorb.${donor.groupId}`, `${donor.name} will be merged into ${group}, so ${donor.name} stops existing.`);
156
+ } else if (donor.dissolves) {
157
+ say(summary, `summary.donor-dissolve.${donor.groupId}`, `Moving ${names(donor.losing)} dissolves ${donor.name}, because a consent group needs at least two sites. ${names(donor.remaining)} will send on its own.`, 'warning');
158
+ }
159
+ }
160
+ /*---- Signup forms ----*/ if (!dissolving) {
161
+ var _ref5, _change_groupId, _ref6;
162
+ var _preview_disclosures_find, _preview_disclosures;
163
+ const disclosure = (_ref5 = preview == null ? void 0 : (_preview_disclosures = preview.disclosures) == null ? void 0 : (_preview_disclosures_find = _preview_disclosures.find((entry)=>entry && entry.after && (entry.groupId === change.groupId || Array.isArray(entry.hostIds) && entry.hostIds.length === change.hostIds.length && entry.hostIds.every((hostId)=>change.hostIds.includes(hostId))))) == null ? void 0 : _preview_disclosures_find.after) != null ? _ref5 : draftDisclosure(group, change.hostIds, (_change_groupId = change.groupId) != null ? _change_groupId : 'new');
164
+ if (disclosure && (joins || change.renamed || change.removed.length)) {
165
+ say(forms, 'forms.disclosure', change.kind === 'create' ? `Signup forms on these sites will say: “${disclosure}”` : `Signup forms on ${group}’s sites will say: “${disclosure}”`);
166
+ }
167
+ if (change.renamed) {
168
+ say(forms, 'forms.rename', `Signup forms and preference pages will show ${group} from now on. People who already signed up saw ${change.previousName}; their records keep it.`);
169
+ }
170
+ const surfaces = ((_ref6 = preview == null ? void 0 : preview.capturesDisclosing) != null ? _ref6 : []).map((surface)=>{
171
+ var _CAPTURE_SURFACE_NAMES_surface;
172
+ return (_CAPTURE_SURFACE_NAMES_surface = CAPTURE_SURFACE_NAMES[surface]) != null ? _CAPTURE_SURFACE_NAMES_surface : surface;
173
+ }).filter(Boolean);
174
+ if (joins && surfaces.length) {
175
+ say(forms, 'forms.surfaces', `Only ${joinNames(surfaces)} show this name for now. Anywhere else someone signs up, their consent is recorded for the one site they used.`);
176
+ }
177
+ }
178
+ if (leaving.length) {
179
+ say(forms, 'forms.leave', dissolving ? `${names(leaving)} will each send on their own again; their signup forms will stop naming ${group}.` : leaving.length === 1 ? `${names(leaving)} will send on its own again; its signup forms will stop naming ${group}.` : `${names(leaving)} will each send on their own again; their signup forms will stop naming ${group}.`);
180
+ }
181
+ /*---- Who may be emailed: grants ----*/ if (change.kind === 'create') {
182
+ say(consent, 'consent.forward', 'People who signed up before now agreed to hear from one site. They are not shared; only people who sign up after this change, on a form that shows this name, are.');
183
+ } else if (joins) {
184
+ say(consent, 'consent.forward', `People who signed up before now keep what they agreed to: nobody who signed up on ${names(joining)} is shared with ${group}’s other sites, or the other way around. Only people who sign up after this change, on a form that shows this name, are.`);
185
+ }
186
+ for (const donor of change.donors){
187
+ if (donor.absorbed) continue;
188
+ say(consent, `consent.kept.${donor.groupId}`, `${names(donor.losing)} can still email the people who signed up to ${donor.name} while ${donor.losing.length === 1 ? 'it was' : 'they were'} part of it.`);
189
+ }
190
+ if (leaving.length) {
191
+ const kept = lineCount(preview, 'crm.grants');
192
+ const who = kept != null ? `the ${people(kept)}` : 'the people';
193
+ say(consent, 'consent.kept', dissolving || leaving.length > 1 ? `Each site can still email ${who} who signed up to ${group} while it was part of it.` : `${names(leaving)} can still email ${who} who signed up to ${group} while it was part of it.`);
194
+ }
195
+ /*---- Who stops getting mail: refusals ----*/ if (joins) {
196
+ var _ref7;
197
+ const inherited = sum(((_ref7 = preview == null ? void 0 : preview.inherited) != null ? _ref7 : []).map((entry)=>{
198
+ var _ref;
199
+ return (_ref = entry == null ? void 0 : entry.refusals) != null ? _ref : 0;
200
+ }));
201
+ say(optOuts, 'opt-outs.inherit', `Anyone who unsubscribed from, opted out on, or declined email from any of these sites will stop getting marketing email from all of them${preview ? ` (${count(inherited)} ${inherited === 1 ? 'opt-out' : 'opt-outs'} on record)` : ''}.`);
202
+ if (awaitsConfirmation) {
203
+ say(optOuts, 'opt-outs.confirmation', 'A confirmation one site is waiting for will hold the other sites’ email on that topic.');
204
+ }
205
+ }
206
+ /*
207
+ * Separations: every pair of sites that stops reading each other keeps the
208
+ * other's refusals. The copies are counted once over the whole plan, so the
209
+ * count rides on the sentence when there is one, and on a total when there
210
+ * are several.
211
+ */ const separations = [];
212
+ if (dissolving) {
213
+ separations.push({
214
+ id: 'opt-outs.dissolve',
215
+ text: `Everyone who opted out on any of ${group}’s sites stays opted out on every one of them`
216
+ });
217
+ } else if (change.removed.length === 1) {
218
+ const site = siteName(change.removed[0]);
219
+ separations.push({
220
+ id: 'opt-outs.leave',
221
+ text: `Everyone who opted out on any of ${group}’s sites stays opted out on ${site}, and everyone who opted out on ${site} stays opted out on ${group}’s other sites`
222
+ });
223
+ } else if (change.removed.length > 1) {
224
+ separations.push({
225
+ id: 'opt-outs.leave',
226
+ text: `Everyone who opted out on any of ${group}’s sites stays opted out on each site that leaves, and everyone who opted out on a site that leaves stays opted out on the rest`
227
+ });
228
+ }
229
+ for (const donor of change.donors){
230
+ if (donor.absorbed) continue;
231
+ separations.push({
232
+ id: `opt-outs.donor.${donor.groupId}`,
233
+ text: `Everyone who opted out on any of ${donor.name}’s sites stays opted out on ${names(donor.losing)}, and everyone who opted out on ${names(donor.losing)} stays opted out on ${names(donor.remaining)}`
234
+ });
235
+ }
236
+ const copied = sum(((_ref = preview == null ? void 0 : preview.carries) != null ? _ref : []).map((carry)=>{
237
+ var _ref, _ref1;
238
+ return ((_ref = carry == null ? void 0 : carry.siteSuppressions) != null ? _ref : 0) + ((_ref1 = carry == null ? void 0 : carry.topicOptOuts) != null ? _ref1 : 0);
239
+ })) + ((_lineCount = lineCount(preview, 'crm.refusals')) != null ? _lineCount : 0);
240
+ const paces = sum(((_ref1 = preview == null ? void 0 : preview.carries) != null ? _ref1 : []).map((carry)=>{
241
+ var _ref;
242
+ return (_ref = carry == null ? void 0 : carry.paces) != null ? _ref : 0;
243
+ }));
244
+ const copiedNote = preview ? `${count(copied)} ${copied === 1 ? 'opt-out' : 'opt-outs'} copied${paces ? `, and ${count(paces)} email frequency ${paces === 1 ? 'choice' : 'choices'} kept` : ''}` : '';
245
+ if (separations.length === 1) {
246
+ say(optOuts, separations[0].id, `${separations[0].text}${copiedNote ? ` (${copiedNote})` : ''}.`);
247
+ } else if (separations.length > 1) {
248
+ for (const separation of separations){
249
+ say(optOuts, separation.id, `${separation.text}.`);
250
+ }
251
+ if (copiedNote) {
252
+ say(optOuts, 'opt-outs.total', `In all: ${copiedNote}.`);
253
+ }
254
+ }
255
+ const holds = sum(((_ref2 = preview == null ? void 0 : preview.pendingHolds) != null ? _ref2 : []).map((hold)=>{
256
+ var _ref;
257
+ return (_ref = hold == null ? void 0 : hold.count) != null ? _ref : 0;
258
+ }));
259
+ if (holds > 0 && (leaving.length || change.donors.length)) {
260
+ say(optOuts, 'opt-outs.pending', leaving.length === 1 && !dissolving && !change.donors.length ? `${people(holds)} waiting to confirm on ${names(leaving)} will no longer hold ${group}’s email, and vice versa.` : `${people(holds)} waiting to confirm on one of these sites will no longer hold the email of the sites it separates from.`);
261
+ }
262
+ /*---- CRM records ----*/ if (crm && joins) {
263
+ const combined = lineCount(preview, 'crm.combine');
264
+ say(records, 'records.combine', `The CRM records these sites keep about the same person will be combined into one record, visible to everyone who works on any of these sites. Where two records disagree about one value, such as the owner or stage, ${change.kind === 'create' ? 'the record from the site that met the person first' : `${group}’s record`} is kept.${combined != null ? ` This combines records for ${people(combined)}.` : ''}`);
265
+ }
266
+ if (crm && leaving.length) {
267
+ const copies = lineCount(preview, 'crm.copy');
268
+ const met = copies != null ? `the ${people(copies)}` : 'the people';
269
+ say(records, 'records.copy', dissolving || leaving.length > 1 ? `Each site keeps a copy of the CRM record for ${met} it met itself.` : `${names(leaving)} keeps a copy of the CRM record for ${met} it met itself. ${group} keeps its records, including order totals for people who also bought elsewhere.`);
270
+ const seen = lineCount(preview, 'crm.visibility');
271
+ if (seen != null && seen > 0) {
272
+ say(records, 'records.visibility', dissolving || leaving.length > 1 ? `Each site will still see the ${people(seen)} it met only through ${group}’s other sites, without their CRM details.` : `${names(leaving)} will still see ${people(seen)} it met only through ${group}’s other sites, without their CRM details.`);
273
+ }
274
+ }
275
+ if (crm && dissolving) {
276
+ const figures = lineCount(preview, 'crm.figures');
277
+ if (figures == null || figures > 0) {
278
+ say(records, 'records.figures', figures != null ? `Order totals for ${people(figures)} who bought on more than one of these sites can’t be split and will be removed.` : 'Order totals for people who bought on more than one of these sites can’t be split and will be removed.', 'warning');
279
+ }
280
+ }
281
+ if (joins) {
282
+ var _ref8;
283
+ const partial = Number((_ref8 = preview == null ? void 0 : preview.partialAccessMembers) != null ? _ref8 : 0);
284
+ if (partial > 0) {
285
+ say(records, 'records.partial-access', `${count(partial)} team ${partial === 1 ? 'member' : 'members'} can open only some of these sites. They’ll keep seeing only those sites’ records.`);
286
+ }
287
+ const forward = preview == null ? void 0 : preview.forwardPolicyWarning;
288
+ if (forward && typeof forward === 'object') {
289
+ const hostIds = Array.isArray(forward.hostIds) ? forward.hostIds : joining;
290
+ const before = typeof forward.enforceFromMs === 'number' ? ` before ${dateFormat.format(new Date(forward.enforceFromMs))}` : '';
291
+ say(records, 'records.forward', `${names(hostIds.length ? hostIds : change.hostIds)} will be able to email people these sites captured${before} without a recorded opt-in.`, 'warning');
292
+ }
293
+ }
294
+ /*---- Anything else the plugins reported, as they wrote it ----*/ for (const line of participantLines(preview)){
295
+ if (!line || FOLDED_LINE_IDS.has(line.id) || !line.text) continue;
296
+ say(other, `line.${line.id}`, line.text, line.severity === 'warning' ? 'warning' : 'info');
297
+ }
298
+ if (((_ref3 = preview == null ? void 0 : preview.participants) != null ? _ref3 : []).some((participant)=>participant && participant.lines === null)) {
299
+ say(other, 'line.unanswered', 'Part of this change couldn’t be counted ahead of time. It still runs, and nobody who opted out starts getting email.');
300
+ }
301
+ const discarded = ((_ref4 = preview == null ? void 0 : preview.discarded) != null ? _ref4 : []).filter((id)=>typeof id === 'string' && id !== '');
302
+ if (discarded.length) {
303
+ say(other, 'discarded', `${discarded.length === 1 ? 'One saved consent group' : `${count(discarded.length)} saved consent groups`} can’t be used — no name, too few or too many sites, or a site another group also claims — so nothing honors ${discarded.length === 1 ? 'it' : 'them'} today. This change removes ${discarded.length === 1 ? 'it' : 'them'}: ${joinNames(discarded)}.`);
304
+ }
305
+ /*---- How long ----*/ const timing = [];
306
+ if ((preview == null ? void 0 : preview.estimate) && CONSENT_GROUP_ESTIMATE_TEXT[preview.estimate]) {
307
+ say(timing, 'timing.estimate', CONSENT_GROUP_ESTIMATE_TEXT[preview.estimate]);
308
+ }
309
+ say(timing, 'timing.leave', 'You can leave this page; it finishes on its own.');
310
+ return {
311
+ title: title(change),
312
+ confirmLabel: confirmLabel(change),
313
+ sections: [
314
+ {
315
+ id: 'summary',
316
+ heading: null,
317
+ items: summary
318
+ },
319
+ {
320
+ id: 'forms',
321
+ heading: 'Signup forms',
322
+ items: forms
323
+ },
324
+ {
325
+ id: 'consent',
326
+ heading: 'Who can be emailed',
327
+ items: consent
328
+ },
329
+ {
330
+ id: 'opt-outs',
331
+ heading: 'Opt-outs',
332
+ items: optOuts
333
+ },
334
+ {
335
+ id: 'records',
336
+ heading: 'CRM records and access',
337
+ items: records
338
+ },
339
+ {
340
+ id: 'other',
341
+ heading: 'Also',
342
+ items: other
343
+ },
344
+ {
345
+ id: 'timing',
346
+ heading: null,
347
+ items: timing
348
+ }
349
+ ].filter((section)=>section.items.length > 0)
350
+ };
351
+ }
352
+
353
+ //# sourceMappingURL=consent-group-review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/email/src/lib/components/consent-group-review.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 * What the admin is told before a consent group change is applied.\n *\n * A change here decides who may email whom, so the review says it in the\n * terms the admin will be held to rather than in the terms the code uses:\n * what signup forms will say, who stops getting mail, who can still be\n * mailed, and what happens to the CRM's records. Every number comes from the\n * route's preview, which counted it without writing anything; a sentence\n * whose number the preview did not supply is said without one rather than\n * with a guess.\n *\n * Pure, so each wording can be asserted against a preview without rendering.\n */\n\nimport type {\n ConsentGroupChangeEstimate,\n ConsentGroupChangePreview,\n ConsentGroupParticipantLine,\n} from './consent-groups-api'\nimport {\n draftDisclosure,\n type ConsentGroupDraftChange,\n} from './consent-group-editing'\n\n/** One sentence of the review. `warning` is the tone of an irreversible loss. */\nexport interface ConsentGroupReviewItem {\n id: string\n text: string\n tone: 'info' | 'warning'\n}\n\nexport interface ConsentGroupReviewSection {\n id: string\n heading: string | null\n items: ConsentGroupReviewItem[]\n}\n\nexport interface ConsentGroupReview {\n title: string\n /** The primary button, named for what it does. */\n confirmLabel: string\n sections: ConsentGroupReviewSection[]\n}\n\nexport interface ConsentGroupReviewInput {\n change: ConsentGroupDraftChange\n /** `null` while the preview is still being counted. */\n preview: ConsentGroupChangePreview | null\n siteName: (hostId: string) => string\n /** The org's switch: a confirmation one site waits on holds its siblings. */\n awaitsConfirmation: boolean\n}\n\n/*==========================================\n * WORDS\n *=========================================*/\n\nconst listFormat = new Intl.ListFormat('en-US', {\n style: 'long',\n type: 'conjunction',\n})\n\n/** `A`, `A and B`, `A, B, and C`. */\nexport function joinNames(names: readonly string[]): string {\n return listFormat.format(names)\n}\n\nconst count = (n: number) => n.toLocaleString('en-US')\nconst people = (n: number) => `${count(n)} ${n === 1 ? 'person' : 'people'}`\n\nconst sum = (values: readonly number[]) =>\n values.reduce((total, value) => total + (Number.isFinite(value) ? value : 0), 0)\n\nconst dateFormat = new Intl.DateTimeFormat('en-US', {\n month: 'long',\n day: 'numeric',\n year: 'numeric',\n})\n\n/** What an estimate means for the person about to wait on it. */\nexport const CONSENT_GROUP_ESTIMATE_TEXT: Readonly<\n Record<ConsentGroupChangeEstimate, string>\n> = {\n instant: 'This takes effect right away.',\n 'under-a-minute': 'This should take less than a minute.',\n minutes: 'This will take a few minutes.',\n long: 'This will take a while — longer for organizations with many contacts.',\n}\n\n/** What a capture surface is called where a sentence names it. */\nconst CAPTURE_SURFACE_NAMES: Readonly<Record<string, string>> = {\n form: 'signup forms',\n newsletter: 'newsletter signups',\n}\n\n/*==========================================\n * THE PARTICIPANTS' OWN LINES\n *=========================================*/\n\n/**\n * The CRM's counted lines the review writes its own sentences around.\n *\n * The CRM reports what the change does to its records as lines with these\n * ids; the review folds each count into the sentence it belongs to, so the\n * admin reads one account of the change rather than two. A line with any\n * other id is printed as the plugin wrote it.\n */\nconst FOLDED_LINE_IDS = new Set([\n 'crm.combine',\n 'crm.copy',\n 'crm.figures',\n 'crm.refusals',\n 'crm.grants',\n 'crm.visibility',\n])\n\nfunction participantLines(\n preview: ConsentGroupChangePreview | null,\n): ConsentGroupParticipantLine[] {\n return (preview?.participants ?? []).flatMap((participant) =>\n Array.isArray(participant?.lines) ? participant.lines : [],\n )\n}\n\n/** A folded line's count, or `null` when it was not reported or not counted. */\nfunction lineCount(\n preview: ConsentGroupChangePreview | null,\n id: string,\n): number | null {\n const line = participantLines(preview).find((entry) => entry?.id === id)\n return line && typeof line.count === 'number' ? line.count : null\n}\n\n/** Whether the CRM spoke at all — its sentences are said only if it did. */\nfunction crmReported(preview: ConsentGroupChangePreview | null): boolean {\n return participantLines(preview).some((line) =>\n String(line?.id ?? '').startsWith('crm.'),\n )\n}\n\n/*==========================================\n * THE REVIEW\n *=========================================*/\n\nfunction confirmLabel(change: ConsentGroupDraftChange): string {\n if (change.kind === 'create') return 'Create group'\n if (change.kind === 'dissolve') return 'Dissolve group'\n const joining = change.added.length + change.movedIn.length\n const leaving = change.removed.length\n if (change.renamed && !joining && !leaving) return 'Rename group'\n if (!change.renamed && joining && !leaving) {\n return joining === 1 ? 'Add site' : 'Add sites'\n }\n if (!change.renamed && leaving && !joining) {\n return leaving === 1 ? 'Remove site' : 'Remove sites'\n }\n return 'Apply changes'\n}\n\nfunction title(change: ConsentGroupDraftChange): string {\n if (change.kind === 'create') return `Create “${change.name}”`\n if (change.kind === 'dissolve') return `Dissolve “${change.name}”`\n return `Change “${change.previousName ?? change.name}”`\n}\n\n/** Builds every section of the review, in the order it is read. */\nexport function describeConsentGroupReview(\n input: ConsentGroupReviewInput,\n): ConsentGroupReview {\n const { change, preview, siteName, awaitsConfirmation } = input\n const names = (ids: readonly string[]) => joinNames(ids.map(siteName))\n const group = change.name\n const joining = [...change.added, ...change.movedIn.map((move) => move.hostId)]\n const joins = change.kind === 'create' || joining.length > 0\n const dissolving = change.kind === 'dissolve'\n const leaving = dissolving ? change.previousHostIds : change.removed\n const crm = crmReported(preview)\n\n const summary: ConsentGroupReviewItem[] = []\n const forms: ConsentGroupReviewItem[] = []\n const consent: ConsentGroupReviewItem[] = []\n const optOuts: ConsentGroupReviewItem[] = []\n const records: ConsentGroupReviewItem[] = []\n const other: ConsentGroupReviewItem[] = []\n const say = (\n into: ConsentGroupReviewItem[],\n id: string,\n text: string,\n tone: ConsentGroupReviewItem['tone'] = 'info',\n ) => into.push({ id, text, tone })\n\n /*---- What changes ----*/\n if (change.kind === 'create') {\n say(summary, 'summary.create', `Create ${group} with ${names(change.hostIds)}.`)\n }\n if (change.tooFewSites) {\n say(\n summary,\n 'summary.too-few',\n change.unselected.length\n ? `A consent group needs at least two sites. Removing ${names(change.unselected)} dissolves ${group}.`\n : `A consent group needs at least two sites, so this dissolves ${group}.`,\n 'warning',\n )\n } else if (dissolving) {\n say(summary, 'summary.dissolve', `Dissolve ${group}.`)\n }\n if (change.renamed) {\n say(\n summary,\n 'summary.rename',\n `Rename ${change.previousName} to ${group}.`,\n )\n }\n if (change.kind === 'edit' && change.added.length) {\n say(summary, 'summary.add', `Add ${names(change.added)} to ${group}.`)\n }\n for (const move of change.movedIn) {\n say(\n summary,\n `summary.move.${move.hostId}`,\n `Move ${siteName(move.hostId)} from ${move.fromName} to ${group}.`,\n )\n }\n if (change.kind === 'edit' && change.removed.length) {\n say(summary, 'summary.remove', `Remove ${names(change.removed)} from ${group}.`)\n }\n for (const donor of change.donors) {\n if (donor.absorbed) {\n say(\n summary,\n `summary.absorb.${donor.groupId}`,\n `${donor.name} will be merged into ${group}, so ${donor.name} stops existing.`,\n )\n } else if (donor.dissolves) {\n say(\n summary,\n `summary.donor-dissolve.${donor.groupId}`,\n `Moving ${names(donor.losing)} dissolves ${donor.name}, because a consent group needs at least two sites. ${names(donor.remaining)} will send on its own.`,\n 'warning',\n )\n }\n }\n\n /*---- Signup forms ----*/\n if (!dissolving) {\n const disclosure =\n preview?.disclosures?.find(\n (entry) =>\n entry &&\n entry.after &&\n (entry.groupId === change.groupId ||\n (Array.isArray(entry.hostIds) &&\n entry.hostIds.length === change.hostIds.length &&\n entry.hostIds.every((hostId) => change.hostIds.includes(hostId)))),\n )?.after ?? draftDisclosure(group, change.hostIds, change.groupId ?? 'new')\n if (disclosure && (joins || change.renamed || change.removed.length)) {\n say(\n forms,\n 'forms.disclosure',\n change.kind === 'create'\n ? `Signup forms on these sites will say: “${disclosure}”`\n : `Signup forms on ${group}’s sites will say: “${disclosure}”`,\n )\n }\n if (change.renamed) {\n say(\n forms,\n 'forms.rename',\n `Signup forms and preference pages will show ${group} from now on. People who already signed up saw ${change.previousName}; their records keep it.`,\n )\n }\n const surfaces = (preview?.capturesDisclosing ?? [])\n .map((surface) => CAPTURE_SURFACE_NAMES[surface] ?? surface)\n .filter(Boolean)\n if (joins && surfaces.length) {\n say(\n forms,\n 'forms.surfaces',\n `Only ${joinNames(surfaces)} show this name for now. Anywhere else someone signs up, their consent is recorded for the one site they used.`,\n )\n }\n }\n if (leaving.length) {\n say(\n forms,\n 'forms.leave',\n dissolving\n ? `${names(leaving)} will each send on their own again; their signup forms will stop naming ${group}.`\n : leaving.length === 1\n ? `${names(leaving)} will send on its own again; its signup forms will stop naming ${group}.`\n : `${names(leaving)} will each send on their own again; their signup forms will stop naming ${group}.`,\n )\n }\n\n /*---- Who may be emailed: grants ----*/\n if (change.kind === 'create') {\n say(\n consent,\n 'consent.forward',\n 'People who signed up before now agreed to hear from one site. They are not shared; only people who sign up after this change, on a form that shows this name, are.',\n )\n } else if (joins) {\n say(\n consent,\n 'consent.forward',\n `People who signed up before now keep what they agreed to: nobody who signed up on ${names(joining)} is shared with ${group}’s other sites, or the other way around. Only people who sign up after this change, on a form that shows this name, are.`,\n )\n }\n for (const donor of change.donors) {\n if (donor.absorbed) continue\n say(\n consent,\n `consent.kept.${donor.groupId}`,\n `${names(donor.losing)} can still email the people who signed up to ${donor.name} while ${\n donor.losing.length === 1 ? 'it was' : 'they were'\n } part of it.`,\n )\n }\n if (leaving.length) {\n const kept = lineCount(preview, 'crm.grants')\n const who = kept != null ? `the ${people(kept)}` : 'the people'\n say(\n consent,\n 'consent.kept',\n dissolving || leaving.length > 1\n ? `Each site can still email ${who} who signed up to ${group} while it was part of it.`\n : `${names(leaving)} can still email ${who} who signed up to ${group} while it was part of it.`,\n )\n }\n\n /*---- Who stops getting mail: refusals ----*/\n if (joins) {\n const inherited = sum((preview?.inherited ?? []).map((entry) => entry?.refusals ?? 0))\n say(\n optOuts,\n 'opt-outs.inherit',\n `Anyone who unsubscribed from, opted out on, or declined email from any of these sites will stop getting marketing email from all of them${preview ? ` (${count(inherited)} ${inherited === 1 ? 'opt-out' : 'opt-outs'} on record)` : ''}.`,\n )\n if (awaitsConfirmation) {\n say(\n optOuts,\n 'opt-outs.confirmation',\n 'A confirmation one site is waiting for will hold the other sites’ email on that topic.',\n )\n }\n }\n /*\n * Separations: every pair of sites that stops reading each other keeps the\n * other's refusals. The copies are counted once over the whole plan, so the\n * count rides on the sentence when there is one, and on a total when there\n * are several.\n */\n const separations: Array<{ id: string; text: string }> = []\n if (dissolving) {\n separations.push({\n id: 'opt-outs.dissolve',\n text: `Everyone who opted out on any of ${group}’s sites stays opted out on every one of them`,\n })\n } else if (change.removed.length === 1) {\n const site = siteName(change.removed[0])\n separations.push({\n id: 'opt-outs.leave',\n text: `Everyone who opted out on any of ${group}’s sites stays opted out on ${site}, and everyone who opted out on ${site} stays opted out on ${group}’s other sites`,\n })\n } else if (change.removed.length > 1) {\n separations.push({\n id: 'opt-outs.leave',\n text: `Everyone who opted out on any of ${group}’s sites stays opted out on each site that leaves, and everyone who opted out on a site that leaves stays opted out on the rest`,\n })\n }\n for (const donor of change.donors) {\n if (donor.absorbed) continue\n separations.push({\n id: `opt-outs.donor.${donor.groupId}`,\n text: `Everyone who opted out on any of ${donor.name}’s sites stays opted out on ${names(donor.losing)}, and everyone who opted out on ${names(donor.losing)} stays opted out on ${names(donor.remaining)}`,\n })\n }\n const copied =\n sum(\n (preview?.carries ?? []).map(\n (carry) => (carry?.siteSuppressions ?? 0) + (carry?.topicOptOuts ?? 0),\n ),\n ) + (lineCount(preview, 'crm.refusals') ?? 0)\n const paces = sum((preview?.carries ?? []).map((carry) => carry?.paces ?? 0))\n const copiedNote = preview\n ? `${count(copied)} ${copied === 1 ? 'opt-out' : 'opt-outs'} copied${\n paces ? `, and ${count(paces)} email frequency ${paces === 1 ? 'choice' : 'choices'} kept` : ''\n }`\n : ''\n if (separations.length === 1) {\n say(\n optOuts,\n separations[0].id,\n `${separations[0].text}${copiedNote ? ` (${copiedNote})` : ''}.`,\n )\n } else if (separations.length > 1) {\n for (const separation of separations) {\n say(optOuts, separation.id, `${separation.text}.`)\n }\n if (copiedNote) {\n say(optOuts, 'opt-outs.total', `In all: ${copiedNote}.`)\n }\n }\n const holds = sum((preview?.pendingHolds ?? []).map((hold) => hold?.count ?? 0))\n if (holds > 0 && (leaving.length || change.donors.length)) {\n say(\n optOuts,\n 'opt-outs.pending',\n leaving.length === 1 && !dissolving && !change.donors.length\n ? `${people(holds)} waiting to confirm on ${names(leaving)} will no longer hold ${group}’s email, and vice versa.`\n : `${people(holds)} waiting to confirm on one of these sites will no longer hold the email of the sites it separates from.`,\n )\n }\n\n /*---- CRM records ----*/\n if (crm && joins) {\n const combined = lineCount(preview, 'crm.combine')\n say(\n records,\n 'records.combine',\n `The CRM records these sites keep about the same person will be combined into one record, visible to everyone who works on any of these sites. Where two records disagree about one value, such as the owner or stage, ${\n change.kind === 'create'\n ? 'the record from the site that met the person first'\n : `${group}’s record`\n } is kept.${combined != null ? ` This combines records for ${people(combined)}.` : ''}`,\n )\n }\n if (crm && leaving.length) {\n const copies = lineCount(preview, 'crm.copy')\n const met = copies != null ? `the ${people(copies)}` : 'the people'\n say(\n records,\n 'records.copy',\n dissolving || leaving.length > 1\n ? `Each site keeps a copy of the CRM record for ${met} it met itself.`\n : `${names(leaving)} keeps a copy of the CRM record for ${met} it met itself. ${group} keeps its records, including order totals for people who also bought elsewhere.`,\n )\n const seen = lineCount(preview, 'crm.visibility')\n if (seen != null && seen > 0) {\n say(\n records,\n 'records.visibility',\n dissolving || leaving.length > 1\n ? `Each site will still see the ${people(seen)} it met only through ${group}’s other sites, without their CRM details.`\n : `${names(leaving)} will still see ${people(seen)} it met only through ${group}’s other sites, without their CRM details.`,\n )\n }\n }\n if (crm && dissolving) {\n const figures = lineCount(preview, 'crm.figures')\n if (figures == null || figures > 0) {\n say(\n records,\n 'records.figures',\n figures != null\n ? `Order totals for ${people(figures)} who bought on more than one of these sites can’t be split and will be removed.`\n : 'Order totals for people who bought on more than one of these sites can’t be split and will be removed.',\n 'warning',\n )\n }\n }\n if (joins) {\n const partial = Number(preview?.partialAccessMembers ?? 0)\n if (partial > 0) {\n say(\n records,\n 'records.partial-access',\n `${count(partial)} team ${partial === 1 ? 'member' : 'members'} can open only some of these sites. They’ll keep seeing only those sites’ records.`,\n )\n }\n const forward = preview?.forwardPolicyWarning\n if (forward && typeof forward === 'object') {\n const hostIds = Array.isArray(forward.hostIds) ? forward.hostIds : joining\n const before =\n typeof forward.enforceFromMs === 'number'\n ? ` before ${dateFormat.format(new Date(forward.enforceFromMs))}`\n : ''\n say(\n records,\n 'records.forward',\n `${names(hostIds.length ? hostIds : change.hostIds)} will be able to email people these sites captured${before} without a recorded opt-in.`,\n 'warning',\n )\n }\n }\n\n /*---- Anything else the plugins reported, as they wrote it ----*/\n for (const line of participantLines(preview)) {\n if (!line || FOLDED_LINE_IDS.has(line.id) || !line.text) continue\n say(other, `line.${line.id}`, line.text, line.severity === 'warning' ? 'warning' : 'info')\n }\n if ((preview?.participants ?? []).some((participant) => participant && participant.lines === null)) {\n say(\n other,\n 'line.unanswered',\n 'Part of this change couldn’t be counted ahead of time. It still runs, and nobody who opted out starts getting email.',\n )\n }\n const discarded = (preview?.discarded ?? []).filter(\n (id) => typeof id === 'string' && id !== '',\n )\n if (discarded.length) {\n say(\n other,\n 'discarded',\n `${discarded.length === 1 ? 'One saved consent group' : `${count(discarded.length)} saved consent groups`} can’t be used — no name, too few or too many sites, or a site another group also claims — so nothing honors ${discarded.length === 1 ? 'it' : 'them'} today. This change removes ${discarded.length === 1 ? 'it' : 'them'}: ${joinNames(discarded)}.`,\n )\n }\n\n /*---- How long ----*/\n const timing: ConsentGroupReviewItem[] = []\n if (preview?.estimate && CONSENT_GROUP_ESTIMATE_TEXT[preview.estimate]) {\n say(timing, 'timing.estimate', CONSENT_GROUP_ESTIMATE_TEXT[preview.estimate])\n }\n say(timing, 'timing.leave', 'You can leave this page; it finishes on its own.')\n\n return {\n title: title(change),\n confirmLabel: confirmLabel(change),\n sections: [\n { id: 'summary', heading: null, items: summary },\n { id: 'forms', heading: 'Signup forms', items: forms },\n { id: 'consent', heading: 'Who can be emailed', items: consent },\n { id: 'opt-outs', heading: 'Opt-outs', items: optOuts },\n { id: 'records', heading: 'CRM records and access', items: records },\n { id: 'other', heading: 'Also', items: other },\n { id: 'timing', heading: null, items: timing },\n ].filter((section) => section.items.length > 0),\n }\n}\n"],"names":["draftDisclosure","listFormat","Intl","ListFormat","style","type","joinNames","names","format","count","n","toLocaleString","people","sum","values","reduce","total","value","Number","isFinite","dateFormat","DateTimeFormat","month","day","year","CONSENT_GROUP_ESTIMATE_TEXT","instant","minutes","long","CAPTURE_SURFACE_NAMES","form","newsletter","FOLDED_LINE_IDS","Set","participantLines","preview","participants","flatMap","participant","Array","isArray","lines","lineCount","id","line","find","entry","crmReported","some","String","startsWith","confirmLabel","change","kind","joining","added","length","movedIn","leaving","removed","renamed","title","name","previousName","describeConsentGroupReview","input","siteName","awaitsConfirmation","ids","map","group","move","hostId","joins","dissolving","previousHostIds","crm","summary","forms","consent","optOuts","records","other","say","into","text","tone","push","hostIds","tooFewSites","unselected","fromName","donor","donors","absorbed","groupId","dissolves","losing","remaining","disclosure","disclosures","after","every","includes","surfaces","capturesDisclosing","surface","filter","Boolean","kept","who","inherited","refusals","separations","site","copied","carries","carry","siteSuppressions","topicOptOuts","paces","copiedNote","separation","holds","pendingHolds","hold","combined","copies","met","seen","figures","partial","partialAccessMembers","forward","forwardPolicyWarning","before","enforceFromMs","Date","has","severity","discarded","timing","estimate","sections","heading","items","section"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;CAYC,GAOD,SACEA,eAAe,QAEV,6BAAyB;AA+BhC;;2CAE2C,GAE3C,MAAMC,aAAa,IAAIC,KAAKC,UAAU,CAAC,SAAS;IAC9CC,OAAO;IACPC,MAAM;AACR;AAEA,mCAAmC,GACnC,OAAO,SAASC,UAAUC,KAAwB;IAChD,OAAON,WAAWO,MAAM,CAACD;AAC3B;AAEA,MAAME,QAAQ,CAACC,IAAcA,EAAEC,cAAc,CAAC;AAC9C,MAAMC,SAAS,CAACF,IAAc,GAAGD,MAAMC,GAAG,CAAC,EAAEA,MAAM,IAAI,WAAW,UAAU;AAE5E,MAAMG,MAAM,CAACC,SACXA,OAAOC,MAAM,CAAC,CAACC,OAAOC,QAAUD,QAASE,CAAAA,OAAOC,QAAQ,CAACF,SAASA,QAAQ,CAAA,GAAI;AAEhF,MAAMG,aAAa,IAAIlB,KAAKmB,cAAc,CAAC,SAAS;IAClDC,OAAO;IACPC,KAAK;IACLC,MAAM;AACR;AAEA,+DAA+D,GAC/D,OAAO,MAAMC,8BAET;IACFC,SAAS;IACT,kBAAkB;IAClBC,SAAS;IACTC,MAAM;AACR,EAAC;AAED,gEAAgE,GAChE,MAAMC,wBAA0D;IAC9DC,MAAM;IACNC,YAAY;AACd;AAEA;;2CAE2C,GAE3C;;;;;;;CAOC,GACD,MAAMC,kBAAkB,IAAIC,IAAI;IAC9B;IACA;IACA;IACA;IACA;IACA;CACD;AAED,SAASC,iBACPC,OAAyC;;IAEzC,OAAO,SAACA,2BAAAA,QAASC,YAAY,mBAAI,EAAE,EAAEC,OAAO,CAAC,CAACC,cAC5CC,MAAMC,OAAO,CAACF,+BAAAA,YAAaG,KAAK,IAAIH,YAAYG,KAAK,GAAG,EAAE;AAE9D;AAEA,8EAA8E,GAC9E,SAASC,UACPP,OAAyC,EACzCQ,EAAU;IAEV,MAAMC,OAAOV,iBAAiBC,SAASU,IAAI,CAAC,CAACC,QAAUA,CAAAA,yBAAAA,MAAOH,EAAE,MAAKA;IACrE,OAAOC,QAAQ,OAAOA,KAAKnC,KAAK,KAAK,WAAWmC,KAAKnC,KAAK,GAAG;AAC/D;AAEA,0EAA0E,GAC1E,SAASsC,YAAYZ,OAAyC;IAC5D,OAAOD,iBAAiBC,SAASa,IAAI,CAAC,CAACJ;;eACrCK,eAAOL,wBAAAA,KAAMD,EAAE,mBAAI,IAAIO,UAAU,CAAC;;AAEtC;AAEA;;2CAE2C,GAE3C,SAASC,aAAaC,MAA+B;IACnD,IAAIA,OAAOC,IAAI,KAAK,UAAU,OAAO;IACrC,IAAID,OAAOC,IAAI,KAAK,YAAY,OAAO;IACvC,MAAMC,UAAUF,OAAOG,KAAK,CAACC,MAAM,GAAGJ,OAAOK,OAAO,CAACD,MAAM;IAC3D,MAAME,UAAUN,OAAOO,OAAO,CAACH,MAAM;IACrC,IAAIJ,OAAOQ,OAAO,IAAI,CAACN,WAAW,CAACI,SAAS,OAAO;IACnD,IAAI,CAACN,OAAOQ,OAAO,IAAIN,WAAW,CAACI,SAAS;QAC1C,OAAOJ,YAAY,IAAI,aAAa;IACtC;IACA,IAAI,CAACF,OAAOQ,OAAO,IAAIF,WAAW,CAACJ,SAAS;QAC1C,OAAOI,YAAY,IAAI,gBAAgB;IACzC;IACA,OAAO;AACT;AAEA,SAASG,MAAMT,MAA+B;QAG1BA;IAFlB,IAAIA,OAAOC,IAAI,KAAK,UAAU,OAAO,CAAC,QAAQ,EAAED,OAAOU,IAAI,CAAC,CAAC,CAAC;IAC9D,IAAIV,OAAOC,IAAI,KAAK,YAAY,OAAO,CAAC,UAAU,EAAED,OAAOU,IAAI,CAAC,CAAC,CAAC;IAClE,OAAO,CAAC,QAAQ,GAAEV,uBAAAA,OAAOW,YAAY,YAAnBX,uBAAuBA,OAAOU,IAAI,CAAC,CAAC,CAAC;AACzD;AAEA,iEAAiE,GACjE,OAAO,SAASE,2BACdC,KAA8B;cAwNvBvB;IAtNP,MAAM,EAAEU,MAAM,EAAEjB,OAAO,EAAE+B,QAAQ,EAAEC,kBAAkB,EAAE,GAAGF;IAC1D,MAAM1D,QAAQ,CAAC6D,MAA2B9D,UAAU8D,IAAIC,GAAG,CAACH;IAC5D,MAAMI,QAAQlB,OAAOU,IAAI;IACzB,MAAMR,UAAU;WAAIF,OAAOG,KAAK;WAAKH,OAAOK,OAAO,CAACY,GAAG,CAAC,CAACE,OAASA,KAAKC,MAAM;KAAE;IAC/E,MAAMC,QAAQrB,OAAOC,IAAI,KAAK,YAAYC,QAAQE,MAAM,GAAG;IAC3D,MAAMkB,aAAatB,OAAOC,IAAI,KAAK;IACnC,MAAMK,UAAUgB,aAAatB,OAAOuB,eAAe,GAAGvB,OAAOO,OAAO;IACpE,MAAMiB,MAAM7B,YAAYZ;IAExB,MAAM0C,UAAoC,EAAE;IAC5C,MAAMC,QAAkC,EAAE;IAC1C,MAAMC,UAAoC,EAAE;IAC5C,MAAMC,UAAoC,EAAE;IAC5C,MAAMC,UAAoC,EAAE;IAC5C,MAAMC,QAAkC,EAAE;IAC1C,MAAMC,MAAM,CACVC,MACAzC,IACA0C,MACAC,OAAuC,MAAM,GAC1CF,KAAKG,IAAI,CAAC;YAAE5C;YAAI0C;YAAMC;QAAK;IAEhC,wBAAwB,GACxB,IAAIlC,OAAOC,IAAI,KAAK,UAAU;QAC5B8B,IAAIN,SAAS,kBAAkB,CAAC,OAAO,EAAEP,MAAM,MAAM,EAAE/D,MAAM6C,OAAOoC,OAAO,EAAE,CAAC,CAAC;IACjF;IACA,IAAIpC,OAAOqC,WAAW,EAAE;QACtBN,IACEN,SACA,mBACAzB,OAAOsC,UAAU,CAAClC,MAAM,GACpB,CAAC,mDAAmD,EAAEjD,MAAM6C,OAAOsC,UAAU,EAAE,WAAW,EAAEpB,MAAM,CAAC,CAAC,GACpG,CAAC,4DAA4D,EAAEA,MAAM,CAAC,CAAC,EAC3E;IAEJ,OAAO,IAAII,YAAY;QACrBS,IAAIN,SAAS,oBAAoB,CAAC,SAAS,EAAEP,MAAM,CAAC,CAAC;IACvD;IACA,IAAIlB,OAAOQ,OAAO,EAAE;QAClBuB,IACEN,SACA,kBACA,CAAC,OAAO,EAAEzB,OAAOW,YAAY,CAAC,IAAI,EAAEO,MAAM,CAAC,CAAC;IAEhD;IACA,IAAIlB,OAAOC,IAAI,KAAK,UAAUD,OAAOG,KAAK,CAACC,MAAM,EAAE;QACjD2B,IAAIN,SAAS,eAAe,CAAC,IAAI,EAAEtE,MAAM6C,OAAOG,KAAK,EAAE,IAAI,EAAEe,MAAM,CAAC,CAAC;IACvE;IACA,KAAK,MAAMC,QAAQnB,OAAOK,OAAO,CAAE;QACjC0B,IACEN,SACA,CAAC,aAAa,EAAEN,KAAKC,MAAM,EAAE,EAC7B,CAAC,KAAK,EAAEN,SAASK,KAAKC,MAAM,EAAE,MAAM,EAAED,KAAKoB,QAAQ,CAAC,IAAI,EAAErB,MAAM,CAAC,CAAC;IAEtE;IACA,IAAIlB,OAAOC,IAAI,KAAK,UAAUD,OAAOO,OAAO,CAACH,MAAM,EAAE;QACnD2B,IAAIN,SAAS,kBAAkB,CAAC,OAAO,EAAEtE,MAAM6C,OAAOO,OAAO,EAAE,MAAM,EAAEW,MAAM,CAAC,CAAC;IACjF;IACA,KAAK,MAAMsB,SAASxC,OAAOyC,MAAM,CAAE;QACjC,IAAID,MAAME,QAAQ,EAAE;YAClBX,IACEN,SACA,CAAC,eAAe,EAAEe,MAAMG,OAAO,EAAE,EACjC,GAAGH,MAAM9B,IAAI,CAAC,qBAAqB,EAAEQ,MAAM,KAAK,EAAEsB,MAAM9B,IAAI,CAAC,gBAAgB,CAAC;QAElF,OAAO,IAAI8B,MAAMI,SAAS,EAAE;YAC1Bb,IACEN,SACA,CAAC,uBAAuB,EAAEe,MAAMG,OAAO,EAAE,EACzC,CAAC,OAAO,EAAExF,MAAMqF,MAAMK,MAAM,EAAE,WAAW,EAAEL,MAAM9B,IAAI,CAAC,oDAAoD,EAAEvD,MAAMqF,MAAMM,SAAS,EAAE,sBAAsB,CAAC,EAC1J;QAEJ;IACF;IAEA,wBAAwB,GACxB,IAAI,CAACxB,YAAY;mBAUsCtB;YARnDjB,2BAAAA;QADF,MAAMgE,sBACJhE,4BAAAA,uBAAAA,QAASiE,WAAW,sBAApBjE,4BAAAA,qBAAsBU,IAAI,CACxB,CAACC,QACCA,SACAA,MAAMuD,KAAK,IACVvD,CAAAA,MAAMiD,OAAO,KAAK3C,OAAO2C,OAAO,IAC9BxD,MAAMC,OAAO,CAACM,MAAM0C,OAAO,KAC1B1C,MAAM0C,OAAO,CAAChC,MAAM,KAAKJ,OAAOoC,OAAO,CAAChC,MAAM,IAC9CV,MAAM0C,OAAO,CAACc,KAAK,CAAC,CAAC9B,SAAWpB,OAAOoC,OAAO,CAACe,QAAQ,CAAC/B,QAAQ,uBAPxErC,0BAQGkE,KAAK,oBAAIrG,gBAAgBsE,OAAOlB,OAAOoC,OAAO,GAAEpC,kBAAAA,OAAO2C,OAAO,YAAd3C,kBAAkB;QACvE,IAAI+C,cAAe1B,CAAAA,SAASrB,OAAOQ,OAAO,IAAIR,OAAOO,OAAO,CAACH,MAAM,AAAD,GAAI;YACpE2B,IACEL,OACA,oBACA1B,OAAOC,IAAI,KAAK,WACZ,CAAC,uCAAuC,EAAE8C,WAAW,CAAC,CAAC,GACvD,CAAC,gBAAgB,EAAE7B,MAAM,oBAAoB,EAAE6B,WAAW,CAAC,CAAC;QAEpE;QACA,IAAI/C,OAAOQ,OAAO,EAAE;YAClBuB,IACEL,OACA,gBACA,CAAC,4CAA4C,EAAER,MAAM,+CAA+C,EAAElB,OAAOW,YAAY,CAAC,wBAAwB,CAAC;QAEvJ;QACA,MAAMyC,WAAW,UAACrE,2BAAAA,QAASsE,kBAAkB,oBAAI,EAAE,EAChDpC,GAAG,CAAC,CAACqC;gBAAY7E;oBAAAA,iCAAAA,qBAAqB,CAAC6E,QAAQ,YAA9B7E,iCAAkC6E;WACnDC,MAAM,CAACC;QACV,IAAInC,SAAS+B,SAAShD,MAAM,EAAE;YAC5B2B,IACEL,OACA,kBACA,CAAC,KAAK,EAAExE,UAAUkG,UAAU,8GAA8G,CAAC;QAE/I;IACF;IACA,IAAI9C,QAAQF,MAAM,EAAE;QAClB2B,IACEL,OACA,eACAJ,aACI,GAAGnE,MAAMmD,SAAS,wEAAwE,EAAEY,MAAM,CAAC,CAAC,GACpGZ,QAAQF,MAAM,KAAK,IACjB,GAAGjD,MAAMmD,SAAS,+DAA+D,EAAEY,MAAM,CAAC,CAAC,GAC3F,GAAG/D,MAAMmD,SAAS,wEAAwE,EAAEY,MAAM,CAAC,CAAC;IAE9G;IAEA,sCAAsC,GACtC,IAAIlB,OAAOC,IAAI,KAAK,UAAU;QAC5B8B,IACEJ,SACA,mBACA;IAEJ,OAAO,IAAIN,OAAO;QAChBU,IACEJ,SACA,mBACA,CAAC,kFAAkF,EAAExE,MAAM+C,SAAS,gBAAgB,EAAEgB,MAAM,wHAAwH,CAAC;IAEzP;IACA,KAAK,MAAMsB,SAASxC,OAAOyC,MAAM,CAAE;QACjC,IAAID,MAAME,QAAQ,EAAE;QACpBX,IACEJ,SACA,CAAC,aAAa,EAAEa,MAAMG,OAAO,EAAE,EAC/B,GAAGxF,MAAMqF,MAAMK,MAAM,EAAE,6CAA6C,EAAEL,MAAM9B,IAAI,CAAC,OAAO,EACtF8B,MAAMK,MAAM,CAACzC,MAAM,KAAK,IAAI,WAAW,YACxC,YAAY,CAAC;IAElB;IACA,IAAIE,QAAQF,MAAM,EAAE;QAClB,MAAMqD,OAAOnE,UAAUP,SAAS;QAChC,MAAM2E,MAAMD,QAAQ,OAAO,CAAC,IAAI,EAAEjG,OAAOiG,OAAO,GAAG;QACnD1B,IACEJ,SACA,gBACAL,cAAchB,QAAQF,MAAM,GAAG,IAC3B,CAAC,0BAA0B,EAAEsD,IAAI,kBAAkB,EAAExC,MAAM,yBAAyB,CAAC,GACrF,GAAG/D,MAAMmD,SAAS,iBAAiB,EAAEoD,IAAI,kBAAkB,EAAExC,MAAM,yBAAyB,CAAC;IAErG;IAEA,4CAA4C,GAC5C,IAAIG,OAAO;;QACT,MAAMsC,YAAYlG,IAAI,UAACsB,2BAAAA,QAAS4E,SAAS,oBAAI,EAAE,EAAE1C,GAAG,CAAC,CAACvB;;2BAAUA,yBAAAA,MAAOkE,QAAQ,mBAAI;;QACnF7B,IACEH,SACA,oBACA,CAAC,wIAAwI,EAAE7C,UAAU,CAAC,EAAE,EAAE1B,MAAMsG,WAAW,CAAC,EAAEA,cAAc,IAAI,YAAY,WAAW,WAAW,CAAC,GAAG,GAAG,CAAC,CAAC;QAE7O,IAAI5C,oBAAoB;YACtBgB,IACEH,SACA,yBACA;QAEJ;IACF;IACA;;;;;GAKC,GACD,MAAMiC,cAAmD,EAAE;IAC3D,IAAIvC,YAAY;QACduC,YAAY1B,IAAI,CAAC;YACf5C,IAAI;YACJ0C,MAAM,CAAC,iCAAiC,EAAEf,MAAM,6CAA6C,CAAC;QAChG;IACF,OAAO,IAAIlB,OAAOO,OAAO,CAACH,MAAM,KAAK,GAAG;QACtC,MAAM0D,OAAOhD,SAASd,OAAOO,OAAO,CAAC,EAAE;QACvCsD,YAAY1B,IAAI,CAAC;YACf5C,IAAI;YACJ0C,MAAM,CAAC,iCAAiC,EAAEf,MAAM,4BAA4B,EAAE4C,KAAK,gCAAgC,EAAEA,KAAK,oBAAoB,EAAE5C,MAAM,cAAc,CAAC;QACvK;IACF,OAAO,IAAIlB,OAAOO,OAAO,CAACH,MAAM,GAAG,GAAG;QACpCyD,YAAY1B,IAAI,CAAC;YACf5C,IAAI;YACJ0C,MAAM,CAAC,iCAAiC,EAAEf,MAAM,+HAA+H,CAAC;QAClL;IACF;IACA,KAAK,MAAMsB,SAASxC,OAAOyC,MAAM,CAAE;QACjC,IAAID,MAAME,QAAQ,EAAE;QACpBmB,YAAY1B,IAAI,CAAC;YACf5C,IAAI,CAAC,eAAe,EAAEiD,MAAMG,OAAO,EAAE;YACrCV,MAAM,CAAC,iCAAiC,EAAEO,MAAM9B,IAAI,CAAC,4BAA4B,EAAEvD,MAAMqF,MAAMK,MAAM,EAAE,gCAAgC,EAAE1F,MAAMqF,MAAMK,MAAM,EAAE,oBAAoB,EAAE1F,MAAMqF,MAAMM,SAAS,GAAG;QAC7M;IACF;IACA,MAAMiB,SACJtG,IACE,SAACsB,2BAAAA,QAASiF,OAAO,mBAAI,EAAE,EAAE/C,GAAG,CAC1B,CAACgD;;eAAU,SAACA,yBAAAA,MAAOC,gBAAgB,mBAAI,eAAMD,yBAAAA,MAAOE,YAAY,oBAAI;YAEnE7E,aAAAA,UAAUP,SAAS,2BAAnBO,aAAsC;IAC7C,MAAM8E,QAAQ3G,IAAI,UAACsB,2BAAAA,QAASiF,OAAO,oBAAI,EAAE,EAAE/C,GAAG,CAAC,CAACgD;;uBAAUA,yBAAAA,MAAOG,KAAK,mBAAI;;IAC1E,MAAMC,aAAatF,UACf,GAAG1B,MAAM0G,QAAQ,CAAC,EAAEA,WAAW,IAAI,YAAY,WAAW,OAAO,EAC/DK,QAAQ,CAAC,MAAM,EAAE/G,MAAM+G,OAAO,iBAAiB,EAAEA,UAAU,IAAI,WAAW,UAAU,KAAK,CAAC,GAAG,IAC7F,GACF;IACJ,IAAIP,YAAYzD,MAAM,KAAK,GAAG;QAC5B2B,IACEH,SACAiC,WAAW,CAAC,EAAE,CAACtE,EAAE,EACjB,GAAGsE,WAAW,CAAC,EAAE,CAAC5B,IAAI,GAAGoC,aAAa,CAAC,EAAE,EAAEA,WAAW,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;IAEpE,OAAO,IAAIR,YAAYzD,MAAM,GAAG,GAAG;QACjC,KAAK,MAAMkE,cAAcT,YAAa;YACpC9B,IAAIH,SAAS0C,WAAW/E,EAAE,EAAE,GAAG+E,WAAWrC,IAAI,CAAC,CAAC,CAAC;QACnD;QACA,IAAIoC,YAAY;YACdtC,IAAIH,SAAS,kBAAkB,CAAC,QAAQ,EAAEyC,WAAW,CAAC,CAAC;QACzD;IACF;IACA,MAAME,QAAQ9G,IAAI,UAACsB,2BAAAA,QAASyF,YAAY,oBAAI,EAAE,EAAEvD,GAAG,CAAC,CAACwD;;uBAASA,wBAAAA,KAAMpH,KAAK,mBAAI;;IAC7E,IAAIkH,QAAQ,KAAMjE,CAAAA,QAAQF,MAAM,IAAIJ,OAAOyC,MAAM,CAACrC,MAAM,AAAD,GAAI;QACzD2B,IACEH,SACA,oBACAtB,QAAQF,MAAM,KAAK,KAAK,CAACkB,cAAc,CAACtB,OAAOyC,MAAM,CAACrC,MAAM,GACxD,GAAG5C,OAAO+G,OAAO,uBAAuB,EAAEpH,MAAMmD,SAAS,qBAAqB,EAAEY,MAAM,yBAAyB,CAAC,GAChH,GAAG1D,OAAO+G,OAAO,uGAAuG,CAAC;IAEjI;IAEA,uBAAuB,GACvB,IAAI/C,OAAOH,OAAO;QAChB,MAAMqD,WAAWpF,UAAUP,SAAS;QACpCgD,IACEF,SACA,mBACA,CAAC,sNAAsN,EACrN7B,OAAOC,IAAI,KAAK,WACZ,uDACA,GAAGiB,MAAM,SAAS,CAAC,CACxB,SAAS,EAAEwD,YAAY,OAAO,CAAC,2BAA2B,EAAElH,OAAOkH,UAAU,CAAC,CAAC,GAAG,IAAI;IAE3F;IACA,IAAIlD,OAAOlB,QAAQF,MAAM,EAAE;QACzB,MAAMuE,SAASrF,UAAUP,SAAS;QAClC,MAAM6F,MAAMD,UAAU,OAAO,CAAC,IAAI,EAAEnH,OAAOmH,SAAS,GAAG;QACvD5C,IACEF,SACA,gBACAP,cAAchB,QAAQF,MAAM,GAAG,IAC3B,CAAC,6CAA6C,EAAEwE,IAAI,eAAe,CAAC,GACpE,GAAGzH,MAAMmD,SAAS,oCAAoC,EAAEsE,IAAI,gBAAgB,EAAE1D,MAAM,gFAAgF,CAAC;QAE3K,MAAM2D,OAAOvF,UAAUP,SAAS;QAChC,IAAI8F,QAAQ,QAAQA,OAAO,GAAG;YAC5B9C,IACEF,SACA,sBACAP,cAAchB,QAAQF,MAAM,GAAG,IAC3B,CAAC,6BAA6B,EAAE5C,OAAOqH,MAAM,qBAAqB,EAAE3D,MAAM,0CAA0C,CAAC,GACrH,GAAG/D,MAAMmD,SAAS,gBAAgB,EAAE9C,OAAOqH,MAAM,qBAAqB,EAAE3D,MAAM,0CAA0C,CAAC;QAEjI;IACF;IACA,IAAIM,OAAOF,YAAY;QACrB,MAAMwD,UAAUxF,UAAUP,SAAS;QACnC,IAAI+F,WAAW,QAAQA,UAAU,GAAG;YAClC/C,IACEF,SACA,mBACAiD,WAAW,OACP,CAAC,iBAAiB,EAAEtH,OAAOsH,SAAS,+EAA+E,CAAC,GACpH,0GACJ;QAEJ;IACF;IACA,IAAIzD,OAAO;;QACT,MAAM0D,UAAUjH,gBAAOiB,2BAAAA,QAASiG,oBAAoB,oBAAI;QACxD,IAAID,UAAU,GAAG;YACfhD,IACEF,SACA,0BACA,GAAGxE,MAAM0H,SAAS,MAAM,EAAEA,YAAY,IAAI,WAAW,UAAU,kFAAkF,CAAC;QAEtJ;QACA,MAAME,UAAUlG,2BAAAA,QAASmG,oBAAoB;QAC7C,IAAID,WAAW,OAAOA,YAAY,UAAU;YAC1C,MAAM7C,UAAUjD,MAAMC,OAAO,CAAC6F,QAAQ7C,OAAO,IAAI6C,QAAQ7C,OAAO,GAAGlC;YACnE,MAAMiF,SACJ,OAAOF,QAAQG,aAAa,KAAK,WAC7B,CAAC,QAAQ,EAAEpH,WAAWZ,MAAM,CAAC,IAAIiI,KAAKJ,QAAQG,aAAa,IAAI,GAC/D;YACNrD,IACEF,SACA,mBACA,GAAG1E,MAAMiF,QAAQhC,MAAM,GAAGgC,UAAUpC,OAAOoC,OAAO,EAAE,kDAAkD,EAAE+C,OAAO,2BAA2B,CAAC,EAC3I;QAEJ;IACF;IAEA,gEAAgE,GAChE,KAAK,MAAM3F,QAAQV,iBAAiBC,SAAU;QAC5C,IAAI,CAACS,QAAQZ,gBAAgB0G,GAAG,CAAC9F,KAAKD,EAAE,KAAK,CAACC,KAAKyC,IAAI,EAAE;QACzDF,IAAID,OAAO,CAAC,KAAK,EAAEtC,KAAKD,EAAE,EAAE,EAAEC,KAAKyC,IAAI,EAAEzC,KAAK+F,QAAQ,KAAK,YAAY,YAAY;IACrF;IACA,IAAI,UAACxG,2BAAAA,QAASC,YAAY,oBAAI,EAAE,EAAEY,IAAI,CAAC,CAACV,cAAgBA,eAAeA,YAAYG,KAAK,KAAK,OAAO;QAClG0C,IACED,OACA,mBACA;IAEJ;IACA,MAAM0D,YAAY,UAACzG,2BAAAA,QAASyG,SAAS,oBAAI,EAAE,EAAEjC,MAAM,CACjD,CAAChE,KAAO,OAAOA,OAAO,YAAYA,OAAO;IAE3C,IAAIiG,UAAUpF,MAAM,EAAE;QACpB2B,IACED,OACA,aACA,GAAG0D,UAAUpF,MAAM,KAAK,IAAI,4BAA4B,GAAG/C,MAAMmI,UAAUpF,MAAM,EAAE,qBAAqB,CAAC,CAAC,6GAA6G,EAAEoF,UAAUpF,MAAM,KAAK,IAAI,OAAO,OAAO,4BAA4B,EAAEoF,UAAUpF,MAAM,KAAK,IAAI,OAAO,OAAO,EAAE,EAAElD,UAAUsI,WAAW,CAAC,CAAC;IAEpW;IAEA,oBAAoB,GACpB,MAAMC,SAAmC,EAAE;IAC3C,IAAI1G,CAAAA,2BAAAA,QAAS2G,QAAQ,KAAIrH,2BAA2B,CAACU,QAAQ2G,QAAQ,CAAC,EAAE;QACtE3D,IAAI0D,QAAQ,mBAAmBpH,2BAA2B,CAACU,QAAQ2G,QAAQ,CAAC;IAC9E;IACA3D,IAAI0D,QAAQ,gBAAgB;IAE5B,OAAO;QACLhF,OAAOA,MAAMT;QACbD,cAAcA,aAAaC;QAC3B2F,UAAU;YACR;gBAAEpG,IAAI;gBAAWqG,SAAS;gBAAMC,OAAOpE;YAAQ;YAC/C;gBAAElC,IAAI;gBAASqG,SAAS;gBAAgBC,OAAOnE;YAAM;YACrD;gBAAEnC,IAAI;gBAAWqG,SAAS;gBAAsBC,OAAOlE;YAAQ;YAC/D;gBAAEpC,IAAI;gBAAYqG,SAAS;gBAAYC,OAAOjE;YAAQ;YACtD;gBAAErC,IAAI;gBAAWqG,SAAS;gBAA0BC,OAAOhE;YAAQ;YACnE;gBAAEtC,IAAI;gBAASqG,SAAS;gBAAQC,OAAO/D;YAAM;YAC7C;gBAAEvC,IAAI;gBAAUqG,SAAS;gBAAMC,OAAOJ;YAAO;SAC9C,CAAClC,MAAM,CAAC,CAACuC,UAAYA,QAAQD,KAAK,CAACzF,MAAM,GAAG;IAC/C;AACF"}
@@ -0,0 +1,35 @@
1
+ import type { ConsolePluginOrgHost } from '@aglyn/aglyn';
2
+ import type { ListedConsentGroup } from './consent-group-editing';
3
+ export interface ConsentGroupSitePickerProps {
4
+ /** Every site the organization has. */
5
+ sites: readonly ConsolePluginOrgHost[];
6
+ /** The groups the org declares now. */
7
+ groups: readonly ListedConsentGroup[];
8
+ /** The group being edited, whose own sites carry no badge; `null` for a new one. */
9
+ groupId: string | null;
10
+ selected: readonly string[];
11
+ onChange: (hostIds: string[]) => void;
12
+ /** What is wrong with the selection, said under the list. */
13
+ error?: string;
14
+ /** Sites the route refused by id, marked where they are listed. */
15
+ refusedHostIds?: ReadonlySet<string>;
16
+ disabled?: boolean;
17
+ }
18
+ /**
19
+ * The sites a consent group names.
20
+ *
21
+ * A site can be one sender with only one set of siblings, so a site already
22
+ * in another group says which — "In Acme" — and, once ticked, what ticking it
23
+ * does: "Moves from Acme". The move is legitimate, and the review spells out
24
+ * what it costs the group it leaves; the badge is there so nobody makes it
25
+ * without seeing it.
26
+ *
27
+ * A site the group names that the org no longer lists is still shown, ticked
28
+ * and marked, so it can be unticked rather than silently carried into the
29
+ * next declaration.
30
+ */
31
+ export declare function ConsentGroupSitePicker(props: ConsentGroupSitePickerProps): import("react").JSX.Element;
32
+ export declare namespace ConsentGroupSitePicker {
33
+ var displayName: string;
34
+ }
35
+ export default ConsentGroupSitePicker;