payload-subscribers-plugin 0.0.6 → 0.0.8

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 (45) hide show
  1. package/README.md +112 -21
  2. package/dist/components/app/RequestMagicLink.d.ts +13 -1
  3. package/dist/components/app/RequestMagicLink.js +18 -4
  4. package/dist/components/app/RequestMagicLink.js.map +1 -1
  5. package/dist/components/app/RequestOrSubscribe.d.ts +21 -5
  6. package/dist/components/app/RequestOrSubscribe.js +18 -4
  7. package/dist/components/app/RequestOrSubscribe.js.map +1 -1
  8. package/dist/components/app/SelectOptInChannels.d.ts +9 -0
  9. package/dist/components/app/SelectOptInChannels.js +7 -1
  10. package/dist/components/app/SelectOptInChannels.js.map +1 -1
  11. package/dist/components/app/Subscribe.d.ts +12 -1
  12. package/dist/components/app/Subscribe.js +22 -4
  13. package/dist/components/app/Subscribe.js.map +1 -1
  14. package/dist/components/app/SubscriberMenu.d.ts +16 -3
  15. package/dist/components/app/SubscriberMenu.js +26 -17
  16. package/dist/components/app/SubscriberMenu.js.map +1 -1
  17. package/dist/components/app/VerifyMagicLink.d.ts +14 -2
  18. package/dist/components/app/VerifyMagicLink.js +23 -22
  19. package/dist/components/app/VerifyMagicLink.js.map +1 -1
  20. package/dist/components/app/helpers.d.ts +8 -0
  21. package/dist/components/app/helpers.js +8 -1
  22. package/dist/components/app/helpers.js.map +1 -1
  23. package/dist/components/app/shared.module.css +10 -1
  24. package/dist/contexts/SubscriberProvider.d.ts +16 -0
  25. package/dist/contexts/SubscriberProvider.js +14 -3
  26. package/dist/contexts/SubscriberProvider.js.map +1 -1
  27. package/dist/endpoints/getOptInChannels.d.ts +6 -2
  28. package/dist/endpoints/getOptInChannels.js +6 -2
  29. package/dist/endpoints/getOptInChannels.js.map +1 -1
  30. package/dist/endpoints/logout.d.ts +5 -5
  31. package/dist/endpoints/logout.js +6 -8
  32. package/dist/endpoints/logout.js.map +1 -1
  33. package/dist/endpoints/requestMagicLink.d.ts +5 -5
  34. package/dist/endpoints/requestMagicLink.js +14 -17
  35. package/dist/endpoints/requestMagicLink.js.map +1 -1
  36. package/dist/endpoints/subscribe.d.ts +6 -5
  37. package/dist/endpoints/subscribe.js +21 -23
  38. package/dist/endpoints/subscribe.js.map +1 -1
  39. package/dist/endpoints/subscriberAuth.d.ts +5 -5
  40. package/dist/endpoints/subscriberAuth.js +11 -12
  41. package/dist/endpoints/subscriberAuth.js.map +1 -1
  42. package/dist/endpoints/verifyMagicLink.d.ts +5 -5
  43. package/dist/endpoints/verifyMagicLink.js +11 -13
  44. package/dist/endpoints/verifyMagicLink.js.map +1 -1
  45. package/package.json +1 -1
package/README.md CHANGED
@@ -3,16 +3,16 @@
3
3
  A plugin to manage subscribers and the "channels" they can subscribe to.
4
4
 
5
5
  This includes ways to allow your subscribers to:
6
- * Sign up or sign in by requesting a magic link email
7
- * Verify the magic link to authenticate
8
- * Opt in or out of "opt-in channels"
6
+
7
+ - Sign up or sign in by requesting a magic link email
8
+ - Verify the magic link to authenticate
9
+ - Opt in or out of "opt-in channels"
9
10
 
10
11
  You manage the opt-in channels via the Payload admin.
11
12
 
12
13
  The plugin relies on your email adapter configured in your payload config to send emails.
13
14
 
14
- That is all this plugin does currently. Potential features might include email authoring and send scheduler or simple CRM features.
15
-
15
+ That is all this plugin does currently. Potential features might include email authoring and send scheduler or simple CRM features.
16
16
 
17
17
  ## Installation
18
18
 
@@ -44,7 +44,7 @@ export default buildConfig({
44
44
  // Specify the collection to use as the subscribers collection
45
45
  // - Optional. If not specified, the plugin will add a 'subscribers' collection.
46
46
  // - Sets auth if not already
47
- // - Adds (or overrides) fields: email, firstName, status, optIns,
47
+ // - Adds (or overrides) fields: email, firstName, status, optIns,
48
48
  // verificationToken, verificationTokenExpires, and source
49
49
  subscribersCollectionSlug?: CollectionSlug
50
50
 
@@ -202,7 +202,9 @@ Shows the [Subscribe](#subscribe) component to authenticated subscribers, otherw
202
202
  </div> -->
203
203
 
204
204
  ```typescript
205
- <RequestMagicLink
205
+ <RequestOrSubscribe
206
+ // Provide the URL the user should go to after clicking the link in the email and having it verified
207
+ afterVerifyUrl={new URL(window.href)}
206
208
  // Provide your own global class names to add to the component elements. Optional
207
209
  classNames={{
208
210
  button: 'customCssClassNames',
@@ -224,6 +226,8 @@ Shows the [Subscribe](#subscribe) component to authenticated subscribers, otherw
224
226
  {text}
225
227
  </button>
226
228
  }
229
+ // Provide the URL to your route that has the VerifyMagicLink component on it.
230
+ verifyUrl={verifyUrl}
227
231
  />
228
232
  ```
229
233
 
@@ -235,6 +239,8 @@ Form to input email address and get a magic link email sent.
235
239
 
236
240
  ```typescript
237
241
  <RequestMagicLink
242
+ // Provide the URL the user should go to after clicking the link in the email and having it verified
243
+ afterVerifyUrl={new URL(window.href)}
238
244
  // Provide your own global class names to add to the component elements. Optional
239
245
  classNames={{
240
246
  button: 'customCssClassNames',
@@ -252,9 +258,22 @@ Form to input email address and get a magic link email sent.
252
258
  {text}
253
259
  </button>
254
260
  }
261
+ // Provide the URL to your route that has the VerifyMagicLink component on it.
262
+ verifyUrl={verifyUrl}
255
263
  />
256
264
  ```
257
265
 
266
+ ```html
267
+ <!-- The HTML scaffolding with global CSS classes you can use -->
268
+ <div class="subscribers-request subscribers-container">
269
+ <p class="subscribers-message subscribers-error">{result}</p>
270
+ <form class="subscribers-form">
271
+ <input class="subscribers-emailInput" type="email" />
272
+ <button class="subscribers-button">Request magic link</button>
273
+ </form>
274
+ </div>
275
+ ```
276
+
258
277
  #### **VerifyMagicLink**
259
278
 
260
279
  Component that verifies a magic link using expected url parameters.
@@ -277,20 +296,37 @@ Component that verifies a magic link using expected url parameters.
277
296
  // Called after a magic link has been verified. Optional
278
297
  handleMagicLinkVerified={async (result: RequestMagicLinkResponse) => {}}
279
298
  // Provided your own button component. Optional
280
- renderButton={({ name, forwardUrl, onClick, text }) =>
281
- forwardUrl ? (
282
- <a href={forwardUrl}>
283
- <button name={name} type="button">
284
- {text}
285
- </button>
286
- </a>
287
- ) : (
299
+ renderButton={({ name, onClick, text }) =>
288
300
  <button name={name} onClick={onClick} type="button">
289
301
  {text}
290
302
  </button>
291
- )
292
303
  }
293
- />
304
+ // Provide the URL to your route that has the VerifyMagicLink component on it.
305
+ // Used when this VerifyMagicLink component provides an option to request another link
306
+ // when verifying the current one fails.
307
+ verifyUrl={verifyUrl}
308
+ >
309
+ // Provide children to render after link is verified. Optional
310
+ // Since you provide the verifyUrl to any of the plugin components, you can include a forwardUrl
311
+ // as a search param, which your route can then use here.
312
+ <a href={forwardUrl}>
313
+ <button className={'customCss'} name={'continue'} type="button">
314
+ Continue
315
+ </button>
316
+ </a>
317
+ </VerifyMagicLink>
318
+ ```
319
+
320
+ ```html
321
+ <!-- The HTML scaffolding with global CSS classes you can use -->
322
+ <div class="subscribers-verify subscribers-container">
323
+ <p class="subscribers-loading">verifying...</p>
324
+ <p class="subscribers-message">{result}</p>
325
+ <div class="subscribers-form">
326
+ {renderButton({ name: "request", onClick: handleRequestAnother, text:"Request another magic
327
+ link", })} {children}
328
+ </div>
329
+ </div>
294
330
  ```
295
331
 
296
332
  #### **Subscribe**
@@ -301,6 +337,8 @@ Allows a subscriber to select from among all active optInChannels.
301
337
 
302
338
  ```typescript
303
339
  <Subscribe
340
+ // Provide the URL the user should go to after clicking the link in the email and having it verified
341
+ afterVerifyUrl={new URL(window.href)}
304
342
  // Provide your own global class names to add to the component elements. Optional
305
343
  classNames={{
306
344
  button: 'customCssClassNames',
@@ -320,9 +358,43 @@ Allows a subscriber to select from among all active optInChannels.
320
358
  {text}
321
359
  </button>
322
360
  }
361
+ // Provide the URL to your route that has the VerifyMagicLink component on it.
362
+ verifyUrl={verifyUrl}
323
363
  />
324
364
  ```
325
365
 
366
+ ```html
367
+ <!-- The HTML scaffolding with global CSS classes you can use -->
368
+ <div class="subscribers-subscribe subscribers-container">
369
+ <h2>Subscribe</h2>
370
+ <div class="subscribers-section">
371
+ <!-- START: SelectOptInChannels -->
372
+ <div class="subscribers-container">
373
+ <h3>Opt-in Channels</h3>
374
+ <p class="subscribers-loading">verifying...</p>
375
+ <div class="subscribers-optionsGroup">
376
+ <!-- START: FOR EACH CHANNEL -->
377
+ <div class="subscribers-optInCheckboxItem">
378
+ <label class="subscribers-optInCheckboxLabel" ,>
379
+ <input class="subscribers-optInCheckbox" type="checkbox" />
380
+ {channel.title}
381
+ </label>
382
+ </div>
383
+ <!-- END: FOR EACH CHANNEL -->
384
+ </div>
385
+ </div>
386
+ <!-- END: SelectOptInChannels -->
387
+ </div>
388
+ <form class="subscribers-form">
389
+ <div class="subscribers-section">
390
+ <input class="subscribers-emailInput" placeholder="enter your email" type="email" />
391
+ <button class="subscribers-button" type="submit">Save choices</button>
392
+ </div>
393
+ </form>
394
+ <p class="subscribers-message subscribers-error">{result}</p>
395
+ </div>
396
+ ```
397
+
326
398
  #### **SubscriberMenu**
327
399
 
328
400
  A simple user menu, most useful for testing. Seen in the screenshots above. Includes a "welcome" message, a link to a /subscribe route, and a log out button.
@@ -330,10 +402,29 @@ A simple user menu, most useful for testing. Seen in the screenshots above. Incl
330
402
  ```typescript
331
403
  // classNames prop
332
404
 
333
- export type SubscriberMenuClasses = {
334
- button?: string
335
- container?: string
336
- }
405
+ <SubscriberMenu
406
+ classNames={{
407
+ button: 'customCssClassNames',
408
+ container: 'customCssClassNames',
409
+ }}
410
+ subscribeUrl={new URL('/subscribe', serverURL)}
411
+ />
412
+
413
+ ```
414
+
415
+ ```html
416
+ <!-- The HTML scaffolding with global CSS classes you can use -->
417
+ <div class="subscribers-menu subscribers-container">
418
+ <div class="subscribers-group">
419
+ <div class="subscribers-welcome">Welcome, {subscriber email}</div>
420
+ <div class="subscribers-subs-link">
421
+ <a href="{subscribeUrl}">Manage subscriptions</a>
422
+ </div>
423
+ <div class="subscribers-logout">
424
+ <button class="subscribers-button" type="button">Log out</button>
425
+ </div>
426
+ </div>
427
+ </div>
337
428
  ```
338
429
 
339
430
  ## Contributing
@@ -1,10 +1,15 @@
1
1
  import type { RequestMagicLinkResponse } from '../../endpoints/requestMagicLink.js';
2
2
  export { RequestMagicLinkResponse };
3
+ /**
4
+ * Props for the RequestMagicLink component.
5
+ */
3
6
  export interface IRequestMagicLink {
4
7
  classNames?: RequestMagicLinkClasses;
5
8
  handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void;
6
9
  props?: any;
10
+ verifyUrl?: string | URL;
7
11
  }
12
+ /** Optional CSS class overrides for RequestMagicLink elements. */
8
13
  export type RequestMagicLinkClasses = {
9
14
  button?: string;
10
15
  container?: string;
@@ -13,4 +18,11 @@ export type RequestMagicLinkClasses = {
13
18
  form?: string;
14
19
  message?: string;
15
20
  };
16
- export declare const RequestMagicLink: ({ classNames, handleMagicLinkRequested, }: IRequestMagicLink) => import("react").JSX.Element;
21
+ /**
22
+ * Form component that lets users request a magic-login link by email. Submits to POST /api/emailToken
23
+ * and shows success or error message. Uses SubscriberProvider for pre-filling email when available.
24
+ *
25
+ * @param props - See IRequestMagicLink
26
+ * @returns Form UI with email input and "Request magic link" button
27
+ */
28
+ export declare const RequestMagicLink: ({ classNames, handleMagicLinkRequested, verifyUrl, }: IRequestMagicLink) => import("react").JSX.Element;
@@ -6,14 +6,23 @@ import { useSubscriber } from '../../contexts/SubscriberProvider.js';
6
6
  import { useServerUrl } from '../../react-hooks/useServerUrl.js';
7
7
  import { mergeClassNames } from './helpers.js';
8
8
  import styles from './shared.module.css';
9
- export const RequestMagicLink = ({ classNames = {
9
+ /**
10
+ * Form component that lets users request a magic-login link by email. Submits to POST /api/emailToken
11
+ * and shows success or error message. Uses SubscriberProvider for pre-filling email when available.
12
+ *
13
+ * @param props - See IRequestMagicLink
14
+ * @returns Form UI with email input and "Request magic link" button
15
+ */ export const RequestMagicLink = ({ classNames = {
10
16
  button: '',
11
17
  container: '',
12
18
  emailInput: '',
13
19
  error: '',
14
20
  form: '',
15
21
  message: ''
16
- }, handleMagicLinkRequested })=>{
22
+ }, handleMagicLinkRequested, verifyUrl })=>{
23
+ if (typeof verifyUrl == 'string') {
24
+ verifyUrl = new URL(verifyUrl);
25
+ }
17
26
  const { subscriber } = useSubscriber();
18
27
  const { serverURL } = useServerUrl();
19
28
  const [status, setStatus] = useState('default');
@@ -29,11 +38,10 @@ export const RequestMagicLink = ({ classNames = {
29
38
  ]);
30
39
  const handleSubmit = async (e)=>{
31
40
  e.preventDefault();
32
- const forwardUrl = window.location.pathname + '?now=' + new Date().toISOString();
33
41
  const emailTokenResponse = await sdk.request({
34
42
  json: {
35
43
  email,
36
- forwardUrl
44
+ verifyUrl: verifyUrl?.href
37
45
  },
38
46
  method: 'POST',
39
47
  path: '/api/emailToken'
@@ -63,15 +71,18 @@ export const RequestMagicLink = ({ classNames = {
63
71
  };
64
72
  return /*#__PURE__*/ _jsxs("div", {
65
73
  className: mergeClassNames([
74
+ 'subscribers-request subscribers-container',
66
75
  styles.container,
67
76
  classNames.container
68
77
  ]),
69
78
  children: [
70
79
  result ? /*#__PURE__*/ _jsx("p", {
71
80
  className: mergeClassNames([
81
+ 'subscribers-message',
72
82
  styles.message,
73
83
  classNames.message,
74
84
  status == 'error' ? [
85
+ 'subscribers-error',
75
86
  styles.error,
76
87
  classNames.error
77
88
  ] : []
@@ -80,6 +91,7 @@ export const RequestMagicLink = ({ classNames = {
80
91
  }) : /*#__PURE__*/ _jsx(_Fragment, {}),
81
92
  /*#__PURE__*/ _jsxs("form", {
82
93
  className: mergeClassNames([
94
+ 'subscribers-form',
83
95
  styles.form,
84
96
  classNames.form
85
97
  ]),
@@ -89,6 +101,7 @@ export const RequestMagicLink = ({ classNames = {
89
101
  /*#__PURE__*/ _jsx("input", {
90
102
  "aria-label": "enter your email",
91
103
  className: mergeClassNames([
104
+ 'subscribers-emailInput',
92
105
  styles.emailInput,
93
106
  classNames.emailInput
94
107
  ]),
@@ -99,6 +112,7 @@ export const RequestMagicLink = ({ classNames = {
99
112
  }),
100
113
  /*#__PURE__*/ _jsx("button", {
101
114
  className: mergeClassNames([
115
+ 'subscribers-button',
102
116
  styles.button,
103
117
  classNames.button
104
118
  ]),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/app/RequestMagicLink.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ChangeEvent, type SubmitEvent, useEffect, useState } from 'react'\n\nimport type { Config } from '../../copied/payload-types.js'\nimport type { RequestMagicLinkResponse } from '../../endpoints/requestMagicLink.js'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\nexport { RequestMagicLinkResponse }\n\nexport interface IRequestMagicLink {\n classNames?: RequestMagicLinkClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n props?: any\n}\n\nexport type RequestMagicLinkClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n message?: string\n}\n\ntype statusValues = 'default' | 'error' | 'sent'\n\nexport const RequestMagicLink = ({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n message: '',\n },\n handleMagicLinkRequested,\n}: IRequestMagicLink) => {\n const { subscriber } = useSubscriber()\n const { serverURL } = useServerUrl()\n\n const [status, setStatus] = useState<statusValues>('default')\n\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n const [result, setResult] = useState<string>()\n const [email, setEmail] = useState(subscriber?.email || '')\n\n useEffect(() => {\n setEmail(subscriber?.email || '')\n }, [subscriber])\n\n const handleSubmit = async (e: SubmitEvent<HTMLFormElement>) => {\n e.preventDefault()\n const forwardUrl = window.location.pathname + '?now=' + new Date().toISOString()\n const emailTokenResponse = await sdk.request({\n json: {\n email,\n forwardUrl,\n },\n method: 'POST',\n path: '/api/emailToken',\n })\n if (emailTokenResponse.ok) {\n const emailTokenResponseJson: RequestMagicLinkResponse = await emailTokenResponse.json()\n if (handleMagicLinkRequested) {\n handleMagicLinkRequested(emailTokenResponseJson)\n }\n // @ts-expect-error One or the other exists\n const { emailResult, error } = emailTokenResponseJson\n if (error) {\n setStatus('error')\n setResult(`An error occured. Please try again. \\n ${error}`)\n } else if (emailResult) {\n setStatus('sent')\n setResult('An email has been sent containing your magic link.')\n } else {\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n } else {\n const emailTokenResponseText = await emailTokenResponse.text()\n setStatus('error')\n setResult(`An error occured. Please try again. \\n${emailTokenResponseText}`)\n }\n }\n\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n {result ? (\n <p\n className={mergeClassNames([\n styles.message,\n classNames.message,\n status == 'error' ? [styles.error, classNames.error] : [],\n ])}\n >\n {result}\n </p>\n ) : (\n <></>\n )}\n <form\n className={mergeClassNames([styles.form, classNames.form])}\n method=\"POST\"\n onSubmit={handleSubmit}\n >\n <input\n aria-label=\"enter your email\"\n className={mergeClassNames([styles.emailInput, classNames.emailInput])}\n onChange={(e: ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}\n placeholder=\"enter your email\"\n type=\"email\"\n value={email}\n />\n <button className={mergeClassNames([styles.button, classNames.button])} type=\"submit\">\n Request magic link\n </button>\n </form>\n </div>\n )\n}\n"],"names":["PayloadSDK","useEffect","useState","useSubscriber","useServerUrl","mergeClassNames","styles","RequestMagicLink","classNames","button","container","emailInput","error","form","message","handleMagicLinkRequested","subscriber","serverURL","status","setStatus","sdk","baseURL","result","setResult","email","setEmail","handleSubmit","e","preventDefault","forwardUrl","window","location","pathname","Date","toISOString","emailTokenResponse","request","json","method","path","ok","emailTokenResponseJson","emailResult","emailTokenResponseText","text","div","className","p","onSubmit","input","aria-label","onChange","target","value","placeholder","type"],"mappings":"AAAA;;AAEA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAA6CC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAK/E,SAASC,aAAa,QAAQ,uCAAsC;AACpE,SAASC,YAAY,QAAQ,oCAAmC;AAChE,SAASC,eAAe,QAAQ,eAAc;AAC9C,OAAOC,YAAY,sBAAqB;AAqBxC,OAAO,MAAMC,mBAAmB,CAAC,EAC/BC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,SAAS;AACX,CAAC,EACDC,wBAAwB,EACN;IAClB,MAAM,EAAEC,UAAU,EAAE,GAAGb;IACvB,MAAM,EAAEc,SAAS,EAAE,GAAGb;IAEtB,MAAM,CAACc,QAAQC,UAAU,GAAGjB,SAAuB;IAEnD,MAAMkB,MAAM,IAAIpB,WAAmB;QACjCqB,SAASJ,aAAa;IACxB;IAEA,MAAM,CAACK,QAAQC,UAAU,GAAGrB;IAC5B,MAAM,CAACsB,OAAOC,SAAS,GAAGvB,SAASc,YAAYQ,SAAS;IAExDvB,UAAU;QACRwB,SAAST,YAAYQ,SAAS;IAChC,GAAG;QAACR;KAAW;IAEf,MAAMU,eAAe,OAAOC;QAC1BA,EAAEC,cAAc;QAChB,MAAMC,aAAaC,OAAOC,QAAQ,CAACC,QAAQ,GAAG,UAAU,IAAIC,OAAOC,WAAW;QAC9E,MAAMC,qBAAqB,MAAMf,IAAIgB,OAAO,CAAC;YAC3CC,MAAM;gBACJb;gBACAK;YACF;YACAS,QAAQ;YACRC,MAAM;QACR;QACA,IAAIJ,mBAAmBK,EAAE,EAAE;YACzB,MAAMC,yBAAmD,MAAMN,mBAAmBE,IAAI;YACtF,IAAItB,0BAA0B;gBAC5BA,yBAAyB0B;YAC3B;YACA,2CAA2C;YAC3C,MAAM,EAAEC,WAAW,EAAE9B,KAAK,EAAE,GAAG6B;YAC/B,IAAI7B,OAAO;gBACTO,UAAU;gBACVI,UAAU,CAAC,uCAAuC,EAAEX,OAAO;YAC7D,OAAO,IAAI8B,aAAa;gBACtBvB,UAAU;gBACVI,UAAU;YACZ,OAAO;gBACLJ,UAAU;gBACVI,UAAU,CAAC,oDAAoD,CAAC;YAClE;QACF,OAAO;YACL,MAAMoB,yBAAyB,MAAMR,mBAAmBS,IAAI;YAC5DzB,UAAU;YACVI,UAAU,CAAC,sCAAsC,EAAEoB,wBAAwB;QAC7E;IACF;IAEA,qBACE,MAACE;QAAIC,WAAWzC,gBAAgB;YAACC,OAAOI,SAAS;YAAEF,WAAWE,SAAS;SAAC;;YACrEY,uBACC,KAACyB;gBACCD,WAAWzC,gBAAgB;oBACzBC,OAAOQ,OAAO;oBACdN,WAAWM,OAAO;oBAClBI,UAAU,UAAU;wBAACZ,OAAOM,KAAK;wBAAEJ,WAAWI,KAAK;qBAAC,GAAG,EAAE;iBAC1D;0BAEAU;+BAGH;0BAEF,MAACT;gBACCiC,WAAWzC,gBAAgB;oBAACC,OAAOO,IAAI;oBAAEL,WAAWK,IAAI;iBAAC;gBACzDyB,QAAO;gBACPU,UAAUtB;;kCAEV,KAACuB;wBACCC,cAAW;wBACXJ,WAAWzC,gBAAgB;4BAACC,OAAOK,UAAU;4BAAEH,WAAWG,UAAU;yBAAC;wBACrEwC,UAAU,CAACxB,IAAqCF,SAASE,EAAEyB,MAAM,CAACC,KAAK;wBACvEC,aAAY;wBACZC,MAAK;wBACLF,OAAO7B;;kCAET,KAACf;wBAAOqC,WAAWzC,gBAAgB;4BAACC,OAAOG,MAAM;4BAAED,WAAWC,MAAM;yBAAC;wBAAG8C,MAAK;kCAAS;;;;;;AAM9F,EAAC"}
1
+ {"version":3,"sources":["../../../src/components/app/RequestMagicLink.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ChangeEvent, type SubmitEvent, useEffect, useState } from 'react'\n\nimport type { Config } from '../../copied/payload-types.js'\nimport type { RequestMagicLinkResponse } from '../../endpoints/requestMagicLink.js'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\nexport { RequestMagicLinkResponse }\n\n/**\n * Props for the RequestMagicLink component.\n */\nexport interface IRequestMagicLink {\n classNames?: RequestMagicLinkClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n props?: any\n verifyUrl?: string | URL\n}\n\n/** Optional CSS class overrides for RequestMagicLink elements. */\nexport type RequestMagicLinkClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n message?: string\n}\n\ntype statusValues = 'default' | 'error' | 'sent'\n\n/**\n * Form component that lets users request a magic-login link by email. Submits to POST /api/emailToken\n * and shows success or error message. Uses SubscriberProvider for pre-filling email when available.\n *\n * @param props - See IRequestMagicLink\n * @returns Form UI with email input and \"Request magic link\" button\n */\nexport const RequestMagicLink = ({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n message: '',\n },\n handleMagicLinkRequested,\n verifyUrl,\n}: IRequestMagicLink) => {\n if (typeof verifyUrl == 'string') {\n verifyUrl = new URL(verifyUrl)\n }\n\n const { subscriber } = useSubscriber()\n const { serverURL } = useServerUrl()\n const [status, setStatus] = useState<statusValues>('default')\n\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n const [result, setResult] = useState<string>()\n const [email, setEmail] = useState(subscriber?.email || '')\n\n useEffect(() => {\n setEmail(subscriber?.email || '')\n }, [subscriber])\n\n const handleSubmit = async (e: SubmitEvent<HTMLFormElement>) => {\n e.preventDefault()\n const emailTokenResponse = await sdk.request({\n json: {\n email,\n verifyUrl: verifyUrl?.href,\n },\n method: 'POST',\n path: '/api/emailToken',\n })\n if (emailTokenResponse.ok) {\n const emailTokenResponseJson: RequestMagicLinkResponse = await emailTokenResponse.json()\n if (handleMagicLinkRequested) {\n handleMagicLinkRequested(emailTokenResponseJson)\n }\n // @ts-expect-error One or the other exists\n const { emailResult, error } = emailTokenResponseJson\n if (error) {\n setStatus('error')\n setResult(`An error occured. Please try again. \\n ${error}`)\n } else if (emailResult) {\n setStatus('sent')\n setResult('An email has been sent containing your magic link.')\n } else {\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n } else {\n const emailTokenResponseText = await emailTokenResponse.text()\n setStatus('error')\n setResult(`An error occured. Please try again. \\n${emailTokenResponseText}`)\n }\n }\n\n return (\n <div\n className={mergeClassNames([\n 'subscribers-request subscribers-container',\n styles.container,\n classNames.container,\n ])}\n >\n {result ? (\n <p\n className={mergeClassNames([\n 'subscribers-message',\n styles.message,\n classNames.message,\n status == 'error' ? ['subscribers-error', styles.error, classNames.error] : [],\n ])}\n >\n {result}\n </p>\n ) : (\n <></>\n )}\n <form\n className={mergeClassNames(['subscribers-form', styles.form, classNames.form])}\n method=\"POST\"\n onSubmit={handleSubmit}\n >\n <input\n aria-label=\"enter your email\"\n className={mergeClassNames([\n 'subscribers-emailInput',\n styles.emailInput,\n classNames.emailInput,\n ])}\n onChange={(e: ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}\n placeholder=\"enter your email\"\n type=\"email\"\n value={email}\n />\n <button\n className={mergeClassNames(['subscribers-button', styles.button, classNames.button])}\n type=\"submit\"\n >\n Request magic link\n </button>\n </form>\n </div>\n )\n}\n"],"names":["PayloadSDK","useEffect","useState","useSubscriber","useServerUrl","mergeClassNames","styles","RequestMagicLink","classNames","button","container","emailInput","error","form","message","handleMagicLinkRequested","verifyUrl","URL","subscriber","serverURL","status","setStatus","sdk","baseURL","result","setResult","email","setEmail","handleSubmit","e","preventDefault","emailTokenResponse","request","json","href","method","path","ok","emailTokenResponseJson","emailResult","emailTokenResponseText","text","div","className","p","onSubmit","input","aria-label","onChange","target","value","placeholder","type"],"mappings":"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"}
@@ -1,5 +1,15 @@
1
1
  import { type RequestMagicLinkResponse, type SubscribeResponse } from '../../exports/ui.js';
2
2
  export type { RequestMagicLinkResponse, SubscribeResponse };
3
+ /**
4
+ * Props for the RequestOrSubscribe component.
5
+ */
6
+ export interface IRequestOrSubscribe {
7
+ classNames?: RequestOrSubscribeClasses;
8
+ handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void;
9
+ handleSubscribe?: (result: SubscribeResponse) => void;
10
+ verifyUrl?: string | URL;
11
+ }
12
+ /** Optional CSS class overrides for RequestOrSubscribe and its child components. */
3
13
  export type RequestOrSubscribeClasses = {
4
14
  button?: string;
5
15
  container?: string;
@@ -10,8 +20,14 @@ export type RequestOrSubscribeClasses = {
10
20
  message?: string;
11
21
  section?: string;
12
22
  };
13
- export declare function RequestOrSubscribe({ classNames, handleMagicLinkRequested, handleSubscribe, }: {
14
- classNames?: RequestOrSubscribeClasses;
15
- handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void;
16
- handleSubscribe?: (result: SubscribeResponse) => void;
17
- }): import("react").JSX.Element;
23
+ /**
24
+ * Composite component that shows Subscribe when a subscriber is authenticated, otherwise
25
+ * RequestMagicLink. Used as a single entry point for "sign in or manage subscriptions."
26
+ *
27
+ * @param props.classNames - Optional class overrides passed to child components
28
+ * @param props.handleMagicLinkRequested - Callback when a magic link is requested (no subscriber yet)
29
+ * @param props.handleSubscribe - Callback when subscription/opt-ins are updated (subscriber present)
30
+ * @param props.verifyUrl - Base URL for verify links in emails
31
+ * @returns Either Subscribe or RequestMagicLink based on subscriber context
32
+ */
33
+ export declare function RequestOrSubscribe({ classNames, handleMagicLinkRequested, handleSubscribe, verifyUrl, }: IRequestOrSubscribe): import("react").JSX.Element;
@@ -2,7 +2,16 @@
2
2
  import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useSubscriber } from '../../contexts/SubscriberProvider.js';
4
4
  import { RequestMagicLink, Subscribe } from '../../exports/ui.js';
5
- export function RequestOrSubscribe({ classNames = {
5
+ /**
6
+ * Composite component that shows Subscribe when a subscriber is authenticated, otherwise
7
+ * RequestMagicLink. Used as a single entry point for "sign in or manage subscriptions."
8
+ *
9
+ * @param props.classNames - Optional class overrides passed to child components
10
+ * @param props.handleMagicLinkRequested - Callback when a magic link is requested (no subscriber yet)
11
+ * @param props.handleSubscribe - Callback when subscription/opt-ins are updated (subscriber present)
12
+ * @param props.verifyUrl - Base URL for verify links in emails
13
+ * @returns Either Subscribe or RequestMagicLink based on subscriber context
14
+ */ export function RequestOrSubscribe({ classNames = {
6
15
  button: '',
7
16
  container: '',
8
17
  emailInput: '',
@@ -11,16 +20,21 @@ export function RequestOrSubscribe({ classNames = {
11
20
  loading: '',
12
21
  message: '',
13
22
  section: ''
14
- }, handleMagicLinkRequested, handleSubscribe }) {
23
+ }, handleMagicLinkRequested, handleSubscribe, verifyUrl }) {
24
+ if (typeof verifyUrl == 'string') {
25
+ verifyUrl = new URL(verifyUrl);
26
+ }
15
27
  const { subscriber } = useSubscriber();
16
28
  // Example: Conditionally render something or pass the state to children
17
29
  return /*#__PURE__*/ _jsx(_Fragment, {
18
30
  children: subscriber ? /*#__PURE__*/ _jsx(Subscribe, {
19
31
  classNames: classNames,
20
- handleSubscribe: handleSubscribe
32
+ handleSubscribe: handleSubscribe,
33
+ verifyUrl: verifyUrl
21
34
  }) : /*#__PURE__*/ _jsx(RequestMagicLink, {
22
35
  classNames: classNames,
23
- handleMagicLinkRequested: handleMagicLinkRequested
36
+ handleMagicLinkRequested: handleMagicLinkRequested,
37
+ verifyUrl: verifyUrl
24
38
  })
25
39
  });
26
40
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/app/RequestOrSubscribe.tsx"],"sourcesContent":["'use client'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport {\n RequestMagicLink,\n type RequestMagicLinkResponse,\n Subscribe,\n type SubscribeResponse,\n} from '../../exports/ui.js'\n\nexport type { RequestMagicLinkResponse, SubscribeResponse }\n\nexport type RequestOrSubscribeClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n section?: string\n}\n\nexport function RequestOrSubscribe({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n section: '',\n },\n handleMagicLinkRequested,\n handleSubscribe,\n}: {\n classNames?: RequestOrSubscribeClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n handleSubscribe?: (result: SubscribeResponse) => void\n}) {\n const { subscriber } = useSubscriber()\n\n // Example: Conditionally render something or pass the state to children\n return (\n <>\n {subscriber ? (\n <Subscribe classNames={classNames} handleSubscribe={handleSubscribe} />\n ) : (\n <RequestMagicLink\n classNames={classNames}\n handleMagicLinkRequested={handleMagicLinkRequested}\n />\n )}\n {/* <div>subscriber = {JSON.stringify(subscriber)}</div> */}\n </>\n )\n}\n"],"names":["useSubscriber","RequestMagicLink","Subscribe","RequestOrSubscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleMagicLinkRequested","handleSubscribe","subscriber"],"mappings":"AAAA;;AAEA,SAASA,aAAa,QAAQ,uCAAsC;AACpE,SACEC,gBAAgB,EAEhBC,SAAS,QAEJ,sBAAqB;AAe5B,OAAO,SAASC,mBAAmB,EACjCC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,SAAS;IACTC,SAAS;IACTC,SAAS;AACX,CAAC,EACDC,wBAAwB,EACxBC,eAAe,EAKhB;IACC,MAAM,EAAEC,UAAU,EAAE,GAAGf;IAEvB,wEAAwE;IACxE,qBACE;kBACGe,2BACC,KAACb;YAAUE,YAAYA;YAAYU,iBAAiBA;2BAEpD,KAACb;YACCG,YAAYA;YACZS,0BAA0BA;;;AAMpC"}
1
+ {"version":3,"sources":["../../../src/components/app/RequestOrSubscribe.tsx"],"sourcesContent":["'use client'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport {\n RequestMagicLink,\n type RequestMagicLinkResponse,\n Subscribe,\n type SubscribeResponse,\n} from '../../exports/ui.js'\n\nexport type { RequestMagicLinkResponse, SubscribeResponse }\n\n/**\n * Props for the RequestOrSubscribe component.\n */\nexport interface IRequestOrSubscribe {\n classNames?: RequestOrSubscribeClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n handleSubscribe?: (result: SubscribeResponse) => void\n verifyUrl?: string | URL\n}\n\n/** Optional CSS class overrides for RequestOrSubscribe and its child components. */\nexport type RequestOrSubscribeClasses = {\n button?: string\n container?: string\n emailInput?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n section?: string\n}\n\n/**\n * Composite component that shows Subscribe when a subscriber is authenticated, otherwise\n * RequestMagicLink. Used as a single entry point for \"sign in or manage subscriptions.\"\n *\n * @param props.classNames - Optional class overrides passed to child components\n * @param props.handleMagicLinkRequested - Callback when a magic link is requested (no subscriber yet)\n * @param props.handleSubscribe - Callback when subscription/opt-ins are updated (subscriber present)\n * @param props.verifyUrl - Base URL for verify links in emails\n * @returns Either Subscribe or RequestMagicLink based on subscriber context\n */\nexport function RequestOrSubscribe({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n section: '',\n },\n handleMagicLinkRequested,\n handleSubscribe,\n verifyUrl,\n}: IRequestOrSubscribe) {\n if (typeof verifyUrl == 'string') {\n verifyUrl = new URL(verifyUrl)\n }\n\n const { subscriber } = useSubscriber()\n\n // Example: Conditionally render something or pass the state to children\n return (\n <>\n {subscriber ? (\n <Subscribe\n classNames={classNames}\n handleSubscribe={handleSubscribe}\n verifyUrl={verifyUrl}\n />\n ) : (\n <RequestMagicLink\n classNames={classNames}\n handleMagicLinkRequested={handleMagicLinkRequested}\n verifyUrl={verifyUrl}\n />\n )}\n {/* <div>subscriber = {JSON.stringify(subscriber)}</div> */}\n </>\n )\n}\n"],"names":["useSubscriber","RequestMagicLink","Subscribe","RequestOrSubscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleMagicLinkRequested","handleSubscribe","verifyUrl","URL","subscriber"],"mappings":"AAAA;;AAEA,SAASA,aAAa,QAAQ,uCAAsC;AACpE,SACEC,gBAAgB,EAEhBC,SAAS,QAEJ,sBAAqB;AA0B5B;;;;;;;;;CASC,GACD,OAAO,SAASC,mBAAmB,EACjCC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,SAAS;IACTC,SAAS;IACTC,SAAS;AACX,CAAC,EACDC,wBAAwB,EACxBC,eAAe,EACfC,SAAS,EACW;IACpB,IAAI,OAAOA,aAAa,UAAU;QAChCA,YAAY,IAAIC,IAAID;IACtB;IAEA,MAAM,EAAEE,UAAU,EAAE,GAAGjB;IAEvB,wEAAwE;IACxE,qBACE;kBACGiB,2BACC,KAACf;YACCE,YAAYA;YACZU,iBAAiBA;YACjBC,WAAWA;2BAGb,KAACd;YACCG,YAAYA;YACZS,0BAA0BA;YAC1BE,WAAWA;;;AAMrB"}
@@ -1,10 +1,12 @@
1
1
  import type { OptInChannel } from '../../copied/payload-types.js';
2
+ /** Props for the SelectOptInChannels component. */
2
3
  export interface ISelectOptInChannels {
3
4
  classNames?: SelectOptInChannelsClasses;
4
5
  handleOptInChannelsSelected?: (result: OptInChannel[]) => void;
5
6
  props?: any;
6
7
  selectedOptInChannelIDs?: string[];
7
8
  }
9
+ /** Optional CSS class overrides for SelectOptInChannels elements. */
8
10
  export type SelectOptInChannelsClasses = {
9
11
  button?: string;
10
12
  container?: string;
@@ -17,4 +19,11 @@ export type SelectOptInChannelsClasses = {
17
19
  optInCheckboxLabel?: string;
18
20
  optionsGroup?: string;
19
21
  };
22
+ /**
23
+ * Fetches active opt-in channels from GET /api/optinchannels and renders a list of checkboxes.
24
+ * Reports selected channels via handleOptInChannelsSelected. Supports pre-selection via selectedOptInChannelIDs.
25
+ *
26
+ * @param props - See ISelectOptInChannels
27
+ * @returns Section titled "Opt-in Channels" with checkboxes and loading/error state
28
+ */
20
29
  export declare const SelectOptInChannels: ({ classNames, handleOptInChannelsSelected, selectedOptInChannelIDs, }: ISelectOptInChannels) => import("react").JSX.Element;
@@ -5,7 +5,13 @@ import { useEffect, useState } from 'react';
5
5
  import { useServerUrl } from '../../react-hooks/useServerUrl.js';
6
6
  import { mergeClassNames } from './helpers.js';
7
7
  import styles from './shared.module.css';
8
- export const SelectOptInChannels = ({ classNames = {
8
+ /**
9
+ * Fetches active opt-in channels from GET /api/optinchannels and renders a list of checkboxes.
10
+ * Reports selected channels via handleOptInChannelsSelected. Supports pre-selection via selectedOptInChannelIDs.
11
+ *
12
+ * @param props - See ISelectOptInChannels
13
+ * @returns Section titled "Opt-in Channels" with checkboxes and loading/error state
14
+ */ export const SelectOptInChannels = ({ classNames = {
9
15
  button: '',
10
16
  container: '',
11
17
  error: '',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/app/SelectOptInChannels.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { useEffect, useState } from 'react'\n\nimport type { Config, OptInChannel } from '../../copied/payload-types.js'\nimport type { GetOptInChannelsResponse } from '../../endpoints/getOptInChannels.js'\n\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\n// const payload = await getPayload({\n// config: configPromise,\n// })\n\n// Pass your config from generated types as generic\n\nexport interface ISelectOptInChannels {\n classNames?: SelectOptInChannelsClasses\n handleOptInChannelsSelected?: (result: OptInChannel[]) => void\n props?: any\n selectedOptInChannelIDs?: string[]\n}\n\nexport type SelectOptInChannelsClasses = {\n button?: string\n container?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n optInCheckbox?: string\n optInCheckboxItem?: string\n optInCheckboxLabel?: string\n optionsGroup?: string\n}\n\nexport const SelectOptInChannels = ({\n classNames = {\n button: '',\n container: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n optInCheckbox: '',\n optInCheckboxItem: '',\n optInCheckboxLabel: '',\n optionsGroup: '',\n },\n handleOptInChannelsSelected,\n selectedOptInChannelIDs,\n}: ISelectOptInChannels) => {\n const { serverURL } = useServerUrl()\n // const { serverURL } = { serverURL: 'http://localhost:3001' }\n type OptInChannelCheckbox = {\n isChecked: boolean\n } & OptInChannel\n const [result, setResult] = useState<any>()\n const [allOptInChannels, setAllOptInChannels] = useState<OptInChannelCheckbox[]>([])\n\n useEffect(() => {\n async function verify() {\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n console.log('calling optinchannels endpoint')\n const result = await sdk.request({\n method: 'GET',\n path: '/api/optinchannels',\n })\n if (result.ok) {\n const resultJson: GetOptInChannelsResponse = await result.json()\n setResult(resultJson)\n } else {\n const resultText = await result.text()\n setResult(resultText)\n }\n }\n void verify()\n }, [serverURL])\n\n useEffect(() => {\n const channels = result?.optInChannels?.map((channel: OptInChannel) => ({\n ...channel,\n isChecked: selectedOptInChannelIDs?.includes(channel.id),\n }))\n setAllOptInChannels(channels)\n }, [result, selectedOptInChannelIDs])\n\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n <h3>Opt-in Channels</h3>\n {!result ? (\n <p className={mergeClassNames([styles.loading, classNames.loading])}>verifying...</p>\n ) : (\n <div className={mergeClassNames([styles.optionsGroup, classNames.optionsGroup])}>\n {// Map over the tasks array to render each checkbox\n allOptInChannels?.map((channel) => (\n <div\n className={mergeClassNames([styles.optInCheckboxItem, classNames.optInCheckboxItem])}\n key={channel.id}\n >\n <label\n className={mergeClassNames([\n styles.optInCheckboxLabel,\n classNames.optInCheckboxLabel,\n ])}\n >\n <input\n aria-label={channel.title}\n // The checked prop is controlled by the state\n checked={channel.isChecked}\n className={mergeClassNames([styles.optInCheckbox, classNames.optInCheckbox])}\n // The onChange handler calls the update function with the item's ID\n onChange={(event) => {\n event.preventDefault()\n\n const checked = event.target.checked\n\n if (handleOptInChannelsSelected) {\n handleOptInChannelsSelected(\n allOptInChannels\n .map((channel) => ({\n ...channel,\n isChecked:\n channel.title == event.target.value ? checked : channel.isChecked,\n }))\n .filter((c) => c.isChecked)\n .map((channel) => ({ ...channel, isChecked: undefined })),\n )\n }\n }}\n type=\"checkbox\"\n value={channel.title}\n />\n {channel.title}\n </label>\n </div>\n ))}\n </div>\n )}\n </div>\n )\n}\n"],"names":["PayloadSDK","useEffect","useState","useServerUrl","mergeClassNames","styles","SelectOptInChannels","classNames","button","container","error","form","loading","message","optInCheckbox","optInCheckboxItem","optInCheckboxLabel","optionsGroup","handleOptInChannelsSelected","selectedOptInChannelIDs","serverURL","result","setResult","allOptInChannels","setAllOptInChannels","verify","sdk","baseURL","console","log","request","method","path","ok","resultJson","json","resultText","text","channels","optInChannels","map","channel","isChecked","includes","id","div","className","h3","p","label","input","aria-label","title","checked","onChange","event","preventDefault","target","value","filter","c","undefined","type"],"mappings":"AAAA;;AAEA,SAASA,UAAU,QAAQ,kBAAiB;AAC5C,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAK3C,SAASC,YAAY,QAAQ,oCAAmC;AAChE,SAASC,eAAe,QAAQ,eAAc;AAC9C,OAAOC,YAAY,sBAAqB;AA4BxC,OAAO,MAAMC,sBAAsB,CAAC,EAClCC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,OAAO;IACPC,MAAM;IACNC,SAAS;IACTC,SAAS;IACTC,eAAe;IACfC,mBAAmB;IACnBC,oBAAoB;IACpBC,cAAc;AAChB,CAAC,EACDC,2BAA2B,EAC3BC,uBAAuB,EACF;IACrB,MAAM,EAAEC,SAAS,EAAE,GAAGjB;IAKtB,MAAM,CAACkB,QAAQC,UAAU,GAAGpB;IAC5B,MAAM,CAACqB,kBAAkBC,oBAAoB,GAAGtB,SAAiC,EAAE;IAEnFD,UAAU;QACR,eAAewB;YACb,MAAMC,MAAM,IAAI1B,WAAmB;gBACjC2B,SAASP,aAAa;YACxB;YAEAQ,QAAQC,GAAG,CAAC;YACZ,MAAMR,SAAS,MAAMK,IAAII,OAAO,CAAC;gBAC/BC,QAAQ;gBACRC,MAAM;YACR;YACA,IAAIX,OAAOY,EAAE,EAAE;gBACb,MAAMC,aAAuC,MAAMb,OAAOc,IAAI;gBAC9Db,UAAUY;YACZ,OAAO;gBACL,MAAME,aAAa,MAAMf,OAAOgB,IAAI;gBACpCf,UAAUc;YACZ;QACF;QACA,KAAKX;IACP,GAAG;QAACL;KAAU;IAEdnB,UAAU;QACR,MAAMqC,WAAWjB,QAAQkB,eAAeC,IAAI,CAACC,UAA2B,CAAA;gBACtE,GAAGA,OAAO;gBACVC,WAAWvB,yBAAyBwB,SAASF,QAAQG,EAAE;YACzD,CAAA;QACApB,oBAAoBc;IACtB,GAAG;QAACjB;QAAQF;KAAwB;IAEpC,qBACE,MAAC0B;QAAIC,WAAW1C,gBAAgB;YAACC,OAAOI,SAAS;YAAEF,WAAWE,SAAS;SAAC;;0BACtE,KAACsC;0BAAG;;YACH,CAAC1B,uBACA,KAAC2B;gBAAEF,WAAW1C,gBAAgB;oBAACC,OAAOO,OAAO;oBAAEL,WAAWK,OAAO;iBAAC;0BAAG;+BAErE,KAACiC;gBAAIC,WAAW1C,gBAAgB;oBAACC,OAAOY,YAAY;oBAAEV,WAAWU,YAAY;iBAAC;0BAE5EM,kBAAkBiB,IAAI,CAACC,wBACrB,KAACI;wBACCC,WAAW1C,gBAAgB;4BAACC,OAAOU,iBAAiB;4BAAER,WAAWQ,iBAAiB;yBAAC;kCAGnF,cAAA,MAACkC;4BACCH,WAAW1C,gBAAgB;gCACzBC,OAAOW,kBAAkB;gCACzBT,WAAWS,kBAAkB;6BAC9B;;8CAED,KAACkC;oCACCC,cAAYV,QAAQW,KAAK;oCACzB,8CAA8C;oCAC9CC,SAASZ,QAAQC,SAAS;oCAC1BI,WAAW1C,gBAAgB;wCAACC,OAAOS,aAAa;wCAAEP,WAAWO,aAAa;qCAAC;oCAC3E,oEAAoE;oCACpEwC,UAAU,CAACC;wCACTA,MAAMC,cAAc;wCAEpB,MAAMH,UAAUE,MAAME,MAAM,CAACJ,OAAO;wCAEpC,IAAInC,6BAA6B;4CAC/BA,4BACEK,iBACGiB,GAAG,CAAC,CAACC,UAAa,CAAA;oDACjB,GAAGA,OAAO;oDACVC,WACED,QAAQW,KAAK,IAAIG,MAAME,MAAM,CAACC,KAAK,GAAGL,UAAUZ,QAAQC,SAAS;gDACrE,CAAA,GACCiB,MAAM,CAAC,CAACC,IAAMA,EAAElB,SAAS,EACzBF,GAAG,CAAC,CAACC,UAAa,CAAA;oDAAE,GAAGA,OAAO;oDAAEC,WAAWmB;gDAAU,CAAA;wCAE5D;oCACF;oCACAC,MAAK;oCACLJ,OAAOjB,QAAQW,KAAK;;gCAErBX,QAAQW,KAAK;;;uBAnCXX,QAAQG,EAAE;;;;AA2C7B,EAAC"}
1
+ {"version":3,"sources":["../../../src/components/app/SelectOptInChannels.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { useEffect, useState } from 'react'\n\nimport type { Config, OptInChannel } from '../../copied/payload-types.js'\nimport type { GetOptInChannelsResponse } from '../../endpoints/getOptInChannels.js'\n\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\n/** Props for the SelectOptInChannels component. */\nexport interface ISelectOptInChannels {\n classNames?: SelectOptInChannelsClasses\n handleOptInChannelsSelected?: (result: OptInChannel[]) => void\n props?: any\n selectedOptInChannelIDs?: string[]\n}\n\n/** Optional CSS class overrides for SelectOptInChannels elements. */\nexport type SelectOptInChannelsClasses = {\n button?: string\n container?: string\n error?: string\n form?: string\n loading?: string\n message?: string\n optInCheckbox?: string\n optInCheckboxItem?: string\n optInCheckboxLabel?: string\n optionsGroup?: string\n}\n\n/**\n * Fetches active opt-in channels from GET /api/optinchannels and renders a list of checkboxes.\n * Reports selected channels via handleOptInChannelsSelected. Supports pre-selection via selectedOptInChannelIDs.\n *\n * @param props - See ISelectOptInChannels\n * @returns Section titled \"Opt-in Channels\" with checkboxes and loading/error state\n */\nexport const SelectOptInChannels = ({\n classNames = {\n button: '',\n container: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n optInCheckbox: '',\n optInCheckboxItem: '',\n optInCheckboxLabel: '',\n optionsGroup: '',\n },\n handleOptInChannelsSelected,\n selectedOptInChannelIDs,\n}: ISelectOptInChannels) => {\n const { serverURL } = useServerUrl()\n // const { serverURL } = { serverURL: 'http://localhost:3001' }\n type OptInChannelCheckbox = {\n isChecked: boolean\n } & OptInChannel\n const [result, setResult] = useState<any>()\n const [allOptInChannels, setAllOptInChannels] = useState<OptInChannelCheckbox[]>([])\n\n useEffect(() => {\n async function verify() {\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n console.log('calling optinchannels endpoint')\n const result = await sdk.request({\n method: 'GET',\n path: '/api/optinchannels',\n })\n if (result.ok) {\n const resultJson: GetOptInChannelsResponse = await result.json()\n setResult(resultJson)\n } else {\n const resultText = await result.text()\n setResult(resultText)\n }\n }\n void verify()\n }, [serverURL])\n\n useEffect(() => {\n const channels = result?.optInChannels?.map((channel: OptInChannel) => ({\n ...channel,\n isChecked: selectedOptInChannelIDs?.includes(channel.id),\n }))\n setAllOptInChannels(channels)\n }, [result, selectedOptInChannelIDs])\n\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n <h3>Opt-in Channels</h3>\n {!result ? (\n <p className={mergeClassNames([styles.loading, classNames.loading])}>verifying...</p>\n ) : (\n <div className={mergeClassNames([styles.optionsGroup, classNames.optionsGroup])}>\n {// Map over the tasks array to render each checkbox\n allOptInChannels?.map((channel) => (\n <div\n className={mergeClassNames([styles.optInCheckboxItem, classNames.optInCheckboxItem])}\n key={channel.id}\n >\n <label\n className={mergeClassNames([\n styles.optInCheckboxLabel,\n classNames.optInCheckboxLabel,\n ])}\n >\n <input\n aria-label={channel.title}\n // The checked prop is controlled by the state\n checked={channel.isChecked}\n className={mergeClassNames([styles.optInCheckbox, classNames.optInCheckbox])}\n // The onChange handler calls the update function with the item's ID\n onChange={(event) => {\n event.preventDefault()\n\n const checked = event.target.checked\n\n if (handleOptInChannelsSelected) {\n handleOptInChannelsSelected(\n allOptInChannels\n .map((channel) => ({\n ...channel,\n isChecked:\n channel.title == event.target.value ? checked : channel.isChecked,\n }))\n .filter((c) => c.isChecked)\n .map((channel) => ({ ...channel, isChecked: undefined })),\n )\n }\n }}\n type=\"checkbox\"\n value={channel.title}\n />\n {channel.title}\n </label>\n </div>\n ))}\n </div>\n )}\n </div>\n )\n}\n"],"names":["PayloadSDK","useEffect","useState","useServerUrl","mergeClassNames","styles","SelectOptInChannels","classNames","button","container","error","form","loading","message","optInCheckbox","optInCheckboxItem","optInCheckboxLabel","optionsGroup","handleOptInChannelsSelected","selectedOptInChannelIDs","serverURL","result","setResult","allOptInChannels","setAllOptInChannels","verify","sdk","baseURL","console","log","request","method","path","ok","resultJson","json","resultText","text","channels","optInChannels","map","channel","isChecked","includes","id","div","className","h3","p","label","input","aria-label","title","checked","onChange","event","preventDefault","target","value","filter","c","undefined","type"],"mappings":"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"}
@@ -1,10 +1,13 @@
1
1
  import type { SubscribeResponse } from '../../endpoints/subscribe.js';
2
2
  export { SubscribeResponse };
3
+ /** Props for the Subscribe component. */
3
4
  export interface ISubscribe {
4
5
  classNames?: SubscribeClasses;
5
6
  handleSubscribe?: (result: SubscribeResponse) => void;
6
7
  props?: any;
8
+ verifyUrl?: string | URL;
7
9
  }
10
+ /** Optional CSS class overrides for Subscribe elements. */
8
11
  export type SubscribeClasses = {
9
12
  button?: string;
10
13
  container?: string;
@@ -15,4 +18,12 @@ export type SubscribeClasses = {
15
18
  message?: string;
16
19
  section?: string;
17
20
  };
18
- export declare const Subscribe: ({ classNames, handleSubscribe, }: ISubscribe) => import("react").JSX.Element;
21
+ /**
22
+ * Subscribe/preferences form for authenticated subscribers. Shows SelectOptInChannels and an email
23
+ * input when not yet authenticated. Submits to POST /api/subscribe to update opt-ins or trigger
24
+ * verification email. Calls refreshSubscriber and handleSubscribe on success.
25
+ *
26
+ * @param props - See ISubscribe
27
+ * @returns Form with channel checkboxes, optional email field, "Save choices" button, and status message
28
+ */
29
+ export declare const Subscribe: ({ classNames, handleSubscribe, verifyUrl, }: ISubscribe) => import("react").JSX.Element;