|
@@ -0,0 +1,197 @@ |
|
|
|
|
|
"use client"; |
|
|
|
|
|
|
|
|
|
|
|
import React, {useEffect, useState} from "react"; |
|
|
|
|
|
import Paper from "@mui/material/Paper"; |
|
|
|
|
|
import Table from "@mui/material/Table"; |
|
|
|
|
|
import TableBody from "@mui/material/TableBody"; |
|
|
|
|
|
import TableCell from "@mui/material/TableCell"; |
|
|
|
|
|
import TableContainer from "@mui/material/TableContainer"; |
|
|
|
|
|
import TableHead from "@mui/material/TableHead"; |
|
|
|
|
|
import TablePagination from "@mui/material/TablePagination"; |
|
|
|
|
|
import TableRow from "@mui/material/TableRow"; |
|
|
|
|
|
import IconButton from "@mui/material/IconButton"; |
|
|
|
|
|
import EditIcon from "@mui/icons-material/Edit"; |
|
|
|
|
|
import SaveIcon from "@mui/icons-material/Save"; |
|
|
|
|
|
import CancelIcon from "@mui/icons-material/Close"; |
|
|
|
|
|
import DeleteIcon from "@mui/icons-material/Delete"; |
|
|
|
|
|
import TextField from "@mui/material/TextField"; |
|
|
|
|
|
import MultiSelect from "@/components/SearchBox/MultiSelect"; |
|
|
|
|
|
|
|
|
|
|
|
export interface ResultWithId { |
|
|
|
|
|
id: string | number; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
interface BaseColumn<T extends ResultWithId> { |
|
|
|
|
|
name: keyof T; |
|
|
|
|
|
label: string; |
|
|
|
|
|
type: string; |
|
|
|
|
|
options: T[]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
interface ColumnWithAction<T extends ResultWithId> extends BaseColumn<T> { |
|
|
|
|
|
onClick: (item: T) => void; |
|
|
|
|
|
buttonIcon: React.ReactNode; |
|
|
|
|
|
buttonColor?: "inherit" | "default" | "primary" | "secondary"; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export type Column<T extends ResultWithId> = |
|
|
|
|
|
| BaseColumn<T> |
|
|
|
|
|
| ColumnWithAction<T>; |
|
|
|
|
|
|
|
|
|
|
|
interface Props<T extends ResultWithId> { |
|
|
|
|
|
items: T[], |
|
|
|
|
|
columns: Column<T>[], |
|
|
|
|
|
noWrapper?: boolean, |
|
|
|
|
|
setPagingController: (value: { pageNum: number; pageSize: number; totalCount: number }) => void, |
|
|
|
|
|
pagingController: { pageNum: number; pageSize: number; totalCount: number }, |
|
|
|
|
|
isAutoPaging: boolean |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
function EditableSearchResults<T extends ResultWithId>({ |
|
|
|
|
|
items, |
|
|
|
|
|
columns, |
|
|
|
|
|
noWrapper, |
|
|
|
|
|
pagingController, |
|
|
|
|
|
setPagingController, |
|
|
|
|
|
isAutoPaging = true, |
|
|
|
|
|
}: Props<T>) { |
|
|
|
|
|
const [page, setPage] = useState(0); |
|
|
|
|
|
const [rowsPerPage, setRowsPerPage] = useState(10); |
|
|
|
|
|
const [editingRowId, setEditingRowId] = useState<number | null>(null); |
|
|
|
|
|
const [editedItems, setEditedItems] = useState<T[]>(items); |
|
|
|
|
|
|
|
|
|
|
|
useEffect(()=>{ |
|
|
|
|
|
setEditedItems(items) |
|
|
|
|
|
},[items]) |
|
|
|
|
|
const handleChangePage = (_event: unknown, newPage: number) => { |
|
|
|
|
|
setPage(newPage); |
|
|
|
|
|
setPagingController({ ...pagingController, pageNum: newPage + 1 }); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => { |
|
|
|
|
|
setRowsPerPage(+event.target.value); |
|
|
|
|
|
setPage(0); |
|
|
|
|
|
setPagingController({ ...pagingController, pageSize: +event.target.value, pageNum: 1 }); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleEditClick = (id: number) => { |
|
|
|
|
|
setEditingRowId(id); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleSaveClick = (item: T) => { |
|
|
|
|
|
setEditingRowId(null); |
|
|
|
|
|
// Call API or any save logic here |
|
|
|
|
|
setEditedItems((prev) => |
|
|
|
|
|
prev.map((row) => (row.id === item.id ? { ...row } : row)) |
|
|
|
|
|
); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleInputChange = (id: number, field: keyof T, value: string | number[]) => { |
|
|
|
|
|
setEditedItems((prev) => |
|
|
|
|
|
prev.map((item) => |
|
|
|
|
|
item.id === id ? { ...item, [field]: value } : item |
|
|
|
|
|
) |
|
|
|
|
|
); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleDeleteClick = (id: number) => { |
|
|
|
|
|
// Implement delete logic here |
|
|
|
|
|
setEditedItems((prev) => prev.filter(item => item.id !== id)); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const table = ( |
|
|
|
|
|
<> |
|
|
|
|
|
<TableContainer sx={{ maxHeight: 440 }}> |
|
|
|
|
|
<Table stickyHeader> |
|
|
|
|
|
<TableHead> |
|
|
|
|
|
<TableRow> |
|
|
|
|
|
<TableCell>Actions</TableCell> {/* Action Column Header */} |
|
|
|
|
|
{columns.map((column, idx) => ( |
|
|
|
|
|
<TableCell key={`${column.name.toString()}${idx}`}> |
|
|
|
|
|
{column.label} |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
))} |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
</TableHead> |
|
|
|
|
|
<TableBody> |
|
|
|
|
|
{(isAutoPaging ? editedItems.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) : editedItems).map((item) => ( |
|
|
|
|
|
<TableRow hover tabIndex={-1} key={item.id}> |
|
|
|
|
|
<TableCell> |
|
|
|
|
|
{editingRowId === item.id ? ( |
|
|
|
|
|
<> |
|
|
|
|
|
<IconButton onClick={() => handleSaveClick(item)}> |
|
|
|
|
|
<SaveIcon /> |
|
|
|
|
|
</IconButton> |
|
|
|
|
|
<IconButton onClick={() => setEditingRowId(null)}> |
|
|
|
|
|
<CancelIcon /> |
|
|
|
|
|
</IconButton> |
|
|
|
|
|
</> |
|
|
|
|
|
) : ( |
|
|
|
|
|
<> |
|
|
|
|
|
<IconButton onClick={() => handleEditClick(item.id as number)}> |
|
|
|
|
|
<EditIcon /> |
|
|
|
|
|
</IconButton> |
|
|
|
|
|
<IconButton onClick={() => handleDeleteClick(item.id as number)}> |
|
|
|
|
|
<DeleteIcon /> |
|
|
|
|
|
</IconButton> |
|
|
|
|
|
</> |
|
|
|
|
|
)} |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
{columns.map((column, idx) => { |
|
|
|
|
|
const columnName = column.name; |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
|
<TableCell key={`${columnName.toString()}-${idx}`}> |
|
|
|
|
|
{editingRowId === item.id ? ( |
|
|
|
|
|
(() => { |
|
|
|
|
|
switch (column.type) { |
|
|
|
|
|
case 'input': |
|
|
|
|
|
return ( |
|
|
|
|
|
<TextField |
|
|
|
|
|
fullWidth |
|
|
|
|
|
defaultValue={item[columnName] as string} |
|
|
|
|
|
onChange={(e) => handleInputChange(item.id as number, columnName, e.target.value)} |
|
|
|
|
|
/> |
|
|
|
|
|
); |
|
|
|
|
|
case 'multi-select': |
|
|
|
|
|
return ( |
|
|
|
|
|
<MultiSelect |
|
|
|
|
|
//label={column.label} |
|
|
|
|
|
options={column.options} |
|
|
|
|
|
selectedValues={[]} |
|
|
|
|
|
onChange={(selectedValues) => handleInputChange(item.id as number, columnName, selectedValues)} |
|
|
|
|
|
/> |
|
|
|
|
|
); |
|
|
|
|
|
default: |
|
|
|
|
|
return null; // Handle any default case if needed |
|
|
|
|
|
} |
|
|
|
|
|
})() |
|
|
|
|
|
) : ( |
|
|
|
|
|
<span onDoubleClick={() => handleEditClick(item.id as number)}> |
|
|
|
|
|
{item[columnName] as string} |
|
|
|
|
|
</span> |
|
|
|
|
|
)} |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
); |
|
|
|
|
|
})} |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
))} |
|
|
|
|
|
</TableBody> |
|
|
|
|
|
</Table> |
|
|
|
|
|
</TableContainer> |
|
|
|
|
|
<TablePagination |
|
|
|
|
|
rowsPerPageOptions={[10, 25, 100]} |
|
|
|
|
|
component="div" |
|
|
|
|
|
count={pagingController.totalCount === 0 ? editedItems.length : pagingController.totalCount} |
|
|
|
|
|
rowsPerPage={rowsPerPage} |
|
|
|
|
|
page={page} |
|
|
|
|
|
onPageChange={handleChangePage} |
|
|
|
|
|
onRowsPerPageChange={handleChangeRowsPerPage} |
|
|
|
|
|
/> |
|
|
|
|
|
</> |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
return noWrapper ? table : <Paper sx={{ overflow: "hidden" }}>{table}</Paper>; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
export default EditableSearchResults; |