From d89cc70ec566c171a95a936a6787b12b749df10f Mon Sep 17 00:00:00 2001 From: Viet Anh Nguyen Date: Thu, 22 Feb 2024 09:03:15 +0700 Subject: [PATCH] Implement editable row --- cope2n-fe/src/pages/reviews/index.tsx | 265 +++++++++++++++++++++----- 1 file changed, 214 insertions(+), 51 deletions(-) diff --git a/cope2n-fe/src/pages/reviews/index.tsx b/cope2n-fe/src/pages/reviews/index.tsx index 74d1315..312aa5c 100644 --- a/cope2n-fe/src/pages/reviews/index.tsx +++ b/cope2n-fe/src/pages/reviews/index.tsx @@ -1,10 +1,10 @@ import { t } from '@lingui/macro'; -import { Button, Input, Table, Tag, DatePicker, Form, Modal, Select, Space, Checkbox } from 'antd'; -import { useState, useEffect } from 'react'; +import { Button, Input, Table, Tag, DatePicker, Form, Modal, Select, Space, Spin, message } from 'antd'; +import React, { useContext, useEffect, useRef, useState } from 'react'; +import type { GetRef } from 'antd'; import { Layout } from 'antd'; import { EditOutlined, DownloadOutlined, CheckCircleOutlined, - ClockCircleOutlined, ArrowLeftOutlined, ArrowRightOutlined, FullscreenOutlined, @@ -16,6 +16,7 @@ const { Sider, Content } = Layout; import { baseURL } from "request/api"; import moment from 'moment'; + const siderStyle: React.CSSProperties = { backgroundColor: '#fafafa', padding: 10, @@ -29,6 +30,110 @@ const StyledTable = styled(Table)` } `; +type InputRef = GetRef; +type FormInstance = GetRef>; + +const EditableContext = React.createContext | null>(null); + +interface Item { + key: string; + accuracy: number; + submitted: string; + revised: string; + action: string; +} + +interface EditableRowProps { + index: number; +} + +const EditableRow: React.FC = ({ index, ...props }) => { + const [form] = Form.useForm(); + return ( +
+ + + +
+ ); +}; + +interface EditableCellProps { + title: React.ReactNode; + editable: boolean; + children: React.ReactNode; + dataIndex: keyof Item; + record: Item; + handleSave: (record: Item) => void; +} + + +const EditableCell: React.FC = ({ + title, + editable, + children, + dataIndex, + record, + handleSave, + ...restProps +}) => { + const [editing, setEditing] = useState(false); + const inputRef = useRef(null); + const form = useContext(EditableContext)!; + + useEffect(() => { + if (editing) { + inputRef.current!.focus(); + } + }, [editing]); + + const toggleEdit = () => { + setEditing(!editing); + form.setFieldsValue({ [dataIndex]: record[dataIndex] }); + }; + + const save = async () => { + try { + const values = await form.validateFields(); + + toggleEdit(); + handleSave({ ...record, ...values }); + } catch (errInfo) { + console.log('Save failed:', errInfo); + } + }; + + let childNode = children; + + if (editable) { + childNode = editing ? ( + + + + ) : ( +
+ {children} +
+ ); + } + + return {childNode}; +}; + +type EditableTableProps = Parameters[0]; + + +type ColumnTypes = Exclude; + const StyledEditOutlined = styled(EditOutlined)` & { @@ -40,7 +145,7 @@ const StyledEditOutlined = styled(EditOutlined)` } `; -const columns = [ +const defaultColumns = [ { title: 'Key', dataIndex: 'key', @@ -70,23 +175,7 @@ const columns = [ title: 'Revised', dataIndex: 'revised', key: 'revised', - render: (text, record) => { - return ( -
- {text} -
- ) - }, - }, - { - title: 'Action', - key: 'operation', - fixed: 'right', - width: 100, - render: () => , + editable: true, }, ]; @@ -194,6 +283,7 @@ const fetchRequest = async (id) => { }; const ReviewPage = () => { + const [loading, setLoading] = useState(false); const [fullscreen, setFullscreen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false); const [selectedFileId, setSelectedFileId] = useState(0); @@ -219,18 +309,13 @@ const ReviewPage = () => { // purchase_date: "2024-01-20", // retailername: "Test Retailer", // sold_to_party: "Test Party", - const dataSource = [ - // { - // key: "imei_number", - // predicted: "352271450941944", - // submitted: "352271450941944", - // revised: "352271450941944", - // }, - ]; + const dataSource = []; const predicted = (currentRequest && currentRequest["Reviewed Result"]) ? currentRequest["Reviewed Result"] : {}; const submitted = (currentRequest && currentRequest["Feedback Result"]) ? currentRequest["Feedback Result"] : {}; const revised = (currentRequest && currentRequest["Reviewed Result"]) ? currentRequest["Reviewed Result"] : {}; + + const keys = Object.keys(predicted); for (let i = 0; i < keys.length; i++) { let instance = {}; @@ -241,37 +326,38 @@ const ReviewPage = () => { dataSource.push(instance); } - const gotoNextRequest = () => { - const nextRequestIndex = currentRequestIndex + 1; - setCurrentRequestIndex(nextRequestIndex); - fetchAllRequests(filterDateRange, filterSubsidiaries, filterReviewState, filterIncludeTests, nextRequestIndex, 2).then((data) => { + const [pageIndexToGoto, setPageIndexToGoto] = useState(1); + + const loadCurrentRequest = (requestID) => { + setLoading(true); + fetchAllRequests(filterDateRange, filterSubsidiaries, filterReviewState, filterIncludeTests, requestID, 2).then((data) => { setRequests(data?.subscription_requests); setHasNextRequest(data?.subscription_requests.length > 1); setTotalPages(data?.page?.total_pages); const requestData = fetchRequest(data?.subscription_requests[0].RequestID); requestData.then(async (data) => { - console.log(data) if (data) setCurrentRequest(data); + }).finally(() => { + setLoading(false); }); + }).finally(() => { + setLoading(false); }); }; + const gotoNextRequest = () => { + const nextRequestIndex = currentRequestIndex + 1; + setCurrentRequestIndex(nextRequestIndex); + loadCurrentRequest(nextRequestIndex); + }; + const gotoPreviousRequest = () => { if (currentRequestIndex === 1) { return; } const previousRequestIndex = currentRequestIndex - 1; setCurrentRequestIndex(previousRequestIndex); - fetchAllRequests(filterDateRange, filterSubsidiaries, filterReviewState, filterIncludeTests, previousRequestIndex, 2).then((data) => { - setRequests(data?.subscription_requests); - setHasNextRequest(data?.subscription_requests.length > 1); - setTotalPages(data?.page?.total_pages); - const requestData = fetchRequest(data?.subscription_requests[0].RequestID); - requestData.then(async (data) => { - console.log(data) - if (data) setCurrentRequest(data); - }); - }); + loadCurrentRequest(previousRequestIndex); }; @@ -304,11 +390,54 @@ const ReviewPage = () => { }); }, []); - const fileURL = currentRequest ? baseURL + currentRequest["Files"][selectedFileId]["File URL"].replace("http://be-ctel-sbt:9000/api", "") : "dummy.pdf"; + const fileURL = (currentRequest && currentRequest["Files"][selectedFileId]) ? baseURL + currentRequest["Files"][selectedFileId]["File URL"].replace("http://be-ctel-sbt:9000/api", "") : "dummy.pdf"; + + const components = { + body: { + row: EditableRow, + cell: EditableCell, + }, + }; + + // "Key", "Accuracy", "Submitted", "Revised" + interface DataType { + key: string; + accuracy: number; + submitted: string; + revised: string; + }; + + const handleSave = (row: DataType) => { + const newData = [...dataSource]; + console.log(row); + // const index = newData.findIndex((item) => row.key === item.key); + // const item = newData[index]; + // newData.splice(index, 1, { + // ...item, + // ...row, + // }); + // setDataSource(newData); + }; + + const columns = defaultColumns.map((col) => { + if (!col.editable) { + return col; + } + return { + ...col, + onCell: (record: DataType) => ({ + record, + editable: col.editable, + dataIndex: col.dataIndex, + title: col.title, + handleSave, + }), + }; + }); return (
{ zIndex: 1000, } : { height: '100%', + position: 'relative', }}> +
+ +
- + - } /> + } + value={pageIndexToGoto} + onChange={e => { + setPageIndexToGoto(parseInt(e.target.value)); + }} + />

{totalPages ? ("Request: " + currentRequestIndex + "/" + totalPages) : "No Request. Adjust your search criteria to see more results."}

@@ -573,7 +734,9 @@ const ReviewPage = () => { overflowY: 'auto', }} > - 'editable-row'} + bordered dataSource={dataSource} columns={columns} />