payload-subscribers-plugin 0.0.7 → 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 +79 -5
  2. package/dist/components/app/RequestMagicLink.d.ts +12 -1
  3. package/dist/components/app/RequestMagicLink.js +16 -1
  4. package/dist/components/app/RequestMagicLink.js.map +1 -1
  5. package/dist/components/app/RequestOrSubscribe.d.ts +21 -6
  6. package/dist/components/app/RequestOrSubscribe.js +13 -1
  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 +11 -1
  12. package/dist/components/app/Subscribe.js +19 -1
  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 +11 -1
  18. package/dist/components/app/VerifyMagicLink.js +17 -1
  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 +11 -13
  35. package/dist/endpoints/requestMagicLink.js.map +1 -1
  36. package/dist/endpoints/subscribe.d.ts +6 -5
  37. package/dist/endpoints/subscribe.js +12 -13
  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
@@ -263,6 +263,17 @@ Form to input email address and get a magic link email sent.
263
263
  />
264
264
  ```
265
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
+
266
277
  #### **VerifyMagicLink**
267
278
 
268
279
  Component that verifies a magic link using expected url parameters.
@@ -296,7 +307,7 @@ Component that verifies a magic link using expected url parameters.
296
307
  verifyUrl={verifyUrl}
297
308
  >
298
309
  // Provide children to render after link is verified. Optional
299
- // Since you provide the verifyUrl to any of the plugin components, you can include a forwardUrl
310
+ // Since you provide the verifyUrl to any of the plugin components, you can include a forwardUrl
300
311
  // as a search param, which your route can then use here.
301
312
  <a href={forwardUrl}>
302
313
  <button className={'customCss'} name={'continue'} type="button">
@@ -306,6 +317,18 @@ Component that verifies a magic link using expected url parameters.
306
317
  </VerifyMagicLink>
307
318
  ```
308
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>
330
+ ```
331
+
309
332
  #### **Subscribe**
310
333
 
311
334
  Allows a subscriber to select from among all active optInChannels.
@@ -340,6 +363,38 @@ Allows a subscriber to select from among all active optInChannels.
340
363
  />
341
364
  ```
342
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
+
343
398
  #### **SubscriberMenu**
344
399
 
345
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.
@@ -347,10 +402,29 @@ A simple user menu, most useful for testing. Seen in the screenshots above. Incl
347
402
  ```typescript
348
403
  // classNames prop
349
404
 
350
- export type SubscriberMenuClasses = {
351
- button?: string
352
- container?: string
353
- }
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>
354
428
  ```
355
429
 
356
430
  ## Contributing
@@ -1,11 +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;
7
- verifyUrl?: URL;
10
+ verifyUrl?: string | URL;
8
11
  }
12
+ /** Optional CSS class overrides for RequestMagicLink elements. */
9
13
  export type RequestMagicLinkClasses = {
10
14
  button?: string;
11
15
  container?: string;
@@ -14,4 +18,11 @@ export type RequestMagicLinkClasses = {
14
18
  form?: string;
15
19
  message?: string;
16
20
  };
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
+ */
17
28
  export declare const RequestMagicLink: ({ classNames, handleMagicLinkRequested, verifyUrl, }: IRequestMagicLink) => import("react").JSX.Element;
@@ -6,7 +6,13 @@ 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: '',
@@ -14,6 +20,9 @@ export const RequestMagicLink = ({ classNames = {
14
20
  form: '',
15
21
  message: ''
16
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');
@@ -62,15 +71,18 @@ export const RequestMagicLink = ({ classNames = {
62
71
  };
63
72
  return /*#__PURE__*/ _jsxs("div", {
64
73
  className: mergeClassNames([
74
+ 'subscribers-request subscribers-container',
65
75
  styles.container,
66
76
  classNames.container
67
77
  ]),
68
78
  children: [
69
79
  result ? /*#__PURE__*/ _jsx("p", {
70
80
  className: mergeClassNames([
81
+ 'subscribers-message',
71
82
  styles.message,
72
83
  classNames.message,
73
84
  status == 'error' ? [
85
+ 'subscribers-error',
74
86
  styles.error,
75
87
  classNames.error
76
88
  ] : []
@@ -79,6 +91,7 @@ export const RequestMagicLink = ({ classNames = {
79
91
  }) : /*#__PURE__*/ _jsx(_Fragment, {}),
80
92
  /*#__PURE__*/ _jsxs("form", {
81
93
  className: mergeClassNames([
94
+ 'subscribers-form',
82
95
  styles.form,
83
96
  classNames.form
84
97
  ]),
@@ -88,6 +101,7 @@ export const RequestMagicLink = ({ classNames = {
88
101
  /*#__PURE__*/ _jsx("input", {
89
102
  "aria-label": "enter your email",
90
103
  className: mergeClassNames([
104
+ 'subscribers-emailInput',
91
105
  styles.emailInput,
92
106
  classNames.emailInput
93
107
  ]),
@@ -98,6 +112,7 @@ export const RequestMagicLink = ({ classNames = {
98
112
  }),
99
113
  /*#__PURE__*/ _jsx("button", {
100
114
  className: mergeClassNames([
115
+ 'subscribers-button',
101
116
  styles.button,
102
117
  classNames.button
103
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 verifyUrl?: URL\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 verifyUrl,\n}: IRequestMagicLink) => {\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 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","verifyUrl","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":"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;AAsBxC,OAAO,MAAMC,mBAAmB,CAAC,EAC/BC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,YAAY;IACZC,OAAO;IACPC,MAAM;IACNC,SAAS;AACX,CAAC,EACDC,wBAAwB,EACxBC,SAAS,EACS;IAClB,MAAM,EAAEC,UAAU,EAAE,GAAGd;IACvB,MAAM,EAAEe,SAAS,EAAE,GAAGd;IACtB,MAAM,CAACe,QAAQC,UAAU,GAAGlB,SAAuB;IAEnD,MAAMmB,MAAM,IAAIrB,WAAmB;QACjCsB,SAASJ,aAAa;IACxB;IAEA,MAAM,CAACK,QAAQC,UAAU,GAAGtB;IAC5B,MAAM,CAACuB,OAAOC,SAAS,GAAGxB,SAASe,YAAYQ,SAAS;IAExDxB,UAAU;QACRyB,SAAST,YAAYQ,SAAS;IAChC,GAAG;QAACR;KAAW;IAEf,MAAMU,eAAe,OAAOC;QAC1BA,EAAEC,cAAc;QAChB,MAAMC,qBAAqB,MAAMT,IAAIU,OAAO,CAAC;YAC3CC,MAAM;gBACJP;gBACAT,WAAWA,WAAWiB;YACxB;YACAC,QAAQ;YACRC,MAAM;QACR;QACA,IAAIL,mBAAmBM,EAAE,EAAE;YACzB,MAAMC,yBAAmD,MAAMP,mBAAmBE,IAAI;YACtF,IAAIjB,0BAA0B;gBAC5BA,yBAAyBsB;YAC3B;YACA,2CAA2C;YAC3C,MAAM,EAAEC,WAAW,EAAE1B,KAAK,EAAE,GAAGyB;YAC/B,IAAIzB,OAAO;gBACTQ,UAAU;gBACVI,UAAU,CAAC,uCAAuC,EAAEZ,OAAO;YAC7D,OAAO,IAAI0B,aAAa;gBACtBlB,UAAU;gBACVI,UAAU;YACZ,OAAO;gBACLJ,UAAU;gBACVI,UAAU,CAAC,oDAAoD,CAAC;YAClE;QACF,OAAO;YACL,MAAMe,yBAAyB,MAAMT,mBAAmBU,IAAI;YAC5DpB,UAAU;YACVI,UAAU,CAAC,sCAAsC,EAAEe,wBAAwB;QAC7E;IACF;IAEA,qBACE,MAACE;QAAIC,WAAWrC,gBAAgB;YAACC,OAAOI,SAAS;YAAEF,WAAWE,SAAS;SAAC;;YACrEa,uBACC,KAACoB;gBACCD,WAAWrC,gBAAgB;oBACzBC,OAAOQ,OAAO;oBACdN,WAAWM,OAAO;oBAClBK,UAAU,UAAU;wBAACb,OAAOM,KAAK;wBAAEJ,WAAWI,KAAK;qBAAC,GAAG,EAAE;iBAC1D;0BAEAW;+BAGH;0BAEF,MAACV;gBACC6B,WAAWrC,gBAAgB;oBAACC,OAAOO,IAAI;oBAAEL,WAAWK,IAAI;iBAAC;gBACzDqB,QAAO;gBACPU,UAAUjB;;kCAEV,KAACkB;wBACCC,cAAW;wBACXJ,WAAWrC,gBAAgB;4BAACC,OAAOK,UAAU;4BAAEH,WAAWG,UAAU;yBAAC;wBACrEoC,UAAU,CAACnB,IAAqCF,SAASE,EAAEoB,MAAM,CAACC,KAAK;wBACvEC,aAAY;wBACZC,MAAK;wBACLF,OAAOxB;;kCAET,KAAChB;wBAAOiC,WAAWrC,gBAAgB;4BAACC,OAAOG,MAAM;4BAAED,WAAWC,MAAM;yBAAC;wBAAG0C,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,9 +20,14 @@ export type RequestOrSubscribeClasses = {
10
20
  message?: string;
11
21
  section?: string;
12
22
  };
13
- export declare function RequestOrSubscribe({ classNames, handleMagicLinkRequested, handleSubscribe, verifyUrl, }: {
14
- classNames?: RequestOrSubscribeClasses;
15
- handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void;
16
- handleSubscribe?: (result: SubscribeResponse) => void;
17
- verifyUrl?: URL;
18
- }): 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: '',
@@ -12,6 +21,9 @@ export function RequestOrSubscribe({ classNames = {
12
21
  message: '',
13
22
  section: ''
14
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, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/app/RequestOrSubscribe.tsx"],"sourcesContent":["'use client'\n\nimport { useEffect, useState } from 'react'\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 verifyUrl,\n}: {\n classNames?: RequestOrSubscribeClasses\n handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void\n handleSubscribe?: (result: SubscribeResponse) => void\n verifyUrl?: URL\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","subscriber"],"mappings":"AAAA;;AAIA,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,EACfC,SAAS,EAMV;IACC,MAAM,EAAEC,UAAU,EAAE,GAAGhB;IAEvB,wEAAwE;IACxE,qBACE;kBACGgB,2BACC,KAACd;YACCE,YAAYA;YACZU,iBAAiBA;YACjBC,WAAWA;2BAGb,KAACd;YACCG,YAAYA;YACZS,0BAA0BA;YAC1BE,WAAWA;;;AAMrB"}
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":"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"}
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,11 +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;
7
- verifyUrl?: URL;
8
+ verifyUrl?: string | URL;
8
9
  }
10
+ /** Optional CSS class overrides for Subscribe elements. */
9
11
  export type SubscribeClasses = {
10
12
  button?: string;
11
13
  container?: string;
@@ -16,4 +18,12 @@ export type SubscribeClasses = {
16
18
  message?: string;
17
19
  section?: string;
18
20
  };
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
+ */
19
29
  export declare const Subscribe: ({ classNames, handleSubscribe, verifyUrl, }: ISubscribe) => import("react").JSX.Element;
@@ -7,7 +7,14 @@ import { useServerUrl } from '../../react-hooks/useServerUrl.js';
7
7
  import { mergeClassNames } from './helpers.js';
8
8
  import { SelectOptInChannels } from './SelectOptInChannels.js';
9
9
  import styles from './shared.module.css';
10
- export const Subscribe = ({ classNames = {
10
+ /**
11
+ * Subscribe/preferences form for authenticated subscribers. Shows SelectOptInChannels and an email
12
+ * input when not yet authenticated. Submits to POST /api/subscribe to update opt-ins or trigger
13
+ * verification email. Calls refreshSubscriber and handleSubscribe on success.
14
+ *
15
+ * @param props - See ISubscribe
16
+ * @returns Form with channel checkboxes, optional email field, "Save choices" button, and status message
17
+ */ export const Subscribe = ({ classNames = {
11
18
  button: '',
12
19
  container: '',
13
20
  emailInput: '',
@@ -17,6 +24,9 @@ export const Subscribe = ({ classNames = {
17
24
  message: '',
18
25
  section: ''
19
26
  }, handleSubscribe, verifyUrl })=>{
27
+ if (typeof verifyUrl == 'string') {
28
+ verifyUrl = new URL(verifyUrl);
29
+ }
20
30
  const { refreshSubscriber, subscriber } = useSubscriber();
21
31
  const { serverURL } = useServerUrl();
22
32
  const sdk = new PayloadSDK({
@@ -96,6 +106,7 @@ export const Subscribe = ({ classNames = {
96
106
  };
97
107
  return /*#__PURE__*/ _jsxs("div", {
98
108
  className: mergeClassNames([
109
+ 'subscribers-subscribe subscribers-container',
99
110
  styles.container,
100
111
  classNames.container
101
112
  ]),
@@ -105,6 +116,7 @@ export const Subscribe = ({ classNames = {
105
116
  }),
106
117
  /*#__PURE__*/ _jsx("div", {
107
118
  className: mergeClassNames([
119
+ 'subscribers-section',
108
120
  styles.section,
109
121
  classNames.section
110
122
  ]),
@@ -115,6 +127,7 @@ export const Subscribe = ({ classNames = {
115
127
  }),
116
128
  /*#__PURE__*/ _jsx("form", {
117
129
  className: mergeClassNames([
130
+ 'subscribers-form',
118
131
  styles.form,
119
132
  classNames.form
120
133
  ]),
@@ -125,6 +138,7 @@ export const Subscribe = ({ classNames = {
125
138
  },
126
139
  children: /*#__PURE__*/ _jsxs("div", {
127
140
  className: mergeClassNames([
141
+ 'subscribers-section',
128
142
  styles.section,
129
143
  classNames.section
130
144
  ]),
@@ -132,6 +146,7 @@ export const Subscribe = ({ classNames = {
132
146
  !subscriber && /*#__PURE__*/ _jsx("input", {
133
147
  "aria-label": "enter your email",
134
148
  className: mergeClassNames([
149
+ 'subscribers-emailInput',
135
150
  styles.emailInput,
136
151
  classNames.emailInput
137
152
  ]),
@@ -142,6 +157,7 @@ export const Subscribe = ({ classNames = {
142
157
  }),
143
158
  /*#__PURE__*/ _jsx("button", {
144
159
  className: mergeClassNames([
160
+ 'subscribers-button',
145
161
  styles.button,
146
162
  classNames.button
147
163
  ]),
@@ -153,9 +169,11 @@ export const Subscribe = ({ classNames = {
153
169
  }),
154
170
  !!result && /*#__PURE__*/ _jsx("p", {
155
171
  className: mergeClassNames([
172
+ 'subscribers-message',
156
173
  styles.message,
157
174
  classNames.message,
158
175
  status == 'error' ? [
176
+ 'subscribers-error',
159
177
  styles.error,
160
178
  classNames.error
161
179
  ] : []
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/app/Subscribe.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ChangeEvent, useEffect, useState } from 'react'\n\nimport type { Config, OptInChannel } from '../../copied/payload-types.js'\nimport type { SubscribeResponse } from '../../endpoints/subscribe.js'\n\nexport { SubscribeResponse }\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport { SelectOptInChannels } from './SelectOptInChannels.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 ISubscribe {\n classNames?: SubscribeClasses\n handleSubscribe?: (result: SubscribeResponse) => void\n props?: any\n verifyUrl?: URL\n}\n\nexport type SubscribeClasses = {\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\ntype statusValues = 'default' | 'error' | 'sent' | 'updated'\n\nexport const Subscribe = ({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n section: '',\n },\n handleSubscribe,\n verifyUrl,\n}: ISubscribe) => {\n const { refreshSubscriber, subscriber } = useSubscriber()\n\n const { serverURL } = useServerUrl()\n\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n const flattenChannels = (channels: (OptInChannel | string)[] | null | undefined) => {\n if (!channels) {\n return []\n }\n return channels.map((channel: OptInChannel | string) =>\n typeof channel == 'string' ? channel : channel.id,\n )\n }\n\n const [status, setStatus] = useState<statusValues>('default')\n const [result, setResult] = useState<string>()\n const [email, setEmail] = useState(subscriber ? subscriber.email : '')\n const [selectedChannelIDs, setSelectedChannelIDs] = useState<string[]>(() =>\n flattenChannels(subscriber?.optIns),\n )\n\n useEffect(() => {\n setEmail(subscriber?.email || '')\n setSelectedChannelIDs(flattenChannels(subscriber?.optIns))\n }, [subscriber])\n\n const handleOptInChannelsSelected = (result: OptInChannel[]) => {\n setSelectedChannelIDs(result.map((channel) => channel.id))\n }\n\n const handleSubmit = async () => {\n const subscribeResult = await sdk.request({\n json: {\n email,\n optIns: selectedChannelIDs,\n verifyUrl: verifyUrl?.href,\n },\n method: 'POST',\n path: '/api/subscribe',\n })\n if (subscribeResult.ok) {\n const resultJson: SubscribeResponse = await subscribeResult.json()\n // // When subscriber optIns are updated...\n // | {\n // email: string\n // now: string\n // optIns: string[]\n // }\n // // When a verify link is emailed...\n // | {\n // emailResult: any\n // now: string\n // }\n // // When any error occurs...\n // | {\n // error: string\n // now: string\n // }\n // @ts-expect-error Silly type confusion\n const { emailResult, error } = resultJson\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 if (email) {\n setStatus('updated')\n setResult(`You're subscriptions have been updated.`)\n } else {\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n\n refreshSubscriber()\n\n if (handleSubscribe) {\n handleSubscribe(resultJson)\n }\n } else {\n // const resultText = await subscribeResult.text()\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n }\n\n return (\n <div className={mergeClassNames([styles.container, classNames.container])}>\n <h2>Subscribe</h2>\n <div className={mergeClassNames([styles.section, classNames.section])}>\n <SelectOptInChannels\n handleOptInChannelsSelected={handleOptInChannelsSelected}\n selectedOptInChannelIDs={selectedChannelIDs}\n />\n </div>\n <form\n className={mergeClassNames([styles.form, classNames.form])}\n method=\"POST\"\n onSubmit={async (e) => {\n e.preventDefault()\n await handleSubmit()\n }}\n >\n <div className={mergeClassNames([styles.section, classNames.section])}>\n {!subscriber && (\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 )}\n <button className={mergeClassNames([styles.button, classNames.button])} type=\"submit\">\n Save choices\n </button>\n </div>\n </form>\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 </div>\n )\n}\n"],"names":["PayloadSDK","useEffect","useState","useSubscriber","useServerUrl","mergeClassNames","SelectOptInChannels","styles","Subscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleSubscribe","verifyUrl","refreshSubscriber","subscriber","serverURL","sdk","baseURL","flattenChannels","channels","map","channel","id","status","setStatus","result","setResult","email","setEmail","selectedChannelIDs","setSelectedChannelIDs","optIns","handleOptInChannelsSelected","handleSubmit","subscribeResult","request","json","href","method","path","ok","resultJson","emailResult","div","className","h2","selectedOptInChannelIDs","onSubmit","e","preventDefault","input","aria-label","onChange","target","value","placeholder","type","p"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/app/Subscribe.tsx"],"sourcesContent":["'use client'\n\nimport { PayloadSDK } from '@payloadcms/sdk'\nimport { type ChangeEvent, useEffect, useState } from 'react'\n\nimport type { Config, OptInChannel } from '../../copied/payload-types.js'\nimport type { SubscribeResponse } from '../../endpoints/subscribe.js'\n\nexport { SubscribeResponse }\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { useServerUrl } from '../../react-hooks/useServerUrl.js'\nimport { mergeClassNames } from './helpers.js'\nimport { SelectOptInChannels } from './SelectOptInChannels.js'\nimport styles from './shared.module.css'\n\n/** Props for the Subscribe component. */\nexport interface ISubscribe {\n classNames?: SubscribeClasses\n handleSubscribe?: (result: SubscribeResponse) => void\n props?: any\n verifyUrl?: string | URL\n}\n\n/** Optional CSS class overrides for Subscribe elements. */\nexport type SubscribeClasses = {\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\ntype statusValues = 'default' | 'error' | 'sent' | 'updated'\n\n/**\n * Subscribe/preferences form for authenticated subscribers. Shows SelectOptInChannels and an email\n * input when not yet authenticated. Submits to POST /api/subscribe to update opt-ins or trigger\n * verification email. Calls refreshSubscriber and handleSubscribe on success.\n *\n * @param props - See ISubscribe\n * @returns Form with channel checkboxes, optional email field, \"Save choices\" button, and status message\n */\nexport const Subscribe = ({\n classNames = {\n button: '',\n container: '',\n emailInput: '',\n error: '',\n form: '',\n loading: '',\n message: '',\n section: '',\n },\n handleSubscribe,\n verifyUrl,\n}: ISubscribe) => {\n if (typeof verifyUrl == 'string') {\n verifyUrl = new URL(verifyUrl)\n }\n\n const { refreshSubscriber, subscriber } = useSubscriber()\n\n const { serverURL } = useServerUrl()\n\n const sdk = new PayloadSDK<Config>({\n baseURL: serverURL || '',\n })\n\n const flattenChannels = (channels: (OptInChannel | string)[] | null | undefined) => {\n if (!channels) {\n return []\n }\n return channels.map((channel: OptInChannel | string) =>\n typeof channel == 'string' ? channel : channel.id,\n )\n }\n\n const [status, setStatus] = useState<statusValues>('default')\n const [result, setResult] = useState<string>()\n const [email, setEmail] = useState(subscriber ? subscriber.email : '')\n const [selectedChannelIDs, setSelectedChannelIDs] = useState<string[]>(() =>\n flattenChannels(subscriber?.optIns),\n )\n\n useEffect(() => {\n setEmail(subscriber?.email || '')\n setSelectedChannelIDs(flattenChannels(subscriber?.optIns))\n }, [subscriber])\n\n const handleOptInChannelsSelected = (result: OptInChannel[]) => {\n setSelectedChannelIDs(result.map((channel) => channel.id))\n }\n\n const handleSubmit = async () => {\n const subscribeResult = await sdk.request({\n json: {\n email,\n optIns: selectedChannelIDs,\n verifyUrl: verifyUrl?.href,\n },\n method: 'POST',\n path: '/api/subscribe',\n })\n if (subscribeResult.ok) {\n const resultJson: SubscribeResponse = await subscribeResult.json()\n // // When subscriber optIns are updated...\n // | {\n // email: string\n // now: string\n // optIns: string[]\n // }\n // // When a verify link is emailed...\n // | {\n // emailResult: any\n // now: string\n // }\n // // When any error occurs...\n // | {\n // error: string\n // now: string\n // }\n // @ts-expect-error Silly type confusion\n const { emailResult, error } = resultJson\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 if (email) {\n setStatus('updated')\n setResult(`You're subscriptions have been updated.`)\n } else {\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n\n refreshSubscriber()\n\n if (handleSubscribe) {\n handleSubscribe(resultJson)\n }\n } else {\n // const resultText = await subscribeResult.text()\n setStatus('error')\n setResult(`An error occured. Please try again. \\nResult unknown`)\n }\n }\n\n return (\n <div\n className={mergeClassNames([\n 'subscribers-subscribe subscribers-container',\n styles.container,\n classNames.container,\n ])}\n >\n <h2>Subscribe</h2>\n <div className={mergeClassNames(['subscribers-section', styles.section, classNames.section])}>\n <SelectOptInChannels\n handleOptInChannelsSelected={handleOptInChannelsSelected}\n selectedOptInChannelIDs={selectedChannelIDs}\n />\n </div>\n <form\n className={mergeClassNames(['subscribers-form', styles.form, classNames.form])}\n method=\"POST\"\n onSubmit={async (e) => {\n e.preventDefault()\n await handleSubmit()\n }}\n >\n <div\n className={mergeClassNames(['subscribers-section', styles.section, classNames.section])}\n >\n {!subscriber && (\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 )}\n <button\n className={mergeClassNames(['subscribers-button', styles.button, classNames.button])}\n type=\"submit\"\n >\n Save choices\n </button>\n </div>\n </form>\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 </div>\n )\n}\n"],"names":["PayloadSDK","useEffect","useState","useSubscriber","useServerUrl","mergeClassNames","SelectOptInChannels","styles","Subscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleSubscribe","verifyUrl","URL","refreshSubscriber","subscriber","serverURL","sdk","baseURL","flattenChannels","channels","map","channel","id","status","setStatus","result","setResult","email","setEmail","selectedChannelIDs","setSelectedChannelIDs","optIns","handleOptInChannelsSelected","handleSubmit","subscribeResult","request","json","href","method","path","ok","resultJson","emailResult","div","className","h2","selectedOptInChannelIDs","onSubmit","e","preventDefault","input","aria-label","onChange","target","value","placeholder","type","p"],"mappings":"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"}
@@ -1,7 +1,20 @@
1
+ /** Props for the VerifyMagicLink component. */
2
+ export interface ISubscriberMenu {
3
+ classNames?: SubscriberMenuClasses;
4
+ subscribeUrl?: string | URL;
5
+ }
6
+ /** Optional CSS class overrides for SubscriberMenu elements. */
1
7
  export type SubscriberMenuClasses = {
2
8
  button?: string;
3
9
  container?: string;
10
+ group?: string;
4
11
  };
5
- export declare const SubscriberMenu: ({ classNames, }: {
6
- classNames?: SubscriberMenuClasses;
7
- }) => import("react").JSX.Element;
12
+ /**
13
+ * Displays subscriber UI when authenticated: welcome message, optional "Manage subscriptions" link,
14
+ * and a logout button. Renders nothing when no subscriber is in context.
15
+ *
16
+ * @param props.classNames - Optional class overrides for container, group, and button
17
+ * @param props.subscribeUrl - If set, shows a "Manage subscriptions" link to this URL
18
+ * @returns Container with welcome text, subscribe link (if subscribeUrl), and Log out button, or null
19
+ */
20
+ export declare const SubscriberMenu: ({ classNames, subscribeUrl, }: ISubscriberMenu) => import("react").JSX.Element;