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