medusa-contact-us 0.0.21 → 0.0.22

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.
@@ -5,6 +5,7 @@ const adminSdk = require("@medusajs/admin-sdk");
5
5
  const ui = require("@medusajs/ui");
6
6
  const icons = require("@medusajs/icons");
7
7
  const reactRouterDom = require("react-router-dom");
8
+ require("@medusajs/admin-shared");
8
9
  const useDebounce$1 = (value, delay) => {
9
10
  const [debouncedValue, setDebouncedValue] = react.useState(value);
10
11
  react.useEffect(() => {
@@ -336,6 +337,7 @@ const ContactRequestsPage = () => {
336
337
  /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
337
338
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Email" }),
338
339
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Status" }),
340
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Assigned To" }),
339
341
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Source" }),
340
342
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Created" }),
341
343
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Actions" })
@@ -358,6 +360,7 @@ const ContactRequestsPage = () => {
358
360
  children: request.status.replace("_", " ")
359
361
  }
360
362
  ) }),
363
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: request.assign_to ? /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", children: request.assign_to }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: "—" }) }),
361
364
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: request.source ?? "storefront" }),
362
365
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: new Date(request.created_at).toLocaleString() }),
363
366
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -412,6 +415,7 @@ const ContactRequestDetailPage = () => {
412
415
  const navigate = reactRouterDom.useNavigate();
413
416
  const { id } = reactRouterDom.useParams();
414
417
  const [request, setRequest] = react.useState(null);
418
+ const [comments, setComments] = react.useState([]);
415
419
  const [nextAllowedStatuses, setNextAllowedStatuses] = react.useState([]);
416
420
  const [selectedStatus, setSelectedStatus] = react.useState("");
417
421
  const [isLoading, setIsLoading] = react.useState(true);
@@ -419,6 +423,15 @@ const ContactRequestDetailPage = () => {
419
423
  const [error, setError] = react.useState(null);
420
424
  const [updateError, setUpdateError] = react.useState(null);
421
425
  const [updateSuccess, setUpdateSuccess] = react.useState(false);
426
+ const [selectedAssignTo, setSelectedAssignTo] = react.useState("");
427
+ const [isAssigning, setIsAssigning] = react.useState(false);
428
+ const [assignError, setAssignError] = react.useState(null);
429
+ const [assignSuccess, setAssignSuccess] = react.useState(false);
430
+ const [commentText, setCommentText] = react.useState("");
431
+ const [commentImages, setCommentImages] = react.useState([]);
432
+ const [isCreatingComment, setIsCreatingComment] = react.useState(false);
433
+ const [commentError, setCommentError] = react.useState(null);
434
+ const [commentSuccess, setCommentSuccess] = react.useState(false);
422
435
  react.useEffect(() => {
423
436
  if (!id) {
424
437
  navigate("/contact-requests");
@@ -437,8 +450,10 @@ const ContactRequestDetailPage = () => {
437
450
  }
438
451
  const payload = await response.json();
439
452
  setRequest(payload.request);
453
+ setComments(payload.comments ?? []);
440
454
  setNextAllowedStatuses(payload.next_allowed_statuses ?? []);
441
455
  setSelectedStatus("");
456
+ setSelectedAssignTo("");
442
457
  } catch (loadError) {
443
458
  const message = loadError instanceof Error ? loadError.message : "Unable to load contact request";
444
459
  setError(message);
@@ -478,6 +493,8 @@ const ContactRequestDetailPage = () => {
478
493
  });
479
494
  if (detailResponse.ok) {
480
495
  const detailPayload = await detailResponse.json();
496
+ setRequest(detailPayload.request);
497
+ setComments(detailPayload.comments ?? []);
481
498
  setNextAllowedStatuses(detailPayload.next_allowed_statuses ?? []);
482
499
  }
483
500
  } catch (updateErr) {
@@ -487,6 +504,144 @@ const ContactRequestDetailPage = () => {
487
504
  setIsUpdating(false);
488
505
  }
489
506
  };
507
+ const handleAssign = async () => {
508
+ if (!id) {
509
+ return;
510
+ }
511
+ try {
512
+ setIsAssigning(true);
513
+ setAssignError(null);
514
+ setAssignSuccess(false);
515
+ const response = await fetch(`/admin/contact-requests/${id}/assign`, {
516
+ method: "POST",
517
+ headers: {
518
+ "Content-Type": "application/json"
519
+ },
520
+ credentials: "include",
521
+ body: JSON.stringify({
522
+ assign_to: selectedAssignTo.trim() || null
523
+ })
524
+ });
525
+ if (!response.ok) {
526
+ const message = await response.text();
527
+ throw new Error(message || "Unable to assign contact request");
528
+ }
529
+ const payload = await response.json();
530
+ setRequest(payload.request);
531
+ setSelectedAssignTo("");
532
+ setAssignSuccess(true);
533
+ setTimeout(() => setAssignSuccess(false), 3e3);
534
+ const detailResponse = await fetch(`/admin/contact-requests/${id}`, {
535
+ credentials: "include"
536
+ });
537
+ if (detailResponse.ok) {
538
+ const detailPayload = await detailResponse.json();
539
+ setRequest(detailPayload.request);
540
+ setComments(detailPayload.comments ?? []);
541
+ setNextAllowedStatuses(detailPayload.next_allowed_statuses ?? []);
542
+ }
543
+ } catch (assignErr) {
544
+ const message = assignErr instanceof Error ? assignErr.message : "Unable to assign contact request";
545
+ setAssignError(message);
546
+ } finally {
547
+ setIsAssigning(false);
548
+ }
549
+ };
550
+ const handleUnassign = async () => {
551
+ if (!id) {
552
+ return;
553
+ }
554
+ try {
555
+ setIsAssigning(true);
556
+ setAssignError(null);
557
+ setAssignSuccess(false);
558
+ const response = await fetch(`/admin/contact-requests/${id}/assign`, {
559
+ method: "POST",
560
+ headers: {
561
+ "Content-Type": "application/json"
562
+ },
563
+ credentials: "include",
564
+ body: JSON.stringify({
565
+ assign_to: null
566
+ })
567
+ });
568
+ if (!response.ok) {
569
+ const message = await response.text();
570
+ throw new Error(message || "Unable to unassign contact request");
571
+ }
572
+ const payload = await response.json();
573
+ setRequest(payload.request);
574
+ setAssignSuccess(true);
575
+ setTimeout(() => setAssignSuccess(false), 3e3);
576
+ const detailResponse = await fetch(`/admin/contact-requests/${id}`, {
577
+ credentials: "include"
578
+ });
579
+ if (detailResponse.ok) {
580
+ const detailPayload = await detailResponse.json();
581
+ setRequest(detailPayload.request);
582
+ setComments(detailPayload.comments ?? []);
583
+ setNextAllowedStatuses(detailPayload.next_allowed_statuses ?? []);
584
+ }
585
+ } catch (assignErr) {
586
+ const message = assignErr instanceof Error ? assignErr.message : "Unable to unassign contact request";
587
+ setAssignError(message);
588
+ } finally {
589
+ setIsAssigning(false);
590
+ }
591
+ };
592
+ const handleCreateComment = async () => {
593
+ if (!id || !commentText.trim() && commentImages.length === 0) {
594
+ setCommentError("Please provide a comment or at least one image");
595
+ return;
596
+ }
597
+ try {
598
+ setIsCreatingComment(true);
599
+ setCommentError(null);
600
+ setCommentSuccess(false);
601
+ const response = await fetch(`/admin/contact-requests/${id}/comments`, {
602
+ method: "POST",
603
+ headers: {
604
+ "Content-Type": "application/json"
605
+ },
606
+ credentials: "include",
607
+ body: JSON.stringify({
608
+ comment: commentText.trim() || void 0,
609
+ images: commentImages.filter((img) => img.trim()).length > 0 ? commentImages.filter((img) => img.trim()) : void 0
610
+ })
611
+ });
612
+ if (!response.ok) {
613
+ const message = await response.text();
614
+ throw new Error(message || "Unable to create comment");
615
+ }
616
+ const detailResponse = await fetch(`/admin/contact-requests/${id}`, {
617
+ credentials: "include"
618
+ });
619
+ if (detailResponse.ok) {
620
+ const detailPayload = await detailResponse.json();
621
+ setComments(detailPayload.comments ?? []);
622
+ }
623
+ setCommentText("");
624
+ setCommentImages([]);
625
+ setCommentSuccess(true);
626
+ setTimeout(() => setCommentSuccess(false), 3e3);
627
+ } catch (commentErr) {
628
+ const message = commentErr instanceof Error ? commentErr.message : "Unable to create comment";
629
+ setCommentError(message);
630
+ } finally {
631
+ setIsCreatingComment(false);
632
+ }
633
+ };
634
+ const addImageInput = () => {
635
+ setCommentImages([...commentImages, ""]);
636
+ };
637
+ const removeImageInput = (index) => {
638
+ setCommentImages(commentImages.filter((_, i) => i !== index));
639
+ };
640
+ const updateImageUrl = (index, value) => {
641
+ const updated = [...commentImages];
642
+ updated[index] = value;
643
+ setCommentImages(updated);
644
+ };
490
645
  if (isLoading) {
491
646
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full p-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "mx-auto flex w-full max-w-5xl flex-col gap-6 p-6", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-16", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Loading contact request..." }) }) }) });
492
647
  }
@@ -558,6 +713,54 @@ const ContactRequestDetailPage = () => {
558
713
  updateError && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "mt-2 text-ui-fg-error", children: updateError }),
559
714
  updateSuccess && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "mt-2 text-ui-fg-success", children: "Status updated successfully" })
560
715
  ] }),
716
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-base p-6", children: [
717
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-4 text-lg", children: "Assignment" }),
718
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
719
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
720
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Currently Assigned To" }),
721
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: request.assign_to ? /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "small", className: "mt-1", children: request.assign_to }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-ui-fg-subtle", children: "Unassigned" }) })
722
+ ] }),
723
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-end", children: [
724
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
725
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mb-2 block text-sm font-medium text-ui-fg-base", children: "Admin User ID" }),
726
+ /* @__PURE__ */ jsxRuntime.jsx(
727
+ ui.Input,
728
+ {
729
+ type: "text",
730
+ value: selectedAssignTo,
731
+ onChange: (event) => setSelectedAssignTo(event.target.value),
732
+ placeholder: "Enter admin user ID",
733
+ className: "w-full"
734
+ }
735
+ )
736
+ ] }),
737
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
738
+ /* @__PURE__ */ jsxRuntime.jsx(
739
+ ui.Button,
740
+ {
741
+ variant: "primary",
742
+ onClick: handleAssign,
743
+ disabled: isAssigning,
744
+ isLoading: isAssigning,
745
+ children: "Assign"
746
+ }
747
+ ),
748
+ request.assign_to && /* @__PURE__ */ jsxRuntime.jsx(
749
+ ui.Button,
750
+ {
751
+ variant: "secondary",
752
+ onClick: handleUnassign,
753
+ disabled: isAssigning,
754
+ isLoading: isAssigning,
755
+ children: "Unassign"
756
+ }
757
+ )
758
+ ] })
759
+ ] }),
760
+ assignError && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-error", children: assignError }),
761
+ assignSuccess && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-success", children: "Assignment updated successfully" })
762
+ ] })
763
+ ] }),
561
764
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-6 md:grid-cols-2", children: [
562
765
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-base p-6", children: [
563
766
  /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-4 text-lg", children: "Contact Information" }),
@@ -631,6 +834,103 @@ const ContactRequestDetailPage = () => {
631
834
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: key }),
632
835
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "font-medium", children: typeof value === "string" ? value : JSON.stringify(value) })
633
836
  ] }, key)) })
837
+ ] }),
838
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-base p-6", children: [
839
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-4 text-lg", children: "Comments" }),
840
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 space-y-4 rounded-lg border border-ui-border-subtle bg-ui-bg-subtle p-4", children: [
841
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
842
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mb-2 block text-sm font-medium text-ui-fg-base", children: "Add Comment" }),
843
+ /* @__PURE__ */ jsxRuntime.jsx(
844
+ ui.Textarea,
845
+ {
846
+ value: commentText,
847
+ onChange: (event) => setCommentText(event.target.value),
848
+ placeholder: "Enter your comment...",
849
+ className: "min-h-[80px]"
850
+ }
851
+ )
852
+ ] }),
853
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
854
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
855
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium text-ui-fg-base", children: "Images (URLs)" }),
856
+ /* @__PURE__ */ jsxRuntime.jsx(
857
+ ui.Button,
858
+ {
859
+ variant: "transparent",
860
+ size: "small",
861
+ onClick: addImageInput,
862
+ type: "button",
863
+ children: "Add Image URL"
864
+ }
865
+ )
866
+ ] }),
867
+ commentImages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: commentImages.map((url, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
868
+ /* @__PURE__ */ jsxRuntime.jsx(
869
+ ui.Input,
870
+ {
871
+ type: "text",
872
+ value: url,
873
+ onChange: (event) => updateImageUrl(index, event.target.value),
874
+ placeholder: "https://example.com/image.jpg",
875
+ className: "flex-1"
876
+ }
877
+ ),
878
+ /* @__PURE__ */ jsxRuntime.jsx(
879
+ ui.Button,
880
+ {
881
+ variant: "transparent",
882
+ size: "small",
883
+ onClick: () => removeImageInput(index),
884
+ type: "button",
885
+ children: "Remove"
886
+ }
887
+ )
888
+ ] }, index)) })
889
+ ] }),
890
+ /* @__PURE__ */ jsxRuntime.jsx(
891
+ ui.Button,
892
+ {
893
+ variant: "primary",
894
+ onClick: handleCreateComment,
895
+ disabled: isCreatingComment || !commentText.trim() && commentImages.filter((img) => img.trim()).length === 0,
896
+ isLoading: isCreatingComment,
897
+ children: "Add Comment"
898
+ }
899
+ ),
900
+ commentError && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-error", children: commentError }),
901
+ commentSuccess && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-success", children: "Comment added successfully" })
902
+ ] }),
903
+ comments.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ jsxRuntime.jsxs(
904
+ "div",
905
+ {
906
+ className: "rounded-lg border border-ui-border-subtle bg-ui-bg-subtle p-4",
907
+ children: [
908
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2 flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
909
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", children: comment.admin_id }),
910
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: new Date(comment.created_at).toLocaleString() })
911
+ ] }) }),
912
+ comment.comment && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-2 whitespace-pre-wrap", children: comment.comment }),
913
+ comment.images && comment.images.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 space-y-2", children: [
914
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Images:" }),
915
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: comment.images.map((imageUrl, index) => /* @__PURE__ */ jsxRuntime.jsxs(
916
+ "a",
917
+ {
918
+ href: imageUrl,
919
+ target: "_blank",
920
+ rel: "noopener noreferrer",
921
+ className: "text-sm text-ui-fg-interactive hover:underline",
922
+ children: [
923
+ "Image ",
924
+ index + 1
925
+ ]
926
+ },
927
+ index
928
+ )) })
929
+ ] })
930
+ ]
931
+ },
932
+ comment.id
933
+ )) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "No comments yet" })
634
934
  ] })
635
935
  ] }) });
636
936
  };