medusa-contact-us 0.0.22 → 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.
|
@@ -427,11 +427,54 @@ const ContactRequestDetailPage = () => {
|
|
|
427
427
|
const [isAssigning, setIsAssigning] = react.useState(false);
|
|
428
428
|
const [assignError, setAssignError] = react.useState(null);
|
|
429
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);
|
|
430
437
|
const [commentText, setCommentText] = react.useState("");
|
|
431
438
|
const [commentImages, setCommentImages] = react.useState([]);
|
|
432
439
|
const [isCreatingComment, setIsCreatingComment] = react.useState(false);
|
|
433
440
|
const [commentError, setCommentError] = react.useState(null);
|
|
434
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
|
+
});
|
|
435
478
|
react.useEffect(() => {
|
|
436
479
|
if (!id) {
|
|
437
480
|
navigate("/contact-requests");
|
|
@@ -454,6 +497,7 @@ const ContactRequestDetailPage = () => {
|
|
|
454
497
|
setNextAllowedStatuses(payload.next_allowed_statuses ?? []);
|
|
455
498
|
setSelectedStatus("");
|
|
456
499
|
setSelectedAssignTo("");
|
|
500
|
+
setAdminSearchQuery("");
|
|
457
501
|
} catch (loadError) {
|
|
458
502
|
const message = loadError instanceof Error ? loadError.message : "Unable to load contact request";
|
|
459
503
|
setError(message);
|
|
@@ -463,6 +507,59 @@ const ContactRequestDetailPage = () => {
|
|
|
463
507
|
};
|
|
464
508
|
void loadRequest();
|
|
465
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]);
|
|
466
563
|
const handleStatusUpdate = async () => {
|
|
467
564
|
if (!id || !selectedStatus) {
|
|
468
565
|
return;
|
|
@@ -721,18 +818,67 @@ const ContactRequestDetailPage = () => {
|
|
|
721
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" }) })
|
|
722
819
|
] }),
|
|
723
820
|
/* @__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: "
|
|
726
|
-
/* @__PURE__ */ jsxRuntime.
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
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
|
+
] })
|
|
736
882
|
] }),
|
|
737
883
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
738
884
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -740,7 +886,7 @@ const ContactRequestDetailPage = () => {
|
|
|
740
886
|
{
|
|
741
887
|
variant: "primary",
|
|
742
888
|
onClick: handleAssign,
|
|
743
|
-
disabled: isAssigning,
|
|
889
|
+
disabled: isAssigning || !selectedAssignTo,
|
|
744
890
|
isLoading: isAssigning,
|
|
745
891
|
children: "Assign"
|
|
746
892
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
2
|
+
import { useState, useCallback, useEffect, useMemo, useRef } from "react";
|
|
3
3
|
import { defineRouteConfig } from "@medusajs/admin-sdk";
|
|
4
4
|
import { Container, Heading, Text, Button, Input, Badge, Textarea } from "@medusajs/ui";
|
|
5
5
|
import { Envelope, ChatBubbleLeftRight, ArrowLeft } from "@medusajs/icons";
|
|
@@ -426,11 +426,54 @@ const ContactRequestDetailPage = () => {
|
|
|
426
426
|
const [isAssigning, setIsAssigning] = useState(false);
|
|
427
427
|
const [assignError, setAssignError] = useState(null);
|
|
428
428
|
const [assignSuccess, setAssignSuccess] = useState(false);
|
|
429
|
+
const [adminUsers, setAdminUsers] = useState([]);
|
|
430
|
+
const [adminSearchQuery, setAdminSearchQuery] = useState("");
|
|
431
|
+
const [isAdminDropdownOpen, setIsAdminDropdownOpen] = useState(false);
|
|
432
|
+
const [isLoadingAdmins, setIsLoadingAdmins] = useState(false);
|
|
433
|
+
const [selectedAdminIndex, setSelectedAdminIndex] = useState(-1);
|
|
434
|
+
const adminDropdownRef = useRef(null);
|
|
435
|
+
const adminInputRef = useRef(null);
|
|
429
436
|
const [commentText, setCommentText] = useState("");
|
|
430
437
|
const [commentImages, setCommentImages] = useState([]);
|
|
431
438
|
const [isCreatingComment, setIsCreatingComment] = useState(false);
|
|
432
439
|
const [commentError, setCommentError] = useState(null);
|
|
433
440
|
const [commentSuccess, setCommentSuccess] = useState(false);
|
|
441
|
+
const useDebounce2 = (value, delay) => {
|
|
442
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
443
|
+
useEffect(() => {
|
|
444
|
+
const handler = setTimeout(() => setDebouncedValue(value), delay);
|
|
445
|
+
return () => clearTimeout(handler);
|
|
446
|
+
}, [value, delay]);
|
|
447
|
+
return debouncedValue;
|
|
448
|
+
};
|
|
449
|
+
const debouncedAdminSearch = useDebounce2(adminSearchQuery, 300);
|
|
450
|
+
const fetchAdminUsers = useCallback(async (searchQuery) => {
|
|
451
|
+
try {
|
|
452
|
+
setIsLoadingAdmins(true);
|
|
453
|
+
const params = new URLSearchParams();
|
|
454
|
+
if (searchQuery) {
|
|
455
|
+
params.set("q", searchQuery);
|
|
456
|
+
}
|
|
457
|
+
const response = await fetch(`/admin/users?${params.toString()}`, {
|
|
458
|
+
credentials: "include"
|
|
459
|
+
});
|
|
460
|
+
if (!response.ok) {
|
|
461
|
+
throw new Error("Unable to load admin users");
|
|
462
|
+
}
|
|
463
|
+
const payload = await response.json();
|
|
464
|
+
setAdminUsers(payload.users || []);
|
|
465
|
+
} catch (fetchError) {
|
|
466
|
+
console.error("Error fetching admin users:", fetchError);
|
|
467
|
+
setAdminUsers([]);
|
|
468
|
+
} finally {
|
|
469
|
+
setIsLoadingAdmins(false);
|
|
470
|
+
}
|
|
471
|
+
}, []);
|
|
472
|
+
const filteredAdmins = adminUsers.filter((admin) => {
|
|
473
|
+
if (!debouncedAdminSearch) return true;
|
|
474
|
+
const query = debouncedAdminSearch.toLowerCase();
|
|
475
|
+
return admin.name.toLowerCase().includes(query) || admin.email.toLowerCase().includes(query) || admin.id.toLowerCase().includes(query);
|
|
476
|
+
});
|
|
434
477
|
useEffect(() => {
|
|
435
478
|
if (!id) {
|
|
436
479
|
navigate("/contact-requests");
|
|
@@ -453,6 +496,7 @@ const ContactRequestDetailPage = () => {
|
|
|
453
496
|
setNextAllowedStatuses(payload.next_allowed_statuses ?? []);
|
|
454
497
|
setSelectedStatus("");
|
|
455
498
|
setSelectedAssignTo("");
|
|
499
|
+
setAdminSearchQuery("");
|
|
456
500
|
} catch (loadError) {
|
|
457
501
|
const message = loadError instanceof Error ? loadError.message : "Unable to load contact request";
|
|
458
502
|
setError(message);
|
|
@@ -462,6 +506,59 @@ const ContactRequestDetailPage = () => {
|
|
|
462
506
|
};
|
|
463
507
|
void loadRequest();
|
|
464
508
|
}, [id, navigate]);
|
|
509
|
+
useEffect(() => {
|
|
510
|
+
if (isAdminDropdownOpen) {
|
|
511
|
+
void fetchAdminUsers(debouncedAdminSearch || void 0);
|
|
512
|
+
}
|
|
513
|
+
}, [isAdminDropdownOpen, debouncedAdminSearch, fetchAdminUsers]);
|
|
514
|
+
useEffect(() => {
|
|
515
|
+
const handleClickOutside = (event) => {
|
|
516
|
+
if (adminDropdownRef.current && !adminDropdownRef.current.contains(event.target) && adminInputRef.current && !adminInputRef.current.contains(event.target)) {
|
|
517
|
+
setIsAdminDropdownOpen(false);
|
|
518
|
+
setSelectedAdminIndex(-1);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
if (isAdminDropdownOpen) {
|
|
522
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
523
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
524
|
+
}
|
|
525
|
+
}, [isAdminDropdownOpen]);
|
|
526
|
+
const handleAdminKeyDown = (event) => {
|
|
527
|
+
if (event.key === "ArrowDown") {
|
|
528
|
+
event.preventDefault();
|
|
529
|
+
setSelectedAdminIndex(
|
|
530
|
+
(prev) => prev < filteredAdmins.length - 1 ? prev + 1 : prev
|
|
531
|
+
);
|
|
532
|
+
setIsAdminDropdownOpen(true);
|
|
533
|
+
} else if (event.key === "ArrowUp") {
|
|
534
|
+
event.preventDefault();
|
|
535
|
+
setSelectedAdminIndex((prev) => prev > 0 ? prev - 1 : -1);
|
|
536
|
+
setIsAdminDropdownOpen(true);
|
|
537
|
+
} else if (event.key === "Enter" && selectedAdminIndex >= 0) {
|
|
538
|
+
event.preventDefault();
|
|
539
|
+
const admin = filteredAdmins[selectedAdminIndex];
|
|
540
|
+
if (admin) {
|
|
541
|
+
handleAdminSelect(admin);
|
|
542
|
+
}
|
|
543
|
+
} else if (event.key === "Escape") {
|
|
544
|
+
setIsAdminDropdownOpen(false);
|
|
545
|
+
setSelectedAdminIndex(-1);
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
const handleAdminSelect = (admin) => {
|
|
549
|
+
setSelectedAssignTo(admin.id);
|
|
550
|
+
setAdminSearchQuery(admin.name || admin.email);
|
|
551
|
+
setIsAdminDropdownOpen(false);
|
|
552
|
+
setSelectedAdminIndex(-1);
|
|
553
|
+
};
|
|
554
|
+
useEffect(() => {
|
|
555
|
+
if ((request == null ? void 0 : request.assign_to) && adminUsers.length > 0) {
|
|
556
|
+
const assignedAdmin = adminUsers.find((admin) => admin.id === request.assign_to);
|
|
557
|
+
if (assignedAdmin && !adminSearchQuery) {
|
|
558
|
+
setAdminSearchQuery(assignedAdmin.name || assignedAdmin.email);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}, [request == null ? void 0 : request.assign_to, adminUsers, adminSearchQuery]);
|
|
465
562
|
const handleStatusUpdate = async () => {
|
|
466
563
|
if (!id || !selectedStatus) {
|
|
467
564
|
return;
|
|
@@ -720,18 +817,67 @@ const ContactRequestDetailPage = () => {
|
|
|
720
817
|
/* @__PURE__ */ jsx(Text, { className: "font-medium", children: request.assign_to ? /* @__PURE__ */ jsx(Badge, { size: "small", className: "mt-1", children: request.assign_to }) : /* @__PURE__ */ jsx("span", { className: "text-ui-fg-subtle", children: "Unassigned" }) })
|
|
721
818
|
] }),
|
|
722
819
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-end", children: [
|
|
723
|
-
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
724
|
-
/* @__PURE__ */ jsx("label", { className: "mb-2 block text-sm font-medium text-ui-fg-base", children: "
|
|
725
|
-
/* @__PURE__ */
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
820
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 relative", children: [
|
|
821
|
+
/* @__PURE__ */ jsx("label", { className: "mb-2 block text-sm font-medium text-ui-fg-base", children: "Assign To Admin" }),
|
|
822
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
823
|
+
/* @__PURE__ */ jsx(
|
|
824
|
+
Input,
|
|
825
|
+
{
|
|
826
|
+
ref: adminInputRef,
|
|
827
|
+
type: "text",
|
|
828
|
+
value: adminSearchQuery,
|
|
829
|
+
onChange: (event) => {
|
|
830
|
+
setAdminSearchQuery(event.target.value);
|
|
831
|
+
setIsAdminDropdownOpen(true);
|
|
832
|
+
setSelectedAdminIndex(-1);
|
|
833
|
+
},
|
|
834
|
+
onFocus: () => {
|
|
835
|
+
setIsAdminDropdownOpen(true);
|
|
836
|
+
if (adminUsers.length === 0) {
|
|
837
|
+
void fetchAdminUsers();
|
|
838
|
+
}
|
|
839
|
+
},
|
|
840
|
+
onKeyDown: handleAdminKeyDown,
|
|
841
|
+
placeholder: "Search admin by name, email, or ID...",
|
|
842
|
+
className: "w-full"
|
|
843
|
+
}
|
|
844
|
+
),
|
|
845
|
+
isAdminDropdownOpen && /* @__PURE__ */ jsx(
|
|
846
|
+
"div",
|
|
847
|
+
{
|
|
848
|
+
ref: adminDropdownRef,
|
|
849
|
+
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",
|
|
850
|
+
children: isLoadingAdmins ? /* @__PURE__ */ jsx("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Loading admins..." }) }) : filteredAdmins.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "py-1", children: filteredAdmins.map((admin, index) => /* @__PURE__ */ jsx(
|
|
851
|
+
"li",
|
|
852
|
+
{
|
|
853
|
+
className: `cursor-pointer px-4 py-3 hover:bg-ui-bg-subtle-hover ${index === selectedAdminIndex ? "bg-ui-bg-subtle-hover" : ""}`,
|
|
854
|
+
onClick: () => handleAdminSelect(admin),
|
|
855
|
+
onMouseEnter: () => setSelectedAdminIndex(index),
|
|
856
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
857
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
858
|
+
/* @__PURE__ */ jsx(Text, { className: "font-medium", children: admin.name }),
|
|
859
|
+
admin.id === selectedAssignTo && /* @__PURE__ */ jsx(Badge, { size: "2xsmall", children: "Selected" })
|
|
860
|
+
] }),
|
|
861
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: admin.email }),
|
|
862
|
+
/* @__PURE__ */ jsx(
|
|
863
|
+
Text,
|
|
864
|
+
{
|
|
865
|
+
size: "xsmall",
|
|
866
|
+
className: "text-ui-fg-muted font-mono",
|
|
867
|
+
children: admin.id
|
|
868
|
+
}
|
|
869
|
+
)
|
|
870
|
+
] })
|
|
871
|
+
},
|
|
872
|
+
admin.id
|
|
873
|
+
)) }) : /* @__PURE__ */ jsx("div", { className: "p-4 text-center", children: /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: adminSearchQuery ? "No admins found" : "Start typing to search admins" }) })
|
|
874
|
+
}
|
|
875
|
+
)
|
|
876
|
+
] }),
|
|
877
|
+
selectedAssignTo && /* @__PURE__ */ jsxs(Text, { size: "small", className: "mt-1 text-ui-fg-subtle", children: [
|
|
878
|
+
"Selected Admin ID: ",
|
|
879
|
+
selectedAssignTo
|
|
880
|
+
] })
|
|
735
881
|
] }),
|
|
736
882
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
737
883
|
/* @__PURE__ */ jsx(
|
|
@@ -739,7 +885,7 @@ const ContactRequestDetailPage = () => {
|
|
|
739
885
|
{
|
|
740
886
|
variant: "primary",
|
|
741
887
|
onClick: handleAssign,
|
|
742
|
-
disabled: isAssigning,
|
|
888
|
+
disabled: isAssigning || !selectedAssignTo,
|
|
743
889
|
isLoading: isAssigning,
|
|
744
890
|
children: "Assign"
|
|
745
891
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = void 0;
|
|
4
|
+
const GET = async (req, res) => {
|
|
5
|
+
try {
|
|
6
|
+
const { q } = req.query;
|
|
7
|
+
const searchQuery = q ? String(q).toLowerCase() : "";
|
|
8
|
+
let users = [];
|
|
9
|
+
try {
|
|
10
|
+
// Resolve the EntityManager to query the database
|
|
11
|
+
const manager = req.scope.resolve("manager");
|
|
12
|
+
if (manager) {
|
|
13
|
+
const knex = manager.getKnex();
|
|
14
|
+
if (knex) {
|
|
15
|
+
// Query auth_identity table for admin users
|
|
16
|
+
// In Medusa v2, admin users are stored in auth_identity with entity_type = 'user'
|
|
17
|
+
const result = await knex.raw(`
|
|
18
|
+
SELECT DISTINCT
|
|
19
|
+
ai.id,
|
|
20
|
+
ai.provider_metadata->>'email' as email,
|
|
21
|
+
ai.provider_metadata->>'first_name' as first_name,
|
|
22
|
+
ai.provider_metadata->>'last_name' as last_name
|
|
23
|
+
FROM auth_identity ai
|
|
24
|
+
WHERE ai.entity_type = 'user'
|
|
25
|
+
ORDER BY ai.created_at DESC
|
|
26
|
+
`);
|
|
27
|
+
// Handle different result formats from knex.raw()
|
|
28
|
+
const rows = result.rows || result[0] || [];
|
|
29
|
+
users = rows.map((row) => {
|
|
30
|
+
if (typeof row === "object" && row !== null) {
|
|
31
|
+
const r = row;
|
|
32
|
+
const firstName = r.first_name || "";
|
|
33
|
+
const lastName = r.last_name || "";
|
|
34
|
+
const name = [firstName, lastName].filter(Boolean).join(" ") || r.email || "";
|
|
35
|
+
return {
|
|
36
|
+
id: r.id || "",
|
|
37
|
+
email: r.email || "",
|
|
38
|
+
first_name: r.first_name || null,
|
|
39
|
+
last_name: r.last_name || null,
|
|
40
|
+
name,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}).filter(Boolean);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (serviceError) {
|
|
49
|
+
// If service resolution fails, return empty array
|
|
50
|
+
console.warn("Could not fetch admin users from database:", serviceError);
|
|
51
|
+
}
|
|
52
|
+
// Filter by search query if provided
|
|
53
|
+
if (searchQuery && users.length > 0) {
|
|
54
|
+
users = users.filter((user) => {
|
|
55
|
+
const nameMatch = user.name.toLowerCase().includes(searchQuery);
|
|
56
|
+
const emailMatch = user.email.toLowerCase().includes(searchQuery);
|
|
57
|
+
const idMatch = user.id.toLowerCase().includes(searchQuery);
|
|
58
|
+
return nameMatch || emailMatch || idMatch;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
res.status(200).json({ users });
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
// On error, return empty array so UI doesn't break
|
|
65
|
+
console.error("Error fetching admin users:", error);
|
|
66
|
+
res.status(200).json({ users: [] });
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
exports.GET = GET;
|
|
70
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3VzZXJzL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQVdPLE1BQU0sR0FBRyxHQUFHLEtBQUssRUFBRSxHQUFrQixFQUFFLEdBQW1CLEVBQUUsRUFBRTtJQUNuRSxJQUFJLENBQUM7UUFDSCxNQUFNLEVBQUUsQ0FBQyxFQUFFLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQTtRQUN2QixNQUFNLFdBQVcsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFBO1FBRXBELElBQUksS0FBSyxHQUFnQixFQUFFLENBQUE7UUFFM0IsSUFBSSxDQUFDO1lBQ0gsa0RBQWtEO1lBQ2xELE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFnQixTQUFTLENBQUMsQ0FBQTtZQUUzRCxJQUFJLE9BQU8sRUFBRSxDQUFDO2dCQUNaLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQTtnQkFFOUIsSUFBSSxJQUFJLEVBQUUsQ0FBQztvQkFDVCw0Q0FBNEM7b0JBQzVDLGtGQUFrRjtvQkFDbEYsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsR0FBRyxDQUFDOzs7Ozs7Ozs7V0FTN0IsQ0FBQyxDQUFBO29CQUVGLGtEQUFrRDtvQkFDbEQsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksSUFBSSxNQUFNLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFBO29CQUUzQyxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQVksRUFBRSxFQUFFO3dCQUNoQyxJQUFJLE9BQU8sR0FBRyxLQUFLLFFBQVEsSUFBSSxHQUFHLEtBQUssSUFBSSxFQUFFLENBQUM7NEJBQzVDLE1BQU0sQ0FBQyxHQUFHLEdBS1QsQ0FBQTs0QkFDRCxNQUFNLFNBQVMsR0FBRyxDQUFDLENBQUMsVUFBVSxJQUFJLEVBQUUsQ0FBQTs0QkFDcEMsTUFBTSxRQUFRLEdBQUcsQ0FBQyxDQUFDLFNBQVMsSUFBSSxFQUFFLENBQUE7NEJBQ2xDLE1BQU0sSUFBSSxHQUFHLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUE7NEJBRTdFLE9BQU87Z0NBQ0wsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRTtnQ0FDZCxLQUFLLEVBQUUsQ0FBQyxDQUFDLEtBQUssSUFBSSxFQUFFO2dDQUNwQixVQUFVLEVBQUUsQ0FBQyxDQUFDLFVBQVUsSUFBSSxJQUFJO2dDQUNoQyxTQUFTLEVBQUUsQ0FBQyxDQUFDLFNBQVMsSUFBSSxJQUFJO2dDQUM5QixJQUFJOzZCQUNMLENBQUE7d0JBQ0gsQ0FBQzt3QkFDRCxPQUFPLElBQUksQ0FBQTtvQkFDYixDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFnQixDQUFBO2dCQUNuQyxDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLFlBQVksRUFBRSxDQUFDO1lBQ3RCLGtEQUFrRDtZQUNsRCxPQUFPLENBQUMsSUFBSSxDQUFDLDRDQUE0QyxFQUFFLFlBQVksQ0FBQyxDQUFBO1FBQzFFLENBQUM7UUFFRCxxQ0FBcUM7UUFDckMsSUFBSSxXQUFXLElBQUksS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUNwQyxLQUFLLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFO2dCQUM1QixNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxXQUFXLENBQUMsQ0FBQTtnQkFDL0QsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQUUsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLENBQUE7Z0JBQ2pFLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDLFdBQVcsQ0FBQyxDQUFBO2dCQUMzRCxPQUFPLFNBQVMsSUFBSSxVQUFVLElBQUksT0FBTyxDQUFBO1lBQzNDLENBQUMsQ0FBQyxDQUFBO1FBQ0osQ0FBQztRQUVELEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQTtJQUNqQyxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLG1EQUFtRDtRQUNuRCxPQUFPLENBQUMsS0FBSyxDQUFDLDZCQUE2QixFQUFFLEtBQUssQ0FBQyxDQUFBO1FBQ25ELEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUE7SUFDckMsQ0FBQztBQUNILENBQUMsQ0FBQTtBQTVFWSxRQUFBLEdBQUcsT0E0RWYifQ==
|
package/package.json
CHANGED