FPSMS-frontend
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.
 
 

162 рядки
4.8 KiB

  1. "use client";
  2. import SearchBox, { Criterion } from "../SearchBox";
  3. import { useCallback, useMemo, useState } from "react";
  4. import { useTranslation } from "react-i18next";
  5. import { useUiText } from "@/i18n/useUiText";
  6. import SearchResults, { Column } from "../SearchResults/index";
  7. import EditNote from "@mui/icons-material/EditNote";
  8. import DeleteIcon from "@mui/icons-material/Delete";
  9. import { useRouter } from "next/navigation";
  10. import { deleteDialog, successDialog } from "../Swal/CustomAlerts";
  11. import useUploadContext from "../UploadProvider/useUploadContext";
  12. import { downloadFile } from "@/app/utils/commonUtil";
  13. import { UserResult } from "@/app/api/user";
  14. import { deleteUser } from "@/app/api/user/actions";
  15. import QrCodeIcon from "@mui/icons-material/QrCode";
  16. import UserSearchLoading from "./UserSearchLoading";
  17. import { searchUsersByUsernameOrName } from "@/app/api/user/client";
  18. interface Props {
  19. users: UserResult[];
  20. }
  21. type SearchQuery = Partial<Omit<UserResult, "id">>;
  22. type SearchParamNames = keyof SearchQuery;
  23. const UserSearch: React.FC<Props> = ({ users }) => {
  24. const { t } = useTranslation("user");
  25. const L = useUiText();
  26. const [filteredUser, setFilteredUser] = useState(users);
  27. const [pagingController, setPagingController] = useState({
  28. pageNum: 1,
  29. pageSize: 10,
  30. });
  31. const router = useRouter();
  32. const { setIsUploading } = useUploadContext();
  33. const [isSearching, setIsSearching] = useState(false);
  34. const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
  35. () => [
  36. {
  37. label: L("用戶/姓名", "User / name"),
  38. paramName: "username",
  39. type: "text",
  40. },
  41. {
  42. label: t("staffNo"),
  43. paramName: "staffNo",
  44. type: "text",
  45. },
  46. ],
  47. [t, L],
  48. );
  49. const onUserClick = useCallback(
  50. (users: UserResult) => {
  51. console.log(users);
  52. router.push(`/settings/user/edit?id=${users.id}`);
  53. },
  54. [router, t],
  55. );
  56. const onDeleteClick = useCallback((users: UserResult) => {
  57. deleteDialog(async () => {
  58. await deleteUser(users.id);
  59. setFilteredUser(prev => prev.filter(user => user.id !== users.id));
  60. router.refresh();
  61. successDialog(t("Delete Success"), t);
  62. }, t);
  63. }, [t, router]);
  64. const columns = useMemo<Column<UserResult>[]>(
  65. () => [
  66. {
  67. name: "action",
  68. label: t("Edit"),
  69. onClick: onUserClick,
  70. buttonIcon: <EditNote />,
  71. sx: { width: "10%", minWidth: "80px" },
  72. },
  73. {
  74. name: "username",
  75. label: t("Username"),
  76. align: "left",
  77. headerAlign: "left",
  78. sx: { width: "22.5%", minWidth: "120px" },
  79. },
  80. {
  81. name: "name",
  82. label: t("name"),
  83. align: "left",
  84. headerAlign: "left",
  85. sx: { width: "22.5%", minWidth: "120px" },
  86. },
  87. {
  88. name: "staffNo",
  89. label: t("staffNo"),
  90. align: "left",
  91. headerAlign: "left",
  92. sx: { width: "22.5%", minWidth: "120px" },
  93. },
  94. {
  95. name: "action",
  96. label: t("Delete"),
  97. onClick: onDeleteClick,
  98. buttonIcon: <DeleteIcon />,
  99. color: "error",
  100. sx: { width: "10%", minWidth: "80px" },
  101. },
  102. ],
  103. [t, onUserClick, onDeleteClick],
  104. );
  105. return (
  106. <>
  107. <SearchBox
  108. criteria={searchCriteria}
  109. onSearch={async (query) => {
  110. setIsSearching(true);
  111. try {
  112. let results: UserResult[] = [];
  113. if (query.username && query.username.trim()) {
  114. results = await searchUsersByUsernameOrName(query.username);
  115. } else {
  116. results = users;
  117. }
  118. if (query.staffNo && query.staffNo.trim()) {
  119. results = results.filter((user) =>
  120. user.staffNo?.toString().includes(query.staffNo?.toString() || "")
  121. );
  122. }
  123. setFilteredUser(results);
  124. setPagingController({ pageNum: 1, pageSize: pagingController.pageSize });
  125. } catch (error) {
  126. console.error("Error searching users:", error);
  127. setFilteredUser(
  128. users.filter((user) => {
  129. const userMatch = !query.username ||
  130. user.username?.toLowerCase().includes(query.username?.toLowerCase() || "") ||
  131. user.name?.toLowerCase().includes(query.username?.toLowerCase() || "");
  132. const staffNoMatch = !query.staffNo ||
  133. user.staffNo?.toString().includes(query.staffNo?.toString() || "");
  134. return userMatch && staffNoMatch;
  135. })
  136. );
  137. } finally {
  138. setIsSearching(false);
  139. }
  140. }}
  141. />
  142. <SearchResults<UserResult>
  143. items={filteredUser}
  144. columns={columns}
  145. pagingController={pagingController}
  146. setPagingController={setPagingController}
  147. />
  148. </>
  149. );
  150. };
  151. export default UserSearch;