diff --git a/src/components/EditableTable.jsx b/src/components/EditableTable.jsx
new file mode 100644
index 0000000..24b2ace
--- /dev/null
+++ b/src/components/EditableTable.jsx
@@ -0,0 +1,140 @@
+import { Form, Input, Popconfirm, Typography, Table } from "antd"
+import { useState } from "react";
+
+const EditableCell = ({
+ editing,
+ record,
+ dataIndex,
+ input,
+ isRequired,
+ title,
+ formItemClass,
+ formItemRules,
+ children,
+}) => {
+
+ const inputNode = input ??
+ const rules = formItemRules ?? [{
+ required: isRequired,
+ message: `Please Input ${title}!`,
+ }]
+
+ const editor =
+ {inputNode}
+
+
+ return (
+ {editing ? editor: children}
+ | )
+}
+
+export const EditableTable = ({columns, dataSource, onUpdateData: onChange, ...otherTableProps}) => {
+ const [form] = Form.useForm()
+ const [editingKey, setEditingKey] = useState('')
+
+ const isEditing = (record) => record.key === editingKey
+
+ const edit = (record) => {
+ form.setFieldsValue({...record})
+ setEditingKey(record.key)
+ }
+
+ const cancel = () => {
+ setEditingKey('')
+ }
+
+ const save = async (key) => {
+ try {
+ const row = await form.validateFields()
+ const newData = [...dataSource]
+ const index = newData.findIndex((item) => key === item.key)
+
+ if (index > -1) {
+ const item = newData[index]
+ newData.splice(index, 1, { ...item, ...row })
+ } else {
+ newData.push(row)
+ }
+
+ setEditingKey('')
+ if(onChange)
+ onChange(newData)
+ else
+ console.error(`EditableTable.onChange doesn't connected`)
+ } catch (errInfo) {
+ console.log('Validate Failed:', errInfo)
+ }
+ }
+
+ const operationColumn = {
+ title: '',
+ dataIndex: 'operation',
+ render: (_, record) => {
+ const editable = isEditing(record)
+ return editable ? (
+
+ save(record.key)}
+ style={{
+ marginRight: 8,
+ }}
+ >
+ Save
+
+
+ Cancel
+
+
+ ) : (
+ edit(record)}>
+ Edit
+
+ )
+ },
+ }
+
+ const handleColumn = (col) => {
+ if (col.children)
+ col.children = col.children.map(handleColumn)
+
+ if (!col.editable)
+ return col
+
+ return {
+ ...col,
+ onCell: (record) => ({
+ editing: isEditing(record),
+ record,
+ dataIndex: col.dataIndex ?? col.key,
+ input: col.input,
+ isRequired: col.isRequired,
+ title: col.title,
+ dataType: col.dataType,
+ formItemClass: col.formItemClass,
+ formItemRules: col.formItemRules,
+ }),
+ }
+ }
+
+ const mergedColumns = [...columns.map(handleColumn), operationColumn]
+
+ return (
+
+ )
+}
\ No newline at end of file