From 2dd0d549381d98bf48312780b227ac35f31cbdc9 Mon Sep 17 00:00:00 2001 From: Rizqika Date: Thu, 14 Nov 2024 09:42:32 +0700 Subject: [PATCH] Improve code --- package-lock.json | 9 + package.json | 1 + .../FaceRecognition/Section/Enroll.jsx | 65 ++- .../FaceRecognition/Section/Verify.jsx | 436 +++++++++-------- .../Biometric/FaceRecognition/Transaction.jsx | 448 +++++++++++++++++- 5 files changed, 699 insertions(+), 260 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4577617..a7c2c54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-drag-drop-files": "^2.4.0", + "react-icons": "^5.3.0", "react-router-dom": "^6.28.0", "react-scripts": "^5.0.1", "react-select": "^5.8.2", @@ -13472,6 +13473,14 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-icons": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", + "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index 7cc0f84..ba400aa 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-drag-drop-files": "^2.4.0", + "react-icons": "^5.3.0", "react-router-dom": "^6.28.0", "react-scripts": "^5.0.1", "react-select": "^5.8.2", diff --git a/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx b/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx index a50fcec..3c7bd55 100644 --- a/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx +++ b/src/screens/Biometric/FaceRecognition/Section/Enroll.jsx @@ -14,6 +14,7 @@ const Enroll = () => { const [errorMessage, setErrorMessage] = useState(''); const [selectedImageName, setSelectedImageName] = useState(''); + const [resultImageLabel, setresultImageLabel] = useState(""); const fileInputRef = useRef(null); const [showResult, setShowResult] = useState(false); const [applicationId, setApplicationId] = useState(''); @@ -102,7 +103,7 @@ const Enroll = () => { const fetchSubjectIds = async (appId) => { setIsLoading(true); try { - const response = await fetch(`${BASE_URL}/trx_face/list/subject?application_id=${appId}&search=${subjectId}&limit=10`, { + const response = await fetch(`${BASE_URL}/trx_face/list/subject?application_id=${appId}&search=${subjectId}&limit=99`, { method: 'GET', headers: { 'accept': 'application/json', @@ -153,15 +154,15 @@ const Enroll = () => { }; const handleEnrollClick = async () => { - let hasError = false; // Track if there are any errors - + let hasError = false; + // Validate inputs and set corresponding errors const validationErrors = { imageError: !selectedImageName ? 'Please upload a face photo before enrolling.' : '', applicationError: !applicationId ? 'Please select an Application ID before enrolling.' : '', subjectError: !subjectId ? 'Please enter a Subject ID before enrolling.' : '', }; - + // Update state with errors if (validationErrors.imageError) { setImageError(validationErrors.imageError); @@ -169,43 +170,43 @@ const Enroll = () => { } else { setImageError(''); // Clear error if valid } - + if (validationErrors.applicationError) { setApplicationError(validationErrors.applicationError); hasError = true; } else { setApplicationError(''); // Clear error if valid } - + if (validationErrors.subjectError) { setSubjectError(validationErrors.subjectError); hasError = true; } else { setSubjectError(''); // Clear error if valid } - + // If there are errors, return early if (hasError) return; - + if (!file) { setImageError('No file selected. Please upload a valid image file.'); return; } - + const formData = new FormData(); formData.append('application_id', String(applicationId)); formData.append('subject_id', subjectId); formData.append('file', file); - + console.log('Inputs:', { applicationId, subjectId, file: file.name, }); - + setIsLoading(true); setErrorMessage(''); // Clear previous error message - + try { const response = await fetch(`${BASE_URL}/face_recognition/enroll`, { method: 'POST', @@ -215,31 +216,33 @@ const Enroll = () => { 'x-api-key': `${API_KEY}`, } }); - + if (!response.ok) { const errorDetails = await response.json(); console.error('Response error details:', errorDetails); - // Periksa jika detail error terkait dengan Subject ID if (errorDetails.detail && errorDetails.detail.includes('Subject ID')) { - setSubjectError(errorDetails.detail); // Tampilkan error di bawah input Subject ID + setSubjectError(errorDetails.detail); } else { setErrorMessage(errorDetails.detail || 'Failed to enroll, please try again'); - } + } return; } - + const result = await response.json(); console.log('Enrollment response:', result); - + if (result.details && result.details.data && result.details.data.image_url) { const imageFileName = result.details.data.image_url.split('/').pop(); console.log('Image URL:', result.details.data.image_url); await fetchImage(imageFileName); + + // Set resultImageLabel after successful enrollment + setresultImageLabel(selectedImageName); // Set resultImageLabel after success } else { console.error('Image URL not found in response:', result); setErrorMessage('Image URL not found in response. Please try again.'); } - + setShowResult(true); console.log('Enrollment successful:', result); } catch (error) { @@ -248,10 +251,9 @@ const Enroll = () => { } finally { setIsLoading(false); } - }; - - const fetchImage = async (imageFileName) => { + }; + const fetchImage = async (imageFileName) => { setIsLoading(true); try { const response = await fetch(`${BASE_URL}/preview/image/${imageFileName}`, { @@ -261,30 +263,26 @@ const Enroll = () => { 'x-api-key': API_KEY, } }); - + if (!response.ok) { const errorDetails = await response.json(); console.error('Image fetch error details:', errorDetails); setErrorMessage('Failed to fetch image, please try again.'); return; } - - + const imageBlob = await response.blob(); - const imageData = URL.createObjectURL(imageBlob); + const imageData = URL.createObjectURL(imageBlob); console.log('Fetched image URL:', imageData); - - - - setImageUrl(imageData); - + + setImageUrl(imageData); } catch (error) { console.error('Error fetching image:', error); setErrorMessage(error.message); } finally { setIsLoading(false); } - }; + }; const CustomLabel = ({ overRide, children, ...props }) => { // We intentionally don't pass `overRide` to the label @@ -772,7 +770,7 @@ const Enroll = () => { style={isMobile ? styles.imageStyleMobile : styles.imageStyle} />

- {selectedImageName} + {resultImageLabel} {/* Display resultImageLabel instead of selectedImageName */}

@@ -782,6 +780,7 @@ const Enroll = () => { ); } + export default Enroll; diff --git a/src/screens/Biometric/FaceRecognition/Section/Verify.jsx b/src/screens/Biometric/FaceRecognition/Section/Verify.jsx index 11ec051..43ac7a8 100644 --- a/src/screens/Biometric/FaceRecognition/Section/Verify.jsx +++ b/src/screens/Biometric/FaceRecognition/Section/Verify.jsx @@ -1,6 +1,6 @@ import React, { useState, useRef, useEffect } from 'react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faChevronLeft, faChevronDown, faTimes, faImage } from '@fortawesome/free-solid-svg-icons'; +import { faChevronDown, faTimes } from '@fortawesome/free-solid-svg-icons'; import { FileUploader } from 'react-drag-drop-files'; import Select from 'react-select' @@ -11,13 +11,13 @@ const Verify = () => { const fileTypes = ["JPG", "JPEG", "PNG"]; const [file, setFile] = useState(null); - const [isSelectOpen, setIsSelectOpen] = useState(false); const [errorMessage, setErrorMessage] = useState(''); const [uploadError, setUploadError] = useState(''); const [applicationError, setApplicationError] = useState(''); const [subjectError, setSubjectError] = useState(''); const [thresholdError, setThresholdError] = useState(''); const [selectedImageName, setSelectedImageName] = useState(''); + const [resultImageLabel, setResultImageLabel] = useState(''); const fileInputRef = useRef(null); const [showResult, setShowResult] = useState(false); const [applicationId, setApplicationId] = useState(''); @@ -39,103 +39,105 @@ const Verify = () => { { id: 3, name: 'euclidean_l2', displayName: 'High' }, ]; - const options = subjectIds.map(id => ({ value: id, label: id })); const [inputValue, setInputValue] = useState(''); + + const options = subjectIds.map(id => ({ + value: id, + label: id + })); + const applicationOptions = applicationIds.map(app => ({ value: app.id, label: app.name })); - useEffect(() => { - const fetchApplicationIds = async () => { - try { - setIsLoading(true); - const url = `${BASE_URL}/application/list`; - const response = await fetch(url, { - method: 'GET', - headers: { - 'accept': 'application/json', - 'x-api-key': `${API_KEY}`, - }, - }); - const data = await response.json(); - if (data.status_code === 200) { - setApplicationIds(data.details.data); - } else { - console.error('Failed to fetch data:', data.details.message); - } - } catch (error) { - console.error('Error fetching application IDs:', error); - } finally { - setIsLoading(false); - } + const fetchApplicationIds = async () => { + try { + const url = `${BASE_URL}/application/list`; + const response = await fetch(url, { + method: 'GET', + headers: { + 'accept': 'application/json', + 'x-api-key': `${API_KEY}`, + }, + }); + const data = await response.json(); + return data.details.data; // assuming the API returns an array of applications + } catch (error) { + console.error('Error fetching application IDs:', error); + return []; + } + }; + + // Sample function to fetch Subject IDs based on applicationId + const fetchSubjectIds = async (appId) => { + try { + const response = await fetch(`${BASE_URL}/trx_face/list/subject?application_id=${appId}&limit=99`, { + method: 'GET', + headers: { + 'accept': 'application/json', + 'x-api-key': `${API_KEY}`, + }, + }); + const data = await response.json(); + return data.details.data; // assuming the API returns an array of subjects + } catch (error) { + console.error('Error fetching subject IDs:', error); + return []; + } + }; + + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + const data = await fetchApplicationIds(); + setApplicationIds(data); + setIsLoading(false); }; - fetchApplicationIds(); + fetchData(); const handleResize = () => setIsMobile(window.innerWidth <= 768); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); - }, []); + }, []); // Empty dependency array, so this runs only once when the component mounts - const handleApplicationChange = async (selectedOption) => { - if (!selectedOption) { - console.error("Selected option is undefined"); - return; - } - - const selectedId = selectedOption.value; - const selectedApp = applicationIds.find(app => app.id === parseInt(selectedId)); - - if (selectedApp) { - setSelectedQuota(selectedApp.quota); - } - - setApplicationId(selectedId); - - if (selectedId) { - await fetchSubjectIds(selectedId); - } else { - setSubjectIds([]); - setSubjectAvailabilityMessage(''); - } - }; - - - const fetchSubjectIds = async (appId) => { - setIsLoading(true); - try { - const response = await fetch(`${BASE_URL}/trx_face/list/subject?application_id=${appId}&search=${subjectId}&limit=99`, { - method: 'GET', - headers: { - 'accept': 'application/json', - 'x-api-key': API_KEY, - }, - }); - - const data = await response.json(); - console.log("Fetched Subject IDs:", data); // Log data fetched from API - - if (data.status_code === 200) { - setSubjectIds(data.details.data); - } else { - console.error('Failed to fetch subject IDs:', data.details.message); - } - } catch (error) { - console.error('Error fetching subject IDs:', error); - } finally { + // Fetch Subject IDs when applicationId changes + useEffect(() => { + const fetchSubjects = async () => { + if (applicationId) { + setIsLoading(true); + const subjects = await fetchSubjectIds(applicationId); + setSubjectIds(subjects); setIsLoading(false); - } + } else { + setSubjectIds([]); // Clear subjects if no applicationId is selected + } + }; + + fetchSubjects(); + }, [applicationId]); // Runs whenever applicationId changes + + // Handler for changing applicationId + const handleApplicationChange = (selectedOption) => { + const selectedId = selectedOption.value; + const selectedApp = applicationIds.find(app => app.id === parseInt(selectedId)); + + if (selectedApp) { + setSelectedQuota(selectedApp.quota); + } + + setApplicationId(selectedOption.value); // Update applicationId when user selects a new option }; + const handleImageUpload = (file) => { - // Ensure the file is not undefined or null before accessing its properties if (file && file.name) { const fileExtension = file.name.split('.').pop().toUpperCase(); if (fileTypes.includes(fileExtension)) { setSelectedImageName(file.name); setFile(file); - setUploadError(''); // Clear any previous errors + setUploadError(''); } else { alert('Image format is not supported'); setUploadError('Image format is not supported'); @@ -144,7 +146,7 @@ const Verify = () => { } else { console.error('No file selected or invalid file object.'); } - }; + }; const handleImageCancel = () => { @@ -165,23 +167,26 @@ const Verify = () => { let hasError = false; // Track if any errors occur + // Validate the applicationId if (!applicationId) { setApplicationError('Please select an Application ID before enrolling.'); hasError = true; // Mark that an error occurred } + // Validate the subjectId if (!subjectId) { setSubjectError('Please enter a Subject ID before enrolling.'); hasError = true; // Mark that an error occurred } + // Validate thresholdId const selectedThreshold = thresholdIds.find(threshold => threshold.name === thresholdId)?.name; - if (!selectedThreshold) { setThresholdError('Invalid threshold selected.'); hasError = true; // Mark that an error occurred } + // Validate image upload if (!selectedImageName) { setUploadError('Please upload a face photo before verifying.'); hasError = true; // Mark that an error occurred @@ -192,18 +197,18 @@ const Verify = () => { return; } - // Log the input values + // Log the input values for debugging console.log('Selected Image Name:', selectedImageName); console.log('Application ID:', applicationId); console.log('Subject ID:', subjectId); console.log('Selected Threshold:', selectedThreshold); + // Prepare FormData for the API request const formData = new FormData(); formData.append('application_id', applicationId); formData.append('threshold', selectedThreshold); formData.append('subject_id', subjectId); - // const file = fileInputRef.current.files[0]; if (file) { formData.append('file', file, file.name); } else { @@ -218,7 +223,7 @@ const Verify = () => { method: 'POST', headers: { 'accept': 'application/json', - 'x-api-key': `${API_KEY}`, + 'x-api-key': API_KEY, }, body: formData, }); @@ -233,6 +238,7 @@ const Verify = () => { setShowResult(true); setVerified(data.details.data.result.verified); + setResultImageLabel(selectedImageName); } else { const errorMessage = data.message || data.detail || data.details?.message || 'An unknown error occurred.'; setErrorMessage(errorMessage); @@ -245,7 +251,6 @@ const Verify = () => { } }; - const fetchImage = async (imageFileName) => { setIsLoading(true); try { @@ -603,178 +608,171 @@ const Verify = () => { {isLoading && (
-
-

Loading...

+
+

Loading...

)} {/* Application ID Selection */}
-
- option.value === subjectId)} + onChange={(selectedOption) => setSubjectId(selectedOption ? selectedOption.value : '')} + options={options} + inputValue={inputValue} + onInputChange={(newInputValue) => { + if (newInputValue.length <= 15) { // Limit the input length + setInputValue(newInputValue); + } + }} + /> + + {subjectError && {subjectError}} + {subjectAvailabilityMessage && ( + + {subjectAvailabilityMessage} + + )}
-

- Remaining Quota -

-
- {selectedQuota} {/* Display selected quota */} - (times) +
+ + + {thresholdError && {thresholdError}}
- {/* Subject ID Input */} -
+ {/* Upload Section */}
- { - setThresholdId(e.target.value); - setThresholdError(''); // Clear error if valid - }} - > - - {thresholdIds.map((app) => ( - - ))} - - - {thresholdError && {thresholdError}} + {uploadError && {uploadError}}
-
+ + {/* Display uploaded image name */} + {selectedImageName && ( +
+

File: {selectedImageName}

+ {file && ( +

+ Size: {formatFileSize(file.size)} +

+ )} + +
+ )} + {errorMessage && {errorMessage}} - {/* Upload Section */} -
-
- - Upload Face Photo - - handleImageUpload(files[0])} - children={ -
- -

Drag and Drop Here

-

Or

- fileInputRef.current.click()}>Browse -

Recommended size: 300x300 (Max File Size: 2MB)

-

Supported file types: JPG, JPEG

-
- } - /> - {uploadError && {uploadError}} -
-
- - {/* Display uploaded image name */} - {selectedImageName && ( -
-

File: {selectedImageName}

- {file && ( -

- Size: {formatFileSize(file.size)} -

- )} -
- )} - {errorMessage && {errorMessage}} - {/* Submit Button */} -
- -
- {/* Results Section */} - {showResult && ( -
-

Results

-
- - - - - - - -
Similarity - {verified !== null ? (verified ? 'True' : 'False') : 'N/A'} -
+ {/* Results Section */} + {showResult && ( +
+

Results

+
+ + + + + + + +
Similarity + {verified !== null ? (verified ? 'True' : 'False') : 'N/A'} +
-
- Example Image -

- File Name: {selectedImageName} -

+
+ Example Image +

+ File Name: {resultImageLabel} {/* Display the resultImageLabel here */} +

+
-
- )} + )}
); diff --git a/src/screens/Biometric/FaceRecognition/Transaction.jsx b/src/screens/Biometric/FaceRecognition/Transaction.jsx index f106c94..3976893 100644 --- a/src/screens/Biometric/FaceRecognition/Transaction.jsx +++ b/src/screens/Biometric/FaceRecognition/Transaction.jsx @@ -1,11 +1,443 @@ -import React from 'react' +import React, { useState, useEffect } from 'react'; +import { FaChevronLeft, FaChevronRight, FaFastBackward, FaFastForward, FaSort, FaSortUp, FaSortDown } from 'react-icons/fa'; // Icons for sorting +import { NoAvailable } from '../../../assets/icon'; + +// Pagination Component +const Pagination = ({ currentPage, totalPages, onPageChange }) => { + const handlePrev = () => { + if (currentPage > 1) { + onPageChange(currentPage - 1); + } + }; + + const handleNext = () => { + if (currentPage < totalPages) { + onPageChange(currentPage + 1); + } + }; + + const handleFirst = () => { + onPageChange(1); // Go to first page + }; + + const handleLast = () => { + onPageChange(totalPages); // Go to last page + }; + + // Logic to display only 3 pages in pagination + const getPaginationRange = () => { + const range = []; + const totalPagesCount = totalPages; + + let start = currentPage - 1; + let end = currentPage + 1; + + // Adjust start and end if near the boundaries + if (currentPage === 1) { + start = 1; + end = Math.min(3, totalPagesCount); + } else if (currentPage === totalPages) { + start = Math.max(totalPagesCount - 2, 1); + end = totalPagesCount; + } + + for (let i = start; i <= end; i++) { + range.push(i); + } + + return range; + }; + + const pageRange = getPaginationRange(); + + return ( +
+ {/* First Page Button */} + + + + + {/* Page Numbers */} + {pageRange.map((pageNum) => ( + + ))} + + + + {/* Last Page Button */} + +
+ ); +}; const Transaction = () => { - return ( -
-

Transaction Logs

-
- ) -} + const [currentPage, setCurrentPage] = useState(1); + const [isMobile, setIsMobile] = useState(false); // State to detect mobile view + const [transactionData, setTransactionData] = useState([]); + const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); // Sorting state + const dataPerPage = 10; // Data per page (10 data per page) + + const buttonData = [ + { label: 'Copy', enabled: true }, + { label: 'CSV', enabled: true }, + { label: 'Excel', enabled: true }, + { label: 'PDF', enabled: true }, + { label: 'Print', enabled: true }, + { label: 'Column Visibility', enabled: true }, + ]; + + + // Generate 691 dummy transactions + const generateDummyData = (numOfItems) => { + const transactionData = []; + + for (let i = 1; i <= numOfItems; i++) { + transactionData.push({ + transactionId: `TX${String(i).padStart(3, '0')}`, + applicationName: `App ${Math.floor(Math.random() * 5) + 1}`, + dataSent: `${Math.floor(Math.random() * 100) + 50}MB`, + endPoint: `Endpoint ${Math.floor(Math.random() * 5) + 1}`, + subjectId: `S${String(i).padStart(3, '0')}`, + serviceCharged: `$${(Math.random() * 50 + 5).toFixed(2)}`, + mode: Math.random() > 0.5 ? 'Online' : 'Offline', + status: ['Completed', 'Pending', 'Failed'][Math.floor(Math.random() * 3)], + }); + } + + return transactionData; + }; + + // Set the generated transaction data + useEffect(() => { + setTransactionData(generateDummyData(97513)); // count data dummy transactions + }, []); + + // Sorting function + const sortData = (data, config) => { + const { key, direction } = config; + return [...data].sort((a, b) => { + if (a[key] < b[key]) { + return direction === 'asc' ? -1 : 1; + } + if (a[key] > b[key]) { + return direction === 'asc' ? 1 : -1; + } + return 0; + }); + }; + + // Handle column header sort click + const handleSort = (key) => { + let direction = 'asc'; + if (sortConfig.key === key && sortConfig.direction === 'asc') { + direction = 'desc'; // Toggle direction if the same column is clicked + } + setSortConfig({ key, direction }); + }; + + // Get the paginated data + const getPaginatedData = (data, page, perPage) => { + const sortedData = sortData(data, sortConfig); + const startIndex = (page - 1) * perPage; + const endIndex = startIndex + perPage; + return sortedData.slice(startIndex, endIndex); + }; + + // Handle page change + const handlePageChange = (page) => { + setCurrentPage(page); + }; + + // Calculate total pages based on the data and data per page + const totalPages = Math.ceil(transactionData.length / dataPerPage); + + // Paginated data + const paginatedData = getPaginatedData(transactionData, currentPage, dataPerPage); + + // Detect screen size and update isMobile state + useEffect(() => { + const handleResize = () => { + setIsMobile(window.innerWidth <= 768); // Change 768 to your breakpoint + }; + + handleResize(); + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + return ( +
+ {/* Welcome Message */} +
+
+
+

+ Alert +

+

+ Get started now by creating an Application ID and explore all the demo services available on the dashboard. + Experience the ease and flexibility of trying out all our features firsthand. +

+
+
+
+ +
+ {/* Filter Form */} +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + {/* Action Buttons */} +
+
+ {buttonData.map((button, index) => + button.enabled ? ( + + ) : null + )} +
+ + {/* Search Bar with Icon */} +
+ + + {/* FontAwesome search icon */} + +
+
+ + + {/* Table */} +
+ + + + {/* Kolom untuk Nomor Urut */} + + + + + + + + + + + + + {paginatedData.length > 0 ? ( + paginatedData.map((transaction, index) => ( + + {/* Kolom Nomor Urut */} + {/* Nomor urut berdasarkan halaman dan index */} + + + + + + + + + + + )) + ) : ( + + + + )} + + +
No. + + + + + + + + + + + + + + + +
{(currentPage - 1) * dataPerPage + index + 1}{transaction.transactionId}{transaction.applicationName}{transaction.dataSent}{transaction.endPoint}{transaction.subjectId}{transaction.serviceCharged}{transaction.mode}{transaction.status}
+
+ No Data Available +

Data not available

+
+
+
+ + {/* Pagination */} + +
+
+ ); +}; + +export default Transaction; + +const styles = { + contentContainer: { + padding: '20px', + border: '0.1px solid rgba(0, 0, 0, 0.2)', + borderLeft: '4px solid #0542cc', + borderRadius: '10px', + width: '100%', + }, + tableContainer: { + minHeight: '300px', + maxHeight: '1500px', + overflowY: 'auto', + }, + iconStyle: { + width: '50px', + height: '50px', + }, + // Add margin-left style for icons + iconMarginLeft: { + marginLeft: '0.7rem', // Adjust as needed + }, +}; -export default Transaction