medusa-contact-us 0.0.21 → 0.0.23

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,58 @@ 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 [adminUsers, setAdminUsers] = react.useState([]);
431
+ const [adminSearchQuery, setAdminSearchQuery] = react.useState("");
432
+ const [isAdminDropdownOpen, setIsAdminDropdownOpen] = react.useState(false);
433
+ const [isLoadingAdmins, setIsLoadingAdmins] = react.useState(false);
434
+ const [selectedAdminIndex, setSelectedAdminIndex] = react.useState(-1);
435
+ const adminDropdownRef = react.useRef(null);
436
+ const adminInputRef = react.useRef(null);
437
+ const [commentText, setCommentText] = react.useState("");
438
+ const [commentImages, setCommentImages] = react.useState([]);
439
+ const [isCreatingComment, setIsCreatingComment] = react.useState(false);
440
+ const [commentError, setCommentError] = react.useState(null);
441
+ const [commentSuccess, setCommentSuccess] = react.useState(false);
442
+ const useDebounce2 = (value, delay) => {
443
+ const [debouncedValue, setDebouncedValue] = react.useState(value);
444
+ react.useEffect(() => {
445
+ const handler = setTimeout(() => setDebouncedValue(value), delay);
446
+ return () => clearTimeout(handler);
447
+ }, [value, delay]);
448
+ return debouncedValue;
449
+ };
450
+ const debouncedAdminSearch = useDebounce2(adminSearchQuery, 300);
451
+ const fetchAdminUsers = react.useCallback(async (searchQuery) => {
452
+ try {
453
+ setIsLoadingAdmins(true);
454
+ const params = new URLSearchParams();
455
+ if (searchQuery) {
456
+ params.set("q", searchQuery);
457
+ }
458
+ const response = await fetch(`/admin/users?${params.toString()}`, {
459
+ credentials: "include"
460
+ });
461
+ if (!response.ok) {
462
+ throw new Error("Unable to load admin users");
463
+ }
464
+ const payload = await response.json();
465
+ setAdminUsers(payload.users || []);
466
+ } catch (fetchError) {
467
+ console.error("Error fetching admin users:", fetchError);
468
+ setAdminUsers([]);
469
+ } finally {
470
+ setIsLoadingAdmins(false);
471
+ }
472
+ }, []);
473
+ const filteredAdmins = adminUsers.filter((admin) => {
474
+ if (!debouncedAdminSearch) return true;
475
+ const query = debouncedAdminSearch.toLowerCase();
476
+ return admin.name.toLowerCase().includes(query) || admin.email.toLowerCase().includes(query) || admin.id.toLowerCase().includes(query);
477
+ });
422
478
  react.useEffect(() => {
423
479
  if (!id) {
424
480
  navigate("/contact-requests");
@@ -437,8 +493,11 @@ const ContactRequestDetailPage = () => {
437
493
  }
438
494
  const payload = await response.json();
439
495
  setRequest(payload.request);
496
+ setComments(payload.comments ?? []);
440
497
  setNextAllowedStatuses(payload.next_allowed_statuses ?? []);
441
498
  setSelectedStatus("");
499
+ setSelectedAssignTo("");
500
+ setAdminSearchQuery("");
442
501
  } catch (loadError) {
443
502
  const message = loadError instanceof Error ? loadError.message : "Unable to load contact request";
444
503
  setError(message);
@@ -448,6 +507,59 @@ const ContactRequestDetailPage = () => {
448
507
  };
449
508
  void loadRequest();
450
509
  }, [id, navigate]);
510
+ react.useEffect(() => {
511
+ if (isAdminDropdownOpen) {
512
+ void fetchAdminUsers(debouncedAdminSearch || void 0);
513
+ }
514
+ }, [isAdminDropdownOpen, debouncedAdminSearch, fetchAdminUsers]);
515
+ react.useEffect(() => {
516
+ const handleClickOutside = (event) => {
517
+ if (adminDropdownRef.current && !adminDropdownRef.current.contains(event.target) && adminInputRef.current && !adminInputRef.current.contains(event.target)) {
518
+ setIsAdminDropdownOpen(false);
519
+ setSelectedAdminIndex(-1);
520
+ }
521
+ };
522
+ if (isAdminDropdownOpen) {
523
+ document.addEventListener("mousedown", handleClickOutside);
524
+ return () => document.removeEventListener("mousedown", handleClickOutside);
525
+ }
526
+ }, [isAdminDropdownOpen]);
527
+ const handleAdminKeyDown = (event) => {
528
+ if (event.key === "ArrowDown") {
529
+ event.preventDefault();
530
+ setSelectedAdminIndex(
531
+ (prev) => prev < filteredAdmins.length - 1 ? prev + 1 : prev
532
+ );
533
+ setIsAdminDropdownOpen(true);
534
+ } else if (event.key === "ArrowUp") {
535
+ event.preventDefault();
536
+ setSelectedAdminIndex((prev) => prev > 0 ? prev - 1 : -1);
537
+ setIsAdminDropdownOpen(true);
538
+ } else if (event.key === "Enter" && selectedAdminIndex >= 0) {
539
+ event.preventDefault();
540
+ const admin = filteredAdmins[selectedAdminIndex];
541
+ if (admin) {
542
+ handleAdminSelect(admin);
543
+ }
544
+ } else if (event.key === "Escape") {
545
+ setIsAdminDropdownOpen(false);
546
+ setSelectedAdminIndex(-1);
547
+ }
548
+ };
549
+ const handleAdminSelect = (admin) => {
550
+ setSelectedAssignTo(admin.id);
551
+ setAdminSearchQuery(admin.name || admin.email);
552
+ setIsAdminDropdownOpen(false);
553
+ setSelectedAdminIndex(-1);
554
+ };
555
+ react.useEffect(() => {
556
+ if ((request == null ? void 0 : request.assign_to) && adminUsers.length > 0) {
557
+ const assignedAdmin = adminUsers.find((admin) => admin.id === request.assign_to);
558
+ if (assignedAdmin && !adminSearchQuery) {
559
+ setAdminSearchQuery(assignedAdmin.name || assignedAdmin.email);
560
+ }
561
+ }
562
+ }, [request == null ? void 0 : request.assign_to, adminUsers, adminSearchQuery]);
451
563
  const handleStatusUpdate = async () => {
452
564
  if (!id || !selectedStatus) {
453
565
  return;
@@ -478,6 +590,8 @@ const ContactRequestDetailPage = () => {
478
590
  });
479
591
  if (detailResponse.ok) {
480
592
  const detailPayload = await detailResponse.json();
593
+ setRequest(detailPayload.request);
594
+ setComments(detailPayload.comments ?? []);
481
595
  setNextAllowedStatuses(detailPayload.next_allowed_statuses ?? []);
482
596
  }
483
597
  } catch (updateErr) {
@@ -487,6 +601,144 @@ const ContactRequestDetailPage = () => {
487
601
  setIsUpdating(false);
488
602
  }
489
603
  };
604
+ const handleAssign = async () => {
605
+ if (!id) {
606
+ return;
607
+ }
608
+ try {
609
+ setIsAssigning(true);
610
+ setAssignError(null);
611
+ setAssignSuccess(false);
612
+ const response = await fetch(`/admin/contact-requests/${id}/assign`, {
613
+ method: "POST",
614
+ headers: {
615
+ "Content-Type": "application/json"
616
+ },
617
+ credentials: "include",
618
+ body: JSON.stringify({
619
+ assign_to: selectedAssignTo.trim() || null
620
+ })
621
+ });
622
+ if (!response.ok) {
623
+ const message = await response.text();
624
+ throw new Error(message || "Unable to assign contact request");
625
+ }
626
+ const payload = await response.json();
627
+ setRequest(payload.request);
628
+ setSelectedAssignTo("");
629
+ setAssignSuccess(true);
630
+ setTimeout(() => setAssignSuccess(false), 3e3);
631
+ const detailResponse = await fetch(`/admin/contact-requests/${id}`, {
632
+ credentials: "include"
633
+ });
634
+ if (detailResponse.ok) {
635
+ const detailPayload = await detailResponse.json();
636
+ setRequest(detailPayload.request);
637
+ setComments(detailPayload.comments ?? []);
638
+ setNextAllowedStatuses(detailPayload.next_allowed_statuses ?? []);
639
+ }
640
+ } catch (assignErr) {
641
+ const message = assignErr instanceof Error ? assignErr.message : "Unable to assign contact request";
642
+ setAssignError(message);
643
+ } finally {
644
+ setIsAssigning(false);
645
+ }
646
+ };
647
+ const handleUnassign = async () => {
648
+ if (!id) {
649
+ return;
650
+ }
651
+ try {
652
+ setIsAssigning(true);
653
+ setAssignError(null);
654
+ setAssignSuccess(false);
655
+ const response = await fetch(`/admin/contact-requests/${id}/assign`, {
656
+ method: "POST",
657
+ headers: {
658
+ "Content-Type": "application/json"
659
+ },
660
+ credentials: "include",
661
+ body: JSON.stringify({
662
+ assign_to: null
663
+ })
664
+ });
665
+ if (!response.ok) {
666
+ const message = await response.text();
667
+ throw new Error(message || "Unable to unassign contact request");
668
+ }
669
+ const payload = await response.json();
670
+ setRequest(payload.request);
671
+ setAssignSuccess(true);
672
+ setTimeout(() => setAssignSuccess(false), 3e3);
673
+ const detailResponse = await fetch(`/admin/contact-requests/${id}`, {
674
+ credentials: "include"
675
+ });
676
+ if (detailResponse.ok) {
677
+ const detailPayload = await detailResponse.json();
678
+ setRequest(detailPayload.request);
679
+ setComments(detailPayload.comments ?? []);
680
+ setNextAllowedStatuses(detailPayload.next_allowed_statuses ?? []);
681
+ }
682
+ } catch (assignErr) {
683
+ const message = assignErr instanceof Error ? assignErr.message : "Unable to unassign contact request";
684
+ setAssignError(message);
685
+ } finally {
686
+ setIsAssigning(false);
687
+ }
688
+ };
689
+ const handleCreateComment = async () => {
690
+ if (!id || !commentText.trim() && commentImages.length === 0) {
691
+ setCommentError("Please provide a comment or at least one image");
692
+ return;
693
+ }
694
+ try {
695
+ setIsCreatingComment(true);
696
+ setCommentError(null);
697
+ setCommentSuccess(false);
698
+ const response = await fetch(`/admin/contact-requests/${id}/comments`, {
699
+ method: "POST",
700
+ headers: {
701
+ "Content-Type": "application/json"
702
+ },
703
+ credentials: "include",
704
+ body: JSON.stringify({
705
+ comment: commentText.trim() || void 0,
706
+ images: commentImages.filter((img) => img.trim()).length > 0 ? commentImages.filter((img) => img.trim()) : void 0
707
+ })
708
+ });
709
+ if (!response.ok) {
710
+ const message = await response.text();
711
+ throw new Error(message || "Unable to create comment");
712
+ }
713
+ const detailResponse = await fetch(`/admin/contact-requests/${id}`, {
714
+ credentials: "include"
715
+ });
716
+ if (detailResponse.ok) {
717
+ const detailPayload = await detailResponse.json();
718
+ setComments(detailPayload.comments ?? []);
719
+ }
720
+ setCommentText("");
721
+ setCommentImages([]);
722
+ setCommentSuccess(true);
723
+ setTimeout(() => setCommentSuccess(false), 3e3);
724
+ } catch (commentErr) {
725
+ const message = commentErr instanceof Error ? commentErr.message : "Unable to create comment";
726
+ setCommentError(message);
727
+ } finally {
728
+ setIsCreatingComment(false);
729
+ }
730
+ };
731
+ const addImageInput = () => {
732
+ setCommentImages([...commentImages, ""]);
733
+ };
734
+ const removeImageInput = (index) => {
735
+ setCommentImages(commentImages.filter((_, i) => i !== index));
736
+ };
737
+ const updateImageUrl = (index, value) => {
738
+ const updated = [...commentImages];
739
+ updated[index] = value;
740
+ setCommentImages(updated);
741
+ };
490
742
  if (isLoading) {
491
743
  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
744
  }
@@ -558,6 +810,103 @@ const ContactRequestDetailPage = () => {
558
810
  updateError && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "mt-2 text-ui-fg-error", children: updateError }),
559
811
  updateSuccess && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "mt-2 text-ui-fg-success", children: "Status updated successfully" })
560
812
  ] }),
813
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-base p-6", children: [
814
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-4 text-lg", children: "Assignment" }),
815
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
816
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
817
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Currently Assigned To" }),
818
+ /* @__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" }) })
819
+ ] }),
820
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-end", children: [
821
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 relative", children: [
822
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mb-2 block text-sm font-medium text-ui-fg-base", children: "Assign To Admin" }),
823
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
824
+ /* @__PURE__ */ jsxRuntime.jsx(
825
+ ui.Input,
826
+ {
827
+ ref: adminInputRef,
828
+ type: "text",
829
+ value: adminSearchQuery,
830
+ onChange: (event) => {
831
+ setAdminSearchQuery(event.target.value);
832
+ setIsAdminDropdownOpen(true);
833
+ setSelectedAdminIndex(-1);
834
+ },
835
+ onFocus: () => {
836
+ setIsAdminDropdownOpen(true);
837
+ if (adminUsers.length === 0) {
838
+ void fetchAdminUsers();
839
+ }
840
+ },
841
+ onKeyDown: handleAdminKeyDown,
842
+ placeholder: "Search admin by name, email, or ID...",
843
+ className: "w-full"
844
+ }
845
+ ),
846
+ isAdminDropdownOpen && /* @__PURE__ */ jsxRuntime.jsx(
847
+ "div",
848
+ {
849
+ ref: adminDropdownRef,
850
+ className: "absolute z-50 mt-1 w-full max-h-60 overflow-auto rounded-md border border-ui-border-base bg-ui-bg-base shadow-lg",
851
+ children: isLoadingAdmins ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Loading admins..." }) }) : filteredAdmins.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "py-1", children: filteredAdmins.map((admin, index) => /* @__PURE__ */ jsxRuntime.jsx(
852
+ "li",
853
+ {
854
+ className: `cursor-pointer px-4 py-3 hover:bg-ui-bg-subtle-hover ${index === selectedAdminIndex ? "bg-ui-bg-subtle-hover" : ""}`,
855
+ onClick: () => handleAdminSelect(admin),
856
+ onMouseEnter: () => setSelectedAdminIndex(index),
857
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
858
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
859
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium", children: admin.name }),
860
+ admin.id === selectedAssignTo && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", children: "Selected" })
861
+ ] }),
862
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: admin.email }),
863
+ /* @__PURE__ */ jsxRuntime.jsx(
864
+ ui.Text,
865
+ {
866
+ size: "xsmall",
867
+ className: "text-ui-fg-muted font-mono",
868
+ children: admin.id
869
+ }
870
+ )
871
+ ] })
872
+ },
873
+ admin.id
874
+ )) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: adminSearchQuery ? "No admins found" : "Start typing to search admins" }) })
875
+ }
876
+ )
877
+ ] }),
878
+ selectedAssignTo && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "mt-1 text-ui-fg-subtle", children: [
879
+ "Selected Admin ID: ",
880
+ selectedAssignTo
881
+ ] })
882
+ ] }),
883
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
884
+ /* @__PURE__ */ jsxRuntime.jsx(
885
+ ui.Button,
886
+ {
887
+ variant: "primary",
888
+ onClick: handleAssign,
889
+ disabled: isAssigning || !selectedAssignTo,
890
+ isLoading: isAssigning,
891
+ children: "Assign"
892
+ }
893
+ ),
894
+ request.assign_to && /* @__PURE__ */ jsxRuntime.jsx(
895
+ ui.Button,
896
+ {
897
+ variant: "secondary",
898
+ onClick: handleUnassign,
899
+ disabled: isAssigning,
900
+ isLoading: isAssigning,
901
+ children: "Unassign"
902
+ }
903
+ )
904
+ ] })
905
+ ] }),
906
+ assignError && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-error", children: assignError }),
907
+ assignSuccess && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-success", children: "Assignment updated successfully" })
908
+ ] })
909
+ ] }),
561
910
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-6 md:grid-cols-2", children: [
562
911
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-base p-6", children: [
563
912
  /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-4 text-lg", children: "Contact Information" }),
@@ -631,6 +980,103 @@ const ContactRequestDetailPage = () => {
631
980
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: key }),
632
981
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "font-medium", children: typeof value === "string" ? value : JSON.stringify(value) })
633
982
  ] }, key)) })
983
+ ] }),
984
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-base p-6", children: [
985
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-4 text-lg", children: "Comments" }),
986
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6 space-y-4 rounded-lg border border-ui-border-subtle bg-ui-bg-subtle p-4", children: [
987
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
988
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mb-2 block text-sm font-medium text-ui-fg-base", children: "Add Comment" }),
989
+ /* @__PURE__ */ jsxRuntime.jsx(
990
+ ui.Textarea,
991
+ {
992
+ value: commentText,
993
+ onChange: (event) => setCommentText(event.target.value),
994
+ placeholder: "Enter your comment...",
995
+ className: "min-h-[80px]"
996
+ }
997
+ )
998
+ ] }),
999
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1000
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
1001
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-sm font-medium text-ui-fg-base", children: "Images (URLs)" }),
1002
+ /* @__PURE__ */ jsxRuntime.jsx(
1003
+ ui.Button,
1004
+ {
1005
+ variant: "transparent",
1006
+ size: "small",
1007
+ onClick: addImageInput,
1008
+ type: "button",
1009
+ children: "Add Image URL"
1010
+ }
1011
+ )
1012
+ ] }),
1013
+ 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: [
1014
+ /* @__PURE__ */ jsxRuntime.jsx(
1015
+ ui.Input,
1016
+ {
1017
+ type: "text",
1018
+ value: url,
1019
+ onChange: (event) => updateImageUrl(index, event.target.value),
1020
+ placeholder: "https://example.com/image.jpg",
1021
+ className: "flex-1"
1022
+ }
1023
+ ),
1024
+ /* @__PURE__ */ jsxRuntime.jsx(
1025
+ ui.Button,
1026
+ {
1027
+ variant: "transparent",
1028
+ size: "small",
1029
+ onClick: () => removeImageInput(index),
1030
+ type: "button",
1031
+ children: "Remove"
1032
+ }
1033
+ )
1034
+ ] }, index)) })
1035
+ ] }),
1036
+ /* @__PURE__ */ jsxRuntime.jsx(
1037
+ ui.Button,
1038
+ {
1039
+ variant: "primary",
1040
+ onClick: handleCreateComment,
1041
+ disabled: isCreatingComment || !commentText.trim() && commentImages.filter((img) => img.trim()).length === 0,
1042
+ isLoading: isCreatingComment,
1043
+ children: "Add Comment"
1044
+ }
1045
+ ),
1046
+ commentError && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-error", children: commentError }),
1047
+ commentSuccess && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-success", children: "Comment added successfully" })
1048
+ ] }),
1049
+ comments.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4", children: comments.map((comment) => /* @__PURE__ */ jsxRuntime.jsxs(
1050
+ "div",
1051
+ {
1052
+ className: "rounded-lg border border-ui-border-subtle bg-ui-bg-subtle p-4",
1053
+ children: [
1054
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2 flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1055
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", children: comment.admin_id }),
1056
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: new Date(comment.created_at).toLocaleString() })
1057
+ ] }) }),
1058
+ comment.comment && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-2 whitespace-pre-wrap", children: comment.comment }),
1059
+ comment.images && comment.images.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 space-y-2", children: [
1060
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Images:" }),
1061
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: comment.images.map((imageUrl, index) => /* @__PURE__ */ jsxRuntime.jsxs(
1062
+ "a",
1063
+ {
1064
+ href: imageUrl,
1065
+ target: "_blank",
1066
+ rel: "noopener noreferrer",
1067
+ className: "text-sm text-ui-fg-interactive hover:underline",
1068
+ children: [
1069
+ "Image ",
1070
+ index + 1
1071
+ ]
1072
+ },
1073
+ index
1074
+ )) })
1075
+ ] })
1076
+ ]
1077
+ },
1078
+ comment.id
1079
+ )) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "No comments yet" })
634
1080
  ] })
635
1081
  ] }) });
636
1082
  };