payload-plugin-marketing 0.9.3 → 0.9.5
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 +68 -19
- package/dist/admin/client.cjs +3 -0
- package/dist/admin/client.cjs.map +1 -0
- package/dist/admin/client.d.ts +7 -0
- package/dist/admin/client.d.ts.map +1 -0
- package/dist/admin/client.js +3 -0
- package/dist/admin/client.js.map +1 -0
- package/dist/admin/components/audience-buttons.d.ts.map +1 -1
- package/dist/admin/components/broadcast-list.d.ts.map +1 -1
- package/dist/admin/components/broadcasts-table.d.ts.map +1 -1
- package/dist/admin/components/contacts-table.d.ts.map +1 -1
- package/dist/admin/components/create-broadcast-button.d.ts +9 -1
- package/dist/admin/components/create-broadcast-button.d.ts.map +1 -1
- package/dist/admin/components/marketing-components.d.ts +0 -6
- package/dist/admin/components/marketing-components.d.ts.map +1 -1
- package/dist/admin/components/marketing-view-shell.d.ts.map +1 -1
- package/dist/admin/components/payload-modal.d.ts +8 -8
- package/dist/admin/components/payload-modal.d.ts.map +1 -1
- package/dist/admin/constants.cjs +2 -0
- package/dist/admin/constants.cjs.map +1 -0
- package/dist/admin/constants.d.ts +2 -0
- package/dist/admin/constants.d.ts.map +1 -0
- package/dist/admin/constants.js +2 -0
- package/dist/admin/constants.js.map +1 -0
- package/dist/admin/index.cjs +1 -1
- package/dist/admin/index.cjs.map +1 -1
- package/dist/admin/index.d.ts +3 -3
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/locale-options.d.ts +7 -0
- package/dist/admin/locale-options.d.ts.map +1 -0
- package/dist/admin/server.cjs +2 -0
- package/dist/admin/server.cjs.map +1 -0
- package/dist/admin/server.d.ts +2 -0
- package/dist/admin/server.d.ts.map +1 -0
- package/dist/admin/server.js +2 -0
- package/dist/admin/server.js.map +1 -0
- package/dist/admin/use-marketing-api.d.ts.map +1 -1
- package/dist/{chunk-S2EABBIN.js → chunk-G6DIJ7B2.js} +1 -1
- package/dist/chunk-G6DIJ7B2.js.map +1 -0
- package/dist/email-broadcast-template.d.ts +15 -0
- package/dist/email-broadcast-template.d.ts.map +1 -0
- package/dist/endpoints/marketing-endpoints.d.ts.map +1 -1
- package/dist/form-builder/index.cjs.map +1 -1
- package/dist/form-builder/index.js +1 -1
- package/dist/form-builder/normalize-placeholder-rows.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/marketing-integration.d.ts +2 -1
- package/dist/marketing-integration.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/types.d.ts +31 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -2
- package/dist/chunk-S2EABBIN.js.map +0 -1
- package/dist/chunk-SX3OTOU2.js +0 -2
- package/dist/chunk-SX3OTOU2.js.map +0 -1
package/README.md
CHANGED
|
@@ -15,7 +15,8 @@ pnpm add resend
|
|
|
15
15
|
pnpm add @mailchimp/mailchimp_marketing
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Import adapters from **`payload-plugin-marketing/adapters/resend`** or
|
|
18
|
+
Import adapters from **`payload-plugin-marketing/adapters/resend`** or
|
|
19
|
+
**`payload-plugin-marketing/adapters/mailchimp`**.
|
|
19
20
|
|
|
20
21
|
## Usage
|
|
21
22
|
|
|
@@ -55,13 +56,12 @@ marketingPlugin({
|
|
|
55
56
|
## Form Builder
|
|
56
57
|
|
|
57
58
|
The plugin adds `url`, `phone`, and `acceptance` blocks to the generated forms collection. For the
|
|
58
|
-
built-in form-builder blocks it ships with (such as `text`, `textarea`, `email`, `number`,
|
|
59
|
-
`
|
|
60
|
-
|
|
61
|
-
`
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
`{ localized: false }`.
|
|
59
|
+
built-in form-builder blocks it ships with (such as `text`, `textarea`, `email`, `number`, `select`,
|
|
60
|
+
`radio`, `date`, `state`, and `country`), it normalizes the second admin row to **width (20%)**,
|
|
61
|
+
**placeholder (40%)**, and **defaultValue (40%)**. `checkbox`, `message`, `payment`, `upload`, and
|
|
62
|
+
`acceptance` blocks keep their original layout. Placeholder fields are **localized** (per-locale
|
|
63
|
+
strings in the admin when your project has localization enabled). To turn that off for a block, pass
|
|
64
|
+
`formBuilder.fields.<blockSlug>.fields.placeholder` with `{ localized: false }`.
|
|
65
65
|
|
|
66
66
|
If placeholders still never appear in the admin, check that you do **not** define a root-level
|
|
67
67
|
`config.blocks` entry with the **same `slug`** as a form-builder block (for example `text`). Payload
|
|
@@ -144,7 +144,9 @@ marketingPlugin({
|
|
|
144
144
|
})
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
Use **`createBroadcast`** together with **`templateId`** for Mailchimp saved templates (see
|
|
147
|
+
Use **`createBroadcast`** together with **`templateId`** for Mailchimp saved templates (see
|
|
148
|
+
[Broadcasts](#broadcasts)). The Resend adapter does not accept **`templateId`**; pass **`html`** or
|
|
149
|
+
**`react`** instead.
|
|
148
150
|
|
|
149
151
|
## Admin Components
|
|
150
152
|
|
|
@@ -172,13 +174,14 @@ Components are also exported from `payload-plugin-marketing/admin`.
|
|
|
172
174
|
|
|
173
175
|
## Endpoint permissions
|
|
174
176
|
|
|
175
|
-
Marketing REST routes (`GET /marketing/meta`, audiences, contacts, broadcasts) require an
|
|
176
|
-
admin user. You can narrow access with **`permissions`**: each of **`audiences`**,
|
|
177
|
-
**`broadcasts`** accepts optional **`read`** and **`write`**. Omitted flags
|
|
178
|
-
that resource. If you omit **`permissions`** entirely, behavior matches
|
|
179
|
-
user can call all routes).
|
|
177
|
+
Marketing REST routes (`GET /marketing/meta`, audiences, contacts, broadcasts) require an
|
|
178
|
+
authenticated admin user. You can narrow access with **`permissions`**: each of **`audiences`**,
|
|
179
|
+
**`contacts`**, and **`broadcasts`** accepts optional **`read`** and **`write`**. Omitted flags
|
|
180
|
+
default to **`true`** for that resource. If you omit **`permissions`** entirely, behavior matches
|
|
181
|
+
previous releases (any signed-in user can call all routes).
|
|
180
182
|
|
|
181
|
-
**`GET /marketing/meta`** is allowed when at least one resource has **`read: true`** after
|
|
183
|
+
**`GET /marketing/meta`** is allowed when at least one resource has **`read: true`** after
|
|
184
|
+
resolution.
|
|
182
185
|
|
|
183
186
|
```ts
|
|
184
187
|
marketingPlugin({
|
|
@@ -195,7 +198,9 @@ Denied requests respond with **403** and `{ message: "Forbidden" }`.
|
|
|
195
198
|
|
|
196
199
|
## Using the adapter in your app
|
|
197
200
|
|
|
198
|
-
The plugin attaches your **`MarketingAdapter`** to **`config.custom.payloadPluginMarketing`**
|
|
201
|
+
The plugin attaches your **`MarketingAdapter`** to **`config.custom.payloadPluginMarketing`**
|
|
202
|
+
(server-only; not sent to the admin client bundle). Resolve it from **`payload`** anywhere you
|
|
203
|
+
already have **`Payload`**:
|
|
199
204
|
|
|
200
205
|
```ts
|
|
201
206
|
import { getMarketingIntegration } from "payload-plugin-marketing"
|
|
@@ -217,11 +222,54 @@ await adapter.contacts.upsert({ audienceId, email: "person@example.com", subscri
|
|
|
217
222
|
|
|
218
223
|
## Broadcasts
|
|
219
224
|
|
|
220
|
-
Same adapter as **`marketingPlugin`**. **`broadcastId`** for send/delete comes from your provider
|
|
225
|
+
Same adapter as **`marketingPlugin`**. **`broadcastId`** for send/delete comes from your provider
|
|
226
|
+
(for example **`adapter.broadcasts.list()`** after create, or the provider dashboard).
|
|
227
|
+
|
|
228
|
+
### React email templates (Resend admin + API)
|
|
229
|
+
|
|
230
|
+
Register templates on the plugin with **`emailBroadcastTemplates`**: each entry needs **`id`**,
|
|
231
|
+
**`name`**, and exactly one of:
|
|
232
|
+
|
|
233
|
+
- **`path`** — an import path string (same idea as Payload admin **`path`**: the server runs
|
|
234
|
+
**`import(specifier)`** when a broadcast is created). Add **`#ExportName`** for a named export,
|
|
235
|
+
e.g. **`@/emails/digest#DigestEmail`**. If there is no `#` fragment, use optional **`exportName`**
|
|
236
|
+
for a named export, or rely on the module’s **default** export.
|
|
237
|
+
- **`component`** — a server-side React component reference, if you prefer to import the file in
|
|
238
|
+
your Payload config instead of using a string.
|
|
239
|
+
|
|
240
|
+
The create-broadcast admin view shows an **Email content** select (HTML vs your templates) when you
|
|
241
|
+
use the Resend adapter.
|
|
242
|
+
|
|
243
|
+
Creating a broadcast via **`POST .../marketing/broadcasts`**, pass **`emailTemplateId`** (matching a
|
|
244
|
+
registered **`id`**) instead of **`html`** or Mailchimp’s **`templateId`**. Optional **`locale`** is
|
|
245
|
+
forwarded to the template props. **`emailTemplateId` requires the Resend adapter**; Mailchimp
|
|
246
|
+
continues to use **`templateId`** for saved templates or **`html`**.
|
|
247
|
+
|
|
248
|
+
Path strings must be resolvable by the Node process that runs Payload (aliases like `@/` only work
|
|
249
|
+
if your runtime resolves them). A file URL or relative path from the config file is a reliable
|
|
250
|
+
choice when in doubt.
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
marketingPlugin({
|
|
254
|
+
adapter: resendAdapter({ apiKey: process.env.RESEND_API_KEY! }),
|
|
255
|
+
emailBroadcastTemplates: [
|
|
256
|
+
{
|
|
257
|
+
id: "welcome",
|
|
258
|
+
name: "Welcome",
|
|
259
|
+
// default export, or: new URL("./emails/welcome.tsx", import.meta.url).href
|
|
260
|
+
path: "@/emails/welcome",
|
|
261
|
+
},
|
|
262
|
+
{ id: "digest", name: "Digest", path: "@/emails/digest#DigestEmail" },
|
|
263
|
+
// named export without #: { path: "@/emails/digest", exportName: "DigestEmail" },
|
|
264
|
+
// or pass `component: Welcome` instead of `path` if you already import it here
|
|
265
|
+
],
|
|
266
|
+
})
|
|
267
|
+
```
|
|
221
268
|
|
|
222
269
|
### Resend (HTML or React)
|
|
223
270
|
|
|
224
|
-
`defaultSender` on `resendAdapter` is sent as `from`. **`templateId` is not supported** and will
|
|
271
|
+
`defaultSender` on `resendAdapter` is sent as `from`. **`templateId` is not supported** and will
|
|
272
|
+
throw.
|
|
225
273
|
|
|
226
274
|
```ts
|
|
227
275
|
import { resendAdapter } from "payload-plugin-marketing/adapters/resend"
|
|
@@ -278,7 +326,8 @@ if (id) {
|
|
|
278
326
|
|
|
279
327
|
### Mailchimp (saved template)
|
|
280
328
|
|
|
281
|
-
Use the numeric template id from your Mailchimp account as a string. Do not pass **`html`** in the
|
|
329
|
+
Use the numeric template id from your Mailchimp account as a string. Do not pass **`html`** in the
|
|
330
|
+
same call.
|
|
282
331
|
|
|
283
332
|
```ts
|
|
284
333
|
await adapter.broadcasts.create({
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";var Ee=Object.create;var W=Object.defineProperty;var Ie=Object.getOwnPropertyDescriptor;var qe=Object.getOwnPropertyNames;var Oe=Object.getPrototypeOf,$e=Object.prototype.hasOwnProperty;var _e=(t,e)=>{for(var a in e)W(t,a,{get:e[a],enumerable:!0})},fe=(t,e,a,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of qe(e))!$e.call(t,n)&&n!==a&&W(t,n,{get:()=>e[n],enumerable:!(r=Ie(e,n))||r.enumerable});return t};var R=(t,e,a)=>(a=t!=null?Ee(Oe(t)):{},fe(e||!t||!t.__esModule?W(a,"default",{value:t,enumerable:!0}):a,t)),Le=t=>fe(W({},"__esModule",{value:!0}),t);var Xe={};_e(Xe,{AudienceContactsTable:()=>Te,AudienceSelect:()=>ge,AudienceTable:()=>he,BroadcastsTable:()=>ke,ContactsTable:()=>Me,CreateAudienceButton:()=>ve,CreateBroadcastButton:()=>Fe,DeleteAudienceButton:()=>xe,EditContactButton:()=>oe,MarketingMenu:()=>be});module.exports=Le(Xe);var D=require("@payloadcms/ui"),Q=R(require("react"),1);function He(t,...e){let a=t.replace(/\/+$/,"")||"",r=e.flatMap(o=>String(o).split("/")).map(o=>o.replace(/^\/+|\/+$/g,"")).filter(Boolean).join("/");return(a===""?`/${r}`:`${a}/${r}`).replace(/\/{2,}/g,"/")}function Y(t,...e){let r=[t?.replace(/^\/+|\/+$/g,"")??"",...e].filter(Boolean);return He("/admin",...r)}var ye=require("@payloadcms/ui"),G=require("react");function w(){let t=(0,ye.useConfig)(),e=(0,G.useMemo)(()=>`${t.serverURL??""}${t.routes?.api??"/api"}`,[t.routes?.api,t.serverURL]),a=(0,G.useCallback)(async(r,n)=>{let{headers:o,...l}=n??{},p=new Headers(n?.headers);l.body!==void 0&&l.body!==""&&l.body!==null&&(p.has("Content-Type")||p.set("Content-Type","application/json"));let u=await fetch(`${e}${r}`,{...l,credentials:"include",headers:p});if(!u.ok){let b=`${u.status} ${u.statusText}`;try{let P=await u.json();typeof P.message=="string"&&(b=P.message)}catch{}throw new Error(b)}if(u.status!==204)try{return await u.json()}catch{return}},[e]);return{base:e,requestJson:a}}var h=require("react/jsx-runtime");function Je(t){if(!Array.isArray(t))return[];let e=[];for(let a of t){if(!a||typeof a!="object")continue;let r=a,n=r.id,o=r.name;typeof n=="string"&&typeof o=="string"&&e.push({id:n,name:o})}return e}var ge=({field:t,path:e,readOnly:a})=>{let{requestJson:r}=w(),[n,o]=Q.default.useState([]),[l,p]=Q.default.useState(null);return Q.default.useEffect(()=>{let u=!1;return(async()=>{try{let b=await r("/marketing/audiences");u||(o(Je(b)),p(null))}catch(b){u||p(b instanceof Error?b.message:"Failed to load audiences")}})(),()=>{u=!0}},[r]),(0,h.jsxs)("div",{className:"field-type relative",children:[l?(0,h.jsx)("p",{className:"text-red-500",role:"alert",children:l}):null,(0,h.jsx)(D.SelectField,{field:{label:typeof t.label=="string"?t.label:"Audience",name:t.name,options:n.map(u=>({label:u.name,value:u.id})),required:t.required===!0,type:"select"},path:e,readOnly:a})]})};function be({basePath:t=""}){return(0,h.jsxs)(D.NavGroup,{isOpen:!0,label:"Marketing",children:[(0,h.jsx)(D.Link,{className:"nav__link",href:Y(t,"audience"),children:"Audience"}),(0,h.jsx)(D.Link,{className:"nav__link",href:Y(t,"broadcast"),children:"Broadcast"})]})}function he({audiences:t}){return(0,h.jsx)("table",{children:(0,h.jsx)("tbody",{children:t.map(e=>(0,h.jsx)("tr",{children:(0,h.jsx)("td",{children:e.name})},e.id))})})}function Me({contacts:t}){return(0,h.jsx)("table",{children:(0,h.jsx)("tbody",{children:t.map(e=>(0,h.jsx)("tr",{children:(0,h.jsx)("td",{children:e.email})},e.id))})})}var c=require("@payloadcms/ui"),we=R(require("next/link"),1),Z=require("next/navigation"),V=R(require("react"),1);function H(t){if(!t)return"\u2014";let e=new Date(t);return Number.isNaN(e.valueOf())?String(t):new Intl.DateTimeFormat(void 0,{dateStyle:"medium"}).format(e)}var A=require("@payloadcms/ui"),J=R(require("react"),1),N=require("react/jsx-runtime");function I(...t){return t.filter(Boolean).join(" ")}var Pe=J.default.createContext({modalSlug:""});function C({children:t,className:e,slug:a,...r}){let[n,o]=J.default.useState(!1);return J.default.useEffect(()=>{o(!0)},[]),(0,N.jsx)(Pe.Provider,{value:{modalSlug:a},children:n&&(0,N.jsx)(A.Modal,{slug:a,className:I("confirmation-modal",e),...r,children:t})})}function v({children:t,className:e,...a}){return(0,N.jsx)("div",{className:I("confirmation-modal__wrapper",e),"data-slot":"payload-modal-content",...a,children:t})}function x({children:t,className:e,...a}){return(0,N.jsx)("div",{className:I("confirmation-modal__content",e),"data-slot":"payload-modal-body",...a,children:t})}function q({children:t,className:e,...a}){return(0,N.jsx)("h1",{className:I("",e),"data-slot":"payload-modal-title",...a,children:t})}function S({children:t,className:e,...a}){return(0,N.jsx)("div",{className:I("confirmation-modal__controls",e),"data-slot":"payload-modal-footer",...a,children:t})}function B({children:t,className:e,...a}){let{t:r}=(0,A.useTranslation)(),{modalSlug:n}=J.default.useContext(Pe),o=(0,A.useModal)();return(0,N.jsx)(A.Button,{className:I("",e),buttonStyle:"secondary",size:"large","data-slot":"payload-modal-close",...a,onClick:()=>o.closeModal(n),children:t??r("general:cancel")})}var i=require("react/jsx-runtime");function ke({broadcasts:t}){let[e,a]=V.default.useState(1),[r]=V.default.useState(100),n=V.default.useMemo(()=>t.slice((e-1)*r,e*r),[t,r,e]);return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(c.Table,{columns:[{Heading:"Campaign name",accessor:"name",active:!0,field:{name:"name",type:"text"},renderedCells:n.map(o=>(0,i.jsx)("div",{children:o.name},o.id))},{Heading:"Schedule date",accessor:"scheduledAt",active:!0,field:{name:"scheduledAt",type:"text"},renderedCells:n.map(o=>(0,i.jsx)("div",{children:H(o.scheduledAt)},o.id))},{Heading:"Sent date",accessor:"sentAt",active:!0,field:{name:"sentAt",type:"text"},renderedCells:n.map(o=>(0,i.jsx)("div",{children:H(o.sentAt)},o.id))},{Heading:"Status",accessor:"status",active:!0,field:{name:"status",type:"text"},renderedCells:n.map(o=>(0,i.jsx)("div",{children:(0,i.jsx)(je,{status:o.status})},o.id))},{Heading:"",accessor:"",active:!0,field:{name:"_",type:"text"},renderedCells:n.map(o=>(0,i.jsxs)("div",{className:"flex items-center gap-4 flex-wrap",children:[o.externalDashboardUrl?(0,i.jsx)(we.default,{href:o.externalDashboardUrl,rel:"noreferrer noopener",target:"_blank",children:(0,i.jsx)(c.Pill,{children:"dashboard \u2197"})}):null,o.status==="draft"||o.status==="save"?(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(Ue,{broadcastId:o.id,broadcastName:o.name}),(0,i.jsx)(ze,{broadcastId:o.id,broadcastName:o.name})]}):null]},o.id))}],data:n}),(0,i.jsx)(c.Pagination,{hasNextPage:e*r<t.length,hasPrevPage:e>1,limit:r,onChange:o=>a(o),page:e,totalPages:Math.ceil(t.length/r)})]})}function je({status:t}){switch(t){case"draft":case"save":return(0,i.jsx)(c.Pill,{pillStyle:"light-gray",children:"Draft"});case"queued":return(0,i.jsx)(c.Pill,{pillStyle:"white",children:"Queued"});case"sent":return(0,i.jsx)(c.Pill,{pillStyle:"success",children:"Sent"});default:return(0,i.jsx)(c.Pill,{children:t})}}function Ue({broadcastId:t,broadcastName:e}){let a=(0,c.useModal)(),{t:r}=(0,c.useTranslation)(),n=(0,Z.useRouter)(),{requestJson:o}=w(),l=`delete-broadcast_${t}`;async function p(){await o(`/marketing/broadcasts/${encodeURIComponent(t)}`,{method:"DELETE"}),c.toast.success(r("general:deletedSuccessfully")),a.closeModal(l),n.refresh()}return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(C,{slug:l,children:(0,i.jsxs)(v,{children:[(0,i.jsxs)(x,{children:[(0,i.jsx)(q,{children:r("general:confirmDeletion")}),(0,i.jsx)("p",{children:`Delete draft broadcast "${e}"?`})]}),(0,i.jsxs)(S,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[(0,i.jsx)(B,{}),(0,i.jsx)(c.Button,{buttonStyle:"error",onClick:()=>{p().catch(u=>c.toast.error(u instanceof Error?u.message:"Delete failed"))},children:r("general:confirm")})]})]})}),(0,i.jsx)("button",{onClick:()=>a.openModal(l),type:"button",children:r("general:delete")})]})}function ze({broadcastId:t,broadcastName:e}){let a=(0,c.useModal)(),{t:r}=(0,c.useTranslation)(),n=(0,Z.useRouter)(),{requestJson:o}=w(),l=`send-broadcast_${t}`,p=(u,b)=>{let k=b.scheduledAt,M="";typeof k=="string"?M=k:k instanceof Date&&(M=k.toISOString()),(async()=>{try{await o(`/marketing/broadcasts/${encodeURIComponent(t)}/send`,{body:JSON.stringify({...M&&M.trim()!==""?{scheduledAt:M.trim()}:{}}),method:"POST"}),a.closeModal(l),c.toast.success(r("general:success")),n.refresh()}catch(E){c.toast.error(E instanceof Error?E.message:"Send failed")}})()};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(C,{slug:l,children:(0,i.jsx)(v,{style:{width:"100%",maxWidth:"24rem"},children:(0,i.jsxs)(x,{children:[(0,i.jsx)(q,{children:`Send \u201C${e}\u201D`}),(0,i.jsxs)(c.Form,{initialState:{},onSubmit:p,waitForAutocomplete:!0,children:[(0,i.jsx)(c.DateTimeField,{field:{label:"Schedule at (optional)",name:"scheduledAt",required:!1},path:"scheduledAt"}),(0,i.jsxs)(S,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[(0,i.jsx)(B,{}),(0,i.jsx)(c.Button,{size:"large",type:"submit",children:"Send"})]})]})]})})}),(0,i.jsx)(c.Pill,{pillStyle:"dark",onClick:()=>a.openModal(l),size:"small",children:"Send"})]})}var g=require("@payloadcms/ui"),te=require("next/navigation"),Ce=require("react");var f=require("react/jsx-runtime"),ee="create-audience";function ve(){let t=(0,g.useModal)(),{t:e}=(0,g.useTranslation)();return(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(We,{}),(0,f.jsx)(g.Button,{onClick:()=>t.openModal(ee),size:"large",type:"button",children:e("general:createNew")})]})}function We(){let t=(0,g.useModal)(),{t:e}=(0,g.useTranslation)(),a=(0,te.useRouter)(),{requestJson:r}=w(),[n,o]=(0,Ce.useTransition)();return(0,f.jsx)(C,{closeOnBlur:!0,slug:ee,children:(0,f.jsx)(v,{style:{width:"100%",maxWidth:"24rem"},children:(0,f.jsx)(g.Form,{initialState:{},onSubmit:(p,u)=>{let P=u.audienceName,k=typeof P=="string"?P.trim():"";o(()=>{(async()=>{try{if(!k)throw new Error("Name is required.");await r("/marketing/audiences",{body:JSON.stringify({name:k}),method:"POST"}),t.closeModal(ee),g.toast.success(e("general:successfullyCreated")),a.refresh()}catch(M){g.toast.error(M instanceof Error?M.message:"Create failed")}})()})},waitForAutocomplete:!0,children:(0,f.jsxs)(x,{children:[(0,f.jsx)(q,{children:e("general:createNew")}),(0,f.jsx)(g.TextField,{field:{name:"audienceName",required:!0,type:"text"},path:"audienceName",validate:p=>typeof p=="string"&&p.trim().length>0?!0:"Name is required"}),(0,f.jsxs)(S,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[(0,f.jsx)(B,{}),(0,f.jsx)(g.Button,{disabled:n,size:"large",type:"submit",children:e("general:confirm")})]})]})})})})}function xe({audienceId:t,audienceName:e}){let a=(0,g.useModal)(),{t:r}=(0,g.useTranslation)(),n=(0,te.useRouter)(),{requestJson:o}=w(),l=`delete-audience_${t}`;async function p(){await o(`/marketing/audiences/${encodeURIComponent(t)}`,{method:"DELETE"}),g.toast.success(r("general:deletedSuccessfully")),a.closeModal(l),n.refresh()}return(0,f.jsxs)(f.Fragment,{children:[(0,f.jsx)(C,{slug:l,children:(0,f.jsxs)(v,{children:[(0,f.jsxs)(x,{children:[(0,f.jsx)(q,{children:r("general:confirmDeletion")}),(0,f.jsx)("p",{children:`Remove audience "${e}"?`})]}),(0,f.jsxs)(S,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[(0,f.jsx)(B,{}),(0,f.jsx)(g.Button,{buttonStyle:"error",onClick:()=>{p().catch(u=>g.toast.error(u instanceof Error?u.message:"Delete failed"))},children:r("general:confirm")})]})]})}),(0,f.jsx)("button",{onClick:()=>a.openModal(l),type:"button",children:r("general:delete")})]})}var m=require("@payloadcms/ui"),ae=require("next/navigation"),K=R(require("react"),1),Se=R(require("react"),1),Be=require("react");var s=require("react/jsx-runtime");function Te({audienceId:t,contacts:e}){let[a,r]=K.default.useState(1),[n]=K.default.useState(100),o=K.default.useMemo(()=>e.slice((a-1)*n,a*n),[e,n,a]);return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(m.Table,{columns:[{Heading:"First name",accessor:"firstName",active:!0,field:{name:"firstName",type:"text"},renderedCells:o.map(l=>(0,s.jsx)("div",{children:l.firstName??"\u2014"},l.id))},{Heading:"Last name",accessor:"lastName",active:!0,field:{name:"lastName",type:"text"},renderedCells:o.map(l=>(0,s.jsx)("div",{children:l.lastName??"\u2014"},l.id))},{Heading:"Email",accessor:"email",active:!0,field:{name:"email",type:"text"},renderedCells:o.map(l=>(0,s.jsx)("div",{children:l.email},l.id))},{Heading:"Created",accessor:"createdAt",active:!0,field:{name:"createdAt",type:"text"},renderedCells:o.map(l=>(0,s.jsx)("div",{children:H(l.createdAt)},l.id))},{Heading:"Status",accessor:"subscribed",active:!0,field:{name:"subscribed",type:"text"},renderedCells:o.map(l=>(0,s.jsxs)("div",{className:"flex items-center gap-4 flex-wrap",children:[(0,s.jsx)("span",{children:l.subscribed!==!1?"\u2705 subscribed":"\u274C unsubscribed"}),(0,s.jsx)(oe,{audienceId:t,contact:l}),(0,s.jsx)(Ge,{audienceId:t,contactEmail:l.email,contactId:l.id})]},l.id))}],data:o}),(0,s.jsx)(m.Pagination,{hasNextPage:a*n<e.length,hasPrevPage:a>1,limit:n,onChange:l=>{r(l)},page:a,totalPages:Math.ceil(e.length/n)})]})}function Ge({audienceId:t,contactId:e,contactEmail:a}){let r=(0,m.useModal)(),{t:n}=(0,m.useTranslation)(),o=(0,ae.useRouter)(),{requestJson:l}=w(),p=`delete-contact_${e}`;async function u(){await l(`/marketing/audiences/${encodeURIComponent(t)}/contacts/${encodeURIComponent(e)}`,{method:"DELETE"}),m.toast.success(n("general:deletedSuccessfully")),r.closeModal(p),o.refresh()}return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(C,{slug:p,children:(0,s.jsxs)(v,{children:[(0,s.jsxs)(x,{children:[(0,s.jsx)("h1",{children:n("general:confirmDeletion")}),(0,s.jsx)("p",{children:`Remove contact ${a}?`})]}),(0,s.jsxs)(S,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[(0,s.jsx)(B,{}),(0,s.jsx)(m.Button,{buttonStyle:"error",onClick:()=>{u().catch(b=>m.toast.error(b instanceof Error?b.message:"Delete failed"))},children:n("general:confirm")})]})]})}),(0,s.jsx)("button",{onClick:()=>r.openModal(p),type:"button",children:n("general:delete")})]})}function oe({audienceId:t,contact:e}){let a=(0,m.useModal)(),{t:r}=(0,m.useTranslation)(),n=Se.default.useId(),o=e?`edit-contact_${e.id}`:`create-contact_${t}_${n}`;return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(Qe,{audienceId:t,contact:e,modalSlug:o}),(0,s.jsx)(e?"button":m.Button,{...e?{}:{size:"large"},onClick:()=>a.openModal(o),type:"button",children:r(e?"general:edit":"general:createNew")})]})}function Qe({audienceId:t,contact:e,modalSlug:a}){let r=(0,m.useModal)(),{t:n}=(0,m.useTranslation)(),o=(0,ae.useRouter)(),{requestJson:l}=w(),[,p]=(0,Be.useTransition)(),u=(b,P)=>{let k=P.email,M=String(P.firstName??""),E=String(P.lastName??""),O=!!P.subscribed;p(()=>{(async()=>{try{await l("/marketing/contacts",{body:JSON.stringify({audienceId:t,email:k,firstName:M,id:e?.id,lastName:E,subscribed:O}),method:"POST"}),m.toast.success(e?"Contact updated.":"Contact created."),r.closeModal(a),o.refresh()}catch(j){m.toast.error(j instanceof Error?j.message:"Save failed")}})()})};return(0,s.jsx)(C,{slug:a,children:(0,s.jsx)(v,{style:{width:"100%",maxWidth:"32rem"},children:(0,s.jsxs)(x,{children:[(0,s.jsx)("h1",{children:n(e?"general:edit":"general:createNew")}),(0,s.jsxs)(m.Form,{className:"flex w-full flex-col gap-6",initialState:{email:{value:e?.email??""},firstName:{value:e?.firstName??""},lastName:{value:e?.lastName??""},subscribed:{value:e?.subscribed!==!1}},onSubmit:u,waitForAutocomplete:!0,children:[(0,s.jsx)(m.TextField,{field:{label:n("general:email"),name:"email",required:!0,type:"text"},path:"email",validate:b=>typeof b=="string"&&b.includes("@")?!0:"Valid email required"}),(0,s.jsx)(m.TextField,{field:{label:"First name",name:"firstName",type:"text"},path:"firstName"}),(0,s.jsx)(m.TextField,{field:{label:"Last name",name:"lastName",type:"text"},path:"lastName"}),(0,s.jsx)(m.CheckboxField,{field:{label:"Subscribed",name:"subscribed"},path:"subscribed"}),(0,s.jsxs)(S,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[(0,s.jsx)(B,{}),(0,s.jsx)(m.Button,{size:"large",type:"submit",children:n(e?"general:save":"general:create")})]})]})]})})})}var d=require("@payloadcms/ui"),Ne=require("next/navigation"),Ae=R(require("react"),1);var y=require("react/jsx-runtime"),ne="create-marketing-broadcast";function Fe({audiences:t,emailBroadcastTemplates:e,localeOptions:a,provider:r}){let n=(0,d.useModal)(),{t:o}=(0,d.useTranslation)();return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(Ke,{audiences:t,emailBroadcastTemplates:e,localeOptions:a??[],provider:r}),(0,y.jsx)(d.Button,{onClick:()=>n.openModal(ne),size:"large",type:"button",children:o("general:createNew")})]})}function Ve({localeOptions:t,showReactEmailTemplates:e,showTemplateField:a,templateOptions:r}){let n=(0,d.useFormFields)(([o])=>{if(!e)return"html";let l=o?.broadcastFormat?.value;return typeof l=="string"&&l!==""?l:"html"});if(e){let o=n==="html";return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(d.SelectField,{field:{label:"Email content",name:"broadcastFormat",options:[{label:"<html>",value:"html"},...r.map(l=>({label:l.name,value:l.id}))],required:!0},path:"broadcastFormat"}),!o&&t.length>0?(0,y.jsx)(d.SelectField,{field:{label:"Locale",name:"locale",options:t,required:!1},path:"locale"}):null,o?(0,y.jsx)(d.TextareaField,{field:{admin:{description:"Shown when Email content is <html>."},label:"<html>",name:"htmlBody",required:!0},path:"htmlBody"}):null]})}return(0,y.jsxs)(y.Fragment,{children:[a?(0,y.jsx)(d.TextField,{field:{admin:{description:"Optional Mailchimp saved-template id (numeric string). Leave <html> empty when using templates."},label:"Template id",name:"templateId",required:!1,type:"text"},path:"templateId"}):null,(0,y.jsx)(d.TextareaField,{field:{admin:{description:a?"Optional plain HTML alternative to a Mailchimp template.":"HTML broadcast body."},label:a?"<html> (optional with Mailchimp)":"<html>",name:"htmlBody",required:!a},path:"htmlBody"})]})}function Ke({audiences:t,emailBroadcastTemplates:e,localeOptions:a,provider:r}){let n=(0,Ne.useRouter)(),o=(0,d.useModal)(),{t:l}=(0,d.useTranslation)(),{requestJson:p}=w(),[u,b]=Ae.default.useState(!1),P=r==="mailchimp",k=e??[],M=r==="resend"&&k.length>0,E=(O,j)=>{let T=j,re=T.audienceId,le=T.name,ie=T.subject,se=T.htmlBody,de=T.templateId,ce=T.replyTo,me=T.broadcastFormat,ue=T.locale,$=typeof re=="string"?re:"",_=typeof le=="string"?le.trim():"",L=typeof ie=="string"?ie.trim():"",F=typeof se=="string"?se.trim():"",X=typeof de=="string"?de.trim():"",U=typeof ce=="string"?ce.trim():"",pe=typeof me=="string"?me.trim():"html",Re=typeof ue=="string"?ue.trim():"";b(!0),(async()=>{try{if(!$||!_||!L)throw new Error("Audience, name, and subject are required.");let z=P,De=M&&pe!=="html";if(M)if(De)await p("/marketing/broadcasts",{body:JSON.stringify({audienceId:$,emailTemplateId:pe,locale:Re||void 0,name:_,replyTo:U,subject:L}),method:"POST"});else if(F)await p("/marketing/broadcasts",{body:JSON.stringify({audienceId:$,html:F,name:_,replyTo:U,subject:L}),method:"POST"});else throw new Error("<html> is required when using the <html> content option.");else if(z){if(!X&&!F)throw new Error("Provide <html> or a Mailchimp template id.");if(X&&F)throw new Error("Use either <html> or Mailchimp template id, not both.");await p("/marketing/broadcasts",{body:JSON.stringify({audienceId:$,html:F,name:_,replyTo:U,subject:L,templateId:X||void 0}),method:"POST"})}else if(F)await p("/marketing/broadcasts",{body:JSON.stringify({audienceId:$,html:F,name:_,replyTo:U,subject:L}),method:"POST"});else throw new Error("<html> is required for this provider.");o.closeModal(ne),d.toast.success(l("general:successfullyCreated",{label:"Broadcast"})),n.refresh()}catch(z){d.toast.error(z instanceof Error?z.message:"Create failed")}finally{b(!1)}})()};return(0,y.jsx)(d.Drawer,{slug:ne,title:l("general:createNew"),children:(0,y.jsxs)(d.Form,{className:"flex w-full flex-col gap-8",initialState:M?{broadcastFormat:{value:"html"}}:{},onSubmit:E,waitForAutocomplete:!0,children:[(0,y.jsx)(d.SelectField,{field:{label:"Audience",name:"audienceId",options:t.map(O=>({label:O.name,value:O.id})),required:!0},path:"audienceId"}),(0,y.jsx)(d.TextField,{field:{label:"Campaign name",name:"name",required:!0,type:"text"},path:"name"}),(0,y.jsx)(d.TextField,{field:{label:"Subject",name:"subject",required:!0,type:"text"},path:"subject"}),(0,y.jsx)(d.TextField,{field:{label:"Reply-to (optional)",name:"replyTo",type:"text"},path:"replyTo"}),(0,y.jsx)(Ve,{localeOptions:a,showReactEmailTemplates:M,showTemplateField:P,templateOptions:k}),(0,y.jsx)(d.Button,{className:"w-full",disabled:u,type:"submit",children:l("general:create")})]})})}0&&(module.exports={AudienceContactsTable,AudienceSelect,AudienceTable,BroadcastsTable,ContactsTable,CreateAudienceButton,CreateBroadcastButton,DeleteAudienceButton,EditContactButton,MarketingMenu});
|
|
3
|
+
//# sourceMappingURL=client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/admin/client.ts","../../src/admin/components/marketing-components.tsx","../../src/admin/paths.ts","../../src/admin/use-marketing-api.ts","../../src/admin/components/broadcasts-table.tsx","../../src/admin/date-format.ts","../../src/admin/components/payload-modal.tsx","../../src/admin/components/audience-buttons.tsx","../../src/admin/components/contacts-table.tsx","../../src/admin/components/create-broadcast-button.tsx"],"sourcesContent":["export {\n AudienceSelect,\n AudienceTable,\n ContactsTable,\n MarketingMenu,\n} from \"./components/marketing-components\"\n\nexport { BroadcastsTable } from \"./components/broadcasts-table\"\n\nexport { CreateAudienceButton, DeleteAudienceButton } from \"./components/audience-buttons\"\n\nexport {\n ContactsTable as AudienceContactsTable,\n EditContactButton,\n} from \"./components/contacts-table\"\n\nexport { CreateBroadcastButton } from \"./components/create-broadcast-button\"\n\nexport type { MarketingBroadcastRow } from \"./components/broadcasts-table\"\n","\"use client\"\n\nimport { Link, NavGroup, SelectField } from \"@payloadcms/ui\"\nimport React from \"react\"\n\nimport { marketingAdminHref } from \"../paths\"\nimport { useMarketingApi } from \"../use-marketing-api\"\n\nimport type { MarketingAudience } from \"../../types\"\nimport type { TextFieldClientComponent } from \"payload\"\n\nfunction parseAudiences(payload: unknown): MarketingAudience[] {\n if (!Array.isArray(payload)) return []\n const out: MarketingAudience[] = []\n for (const item of payload) {\n if (!item || typeof item !== \"object\") continue\n const rec = item as Record<string, unknown>\n const id = rec.id\n const name = rec.name\n if (typeof id === \"string\" && typeof name === \"string\") {\n out.push({ id, name })\n }\n }\n return out\n}\n\nexport const AudienceSelect: TextFieldClientComponent = ({ field, path, readOnly }) => {\n const { requestJson } = useMarketingApi()\n const [audiences, setAudiences] = React.useState<MarketingAudience[]>([])\n const [loadError, setLoadError] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n let cancelled = false\n void (async () => {\n try {\n const data = await requestJson(\"/marketing/audiences\")\n if (!cancelled) {\n setAudiences(parseAudiences(data))\n setLoadError(null)\n }\n } catch (err) {\n if (!cancelled) {\n setLoadError(err instanceof Error ? err.message : \"Failed to load audiences\")\n }\n }\n })()\n return () => {\n cancelled = true\n }\n }, [requestJson])\n\n return (\n <div className=\"field-type relative\">\n {loadError ? (\n <p className=\"text-red-500\" role=\"alert\">\n {loadError}\n </p>\n ) : null}\n <SelectField\n field={{\n label: typeof field.label === \"string\" ? field.label : \"Audience\",\n name: field.name,\n options: audiences.map((a) => ({ label: a.name, value: a.id })),\n required: field.required === true,\n type: \"select\",\n }}\n path={path}\n readOnly={readOnly}\n />\n </div>\n )\n}\n\nexport function MarketingMenu({ basePath = \"\" }: { basePath?: string }) {\n return (\n <NavGroup isOpen label=\"Marketing\">\n <Link className=\"nav__link\" href={marketingAdminHref(basePath, \"audience\")}>\n Audience\n </Link>\n <Link className=\"nav__link\" href={marketingAdminHref(basePath, \"broadcast\")}>\n Broadcast\n </Link>\n </NavGroup>\n )\n}\n\nexport function AudienceTable({ audiences }: { audiences: Array<{ id: string; name: string }> }) {\n return (\n <table>\n <tbody>\n {audiences.map((audience) => (\n <tr key={audience.id}>\n <td>{audience.name}</td>\n </tr>\n ))}\n </tbody>\n </table>\n )\n}\n\nexport function ContactsTable({ contacts }: { contacts: Array<{ email: string; id: string }> }) {\n return (\n <table>\n <tbody>\n {contacts.map((contact) => (\n <tr key={contact.id}>\n <td>{contact.email}</td>\n </tr>\n ))}\n </tbody>\n </table>\n )\n}\n","export function joinAdminSegments(adminRoute: string, ...segments: string[]): string {\n const trimmedAdmin = adminRoute.replace(/\\/+$/, \"\") || \"\"\n const body = segments\n .flatMap((s) => String(s).split(\"/\"))\n .map((segment) => segment.replace(/^\\/+|\\/+$/g, \"\"))\n .filter(Boolean)\n .join(\"/\")\n const combined = trimmedAdmin === \"\" ? `/${body}` : `${trimmedAdmin}/${body}`\n return combined.replace(/\\/{2,}/g, \"/\")\n}\n\n/** Absolute admin hrefs for marketing custom views (`/admin`, optional plugin `basePath`, then path segments). */\nexport function marketingAdminHref(basePath: string | undefined, ...segments: string[]): string {\n const normalized = basePath?.replace(/^\\/+|\\/+$/g, \"\") ?? \"\"\n const parts = [normalized, ...segments].filter(Boolean)\n return joinAdminSegments(\"/admin\", ...parts)\n}\n","\"use client\"\n\nimport { useConfig } from \"@payloadcms/ui\"\nimport { useCallback, useMemo } from \"react\"\n\nexport function useMarketingApi() {\n const context = useConfig() as unknown as {\n routes?: { api?: string }\n serverURL?: string\n }\n\n const base = useMemo(\n () => `${context.serverURL ?? \"\"}${context.routes?.api ?? \"/api\"}`,\n [context.routes?.api, context.serverURL],\n )\n\n const requestJson = useCallback(\n async (pathSegment: string, init?: RequestInit): Promise<unknown> => {\n const { headers: _headersIgnored, ...restInit } = init ?? {}\n const headers = new Headers(init?.headers)\n if (restInit.body !== undefined && restInit.body !== \"\" && restInit.body !== null) {\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json\")\n }\n }\n\n const res = await fetch(`${base}${pathSegment}`, {\n ...restInit,\n credentials: \"include\",\n headers,\n })\n\n if (!res.ok) {\n let detail = `${res.status} ${res.statusText}`\n try {\n const parsed = (await res.json()) as { message?: string }\n if (typeof parsed.message === \"string\") detail = parsed.message\n } catch {\n //\n }\n throw new Error(detail)\n }\n\n if (res.status === 204) {\n return undefined\n }\n\n try {\n return await res.json()\n } catch {\n return undefined\n }\n },\n [base],\n )\n\n return { base, requestJson }\n}\n","\"use client\"\n\nimport {\n Button as PayloadButton,\n DateTimeField,\n Form,\n Pagination,\n Pill,\n Table,\n toast,\n useModal,\n useTranslation,\n} from \"@payloadcms/ui\"\nimport Link from \"next/link\"\nimport { useRouter } from \"next/navigation\"\nimport React from \"react\"\n\nimport { formatMarketingDate } from \"../date-format\"\nimport { useMarketingApi } from \"../use-marketing-api\"\nimport {\n PayloadModal,\n PayloadModalBody,\n PayloadModalClose,\n PayloadModalContent,\n PayloadModalFooter,\n PayloadModalTitle,\n} from \"./payload-modal\"\n\nimport type { MarketingBroadcast } from \"../../types\"\nimport type { Data, FormState } from \"payload\"\n\nexport interface MarketingBroadcastRow extends MarketingBroadcast {\n externalDashboardUrl?: string\n}\n\ninterface BroadcastsTableProps {\n broadcasts: MarketingBroadcastRow[]\n}\n\nexport function BroadcastsTable({ broadcasts }: BroadcastsTableProps) {\n const [page, setPage] = React.useState(1)\n const [limit] = React.useState(100)\n const currentPage = React.useMemo(\n () => broadcasts.slice((page - 1) * limit, page * limit),\n [broadcasts, limit, page],\n )\n\n return (\n <>\n <Table\n columns={[\n {\n Heading: \"Campaign name\",\n accessor: \"name\",\n active: true,\n field: { name: \"name\", type: \"text\" },\n renderedCells: currentPage.map((b) => <div key={b.id}>{b.name}</div>),\n },\n {\n Heading: \"Schedule date\",\n accessor: \"scheduledAt\",\n active: true,\n field: { name: \"scheduledAt\", type: \"text\" },\n renderedCells: currentPage.map((b) => (\n <div key={b.id}>{formatMarketingDate(b.scheduledAt)}</div>\n )),\n },\n {\n Heading: \"Sent date\",\n accessor: \"sentAt\",\n active: true,\n field: { name: \"sentAt\", type: \"text\" },\n renderedCells: currentPage.map((b) => (\n <div key={b.id}>{formatMarketingDate(b.sentAt)}</div>\n )),\n },\n {\n Heading: \"Status\",\n accessor: \"status\",\n active: true,\n field: { name: \"status\", type: \"text\" },\n renderedCells: currentPage.map((broadcast) => (\n <div key={broadcast.id}>\n <BroadcastStatusPill status={broadcast.status} />\n </div>\n )),\n },\n {\n Heading: \"\",\n accessor: \"\",\n active: true,\n field: { name: \"_\", type: \"text\" },\n renderedCells: currentPage.map((broadcast) => (\n <div key={broadcast.id} className=\"flex items-center gap-4 flex-wrap\">\n {broadcast.externalDashboardUrl ? (\n <Link\n href={broadcast.externalDashboardUrl}\n rel=\"noreferrer noopener\"\n target=\"_blank\"\n >\n <Pill>dashboard ↗</Pill>\n </Link>\n ) : null}\n {broadcast.status === \"draft\" || broadcast.status === \"save\" ? (\n <>\n <DeleteBroadcastButton\n broadcastId={broadcast.id}\n broadcastName={broadcast.name}\n />\n <SendBroadcastButton\n broadcastId={broadcast.id}\n broadcastName={broadcast.name}\n />\n </>\n ) : null}\n </div>\n )),\n },\n ]}\n data={currentPage as unknown as Record<string, unknown>[]}\n />\n <Pagination\n hasNextPage={page * limit < broadcasts.length}\n hasPrevPage={page > 1}\n limit={limit}\n onChange={(p) => setPage(p)}\n page={page}\n totalPages={Math.ceil(broadcasts.length / limit)}\n />\n </>\n )\n}\n\nfunction BroadcastStatusPill({ status }: { status: string }) {\n switch (status) {\n case \"draft\":\n case \"save\": {\n return <Pill pillStyle=\"light-gray\">Draft</Pill>\n }\n case \"queued\": {\n return <Pill pillStyle=\"white\">Queued</Pill>\n }\n case \"sent\": {\n return <Pill pillStyle=\"success\">Sent</Pill>\n }\n default: {\n return <Pill>{status}</Pill>\n }\n }\n}\n\nfunction DeleteBroadcastButton({\n broadcastId,\n broadcastName,\n}: {\n broadcastId: string\n broadcastName: string\n}) {\n const modal = useModal()\n const { t } = useTranslation()\n const router = useRouter()\n const { requestJson } = useMarketingApi()\n const modalSlug = `delete-broadcast_${broadcastId}`\n\n async function confirmDelete(): Promise<void> {\n await requestJson(`/marketing/broadcasts/${encodeURIComponent(broadcastId)}`, {\n method: \"DELETE\",\n })\n toast.success(t(\"general:deletedSuccessfully\"))\n modal.closeModal(modalSlug)\n router.refresh()\n }\n\n return (\n <>\n <PayloadModal slug={modalSlug}>\n <PayloadModalContent>\n <PayloadModalBody>\n <PayloadModalTitle>{t(\"general:confirmDeletion\")}</PayloadModalTitle>\n <p>{`Delete draft broadcast \"${broadcastName}\"?`}</p>\n </PayloadModalBody>\n <PayloadModalFooter style={{ display: \"flex\", gap: \"8px\", justifyContent: \"flex-end\" }}>\n <PayloadModalClose />\n <PayloadButton\n buttonStyle=\"error\"\n onClick={() => {\n void confirmDelete().catch((err: unknown) =>\n toast.error(err instanceof Error ? err.message : \"Delete failed\"),\n )\n }}\n >\n {t(\"general:confirm\")}\n </PayloadButton>\n </PayloadModalFooter>\n </PayloadModalContent>\n </PayloadModal>\n <button onClick={() => modal.openModal(modalSlug)} type=\"button\">\n {t(\"general:delete\")}\n </button>\n </>\n )\n}\n\nexport function SendBroadcastButton({\n broadcastId,\n broadcastName,\n}: {\n broadcastId: string\n broadcastName: string\n}) {\n const modal = useModal()\n const { t } = useTranslation()\n const router = useRouter()\n const { requestJson } = useMarketingApi()\n const modalSlug = `send-broadcast_${broadcastId}`\n\n const submit = (fields: FormState, data: Data) => {\n const row = data as Record<string, unknown>\n const scheduledRaw: unknown = row.scheduledAt\n let scheduledAt = \"\"\n if (typeof scheduledRaw === \"string\") {\n scheduledAt = scheduledRaw\n } else if (scheduledRaw instanceof Date) {\n scheduledAt = scheduledRaw.toISOString()\n }\n\n void (async () => {\n try {\n await requestJson(`/marketing/broadcasts/${encodeURIComponent(broadcastId)}/send`, {\n body: JSON.stringify({\n ...(scheduledAt && scheduledAt.trim() !== \"\"\n ? { scheduledAt: scheduledAt.trim() }\n : {}),\n }),\n method: \"POST\",\n })\n modal.closeModal(modalSlug)\n toast.success(t(\"general:success\"))\n router.refresh()\n } catch (err) {\n toast.error(err instanceof Error ? err.message : \"Send failed\")\n }\n })()\n }\n\n return (\n <>\n <PayloadModal slug={modalSlug}>\n <PayloadModalContent style={{ width: \"100%\", maxWidth: \"24rem\" }}>\n <PayloadModalBody>\n <PayloadModalTitle>{`Send “${broadcastName}”`}</PayloadModalTitle>\n <Form initialState={{}} onSubmit={submit} waitForAutocomplete>\n <DateTimeField\n field={{\n label: \"Schedule at (optional)\",\n name: \"scheduledAt\",\n required: false,\n }}\n path=\"scheduledAt\"\n />\n <PayloadModalFooter\n style={{ display: \"flex\", gap: \"8px\", justifyContent: \"flex-end\" }}\n >\n <PayloadModalClose />\n <PayloadButton size=\"large\" type=\"submit\">\n Send\n </PayloadButton>\n </PayloadModalFooter>\n </Form>\n </PayloadModalBody>\n </PayloadModalContent>\n </PayloadModal>\n <Pill pillStyle=\"dark\" onClick={() => modal.openModal(modalSlug)} size=\"small\">\n Send\n </Pill>\n </>\n )\n}\n","export function formatMarketingDate(iso?: string | null): string {\n if (!iso) return \"—\"\n const d = new Date(iso)\n return Number.isNaN(d.valueOf())\n ? String(iso)\n : new Intl.DateTimeFormat(undefined, { dateStyle: \"medium\" }).format(d)\n}\n","\"use client\"\n\nimport { Button, Modal, useModal, useTranslation } from \"@payloadcms/ui\"\nimport React from \"react\"\n\nfunction cn(...parts: Array<string | undefined | false | null>): string {\n return parts.filter(Boolean).join(\" \")\n}\n\nconst PayloadModalContext = React.createContext({\n modalSlug: \"\",\n})\n\ninterface PayloadModalProps extends React.ComponentProps<typeof Modal> {\n children: React.ReactNode\n}\n\nfunction PayloadModal({ children, className, slug: modalSlug, ...props }: PayloadModalProps) {\n const [loaded, setLoaded] = React.useState(false)\n\n React.useEffect(() => {\n setLoaded(true)\n }, [])\n\n return (\n <PayloadModalContext.Provider value={{ modalSlug }}>\n {/* Fix hydration error */}\n {loaded && (\n <Modal slug={modalSlug} className={cn(\"confirmation-modal\", className)} {...props}>\n {children}\n </Modal>\n )}\n </PayloadModalContext.Provider>\n )\n}\n\nfunction PayloadModalContent({ children, className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n className={cn(\"confirmation-modal__wrapper\", className)}\n data-slot=\"payload-modal-content\"\n {...props}\n >\n {children}\n </div>\n )\n}\n\nfunction PayloadModalBody({ children, className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n className={cn(\"confirmation-modal__content\", className)}\n data-slot=\"payload-modal-body\"\n {...props}\n >\n {children}\n </div>\n )\n}\n\nfunction PayloadModalTitle({ children, className, ...props }: React.ComponentProps<\"h1\">) {\n return (\n <h1 className={cn(\"\", className)} data-slot=\"payload-modal-title\" {...props}>\n {children}\n </h1>\n )\n}\n\nfunction PayloadModalFooter({ children, className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n className={cn(\"confirmation-modal__controls\", className)}\n data-slot=\"payload-modal-footer\"\n {...props}\n >\n {children}\n </div>\n )\n}\n\nfunction PayloadModalClose({ children, className, ...props }: React.ComponentProps<typeof Button>) {\n const { t } = useTranslation()\n const { modalSlug } = React.useContext(PayloadModalContext)\n const modal = useModal()\n\n return (\n <Button\n className={cn(\"\", className)}\n buttonStyle=\"secondary\"\n size=\"large\"\n data-slot=\"payload-modal-close\"\n {...props}\n onClick={() => modal.closeModal(modalSlug)}\n >\n {children ?? t(\"general:cancel\")}\n </Button>\n )\n}\n\nexport {\n PayloadModal,\n PayloadModalContent,\n PayloadModalBody,\n PayloadModalTitle,\n PayloadModalFooter,\n PayloadModalClose,\n}\n","\"use client\"\n\nimport {\n Button,\n Form,\n TextField,\n toast,\n useModal,\n useTranslation,\n Button as PayloadButton,\n} from \"@payloadcms/ui\"\nimport { useRouter } from \"next/navigation\"\nimport { useTransition } from \"react\"\n\nimport { useMarketingApi } from \"../use-marketing-api\"\nimport {\n PayloadModal,\n PayloadModalBody,\n PayloadModalClose,\n PayloadModalContent,\n PayloadModalFooter,\n PayloadModalTitle,\n} from \"./payload-modal\"\n\nimport type { Data, FormState } from \"payload\"\n\nconst createAudienceSlug = \"create-audience\"\n\nexport function CreateAudienceButton() {\n const modal = useModal()\n const { t } = useTranslation()\n\n return (\n <>\n <CreateAudienceModal />\n <Button onClick={() => modal.openModal(createAudienceSlug)} size=\"large\" type=\"button\">\n {t(\"general:createNew\")}\n </Button>\n </>\n )\n}\n\nfunction CreateAudienceModal() {\n const modal = useModal()\n const { t } = useTranslation()\n const router = useRouter()\n const { requestJson } = useMarketingApi()\n const [isPending, startTransition] = useTransition()\n\n const submit = (fields: FormState, data: Data) => {\n const row = data as Record<string, unknown>\n const nameRaw: unknown = row.audienceName\n const name = typeof nameRaw === \"string\" ? nameRaw.trim() : \"\"\n startTransition(() => {\n void (async () => {\n try {\n if (!name) {\n throw new Error(\"Name is required.\")\n }\n await requestJson(\"/marketing/audiences\", {\n body: JSON.stringify({ name }),\n method: \"POST\",\n })\n modal.closeModal(createAudienceSlug)\n toast.success(t(\"general:successfullyCreated\"))\n router.refresh()\n } catch (err) {\n toast.error(err instanceof Error ? err.message : \"Create failed\")\n }\n })()\n })\n }\n\n return (\n <PayloadModal closeOnBlur slug={createAudienceSlug}>\n <PayloadModalContent style={{ width: \"100%\", maxWidth: \"24rem\" }}>\n <Form initialState={{}} onSubmit={submit} waitForAutocomplete>\n <PayloadModalBody>\n <PayloadModalTitle>{t(\"general:createNew\")}</PayloadModalTitle>\n <TextField\n field={{\n name: \"audienceName\",\n required: true,\n type: \"text\",\n }}\n path=\"audienceName\"\n validate={(v) =>\n typeof v === \"string\" && v.trim().length > 0 ? true : \"Name is required\"\n }\n />\n <PayloadModalFooter style={{ display: \"flex\", gap: \"8px\", justifyContent: \"flex-end\" }}>\n <PayloadModalClose />\n <Button disabled={isPending} size=\"large\" type=\"submit\">\n {t(\"general:confirm\")}\n </Button>\n </PayloadModalFooter>\n </PayloadModalBody>\n </Form>\n </PayloadModalContent>\n </PayloadModal>\n )\n}\n\nexport function DeleteAudienceButton({\n audienceId,\n audienceName,\n}: {\n audienceId: string\n audienceName: string\n}) {\n const modal = useModal()\n const { t } = useTranslation()\n const router = useRouter()\n const { requestJson } = useMarketingApi()\n const modalSlug = `delete-audience_${audienceId}`\n\n async function confirmDelete(): Promise<void> {\n await requestJson(`/marketing/audiences/${encodeURIComponent(audienceId)}`, {\n method: \"DELETE\",\n })\n toast.success(t(\"general:deletedSuccessfully\"))\n modal.closeModal(modalSlug)\n router.refresh()\n }\n\n return (\n <>\n <PayloadModal slug={modalSlug}>\n <PayloadModalContent>\n <PayloadModalBody>\n <PayloadModalTitle>{t(\"general:confirmDeletion\")}</PayloadModalTitle>\n <p>{`Remove audience \"${audienceName}\"?`}</p>\n </PayloadModalBody>\n <PayloadModalFooter style={{ display: \"flex\", gap: \"8px\", justifyContent: \"flex-end\" }}>\n <PayloadModalClose />\n <PayloadButton\n buttonStyle=\"error\"\n onClick={() => {\n void confirmDelete().catch((err: unknown) =>\n toast.error(err instanceof Error ? err.message : \"Delete failed\"),\n )\n }}\n >\n {t(\"general:confirm\")}\n </PayloadButton>\n </PayloadModalFooter>\n </PayloadModalContent>\n </PayloadModal>\n <button onClick={() => modal.openModal(modalSlug)} type=\"button\">\n {t(\"general:delete\")}\n </button>\n </>\n )\n}\n","\"use client\"\n\nimport {\n CheckboxField,\n Form,\n Pagination,\n Table,\n TextField,\n toast,\n useModal,\n useTranslation,\n Button as PayloadButton,\n} from \"@payloadcms/ui\"\nimport { useRouter } from \"next/navigation\"\nimport React from \"react\"\nimport ReactRaw from \"react\"\nimport { useTransition } from \"react\"\n\nimport { formatMarketingDate } from \"../date-format\"\nimport { useMarketingApi } from \"../use-marketing-api\"\nimport {\n PayloadModal,\n PayloadModalBody,\n PayloadModalClose,\n PayloadModalContent,\n PayloadModalFooter,\n} from \"./payload-modal\"\n\nimport type { MarketingContact } from \"../../types\"\nimport type { Data, FormState } from \"payload\"\n\ninterface ContactsTableProps {\n audienceId: string\n contacts: MarketingContact[]\n}\n\nexport function ContactsTable({ audienceId, contacts }: ContactsTableProps) {\n const [page, setPage] = React.useState(1)\n const [limit] = React.useState(100)\n const currentPage = React.useMemo(\n () => contacts.slice((page - 1) * limit, page * limit),\n [contacts, limit, page],\n )\n\n return (\n <>\n <Table\n columns={[\n {\n Heading: \"First name\",\n accessor: \"firstName\",\n active: true,\n field: { name: \"firstName\", type: \"text\" },\n renderedCells: currentPage.map((contact) => (\n <div key={contact.id}>{contact.firstName ?? \"—\"}</div>\n )),\n },\n {\n Heading: \"Last name\",\n accessor: \"lastName\",\n active: true,\n field: { name: \"lastName\", type: \"text\" },\n renderedCells: currentPage.map((contact) => (\n <div key={contact.id}>{contact.lastName ?? \"—\"}</div>\n )),\n },\n {\n Heading: \"Email\",\n accessor: \"email\",\n active: true,\n field: { name: \"email\", type: \"text\" },\n renderedCells: currentPage.map((contact) => (\n <div key={contact.id}>{contact.email}</div>\n )),\n },\n {\n Heading: \"Created\",\n accessor: \"createdAt\",\n active: true,\n field: { name: \"createdAt\", type: \"text\" },\n renderedCells: currentPage.map((contact) => (\n <div key={contact.id}>{formatMarketingDate(contact.createdAt)}</div>\n )),\n },\n {\n Heading: \"Status\",\n accessor: \"subscribed\",\n active: true,\n field: { name: \"subscribed\", type: \"text\" },\n renderedCells: currentPage.map((contact) => (\n <div key={contact.id} className=\"flex items-center gap-4 flex-wrap\">\n <span>{contact.subscribed !== false ? \"✅ subscribed\" : \"❌ unsubscribed\"}</span>\n <EditContactButton audienceId={audienceId} contact={contact} />\n <DeleteContactButton\n audienceId={audienceId}\n contactEmail={contact.email}\n contactId={contact.id}\n />\n </div>\n )),\n },\n ]}\n data={currentPage as unknown as Record<string, unknown>[]}\n />\n <Pagination\n hasNextPage={page * limit < contacts.length}\n hasPrevPage={page > 1}\n limit={limit}\n onChange={(p) => {\n setPage(p)\n }}\n page={page}\n totalPages={Math.ceil(contacts.length / limit)}\n />\n </>\n )\n}\n\nfunction DeleteContactButton({\n audienceId,\n contactId,\n contactEmail,\n}: {\n audienceId: string\n contactEmail: string\n contactId: string\n}) {\n const modal = useModal()\n const { t } = useTranslation()\n const router = useRouter()\n const { requestJson } = useMarketingApi()\n const modalSlug = `delete-contact_${contactId}`\n\n async function confirmDelete(): Promise<void> {\n await requestJson(\n `/marketing/audiences/${encodeURIComponent(audienceId)}/contacts/${encodeURIComponent(contactId)}`,\n {\n method: \"DELETE\",\n },\n )\n toast.success(t(\"general:deletedSuccessfully\"))\n modal.closeModal(modalSlug)\n router.refresh()\n }\n\n return (\n <>\n <PayloadModal slug={modalSlug}>\n <PayloadModalContent>\n <PayloadModalBody>\n <h1>{t(\"general:confirmDeletion\")}</h1>\n <p>{`Remove contact ${contactEmail}?`}</p>\n </PayloadModalBody>\n <PayloadModalFooter style={{ display: \"flex\", gap: \"8px\", justifyContent: \"flex-end\" }}>\n <PayloadModalClose />\n <PayloadButton\n buttonStyle=\"error\"\n onClick={() => {\n void confirmDelete().catch((err: unknown) =>\n toast.error(err instanceof Error ? err.message : \"Delete failed\"),\n )\n }}\n >\n {t(\"general:confirm\")}\n </PayloadButton>\n </PayloadModalFooter>\n </PayloadModalContent>\n </PayloadModal>\n <button onClick={() => modal.openModal(modalSlug)} type=\"button\">\n {t(\"general:delete\")}\n </button>\n </>\n )\n}\n\nexport function EditContactButton({\n audienceId,\n contact,\n}: {\n audienceId: string\n contact: MarketingContact | null\n}) {\n const modal = useModal()\n const { t } = useTranslation()\n const id = ReactRaw.useId()\n const slug = contact ? `edit-contact_${contact.id}` : `create-contact_${audienceId}_${id}`\n\n const ButtonComp = contact ? \"button\" : PayloadButton\n\n return (\n <>\n <EditContactModalWrapper audienceId={audienceId} contact={contact} modalSlug={slug} />\n <ButtonComp\n {...(contact ? {} : { size: \"large\" })}\n onClick={() => modal.openModal(slug)}\n type=\"button\"\n >\n {contact ? t(\"general:edit\") : t(\"general:createNew\")}\n </ButtonComp>\n </>\n )\n}\n\nfunction EditContactModalWrapper({\n audienceId,\n contact,\n modalSlug,\n}: {\n audienceId: string\n contact: MarketingContact | null\n modalSlug: string\n}) {\n const modal = useModal()\n const { t } = useTranslation()\n const router = useRouter()\n const { requestJson } = useMarketingApi()\n const [, startTransition] = useTransition()\n\n const submit = (fields: FormState, data: Data) => {\n const email = data.email as string\n const firstName = String(data.firstName ?? \"\")\n const lastName = String(data.lastName ?? \"\")\n const subscribed = Boolean(data.subscribed)\n\n startTransition(() => {\n void (async () => {\n try {\n await requestJson(\"/marketing/contacts\", {\n body: JSON.stringify({\n audienceId,\n email,\n firstName,\n id: contact?.id,\n lastName,\n subscribed,\n }),\n method: \"POST\",\n })\n toast.success(contact ? \"Contact updated.\" : \"Contact created.\")\n modal.closeModal(modalSlug)\n router.refresh()\n } catch (err) {\n toast.error(err instanceof Error ? err.message : \"Save failed\")\n }\n })()\n })\n }\n\n return (\n <PayloadModal slug={modalSlug}>\n <PayloadModalContent style={{ width: \"100%\", maxWidth: \"32rem\" }}>\n <PayloadModalBody>\n <h1>{contact ? t(\"general:edit\") : t(\"general:createNew\")}</h1>\n <Form\n className=\"flex w-full flex-col gap-6\"\n initialState={{\n email: {\n value: contact?.email ?? \"\",\n },\n firstName: {\n value: contact?.firstName ?? \"\",\n },\n lastName: {\n value: contact?.lastName ?? \"\",\n },\n subscribed: {\n value: contact?.subscribed !== false,\n },\n }}\n onSubmit={submit}\n waitForAutocomplete\n >\n <TextField\n field={{\n label: t(\"general:email\"),\n name: \"email\",\n required: true,\n type: \"text\",\n }}\n path=\"email\"\n validate={(v) =>\n typeof v === \"string\" && v.includes(\"@\") ? true : \"Valid email required\"\n }\n />\n <TextField\n field={{\n label: \"First name\",\n name: \"firstName\",\n type: \"text\",\n }}\n path=\"firstName\"\n />\n <TextField\n field={{\n label: \"Last name\",\n name: \"lastName\",\n type: \"text\",\n }}\n path=\"lastName\"\n />\n <CheckboxField\n field={{\n label: \"Subscribed\",\n name: \"subscribed\",\n }}\n path=\"subscribed\"\n />\n <PayloadModalFooter style={{ display: \"flex\", gap: \"8px\", justifyContent: \"flex-end\" }}>\n <PayloadModalClose />\n <PayloadButton size=\"large\" type=\"submit\">\n {contact ? t(\"general:save\") : t(\"general:create\")}\n </PayloadButton>\n </PayloadModalFooter>\n </Form>\n </PayloadModalBody>\n </PayloadModalContent>\n </PayloadModal>\n )\n}\n","\"use client\"\n\nimport {\n Button,\n Drawer,\n Form,\n SelectField,\n TextareaField,\n TextField,\n toast,\n useFormFields,\n useModal,\n useTranslation,\n} from \"@payloadcms/ui\"\nimport { useRouter } from \"next/navigation\"\nimport React from \"react\"\n\nimport { useMarketingApi } from \"../use-marketing-api\"\n\nimport type { MarketingAudience } from \"../../types\"\nimport type { Data, FormState } from \"payload\"\n\nconst modalSlug = \"create-marketing-broadcast\"\n\nexport function CreateBroadcastButton({\n audiences,\n emailBroadcastTemplates,\n localeOptions,\n provider,\n}: {\n audiences: MarketingAudience[]\n emailBroadcastTemplates?: { id: string; name: string }[]\n localeOptions?: { label: string; value: string }[]\n provider: string\n}) {\n const modal = useModal()\n const { t } = useTranslation()\n\n return (\n <>\n <CreateBroadcastModal\n audiences={audiences}\n emailBroadcastTemplates={emailBroadcastTemplates}\n localeOptions={localeOptions ?? []}\n provider={provider}\n />\n <Button onClick={() => modal.openModal(modalSlug)} size=\"large\" type=\"button\">\n {t(\"general:createNew\")}\n </Button>\n </>\n )\n}\n\nfunction BroadcastContentFields({\n localeOptions,\n showReactEmailTemplates,\n showTemplateField,\n templateOptions,\n}: {\n localeOptions: { label: string; value: string }[]\n showReactEmailTemplates: boolean\n showTemplateField: boolean\n templateOptions: { id: string; name: string }[]\n}) {\n const broadcastFormat = useFormFields(([fields]) => {\n if (!showReactEmailTemplates) {\n return \"html\"\n }\n const v = fields?.broadcastFormat?.value\n return typeof v === \"string\" && v !== \"\" ? v : \"html\"\n })\n\n if (showReactEmailTemplates) {\n const isHtml = broadcastFormat === \"html\"\n return (\n <>\n <SelectField\n field={{\n label: \"Email content\",\n name: \"broadcastFormat\",\n options: [\n { label: \"<html>\", value: \"html\" },\n ...templateOptions.map((opt) => ({ label: opt.name, value: opt.id })),\n ],\n required: true,\n }}\n path=\"broadcastFormat\"\n />\n {!isHtml && localeOptions.length > 0 ? (\n <SelectField\n field={{\n label: \"Locale\",\n name: \"locale\",\n options: localeOptions,\n required: false,\n }}\n path=\"locale\"\n />\n ) : null}\n {isHtml ? (\n <TextareaField\n field={{\n admin: {\n description: \"Shown when Email content is <html>.\",\n },\n label: \"<html>\",\n name: \"htmlBody\",\n required: true,\n }}\n path=\"htmlBody\"\n />\n ) : null}\n </>\n )\n }\n\n return (\n <>\n {showTemplateField ? (\n <TextField\n field={{\n admin: {\n description:\n \"Optional Mailchimp saved-template id (numeric string). Leave <html> empty when using templates.\",\n },\n label: \"Template id\",\n name: \"templateId\",\n required: false,\n type: \"text\",\n }}\n path=\"templateId\"\n />\n ) : null}\n <TextareaField\n field={{\n admin: {\n description: showTemplateField\n ? \"Optional plain HTML alternative to a Mailchimp template.\"\n : \"HTML broadcast body.\",\n },\n label: showTemplateField ? \"<html> (optional with Mailchimp)\" : \"<html>\",\n name: \"htmlBody\",\n required: !showTemplateField,\n }}\n path=\"htmlBody\"\n />\n </>\n )\n}\n\nfunction CreateBroadcastModal({\n audiences,\n emailBroadcastTemplates,\n localeOptions,\n provider,\n}: {\n audiences: MarketingAudience[]\n emailBroadcastTemplates?: { id: string; name: string }[]\n localeOptions: { label: string; value: string }[]\n provider: string\n}) {\n const router = useRouter()\n const modal = useModal()\n const { t } = useTranslation()\n const { requestJson } = useMarketingApi()\n const [isPending, setIsPending] = React.useState(false)\n\n const showTemplateField = provider === \"mailchimp\"\n const templateOptions = emailBroadcastTemplates ?? []\n const showReactEmailTemplates = provider === \"resend\" && templateOptions.length > 0\n\n const submit = (fields: FormState, data: Data) => {\n const row = data as Record<string, unknown>\n const audienceIdRaw: unknown = row.audienceId\n const nameRaw: unknown = row.name\n const subjectRaw: unknown = row.subject\n const htmlRaw: unknown = row.htmlBody\n const templateIdRaw: unknown = row.templateId\n const replyToRaw: unknown = row.replyTo\n\n const broadcastFormatRaw: unknown = row.broadcastFormat\n const localeRaw: unknown = row.locale\n\n const audienceId = typeof audienceIdRaw === \"string\" ? audienceIdRaw : \"\"\n const name = typeof nameRaw === \"string\" ? nameRaw.trim() : \"\"\n const subject = typeof subjectRaw === \"string\" ? subjectRaw.trim() : \"\"\n const htmlBody = typeof htmlRaw === \"string\" ? htmlRaw.trim() : \"\"\n const templateId = typeof templateIdRaw === \"string\" ? templateIdRaw.trim() : \"\"\n const replyTo = typeof replyToRaw === \"string\" ? replyToRaw.trim() : \"\"\n const broadcastFormat =\n typeof broadcastFormatRaw === \"string\" ? broadcastFormatRaw.trim() : \"html\"\n const locale = typeof localeRaw === \"string\" ? localeRaw.trim() : \"\"\n\n setIsPending(true)\n void (async () => {\n try {\n if (!audienceId || !name || !subject) {\n throw new Error(\"Audience, name, and subject are required.\")\n }\n\n const mailchimpMode = showTemplateField\n const usingReactTemplate = showReactEmailTemplates && broadcastFormat !== \"html\"\n\n if (showReactEmailTemplates) {\n if (usingReactTemplate) {\n await requestJson(\"/marketing/broadcasts\", {\n body: JSON.stringify({\n audienceId,\n emailTemplateId: broadcastFormat,\n locale: locale || undefined,\n name,\n replyTo,\n subject,\n }),\n method: \"POST\",\n })\n } else if (!htmlBody) {\n throw new Error(\"<html> is required when using the <html> content option.\")\n } else {\n await requestJson(\"/marketing/broadcasts\", {\n body: JSON.stringify({\n audienceId,\n html: htmlBody,\n name,\n replyTo,\n subject,\n }),\n method: \"POST\",\n })\n }\n } else if (mailchimpMode) {\n if (!templateId && !htmlBody) {\n throw new Error(\"Provide <html> or a Mailchimp template id.\")\n }\n if (templateId && htmlBody) {\n throw new Error(\"Use either <html> or Mailchimp template id, not both.\")\n }\n await requestJson(\"/marketing/broadcasts\", {\n body: JSON.stringify({\n audienceId,\n html: htmlBody,\n name,\n replyTo,\n subject,\n templateId: templateId || undefined,\n }),\n method: \"POST\",\n })\n } else if (!htmlBody) {\n throw new Error(\"<html> is required for this provider.\")\n } else {\n await requestJson(\"/marketing/broadcasts\", {\n body: JSON.stringify({\n audienceId,\n html: htmlBody,\n name,\n replyTo,\n subject,\n }),\n method: \"POST\",\n })\n }\n\n modal.closeModal(modalSlug)\n toast.success(t(\"general:successfullyCreated\", { label: \"Broadcast\" }))\n router.refresh()\n } catch (err) {\n toast.error(err instanceof Error ? err.message : \"Create failed\")\n } finally {\n setIsPending(false)\n }\n })()\n }\n\n return (\n <Drawer slug={modalSlug} title={t(\"general:createNew\")}>\n <Form\n className=\"flex w-full flex-col gap-8\"\n initialState={showReactEmailTemplates ? { broadcastFormat: { value: \"html\" } } : {}}\n onSubmit={submit}\n waitForAutocomplete\n >\n <SelectField\n field={{\n label: \"Audience\",\n name: \"audienceId\",\n options: audiences.map((a) => ({ label: a.name, value: a.id })),\n required: true,\n }}\n path=\"audienceId\"\n />\n <TextField\n field={{\n label: \"Campaign name\",\n name: \"name\",\n required: true,\n type: \"text\",\n }}\n path=\"name\"\n />\n <TextField\n field={{\n label: \"Subject\",\n name: \"subject\",\n required: true,\n type: \"text\",\n }}\n path=\"subject\"\n />\n <TextField\n field={{\n label: \"Reply-to (optional)\",\n name: \"replyTo\",\n type: \"text\",\n }}\n path=\"replyTo\"\n />\n\n <BroadcastContentFields\n localeOptions={localeOptions}\n showReactEmailTemplates={showReactEmailTemplates}\n showTemplateField={showTemplateField}\n templateOptions={templateOptions}\n />\n\n <Button className=\"w-full\" disabled={isPending} type=\"submit\">\n {t(\"general:create\")}\n </Button>\n </Form>\n </Drawer>\n )\n}\n"],"mappings":"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","names":["client_exports","__export","ContactsTable","AudienceSelect","AudienceTable","BroadcastsTable","CreateAudienceButton","CreateBroadcastButton","DeleteAudienceButton","EditContactButton","MarketingMenu","__toCommonJS","import_ui","import_react","joinAdminSegments","adminRoute","segments","trimmedAdmin","body","s","segment","marketingAdminHref","basePath","parts","import_ui","import_react","useMarketingApi","context","base","requestJson","pathSegment","init","_headersIgnored","restInit","headers","res","detail","parsed","import_jsx_runtime","parseAudiences","payload","out","item","rec","id","name","AudienceSelect","field","path","readOnly","requestJson","useMarketingApi","audiences","setAudiences","React","loadError","setLoadError","cancelled","data","err","a","MarketingMenu","basePath","marketingAdminHref","AudienceTable","audience","ContactsTable","contacts","contact","import_ui","import_link","import_navigation","import_react","formatMarketingDate","iso","d","import_ui","import_react","import_jsx_runtime","cn","parts","PayloadModalContext","React","PayloadModal","children","className","modalSlug","props","loaded","setLoaded","PayloadModalContent","PayloadModalBody","PayloadModalTitle","PayloadModalFooter","PayloadModalClose","t","modal","import_jsx_runtime","BroadcastsTable","broadcasts","page","setPage","React","limit","currentPage","b","formatMarketingDate","broadcast","BroadcastStatusPill","Link","DeleteBroadcastButton","SendBroadcastButton","p","status","broadcastId","broadcastName","modal","t","router","requestJson","useMarketingApi","modalSlug","confirmDelete","PayloadModal","PayloadModalContent","PayloadModalBody","PayloadModalTitle","PayloadModalFooter","PayloadModalClose","PayloadButton","err","submit","fields","data","scheduledRaw","scheduledAt","import_ui","import_navigation","import_react","import_jsx_runtime","createAudienceSlug","CreateAudienceButton","modal","t","CreateAudienceModal","router","requestJson","useMarketingApi","isPending","startTransition","PayloadModal","PayloadModalContent","fields","data","nameRaw","name","err","PayloadModalBody","PayloadModalTitle","v","PayloadModalFooter","PayloadModalClose","DeleteAudienceButton","audienceId","audienceName","modalSlug","confirmDelete","PayloadButton","import_ui","import_navigation","import_react","import_jsx_runtime","ContactsTable","audienceId","contacts","page","setPage","React","limit","currentPage","contact","formatMarketingDate","EditContactButton","DeleteContactButton","p","contactId","contactEmail","modal","t","router","requestJson","useMarketingApi","modalSlug","confirmDelete","PayloadModal","PayloadModalContent","PayloadModalBody","PayloadModalFooter","PayloadModalClose","PayloadButton","err","id","ReactRaw","slug","EditContactModalWrapper","startTransition","submit","fields","data","email","firstName","lastName","subscribed","v","import_ui","import_navigation","import_react","import_jsx_runtime","modalSlug","CreateBroadcastButton","audiences","emailBroadcastTemplates","localeOptions","provider","modal","t","CreateBroadcastModal","BroadcastContentFields","showReactEmailTemplates","showTemplateField","templateOptions","broadcastFormat","fields","v","isHtml","opt","router","requestJson","useMarketingApi","isPending","setIsPending","React","submit","data","row","audienceIdRaw","nameRaw","subjectRaw","htmlRaw","templateIdRaw","replyToRaw","broadcastFormatRaw","localeRaw","audienceId","name","subject","htmlBody","templateId","replyTo","locale","mailchimpMode","usingReactTemplate","err","a"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { AudienceSelect, AudienceTable, ContactsTable, MarketingMenu, } from "./components/marketing-components";
|
|
2
|
+
export { BroadcastsTable } from "./components/broadcasts-table";
|
|
3
|
+
export { CreateAudienceButton, DeleteAudienceButton } from "./components/audience-buttons";
|
|
4
|
+
export { ContactsTable as AudienceContactsTable, EditContactButton, } from "./components/contacts-table";
|
|
5
|
+
export { CreateBroadcastButton } from "./components/create-broadcast-button";
|
|
6
|
+
export type { MarketingBroadcastRow } from "./components/broadcasts-table";
|
|
7
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/admin/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,aAAa,EACb,aAAa,EACb,aAAa,GACd,MAAM,mCAAmC,CAAA;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAE/D,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AAE1F,OAAO,EACL,aAAa,IAAI,qBAAqB,EACtC,iBAAiB,GAClB,MAAM,6BAA6B,CAAA;AAEpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,sCAAsC,CAAA;AAE5E,YAAY,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import{Link as Pe,NavGroup as je,SelectField as Ue}from"@payloadcms/ui";import X from"react";function _e(t,...e){let o=t.replace(/\/+$/,"")||"",r=e.flatMap(a=>String(a).split("/")).map(a=>a.replace(/^\/+|\/+$/g,"")).filter(Boolean).join("/");return(o===""?`/${r}`:`${o}/${r}`).replace(/\/{2,}/g,"/")}function K(t,...e){let r=[t?.replace(/^\/+|\/+$/g,"")??"",...e].filter(Boolean);return _e("/admin",...r)}import{useConfig as Le}from"@payloadcms/ui";import{useCallback as He,useMemo as Je}from"react";function g(){let t=Le(),e=Je(()=>`${t.serverURL??""}${t.routes?.api??"/api"}`,[t.routes?.api,t.serverURL]),o=He(async(r,l)=>{let{headers:a,...n}=l??{},d=new Headers(l?.headers);n.body!==void 0&&n.body!==""&&n.body!==null&&(d.has("Content-Type")||d.set("Content-Type","application/json"));let i=await fetch(`${e}${r}`,{...n,credentials:"include",headers:d});if(!i.ok){let m=`${i.status} ${i.statusText}`;try{let f=await i.json();typeof f.message=="string"&&(m=f.message)}catch{}throw new Error(m)}if(i.status!==204)try{return await i.json()}catch{return}},[e]);return{base:e,requestJson:o}}import{jsx as h,jsxs as we}from"react/jsx-runtime";function ze(t){if(!Array.isArray(t))return[];let e=[];for(let o of t){if(!o||typeof o!="object")continue;let r=o,l=r.id,a=r.name;typeof l=="string"&&typeof a=="string"&&e.push({id:l,name:a})}return e}var We=({field:t,path:e,readOnly:o})=>{let{requestJson:r}=g(),[l,a]=X.useState([]),[n,d]=X.useState(null);return X.useEffect(()=>{let i=!1;return(async()=>{try{let m=await r("/marketing/audiences");i||(a(ze(m)),d(null))}catch(m){i||d(m instanceof Error?m.message:"Failed to load audiences")}})(),()=>{i=!0}},[r]),we("div",{className:"field-type relative",children:[n?h("p",{className:"text-red-500",role:"alert",children:n}):null,h(Ue,{field:{label:typeof t.label=="string"?t.label:"Audience",name:t.name,options:l.map(i=>({label:i.name,value:i.id})),required:t.required===!0,type:"select"},path:e,readOnly:o})]})};function Ge({basePath:t=""}){return we(je,{isOpen:!0,label:"Marketing",children:[h(Pe,{className:"nav__link",href:K(t,"audience"),children:"Audience"}),h(Pe,{className:"nav__link",href:K(t,"broadcast"),children:"Broadcast"})]})}function Qe({audiences:t}){return h("table",{children:h("tbody",{children:t.map(e=>h("tr",{children:h("td",{children:e.name})},e.id))})})}function Ve({contacts:t}){return h("table",{children:h("tbody",{children:t.map(e=>h("tr",{children:h("td",{children:e.email})},e.id))})})}import{Button as Ce,DateTimeField as et,Form as tt,Pagination as at,Pill as D,Table as ot,toast as j,useModal as ve,useTranslation as xe}from"@payloadcms/ui";import nt from"next/link";import{useRouter as Se}from"next/navigation";import Y from"react";function $(t){if(!t)return"\u2014";let e=new Date(t);return Number.isNaN(e.valueOf())?String(t):new Intl.DateTimeFormat(void 0,{dateStyle:"medium"}).format(e)}import{Button as Ke,Modal as Xe,useModal as Ye,useTranslation as Ze}from"@payloadcms/ui";import J from"react";import{jsx as T}from"react/jsx-runtime";function F(...t){return t.filter(Boolean).join(" ")}var ke=J.createContext({modalSlug:""});function w({children:t,className:e,slug:o,...r}){let[l,a]=J.useState(!1);return J.useEffect(()=>{a(!0)},[]),T(ke.Provider,{value:{modalSlug:o},children:l&&T(Xe,{slug:o,className:F("confirmation-modal",e),...r,children:t})})}function k({children:t,className:e,...o}){return T("div",{className:F("confirmation-modal__wrapper",e),"data-slot":"payload-modal-content",...o,children:t})}function C({children:t,className:e,...o}){return T("div",{className:F("confirmation-modal__content",e),"data-slot":"payload-modal-body",...o,children:t})}function R({children:t,className:e,...o}){return T("h1",{className:F("",e),"data-slot":"payload-modal-title",...o,children:t})}function v({children:t,className:e,...o}){return T("div",{className:F("confirmation-modal__controls",e),"data-slot":"payload-modal-footer",...o,children:t})}function x({children:t,className:e,...o}){let{t:r}=Ze(),{modalSlug:l}=J.useContext(ke),a=Ye();return T(Ke,{className:F("",e),buttonStyle:"secondary",size:"large","data-slot":"payload-modal-close",...o,onClick:()=>a.closeModal(l),children:t??r("general:cancel")})}import{Fragment as U,jsx as s,jsxs as M}from"react/jsx-runtime";function rt({broadcasts:t}){let[e,o]=Y.useState(1),[r]=Y.useState(100),l=Y.useMemo(()=>t.slice((e-1)*r,e*r),[t,r,e]);return M(U,{children:[s(ot,{columns:[{Heading:"Campaign name",accessor:"name",active:!0,field:{name:"name",type:"text"},renderedCells:l.map(a=>s("div",{children:a.name},a.id))},{Heading:"Schedule date",accessor:"scheduledAt",active:!0,field:{name:"scheduledAt",type:"text"},renderedCells:l.map(a=>s("div",{children:$(a.scheduledAt)},a.id))},{Heading:"Sent date",accessor:"sentAt",active:!0,field:{name:"sentAt",type:"text"},renderedCells:l.map(a=>s("div",{children:$(a.sentAt)},a.id))},{Heading:"Status",accessor:"status",active:!0,field:{name:"status",type:"text"},renderedCells:l.map(a=>s("div",{children:s(lt,{status:a.status})},a.id))},{Heading:"",accessor:"",active:!0,field:{name:"_",type:"text"},renderedCells:l.map(a=>M("div",{className:"flex items-center gap-4 flex-wrap",children:[a.externalDashboardUrl?s(nt,{href:a.externalDashboardUrl,rel:"noreferrer noopener",target:"_blank",children:s(D,{children:"dashboard \u2197"})}):null,a.status==="draft"||a.status==="save"?M(U,{children:[s(it,{broadcastId:a.id,broadcastName:a.name}),s(st,{broadcastId:a.id,broadcastName:a.name})]}):null]},a.id))}],data:l}),s(at,{hasNextPage:e*r<t.length,hasPrevPage:e>1,limit:r,onChange:a=>o(a),page:e,totalPages:Math.ceil(t.length/r)})]})}function lt({status:t}){switch(t){case"draft":case"save":return s(D,{pillStyle:"light-gray",children:"Draft"});case"queued":return s(D,{pillStyle:"white",children:"Queued"});case"sent":return s(D,{pillStyle:"success",children:"Sent"});default:return s(D,{children:t})}}function it({broadcastId:t,broadcastName:e}){let o=ve(),{t:r}=xe(),l=Se(),{requestJson:a}=g(),n=`delete-broadcast_${t}`;async function d(){await a(`/marketing/broadcasts/${encodeURIComponent(t)}`,{method:"DELETE"}),j.success(r("general:deletedSuccessfully")),o.closeModal(n),l.refresh()}return M(U,{children:[s(w,{slug:n,children:M(k,{children:[M(C,{children:[s(R,{children:r("general:confirmDeletion")}),s("p",{children:`Delete draft broadcast "${e}"?`})]}),M(v,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[s(x,{}),s(Ce,{buttonStyle:"error",onClick:()=>{d().catch(i=>j.error(i instanceof Error?i.message:"Delete failed"))},children:r("general:confirm")})]})]})}),s("button",{onClick:()=>o.openModal(n),type:"button",children:r("general:delete")})]})}function st({broadcastId:t,broadcastName:e}){let o=ve(),{t:r}=xe(),l=Se(),{requestJson:a}=g(),n=`send-broadcast_${t}`,d=(i,m)=>{let b=m.scheduledAt,u="";typeof b=="string"?u=b:b instanceof Date&&(u=b.toISOString()),(async()=>{try{await a(`/marketing/broadcasts/${encodeURIComponent(t)}/send`,{body:JSON.stringify({...u&&u.trim()!==""?{scheduledAt:u.trim()}:{}}),method:"POST"}),o.closeModal(n),j.success(r("general:success")),l.refresh()}catch(A){j.error(A instanceof Error?A.message:"Send failed")}})()};return M(U,{children:[s(w,{slug:n,children:s(k,{style:{width:"100%",maxWidth:"24rem"},children:M(C,{children:[s(R,{children:`Send \u201C${e}\u201D`}),M(tt,{initialState:{},onSubmit:d,waitForAutocomplete:!0,children:[s(et,{field:{label:"Schedule at (optional)",name:"scheduledAt",required:!1},path:"scheduledAt"}),M(v,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[s(x,{}),s(Ce,{size:"large",type:"submit",children:"Send"})]})]})]})})}),s(D,{pillStyle:"dark",onClick:()=>o.openModal(n),size:"small",children:"Send"})]})}import{Button as Be,Form as dt,TextField as ct,toast as z,useModal as ee,useTranslation as te,Button as mt}from"@payloadcms/ui";import{useRouter as Te}from"next/navigation";import{useTransition as ut}from"react";import{Fragment as Ne,jsx as p,jsxs as N}from"react/jsx-runtime";var Z="create-audience";function pt(){let t=ee(),{t:e}=te();return N(Ne,{children:[p(ft,{}),p(Be,{onClick:()=>t.openModal(Z),size:"large",type:"button",children:e("general:createNew")})]})}function ft(){let t=ee(),{t:e}=te(),o=Te(),{requestJson:r}=g(),[l,a]=ut();return p(w,{closeOnBlur:!0,slug:Z,children:p(k,{style:{width:"100%",maxWidth:"24rem"},children:p(dt,{initialState:{},onSubmit:(d,i)=>{let f=i.audienceName,b=typeof f=="string"?f.trim():"";a(()=>{(async()=>{try{if(!b)throw new Error("Name is required.");await r("/marketing/audiences",{body:JSON.stringify({name:b}),method:"POST"}),t.closeModal(Z),z.success(e("general:successfullyCreated")),o.refresh()}catch(u){z.error(u instanceof Error?u.message:"Create failed")}})()})},waitForAutocomplete:!0,children:N(C,{children:[p(R,{children:e("general:createNew")}),p(ct,{field:{name:"audienceName",required:!0,type:"text"},path:"audienceName",validate:d=>typeof d=="string"&&d.trim().length>0?!0:"Name is required"}),N(v,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[p(x,{}),p(Be,{disabled:l,size:"large",type:"submit",children:e("general:confirm")})]})]})})})})}function yt({audienceId:t,audienceName:e}){let o=ee(),{t:r}=te(),l=Te(),{requestJson:a}=g(),n=`delete-audience_${t}`;async function d(){await a(`/marketing/audiences/${encodeURIComponent(t)}`,{method:"DELETE"}),z.success(r("general:deletedSuccessfully")),o.closeModal(n),l.refresh()}return N(Ne,{children:[p(w,{slug:n,children:N(k,{children:[N(C,{children:[p(R,{children:r("general:confirmDeletion")}),p("p",{children:`Remove audience "${e}"?`})]}),N(v,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[p(x,{}),p(mt,{buttonStyle:"error",onClick:()=>{d().catch(i=>z.error(i instanceof Error?i.message:"Delete failed"))},children:r("general:confirm")})]})]})}),p("button",{onClick:()=>o.openModal(n),type:"button",children:r("general:delete")})]})}import{CheckboxField as gt,Form as bt,Pagination as ht,Table as Mt,TextField as ae,toast as W,useModal as ne,useTranslation as re,Button as le}from"@payloadcms/ui";import{useRouter as Ae}from"next/navigation";import oe from"react";import Pt from"react";import{useTransition as wt}from"react";import{Fragment as ie,jsx as c,jsxs as P}from"react/jsx-runtime";function kt({audienceId:t,contacts:e}){let[o,r]=oe.useState(1),[l]=oe.useState(100),a=oe.useMemo(()=>e.slice((o-1)*l,o*l),[e,l,o]);return P(ie,{children:[c(Mt,{columns:[{Heading:"First name",accessor:"firstName",active:!0,field:{name:"firstName",type:"text"},renderedCells:a.map(n=>c("div",{children:n.firstName??"\u2014"},n.id))},{Heading:"Last name",accessor:"lastName",active:!0,field:{name:"lastName",type:"text"},renderedCells:a.map(n=>c("div",{children:n.lastName??"\u2014"},n.id))},{Heading:"Email",accessor:"email",active:!0,field:{name:"email",type:"text"},renderedCells:a.map(n=>c("div",{children:n.email},n.id))},{Heading:"Created",accessor:"createdAt",active:!0,field:{name:"createdAt",type:"text"},renderedCells:a.map(n=>c("div",{children:$(n.createdAt)},n.id))},{Heading:"Status",accessor:"subscribed",active:!0,field:{name:"subscribed",type:"text"},renderedCells:a.map(n=>P("div",{className:"flex items-center gap-4 flex-wrap",children:[c("span",{children:n.subscribed!==!1?"\u2705 subscribed":"\u274C unsubscribed"}),c(Fe,{audienceId:t,contact:n}),c(Ct,{audienceId:t,contactEmail:n.email,contactId:n.id})]},n.id))}],data:a}),c(ht,{hasNextPage:o*l<e.length,hasPrevPage:o>1,limit:l,onChange:n=>{r(n)},page:o,totalPages:Math.ceil(e.length/l)})]})}function Ct({audienceId:t,contactId:e,contactEmail:o}){let r=ne(),{t:l}=re(),a=Ae(),{requestJson:n}=g(),d=`delete-contact_${e}`;async function i(){await n(`/marketing/audiences/${encodeURIComponent(t)}/contacts/${encodeURIComponent(e)}`,{method:"DELETE"}),W.success(l("general:deletedSuccessfully")),r.closeModal(d),a.refresh()}return P(ie,{children:[c(w,{slug:d,children:P(k,{children:[P(C,{children:[c("h1",{children:l("general:confirmDeletion")}),c("p",{children:`Remove contact ${o}?`})]}),P(v,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[c(x,{}),c(le,{buttonStyle:"error",onClick:()=>{i().catch(m=>W.error(m instanceof Error?m.message:"Delete failed"))},children:l("general:confirm")})]})]})}),c("button",{onClick:()=>r.openModal(d),type:"button",children:l("general:delete")})]})}function Fe({audienceId:t,contact:e}){let o=ne(),{t:r}=re(),l=Pt.useId(),a=e?`edit-contact_${e.id}`:`create-contact_${t}_${l}`;return P(ie,{children:[c(vt,{audienceId:t,contact:e,modalSlug:a}),c(e?"button":le,{...e?{}:{size:"large"},onClick:()=>o.openModal(a),type:"button",children:r(e?"general:edit":"general:createNew")})]})}function vt({audienceId:t,contact:e,modalSlug:o}){let r=ne(),{t:l}=re(),a=Ae(),{requestJson:n}=g(),[,d]=wt(),i=(m,f)=>{let b=f.email,u=String(f.firstName??""),A=String(f.lastName??""),E=!!f.subscribed;d(()=>{(async()=>{try{await n("/marketing/contacts",{body:JSON.stringify({audienceId:t,email:b,firstName:u,id:e?.id,lastName:A,subscribed:E}),method:"POST"}),W.success(e?"Contact updated.":"Contact created."),r.closeModal(o),a.refresh()}catch(_){W.error(_ instanceof Error?_.message:"Save failed")}})()})};return c(w,{slug:o,children:c(k,{style:{width:"100%",maxWidth:"32rem"},children:P(C,{children:[c("h1",{children:l(e?"general:edit":"general:createNew")}),P(bt,{className:"flex w-full flex-col gap-6",initialState:{email:{value:e?.email??""},firstName:{value:e?.firstName??""},lastName:{value:e?.lastName??""},subscribed:{value:e?.subscribed!==!1}},onSubmit:i,waitForAutocomplete:!0,children:[c(ae,{field:{label:l("general:email"),name:"email",required:!0,type:"text"},path:"email",validate:m=>typeof m=="string"&&m.includes("@")?!0:"Valid email required"}),c(ae,{field:{label:"First name",name:"firstName",type:"text"},path:"firstName"}),c(ae,{field:{label:"Last name",name:"lastName",type:"text"},path:"lastName"}),c(gt,{field:{label:"Subscribed",name:"subscribed"},path:"subscribed"}),P(v,{style:{display:"flex",gap:"8px",justifyContent:"flex-end"},children:[c(x,{}),c(le,{size:"large",type:"submit",children:l(e?"general:save":"general:create")})]})]})]})})})}import{Button as Ee,Drawer as xt,Form as St,SelectField as se,TextareaField as Re,TextField as G,toast as De,useFormFields as Bt,useModal as Ie,useTranslation as qe}from"@payloadcms/ui";import{useRouter as Tt}from"next/navigation";import Nt from"react";import{Fragment as ce,jsx as y,jsxs as Q}from"react/jsx-runtime";var de="create-marketing-broadcast";function At({audiences:t,emailBroadcastTemplates:e,localeOptions:o,provider:r}){let l=Ie(),{t:a}=qe();return Q(ce,{children:[y(Rt,{audiences:t,emailBroadcastTemplates:e,localeOptions:o??[],provider:r}),y(Ee,{onClick:()=>l.openModal(de),size:"large",type:"button",children:a("general:createNew")})]})}function Ft({localeOptions:t,showReactEmailTemplates:e,showTemplateField:o,templateOptions:r}){let l=Bt(([a])=>{if(!e)return"html";let n=a?.broadcastFormat?.value;return typeof n=="string"&&n!==""?n:"html"});if(e){let a=l==="html";return Q(ce,{children:[y(se,{field:{label:"Email content",name:"broadcastFormat",options:[{label:"<html>",value:"html"},...r.map(n=>({label:n.name,value:n.id}))],required:!0},path:"broadcastFormat"}),!a&&t.length>0?y(se,{field:{label:"Locale",name:"locale",options:t,required:!1},path:"locale"}):null,a?y(Re,{field:{admin:{description:"Shown when Email content is <html>."},label:"<html>",name:"htmlBody",required:!0},path:"htmlBody"}):null]})}return Q(ce,{children:[o?y(G,{field:{admin:{description:"Optional Mailchimp saved-template id (numeric string). Leave <html> empty when using templates."},label:"Template id",name:"templateId",required:!1,type:"text"},path:"templateId"}):null,y(Re,{field:{admin:{description:o?"Optional plain HTML alternative to a Mailchimp template.":"HTML broadcast body."},label:o?"<html> (optional with Mailchimp)":"<html>",name:"htmlBody",required:!o},path:"htmlBody"})]})}function Rt({audiences:t,emailBroadcastTemplates:e,localeOptions:o,provider:r}){let l=Tt(),a=Ie(),{t:n}=qe(),{requestJson:d}=g(),[i,m]=Nt.useState(!1),f=r==="mailchimp",b=e??[],u=r==="resend"&&b.length>0,A=(E,_)=>{let S=_,me=S.audienceId,ue=S.name,pe=S.subject,fe=S.htmlBody,ye=S.templateId,ge=S.replyTo,be=S.broadcastFormat,he=S.locale,I=typeof me=="string"?me:"",q=typeof ue=="string"?ue.trim():"",O=typeof pe=="string"?pe.trim():"",B=typeof fe=="string"?fe.trim():"",V=typeof ye=="string"?ye.trim():"",L=typeof ge=="string"?ge.trim():"",Me=typeof be=="string"?be.trim():"html",Oe=typeof he=="string"?he.trim():"";m(!0),(async()=>{try{if(!I||!q||!O)throw new Error("Audience, name, and subject are required.");let H=f,$e=u&&Me!=="html";if(u)if($e)await d("/marketing/broadcasts",{body:JSON.stringify({audienceId:I,emailTemplateId:Me,locale:Oe||void 0,name:q,replyTo:L,subject:O}),method:"POST"});else if(B)await d("/marketing/broadcasts",{body:JSON.stringify({audienceId:I,html:B,name:q,replyTo:L,subject:O}),method:"POST"});else throw new Error("<html> is required when using the <html> content option.");else if(H){if(!V&&!B)throw new Error("Provide <html> or a Mailchimp template id.");if(V&&B)throw new Error("Use either <html> or Mailchimp template id, not both.");await d("/marketing/broadcasts",{body:JSON.stringify({audienceId:I,html:B,name:q,replyTo:L,subject:O,templateId:V||void 0}),method:"POST"})}else if(B)await d("/marketing/broadcasts",{body:JSON.stringify({audienceId:I,html:B,name:q,replyTo:L,subject:O}),method:"POST"});else throw new Error("<html> is required for this provider.");a.closeModal(de),De.success(n("general:successfullyCreated",{label:"Broadcast"})),l.refresh()}catch(H){De.error(H instanceof Error?H.message:"Create failed")}finally{m(!1)}})()};return y(xt,{slug:de,title:n("general:createNew"),children:Q(St,{className:"flex w-full flex-col gap-8",initialState:u?{broadcastFormat:{value:"html"}}:{},onSubmit:A,waitForAutocomplete:!0,children:[y(se,{field:{label:"Audience",name:"audienceId",options:t.map(E=>({label:E.name,value:E.id})),required:!0},path:"audienceId"}),y(G,{field:{label:"Campaign name",name:"name",required:!0,type:"text"},path:"name"}),y(G,{field:{label:"Subject",name:"subject",required:!0,type:"text"},path:"subject"}),y(G,{field:{label:"Reply-to (optional)",name:"replyTo",type:"text"},path:"replyTo"}),y(Ft,{localeOptions:o,showReactEmailTemplates:u,showTemplateField:f,templateOptions:b}),y(Ee,{className:"w-full",disabled:i,type:"submit",children:n("general:create")})]})})}export{kt as AudienceContactsTable,We as AudienceSelect,Qe as AudienceTable,rt as BroadcastsTable,Ve as ContactsTable,pt as CreateAudienceButton,At as CreateBroadcastButton,yt as DeleteAudienceButton,Fe as EditContactButton,Ge as MarketingMenu};
|
|
3
|
+
//# sourceMappingURL=client.js.map
|