medusa-contact-us 0.0.3 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/index.js +176 -1
- package/.medusa/server/src/admin/index.mjs +176 -1
- package/.medusa/server/src/api/admin/contact-email-subscriptions/route.js +29 -0
- package/.medusa/server/src/api/admin/contact-email-subscriptions/validators.js +12 -0
- package/.medusa/server/src/api/store/contact-email-subscriptions/route.js +19 -0
- package/.medusa/server/src/api/store/contact-email-subscriptions/validators.js +15 -0
- package/.medusa/server/src/constants.js +3 -2
- package/.medusa/server/src/helpers/__tests__/contact-subscription.test.js +109 -0
- package/.medusa/server/src/helpers/__tests__/submit-contact-request.test.js +33 -1
- package/.medusa/server/src/helpers/base-client.js +36 -0
- package/.medusa/server/src/helpers/contact-subscription.js +45 -0
- package/.medusa/server/src/helpers/index.js +5 -2
- package/.medusa/server/src/helpers/submit-contact-request.js +6 -35
- package/.medusa/server/src/index.js +7 -2
- package/.medusa/server/src/modules/contact-requests/migrations/Migration20241124090000.js +7 -2
- package/.medusa/server/src/modules/contact-requests/models/contact-request-comment.js +1 -2
- package/.medusa/server/src/modules/contact-requests/models/contact-request.js +1 -3
- package/.medusa/server/src/modules/contact-subscriptions/index.js +15 -0
- package/.medusa/server/src/modules/contact-subscriptions/migrations/Migration20241126103000.js +38 -0
- package/.medusa/server/src/modules/contact-subscriptions/models/contact-email-subscription.js +13 -0
- package/.medusa/server/src/modules/contact-subscriptions/service.js +57 -0
- package/README.md +107 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Collect, triage, and resolve storefront “contact us” submissions inside the
|
|
|
10
10
|
- 🗒️ Admin-only comment trail per request with rich status history
|
|
11
11
|
- 🖥️ React Admin extension with list + detail views, filters, status change controls, and comment composer
|
|
12
12
|
- 🔌 Frontend helper (`submitContactRequest`) to abstract API wiring
|
|
13
|
+
- ✉️ Storefront opt-in helper + admin list for email subscriptions (subscribed/unsubscribed)
|
|
13
14
|
- 🧪 Table-driven tests for payload validation and helper logic
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
@@ -29,6 +30,7 @@ import type { ConfigModule } from "@medusajs/framework/types"
|
|
|
29
30
|
import {
|
|
30
31
|
defineContactUsPluginOptions,
|
|
31
32
|
ContactRequestModule,
|
|
33
|
+
ContactSubscriptionModule,
|
|
32
34
|
} from "medusa-contact-us"
|
|
33
35
|
|
|
34
36
|
const plugins = [
|
|
@@ -174,7 +176,7 @@ const plugins = [
|
|
|
174
176
|
]
|
|
175
177
|
|
|
176
178
|
// Register the ContactRequestModule
|
|
177
|
-
const modules = [ContactRequestModule]
|
|
179
|
+
const modules = [ContactRequestModule, ContactSubscriptionModule]
|
|
178
180
|
|
|
179
181
|
export default {
|
|
180
182
|
projectConfig: {
|
|
@@ -235,6 +237,38 @@ Response:
|
|
|
235
237
|
}
|
|
236
238
|
```
|
|
237
239
|
|
|
240
|
+
#### Email subscriptions
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
curl -X POST https://your-medusa.com/store/contact-email-subscriptions \
|
|
244
|
+
-H "Content-Type: application/json" \
|
|
245
|
+
-H "x-publishable-api-key: pk_storefront" \
|
|
246
|
+
-d '{
|
|
247
|
+
"email": "newsletter@example.com",
|
|
248
|
+
"status": "subscribed",
|
|
249
|
+
"source": "footer"
|
|
250
|
+
}'
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Body fields:
|
|
254
|
+
- `email` – required, unique per entry (case-insensitive).
|
|
255
|
+
- `status` – optional, defaults to `subscribed`. Pass `"unsubscribed"` to honor opt-outs.
|
|
256
|
+
- `metadata` / `source` – optional context stored alongside the entry.
|
|
257
|
+
|
|
258
|
+
Response:
|
|
259
|
+
|
|
260
|
+
```json
|
|
261
|
+
{
|
|
262
|
+
"subscription": {
|
|
263
|
+
"id": "csub_123",
|
|
264
|
+
"email": "newsletter@example.com",
|
|
265
|
+
"status": "subscribed",
|
|
266
|
+
"source": "footer",
|
|
267
|
+
"created_at": "2024-11-26T10:00:00.000Z"
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
238
272
|
### Admin (requires admin auth cookie/token)
|
|
239
273
|
|
|
240
274
|
List:
|
|
@@ -276,49 +310,50 @@ After running `medusa admin dev --plugins medusa-contact-us`, the sidebar will i
|
|
|
276
310
|
- **List view** – search by email, filter by status, inspect creation dates, open details with a single click.
|
|
277
311
|
- **Detail view** – see submitted fields, live status badge, change status (with optional notes), inspect the full history timeline, and append internal comments.
|
|
278
312
|
- **Comments** – only admins can add comments. Comments are persisted and shown newest first.
|
|
313
|
+
- **Contact email list** – dedicated table that displays every storefront opt-in, highlights unsubscribes, and supports filtering/searching by status or email.
|
|
279
314
|
|
|
280
315
|
All UI components follow the Medusa UI kit spacing (8pt grid), color, and accessibility guidelines.
|
|
281
316
|
|
|
282
317
|
## Frontend helper
|
|
283
318
|
|
|
284
|
-
Skip hand-writing `fetch` calls by importing the provided
|
|
319
|
+
Skip hand-writing `fetch` calls by importing the provided helpers. **Storefront requests must include a publishable API key** (create one under `Settings → API Keys` in the Medusa admin). The helpers automatically attach the header for you.
|
|
320
|
+
|
|
321
|
+
### Contact requests
|
|
285
322
|
|
|
286
323
|
```ts
|
|
287
324
|
import { submitContactRequest } from "medusa-contact-us"
|
|
288
325
|
|
|
289
|
-
const options = {
|
|
290
|
-
baseUrl: "https://store.myshop.com",
|
|
291
|
-
publishableApiKey: "pk_your_publishable_api_key_here",
|
|
292
|
-
}
|
|
293
|
-
|
|
294
326
|
await submitContactRequest(
|
|
295
327
|
{
|
|
296
328
|
email: "customer@example.com",
|
|
297
329
|
payload: { subject: "Question", priority: "high" },
|
|
298
330
|
},
|
|
299
|
-
|
|
331
|
+
{
|
|
332
|
+
baseUrl: "https://store.myshop.com",
|
|
333
|
+
publishableApiKey: "pk_test_storefront",
|
|
334
|
+
}
|
|
300
335
|
)
|
|
301
336
|
```
|
|
302
337
|
|
|
303
|
-
|
|
338
|
+
Using a Medusa JS client keeps credentials in one place while still letting you override headers (including publishable keys) per call:
|
|
304
339
|
|
|
305
340
|
```ts
|
|
306
341
|
import Medusa from "@medusajs/medusa-js"
|
|
307
342
|
import { submitContactRequest } from "medusa-contact-us"
|
|
308
343
|
|
|
309
|
-
const
|
|
344
|
+
const medusa = new Medusa({
|
|
310
345
|
baseUrl: "https://store.myshop.com",
|
|
311
|
-
publishableKey: "
|
|
346
|
+
publishableKey: "pk_live_client",
|
|
312
347
|
})
|
|
313
348
|
|
|
314
349
|
await submitContactRequest(
|
|
315
350
|
{
|
|
316
|
-
client,
|
|
317
351
|
email: "customer@example.com",
|
|
318
352
|
payload: { subject: "Returns" },
|
|
319
353
|
},
|
|
320
354
|
{
|
|
321
|
-
|
|
355
|
+
client: medusa,
|
|
356
|
+
publishableApiKey: "pk_live_client",
|
|
322
357
|
headers: {
|
|
323
358
|
Cookie: "connect.sid=...",
|
|
324
359
|
},
|
|
@@ -326,13 +361,69 @@ await submitContactRequest(
|
|
|
326
361
|
)
|
|
327
362
|
```
|
|
328
363
|
|
|
329
|
-
|
|
364
|
+
For SSR or edge runtimes, preconfigure the helper once:
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
import { createSubmitContactRequest } from "medusa-contact-us"
|
|
368
|
+
|
|
369
|
+
const submitContactRequest = createSubmitContactRequest({
|
|
370
|
+
baseUrl: process.env.NEXT_PUBLIC_MEDUSA_URL,
|
|
371
|
+
publishableApiKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
export async function action(formData: FormData) {
|
|
375
|
+
await submitContactRequest({
|
|
376
|
+
email: formData.get("email") as string,
|
|
377
|
+
payload: {
|
|
378
|
+
subject: formData.get("subject"),
|
|
379
|
+
message: formData.get("message"),
|
|
380
|
+
},
|
|
381
|
+
})
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Email subscriptions
|
|
386
|
+
|
|
387
|
+
```ts
|
|
388
|
+
import { upsertContactSubscription } from "medusa-contact-us"
|
|
389
|
+
|
|
390
|
+
await upsertContactSubscription(
|
|
391
|
+
{
|
|
392
|
+
email: "newsletter@example.com",
|
|
393
|
+
status: "subscribed",
|
|
394
|
+
metadata: { channel: "footer" },
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
baseUrl: "https://store.myshop.com",
|
|
398
|
+
publishableApiKey: "pk_test_storefront",
|
|
399
|
+
}
|
|
400
|
+
)
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
To keep deploys DRY, build a preconfigured helper and reuse it everywhere:
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
import { createUpsertContactSubscription } from "medusa-contact-us"
|
|
407
|
+
|
|
408
|
+
export const upsertSubscription = createUpsertContactSubscription({
|
|
409
|
+
baseUrl: process.env.NEXT_PUBLIC_MEDUSA_URL,
|
|
410
|
+
publishableApiKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
await upsertSubscription({
|
|
414
|
+
email: input.email,
|
|
415
|
+
status: input.unsubscribe ? "unsubscribed" : "subscribed",
|
|
416
|
+
source: input.source ?? "footer_form",
|
|
417
|
+
})
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Shared helper options
|
|
330
421
|
|
|
331
|
-
- `publishableApiKey` –
|
|
422
|
+
- `publishableApiKey` – Automatically sets the `x-publishable-api-key` header when no Medusa client is used.
|
|
332
423
|
- `baseUrl` – Storefront API origin (ignored when `client` is provided).
|
|
333
424
|
- `client` – Pre-configured Medusa JS/SDK instance (reuses its base URL and publishable key).
|
|
334
425
|
- `fetchImpl` – Custom fetch implementation (SSR, React Native, etc.).
|
|
335
|
-
- `headers` – Additional headers merged into the request (e.g., session cookie, localization).
|
|
426
|
+
- `headers` – Additional headers merged into the request (e.g., session cookie, localization). Values you pass here override the defaults, including the publishable key header if you need a per-request key.
|
|
336
427
|
|
|
337
428
|
Customer-authenticated endpoints (like submitting a request from a logged-in area) still require the appropriate session cookie or JWT. Provide those via the `headers` option if they’re not already managed by the browser fetch call.
|
|
338
429
|
|