|
1 | | -import React, { useState } from 'react'; |
| 1 | +import React, { useState, useEffect } from 'react'; |
| 2 | +import SearchBox from './components/SearchBox'; |
| 3 | +import { NETWORK_ICON_MAP, NETWORK_LIST } from '@/components/common/constants'; |
| 4 | +import { getLatestBundles, getLatestUserOps, getTopBundlers, getTopPaymasters } from '@/components/common/apiCalls/jiffyApis'; |
| 5 | +import { ToastContainer, toast } from 'react-toastify'; |
| 6 | +import 'react-toastify/dist/ReactToastify.css'; |
| 7 | +import Table, { tableDataT, tableDataTCollection } from '@/components/common/table/Table'; |
| 8 | +import { getFee, getTimePassed } from '@/components/common/utils'; |
| 9 | +import BundlesTable from '../home/bundles_table.json'; |
| 10 | +import BundlersTable from '../home/bundlers_table.json'; |
| 11 | +import PaymastersTable from '../home/paymasters_table.json'; |
| 12 | +import OperationsTable from '../home/operations_table.json'; |
2 | 13 |
|
3 | 14 | const SearchBar = () => { |
4 | | - const [isDropdownOpen, setIsDropdownOpen] = useState(false); |
| 15 | + const [activeTable, setActiveTable] = useState<string | null>('Bundles'); |
| 16 | + const [bundlesData, setBundlesData] = useState<any[]>([]); |
| 17 | + const [userOpsData, setUserOpsData] = useState<any[]>([]); |
| 18 | + const [bundlersData, setBundlersData] = useState<any[]>([]); |
| 19 | + const [paymastersData, setPaymastersData] = useState<any[]>([]); |
| 20 | + const [loading, setLoading] = useState(false); |
| 21 | + const [networkValue, setNetworkValue] = useState<number>(-1); |
| 22 | + const [networkName, setNetworkName] = useState<string>('mainnet'); |
5 | 23 |
|
6 | | - const handleDropdownToggle = () => { |
7 | | - setIsDropdownOpen(!isDropdownOpen); |
| 24 | + const [block, setBlock] = useState(false); |
| 25 | + const [triggerBlock, setTriggerBlock] = useState(false); |
| 26 | + const [bundlesTables, setBundlesTables] = useState<tableDataTCollection>({ |
| 27 | + ['mainnet']: BundlesTable, |
| 28 | + } as unknown as tableDataTCollection); |
| 29 | + const [operationsTables, setOperationsTables] = useState<tableDataTCollection>({ |
| 30 | + [networkValue]: OperationsTable, |
| 31 | + } as tableDataTCollection); |
| 32 | + const [bundlersTables, setBundlersTables] = useState<tableDataTCollection>({ |
| 33 | + [networkValue]: BundlersTable, |
| 34 | + } as tableDataTCollection); |
| 35 | + const [paymastersTables, setPaymastersTables] = useState<tableDataTCollection>({ |
| 36 | + [networkValue]: PaymastersTable, |
| 37 | + } as tableDataTCollection); |
| 38 | + const [userOpTableLoading, setUserOpTableLoading] = useState(true); |
| 39 | + const [bundleTableLoading, setBundleTableLoading] = useState(true); |
| 40 | + const [bundlerTableLoading, setBundlerTableLoading] = useState(true); |
| 41 | + const [paymasterTableLoading, setPaymasterTableLoading] = useState(true); |
| 42 | + const [badgeValue, setBadgeValue] = useState('Bundles'); |
| 43 | + |
| 44 | + const refreshBundlesTable = async (network: string) => { |
| 45 | + setBundleTableLoading(true); |
| 46 | + const bundles = await getLatestBundles(network, 5, 0, toast); |
| 47 | + console.log(bundles); |
| 48 | + let newRows = [] as tableDataT['rows']; |
| 49 | + bundles.forEach((bundle) => { |
| 50 | + newRows.push({ |
| 51 | + token: { |
| 52 | + text: bundle.transactionHash, |
| 53 | + icon: NETWORK_ICON_MAP[network], |
| 54 | + type: 'bundle', |
| 55 | + }, |
| 56 | + ago: getTimePassed(bundle.blockTime), |
| 57 | + userOps: bundle.userOpsLength + ' ops', |
| 58 | + status: true, |
| 59 | + }); |
| 60 | + }); |
| 61 | + bundlesTables[network] = { ...bundlesTables[network], rows: newRows.slice(0, 5) }; |
| 62 | + setBundlesTables(bundlesTables); |
| 63 | + // console.log("bundlesTables",{ ...bundlesTables[network], rows: newRows.slice(0, 5) }) |
| 64 | + setBundleTableLoading(false); |
8 | 65 | }; |
9 | 66 |
|
10 | | - return ( |
11 | | - <form className="w-full max-w-[1080px] h-16 mx-auto mt-12 md:mt-48 px-4 md:px-0"> |
12 | | - <label htmlFor="default-search" className="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white"> |
13 | | - Search |
14 | | - </label> |
15 | | - <div className="relative"> |
16 | | - <div className="absolute inset-y-0 left-2 flex items-center ps-3 cursor-pointer" onClick={handleDropdownToggle}> |
17 | | - <svg width="20" height="28" viewBox="0 0 20 28" fill="none" xmlns="http://www.w3.org/2000/svg"> |
18 | | - <path |
19 | | - opacity="0.2" |
20 | | - d="M5.86699 27.3333L7.60033 18.2857L0.666992 15.5714L12.8003 2L11.067 11.0476L18.0003 13.7619L5.86699 27.3333Z" |
21 | | - fill="#FFB300" |
22 | | - /> |
23 | | - <path |
24 | | - d="M19.3117 12.9087C19.2796 12.7662 19.2144 12.6342 19.1219 12.5245C19.0294 12.4147 18.9125 12.3306 18.7816 12.2796L12.6729 9.8777L14.2274 1.7272C14.2626 1.53783 14.2381 1.34159 14.1576 1.16811C14.077 0.994621 13.9448 0.853297 13.781 0.765461C13.6171 0.677626 13.4304 0.648046 13.2491 0.681184C13.0678 0.714322 12.9016 0.80838 12.7758 0.949166L0.899971 14.287C0.803517 14.3935 0.733741 14.5234 0.696877 14.6651C0.660013 14.8068 0.657209 14.9559 0.688715 15.099C0.720222 15.2421 0.785058 15.3748 0.877431 15.4853C0.969804 15.5957 1.08684 15.6804 1.21807 15.7319L7.32882 18.1338L5.7786 26.2754C5.7434 26.4648 5.76792 26.661 5.84845 26.8345C5.92898 27.008 6.06116 27.1493 6.22504 27.2371C6.38891 27.325 6.5756 27.3546 6.75692 27.3214C6.93824 27.2883 7.10436 27.1942 7.23021 27.0534L19.106 13.7157C19.2007 13.6091 19.269 13.4798 19.3049 13.3391C19.3408 13.1984 19.3431 13.0506 19.3117 12.9087ZM8.02759 23.56L9.13776 17.7381C9.1775 17.5316 9.14601 17.3171 9.04892 17.1326C8.95183 16.9482 8.79543 16.8059 8.60759 16.7311L3.00475 14.5248L11.9774 4.44811L10.8682 10.2701C10.8285 10.4765 10.86 10.6911 10.9571 10.8755C11.0542 11.0599 11.2106 11.2022 11.3984 11.2771L16.997 13.4778L8.02759 23.56Z" |
25 | | - fill="#FFB300" |
| 67 | + const refreshPaymastersTable = async (network: string) => { |
| 68 | + setPaymasterTableLoading(true); |
| 69 | + const paymasters = await getTopPaymasters(network, 5, 0, toast); |
| 70 | + let newRows: tableDataT['rows'] = []; |
| 71 | + paymasters.forEach((paymaster) => { |
| 72 | + newRows.push({ |
| 73 | + token: { |
| 74 | + text: paymaster.address, |
| 75 | + icon: NETWORK_ICON_MAP[network], |
| 76 | + type: 'paymaster', |
| 77 | + }, |
| 78 | + userOps: `${paymaster.userOpsLength} ops`, |
| 79 | + fee: getFee(parseInt(paymaster.gasSponsored), network), |
| 80 | + }); |
| 81 | + }); |
| 82 | + paymastersTables[network] = { ...paymastersTables[network], rows: newRows.slice(0, 10) }; |
| 83 | + setPaymastersTables(paymastersTables); |
| 84 | + setPaymasterTableLoading(false); |
| 85 | + }; |
| 86 | + |
| 87 | + const refreshBundlersTable = async (network: string) => { |
| 88 | + setBundlerTableLoading(true); |
| 89 | + const bundlers = await getTopBundlers(network, 5, 0, toast); |
| 90 | + let newRows: tableDataT['rows'] = []; |
| 91 | + bundlers.forEach((bundler) => { |
| 92 | + newRows.push({ |
| 93 | + token: { |
| 94 | + text: bundler.address, |
| 95 | + icon: NETWORK_ICON_MAP[network], |
| 96 | + type: 'bundler', |
| 97 | + }, |
| 98 | + userOps: `${bundler.bundleLength} bundles`, |
| 99 | + fee: getFee(parseInt(bundler.actualGasCostSum), network), |
| 100 | + }); |
| 101 | + }); |
| 102 | + bundlersTables[network] = { ...bundlersTables[network], rows: newRows.slice(0, 5) }; |
| 103 | + setBundlersTables(bundlersTables); |
| 104 | + // console.log('bundlersTables',{ ...bundlersTables[network], rows: newRows.slice(0, 5) }) |
| 105 | + setBundlerTableLoading(false); |
| 106 | + }; |
| 107 | + |
| 108 | + const refreshUserOpsTable = async (network: string) => { |
| 109 | + setUserOpTableLoading(true); |
| 110 | + const userOps = await getLatestUserOps(network, 5, 0, toast); |
| 111 | + let newRows = [] as tableDataT['rows']; |
| 112 | + userOps.forEach((userOp) => { |
| 113 | + newRows.push({ |
| 114 | + token: { |
| 115 | + text: userOp.userOpHash, |
| 116 | + icon: NETWORK_ICON_MAP[network], |
| 117 | + type: 'userOp', |
| 118 | + }, |
| 119 | + ago: getTimePassed(userOp.blockTime!), |
| 120 | + sender: userOp.sender, |
| 121 | + target: userOp.target!, |
| 122 | + status: userOp.success!, |
| 123 | + }); |
| 124 | + }); |
| 125 | + setLoading(false); |
| 126 | + operationsTables[network] = { ...operationsTables[network], rows: newRows.slice(0, 5) }; |
| 127 | + setOperationsTables(operationsTables); |
| 128 | + setUserOpTableLoading(false); |
| 129 | + }; |
| 130 | + |
| 131 | + useEffect(() => { |
| 132 | + const networkName = networkValue !== -1 ? NETWORK_LIST[networkValue].key : 'mainnet'; |
| 133 | + switch (badgeValue) { |
| 134 | + case 'Bundles': |
| 135 | + refreshBundlesTable(networkName); |
| 136 | + break; |
| 137 | + case 'UserOperations': |
| 138 | + refreshUserOpsTable(networkName); |
| 139 | + break; |
| 140 | + case 'Bundlers': |
| 141 | + refreshBundlersTable(networkName); |
| 142 | + break; |
| 143 | + case 'Paymaster': |
| 144 | + refreshPaymastersTable(networkName); |
| 145 | + break; |
| 146 | + default: |
| 147 | + break; |
| 148 | + } |
| 149 | + }, [networkValue, badgeValue]); |
| 150 | + |
| 151 | + const renderTable = () => { |
| 152 | + switch (activeTable) { |
| 153 | + case 'Bundles': |
| 154 | + return ( |
| 155 | + <div style={{ padding: '0px 16px 16px 16px' }}> |
| 156 | + <Table |
| 157 | + {...(bundlesTables[networkValue !== -1 ? NETWORK_LIST[networkValue].key : 'mainnet'] as tableDataT)} |
| 158 | + columns={BundlesTable['columns']} |
| 159 | + loading={bundleTableLoading} |
| 160 | + key="recentBundlesTable" |
26 | 161 | /> |
27 | | - </svg> |
28 | | - <svg className={`ml-2 mr-2 transition-transform duration-200 ${ |
29 | | - isDropdownOpen ? 'transform rotate-180' : '' |
30 | | - }`} width="12" height="8" viewBox="0 0 12 8" fill="none" xmlns="http://www.w3.org/2000/svg"> |
31 | | - <path d="M1.41 0.578125L6 5.16813L10.59 0.578125L12 1.99813L6 7.99813L0 1.99813L1.41 0.578125Z" fill="#9E9E9E" /> |
32 | | - </svg> |
33 | | - <div className="border-r-2 h-6 mx-2"></div> |
34 | | - </div> |
35 | | - |
36 | | - {isDropdownOpen && ( |
37 | | - <div className="absolute top-full left-0 mt-2 bg-white divide-y divide-gray-100 rounded-lg shadow w-44 dark:bg-gray-700"> |
38 | | - <ul className="py-2 text-sm text-gray-700 dark:text-gray-200" aria-labelledby="dropdown"> |
39 | | - <li> |
40 | | - <a href="#" className="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> |
41 | | - Option 1 |
42 | | - </a> |
43 | | - </li> |
44 | | - <li> |
45 | | - <a href="#" className="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> |
46 | | - Option 2 |
47 | | - </a> |
48 | | - </li> |
49 | | - <li> |
50 | | - <a href="#" className="block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"> |
51 | | - Option 3 |
52 | | - </a> |
53 | | - </li> |
54 | | - </ul> |
55 | 162 | </div> |
56 | | - )} |
57 | | - |
58 | | - <input |
59 | | - type="search" |
60 | | - id="default-search" |
61 | | - className="block w-full p-4 pl-24 text-sm md:text-base text-gray-900 border border-gray-300 rounded-2xl bg-[#FFFFFF] focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" |
62 | | - placeholder="Search for a transaction using Transaction Id" |
63 | | - required |
64 | | - /> |
65 | | - |
66 | | - <p |
67 | | - className="text-[#9E9E9E] hidden md:block border absolute right-11 bottom-3.5 bg-white focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg shadow-sm text-sm px-2.5 py-1 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" |
68 | | - > |
69 | | - Ctrl |
70 | | - </p> |
71 | | - <p |
72 | | - className="text-[#9E9E9E] hidden md:block border absolute right-2.5 bottom-3.5 bg-white focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg shadow-sm text-sm px-2.5 py-1 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" |
73 | | - > |
74 | | - K |
75 | | - </p> |
| 163 | + ); |
| 164 | + case 'UserOperations': |
| 165 | + return ( |
| 166 | + <div style={{ padding: '0px 16px 16px 16px' }}> |
| 167 | + <Table |
| 168 | + {...(operationsTables[networkValue !== -1 ? NETWORK_LIST[networkValue].key : 'mainnet'] as tableDataT)} |
| 169 | + columns={OperationsTable['columns']} |
| 170 | + loading={userOpTableLoading} |
| 171 | + key="recentUserOpsTables" |
| 172 | + /> |
| 173 | + </div> |
| 174 | + ); |
| 175 | + case 'Bundlers': |
| 176 | + return ( |
| 177 | + <div style={{ padding: '0px 16px 16px 16px' }}> |
| 178 | + <Table |
| 179 | + {...(bundlersTables[networkValue !== -1 ? NETWORK_LIST[networkValue].key : 'mainnet'] as tableDataT)} |
| 180 | + columns={BundlersTable['columns']} |
| 181 | + loading={bundlerTableLoading} |
| 182 | + key="topBundlersTable" |
| 183 | + /> |
| 184 | + </div> |
| 185 | + ); |
| 186 | + case 'Paymaster': |
| 187 | + return ( |
| 188 | + <div style={{ padding: '0px 16px 16px 16px' }}> |
| 189 | + <Table |
| 190 | + {...(paymastersTables[networkValue !== -1 ? NETWORK_LIST[networkValue].key : 'mainnet'] as tableDataT)} |
| 191 | + columns={PaymastersTable['columns']} |
| 192 | + loading={paymasterTableLoading} |
| 193 | + key="topPaymastersTable" |
| 194 | + /> |
| 195 | + </div> |
| 196 | + ); |
| 197 | + default: |
| 198 | + return null; |
| 199 | + } |
| 200 | + }; |
| 201 | + |
| 202 | + return ( |
| 203 | + <div className="w-full max-w-[1080px] mx-auto mt-12 md:mt-48 px-4 md:px-0"> |
| 204 | + <SearchBox networkValue={networkValue} setNetworkValue={setNetworkValue} /> |
| 205 | + |
| 206 | + <div className="-mt-2 w-full max-w-[1080px] p-4 bg-[#F3F4F7] rounded-b-2xl shadow-md"> |
| 207 | + <div className="flex flex-wrap"> |
| 208 | + <h1 className="hidden md:block p-4 text-md text-[#959595]">SEE LATEST</h1> |
| 209 | + {['Bundles', 'UserOperations', 'Bundlers', 'Paymaster'].map((badge, index) => ( |
| 210 | + <div key={index} className="m-2 gap-4"> |
| 211 | + <button |
| 212 | + className={`px-6 py-2 rounded-full text-ph ${ |
| 213 | + activeTable === badge |
| 214 | + ? 'bg-[#6C47FF] border-2 font-medium text-white' |
| 215 | + : 'border-2 font-regular text-black' |
| 216 | + }`} |
| 217 | + onClick={() => { |
| 218 | + setBadgeValue(badge); |
| 219 | + setActiveTable(activeTable === badge ? null : badge); |
| 220 | + setLoading(false); |
| 221 | + }} |
| 222 | + > |
| 223 | + {badge} |
| 224 | + </button> |
| 225 | + </div> |
| 226 | + ))} |
| 227 | + </div> |
| 228 | + <div>{loading ? <p className="mt-4">Loading...</p> : activeTable && <div className="mt-4">{renderTable()}</div>}</div> |
76 | 229 | </div> |
77 | | - </form> |
| 230 | + <ToastContainer /> |
| 231 | + </div> |
78 | 232 | ); |
79 | 233 | }; |
80 | 234 |
|
|
0 commit comments