payload-subscribers-plugin 0.0.1 → 0.0.4

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 (54) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +2 -3
  3. package/dist/components/BeforeDashboardClient.d.ts +0 -1
  4. package/dist/components/BeforeDashboardClient.js +0 -40
  5. package/dist/components/BeforeDashboardClient.js.map +0 -1
  6. package/dist/components/BeforeDashboardServer.d.ts +0 -2
  7. package/dist/components/BeforeDashboardServer.js +0 -22
  8. package/dist/components/BeforeDashboardServer.js.map +0 -1
  9. package/dist/components/BeforeDashboardServer.module.css +0 -5
  10. package/dist/components/app/RequestMagicLink.module.css +0 -5
  11. package/dist/components/app/SelectOptInChannels.module.css +0 -5
  12. package/dist/components/app/Subscribe.module.css +0 -5
  13. package/dist/components/app/VerifyMagicLink.module.css +0 -5
  14. package/dist/copied/payload.config.d.ts +0 -2
  15. package/dist/endpoints/customEndpointHandler.d.ts +0 -2
  16. package/dist/endpoints/customEndpointHandler.js +0 -7
  17. package/dist/endpoints/customEndpointHandler.js.map +0 -1
  18. package/dist/exports/client.d.ts +0 -1
  19. package/dist/exports/client.js +0 -3
  20. package/dist/exports/client.js.map +0 -1
  21. package/dist/exports/rsc.d.ts +0 -1
  22. package/dist/exports/rsc.js +0 -3
  23. package/dist/exports/rsc.js.map +0 -1
  24. package/dist/helpers/serverConfig.d.ts +0 -4
  25. package/dist/helpers/serverConfig.js +0 -22
  26. package/dist/helpers/serverConfig.js.map +0 -1
  27. package/dist/server-functions/subscriberAuth.d.ts +0 -11
  28. package/src/collections/OptInChannels.ts +0 -45
  29. package/src/collections/Subscribers.ts +0 -99
  30. package/src/collections/fields/OptedInChannels.ts +0 -12
  31. package/src/components/app/RequestMagicLink.tsx +0 -129
  32. package/src/components/app/RequestOrSubscribe.tsx +0 -58
  33. package/src/components/app/SelectOptInChannels.tsx +0 -147
  34. package/src/components/app/Subscribe.tsx +0 -190
  35. package/src/components/app/SubscriberMenu.tsx +0 -46
  36. package/src/components/app/VerifyMagicLink.tsx +0 -197
  37. package/src/components/app/helpers.ts +0 -6
  38. package/src/components/app/shared.module.css +0 -14
  39. package/src/contexts/SubscriberProvider.tsx +0 -122
  40. package/src/copied/payload-types.ts +0 -478
  41. package/src/endpoints/getOptInChannels.ts +0 -56
  42. package/src/endpoints/logout.ts +0 -104
  43. package/src/endpoints/requestMagicLink.ts +0 -139
  44. package/src/endpoints/subscribe.ts +0 -435
  45. package/src/endpoints/subscriberAuth.ts +0 -100
  46. package/src/endpoints/verifyMagicLink.ts +0 -164
  47. package/src/exports/index.ts +0 -1
  48. package/src/exports/ui.ts +0 -17
  49. package/src/helpers/testData.ts +0 -2
  50. package/src/helpers/token.ts +0 -14
  51. package/src/helpers/verifyOptIns.ts +0 -39
  52. package/src/index.ts +0 -207
  53. package/src/react-hooks/useServerUrl.tsx +0 -18
  54. package/src/server-functions/serverUrl.ts +0 -38
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 C C C
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json CHANGED
@@ -23,8 +23,7 @@
23
23
  "main": "./dist/index.js",
24
24
  "types": "./dist/index.d.ts",
25
25
  "files": [
26
- "dist",
27
- "src"
26
+ "dist/"
28
27
  ],
29
28
  "devDependencies": {
30
29
  "@eslint/eslintrc": "^3.3.3",
@@ -70,7 +69,7 @@
70
69
  },
71
70
  "registry": "https://registry.npmjs.org/",
72
71
  "dependencies": {},
73
- "version": "0.0.1",
72
+ "version": "0.0.4",
74
73
  "scripts": {
75
74
  "build": "pnpm copyfiles && pnpm build:types && pnpm build:swc",
76
75
  "build:swc": "swc ./src -d ./dist --config-file .swcrc --strip-leading-paths",
@@ -1 +0,0 @@
1
- export declare const BeforeDashboardClient: () => import("react").JSX.Element;
@@ -1,40 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useConfig } from '@payloadcms/ui';
4
- import { formatAdminURL } from 'payload/shared';
5
- import { useEffect, useState } from 'react';
6
- export const BeforeDashboardClient = ()=>{
7
- const { config } = useConfig();
8
- const [message, setMessage] = useState('');
9
- useEffect(()=>{
10
- const fetchMessage = async ()=>{
11
- const response = await fetch(formatAdminURL({
12
- adminRoute: config.routes.api,
13
- path: '/my-plugin-endpoint'
14
- }));
15
- const result = await response.json();
16
- setMessage(result.message);
17
- };
18
- void fetchMessage();
19
- }, [
20
- config.serverURL,
21
- config.routes.api
22
- ]);
23
- return /*#__PURE__*/ _jsxs("div", {
24
- children: [
25
- /*#__PURE__*/ _jsx("h1", {
26
- children: "Added by the plugin: Before Dashboard Client"
27
- }),
28
- /*#__PURE__*/ _jsxs("div", {
29
- children: [
30
- "Message from the endpoint:",
31
- /*#__PURE__*/ _jsx("div", {
32
- children: message || 'Loading...'
33
- })
34
- ]
35
- })
36
- ]
37
- });
38
- };
39
-
40
- //# sourceMappingURL=BeforeDashboardClient.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/components/BeforeDashboardClient.tsx"],"sourcesContent":["'use client'\nimport { useConfig } from '@payloadcms/ui'\nimport { formatAdminURL } from 'payload/shared'\nimport { useEffect, useState } from 'react'\n\nexport const BeforeDashboardClient = () => {\n const { config } = useConfig()\n\n const [message, setMessage] = useState('')\n\n useEffect(() => {\n const fetchMessage = async () => {\n const response = await fetch(\n formatAdminURL({\n adminRoute: config.routes.api,\n path: '/my-plugin-endpoint',\n }),\n )\n const result = await response.json()\n setMessage(result.message)\n }\n\n void fetchMessage()\n }, [config.serverURL, config.routes.api])\n\n return (\n <div>\n <h1>Added by the plugin: Before Dashboard Client</h1>\n <div>\n Message from the endpoint:\n <div>{message || 'Loading...'}</div>\n </div>\n </div>\n )\n}\n"],"names":["useConfig","formatAdminURL","useEffect","useState","BeforeDashboardClient","config","message","setMessage","fetchMessage","response","fetch","adminRoute","routes","api","path","result","json","serverURL","div","h1"],"mappings":"AAAA;;AACA,SAASA,SAAS,QAAQ,iBAAgB;AAC1C,SAASC,cAAc,QAAQ,iBAAgB;AAC/C,SAASC,SAAS,EAAEC,QAAQ,QAAQ,QAAO;AAE3C,OAAO,MAAMC,wBAAwB;IACnC,MAAM,EAAEC,MAAM,EAAE,GAAGL;IAEnB,MAAM,CAACM,SAASC,WAAW,GAAGJ,SAAS;IAEvCD,UAAU;QACR,MAAMM,eAAe;YACnB,MAAMC,WAAW,MAAMC,MACrBT,eAAe;gBACbU,YAAYN,OAAOO,MAAM,CAACC,GAAG;gBAC7BC,MAAM;YACR;YAEF,MAAMC,SAAS,MAAMN,SAASO,IAAI;YAClCT,WAAWQ,OAAOT,OAAO;QAC3B;QAEA,KAAKE;IACP,GAAG;QAACH,OAAOY,SAAS;QAAEZ,OAAOO,MAAM,CAACC,GAAG;KAAC;IAExC,qBACE,MAACK;;0BACC,KAACC;0BAAG;;0BACJ,MAACD;;oBAAI;kCAEH,KAACA;kCAAKZ,WAAW;;;;;;AAIzB,EAAC"}
@@ -1,2 +0,0 @@
1
- import type { ServerComponentProps } from 'payload';
2
- export declare const BeforeDashboardServer: (props: ServerComponentProps) => Promise<import("react").JSX.Element>;
@@ -1,22 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import styles from './BeforeDashboardServer.module.css';
3
- export const BeforeDashboardServer = async (props)=>{
4
- const { payload } = props;
5
- const { docs } = await payload.find({
6
- collection: 'subscribers'
7
- });
8
- return /*#__PURE__*/ _jsxs("div", {
9
- className: styles.wrapper,
10
- children: [
11
- /*#__PURE__*/ _jsx("h1", {
12
- children: "Added by the plugin: Before Dashboard Server"
13
- }),
14
- "Docs from Local API:",
15
- docs.map((doc)=>/*#__PURE__*/ _jsx("div", {
16
- children: doc.id
17
- }, doc.id))
18
- ]
19
- });
20
- };
21
-
22
- //# sourceMappingURL=BeforeDashboardServer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/components/BeforeDashboardServer.tsx"],"sourcesContent":["import type { ServerComponentProps } from 'payload'\n\nimport styles from './BeforeDashboardServer.module.css'\n\nexport const BeforeDashboardServer = async (props: ServerComponentProps) => {\n const { payload } = props\n\n const { docs } = await payload.find({ collection: 'subscribers' })\n\n return (\n <div className={styles.wrapper}>\n <h1>Added by the plugin: Before Dashboard Server</h1>\n Docs from Local API:\n {docs.map((doc) => (\n <div key={doc.id}>{doc.id}</div>\n ))}\n </div>\n )\n}\n"],"names":["styles","BeforeDashboardServer","props","payload","docs","find","collection","div","className","wrapper","h1","map","doc","id"],"mappings":";AAEA,OAAOA,YAAY,qCAAoC;AAEvD,OAAO,MAAMC,wBAAwB,OAAOC;IAC1C,MAAM,EAAEC,OAAO,EAAE,GAAGD;IAEpB,MAAM,EAAEE,IAAI,EAAE,GAAG,MAAMD,QAAQE,IAAI,CAAC;QAAEC,YAAY;IAAc;IAEhE,qBACE,MAACC;QAAIC,WAAWR,OAAOS,OAAO;;0BAC5B,KAACC;0BAAG;;YAAiD;YAEpDN,KAAKO,GAAG,CAAC,CAACC,oBACT,KAACL;8BAAkBK,IAAIC,EAAE;mBAAfD,IAAIC,EAAE;;;AAIxB,EAAC"}
@@ -1,5 +0,0 @@
1
- .wrapper {
2
- display: flex;
3
- gap: 5px;
4
- flex-direction: column;
5
- }
@@ -1,5 +0,0 @@
1
- .wrapper {
2
- display: flex;
3
- gap: 5px;
4
- flex-direction: column;
5
- }
@@ -1,5 +0,0 @@
1
- .wrapper {
2
- display: flex;
3
- gap: 5px;
4
- flex-direction: column;
5
- }
@@ -1,5 +0,0 @@
1
- .wrapper {
2
- display: flex;
3
- gap: 5px;
4
- flex-direction: column;
5
- }
@@ -1,5 +0,0 @@
1
- .wrapper {
2
- display: flex;
3
- gap: 5px;
4
- flex-direction: column;
5
- }
@@ -1,2 +0,0 @@
1
- declare const _default: Promise<import("payload").SanitizedConfig>;
2
- export default _default;
@@ -1,2 +0,0 @@
1
- import type { PayloadHandler } from 'payload';
2
- export declare const customEndpointHandler: PayloadHandler;
@@ -1,7 +0,0 @@
1
- export const customEndpointHandler = ()=>{
2
- return Response.json({
3
- message: 'Hello from custom endpoint'
4
- });
5
- };
6
-
7
- //# sourceMappingURL=customEndpointHandler.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/endpoints/customEndpointHandler.ts"],"sourcesContent":["import type { PayloadHandler } from 'payload'\n\nexport const customEndpointHandler: PayloadHandler = () => {\n return Response.json({ message: 'Hello from custom endpoint' })\n}\n"],"names":["customEndpointHandler","Response","json","message"],"mappings":"AAEA,OAAO,MAAMA,wBAAwC;IACnD,OAAOC,SAASC,IAAI,CAAC;QAAEC,SAAS;IAA6B;AAC/D,EAAC"}
@@ -1 +0,0 @@
1
- export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js';
@@ -1,3 +0,0 @@
1
- export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js';
2
-
3
- //# sourceMappingURL=client.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/exports/client.ts"],"sourcesContent":["export { BeforeDashboardClient } from '../components/BeforeDashboardClient.js'\n"],"names":["BeforeDashboardClient"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC"}
@@ -1 +0,0 @@
1
- export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js';
@@ -1,3 +0,0 @@
1
- export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js';
2
-
3
- //# sourceMappingURL=rsc.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/exports/rsc.ts"],"sourcesContent":["export { BeforeDashboardServer } from '../components/BeforeDashboardServer.js'\n"],"names":["BeforeDashboardServer"],"mappings":"AAAA,SAASA,qBAAqB,QAAQ,yCAAwC"}
@@ -1,4 +0,0 @@
1
- export declare const canUseDOM: boolean;
2
- export declare const getServerSideURL: () => string;
3
- export declare const getClientSideURL: () => string;
4
- export declare const serverURL: string;
@@ -1,22 +0,0 @@
1
- export const canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
2
- export const getServerSideURL = ()=>{
3
- const serverSideURL = process.env.NEXT_PUBLIC_VERCEL_URL ? `https://${process.env.NEXT_PUBLIC_VERCEL_URL}` : process.env.VERCEL_PROJECT_PRODUCTION_URL ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}` : process.env.NEXT_PUBLIC_DEV_URL ? `http://${process.env.NEXT_PUBLIC_DEV_URL}` : 'http://localhost:3000';
4
- // console.log(`process.env.NEXT_PUBLIC_DEV_URL: ${process.env.NEXT_PUBLIC_DEV_URL}`)
5
- // console.log(`serverSideURL: ${serverSideURL}`)
6
- return serverSideURL;
7
- };
8
- export const getClientSideURL = ()=>{
9
- if (canUseDOM) {
10
- const protocol = window.location.protocol;
11
- const domain = window.location.hostname;
12
- const port = window.location.port;
13
- // `${window.location.protocol}//${window.location.host}
14
- const clientSideURL = `${protocol}//${domain}${port ? `:${port}` : ''}`;
15
- // console.log(`clientSideURL: ${clientSideURL}`)
16
- return clientSideURL;
17
- }
18
- return getServerSideURL();
19
- };
20
- export const serverURL = getClientSideURL();
21
-
22
- //# sourceMappingURL=serverConfig.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/helpers/serverConfig.ts"],"sourcesContent":["export const canUseDOM = !!(\n typeof window !== 'undefined' &&\n window.document &&\n window.document.createElement\n)\n\nexport const getServerSideURL = () => {\n const serverSideURL = process.env.NEXT_PUBLIC_VERCEL_URL\n ? `https://${process.env.NEXT_PUBLIC_VERCEL_URL}`\n : process.env.VERCEL_PROJECT_PRODUCTION_URL\n ? `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`\n : process.env.NEXT_PUBLIC_DEV_URL\n ? `http://${process.env.NEXT_PUBLIC_DEV_URL}`\n : 'http://localhost:3000'\n // console.log(`process.env.NEXT_PUBLIC_DEV_URL: ${process.env.NEXT_PUBLIC_DEV_URL}`)\n // console.log(`serverSideURL: ${serverSideURL}`)\n return serverSideURL\n}\n\nexport const getClientSideURL = () => {\n if (canUseDOM) {\n const protocol = window.location.protocol\n const domain = window.location.hostname\n const port = window.location.port\n // `${window.location.protocol}//${window.location.host}\n const clientSideURL = `${protocol}//${domain}${port ? `:${port}` : ''}`\n // console.log(`clientSideURL: ${clientSideURL}`)\n return clientSideURL\n }\n\n return getServerSideURL()\n}\n\nexport const serverURL = getClientSideURL()\n"],"names":["canUseDOM","window","document","createElement","getServerSideURL","serverSideURL","process","env","NEXT_PUBLIC_VERCEL_URL","VERCEL_PROJECT_PRODUCTION_URL","NEXT_PUBLIC_DEV_URL","getClientSideURL","protocol","location","domain","hostname","port","clientSideURL","serverURL"],"mappings":"AAAA,OAAO,MAAMA,YAAY,CAAC,CACxB,CAAA,OAAOC,WAAW,eAClBA,OAAOC,QAAQ,IACfD,OAAOC,QAAQ,CAACC,aAAa,AAAD,EAC7B;AAED,OAAO,MAAMC,mBAAmB;IAC9B,MAAMC,gBAAgBC,QAAQC,GAAG,CAACC,sBAAsB,GACpD,CAAC,QAAQ,EAAEF,QAAQC,GAAG,CAACC,sBAAsB,EAAE,GAC/CF,QAAQC,GAAG,CAACE,6BAA6B,GACvC,CAAC,QAAQ,EAAEH,QAAQC,GAAG,CAACE,6BAA6B,EAAE,GACtDH,QAAQC,GAAG,CAACG,mBAAmB,GAC7B,CAAC,OAAO,EAAEJ,QAAQC,GAAG,CAACG,mBAAmB,EAAE,GAC3C;IACR,qFAAqF;IACrF,iDAAiD;IACjD,OAAOL;AACT,EAAC;AAED,OAAO,MAAMM,mBAAmB;IAC9B,IAAIX,WAAW;QACb,MAAMY,WAAWX,OAAOY,QAAQ,CAACD,QAAQ;QACzC,MAAME,SAASb,OAAOY,QAAQ,CAACE,QAAQ;QACvC,MAAMC,OAAOf,OAAOY,QAAQ,CAACG,IAAI;QACjC,wDAAwD;QACxD,MAAMC,gBAAgB,GAAGL,SAAS,EAAE,EAAEE,SAASE,OAAO,CAAC,CAAC,EAAEA,MAAM,GAAG,IAAI;QACvE,iDAAiD;QACjD,OAAOC;IACT;IAEA,OAAOb;AACT,EAAC;AAED,OAAO,MAAMc,YAAYP,mBAAkB"}
@@ -1,11 +0,0 @@
1
- export type SubscriberAuthReturn = {
2
- error: any;
3
- } | {
4
- permissions: any;
5
- subscriber: any;
6
- };
7
- export declare const subscriberAuth: () => Promise<SubscriberAuthReturn>;
8
- export declare function logoutAction(): Promise<{
9
- message: string;
10
- success: boolean;
11
- }>;
@@ -1,45 +0,0 @@
1
- import type { CollectionConfig } from 'payload'
2
-
3
- export const OptInChannels: CollectionConfig = {
4
- slug: 'opt-in-channels',
5
- access: {
6
- // Public access for creation (signup form)
7
- create: () => true,
8
- // Admin-only access for reading, updating, and deleting
9
- delete: ({ req }) => (req.user ? true : false),
10
- // read: ({ req }) => (req.user ? true : false),
11
- read: () => true,
12
- update: ({ req }) => (req.user ? true : false),
13
- },
14
- admin: {
15
- useAsTitle: 'title', // Specify the field to use as the title
16
- },
17
- fields: [
18
- {
19
- name: 'title',
20
- type: 'text', // Enforces valid email format
21
- label: 'Title',
22
- required: true,
23
- unique: true, // Ensures no duplicate titles
24
- },
25
- {
26
- name: 'description',
27
- type: 'text',
28
- label: 'Description',
29
- },
30
- {
31
- name: 'active',
32
- type: 'checkbox',
33
- defaultValue: true, // Default to pending until verified
34
- label: 'Active',
35
- required: true,
36
- },
37
- {
38
- name: 'slug',
39
- type: 'text',
40
- label: 'slug',
41
- },
42
- ],
43
- }
44
-
45
- export default OptInChannels
@@ -1,99 +0,0 @@
1
- import type { CollectionConfig, CollectionSlug, Field } from 'payload'
2
-
3
- import { OptedInChannels } from './fields/OptedInChannels.js'
4
-
5
- export const defaultTokenExpiration = 30 * 60 // 30 minutes
6
-
7
- export const defaultCollectionSlug = 'subscribers'
8
-
9
- export const SubscribersCollectionFactory = ({
10
- slug,
11
- tokenExpiration = defaultTokenExpiration,
12
- }: {
13
- slug?: CollectionSlug
14
- tokenExpiration?: number
15
- }) => {
16
- const Subscribers: CollectionConfig = {
17
- slug: slug ? slug : defaultCollectionSlug,
18
- access: {
19
- // Public access for creation (signup form)
20
- create: () => true,
21
- // Admin-only access for reading, updating, and deleting
22
- delete: ({ req }) => (req.user ? true : false),
23
- read: ({ req }) => (req.user ? true : false),
24
- update: ({ req }) => (req.user ? true : false),
25
- },
26
- admin: { useAsTitle: 'email' },
27
- auth: {
28
- tokenExpiration,
29
- // verify: true, // Require email verification before being allowed to authenticate
30
- // maxLoginAttempts: 5, // Automatically lock a user out after X amount of failed logins
31
- // lockTime: 600 * 1000, // Time period to allow the max login attempts
32
- },
33
- fields: [...subscribersCollectionFields],
34
- }
35
-
36
- return Subscribers
37
- }
38
-
39
- export const subscribersCollectionFields: Field[] = [
40
- {
41
- name: 'email',
42
- type: 'email', // Enforces valid email format
43
- label: 'Email Address',
44
- required: true,
45
- unique: true, // Ensures no duplicate emails
46
- },
47
- {
48
- name: 'firstName',
49
- type: 'text',
50
- label: 'First Name',
51
- },
52
- {
53
- name: 'status',
54
- type: 'select',
55
- defaultValue: 'pending', // Default to pending until verified
56
- label: 'Subscription Status',
57
- options: [
58
- {
59
- label: 'Subscribed',
60
- value: 'subscribed',
61
- },
62
- {
63
- label: 'Unsubscribed',
64
- value: 'unsubscribed',
65
- },
66
- {
67
- label: 'Pending Verification',
68
- value: 'pending',
69
- },
70
- ],
71
- required: true,
72
- },
73
- {
74
- name: 'source',
75
- type: 'text', // e.g., 'Homepage form', 'Blog post A', etc.
76
- label: 'Signup Source',
77
- },
78
- {
79
- name: 'verificationToken',
80
- type: 'text',
81
- admin: {
82
- hidden: true, // Hide this field in the admin panel for security/cleanliness
83
- },
84
- label: 'Verification Token',
85
- },
86
- {
87
- name: 'verificationTokenExpires',
88
- type: 'date',
89
- admin: {
90
- hidden: true, // Hide this field in the admin panel for security/cleanliness
91
- },
92
- label: 'Verification Token Expiration',
93
- },
94
-
95
- /**
96
- * Plugin field relationship to optinchannels
97
- */
98
- OptedInChannels,
99
- ]
@@ -1,12 +0,0 @@
1
- import type { Field } from 'payload'
2
-
3
- export const OptedInChannels: Field = {
4
- name: 'optIns',
5
- type: 'relationship',
6
- admin: {
7
- position: 'sidebar',
8
- },
9
- hasMany: true,
10
- label: 'Opted-in channels',
11
- relationTo: 'opt-in-channels',
12
- }
@@ -1,129 +0,0 @@
1
- 'use client'
2
-
3
- import { PayloadSDK } from '@payloadcms/sdk'
4
- import { type ChangeEvent, type SubmitEvent, useEffect, useState } from 'react'
5
-
6
- import type { Config } from '../../copied/payload-types.js'
7
- import type { RequestMagicLinkResponse } from '../../endpoints/requestMagicLink.js'
8
-
9
- import { useSubscriber } from '../../contexts/SubscriberProvider.js'
10
- import { useServerUrl } from '../../react-hooks/useServerUrl.js'
11
- import { mergeClassNames } from './helpers.js'
12
- import styles from './shared.module.css'
13
-
14
- export { RequestMagicLinkResponse }
15
-
16
- export interface IRequestMagicLink {
17
- classNames?: RequestMagicLinkClasses
18
- handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void
19
- props?: any
20
- }
21
-
22
- export type RequestMagicLinkClasses = {
23
- button?: string
24
- container?: string
25
- emailInput?: string
26
- error?: string
27
- form?: string
28
- message?: string
29
- }
30
-
31
- type statusValues = 'default' | 'error' | 'sent'
32
-
33
- export const RequestMagicLink = ({
34
- classNames = {
35
- button: '',
36
- container: '',
37
- emailInput: '',
38
- error: '',
39
- form: '',
40
- message: '',
41
- },
42
- handleMagicLinkRequested,
43
- }: IRequestMagicLink) => {
44
- const { subscriber } = useSubscriber()
45
- const { serverURL } = useServerUrl()
46
-
47
- const [status, setStatus] = useState<statusValues>('default')
48
-
49
- const sdk = new PayloadSDK<Config>({
50
- baseURL: serverURL || '',
51
- })
52
-
53
- const [result, setResult] = useState<string>()
54
- const [email, setEmail] = useState(subscriber?.email || '')
55
-
56
- useEffect(() => {
57
- setEmail(subscriber?.email || '')
58
- }, [subscriber])
59
-
60
- const handleSubmit = async (e: SubmitEvent<HTMLFormElement>) => {
61
- e.preventDefault()
62
- const forwardUrl = window.location.pathname + '?now=' + new Date().toISOString()
63
- const emailTokenResponse = await sdk.request({
64
- json: {
65
- email,
66
- forwardUrl,
67
- },
68
- method: 'POST',
69
- path: '/api/emailToken',
70
- })
71
- if (emailTokenResponse.ok) {
72
- const emailTokenResponseJson: RequestMagicLinkResponse = await emailTokenResponse.json()
73
- if (handleMagicLinkRequested) {
74
- handleMagicLinkRequested(emailTokenResponseJson)
75
- }
76
- // @ts-expect-error One or the other exists
77
- const { emailResult, error } = emailTokenResponseJson
78
- if (error) {
79
- setStatus('error')
80
- setResult(`An error occured. Please try again. \n ${error}`)
81
- } else if (emailResult) {
82
- setStatus('sent')
83
- setResult('An email has been sent containing your magic link.')
84
- } else {
85
- setStatus('error')
86
- setResult(`An error occured. Please try again. \nResult unknown`)
87
- }
88
- } else {
89
- const emailTokenResponseText = await emailTokenResponse.text()
90
- setStatus('error')
91
- setResult(`An error occured. Please try again. \n${emailTokenResponseText}`)
92
- }
93
- }
94
-
95
- return (
96
- <div className={mergeClassNames([styles.container, classNames.container])}>
97
- {result ? (
98
- <p
99
- className={mergeClassNames([
100
- styles.message,
101
- classNames.message,
102
- status == 'error' ? [styles.error, classNames.error] : [],
103
- ])}
104
- >
105
- {result}
106
- </p>
107
- ) : (
108
- <></>
109
- )}
110
- <form
111
- className={mergeClassNames([styles.form, classNames.form])}
112
- method="POST"
113
- onSubmit={handleSubmit}
114
- >
115
- <input
116
- aria-label="enter your email"
117
- className={mergeClassNames([styles.emailInput, classNames.emailInput])}
118
- onChange={(e: ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)}
119
- placeholder="enter your email"
120
- type="email"
121
- value={email}
122
- />
123
- <button className={mergeClassNames([styles.button, classNames.button])} type="submit">
124
- Request magic link
125
- </button>
126
- </form>
127
- </div>
128
- )
129
- }
@@ -1,58 +0,0 @@
1
- 'use client'
2
-
3
- import { useSubscriber } from '../../contexts/SubscriberProvider.js'
4
- import {
5
- RequestMagicLink,
6
- type RequestMagicLinkResponse,
7
- Subscribe,
8
- type SubscribeResponse,
9
- } from '../../exports/ui.js'
10
-
11
- export type { RequestMagicLinkResponse, SubscribeResponse }
12
-
13
- export type RequestOrSubscribeClasses = {
14
- button?: string
15
- container?: string
16
- emailInput?: string
17
- error?: string
18
- form?: string
19
- loading?: string
20
- message?: string
21
- section?: string
22
- }
23
-
24
- export function RequestOrSubscribe({
25
- classNames = {
26
- button: '',
27
- container: '',
28
- emailInput: '',
29
- error: '',
30
- form: '',
31
- loading: '',
32
- message: '',
33
- section: '',
34
- },
35
- handleMagicLinkRequested,
36
- handleSubscribe,
37
- }: {
38
- classNames?: RequestOrSubscribeClasses
39
- handleMagicLinkRequested?: (result: RequestMagicLinkResponse) => void
40
- handleSubscribe?: (result: SubscribeResponse) => void
41
- }) {
42
- const { subscriber } = useSubscriber()
43
-
44
- // Example: Conditionally render something or pass the state to children
45
- return (
46
- <>
47
- {subscriber ? (
48
- <Subscribe classNames={classNames} handleSubscribe={handleSubscribe} />
49
- ) : (
50
- <RequestMagicLink
51
- classNames={classNames}
52
- handleMagicLinkRequested={handleMagicLinkRequested}
53
- />
54
- )}
55
- {/* <div>subscriber = {JSON.stringify(subscriber)}</div> */}
56
- </>
57
- )
58
- }