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.
 
 
 

202 lines
9.4 KiB

  1. "use client";
  2. import React, {useEffect, useState} from "react";
  3. import Paper from "@mui/material/Paper";
  4. import Table from "@mui/material/Table";
  5. import TableBody from "@mui/material/TableBody";
  6. import TableCell from "@mui/material/TableCell";
  7. import TableContainer from "@mui/material/TableContainer";
  8. import TableHead from "@mui/material/TableHead";
  9. import TablePagination from "@mui/material/TablePagination";
  10. import TableRow from "@mui/material/TableRow";
  11. import IconButton from "@mui/material/IconButton";
  12. import EditIcon from "@mui/icons-material/Edit";
  13. import SaveIcon from "@mui/icons-material/Save";
  14. import CancelIcon from "@mui/icons-material/Close";
  15. import DeleteIcon from "@mui/icons-material/Delete";
  16. import TextField from "@mui/material/TextField";
  17. import MultiSelect from "@/components/SearchBox/MultiSelect";
  18. export interface ResultWithId {
  19. id: string | number;
  20. }
  21. interface BaseColumn<T extends ResultWithId> {
  22. name: keyof T;
  23. label: string;
  24. type: string;
  25. options: T[];
  26. renderCell? : (T) => void;
  27. }
  28. interface ColumnWithAction<T extends ResultWithId> extends BaseColumn<T> {
  29. onClick: (item: T) => void;
  30. buttonIcon: React.ReactNode;
  31. buttonColor?: "inherit" | "default" | "primary" | "secondary";
  32. }
  33. export type Column<T extends ResultWithId> =
  34. | BaseColumn<T>
  35. | ColumnWithAction<T>;
  36. interface Props<T extends ResultWithId> {
  37. items: T[],
  38. columns: Column<T>[],
  39. noWrapper?: boolean,
  40. setPagingController: (value: { pageNum: number; pageSize: number; totalCount: number }) => void,
  41. pagingController: { pageNum: number; pageSize: number; totalCount: number },
  42. isAutoPaging: boolean
  43. }
  44. function EditableSearchResults<T extends ResultWithId>({
  45. items,
  46. columns,
  47. noWrapper,
  48. pagingController,
  49. setPagingController,
  50. isAutoPaging = true,
  51. }: Props<T>) {
  52. const [page, setPage] = useState(0);
  53. const [rowsPerPage, setRowsPerPage] = useState(10);
  54. const [editingRowId, setEditingRowId] = useState<number | null>(null);
  55. const [editedItems, setEditedItems] = useState<T[]>(items);
  56. useEffect(()=>{
  57. setEditedItems(items)
  58. },[items])
  59. const handleChangePage = (_event: unknown, newPage: number) => {
  60. setPage(newPage);
  61. setPagingController({ ...pagingController, pageNum: newPage + 1 });
  62. };
  63. const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => {
  64. setRowsPerPage(+event.target.value);
  65. setPage(0);
  66. setPagingController({ ...pagingController, pageSize: +event.target.value, pageNum: 1 });
  67. };
  68. const handleEditClick = (id: number) => {
  69. setEditingRowId(id);
  70. };
  71. const handleSaveClick = (item: T) => {
  72. setEditingRowId(null);
  73. // Call API or any save logic here
  74. setEditedItems((prev) =>
  75. prev.map((row) => (row.id === item.id ? { ...row } : row))
  76. );
  77. };
  78. const handleInputChange = (id: number, field: keyof T, value: string | number[]) => {
  79. setEditedItems((prev) =>
  80. prev.map((item) =>
  81. item.id === id ? { ...item, [field]: value } : item
  82. )
  83. );
  84. };
  85. const handleDeleteClick = (id: number) => {
  86. // Implement delete logic here
  87. setEditedItems((prev) => prev.filter(item => item.id !== id));
  88. };
  89. const table = (
  90. <>
  91. <TableContainer sx={{ maxHeight: 440 }}>
  92. <Table stickyHeader>
  93. <TableHead>
  94. <TableRow>
  95. <TableCell>Actions</TableCell> {/* Action Column Header */}
  96. {columns.map((column, idx) => (
  97. <TableCell key={`${column.name.toString()}${idx}`}>
  98. {column.label}
  99. </TableCell>
  100. ))}
  101. </TableRow>
  102. </TableHead>
  103. <TableBody>
  104. {(isAutoPaging ? editedItems.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) : editedItems).map((item) => (
  105. <TableRow hover tabIndex={-1} key={item.id}>
  106. <TableCell>
  107. {editingRowId === item.id ? (
  108. <>
  109. <IconButton onClick={() => handleSaveClick(item)}>
  110. <SaveIcon />
  111. </IconButton>
  112. <IconButton onClick={() => setEditingRowId(null)}>
  113. <CancelIcon />
  114. </IconButton>
  115. </>
  116. ) : (
  117. <>
  118. <IconButton onClick={() => handleEditClick(item.id as number)}>
  119. <EditIcon />
  120. </IconButton>
  121. <IconButton onClick={() => handleDeleteClick(item.id as number)}>
  122. <DeleteIcon />
  123. </IconButton>
  124. </>
  125. )}
  126. </TableCell>
  127. {columns.map((column, idx) => {
  128. const columnName = column.name;
  129. return (
  130. <TableCell key={`${columnName.toString()}-${idx}`}>
  131. {editingRowId === item.id ? (
  132. (() => {
  133. switch (column.type) {
  134. case 'input':
  135. return (
  136. <TextField
  137. hiddenLabel={true}
  138. fullWidth
  139. defaultValue={item[columnName] as string}
  140. onChange={(e) => handleInputChange(item.id as number, columnName, e.target.value)}
  141. />
  142. );
  143. case 'multi-select':
  144. return (
  145. <MultiSelect
  146. //label={column.label}
  147. options={column.options}
  148. selectedValues={[]}
  149. onChange={(selectedValues) => handleInputChange(item.id as number, columnName, selectedValues)}
  150. />
  151. );
  152. default:
  153. return null; // Handle any default case if needed
  154. }
  155. })()
  156. ) : (
  157. column.renderCell ?
  158. column.renderCell(item)
  159. :
  160. <span onDoubleClick={() => handleEditClick(item.id as number)}>
  161. {item[columnName] as string}
  162. </span>
  163. )}
  164. </TableCell>
  165. );
  166. })}
  167. </TableRow>
  168. ))}
  169. </TableBody>
  170. </Table>
  171. </TableContainer>
  172. <TablePagination
  173. rowsPerPageOptions={[10, 25, 100]}
  174. component="div"
  175. count={pagingController.totalCount === 0 ? editedItems.length : pagingController.totalCount}
  176. rowsPerPage={rowsPerPage}
  177. page={page}
  178. onPageChange={handleChangePage}
  179. onRowsPerPageChange={handleChangeRowsPerPage}
  180. />
  181. </>
  182. );
  183. return noWrapper ? table : <Paper sx={{ overflow: "hidden" }}>{table}</Paper>;
  184. }
  185. export default EditableSearchResults;