medusa-contact-us 0.0.11 → 0.0.17

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 (39) hide show
  1. package/.medusa/server/src/admin/index.js +4 -494
  2. package/.medusa/server/src/admin/index.mjs +6 -496
  3. package/.medusa/server/src/api/admin/contact-email-subscriptions/route.js +3 -3
  4. package/.medusa/server/src/api/store/contact-email-subscriptions/route.js +3 -3
  5. package/.medusa/server/src/api/store/contact-email-subscriptions/validators.js +2 -2
  6. package/.medusa/server/src/constants.js +10 -4
  7. package/.medusa/server/src/helpers/contact-subscription.js +15 -4
  8. package/.medusa/server/src/helpers/index.js +2 -4
  9. package/.medusa/server/src/index.js +8 -13
  10. package/.medusa/server/src/modules/contact-subscriptions/index.js +4 -5
  11. package/.medusa/server/src/modules/contact-subscriptions/service.js +20 -12
  12. package/.medusa/server/src/types.js +1 -208
  13. package/README.md +34 -287
  14. package/package.json +2 -3
  15. package/.medusa/server/src/api/admin/contact-requests/[id]/comments/route.js +0 -22
  16. package/.medusa/server/src/api/admin/contact-requests/[id]/route.js +0 -41
  17. package/.medusa/server/src/api/admin/contact-requests/route.js +0 -31
  18. package/.medusa/server/src/api/admin/contact-requests/validators.js +0 -19
  19. package/.medusa/server/src/api/admin/plugin/route.js +0 -11
  20. package/.medusa/server/src/api/store/contact-requests/route.js +0 -29
  21. package/.medusa/server/src/api/store/contact-requests/validators.js +0 -15
  22. package/.medusa/server/src/api/store/plugin/route.js +0 -11
  23. package/.medusa/server/src/helpers/__tests__/submit-contact-request.test.js +0 -125
  24. package/.medusa/server/src/helpers/submit-contact-request.js +0 -45
  25. package/.medusa/server/src/modules/contact-requests/index.js +0 -15
  26. package/.medusa/server/src/modules/contact-requests/migrations/Migration20241124090000.js +0 -51
  27. package/.medusa/server/src/modules/contact-requests/models/contact-request-comment.js +0 -11
  28. package/.medusa/server/src/modules/contact-requests/models/contact-request.js +0 -14
  29. package/.medusa/server/src/modules/contact-requests/service.js +0 -186
  30. package/.medusa/server/src/plugin-options.js +0 -16
  31. package/.medusa/server/src/types/__tests__/contact-options.test.js +0 -83
  32. package/.medusa/server/src/utils/__tests__/payload-validator.test.js +0 -81
  33. package/.medusa/server/src/utils/payload.js +0 -127
  34. package/.medusa/server/src/workflows/create-contact-request.js +0 -30
  35. package/.medusa/server/src/workflows/index.js +0 -8
  36. package/.medusa/server/src/workflows/steps/create-contact-request-step.js +0 -17
  37. package/.medusa/server/src/workflows/steps/send-contact-notification-step.js +0 -48
  38. package/.medusa/server/src/workflows/steps/update-contact-request-status-step.js +0 -17
  39. package/.medusa/server/src/workflows/update-contact-request-status.js +0 -32
@@ -4,8 +4,7 @@ const react = require("react");
4
4
  const adminSdk = require("@medusajs/admin-sdk");
5
5
  const ui = require("@medusajs/ui");
6
6
  const icons = require("@medusajs/icons");
7
- const reactRouterDom = require("react-router-dom");
8
- const useDebounce$1 = (value, delay) => {
7
+ const useDebounce = (value, delay) => {
9
8
  const [debouncedValue, setDebouncedValue] = react.useState(value);
10
9
  react.useEffect(() => {
11
10
  const handler = setTimeout(() => setDebouncedValue(value), delay);
@@ -28,7 +27,7 @@ const ContactEmailSubscriptionsPage = () => {
28
27
  const [items, setItems] = react.useState([]);
29
28
  const [statusFilter, setStatusFilter] = react.useState("all");
30
29
  const [query, setQuery] = react.useState("");
31
- const debouncedQuery = useDebounce$1(query, 300);
30
+ const debouncedQuery = useDebounce(query, 300);
32
31
  const [isLoading, setIsLoading] = react.useState(true);
33
32
  const [isFetchingMore, setIsFetchingMore] = react.useState(false);
34
33
  const [error, setError] = react.useState(null);
@@ -166,486 +165,11 @@ const ContactEmailSubscriptionsPage = () => {
166
165
  ) }) : null
167
166
  ] }) });
168
167
  };
169
- const config$1 = adminSdk.defineRouteConfig({
170
- label: "Contact email list",
171
- icon: icons.Envelope
172
- });
173
- const useDebounce = (value, delay) => {
174
- const [debouncedValue, setDebouncedValue] = react.useState(value);
175
- react.useEffect(() => {
176
- const handler = setTimeout(() => setDebouncedValue(value), delay);
177
- return () => clearTimeout(handler);
178
- }, [value, delay]);
179
- return debouncedValue;
180
- };
181
- const statusBadgeClass = (status) => {
182
- if (status.includes("close") || status.includes("resolved")) {
183
- return "bg-ui-tag-green-bg text-ui-tag-green-text";
184
- }
185
- if (status.includes("review") || status.includes("progress")) {
186
- return "bg-ui-tag-orange-bg text-ui-tag-orange-text";
187
- }
188
- return "bg-ui-tag-neutral-bg text-ui-tag-neutral-text";
189
- };
190
- const ContactRequestsPage = () => {
191
- const [requests, setRequests] = react.useState([]);
192
- const [statuses, setStatuses] = react.useState([]);
193
- const [statusFilter, setStatusFilter] = react.useState("all");
194
- const [query, setQuery] = react.useState("");
195
- const debouncedQuery = useDebounce(query, 300);
196
- const [isLoading, setIsLoading] = react.useState(true);
197
- const [isFetchingMore, setIsFetchingMore] = react.useState(false);
198
- const [error, setError] = react.useState(null);
199
- const [offset, setOffset] = react.useState(0);
200
- const [count, setCount] = react.useState(0);
201
- const limit = 20;
202
- const loadRequests = react.useCallback(
203
- async (nextOffset, replace = false) => {
204
- var _a;
205
- try {
206
- if (replace) {
207
- setIsLoading(true);
208
- } else {
209
- setIsFetchingMore(true);
210
- }
211
- setError(null);
212
- const params = new URLSearchParams();
213
- params.set("limit", String(limit));
214
- params.set("offset", String(nextOffset));
215
- if (statusFilter !== "all") {
216
- params.set("status", statusFilter);
217
- }
218
- if (debouncedQuery.trim()) {
219
- params.set("q", debouncedQuery.trim());
220
- }
221
- const response = await fetch(
222
- `/admin/contact-requests?${params.toString()}`,
223
- { credentials: "include" }
224
- );
225
- if (!response.ok) {
226
- const message = await response.text();
227
- throw new Error(message || "Unable to load contact requests");
228
- }
229
- const payload = await response.json();
230
- setStatuses(payload.statuses ?? []);
231
- setCount(payload.count ?? 0);
232
- setOffset(nextOffset + (((_a = payload.contact_requests) == null ? void 0 : _a.length) ?? 0));
233
- setRequests(
234
- (prev) => replace ? payload.contact_requests ?? [] : [...prev, ...payload.contact_requests ?? []]
235
- );
236
- } catch (loadError) {
237
- const message = loadError instanceof Error ? loadError.message : "Unable to load contact requests";
238
- setError(message);
239
- } finally {
240
- setIsLoading(false);
241
- setIsFetchingMore(false);
242
- }
243
- },
244
- [statusFilter, debouncedQuery]
245
- );
246
- react.useEffect(() => {
247
- void loadRequests(0, true);
248
- }, [statusFilter, debouncedQuery, loadRequests]);
249
- const hasMore = react.useMemo(() => offset < count, [offset, count]);
250
- const displayedStatuses = react.useMemo(
251
- () => [{ code: "all", label: "All statuses" }, ...statuses],
252
- [statuses]
253
- );
254
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full p-6", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "mx-auto flex w-full max-w-6xl flex-col gap-6 p-6", children: [
255
- /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between", children: [
256
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
257
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Contact Requests" }),
258
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Monitor inbound storefront messages and keep track of their statuses without leaving the admin." })
259
- ] }),
260
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "primary", onClick: () => loadRequests(0, true), children: "Refresh" })
261
- ] }),
262
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
263
- /* @__PURE__ */ jsxRuntime.jsx(
264
- ui.Input,
265
- {
266
- placeholder: "Search by email",
267
- value: query,
268
- onChange: (event) => setQuery(event.target.value),
269
- className: "md:max-w-sm"
270
- }
271
- ),
272
- /* @__PURE__ */ jsxRuntime.jsx(
273
- "select",
274
- {
275
- value: statusFilter,
276
- onChange: (event) => setStatusFilter(event.target.value),
277
- className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive md:max-w-xs",
278
- children: displayedStatuses.map((status) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: status.code, children: status.label }, status.code))
279
- }
280
- )
281
- ] }),
282
- error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-strong p-6 text-center", children: [
283
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "plus", className: "text-ui-fg-error", children: error }),
284
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: () => loadRequests(0, true), children: "Try again" }) })
285
- ] }) : null,
286
- isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-16", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Loading contact requests..." }) }) : requests.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-dashed border-ui-border-strong p-10 text-center", children: [
287
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", className: "text-xl", children: "No contact requests yet" }),
288
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "mt-2 text-ui-fg-subtle", children: "The contact form is ready. Share it with your customers and new requests will show up here." })
289
- ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-xl border border-ui-border-base", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "min-w-full divide-y divide-ui-border-base", children: [
290
- /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
291
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Email" }),
292
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Status" }),
293
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Created" }),
294
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-right text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Details" })
295
- ] }) }),
296
- /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-ui-border-subtle", children: requests.map((request) => {
297
- const statusOption = statuses.find(
298
- (status) => status.code === request.status
299
- );
300
- return /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "hover:bg-ui-bg-subtle/60", children: [
301
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4 font-medium text-ui-fg-base", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
302
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: request.email }),
303
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: request.id })
304
- ] }) }),
305
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(
306
- ui.Badge,
307
- {
308
- size: "2xsmall",
309
- className: `uppercase ${statusBadgeClass(
310
- request.status
311
- )}`,
312
- children: (statusOption == null ? void 0 : statusOption.label) ?? request.status
313
- }
314
- ) }),
315
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: new Date(request.created_at).toLocaleString() }),
316
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4 text-right", children: /* @__PURE__ */ jsxRuntime.jsx(
317
- reactRouterDom.Link,
318
- {
319
- to: `/contact-requests/${request.id}`,
320
- className: "text-ui-fg-interactive underline",
321
- children: "View"
322
- }
323
- ) })
324
- ] }, request.id);
325
- }) })
326
- ] }) }),
327
- hasMore ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
328
- ui.Button,
329
- {
330
- variant: "secondary",
331
- isLoading: isFetchingMore,
332
- onClick: () => loadRequests(offset, false),
333
- children: "Load more"
334
- }
335
- ) }) : null
336
- ] }) });
337
- };
338
168
  const config = adminSdk.defineRouteConfig({
339
- label: "Contact Requests",
169
+ label: "Contact email list",
340
170
  icon: icons.Envelope
341
171
  });
342
- const formatDateTime = (value) => new Date(value).toLocaleString(void 0, {
343
- dateStyle: "medium",
344
- timeStyle: "short"
345
- });
346
- const ContactRequestDetailPage = () => {
347
- const { id } = reactRouterDom.useParams();
348
- const navigate = reactRouterDom.useNavigate();
349
- const [request, setRequest] = react.useState(null);
350
- const [comments, setComments] = react.useState([]);
351
- const [statusOptions, setStatusOptions] = react.useState([]);
352
- const [transitions, setTransitions] = react.useState([]);
353
- const [statusSelection, setStatusSelection] = react.useState("");
354
- const [statusNote, setStatusNote] = react.useState("");
355
- const [newComment, setNewComment] = react.useState("");
356
- const [isLoading, setIsLoading] = react.useState(true);
357
- const [error, setError] = react.useState(null);
358
- const [isUpdatingStatus, setIsUpdatingStatus] = react.useState(false);
359
- const [isSavingComment, setIsSavingComment] = react.useState(false);
360
- const loadDetail = react.useCallback(async () => {
361
- if (!id) {
362
- return;
363
- }
364
- setIsLoading(true);
365
- setError(null);
366
- try {
367
- const response = await fetch(`/admin/contact-requests/${id}`, {
368
- credentials: "include"
369
- });
370
- if (!response.ok) {
371
- const message = await response.text();
372
- throw new Error(message || "Unable to load contact request");
373
- }
374
- const payload = await response.json();
375
- setRequest(payload.contact_request);
376
- setComments(payload.comments ?? []);
377
- setStatusOptions(payload.statuses ?? []);
378
- setTransitions(payload.transitions ?? []);
379
- } catch (loadError) {
380
- const message = loadError instanceof Error ? loadError.message : "Unable to load contact request";
381
- setError(message);
382
- } finally {
383
- setIsLoading(false);
384
- }
385
- }, [id]);
386
- react.useEffect(() => {
387
- void loadDetail();
388
- }, [loadDetail]);
389
- react.useEffect(() => {
390
- if (transitions.length) {
391
- setStatusSelection(transitions[0]);
392
- } else {
393
- setStatusSelection("");
394
- }
395
- }, [transitions]);
396
- const statusLabelMap = react.useMemo(() => {
397
- const map = /* @__PURE__ */ new Map();
398
- statusOptions.forEach((status) => map.set(status.code, status.label));
399
- return map;
400
- }, [statusOptions]);
401
- const orderedHistory = react.useMemo(() => {
402
- return [...(request == null ? void 0 : request.status_history) ?? []].sort(
403
- (a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
404
- );
405
- }, [request == null ? void 0 : request.status_history]);
406
- const handleStatusUpdate = react.useCallback(async () => {
407
- if (!id || !statusSelection) {
408
- ui.toast.error("Select a status to continue");
409
- return;
410
- }
411
- setIsUpdatingStatus(true);
412
- try {
413
- const response = await fetch(`/admin/contact-requests/${id}`, {
414
- method: "PATCH",
415
- credentials: "include",
416
- headers: {
417
- "Content-Type": "application/json"
418
- },
419
- body: JSON.stringify({
420
- status: statusSelection,
421
- note: statusNote || void 0
422
- })
423
- });
424
- if (!response.ok) {
425
- const message = await response.text();
426
- throw new Error(message || "Unable to update status");
427
- }
428
- const payload = await response.json();
429
- setRequest(payload.contact_request);
430
- setTransitions(payload.transitions ?? []);
431
- setStatusNote("");
432
- ui.toast.success("Status updated");
433
- } catch (updateError) {
434
- const message = updateError instanceof Error ? updateError.message : "Unable to update status";
435
- ui.toast.error(message);
436
- } finally {
437
- setIsUpdatingStatus(false);
438
- }
439
- }, [id, statusSelection, statusNote]);
440
- const handleAddComment = react.useCallback(async () => {
441
- if (!id) {
442
- return;
443
- }
444
- if (!newComment.trim()) {
445
- ui.toast.error("Comment cannot be empty");
446
- return;
447
- }
448
- setIsSavingComment(true);
449
- try {
450
- const response = await fetch(
451
- `/admin/contact-requests/${id}/comments`,
452
- {
453
- method: "POST",
454
- credentials: "include",
455
- headers: {
456
- "Content-Type": "application/json"
457
- },
458
- body: JSON.stringify({ comment: newComment })
459
- }
460
- );
461
- if (!response.ok) {
462
- const message = await response.text();
463
- throw new Error(message || "Unable to add comment");
464
- }
465
- const payload = await response.json();
466
- setComments((prev) => [payload.comment, ...prev]);
467
- setNewComment("");
468
- ui.toast.success("Comment added");
469
- } catch (commentError) {
470
- const message = commentError instanceof Error ? commentError.message : "Unable to add comment";
471
- ui.toast.error(message);
472
- } finally {
473
- setIsSavingComment(false);
474
- }
475
- }, [id, newComment]);
476
- if (isLoading) {
477
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-32", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Loading contact request..." }) });
478
- }
479
- if (error || !request) {
480
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "mx-auto mt-12 max-w-3xl p-8 text-center", children: [
481
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Unable to load request" }),
482
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-ui-fg-subtle", children: error ?? "Unknown error" }),
483
- /* @__PURE__ */ jsxRuntime.jsx(
484
- ui.Button,
485
- {
486
- variant: "secondary",
487
- className: "mt-6",
488
- onClick: () => navigate("/contact-requests"),
489
- children: "Back to list"
490
- }
491
- )
492
- ] });
493
- }
494
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full p-6", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "mx-auto flex w-full max-w-5xl flex-col gap-6 p-6", children: [
495
- /* @__PURE__ */ jsxRuntime.jsxs(
496
- "button",
497
- {
498
- className: "flex w-fit items-center gap-2 text-ui-fg-interactive",
499
- onClick: () => navigate("/contact-requests"),
500
- children: [
501
- /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUturnLeft, { className: "h-4 w-4" }),
502
- "Back to requests"
503
- ]
504
- }
505
- ),
506
- /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-col gap-3", children: [
507
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: request.email }),
508
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
509
- "Request ID ",
510
- request.id
511
- ] })
512
- ] }),
513
- /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "grid gap-6 lg:grid-cols-3", children: [
514
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-ui-border-base p-5 lg:col-span-2", children: [
515
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", className: "text-lg", children: "Submitted fields" }),
516
- /* @__PURE__ */ jsxRuntime.jsx("dl", { className: "mt-4 grid gap-4", children: Object.entries(request.payload ?? {}).length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "No additional fields captured for this request." }) : Object.entries(request.payload ?? {}).map(([key, value]) => /* @__PURE__ */ jsxRuntime.jsxs(
517
- "div",
518
- {
519
- className: "flex flex-col gap-1 rounded-lg bg-ui-bg-subtle p-3",
520
- children: [
521
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs uppercase text-ui-fg-muted", children: key }),
522
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium text-ui-fg-base", children: typeof value === "object" ? JSON.stringify(value, null, 2) : String(value) })
523
- ]
524
- },
525
- key
526
- )) })
527
- ] }),
528
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-ui-border-base p-5", children: [
529
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", className: "text-lg", children: "Status" }),
530
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex flex-col gap-3", children: [
531
- /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { className: "w-fit bg-ui-tag-neutral-bg text-ui-tag-neutral-text", children: statusLabelMap.get(request.status) ?? request.status }),
532
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
533
- "Created ",
534
- formatDateTime(request.created_at)
535
- ] }),
536
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
537
- "Last updated ",
538
- formatDateTime(request.updated_at)
539
- ] })
540
- ] }),
541
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-6 flex flex-col gap-3", children: [
542
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Next status" }),
543
- /* @__PURE__ */ jsxRuntime.jsxs(
544
- "select",
545
- {
546
- value: statusSelection,
547
- onChange: (event) => setStatusSelection(event.target.value),
548
- className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive",
549
- disabled: !transitions.length,
550
- children: [
551
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: transitions.length ? "Select status" : "No further transitions" }),
552
- transitions.map((status) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: status, children: statusLabelMap.get(status) ?? status }, status))
553
- ]
554
- }
555
- ),
556
- /* @__PURE__ */ jsxRuntime.jsx(
557
- ui.Textarea,
558
- {
559
- rows: 3,
560
- value: statusNote,
561
- onChange: (event) => setStatusNote(event.target.value),
562
- placeholder: "Optional note for history"
563
- }
564
- ),
565
- /* @__PURE__ */ jsxRuntime.jsx(
566
- ui.Button,
567
- {
568
- onClick: () => void handleStatusUpdate(),
569
- disabled: !statusSelection,
570
- isLoading: isUpdatingStatus,
571
- children: "Update status"
572
- }
573
- )
574
- ] })
575
- ] })
576
- ] }),
577
- /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "grid gap-6 lg:grid-cols-3", children: [
578
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-ui-border-base p-5 lg:col-span-2", children: [
579
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", className: "text-lg", children: "Status history" }),
580
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex flex-col gap-4", children: orderedHistory.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
581
- "div",
582
- {
583
- className: "rounded-lg border border-ui-border-subtle p-3",
584
- children: [
585
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
586
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "plus", children: statusLabelMap.get(entry.status) ?? entry.status }),
587
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: formatDateTime(entry.updated_at) })
588
- ] }),
589
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: entry.updated_by ? `By ${entry.updated_by}` : "System" }),
590
- entry.note ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "mt-2", children: entry.note }) : null
591
- ]
592
- },
593
- `${entry.status}-${entry.updated_at}`
594
- )) })
595
- ] }),
596
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-ui-border-base p-5", children: [
597
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", className: "text-lg", children: "Admin comments" }),
598
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 flex flex-col gap-3", children: [
599
- /* @__PURE__ */ jsxRuntime.jsx(
600
- ui.Textarea,
601
- {
602
- rows: 4,
603
- placeholder: "Leave an internal note",
604
- value: newComment,
605
- onChange: (event) => setNewComment(event.target.value)
606
- }
607
- ),
608
- /* @__PURE__ */ jsxRuntime.jsx(
609
- ui.Button,
610
- {
611
- variant: "secondary",
612
- onClick: () => void handleAddComment(),
613
- isLoading: isSavingComment,
614
- children: "Add comment"
615
- }
616
- )
617
- ] }),
618
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-6 flex flex-col gap-4", children: comments.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "No comments yet." }) : comments.map((comment) => /* @__PURE__ */ jsxRuntime.jsxs(
619
- "div",
620
- {
621
- className: "rounded-lg bg-ui-bg-subtle p-3",
622
- children: [
623
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
624
- comment.actor_id ?? "Admin",
625
- " •",
626
- " ",
627
- formatDateTime(comment.created_at)
628
- ] }),
629
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2", children: comment.comment })
630
- ]
631
- },
632
- comment.id
633
- )) })
634
- ] })
635
- ] })
636
- ] }) });
637
- };
638
- adminSdk.defineRouteConfig({
639
- parentRoute: "/contact-requests"
640
- });
641
- const en = {
642
- "contact-requests.title": "Contact Requests",
643
- "contact-requests.subtitle": "Monitor submissions and keep track of every response.",
644
- "contact-requests.empty": "No contact requests yet.",
645
- "contact-requests.detail.status": "Status",
646
- "contact-requests.detail.history": "Status history",
647
- "contact-requests.detail.comments": "Admin comments"
648
- };
172
+ const en = {};
649
173
  const i18nTranslations0 = {
650
174
  en
651
175
  };
@@ -655,14 +179,6 @@ const routeModule = {
655
179
  {
656
180
  Component: ContactEmailSubscriptionsPage,
657
181
  path: "/contact-email-subscriptions"
658
- },
659
- {
660
- Component: ContactRequestsPage,
661
- path: "/contact-requests"
662
- },
663
- {
664
- Component: ContactRequestDetailPage,
665
- path: "/contact-requests/:id"
666
182
  }
667
183
  ]
668
184
  };
@@ -671,12 +187,6 @@ const menuItemModule = {
671
187
  {
672
188
  label: config.label,
673
189
  icon: config.icon,
674
- path: "/contact-requests",
675
- nested: void 0
676
- },
677
- {
678
- label: config$1.label,
679
- icon: config$1.icon,
680
190
  path: "/contact-email-subscriptions",
681
191
  nested: void 0
682
192
  }