Search code examples
reactjsfrontendbillingobjectid

how to filter by ObjectId and calculate total cost in react.js


The table is rendered twice for Wade Ivan. Instead, the table should be rendered only once. The total should be 180 and not 680.

I have a search class component. However, when i search for clients by their names based on ObjectId(clientdetails._id), rendering returns multiple tables equivalent to the number of documents with that ObjectId.Moreover, the total bill calculated gives the total cost from all objectIds instead of the total cost derived from documents with similar ObjectId (clientdetails._id)

import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

const isSearched = searchTerm => item => {
  if (searchTerm === "") {
    return;
  }
  return item.clientdetails.fullName.toLowerCase().includes(searchTerm.toLowerCase());
};

export default class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      list:[
        {
          "_id": "6389d19a3b28b9e4af3694f8",
          "clientdetails": {
            "_id": "6389d1193b28b9e4af3694f2",
            "fullName": "WADE IVAN",
            "dateofBirth": "1990-01-01T00:00:00.000Z",
            "age": 32,
            "__v": 0
          },
          "paymentReferenceCode": "1",
          "paymentType": "Cash",
          "paymentDescription": "ACCOMODATION",
          "procedureAmount": "100",
          "paymentDiscount": 20,
          "AmountPaid": 100,
          "Total": 100,
          "__v": 0
        },
        {
          "_id": "6389d1bb3b28b9e4af369500",
          "clientdetails": {
            "_id": "6389d1193b28b9e4af3694f2",
            "fullName": "WADE IVAN",
            "dateofBirth": "1990-01-01T00:00:00.000Z",
            "age": 32,
            "__v": 0
          },
          "paymentReferenceCode": "12",
          "paymentType": "Cash",
          "paymentDescription": "ACCOMODATION",
          "procedureAmount": "100",
          "paymentDiscount": 20,
          "AmountPaid": 80,
          "Total": 80,
          "__v": 0
        },
        {
          "_id": "6389d1e03b28b9e4af369508",
          "clientdetails": {
            "_id": "6389d0ed3b28b9e4af3694ee",
            "fullName": "JOHN MARK",
            "dateofBirth": "2000-01-12T00:00:00.000Z",
            "age": 22,
            "__v": 0
          },
          "paymentReferenceCode": "3",
          "paymentType": "Cash",
          "paymentDescription": "100",
          "procedureAmount": "100",
          "paymentDiscount": 20,
          "AmountPaid": 200,
          "Total": 200,
          "__v": 0
        },
        {
          "_id": "6389d2173b28b9e4af369510",
          "clientdetails": {
            "_id": "6389d0ed3b28b9e4af3694ee",
            "fullName": "JOHN MARK",
            "dateofBirth": "2000-01-12T00:00:00.000Z",
            "age": 22,
            "__v": 0
          },
          "paymentReferenceCode": "9",
          "paymentType": "Cash",
          "paymentDescription": "ACCOMODATION",
          "procedureAmount": "10",
          "paymentDiscount": 2,
          "AmountPaid": 300,
          "Total": 300,
          "__v": 0
        }
      ],
      searchTerm: ""
    };
  }

  onSearchChange = event => {
    this.setState({ searchTerm: event.target.value });
  };

  render() {
    const { searchTerm, list } = this.state;
    
    return (
      <div className="page">
        <div className="interactions">
          <Search 
            value={searchTerm} 
            SearchChange={this.onSearchChange}
          >
            Search
          </Search>
          <Table list={list} pattern={searchTerm} onDismiss={this.onDismiss} />
        </div>
      </div>
    );
  }
}
const Search = ({ value, SearchChange, children }) => {
  return (
    <form>
      {children}
      <input type="text" value={value} onChange={SearchChange} />
    </form>
  )
}


class Table extends React.Component {
  render() {
    const { list, pattern } = this.props;
    const total=(list.reduce((total,item) =>  total = total + item.AmountPaid , 0 ));
    return (
      <div className="table">
        {list.filter(isSearched(pattern)).map(item => (
          <div key={item.clientdetails.fullName} className="table-row" >
<table>
                <thead>
                <tr>
                    <th>Client Name </th>
                    <th>Payment Reference Code</th>
                    <th>Payment Type</th>
                    <th>Payment Description</th>
                    <th>Procedure Amount</th>
                    <th>Payment Discount </th>
                    <th>Amount Paid </th>
                    
                </tr>
            </thead>
            <tbody>
            {list.filter(isSearched(pattern)).map(item => (
                    <tr > 
                        <td>{item.clientdetails.fullName}</td>

                        <td>{item.paymentReferenceCode}</td>
                        <td>{item.paymentType}</td>
                        <td>{item.paymentDescription}</td>
                        <td>{item.procedureAmount}</td>
                        <td>{item.paymentDiscount}</td>
                        <td>{item.AmountPaid}</td>
                       
                       
                    </tr>
                    ))}
                   <tfoot><tr>
                        Total: 
                             {total}
                           </tr>  
                    </tfoot>
            </tbody>
        </table>
          </div>
        ))}
      </div>
    );
  } 
}

How can i improve this code. Thanks in advance


Solution

  • Here is the basic solution

    let items = [
      {
        _id: "6389d19a3b28b9e4af3694f8",
        clientdetails: {
          _id: "6389d1193b28b9e4af3694f2",
          fullName: "WADE IVAN",
          dateofBirth: "1990-01-01T00:00:00.000Z",
          age: 32,
          __v: 0
        },
        paymentReferenceCode: "1",
        paymentType: "Cash",
        paymentDescription: "ACCOMODATION",
        procedureAmount: "100",
        paymentDiscount: 20,
        AmountPaid: 100,
        Total: 100,
        __v: 0
      },
      {
        _id: "6389d1bb3b28b9e4af369500",
        clientdetails: {
          _id: "6389d1193b28b9e4af3694f2",
          fullName: "WADE IVAN",
          dateofBirth: "1990-01-01T00:00:00.000Z",
          age: 32,
          __v: 0
        },
        paymentReferenceCode: "12",
        paymentType: "Cash",
        paymentDescription: "ACCOMODATION",
        procedureAmount: "100",
        paymentDiscount: 20,
        AmountPaid: 80,
        Total: 80,
        __v: 0
      },
      {
        _id: "6389d1e03b28b9e4af369508",
        clientdetails: {
          _id: "6389d0ed3b28b9e4af3694ee",
          fullName: "JOHN MARK",
          dateofBirth: "2000-01-12T00:00:00.000Z",
          age: 22,
          __v: 0
        },
        paymentReferenceCode: "3",
        paymentType: "Cash",
        paymentDescription: "100",
        procedureAmount: "100",
        paymentDiscount: 20,
        AmountPaid: 200,
        Total: 200,
        __v: 0
      },
      {
        _id: "6389d2173b28b9e4af369510",
        clientdetails: {
          _id: "6389d0ed3b28b9e4af3694ee",
          fullName: "JOHN MARK",
          dateofBirth: "2000-01-12T00:00:00.000Z",
          age: 22,
          __v: 0
        },
        paymentReferenceCode: "9",
        paymentType: "Cash",
        paymentDescription: "ACCOMODATION",
        procedureAmount: "10",
        paymentDiscount: 2,
        AmountPaid: 300,
        Total: 300,
        __v: 0
      }
    ];
    
    const reduced = items.reduce((acc, curr) => {
      //console.log(curr);
      const prop = curr.clientdetails._id;
     const existingTotal = acc?.[prop]?.total ?? 0;
      return {
        ...acc,
        [prop]: {
          id: prop,
          name: curr.clientdetails.fullName,
          total: existingTotal + curr.Total
        }
      };
    }, {});
    
    const list = Object.entries(reduced).map(([key, value]) => {
      return {
        id: key,
        name: value.name,
        total: value.total
      };
    });
    
    console.log("reduced", reduced);
    console.log("list", list);
    

    To include it in component

    // if only final total is needed, this function can be optimized
    // if grouping is needed then total needs to be gropued for each entry
    function getTotal(items) {
      try {
    
          const reduced = items?.reduce((acc, curr) => {
          const prop = curr.clientdetails._id;
          const existingTotal = acc?.[prop]?.total ?? 0;
        
          console.log(acc, acc?.[prop]);
          console.log(acc, curr.clientdetails.fullName);
        
          return {
            ...acc,
            [prop]: {
              id: prop,
              name: curr.clientdetails.fullName,
              total: existingTotal + curr.Total
            }
          };
        }, {});
    
        const total = Object.entries(reduced).reduce((acc, [key, value]) => {
         
          return acc + value.total 
         
        }, 0);
    
        return total;
        
      } catch (error) {
        console.log("error", error);
        return 0;
      }
    }
    
    // for grouping filtered items
    function groupItems(items) {
      try {
        const grouped = items?.reduce((acc, curr) => {
          const prop = curr.clientdetails._id;
          //const id = curr.clientdetails._id;
    
          const existingTotal = acc?.[prop]?.total ?? 0;
    
          let entries = acc[prop]?.entries ?? [];
    
          entries.push({
            id: curr._id,
            total: curr.Total,
            paymentType: curr.paymentType
            // include more props
          });
    
          return {
            ...acc,
            [prop]: {
              name: curr.clientdetails.fullName,
              total: existingTotal + curr.Total,
              entries
            }
          };
        }, {});
    
        console.log("list", grouped);
    
        const list = Object.entries(grouped).map(([key, value]) => {
          return {
            id: key,
            name: value.name,
            total: value.total,
            entries: value.entries
          };
        });
    
        return list;
      } catch (error) {
        console.log("error", error);
        return [];
      }
    }
    
    /*
    will contain list of
    {
        id: key,
        name: value.name,
        total: value.total,
        // contains individual entries and the total for the grouped entries
        entries: [
          {
             id: curr._id,
             total: curr.Total,
             paymentType: curr.paymentType
          }
        ]
    
    }
    */
    
    class Table extends React.Component {
    
      render() {
    
        const { list, pattern } = this.props;
    
        const filteredList = list.filter(isSearched(pattern));
    
        const grouped = groupItems(filteredList);
       /*
        will contain list of
       {
        id: key,
        name: value.name,
        total: value.total,
        // contains individual entries and the total for the grouped entries
        entries: [
          {
             id: curr._id,
             total: curr.Total,
             paymentType: curr.paymentType
          }
        ]
    
        }
       */
        // TODO
        // you can write function to get total from grouped items
    
        const total = getTotal(filteredList)
    
        return (
          <div className="table">
            //you can show the gropued entries here
            {grouped?.map(({ id, total: grpTotal, entries = [] }) => (
              <div key={item.id} className="table-row" >
    

    This code an be optimized for performance and made simpler with functional components.

    Hope it helps