Preview:
import React, { useEffect, useState } from "react";
import "./expensesTables.css"
import Button from 'react-bootstrap/Button';
import Offcanvas from 'react-bootstrap/Offcanvas';
import defaultIcon from "../../../../assets/images/icon/default.png";
import {
  Check,
  ChevronLeft,ArrowDown,ArrowUp ,
  Building,Person,Plus
} from "react-bootstrap-icons";



import NodataImg from "../../../../assets/images/img/NodataImg.png";
import nodataBg from "../../../../assets/images/nodataBg.png";
import SearchIcon from "../../../../assets/images/icon/searchIcon.png";


import { Table } from "react-bootstrap";
import TableTopBar from "./TableTopBar";
import { Resizable } from 'react-resizable';
import StatusPopup from "./StatusPopup";


const ExpensesTables = ({ expensesData,rowsNo }) => {
  const [sortField, setSortField] = useState("Quote");
  const [sortDirection, setSortDirection] = useState("asc");
  const [selectedRow, setSelectedRow] = useState(null);

  // Formate Date
  const formatDate = (timestamp) => {
    const date = new Date(timestamp * 1000);
    const day = date.getDate();
    const monthAbbreviation = new Intl.DateTimeFormat("en-US", {
      month: "short",
    }).format(date);
    const year = date.getFullYear();
    return `${day} ${monthAbbreviation} ${year}`;
  };

  const toggleSort = (field) => {
    setSortField(field);
    setSortDirection((prevDirection) =>
      prevDirection === "asc" ? "desc" : "asc"
    );
  };

  const sortedClientsData = [...expensesData].sort((a, b) => {
    const aValue = a[sortField];
    const bValue = b[sortField];
  
    
    if (aValue === undefined || bValue === undefined) {
      return 0; 
    }
  
    if (sortDirection === "asc") {
      return aValue.localeCompare(bValue, undefined, { numeric: true });
    } else {
      return bValue.localeCompare(aValue, undefined, { numeric: true });
    }
  });

  const [show, setShow] = useState(false);

  const handleRowClick = (rowId) => {
    setSelectedRow(rowId === selectedRow ? null : rowId);
    setShow(true)
  };

 

    const handleClose = () => setShow(false);


  const [selectedRows, setSelectedRows] = useState([]);
  const handleSelectAllCheckboxChange = () => {
    const allRowIds = expensesData.map((sale) => sale.id);
    if (selectedRows.length === allRowIds.length) {
      setSelectedRows([]);
    } else {
      setSelectedRows(allRowIds);
    }
  };

  const selectedRowsCount = selectedRows.length;

  const [columns, setColumns] = useState([
    {
      field: "Quote",
      headerName: (
        <div className="styleColor1" onClick={() => toggleSort("Quote")}>
        <span>Expense ID</span>
        {sortField === "Quote" && (
          <span>
            {sortDirection === "asc" ? (
              <ArrowUp size={16} color="#475467" />
            ) : (
              <ArrowDown size={16} color="#475467" />
            )}
          </span>
        )}
      </div>
      ),
 
      renderCell: (params) => (
        <div className="styleColor1">
          <strong>{params.value}</strong>
          <p>{params.row.created}</p>
        
        </div>
      ),
    },

    {
      field: "supplier",
      headerName: (
        <div className="styleColor1" onClick={() => toggleSort("supplier")}>
        <span>Supplier</span>
        {sortField === "supplier" && (
          <span>
            {sortDirection === "asc" ? (
              <ArrowUp size={16} color="#475467" />
            ) : (
              <ArrowDown size={16} color="#475467" />
            )}
          </span>
        )}
      </div>
      ),
  
      renderCell: (params) => (
        <div className="userImgStyle1">
          <div className="innerFlex styleColor2 d-flex justify-content-between">
            <div className="leftStyle spaceBorder d-flex align-items-center isbusinessIcon">
              {params.row.photo ? (
                <img
                  src={params.row.photo}
                  alt={params.row.photo}
                  style={{ marginRight: "5px" }}
                  onError={(e) => {
                    e.target.src = defaultIcon;
                    e.target.alt = "Image Not Found";
                  }}
                />
              ) : (
                params.row.isbusiness ? (
                  <div className="iconBuilding icon">
                    <Building size={13.71} color="#667085" />
                  </div>
                ) : (
                  <div className="iconPerson icon">
                    <Person size={24} color="#667085" />
                  </div>
                )
              )}
              <span>{params.value}</span>
            </div>
            <Button className="linkByttonStyle" variant="link">Open</Button>
          </div>
        </div>
      ),
      
    },
  
    {
      field: "reference",
      sortable: false,
      headerName: "Reference",
      width: 440,
      renderCell: (params) => {
    
        return (
          <div
            className="mainStyle "
            style={{ whiteSpace: "normal", textAlign: "left" }}
          >
           {params.value}
          </div>
        );
      },
    },
    {
      field: "duedate",
      sortable: false,
      headerName: "Due Date",
      width: 120,
      renderCell: (params) => {

        return (
          <div
            className="mainStyle duedate"
            style={{ whiteSpace: "normal", textAlign: "left" }}
          >
            {formatDate(params.value)}
          </div>
        );
      },
    },
    
  
    {
      field: "total",
      sortable: false,
      headerName: "Total",
      width: 131,
      renderCell: (params) => (
          <div
            className="totalpayEx"
            style={{ whiteSpace: "normal", textAlign: "center" }}
          >
         $ {params.value}<span className="lineVerticle"><Plus size={12} color="#079455" /></span>
        </div>
      ),
    },
  
    {
      field: "UserInvoice",
      sortable: false,
      headerName: "Interval/Order",
      width: 144,
      renderCell: (params) => (
        params.value ? (
          <div
            className="intervalorder"
            style={{ whiteSpace: "normal", textAlign: "center" }}
          >
            {params.value}
          </div>
        ) : (
          <span></span>
        )
      ),
      
    },
   
    {
      field: "accountCode",
      sortable: false,
      headerName: "Account Code",
      width: 214,
      renderCell: (params) => (
        params.value ? (
          <div
            className="styleColor1 accountCode"
            style={{ whiteSpace: "normal", textAlign: "left" }}
          >
            {params.value}: {params.row.accountname}
          </div>
        ) : (
          <span></span>
        )
      ),
    },

    {
      field: "country",
      sortable: false,
      headerName: "Xero/Myob",
      width: 111,
      renderCell: (params) => (
        <div
          className="styleGrey01"
          style={{ whiteSpace: "normal", textAlign: "left", textTransform: "uppercase" }}
        >
          
        </div>
      ),
    },

    {
      field: "department",
      sortable: false,
      headerName: "Departments",
      width: 128,
      renderCell: (params) => (
        <div
        className="styleGrey01 departmentstyle"
        style={{ whiteSpace: "normal", textAlign: "left" }}
      >
        {params.value}
        
        </div>
      ),
    },
    {
      field: "paid",
      sortable: false,
      headerName: "Status",
      width: 120,
      renderCell: (params) => (
          <StatusPopup statusValue={params.value} />
      ),
    }
  ]);

  const [rows, setRows] = useState([]);
  useEffect(()=> {
    const rows = expensesData.map((expense) => {

    
      return {
        isSelected: selectedRows.includes(expense.id),
        id: expense.id,
        Quote: expense.number,
        created: expense.created,
        paid: expense.paid,
        supplier: expense.supplier ? expense.supplier.name : null,
        photo: expense.supplier ? expense.supplier.photo : null,
        reference: expense.invoice_reference,
        duedate: expense.created,
        total: expense.total,
        UserInvoice: expense.type,
        accountCode: expense.account_code ? expense.account_code.code : null,
        accountname: expense.account_code ? expense.account_code.name : null,
        department: expense.department ? expense.department.name : null,
      
      };
    });
    
    setRows(rows)
  }, [expensesData,selectedRows])
  

  const onResize = (index) => (event, { size }) => {
    setColumns((prevColumns) => {
      const nextColumns = [...prevColumns];
      nextColumns[index] = {
        ...nextColumns[index],
        width: size.width,
      };
      return nextColumns;
    });
  };
  
  const handleCheckboxChange = (rowId) => {
    const updatedSelectedRows = [...selectedRows];
    if (updatedSelectedRows.includes(rowId)) {
      // Row is already selected, remove it
      updatedSelectedRows.splice(updatedSelectedRows.indexOf(rowId), 1);
    } else {
      // Row is not selected, add it
      updatedSelectedRows.push(rowId);
    }
    setSelectedRows(updatedSelectedRows);
  };
  const isSelected = selectedRows.length > 0;
  const [rowsfilter, setRowsFilter] = useState([]);
  
  const handleRowsFilterChange = (filteredRows) => {
    
    const rows = filteredRows.map((expense) => {
      return {
        isSelected: selectedRows.includes(expense.id),
        id: expense.id,
        Quote: expense.number,
        created: expense.created,
        supplier: expense.supplier ? expense.supplier.name : null,
        photo: expense.supplier ? expense.supplier.photo : null,
        reference: expense.invoice_reference,
        duedate: expense.created,
        total: expense.total,
        UserInvoice: expense.type,
        accountCode: expense.account_code ? expense.account_code.code : null,
        accountname: expense.account_code ? expense.account_code.name : null,
        department: expense.department ? expense.department.name : null,
      };
    });
    
    setRows(rows);
    setRowsFilter(rows);
  };

  return (
    <div className="expensesTableWrap">
      <TableTopBar expensesData={expensesData} rowsfilter={rowsfilter} onRowsFilterChange={handleRowsFilterChange} rows={sortedClientsData} selectedRow={selectedRows} selectClass={isSelected ? "selected-row" : ""} selectedRowCount={selectedRowsCount} />

      <Table responsive>
      <thead style={{ position: "sticky", top: "0px", zIndex: 9 }}>
          <tr>
          <th>
          <label className="customCheckBox">
          <input
            type="checkbox"
            checked={selectedRows.length === expensesData.length}
            onChange={handleSelectAllCheckboxChange}
          />
          <span className="checkmark">
            <Check color="#9E77ED" size={20} />
          </span>
        </label>
          </th>
            {columns.map((column, index) => (
                <th key={column.field} style={{ width: column.width }}>
                <Resizable
                  width={column.width || 100} // Provide a default width if undefined
                  height={0}
                  onResize={onResize(index)}
                >
                  <div>
                    {column.headerName}
                  </div> 
                </Resizable>
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.length === 0 ? (
            <tr className="nodataTableRow">
              <td colSpan={columns.length} style={{ textAlign: "center" }}>
                <div style={{ textAlign: "center", marginTop: "20px" }}>
                  <div
                    className="Nodata"
                    style={{ background: `url(${nodataBg})` }}
                  >
                    <div className="image">
                      <img src={NodataImg} alt="NodataImg" />
                      <img
                        className="SearchIcon"
                        src={SearchIcon}
                        alt="SearchIcon"
                      />
                    </div>
                    <h2>There is no results</h2>
                    <p>
                      The user you are looking for doesn't exist. Here are some
                      helpful links:
                    </p>
                    <Button className="gobackButton mb-4 mt-4" variant="link">
                      {" "}
                      <ChevronLeft color="#000" size={20} />
                      Go back
                    </Button>
                    <Button className="gobackSupport mt-4" variant="link">
                      {" "}
                      Support
                    </Button>
                  </div>
                </div>
              </td>
            </tr>
          ) : (
        
            rows.map((row,index) => (
              <tr data-clientuniqueid={row.clientUniqueId} rowsNo={index}
                key={row.id} className={row.isSelected ? "selected-row" : ""}
                >
                  <td>
                  <label className="customCheckBox">
                  <input
                    type="checkbox"
                    checked={selectedRows.includes(row.id)}
                    onChange={() => handleCheckboxChange(row.id)}
                  />
                  <span className="checkmark">
                    <Check color="#9E77ED" size={20} />
                  </span>
                </label>
                  </td>
                {columns.map((column) => (
                    <td key={column.field} onClick={["Quote", "Client", "category"].includes(column.field) ? () => handleRowClick(row.id) : null}>
                    {column.renderCell({ value: row[column.field], row })}
                  </td>
                ))}
              </tr>
            ))
          )}
        </tbody>
      </Table>
   
        {/* Sidebar */}
        {selectedRow && (
       <Offcanvas show={show} placement="end" onHide={handleClose}>
       <Offcanvas.Header closeButton>
         <Offcanvas.Title><strong>{selectedRow}.</strong> Client Edit Data Head</Offcanvas.Title>
       </Offcanvas.Header>
       <Offcanvas.Body>
        
       </Offcanvas.Body>
     </Offcanvas>
     
      )}
    </div>
  );
};




export default ExpensesTables
downloadDownload PNG downloadDownload JPEG downloadDownload SVG

Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!

Click to optimize width for Twitter