CargoTable.jsx
1 import { useRef, useState, useEffect, useMemo } from "react"; 2 import React from "react"; 3 import styles from "../Form/form.module.scss"; 4 import { useMediaQuery } from "react-responsive"; 5 6 const CargoTable = ({ 7 rows, 8 currencySymbol, 9 onModifyTable, 10 onAddInvoiceRow, 11 onRemoveInvoiceRow, 12 onTableData 13 }) => { 14 const isMobile = useMediaQuery({ query: `(max-width: 760px)` }); 15 const rateRef = useRef([]); 16 const quantityRef = useRef([]); 17 18 const [weight, setWeight] = useState(0); 19 20 const handleWeigthChange = (e) => { 21 const newWeight = parseFloat(e.target.value); 22 setWeight(newWeight); 23 }; 24 25 const calculateAmount = (rate, qty) => { 26 const amount = (parseFloat(rate ? rate : 0) * parseFloat(qty ? qty : 0)).toFixed(2); 27 return amount; 28 }; 29 30 const handleClick = () => { 31 onAddInvoiceRow(); 32 }; 33 34 const handleRemove = (id) => { 35 onRemoveInvoiceRow(id); 36 }; 37 38 const handleChange = (e, item, index) => { 39 let amount; 40 41 if (rateRef?.current[index] && quantityRef?.current[index]) { 42 let rate = Number(rateRef.current[index].value); 43 let quantity = Number(quantityRef.current[index].value); 44 amount = calculateAmount(rate, quantity); 45 } 46 onModifyTable(e, item.id, amount); 47 }; 48 49 const tableRows = useMemo(() => 50 rows.map((item, index) => ( 51 <React.Fragment key={item.id}> 52 {!isMobile && ( 53 <tr className={styles.item__row}> 54 <td className={styles.item__row__actions}> 55 <div className={styles.confirm__delete__button}> 56 <button 57 type="button" 58 title="Remove Item" 59 className={styles.btn__remove} 60 onClick={() => handleRemove(item.id)} 61 > 62 <svg 63 aria-hidden="true" 64 focusable="false" 65 data-prefix="fas" 66 data-icon="times" 67 className={styles.svg__close__icon} 68 role="img" 69 xmlns="http://www.w3.org/2000/svg" 70 viewBox="0 0 352 512" 71 > 72 <path 73 fill="currentColor" 74 d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" 75 ></path> 76 </svg> 77 </button> 78 </div> 79 </td> 80 <td className={styles.description}> 81 <input 82 className={styles.input__default} 83 type="text" 84 name="description" 85 id={`description__${item.id}`} 86 key={`des-input_${item.id}`} 87 placeholder="Product Name" 88 maxLength={30} 89 onChange={(e) => handleChange(e, item)} 90 value={item.description || ""} 91 /> 92 <textarea 93 name="details" 94 id={`details__${item.id}`} 95 key={`details-input_${item.id}`} 96 placeholder="Product Description..." 97 className={`${styles.input__default} ${styles.details}`} 98 onChange={(e) => handleChange(e, item)} 99 value={item.details || ""} 100 ></textarea> 101 </td> 102 <td className={styles.rate}> 103 <input 104 className={styles.input__default} 105 type="number" 106 name="rate" 107 id={`rate__${item.id}`} 108 ref={(el) => (rateRef.current[index] = el)} 109 placeholder="0.00" 110 maxLength={20} 111 key={`rate-input_${item.id}`} 112 onChange={(e) => handleChange(e, item, index)} 113 value={item.rate || ""} 114 /> 115 </td> 116 <td className={styles.qty}> 117 <input 118 className={styles.input__default} 119 type="number" 120 name="quantity" 121 id={`quantity__${item.id}`} 122 ref={(el) => (quantityRef.current[index] = el)} 123 placeholder="0" 124 maxLength={15} 125 key={`qty-input_${item.id}`} 126 onChange={(e) => handleChange(e, item, index)} 127 value={item.quantity || ""} 128 /> 129 </td> 130 <td className={styles.rate}> 131 <input 132 className={styles.input__default} 133 type="number" 134 name="weight" 135 id={`weight__${item.id}`} 136 placeholder="0.00" 137 maxLength={20} 138 key={`weight-input_${item.id}`} 139 onChange={handleWeigthChange} 140 value={weight} 141 /> 142 </td> 143 <td className={styles.amount}> 144 <span>{currencySymbol} </span> 145 <span>{calculateAmount(item.rate, item.quantity, item.id)}</span> 146 </td> 147 {/* <td className={styles.tax}>Tax</td> */} 148 </tr> 149 )} 150 151 {isMobile && ( 152 <div className={styles.item__row}> 153 <div className={styles.description}> 154 <input 155 className={styles.input__default} 156 type="text" 157 name="description" 158 id={`description__${item.id}`} 159 key={`des-input_${item.id}`} 160 placeholder="Product Name" 161 maxLength={20} 162 onChange={(e) => handleChange(e, item)} 163 value={item.description || ""} 164 /> 165 <textarea 166 name="details" 167 id={`details__${item.id}`} 168 key={`details-input_${item.id}`} 169 placeholder="Product Description" 170 className={`${styles.input__default} ${styles.mobile__details}`} 171 onChange={(e) => handleChange(e, item)} 172 value={item.details || ""} 173 ></textarea> 174 </div> 175 <div className={styles.input__group}> 176 <div className={styles.rate}> 177 <input 178 className={styles.input__default} 179 type="number" 180 name="rate" 181 id={`rate__${item.id}`} 182 ref={(el) => (rateRef.current[index] = el)} 183 placeholder="price" 184 maxLength={20} 185 key={`rate-input_${item.id}`} 186 onChange={(e) => handleChange(e, item, index)} 187 value={item.rate || ""} 188 /> 189 </div> 190 <div className={styles.qty}> 191 <input 192 className={styles.input__default} 193 type="number" 194 name="quantity" 195 id={`quantity__${item.id}`} 196 ref={(el) => (quantityRef.current[index] = el)} 197 placeholder="0" 198 maxLength={15} 199 key={`qty-input_${item.id}`} 200 onChange={(e) => handleChange(e, item, index)} 201 value={item.quantity || ""} 202 /> 203 </div> 204 </div> 205 206 <div className={styles.item__row__actions__mobile}> 207 <div className={styles.confirm__delete__button}> 208 <button 209 type="button" 210 title="Remove Item" 211 className={styles.btn__remove} 212 onClick={() => handleRemove(item.id)} 213 > 214 <svg 215 aria-hidden="true" 216 focusable="false" 217 data-prefix="fas" 218 data-icon="times" 219 className={styles.svg__close__icon} 220 role="img" 221 xmlns="http://www.w3.org/2000/svg" 222 viewBox="0 0 352 512" 223 > 224 <path 225 fill="currentColor" 226 d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" 227 ></path> 228 </svg> 229 <span className={styles.btn__text}>Delete</span> 230 </button> 231 </div> 232 <div className={styles.amount}> 233 <span>{currencySymbol} </span> 234 <span> 235 {calculateAmount(item.rate, item.quantity, item.id)} 236 </span> 237 </div> 238 </div> 239 </div> 240 )} 241 </React.Fragment> 242 )) 243 , [rows, isMobile, onModifyTable]); 244 245 useEffect(() => { 246 const updatedTableData = rows.map((item) => ({ 247 productId: item ? item.id : null, 248 productName: item ? item.description : null, 249 productDescription: item ? item.details : null, 250 rate: item ? item.rate : null, 251 quantity: item ? item.quantity : null, 252 weight: weight, 253 })); 254 onTableData(updatedTableData); 255 }, [rows, weight, onTableData]); 256 257 return ( 258 <div className={styles.table__wrapper}> 259 {!isMobile && ( 260 <table className={styles.table}> 261 <thead> 262 <tr className={styles.invoice__headers}> 263 <th className={styles.controls}> </th> 264 <th className={styles.description}>Description</th> 265 <th className={styles.rate}>Rate</th> 266 <th className={styles.qty}>Qty</th> 267 <th className={styles.rate}>Weight(Kg)</th> 268 <th className={styles.amount}>Amount</th> 269 {/* <th className={styles.tax}>Tax</th> */} 270 </tr> 271 </thead> 272 <tbody className={styles.invoice__items}> 273 <>{tableRows}</> 274 <tr className={styles.item__row}> 275 <td className={styles.item__row__actions}> 276 <button 277 type="button" 278 onClick={handleClick} 279 className={`${styles.add__invoice__item} ${styles.btn__add}`} 280 > 281 <svg 282 aria-hidden="true" 283 focusable="false" 284 data-prefix="fas" 285 data-icon="plus" 286 role="img" 287 xmlns="http://www.w3.org/2000/svg" 288 viewBox="0 0 448 512" 289 > 290 <path 291 fill="#ffffff" 292 d="M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z" 293 ></path> 294 </svg> 295 </button> 296 </td> 297 </tr> 298 </tbody> 299 </table> 300 )} 301 {isMobile && ( 302 <div className={styles.mobile__section}> 303 <>{tableRows}</> 304 <button 305 type="button" 306 onClick={handleClick} 307 className={`${styles.add__invoice__item} ${styles.btn__add}`} 308 > 309 <svg 310 aria-hidden="true" 311 focusable="false" 312 data-prefix="fas" 313 data-icon="plus" 314 role="img" 315 xmlns="http://www.w3.org/2000/svg" 316 viewBox="0 0 448 512" 317 > 318 <path 319 fill="#ffffff" 320 d="M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z" 321 ></path> 322 </svg> 323 </button> 324 </div> 325 )} 326 </div> 327 ); 328 }; 329 330 export default CargoTable;