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.
- package/README.md +234 -24
- package/dist/components/app/RequestMagicLink.d.ts +21 -4
- package/dist/components/app/RequestMagicLink.js +10 -42
- package/dist/components/app/RequestMagicLink.js.map +1 -1
- package/dist/components/app/RequestOrSubscribe.d.ts +21 -4
- package/dist/components/app/RequestOrSubscribe.js +5 -8
- package/dist/components/app/RequestOrSubscribe.js.map +1 -1
- package/dist/components/app/SelectOptInChannels.d.ts +26 -3
- package/dist/components/app/SelectOptInChannels.js +4 -1
- package/dist/components/app/SelectOptInChannels.js.map +1 -1
- package/dist/components/app/Subscribe.d.ts +26 -5
- package/dist/components/app/Subscribe.js +63 -81
- package/dist/components/app/Subscribe.js.map +1 -1
- package/dist/components/app/SubscriberMenu.d.ts +18 -6
- package/dist/components/app/SubscriberMenu.js +9 -8
- package/dist/components/app/SubscriberMenu.js.map +1 -1
- package/dist/components/app/Unsubscribe.d.ts +45 -0
- package/dist/components/app/Unsubscribe.js +91 -0
- package/dist/components/app/Unsubscribe.js.map +1 -0
- package/dist/components/app/VerifyMagicLink.d.ts +32 -14
- package/dist/components/app/VerifyMagicLink.js +60 -114
- package/dist/components/app/VerifyMagicLink.js.map +1 -1
- package/dist/contexts/SubscriberProvider.js +1 -1
- package/dist/contexts/SubscriberProvider.js.map +1 -1
- package/dist/endpoints/requestMagicLink.d.ts +6 -2
- package/dist/endpoints/requestMagicLink.js +26 -19
- package/dist/endpoints/requestMagicLink.js.map +1 -1
- package/dist/endpoints/subscribe.d.ts +6 -2
- package/dist/endpoints/subscribe.js +29 -13
- package/dist/endpoints/subscribe.js.map +1 -1
- package/dist/endpoints/unsubscribe.d.ts +21 -0
- package/dist/endpoints/unsubscribe.js +120 -0
- package/dist/endpoints/unsubscribe.js.map +1 -0
- package/dist/endpoints/verifyMagicLink.js +3 -2
- package/dist/endpoints/verifyMagicLink.js.map +1 -1
- package/dist/exports/ui.d.ts +2 -0
- package/dist/exports/ui.js +1 -0
- package/dist/exports/ui.js.map +1 -1
- package/dist/helpers/token.d.ts +3 -0
- package/dist/helpers/token.js +12 -2
- package/dist/helpers/token.js.map +1 -1
- package/dist/helpers/utilities.d.ts +1 -0
- package/dist/helpers/utilities.js +6 -0
- package/dist/helpers/utilities.js.map +1 -0
- package/dist/hooks/useRequestMagicLink.d.ts +35 -0
- package/dist/hooks/useRequestMagicLink.js +61 -0
- package/dist/hooks/useRequestMagicLink.js.map +1 -0
- package/dist/hooks/useSubscribe.d.ts +38 -0
- package/dist/hooks/useSubscribe.js +62 -0
- package/dist/hooks/useSubscribe.js.map +1 -0
- package/dist/hooks/useUnsubscribe.d.ts +43 -0
- package/dist/hooks/useUnsubscribe.js +86 -0
- package/dist/hooks/useUnsubscribe.js.map +1 -0
- package/dist/hooks/useVerifyMagicLink.d.ts +31 -0
- package/dist/hooks/useVerifyMagicLink.js +74 -0
- package/dist/hooks/useVerifyMagicLink.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +16 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
import type { OptInChannel } from '../../copied/payload-types.js';
|
|
2
|
-
/**
|
|
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
|
-
/**
|
|
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 -
|
|
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 -
|
|
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
|
|
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
|
-
/**
|
|
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
|
-
|
|
15
|
+
verifyData?: string;
|
|
9
16
|
}
|
|
10
|
-
/**
|
|
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 -
|
|
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,
|
|
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 {
|
|
6
|
-
import {
|
|
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 -
|
|
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,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
|
|
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__*/
|
|
68
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
118
69
|
className: mergeClassNames([
|
|
119
70
|
'subscribers-section',
|
|
120
71
|
styles.section,
|
|
121
72
|
classNames.section
|
|
122
73
|
]),
|
|
123
|
-
children:
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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__*/
|
|
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:
|
|
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
|
-
!!
|
|
152
|
+
(!!requestResult || !!subscribeResult) && /*#__PURE__*/ _jsx("p", {
|
|
171
153
|
className: mergeClassNames([
|
|
172
154
|
'subscribers-message',
|
|
173
155
|
styles.message,
|
|
174
156
|
classNames.message,
|
|
175
|
-
|
|
157
|
+
requestStatus == 'error' || subscribeStatus == 'error' ? [
|
|
176
158
|
'subscribers-error',
|
|
177
159
|
styles.error,
|
|
178
160
|
classNames.error
|
|
179
161
|
] : []
|
|
180
162
|
]),
|
|
181
|
-
children:
|
|
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
|
-
/**
|
|
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
|
-
|
|
9
|
+
subscribeURL?: string | URL;
|
|
5
10
|
}
|
|
6
|
-
/**
|
|
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.
|
|
18
|
-
* @returns Container with welcome text, subscribe link (if
|
|
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,
|
|
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.
|
|
12
|
-
* @returns Container with welcome text, subscribe link (if
|
|
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
|
-
},
|
|
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
|
-
|
|
43
|
+
subscribeURL && /*#__PURE__*/ _jsx("div", {
|
|
43
44
|
className: "subscribers-subs-link",
|
|
44
45
|
children: /*#__PURE__*/ _jsx("a", {
|
|
45
|
-
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
|
|
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;
|