medusa-contact-us 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/.medusa/server/src/admin/index.js +523 -0
  2. package/.medusa/server/src/admin/index.mjs +524 -0
  3. package/.medusa/server/src/api/admin/contact-requests/[id]/comments/route.js +22 -0
  4. package/.medusa/server/src/api/admin/contact-requests/[id]/route.js +41 -0
  5. package/.medusa/server/src/api/admin/contact-requests/route.js +31 -0
  6. package/.medusa/server/src/api/admin/contact-requests/validators.js +19 -0
  7. package/.medusa/server/src/api/admin/plugin/route.js +11 -0
  8. package/.medusa/server/src/api/store/contact-requests/route.js +29 -0
  9. package/.medusa/server/src/api/store/contact-requests/validators.js +15 -0
  10. package/.medusa/server/src/api/store/plugin/route.js +11 -0
  11. package/.medusa/server/src/constants.js +5 -0
  12. package/.medusa/server/src/helpers/__tests__/submit-contact-request.test.js +93 -0
  13. package/.medusa/server/src/helpers/index.js +6 -0
  14. package/.medusa/server/src/helpers/submit-contact-request.js +66 -0
  15. package/.medusa/server/src/index.js +30 -0
  16. package/.medusa/server/src/modules/contact-requests/index.js +15 -0
  17. package/.medusa/server/src/modules/contact-requests/migrations/Migration20241124090000.js +46 -0
  18. package/.medusa/server/src/modules/contact-requests/models/contact-request-comment.js +12 -0
  19. package/.medusa/server/src/modules/contact-requests/models/contact-request.js +16 -0
  20. package/.medusa/server/src/modules/contact-requests/service.js +186 -0
  21. package/.medusa/server/src/plugin-options.js +16 -0
  22. package/.medusa/server/src/types/__tests__/contact-options.test.js +83 -0
  23. package/.medusa/server/src/types.js +210 -0
  24. package/.medusa/server/src/utils/__tests__/payload-validator.test.js +81 -0
  25. package/.medusa/server/src/utils/payload.js +127 -0
  26. package/.medusa/server/src/workflows/create-contact-request.js +30 -0
  27. package/.medusa/server/src/workflows/index.js +8 -0
  28. package/.medusa/server/src/workflows/steps/create-contact-request-step.js +17 -0
  29. package/.medusa/server/src/workflows/steps/send-contact-notification-step.js +48 -0
  30. package/.medusa/server/src/workflows/steps/update-contact-request-status-step.js +17 -0
  31. package/.medusa/server/src/workflows/update-contact-request-status.js +32 -0
  32. package/README.md +213 -0
  33. package/package.json +72 -0
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateContactRequestStatusStep = void 0;
4
+ const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
5
+ const constants_1 = require("../../constants");
6
+ exports.updateContactRequestStatusStep = (0, workflows_sdk_1.createStep)("update-contact-request-status", async (input, { container }) => {
7
+ const service = container.resolve(constants_1.CONTACT_REQUEST_MODULE);
8
+ const request = await service.updateStatus(input);
9
+ const options = service.getOptions();
10
+ const statusOption = options.status_options.find((option) => option.code === request.status);
11
+ return new workflows_sdk_1.StepResponse({
12
+ request,
13
+ options,
14
+ statusOption,
15
+ });
16
+ });
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLWNvbnRhY3QtcmVxdWVzdC1zdGF0dXMtc3RlcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3Mvc3RlcHMvdXBkYXRlLWNvbnRhY3QtcmVxdWVzdC1zdGF0dXMtc3RlcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxxRUFBNEU7QUFDNUUsK0NBQXdEO0FBZTNDLFFBQUEsOEJBQThCLEdBQUcsSUFBQSwwQkFBVSxFQUN0RCwrQkFBK0IsRUFDL0IsS0FBSyxFQUNILEtBQStCLEVBQy9CLEVBQUUsU0FBUyxFQUFFLEVBQ3NCLEVBQUU7SUFDckMsTUFBTSxPQUFPLEdBQ1gsU0FBUyxDQUFDLE9BQU8sQ0FBOEIsa0NBQXNCLENBQUMsQ0FBQTtJQUV4RSxNQUFNLE9BQU8sR0FBRyxNQUFNLE9BQU8sQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDLENBQUE7SUFDakQsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLFVBQVUsRUFBRSxDQUFBO0lBQ3BDLE1BQU0sWUFBWSxHQUFHLE9BQU8sQ0FBQyxjQUFjLENBQUMsSUFBSSxDQUM5QyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsTUFBTSxDQUFDLElBQUksS0FBSyxPQUFPLENBQUMsTUFBTSxDQUMzQyxDQUFBO0lBRUQsT0FBTyxJQUFJLDRCQUFZLENBQUM7UUFDdEIsT0FBTztRQUNQLE9BQU87UUFDUCxZQUFZO0tBQ2IsQ0FBQyxDQUFBO0FBQ0osQ0FBQyxDQUNGLENBQUEifQ==
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateContactRequestStatusWorkflow = void 0;
4
+ const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
5
+ const send_contact_notification_step_1 = require("./steps/send-contact-notification-step");
6
+ const update_contact_request_status_step_1 = require("./steps/update-contact-request-status-step");
7
+ exports.updateContactRequestStatusWorkflow = (0, workflows_sdk_1.createWorkflow)("contact-us-update-status", (input) => {
8
+ const { request, options, statusOption } = (0, update_contact_request_status_step_1.updateContactRequestStatusStep)(input);
9
+ const isFinal = request.status === options.statuses.final;
10
+ const shouldNotify = isFinal &&
11
+ options.notifications.send_on_final_status &&
12
+ (statusOption?.notify_customer ?? true);
13
+ (0, send_contact_notification_step_1.sendContactNotificationStep)({
14
+ shouldSend: shouldNotify,
15
+ to: request.email,
16
+ from: options.notifications.from_address,
17
+ reply_to: options.notifications.reply_to,
18
+ template: statusOption?.template ??
19
+ options.notifications.final_status_template,
20
+ subject: statusOption?.label ?? "Your contact request was updated",
21
+ data: {
22
+ request_id: request.id,
23
+ status: request.status,
24
+ updated_at: request.updated_at,
25
+ },
26
+ });
27
+ return new workflows_sdk_1.WorkflowResponse({
28
+ request,
29
+ });
30
+ });
31
+ exports.default = exports.updateContactRequestStatusWorkflow;
32
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLWNvbnRhY3QtcmVxdWVzdC1zdGF0dXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvd29ya2Zsb3dzL3VwZGF0ZS1jb250YWN0LXJlcXVlc3Qtc3RhdHVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLHFFQUcwQztBQUUxQywyRkFBb0Y7QUFDcEYsbUdBQTJGO0FBRTlFLFFBQUEsa0NBQWtDLEdBQUcsSUFBQSw4QkFBYyxFQUM5RCwwQkFBMEIsRUFDMUIsQ0FBQyxLQUErQixFQUFFLEVBQUU7SUFDbEMsTUFBTSxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFLEdBQ3RDLElBQUEsbUVBQThCLEVBQUMsS0FBSyxDQUFDLENBQUE7SUFFdkMsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLE1BQU0sS0FBSyxPQUFPLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQTtJQUN6RCxNQUFNLFlBQVksR0FDaEIsT0FBTztRQUNQLE9BQU8sQ0FBQyxhQUFhLENBQUMsb0JBQW9CO1FBQzFDLENBQUMsWUFBWSxFQUFFLGVBQWUsSUFBSSxJQUFJLENBQUMsQ0FBQTtJQUV6QyxJQUFBLDREQUEyQixFQUFDO1FBQzFCLFVBQVUsRUFBRSxZQUFZO1FBQ3hCLEVBQUUsRUFBRSxPQUFPLENBQUMsS0FBSztRQUNqQixJQUFJLEVBQUUsT0FBTyxDQUFDLGFBQWEsQ0FBQyxZQUFZO1FBQ3hDLFFBQVEsRUFBRSxPQUFPLENBQUMsYUFBYSxDQUFDLFFBQVE7UUFDeEMsUUFBUSxFQUNOLFlBQVksRUFBRSxRQUFRO1lBQ3RCLE9BQU8sQ0FBQyxhQUFhLENBQUMscUJBQXFCO1FBQzdDLE9BQU8sRUFBRSxZQUFZLEVBQUUsS0FBSyxJQUFJLGtDQUFrQztRQUNsRSxJQUFJLEVBQUU7WUFDSixVQUFVLEVBQUUsT0FBTyxDQUFDLEVBQUU7WUFDdEIsTUFBTSxFQUFFLE9BQU8sQ0FBQyxNQUFNO1lBQ3RCLFVBQVUsRUFBRSxPQUFPLENBQUMsVUFBVTtTQUMvQjtLQUNGLENBQUMsQ0FBQTtJQUVGLE9BQU8sSUFBSSxnQ0FBZ0IsQ0FBQztRQUMxQixPQUFPO0tBQ1IsQ0FBQyxDQUFBO0FBQ0osQ0FBQyxDQUNGLENBQUE7QUFFRCxrQkFBZSwwQ0FBa0MsQ0FBQSJ9
package/README.md ADDED
@@ -0,0 +1,213 @@
1
+ <h1 align="center">Medusa Contact Us Plugin</h1>
2
+
3
+ Collect, triage, and resolve storefront “contact us” submissions inside the Medusa Admin. The plugin ships a full stack experience: configurable form schema, database-backed requests/comments, workflows with notification hooks, open store endpoint, admin APIs, helper utilities, and a polished admin UI.
4
+
5
+ ## Features
6
+
7
+ - ✅ Configurable form schema (required email + arbitrary fields validated from plugin options)
8
+ - 🧩 Status lifecycle definition (initial/intermediate/final, allowed transitions, metadata)
9
+ - ✉️ Workflow-powered notifications on submission and on final status (re-uses Medusa notification module)
10
+ - 🗒️ Admin-only comment trail per request with rich status history
11
+ - 🖥️ React Admin extension with list + detail views, filters, status change controls, and comment composer
12
+ - 🔌 Frontend helper (`submitContactRequest`) to abstract API wiring
13
+ - 🧪 Table-driven tests for payload validation and helper logic
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ yarn add medusa-contact-us
19
+ # or
20
+ npm install medusa-contact-us
21
+ ```
22
+
23
+ Inside `medusa-config.ts` register the plugin and tailor the options:
24
+
25
+ ```ts
26
+ import {
27
+ defineContactUsPluginOptions,
28
+ ContactRequestModule,
29
+ } from "medusa-contact-us"
30
+
31
+ const plugins = [
32
+ {
33
+ resolve: "medusa-contact-us",
34
+ options: defineContactUsPluginOptions({
35
+ form: {
36
+ max_payload_kb: 128,
37
+ additional_fields: [
38
+ { key: "subject", label: "Subject", type: "text", required: true },
39
+ {
40
+ key: "priority",
41
+ label: "Priority",
42
+ type: "select",
43
+ options: [
44
+ { value: "low", label: "Low" },
45
+ { value: "high", label: "High" },
46
+ ],
47
+ },
48
+ ],
49
+ },
50
+ status_options: [
51
+ { code: "new", label: "New", notify_customer: true },
52
+ { code: "in_review", label: "In review" },
53
+ {
54
+ code: "closed",
55
+ label: "Closed",
56
+ notify_customer: true,
57
+ template: "emails/contact-final.mjml",
58
+ },
59
+ ],
60
+ statuses: {
61
+ initial: "new",
62
+ intermediates: ["in_review"],
63
+ final: "closed",
64
+ transitions: {
65
+ new: ["in_review", "closed"],
66
+ in_review: ["closed"],
67
+ },
68
+ },
69
+ notifications: {
70
+ send_on_create: true,
71
+ acknowledgement_template: "emails/contact-received.mjml",
72
+ send_on_final_status: true,
73
+ },
74
+ comments: {
75
+ enabled: true,
76
+ require_note_on_final: true,
77
+ },
78
+ }),
79
+ },
80
+ ]
81
+
82
+ const modules = [ContactRequestModule]
83
+ ```
84
+
85
+ ### Status lifecycle best practices
86
+
87
+ - Define unique status codes (kebab or snake case) and map them in `status_options`.
88
+ - Provide at least one intermediate state when multiple review steps exist.
89
+ - Set `notify_customer` to `true` for statuses that should trigger emails.
90
+ - Configure `transitions` if you need granular control; otherwise the default is `(all non-final statuses) -> intermediates/final`.
91
+
92
+ ## REST API
93
+
94
+ ### Storefront (open)
95
+
96
+ ```bash
97
+ curl -X POST https://your-medusa.com/store/contact-requests \
98
+ -H "Content-Type: application/json" \
99
+ -d '{
100
+ "email": "customer@example.com",
101
+ "payload": { "subject": "Need help", "priority": "high" },
102
+ "metadata": { "order_id": "order_123" }
103
+ }'
104
+ ```
105
+
106
+ Response:
107
+
108
+ ```json
109
+ {
110
+ "contact_request": {
111
+ "id": "crq_123",
112
+ "email": "customer@example.com",
113
+ "status": "new",
114
+ "payload": { "subject": "Need help", "priority": "high" },
115
+ "status_history": [
116
+ { "status": "new", "updated_at": "2024-11-24T10:00:00.000Z" }
117
+ ]
118
+ }
119
+ }
120
+ ```
121
+
122
+ ### Admin (requires admin auth cookie/token)
123
+
124
+ List:
125
+
126
+ ```bash
127
+ curl -X GET "https://your-medusa.com/admin/contact-requests?status=new&limit=20" \
128
+ -H "Authorization: Bearer <token>"
129
+ ```
130
+
131
+ Detail:
132
+
133
+ ```bash
134
+ curl -X GET https://your-medusa.com/admin/contact-requests/crq_123 \
135
+ -H "Authorization: Bearer <token>"
136
+ ```
137
+
138
+ Update status:
139
+
140
+ ```bash
141
+ curl -X PATCH https://your-medusa.com/admin/contact-requests/crq_123 \
142
+ -H "Authorization: Bearer <token>" \
143
+ -H "Content-Type: application/json" \
144
+ -d '{ "status": "closed", "note": "Resolved via refund" }'
145
+ ```
146
+
147
+ Add comment:
148
+
149
+ ```bash
150
+ curl -X POST https://your-medusa.com/admin/contact-requests/crq_123/comments \
151
+ -H "Authorization: Bearer <token>" \
152
+ -H "Content-Type: application/json" \
153
+ -d '{ "comment": "Waiting on supplier" }'
154
+ ```
155
+
156
+ ## Admin UI
157
+
158
+ After running `medusa admin dev --plugins medusa-contact-us`, the sidebar will include **Contact Requests**:
159
+
160
+ - **List view** – search by email, filter by status, inspect creation dates, open details with a single click.
161
+ - **Detail view** – see submitted fields, live status badge, change status (with optional notes), inspect the full history timeline, and append internal comments.
162
+ - **Comments** – only admins can add comments. Comments are persisted and shown newest first.
163
+
164
+ All UI components follow the Medusa UI kit spacing (8pt grid), color, and accessibility guidelines.
165
+
166
+ ## Frontend helper
167
+
168
+ Skip hand-writing `fetch` calls by importing the provided helper:
169
+
170
+ ```ts
171
+ import { submitContactRequest } from "medusa-contact-us"
172
+
173
+ await submitContactRequest({
174
+ email: "customer@example.com",
175
+ payload: { subject: "Question", priority: "high" },
176
+ })
177
+ ```
178
+
179
+ Passing a Medusa JS client automatically reuses its transport:
180
+
181
+ ```ts
182
+ import Medusa from "@medusajs/medusa-js"
183
+ import { submitContactRequest } from "medusa-contact-us"
184
+
185
+ const client = new Medusa({ baseUrl: "https://your-medusa.com" })
186
+
187
+ await submitContactRequest({
188
+ client,
189
+ email: "customer@example.com",
190
+ payload: { subject: "Returns" },
191
+ })
192
+ ```
193
+
194
+ ## Workflows & notifications
195
+
196
+ - `createContactRequestWorkflow` validates payloads, persists the request, and optionally fires an acknowledgement email.
197
+ - `updateContactRequestStatusWorkflow` enforces transitions, records history entries, and emits final-status notifications.
198
+ - Notifications rely on the standard Medusa notification module. Ensure at least one email provider is configured; otherwise the workflows raise an error to surface misconfiguration early.
199
+
200
+ ## Testing & build
201
+
202
+ ```bash
203
+ yarn test # runs Vitest table-driven suites
204
+ yarn build # compiles the plugin via `medusa plugin:build`
205
+ ```
206
+
207
+ Always run `yarn build` after development to ensure the bundler succeeds before publishing or yalc linking.
208
+
209
+ ## Troubleshooting
210
+
211
+ - Submit endpoint returning `422`: ensure every field defined in `form.additional_fields` is provided and matches its type.
212
+ - Notifications not sent: confirm `notifications.send_on_*` flags and that the notification module is registered (the workflows throw an actionable error otherwise).
213
+ - Admin UI blank: rebuild the plugin (`yarn build`) and restart the Admin app with the plugin registered in `medusa-config.ts`.
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "medusa-contact-us",
3
+ "version": "0.0.1",
4
+ "description": "A starter for Medusa plugins.",
5
+ "author": "Medusa (https://medusajs.com)",
6
+ "license": "MIT",
7
+ "files": [
8
+ ".medusa/server"
9
+ ],
10
+ "exports": {
11
+ "./package.json": "./package.json",
12
+ "./workflows": "./.medusa/server/src/workflows/index.js",
13
+ "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
14
+ "./modules/*": "./.medusa/server/src/modules/*/index.js",
15
+ "./providers/*": "./.medusa/server/src/providers/*/index.js",
16
+ "./*": "./.medusa/server/src/*.js",
17
+ "./admin": {
18
+ "import": "./.medusa/server/src/admin/index.mjs",
19
+ "require": "./.medusa/server/src/admin/index.js",
20
+ "default": "./.medusa/server/src/admin/index.js"
21
+ }
22
+ },
23
+ "keywords": [
24
+ "medusa",
25
+ "plugin",
26
+ "medusa-plugin-other",
27
+ "medusa-plugin",
28
+ "medusa-v2"
29
+ ],
30
+ "dependencies": {
31
+ "zod": "^3.23.8"
32
+ },
33
+ "scripts": {
34
+ "build": "medusa plugin:build",
35
+ "dev": "medusa plugin:develop",
36
+ "test": "vitest run",
37
+ "prepublishOnly": "medusa plugin:build"
38
+ },
39
+ "devDependencies": {
40
+ "@medusajs/admin-sdk": "2.11.2",
41
+ "@medusajs/cli": "2.11.2",
42
+ "@medusajs/framework": "2.11.2",
43
+ "@medusajs/medusa": "2.11.2",
44
+ "@medusajs/test-utils": "2.11.2",
45
+ "@medusajs/ui": "4.0.25",
46
+ "@medusajs/icons": "2.11.2",
47
+ "@swc/core": "1.5.7",
48
+ "@types/node": "^20.0.0",
49
+ "@types/react": "^18.3.2",
50
+ "@types/react-dom": "^18.2.25",
51
+ "prop-types": "^15.8.1",
52
+ "react": "^18.2.0",
53
+ "react-dom": "^18.2.0",
54
+ "ts-node": "^10.9.2",
55
+ "typescript": "^5.6.2",
56
+ "vite": "^5.2.11",
57
+ "vitest": "^1.2.2",
58
+ "yalc": "^1.0.0-pre.53"
59
+ },
60
+ "peerDependencies": {
61
+ "@medusajs/admin-sdk": "2.11.2",
62
+ "@medusajs/cli": "2.11.2",
63
+ "@medusajs/framework": "2.11.2",
64
+ "@medusajs/test-utils": "2.11.2",
65
+ "@medusajs/medusa": "2.11.2",
66
+ "@medusajs/ui": "4.0.25",
67
+ "@medusajs/icons": "2.11.2"
68
+ },
69
+ "engines": {
70
+ "node": ">=20"
71
+ }
72
+ }