payload-subscribers-plugin 0.0.8 → 0.0.10

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 (60) hide show
  1. package/README.md +234 -24
  2. package/dist/components/app/RequestMagicLink.d.ts +21 -4
  3. package/dist/components/app/RequestMagicLink.js +10 -42
  4. package/dist/components/app/RequestMagicLink.js.map +1 -1
  5. package/dist/components/app/RequestOrSubscribe.d.ts +21 -4
  6. package/dist/components/app/RequestOrSubscribe.js +5 -8
  7. package/dist/components/app/RequestOrSubscribe.js.map +1 -1
  8. package/dist/components/app/SelectOptInChannels.d.ts +26 -3
  9. package/dist/components/app/SelectOptInChannels.js +4 -1
  10. package/dist/components/app/SelectOptInChannels.js.map +1 -1
  11. package/dist/components/app/Subscribe.d.ts +26 -5
  12. package/dist/components/app/Subscribe.js +63 -81
  13. package/dist/components/app/Subscribe.js.map +1 -1
  14. package/dist/components/app/SubscriberMenu.d.ts +18 -6
  15. package/dist/components/app/SubscriberMenu.js +9 -8
  16. package/dist/components/app/SubscriberMenu.js.map +1 -1
  17. package/dist/components/app/Unsubscribe.d.ts +45 -0
  18. package/dist/components/app/Unsubscribe.js +91 -0
  19. package/dist/components/app/Unsubscribe.js.map +1 -0
  20. package/dist/components/app/VerifyMagicLink.d.ts +32 -14
  21. package/dist/components/app/VerifyMagicLink.js +60 -114
  22. package/dist/components/app/VerifyMagicLink.js.map +1 -1
  23. package/dist/contexts/SubscriberProvider.js +1 -1
  24. package/dist/contexts/SubscriberProvider.js.map +1 -1
  25. package/dist/endpoints/requestMagicLink.d.ts +6 -2
  26. package/dist/endpoints/requestMagicLink.js +26 -19
  27. package/dist/endpoints/requestMagicLink.js.map +1 -1
  28. package/dist/endpoints/subscribe.d.ts +6 -2
  29. package/dist/endpoints/subscribe.js +29 -13
  30. package/dist/endpoints/subscribe.js.map +1 -1
  31. package/dist/endpoints/unsubscribe.d.ts +21 -0
  32. package/dist/endpoints/unsubscribe.js +120 -0
  33. package/dist/endpoints/unsubscribe.js.map +1 -0
  34. package/dist/endpoints/verifyMagicLink.js +3 -2
  35. package/dist/endpoints/verifyMagicLink.js.map +1 -1
  36. package/dist/exports/ui.d.ts +2 -0
  37. package/dist/exports/ui.js +1 -0
  38. package/dist/exports/ui.js.map +1 -1
  39. package/dist/helpers/token.d.ts +3 -0
  40. package/dist/helpers/token.js +12 -2
  41. package/dist/helpers/token.js.map +1 -1
  42. package/dist/helpers/utilities.d.ts +1 -0
  43. package/dist/helpers/utilities.js +6 -0
  44. package/dist/helpers/utilities.js.map +1 -0
  45. package/dist/hooks/useRequestMagicLink.d.ts +35 -0
  46. package/dist/hooks/useRequestMagicLink.js +61 -0
  47. package/dist/hooks/useRequestMagicLink.js.map +1 -0
  48. package/dist/hooks/useSubscribe.d.ts +38 -0
  49. package/dist/hooks/useSubscribe.js +62 -0
  50. package/dist/hooks/useSubscribe.js.map +1 -0
  51. package/dist/hooks/useUnsubscribe.d.ts +43 -0
  52. package/dist/hooks/useUnsubscribe.js +86 -0
  53. package/dist/hooks/useUnsubscribe.js.map +1 -0
  54. package/dist/hooks/useVerifyMagicLink.d.ts +31 -0
  55. package/dist/hooks/useVerifyMagicLink.js +74 -0
  56. package/dist/hooks/useVerifyMagicLink.js.map +1 -0
  57. package/dist/index.d.ts +8 -0
  58. package/dist/index.js +16 -2
  59. package/dist/index.js.map +1 -1
  60. package/package.json +1 -1
@@ -1,12 +1,32 @@
1
1
  import type { OptInChannel } from '../../copied/payload-types.js';
2
- /** Props for the SelectOptInChannels component. */
2
+ /**
3
+ * Props for the SelectOptInChannels component.
4
+ *
5
+ * @property classNames - Optional CSS class overrides for the component elements
6
+ * @property handleOptInChannelsSelected - Callback with the list of selected opt-in channels
7
+ * @property props - Optional passthrough props (reserved for future use)
8
+ * @property selectedOptInChannelIDs - Optional channel IDs to pre-select
9
+ */
3
10
  export interface ISelectOptInChannels {
4
11
  classNames?: SelectOptInChannelsClasses;
5
12
  handleOptInChannelsSelected?: (result: OptInChannel[]) => void;
6
13
  props?: any;
7
14
  selectedOptInChannelIDs?: string[];
8
15
  }
9
- /** Optional CSS class overrides for SelectOptInChannels elements. */
16
+ /**
17
+ * Optional CSS class overrides for SelectOptInChannels elements.
18
+ *
19
+ * @property button - Class for buttons
20
+ * @property container - Class for the main container
21
+ * @property error - Class for error messages
22
+ * @property form - Class for the form
23
+ * @property loading - Class for loading state
24
+ * @property message - Class for message text
25
+ * @property optInCheckbox - Class for each checkbox input
26
+ * @property optInCheckboxItem - Class for each checkbox row/item
27
+ * @property optInCheckboxLabel - Class for checkbox labels
28
+ * @property optionsGroup - Class for the group wrapping all checkboxes
29
+ */
10
30
  export type SelectOptInChannelsClasses = {
11
31
  button?: string;
12
32
  container?: string;
@@ -23,7 +43,10 @@ export type SelectOptInChannelsClasses = {
23
43
  * Fetches active opt-in channels from GET /api/optinchannels and renders a list of checkboxes.
24
44
  * Reports selected channels via handleOptInChannelsSelected. Supports pre-selection via selectedOptInChannelIDs.
25
45
  *
26
- * @param props - See ISelectOptInChannels
46
+ * @param props - Component props (see ISelectOptInChannels)
47
+ * @param props.classNames - Optional class overrides for the component elements
48
+ * @param props.handleOptInChannelsSelected - Callback with the list of selected opt-in channels
49
+ * @param props.selectedOptInChannelIDs - Optional channel IDs to pre-select
27
50
  * @returns Section titled "Opt-in Channels" with checkboxes and loading/error state
28
51
  */
29
52
  export declare const SelectOptInChannels: ({ classNames, handleOptInChannelsSelected, selectedOptInChannelIDs, }: ISelectOptInChannels) => import("react").JSX.Element;
@@ -9,7 +9,10 @@ import styles from './shared.module.css';
9
9
  * Fetches active opt-in channels from GET /api/optinchannels and renders a list of checkboxes.
10
10
  * Reports selected channels via handleOptInChannelsSelected. Supports pre-selection via selectedOptInChannelIDs.
11
11
  *
12
- * @param props - See ISelectOptInChannels
12
+ * @param props - Component props (see ISelectOptInChannels)
13
+ * @param props.classNames - Optional class overrides for the component elements
14
+ * @param props.handleOptInChannelsSelected - Callback with the list of selected opt-in channels
15
+ * @param props.selectedOptInChannelIDs - Optional channel IDs to pre-select
13
16
  * @returns Section titled "Opt-in Channels" with checkboxes and loading/error state
14
17
  */ export const SelectOptInChannels = ({ classNames = {
15
18
  button: '',
@@ -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/** 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
+ {"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/**\n * Props for the SelectOptInChannels component.\n *\n * @property classNames - Optional CSS class overrides for the component elements\n * @property handleOptInChannelsSelected - Callback with the list of selected opt-in channels\n * @property props - Optional passthrough props (reserved for future use)\n * @property selectedOptInChannelIDs - Optional channel IDs to pre-select\n */\nexport interface ISelectOptInChannels {\n classNames?: SelectOptInChannelsClasses\n handleOptInChannelsSelected?: (result: OptInChannel[]) => void\n props?: any\n selectedOptInChannelIDs?: string[]\n}\n\n/**\n * Optional CSS class overrides for SelectOptInChannels elements.\n *\n * @property button - Class for buttons\n * @property container - Class for the main container\n * @property error - Class for error messages\n * @property form - Class for the form\n * @property loading - Class for loading state\n * @property message - Class for message text\n * @property optInCheckbox - Class for each checkbox input\n * @property optInCheckboxItem - Class for each checkbox row/item\n * @property optInCheckboxLabel - Class for checkbox labels\n * @property optionsGroup - Class for the group wrapping all checkboxes\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\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 - Component props (see ISelectOptInChannels)\n * @param props.classNames - Optional class overrides for the component elements\n * @param props.handleOptInChannelsSelected - Callback with the list of selected opt-in channels\n * @param props.selectedOptInChannelIDs - Optional channel IDs to pre-select\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,13 +1,31 @@
1
1
  import type { SubscribeResponse } from '../../endpoints/subscribe.js';
2
2
  export { SubscribeResponse };
3
- /** Props for the Subscribe component. */
3
+ /**
4
+ * Props for the Subscribe component.
5
+ *
6
+ * @property classNames - Optional CSS class overrides for the component elements
7
+ * @property handleSubscribe - Callback when subscription/opt-ins are updated
8
+ * @property props - Optional passthrough props (reserved for future use)
9
+ * @property verifyData - Optional data for verification (e.g. passed from magic-link URL)
10
+ */
4
11
  export interface ISubscribe {
5
12
  classNames?: SubscribeClasses;
6
13
  handleSubscribe?: (result: SubscribeResponse) => void;
7
14
  props?: any;
8
- verifyUrl?: string | URL;
15
+ verifyData?: string;
9
16
  }
10
- /** Optional CSS class overrides for Subscribe elements. */
17
+ /**
18
+ * Optional CSS class overrides for Subscribe elements.
19
+ *
20
+ * @property button - Class for buttons
21
+ * @property container - Class for the main container
22
+ * @property emailInput - Class for the email input field
23
+ * @property error - Class for error messages
24
+ * @property form - Class for the form
25
+ * @property loading - Class for loading state
26
+ * @property message - Class for status message text
27
+ * @property section - Class for section wrappers
28
+ */
11
29
  export type SubscribeClasses = {
12
30
  button?: string;
13
31
  container?: string;
@@ -23,7 +41,10 @@ export type SubscribeClasses = {
23
41
  * input when not yet authenticated. Submits to POST /api/subscribe to update opt-ins or trigger
24
42
  * verification email. Calls refreshSubscriber and handleSubscribe on success.
25
43
  *
26
- * @param props - See ISubscribe
44
+ * @param props - Component props (see ISubscribe)
45
+ * @param props.classNames - Optional class overrides for the component elements
46
+ * @param props.handleSubscribe - Callback when subscription/opt-ins are updated
47
+ * @param props.verifyData - Optional data for verification (e.g. passed from magic-link URL)
27
48
  * @returns Form with channel checkboxes, optional email field, "Save choices" button, and status message
28
49
  */
29
- export declare const Subscribe: ({ classNames, handleSubscribe, verifyUrl, }: ISubscribe) => import("react").JSX.Element;
50
+ export declare const Subscribe: ({ classNames, handleSubscribe, verifyData, }: ISubscribe) => import("react").JSX.Element;
@@ -1,9 +1,9 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { PayloadSDK } from '@payloadcms/sdk';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
4
3
  import { useEffect, useState } from 'react';
5
- import { useSubscriber } from '../../contexts/SubscriberProvider.js';
6
- import { useServerUrl } from '../../react-hooks/useServerUrl.js';
4
+ import { useRequestMagicLink } from '../../hooks/useRequestMagicLink.js';
5
+ import { useSubscribe } from '../../hooks/useSubscribe.js';
6
+ import { useUnsubscribe } from '../../hooks/useUnsubscribe.js';
7
7
  import { mergeClassNames } from './helpers.js';
8
8
  import { SelectOptInChannels } from './SelectOptInChannels.js';
9
9
  import styles from './shared.module.css';
@@ -12,7 +12,10 @@ import styles from './shared.module.css';
12
12
  * input when not yet authenticated. Submits to POST /api/subscribe to update opt-ins or trigger
13
13
  * verification email. Calls refreshSubscriber and handleSubscribe on success.
14
14
  *
15
- * @param props - See ISubscribe
15
+ * @param props - Component props (see ISubscribe)
16
+ * @param props.classNames - Optional class overrides for the component elements
17
+ * @param props.handleSubscribe - Callback when subscription/opt-ins are updated
18
+ * @param props.verifyData - Optional data for verification (e.g. passed from magic-link URL)
16
19
  * @returns Form with channel checkboxes, optional email field, "Save choices" button, and status message
17
20
  */ export const Subscribe = ({ classNames = {
18
21
  button: '',
@@ -23,14 +26,19 @@ import styles from './shared.module.css';
23
26
  loading: '',
24
27
  message: '',
25
28
  section: ''
26
- }, handleSubscribe, verifyUrl })=>{
27
- if (typeof verifyUrl == 'string') {
28
- verifyUrl = new URL(verifyUrl);
29
- }
30
- const { refreshSubscriber, subscriber } = useSubscriber();
31
- const { serverURL } = useServerUrl();
32
- const sdk = new PayloadSDK({
33
- baseURL: serverURL || ''
29
+ }, handleSubscribe, verifyData })=>{
30
+ const { unsubscribe } = useUnsubscribe({
31
+ handleUnsubscribe: ()=>{}
32
+ });
33
+ const [email, setEmail] = useState('');
34
+ const handleMagicLinkRequested = ()=>{};
35
+ const { result: requestResult, sendMagicLink, status: requestStatus } = useRequestMagicLink({
36
+ handleMagicLinkRequested,
37
+ verifyData
38
+ });
39
+ const { result: subscribeResult, status: subscribeStatus, subscriber, updateSubscriptions } = useSubscribe({
40
+ handleSubscribe,
41
+ verifyData
34
42
  });
35
43
  const flattenChannels = (channels)=>{
36
44
  if (!channels) {
@@ -38,12 +46,8 @@ import styles from './shared.module.css';
38
46
  }
39
47
  return channels.map((channel)=>typeof channel == 'string' ? channel : channel.id);
40
48
  };
41
- const [status, setStatus] = useState('default');
42
- const [result, setResult] = useState();
43
- const [email, setEmail] = useState(subscriber ? subscriber.email : '');
44
49
  const [selectedChannelIDs, setSelectedChannelIDs] = useState(()=>flattenChannels(subscriber?.optIns));
45
50
  useEffect(()=>{
46
- setEmail(subscriber?.email || '');
47
51
  setSelectedChannelIDs(flattenChannels(subscriber?.optIns));
48
52
  }, [
49
53
  subscriber
@@ -51,59 +55,6 @@ import styles from './shared.module.css';
51
55
  const handleOptInChannelsSelected = (result)=>{
52
56
  setSelectedChannelIDs(result.map((channel)=>channel.id));
53
57
  };
54
- const handleSubmit = async ()=>{
55
- const subscribeResult = await sdk.request({
56
- json: {
57
- email,
58
- optIns: selectedChannelIDs,
59
- verifyUrl: verifyUrl?.href
60
- },
61
- method: 'POST',
62
- path: '/api/subscribe'
63
- });
64
- if (subscribeResult.ok) {
65
- const resultJson = await subscribeResult.json();
66
- // // When subscriber optIns are updated...
67
- // | {
68
- // email: string
69
- // now: string
70
- // optIns: string[]
71
- // }
72
- // // When a verify link is emailed...
73
- // | {
74
- // emailResult: any
75
- // now: string
76
- // }
77
- // // When any error occurs...
78
- // | {
79
- // error: string
80
- // now: string
81
- // }
82
- // @ts-expect-error Silly type confusion
83
- const { emailResult, error } = resultJson;
84
- if (error) {
85
- setStatus('error');
86
- setResult(`An error occured. Please try again. \n ${error}`);
87
- } else if (emailResult) {
88
- setStatus('sent');
89
- setResult('An email has been sent containing your magic link.');
90
- } else if (email) {
91
- setStatus('updated');
92
- setResult(`You're subscriptions have been updated.`);
93
- } else {
94
- setStatus('error');
95
- setResult(`An error occured. Please try again. \nResult unknown`);
96
- }
97
- refreshSubscriber();
98
- if (handleSubscribe) {
99
- handleSubscribe(resultJson);
100
- }
101
- } else {
102
- // const resultText = await subscribeResult.text()
103
- setStatus('error');
104
- setResult(`An error occured. Please try again. \nResult unknown`);
105
- }
106
- };
107
58
  return /*#__PURE__*/ _jsxs("div", {
108
59
  className: mergeClassNames([
109
60
  'subscribers-subscribe subscribers-container',
@@ -114,16 +65,21 @@ import styles from './shared.module.css';
114
65
  /*#__PURE__*/ _jsx("h2", {
115
66
  children: "Subscribe"
116
67
  }),
117
- /*#__PURE__*/ _jsx("div", {
68
+ /*#__PURE__*/ _jsxs("div", {
118
69
  className: mergeClassNames([
119
70
  'subscribers-section',
120
71
  styles.section,
121
72
  classNames.section
122
73
  ]),
123
- children: /*#__PURE__*/ _jsx(SelectOptInChannels, {
124
- handleOptInChannelsSelected: handleOptInChannelsSelected,
125
- selectedOptInChannelIDs: selectedChannelIDs
126
- })
74
+ children: [
75
+ subscriber?.status == 'unsubscribed' && /*#__PURE__*/ _jsx("p", {
76
+ children: "You are unsubscribed"
77
+ }),
78
+ /*#__PURE__*/ _jsx(SelectOptInChannels, {
79
+ handleOptInChannelsSelected: handleOptInChannelsSelected,
80
+ selectedOptInChannelIDs: selectedChannelIDs
81
+ })
82
+ ]
127
83
  }),
128
84
  /*#__PURE__*/ _jsx("form", {
129
85
  className: mergeClassNames([
@@ -134,7 +90,11 @@ import styles from './shared.module.css';
134
90
  method: "POST",
135
91
  onSubmit: async (e)=>{
136
92
  e.preventDefault();
137
- await handleSubmit();
93
+ if (subscriber) {
94
+ await updateSubscriptions(selectedChannelIDs);
95
+ } else {
96
+ await sendMagicLink(email);
97
+ }
138
98
  },
139
99
  children: /*#__PURE__*/ _jsxs("div", {
140
100
  className: mergeClassNames([
@@ -155,30 +115,52 @@ import styles from './shared.module.css';
155
115
  type: "email",
156
116
  value: email
157
117
  }),
158
- /*#__PURE__*/ _jsx("button", {
118
+ /*#__PURE__*/ _jsxs("button", {
159
119
  className: mergeClassNames([
160
120
  'subscribers-button',
161
121
  styles.button,
162
122
  classNames.button
163
123
  ]),
164
124
  type: "submit",
165
- children: "Save choices"
125
+ children: [
126
+ !subscriber && /*#__PURE__*/ _jsx(_Fragment, {
127
+ children: "Subscribe"
128
+ }),
129
+ subscriber && subscriber?.status != 'unsubscribed' && /*#__PURE__*/ _jsx(_Fragment, {
130
+ children: "Save choices"
131
+ }),
132
+ subscriber?.status == 'unsubscribed' && /*#__PURE__*/ _jsx(_Fragment, {
133
+ children: "Subscribe and save choices"
134
+ })
135
+ ]
136
+ }),
137
+ subscriber && subscriber?.status != 'unsubscribed' && /*#__PURE__*/ _jsx("button", {
138
+ className: mergeClassNames([
139
+ 'subscribers-button',
140
+ styles.button,
141
+ classNames.button
142
+ ]),
143
+ onClick: async ()=>{
144
+ await unsubscribe();
145
+ },
146
+ type: "button",
147
+ children: "Unsubscribe from all"
166
148
  })
167
149
  ]
168
150
  })
169
151
  }),
170
- !!result && /*#__PURE__*/ _jsx("p", {
152
+ (!!requestResult || !!subscribeResult) && /*#__PURE__*/ _jsx("p", {
171
153
  className: mergeClassNames([
172
154
  'subscribers-message',
173
155
  styles.message,
174
156
  classNames.message,
175
- status == 'error' ? [
157
+ requestStatus == 'error' || subscribeStatus == 'error' ? [
176
158
  'subscribers-error',
177
159
  styles.error,
178
160
  classNames.error
179
161
  ] : []
180
162
  ]),
181
- children: result
163
+ children: requestResult || subscribeResult
182
164
  })
183
165
  ]
184
166
  });
@@ -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/** 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
+ {"version":3,"sources":["../../../src/components/app/Subscribe.tsx"],"sourcesContent":["'use client'\n\nimport { type ChangeEvent, useEffect, useState } from 'react'\n\nimport type { OptInChannel, Subscriber } from '../../copied/payload-types.js'\nimport type { SubscribeResponse } from '../../endpoints/subscribe.js'\n\nexport { SubscribeResponse }\n\nimport type { RequestMagicLinkStatusValue } from '../../hooks/useRequestMagicLink.js'\nimport type { UpdateSubscriptionStatusValue } from '../../hooks/useSubscribe.js'\n\nimport { useRequestMagicLink } from '../../hooks/useRequestMagicLink.js'\nimport { useSubscribe } from '../../hooks/useSubscribe.js'\nimport { useUnsubscribe } from '../../hooks/useUnsubscribe.js'\nimport { mergeClassNames } from './helpers.js'\nimport { SelectOptInChannels } from './SelectOptInChannels.js'\nimport styles from './shared.module.css'\n\n/**\n * Props for the Subscribe component.\n *\n * @property classNames - Optional CSS class overrides for the component elements\n * @property handleSubscribe - Callback when subscription/opt-ins are updated\n * @property props - Optional passthrough props (reserved for future use)\n * @property verifyData - Optional data for verification (e.g. passed from magic-link URL)\n */\nexport interface ISubscribe {\n classNames?: SubscribeClasses\n handleSubscribe?: (result: SubscribeResponse) => void\n props?: any\n verifyData?: string\n}\n\n/**\n * Optional CSS class overrides for Subscribe elements.\n *\n * @property button - Class for buttons\n * @property container - Class for the main container\n * @property emailInput - Class for the email input field\n * @property error - Class for error messages\n * @property form - Class for the form\n * @property loading - Class for loading state\n * @property message - Class for status message text\n * @property section - Class for section wrappers\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\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 - Component props (see ISubscribe)\n * @param props.classNames - Optional class overrides for the component elements\n * @param props.handleSubscribe - Callback when subscription/opt-ins are updated\n * @param props.verifyData - Optional data for verification (e.g. passed from magic-link URL)\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 verifyData,\n}: ISubscribe) => {\n const { unsubscribe } = useUnsubscribe({ handleUnsubscribe: () => {} })\n\n const [email, setEmail] = useState<string>('')\n\n const handleMagicLinkRequested = () => {}\n const {\n result: requestResult,\n sendMagicLink,\n status: requestStatus,\n } = useRequestMagicLink({\n handleMagicLinkRequested,\n verifyData,\n })\n const {\n result: subscribeResult,\n status: subscribeStatus,\n subscriber,\n updateSubscriptions,\n } = useSubscribe({\n handleSubscribe,\n verifyData,\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 [selectedChannelIDs, setSelectedChannelIDs] = useState<string[]>(() =>\n flattenChannels(subscriber?.optIns),\n )\n\n useEffect(() => {\n setSelectedChannelIDs(flattenChannels(subscriber?.optIns))\n }, [subscriber])\n\n const handleOptInChannelsSelected = (result: OptInChannel[]) => {\n setSelectedChannelIDs(result.map((channel) => channel.id))\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 {subscriber?.status == 'unsubscribed' && <p>You are unsubscribed</p>}\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 if (subscriber) {\n await updateSubscriptions(selectedChannelIDs)\n } else {\n await sendMagicLink(email)\n }\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 {!subscriber && <>Subscribe</>}\n {subscriber && subscriber?.status != 'unsubscribed' && <>Save choices</>}\n {subscriber?.status == 'unsubscribed' && <>Subscribe and save choices</>}\n </button>\n {subscriber && subscriber?.status != 'unsubscribed' && (\n <button\n className={mergeClassNames(['subscribers-button', styles.button, classNames.button])}\n onClick={async () => {\n await unsubscribe()\n }}\n type=\"button\"\n >\n Unsubscribe from all\n </button>\n )}\n </div>\n </form>\n {(!!requestResult || !!subscribeResult) && (\n <p\n className={mergeClassNames([\n 'subscribers-message',\n styles.message,\n classNames.message,\n requestStatus == 'error' || subscribeStatus == 'error'\n ? ['subscribers-error', styles.error, classNames.error]\n : [],\n ])}\n >\n {requestResult || subscribeResult}\n </p>\n )}\n </div>\n )\n}\n"],"names":["useEffect","useState","useRequestMagicLink","useSubscribe","useUnsubscribe","mergeClassNames","SelectOptInChannels","styles","Subscribe","classNames","button","container","emailInput","error","form","loading","message","section","handleSubscribe","verifyData","unsubscribe","handleUnsubscribe","email","setEmail","handleMagicLinkRequested","result","requestResult","sendMagicLink","status","requestStatus","subscribeResult","subscribeStatus","subscriber","updateSubscriptions","flattenChannels","channels","map","channel","id","selectedChannelIDs","setSelectedChannelIDs","optIns","handleOptInChannelsSelected","div","className","h2","p","selectedOptInChannelIDs","method","onSubmit","e","preventDefault","input","aria-label","onChange","target","value","placeholder","type","onClick"],"mappings":"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"}
@@ -1,9 +1,20 @@
1
- /** Props for the VerifyMagicLink component. */
1
+ /**
2
+ * Props for the SubscriberMenu component.
3
+ *
4
+ * @property classNames - Optional CSS class overrides for the component elements
5
+ * @property subscribeURL - If set, shows a "Manage subscriptions" link to this URL (string or URL)
6
+ */
2
7
  export interface ISubscriberMenu {
3
8
  classNames?: SubscriberMenuClasses;
4
- subscribeUrl?: string | URL;
9
+ subscribeURL?: string | URL;
5
10
  }
6
- /** Optional CSS class overrides for SubscriberMenu elements. */
11
+ /**
12
+ * Optional CSS class overrides for SubscriberMenu elements.
13
+ *
14
+ * @property button - Class for the logout button
15
+ * @property container - Class for the main container
16
+ * @property group - Class for the inner group (welcome, link, button)
17
+ */
7
18
  export type SubscriberMenuClasses = {
8
19
  button?: string;
9
20
  container?: string;
@@ -13,8 +24,9 @@ export type SubscriberMenuClasses = {
13
24
  * Displays subscriber UI when authenticated: welcome message, optional "Manage subscriptions" link,
14
25
  * and a logout button. Renders nothing when no subscriber is in context.
15
26
  *
27
+ * @param props - Component props (see ISubscriberMenu)
16
28
  * @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
29
+ * @param props.subscribeURL - If set, shows a "Manage subscriptions" link to this URL
30
+ * @returns Container with welcome text, subscribe link (if subscribeURL), and Log out button, or null
19
31
  */
20
- export declare const SubscriberMenu: ({ classNames, subscribeUrl, }: ISubscriberMenu) => import("react").JSX.Element;
32
+ export declare const SubscriberMenu: ({ classNames, subscribeURL, }: ISubscriberMenu) => import("react").JSX.Element;
@@ -1,24 +1,25 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useSubscriber } from '../../contexts/SubscriberProvider.js';
4
+ import { isAbsoluteURL } from '../../helpers/utilities.js';
4
5
  import { mergeClassNames } from './helpers.js';
5
6
  import styles from './shared.module.css';
6
7
  /**
7
8
  * Displays subscriber UI when authenticated: welcome message, optional "Manage subscriptions" link,
8
9
  * and a logout button. Renders nothing when no subscriber is in context.
9
10
  *
11
+ * @param props - Component props (see ISubscriberMenu)
10
12
  * @param props.classNames - Optional class overrides for container, group, and button
11
- * @param props.subscribeUrl - If set, shows a "Manage subscriptions" link to this URL
12
- * @returns Container with welcome text, subscribe link (if subscribeUrl), and Log out button, or null
13
+ * @param props.subscribeURL - If set, shows a "Manage subscriptions" link to this URL
14
+ * @returns Container with welcome text, subscribe link (if subscribeURL), and Log out button, or null
13
15
  */ export const SubscriberMenu = ({ classNames = {
14
16
  button: '',
15
17
  container: '',
16
18
  group: ''
17
- }, subscribeUrl })=>{
19
+ }, subscribeURL })=>{
20
+ // Get a URL object from the subscribeURL option
21
+ subscribeURL = !subscribeURL ? undefined : typeof subscribeURL == 'string' && isAbsoluteURL(subscribeURL) ? new URL(subscribeURL) : window.location ? new URL(subscribeURL, window.location.protocol + window.location.host) : undefined;
18
22
  const { logOut, subscriber } = useSubscriber();
19
- if (typeof subscribeUrl == 'string') {
20
- subscribeUrl = new URL(subscribeUrl);
21
- }
22
23
  return /*#__PURE__*/ _jsx("div", {
23
24
  className: mergeClassNames([
24
25
  'subscribers-menu subscribers-container',
@@ -39,10 +40,10 @@ import styles from './shared.module.css';
39
40
  subscriber?.email
40
41
  ]
41
42
  }),
42
- subscribeUrl && /*#__PURE__*/ _jsx("div", {
43
+ subscribeURL && /*#__PURE__*/ _jsx("div", {
43
44
  className: "subscribers-subs-link",
44
45
  children: /*#__PURE__*/ _jsx("a", {
45
- href: subscribeUrl.href,
46
+ href: subscribeURL.href,
46
47
  children: "Manage subscriptions"
47
48
  })
48
49
  }),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/app/SubscriberMenu.tsx"],"sourcesContent":["'use client'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\n/** Props for the VerifyMagicLink component. */\nexport interface ISubscriberMenu {\n classNames?: SubscriberMenuClasses\n subscribeUrl?: string | URL\n}\n\n/** Optional CSS class overrides for SubscriberMenu elements. */\nexport type SubscriberMenuClasses = {\n button?: string\n container?: string\n group?: string\n}\n\n/**\n * Displays subscriber UI when authenticated: welcome message, optional \"Manage subscriptions\" link,\n * and a logout button. Renders nothing when no subscriber is in context.\n *\n * @param props.classNames - Optional class overrides for container, group, and button\n * @param props.subscribeUrl - If set, shows a \"Manage subscriptions\" link to this URL\n * @returns Container with welcome text, subscribe link (if subscribeUrl), and Log out button, or null\n */\nexport const SubscriberMenu = ({\n classNames = {\n button: '',\n container: '',\n group: '',\n },\n subscribeUrl,\n}: ISubscriberMenu) => {\n const { logOut, subscriber } = useSubscriber()\n if (typeof subscribeUrl == 'string') {\n subscribeUrl = new URL(subscribeUrl)\n }\n return (\n <div\n className={mergeClassNames([\n 'subscribers-menu subscribers-container',\n styles.container,\n classNames.container,\n ])}\n >\n {/* <pre>{JSON.stringify(result, null, 2)}</pre> */}\n {subscriber && (\n <div className={mergeClassNames(['subscribers-group', styles.group, classNames.group])}>\n <div className=\"subscribers-welcome\">Welcome, {subscriber?.email}</div>\n {subscribeUrl && (\n <div className=\"subscribers-subs-link\">\n <a href={subscribeUrl.href}>Manage subscriptions</a>\n </div>\n )}\n <div className=\"subscribers-logout\">\n <button\n className={mergeClassNames(['subscribers-button', styles.button, classNames.button])}\n onClick={(e) => {\n e.preventDefault()\n logOut()\n }}\n type=\"button\"\n >\n Log out\n </button>\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"names":["useSubscriber","mergeClassNames","styles","SubscriberMenu","classNames","button","container","group","subscribeUrl","logOut","subscriber","URL","div","className","email","a","href","onClick","e","preventDefault","type"],"mappings":"AAAA;;AAEA,SAASA,aAAa,QAAQ,uCAAsC;AACpE,SAASC,eAAe,QAAQ,eAAc;AAC9C,OAAOC,YAAY,sBAAqB;AAexC;;;;;;;CAOC,GACD,OAAO,MAAMC,iBAAiB,CAAC,EAC7BC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,OAAO;AACT,CAAC,EACDC,YAAY,EACI;IAChB,MAAM,EAAEC,MAAM,EAAEC,UAAU,EAAE,GAAGV;IAC/B,IAAI,OAAOQ,gBAAgB,UAAU;QACnCA,eAAe,IAAIG,IAAIH;IACzB;IACA,qBACE,KAACI;QACCC,WAAWZ,gBAAgB;YACzB;YACAC,OAAOI,SAAS;YAChBF,WAAWE,SAAS;SACrB;kBAGAI,4BACC,MAACE;YAAIC,WAAWZ,gBAAgB;gBAAC;gBAAqBC,OAAOK,KAAK;gBAAEH,WAAWG,KAAK;aAAC;;8BACnF,MAACK;oBAAIC,WAAU;;wBAAsB;wBAAUH,YAAYI;;;gBAC1DN,8BACC,KAACI;oBAAIC,WAAU;8BACb,cAAA,KAACE;wBAAEC,MAAMR,aAAaQ,IAAI;kCAAE;;;8BAGhC,KAACJ;oBAAIC,WAAU;8BACb,cAAA,KAACR;wBACCQ,WAAWZ,gBAAgB;4BAAC;4BAAsBC,OAAOG,MAAM;4BAAED,WAAWC,MAAM;yBAAC;wBACnFY,SAAS,CAACC;4BACRA,EAAEC,cAAc;4BAChBV;wBACF;wBACAW,MAAK;kCACN;;;;;;AAQb,EAAC"}
1
+ {"version":3,"sources":["../../../src/components/app/SubscriberMenu.tsx"],"sourcesContent":["'use client'\n\nimport type { Subscriber } from '../../copied/payload-types.js'\n\nimport { useSubscriber } from '../../contexts/SubscriberProvider.js'\nimport { isAbsoluteURL } from '../../helpers/utilities.js'\nimport { mergeClassNames } from './helpers.js'\nimport styles from './shared.module.css'\n\n/**\n * Props for the SubscriberMenu component.\n *\n * @property classNames - Optional CSS class overrides for the component elements\n * @property subscribeURL - If set, shows a \"Manage subscriptions\" link to this URL (string or URL)\n */\nexport interface ISubscriberMenu {\n classNames?: SubscriberMenuClasses\n subscribeURL?: string | URL\n}\n\n/**\n * Optional CSS class overrides for SubscriberMenu elements.\n *\n * @property button - Class for the logout button\n * @property container - Class for the main container\n * @property group - Class for the inner group (welcome, link, button)\n */\nexport type SubscriberMenuClasses = {\n button?: string\n container?: string\n group?: string\n}\n\n/**\n * Displays subscriber UI when authenticated: welcome message, optional \"Manage subscriptions\" link,\n * and a logout button. Renders nothing when no subscriber is in context.\n *\n * @param props - Component props (see ISubscriberMenu)\n * @param props.classNames - Optional class overrides for container, group, and button\n * @param props.subscribeURL - If set, shows a \"Manage subscriptions\" link to this URL\n * @returns Container with welcome text, subscribe link (if subscribeURL), and Log out button, or null\n */\nexport const SubscriberMenu = ({\n classNames = {\n button: '',\n container: '',\n group: '',\n },\n subscribeURL,\n}: ISubscriberMenu) => {\n // Get a URL object from the subscribeURL option\n subscribeURL = !subscribeURL\n ? undefined\n : typeof subscribeURL == 'string' && isAbsoluteURL(subscribeURL)\n ? new URL(subscribeURL)\n : window.location\n ? new URL(subscribeURL, window.location.protocol + window.location.host)\n : undefined\n\n const { logOut, subscriber } = useSubscriber()\n\n return (\n <div\n className={mergeClassNames([\n 'subscribers-menu subscribers-container',\n styles.container,\n classNames.container,\n ])}\n >\n {/* <pre>{JSON.stringify(result, null, 2)}</pre> */}\n {subscriber && (\n <div className={mergeClassNames(['subscribers-group', styles.group, classNames.group])}>\n <div className=\"subscribers-welcome\">Welcome, {subscriber?.email}</div>\n {subscribeURL && (\n <div className=\"subscribers-subs-link\">\n <a href={subscribeURL.href}>Manage subscriptions</a>\n </div>\n )}\n <div className=\"subscribers-logout\">\n <button\n className={mergeClassNames(['subscribers-button', styles.button, classNames.button])}\n onClick={(e) => {\n e.preventDefault()\n logOut()\n }}\n type=\"button\"\n >\n Log out\n </button>\n </div>\n </div>\n )}\n </div>\n )\n}\n"],"names":["useSubscriber","isAbsoluteURL","mergeClassNames","styles","SubscriberMenu","classNames","button","container","group","subscribeURL","undefined","URL","window","location","protocol","host","logOut","subscriber","div","className","email","a","href","onClick","e","preventDefault","type"],"mappings":"AAAA;;AAIA,SAASA,aAAa,QAAQ,uCAAsC;AACpE,SAASC,aAAa,QAAQ,6BAA4B;AAC1D,SAASC,eAAe,QAAQ,eAAc;AAC9C,OAAOC,YAAY,sBAAqB;AA0BxC;;;;;;;;CAQC,GACD,OAAO,MAAMC,iBAAiB,CAAC,EAC7BC,aAAa;IACXC,QAAQ;IACRC,WAAW;IACXC,OAAO;AACT,CAAC,EACDC,YAAY,EACI;IAChB,gDAAgD;IAChDA,eAAe,CAACA,eACZC,YACA,OAAOD,gBAAgB,YAAYR,cAAcQ,gBAC/C,IAAIE,IAAIF,gBACRG,OAAOC,QAAQ,GACb,IAAIF,IAAIF,cAAcG,OAAOC,QAAQ,CAACC,QAAQ,GAAGF,OAAOC,QAAQ,CAACE,IAAI,IACrEL;IAER,MAAM,EAAEM,MAAM,EAAEC,UAAU,EAAE,GAAGjB;IAE/B,qBACE,KAACkB;QACCC,WAAWjB,gBAAgB;YACzB;YACAC,OAAOI,SAAS;YAChBF,WAAWE,SAAS;SACrB;kBAGAU,4BACC,MAACC;YAAIC,WAAWjB,gBAAgB;gBAAC;gBAAqBC,OAAOK,KAAK;gBAAEH,WAAWG,KAAK;aAAC;;8BACnF,MAACU;oBAAIC,WAAU;;wBAAsB;wBAAUF,YAAYG;;;gBAC1DX,8BACC,KAACS;oBAAIC,WAAU;8BACb,cAAA,KAACE;wBAAEC,MAAMb,aAAaa,IAAI;kCAAE;;;8BAGhC,KAACJ;oBAAIC,WAAU;8BACb,cAAA,KAACb;wBACCa,WAAWjB,gBAAgB;4BAAC;4BAAsBC,OAAOG,MAAM;4BAAED,WAAWC,MAAM;yBAAC;wBACnFiB,SAAS,CAACC;4BACRA,EAAEC,cAAc;4BAChBT;wBACF;wBACAU,MAAK;kCACN;;;;;;AAQb,EAAC"}
@@ -0,0 +1,45 @@
1
+ import type { UnsubscribeResponse } from '../../endpoints/unsubscribe.js';
2
+ export { UnsubscribeResponse };
3
+ /**
4
+ * Props for the Unsubscribe component.
5
+ *
6
+ * @property children - Optional React nodes rendered after unsubscribe is attempted
7
+ * @property classNames - Optional CSS class overrides for the component elements
8
+ * @property handleUnsubscribe - Callback when unsubscribe is attempted (success or error)
9
+ */
10
+ export interface IUnsubscribe {
11
+ children?: React.ReactNode;
12
+ classNames?: UnsubscribeClasses;
13
+ handleUnsubscribe?: (result: UnsubscribeResponse) => void;
14
+ }
15
+ /**
16
+ * Optional CSS class overrides for Unsubscribe elements.
17
+ *
18
+ * @property button - Class for buttons
19
+ * @property container - Class for the main container
20
+ * @property emailInput - Class for the email input field
21
+ * @property error - Class for error messages
22
+ * @property form - Class for the form
23
+ * @property loading - Class for loading state
24
+ * @property message - Class for result message text
25
+ */
26
+ export type UnsubscribeClasses = {
27
+ button?: string;
28
+ container?: string;
29
+ emailInput?: string;
30
+ error?: string;
31
+ form?: string;
32
+ loading?: string;
33
+ message?: string;
34
+ };
35
+ /**
36
+ * Handles the unsubscribe action, for use with unsubscribe URLs in emails, etc.
37
+ * Uses URL params email and hash to call POST /api/unsubscribe. Displays children after attempt.
38
+ *
39
+ * @param props - Component props (see IUnsubscribe)
40
+ * @param props.children - Optional React nodes rendered after unsubscribe is attempted
41
+ * @param props.classNames - Optional class overrides for the component elements
42
+ * @param props.handleUnsubscribe - Callback when unsubscribe is attempted (success or error)
43
+ * @returns Loading status, result message, and children
44
+ */
45
+ export declare const Unsubscribe: ({ children, classNames, handleUnsubscribe, }: IUnsubscribe) => import("react").JSX.Element;