ProductTable

ProductTable component client script is also using ReactJs and Ant Design react component library. This component renders the list of all products in the database. User can view information about the product.

const {
  Button,
  Input,
  Space,
  Switch,
  Table,
  GetRef,
  Select,
  TableColumnsType,
  InputNumber,
  Popconfirm,
  Typography,
  TableColumnType,
  Spin
} = antd;
const { useState, useEffect, useRef, useMemo } = React;
const { SearchOutlined } = antdIcons;
const ProductTable = ({ products, edit }) => {
  const [data, setData] = useState(products);
  const [enableEditing, setEnableEditing] = useState(edit);
  const [searchText, setSearchText] = useState("");
  const [searchedColumn, setSearchedColumn] = useState("");
  const searchInput = useRef(null);
  const [loading, setLoading] = React.useState(false);
  const handleSearch = (selectedKeys, confirm, dataIndex) => {
    confirm();
    setSearchText(selectedKeys[0]);
    setSearchedColumn(dataIndex);
  };

  const handleReset = clearFilters => {
    clearFilters();
    setSearchText("");
  };

  const getColumnSearchProps = dataIndex => ({
    filterDropdown: ({
      setSelectedKeys,
      selectedKeys,
      confirm,
      clearFilters,
      close
    }) => (
      <div style={{ padding: 8 }} onKeyDown={e => e.stopPropagation()}>
        <Input
          ref={searchInput}
          placeholder={`Search ${dataIndex}`}
          value={selectedKeys[0]}
          onChange={e =>
            setSelectedKeys(e.target.value ? [e.target.value] : [])
          }
          onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)}
          style={{ marginBottom: 8, display: "block" }}
        />
        <Space>
          <Button
            type="primary"
            onClick={() => handleSearch(selectedKeys, confirm, dataIndex)}
            icon={<SearchOutlined />}
            size="small"
            style={{ width: 90 }}
          >
            Search
          </Button>
          <Button
            onClick={() => clearFilters && handleReset(clearFilters)}
            size="small"
            style={{ width: 90 }}
          >
            Reset
          </Button>
          <Button
            type="link"
            size="small"
            onClick={() => {
              confirm({ closeDropdown: false });
              setSearchText(selectedKeys[0]);
              setSearchedColumn(dataIndex);
            }}
          >
            Filter
          </Button>
          <Button
            type="link"
            size="small"
            onClick={() => {
              close();
            }}
          >
            close
          </Button>
        </Space>
      </div>
    ),
    filterIcon: filtered => (
      <SearchOutlined style={{ color: filtered ? "#1677ff" : undefined }} />
    ),
    onFilter: (value, record) =>
      record[dataIndex]
        .toString()
        .toLowerCase()
        .includes(value.toLowerCase()),
    onFilterDropdownOpenChange: visible => {
      if (visible) {
        setTimeout(() => searchInput.current?.select(), 100);
      }
    }
  });

  const options = [
    {
      label: "NUR",
      value: "NUR"
    },
    {
      label: "I",
      value: "I"
    },
    {
      label: "II",
      value: "II"
    },
    {
      label: "III",
      value: "III"
    },
    {
      label: "IV",
      value: "IV"
    },
    {
      label: "V",
      value: "V"
    },
    {
      label: "VI",
      value: "VI"
    },
    {
      label: "VII",
      value: "VII"
    },
    {
      label: "VIII",
      value: "VIII"
    },
    {
      label: "IX",
      value: "IX"
    },
    {
      label: "X",
      value: "X"
    },
    {
      label: "XI",
      value: "XI"
    },
    {
      label: "XII",
      value: "XII"
    }
  ];

  const handlePriceChange = (record, parentRecord, newPrice) => {
    console.log(
      `Price changed ${record.price} ${parentRecord.key} ${newPrice}`
    );
    const editIndex = data.findIndex(val => val.key === parentRecord.key);
    const stockRowIndex = data[editIndex].stock.findIndex(
      val => val.key === record.key
    );
    data[editIndex].stock[stockRowIndex].price = newPrice;
    setData([...data]);
  };

  const expandedRowRender = record => {
    const columns = [
      {
        title: "Price",
        dataIndex: "price",
        key: "price",
        render: (text, _record) => {
          return (
            <InputNumber
              disabled={!enableEditing}
              value={_record.price}
              onChange={value => {
                handlePriceChange(_record, record, value);
              }}
            />
          );
        }
      },
      {
        title: "Quantity",
        dataIndex: "quantity",
        key: "quantity"
      }
    ];
    const data = record.stock;
    return <Table columns={columns} dataSource={data} pagination={false} />;
  };

  const handleDelete = async key => {
    const newData = data.filter(item => item.key !== key);
    setData(newData);
    const removedRecord = data.filter(item => item.key === key);
    const delPayload = { oper: "del", _id: removedRecord[0]._id };
    console.log(delPayload);
    const response = await http.post(`/o/products`, delPayload);
  };

  const handleCategoryChange = (record, newValue) => {
    console.log(`Category changed ${record.category} ${newValue}`);
    console.log(newValue instanceof Array);
    const editIndex = data.findIndex(val => val.key === record.key);
    data[editIndex].category = newValue;
    setData([...data]);
  };

  const update = async record => {
    console.log(record);
    setLoading(true);
    const response = await http.post("v1/postProducts", record);
    console.log(response);
    setLoading(false);
  };

  const columns = [
    {
      title: "SKU",
      dataIndex: "sku",
      key: "sku"
    },
    {
      title: "Title",
      dataIndex: "title",
      key: "title",
      ...getColumnSearchProps("title"),
      sortDirections: ["descend", "ascend"]
    },
    {
      title: "Description",
      dataIndex: "description",
      key: "description"
    },
    {
      title: "Category",
      dataIndex: "category",
      key: "category",
      render: (text, record) => (
        <Select
          mode="multiple"
          allowClear
          style={{
            width: "100%"
          }}
          disabled={!enableEditing}
          placeholder="Please select"
          onChange={value => {
            handleCategoryChange(record, value);
          }}
          defaultValue={record.category}
          options={options}
        />
      )
    },
    {
      title: "Last Stock Price",
      dataIndex: "last_stock_price",
      key: "last_stock_price"
    },
    {
      title: "Total Quantity",
      dataIndex: "total_quantity",
      key: "total_quantity"
    },
    {
      title: "Actions",
      dataIndex: "actions",
      render: (_, record) =>
        data.length >= 1 ? (
          <>
            <Popconfirm
              title="Sure to delete?"
              onConfirm={value => handleDelete(record.key, value)}
            >
              <a>Delete</a>
            </Popconfirm>
            <Typography.Link style={{marginLeft: "10px"}}onClick={() => update(record)}>
              Update
            </Typography.Link>
          </>
        ) : null
    }
  ];

  useMemo(() => {
    // fetchAllProducts(); // This will always use latest value of count
    // setData([...products])
    setEnableEditing(edit);
  }, [edit]);

  useMemo(() => {
    setData([...products])// This will always use latest value of count
  }, [products]);

  return (
    <>
      <Spin spinning={loading} delay={500}>
        <Table
          dataSource={data}
          columns={columns}
          expandable={{
            expandedRowRender
          }}
        />
      </Spin>
    </>
  );
};

return ProductTable;

Last updated