FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

206 lines
5.8 KiB

  1. "use client";
  2. import SearchBox, { Criterion } from "../SearchBox";
  3. import { useCallback, useMemo, useState, useEffect } from "react";
  4. import { useTranslation } from "react-i18next";
  5. import SearchResults, { Column } from "../SearchResults/index";
  6. import EditNote from "@mui/icons-material/EditNote";
  7. import DeleteIcon from "@mui/icons-material/Delete";
  8. import { useRouter } from "next/navigation";
  9. import { deleteDialog, successDialog } from "../Swal/CustomAlerts";
  10. import { PrinterResult } from "@/app/api/settings/printer";
  11. import { deletePrinter } from "@/app/api/settings/printer/actions";
  12. import PrinterSearchLoading from "./PrinterSearchLoading";
  13. interface Props {
  14. printers: PrinterResult[];
  15. }
  16. type SearchQuery = Partial<Omit<PrinterResult, "id">>;
  17. type SearchParamNames = keyof SearchQuery;
  18. const PrinterSearch: React.FC<Props> = ({ printers }) => {
  19. const { t } = useTranslation("common");
  20. const [filteredPrinters, setFilteredPrinters] = useState(printers);
  21. const [pagingController, setPagingController] = useState({
  22. pageNum: 1,
  23. pageSize: 10,
  24. });
  25. const router = useRouter();
  26. const [isSearching, setIsSearching] = useState(false);
  27. useEffect(() => {
  28. console.log("Printers prop changed:", printers);
  29. setFilteredPrinters(printers);
  30. }, [printers]);
  31. const searchCriteria: Criterion<SearchParamNames>[] = useMemo(
  32. () => [
  33. {
  34. label: t("Name"),
  35. paramName: "name",
  36. type: "text",
  37. },
  38. {
  39. label: "IP",
  40. paramName: "ip",
  41. type: "text",
  42. },
  43. {
  44. label: t("Type"),
  45. paramName: "type",
  46. type: "text",
  47. },
  48. ],
  49. [t],
  50. );
  51. const onPrinterClick = useCallback(
  52. (printer: PrinterResult) => {
  53. console.log(printer);
  54. router.push(`/settings/printer/edit?id=${printer.id}`);
  55. },
  56. [router],
  57. );
  58. const onDeleteClick = useCallback((printer: PrinterResult) => {
  59. deleteDialog(async () => {
  60. try {
  61. console.log("Deleting printer with id:", printer.id);
  62. const result = await deletePrinter(printer.id);
  63. console.log("Delete result:", result);
  64. setFilteredPrinters(prev => prev.filter(p => p.id !== printer.id));
  65. router.refresh();
  66. setTimeout(() => {
  67. successDialog(t("Delete Success") || "刪除成功", t);
  68. }, 100);
  69. } catch (error) {
  70. console.error("Failed to delete printer:", error);
  71. const errorMessage = error instanceof Error ? error.message : (t("Delete Failed") || "刪除失敗");
  72. alert(errorMessage);
  73. router.refresh();
  74. }
  75. }, t);
  76. }, [t, router]);
  77. const columns = useMemo<Column<PrinterResult>[]>(
  78. () => [
  79. {
  80. name: "action",
  81. label: t("Edit"),
  82. onClick: onPrinterClick,
  83. buttonIcon: <EditNote />,
  84. sx: { width: "10%", minWidth: "80px" },
  85. },
  86. {
  87. name: "name",
  88. label: t("Name"),
  89. align: "left",
  90. headerAlign: "left",
  91. sx: { width: "20%", minWidth: "120px" },
  92. },
  93. {
  94. name: "description",
  95. label: t("Description"),
  96. align: "left",
  97. headerAlign: "left",
  98. sx: { width: "20%", minWidth: "140px" },
  99. },
  100. {
  101. name: "ip",
  102. label: "IP",
  103. align: "left",
  104. headerAlign: "left",
  105. sx: { width: "15%", minWidth: "100px" },
  106. },
  107. {
  108. name: "port",
  109. label: "Port",
  110. align: "left",
  111. headerAlign: "left",
  112. sx: { width: "10%", minWidth: "80px" },
  113. },
  114. {
  115. name: "type",
  116. label: t("Type"),
  117. align: "left",
  118. headerAlign: "left",
  119. sx: { width: "15%", minWidth: "100px" },
  120. },
  121. {
  122. name: "dpi",
  123. label: "DPI",
  124. align: "left",
  125. headerAlign: "left",
  126. sx: { width: "10%", minWidth: "80px" },
  127. },
  128. {
  129. name: "action",
  130. label: t("Delete"),
  131. onClick: onDeleteClick,
  132. buttonIcon: <DeleteIcon />,
  133. color: "error",
  134. sx: { width: "10%", minWidth: "80px" },
  135. },
  136. ],
  137. [t, onPrinterClick, onDeleteClick],
  138. );
  139. console.log("PrinterSearch render - filteredPrinters:", filteredPrinters);
  140. console.log("PrinterSearch render - printers prop:", printers);
  141. return (
  142. <>
  143. <SearchBox
  144. criteria={searchCriteria}
  145. onReset={() => {
  146. setFilteredPrinters(printers);
  147. setPagingController({ pageNum: 1, pageSize: pagingController.pageSize });
  148. }}
  149. onSearch={async (query) => {
  150. setIsSearching(true);
  151. try {
  152. let results: PrinterResult[] = printers;
  153. if (query.name && query.name.trim()) {
  154. results = results.filter((printer) =>
  155. printer.name?.toLowerCase().includes(query.name?.toLowerCase() || "")
  156. );
  157. }
  158. if (query.ip && query.ip.trim()) {
  159. results = results.filter((printer) =>
  160. printer.ip?.toLowerCase().includes(query.ip?.toLowerCase() || "")
  161. );
  162. }
  163. if (query.type && query.type.trim()) {
  164. results = results.filter((printer) =>
  165. printer.type?.toLowerCase().includes(query.type?.toLowerCase() || "")
  166. );
  167. }
  168. setFilteredPrinters(results);
  169. setPagingController({ pageNum: 1, pageSize: pagingController.pageSize });
  170. } catch (error) {
  171. console.error("Error searching printers:", error);
  172. setFilteredPrinters(printers);
  173. } finally {
  174. setIsSearching(false);
  175. }
  176. }}
  177. />
  178. <SearchResults<PrinterResult>
  179. items={filteredPrinters}
  180. columns={columns}
  181. pagingController={pagingController}
  182. setPagingController={setPagingController}
  183. />
  184. </>
  185. );
  186. };
  187. export default PrinterSearch;