Merge pull request #58 from blockworks-foundation/account-search

account search
This commit is contained in:
tylersssss 2023-01-11 14:28:46 -05:00 committed by GitHub
commit f54787e955
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 322 additions and 3 deletions

View File

@ -11,6 +11,7 @@ import {
ArrowsRightLeftIcon,
ArrowTrendingUpIcon,
XMarkIcon,
MagnifyingGlassIcon,
} from '@heroicons/react/20/solid'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
@ -27,7 +28,7 @@ import { useTheme } from 'next-themes'
import { IconButton } from './shared/Button'
const SideNav = ({ collapsed }: { collapsed: boolean }) => {
const { t } = useTranslation('common')
const { t } = useTranslation(['common', 'search'])
const { connected } = useWallet()
const group = mangoStore.getState().group
const { mangoAccount } = useMangoAccount()
@ -120,6 +121,15 @@ const SideNav = ({ collapsed }: { collapsed: boolean }) => {
pagePath="/fees"
hideIconBg
/> */}
<MenuItem
active={pathname === '/search'}
collapsed={false}
icon={<MagnifyingGlassIcon className="h-5 w-5" />}
title={t('search:search-accounts')}
pagePath="/search"
hideIconBg
showTooltip={false}
/>
<MenuItem
collapsed={false}
icon={<LightBulbIcon className="h-5 w-5" />}

View File

@ -14,6 +14,7 @@ import {
Cog8ToothIcon,
BuildingLibraryIcon,
ArrowTrendingUpIcon,
MagnifyingGlassIcon,
} from '@heroicons/react/20/solid'
import SolanaTps from '@components/SolanaTps'
@ -103,7 +104,7 @@ const MoreMenuPanel = ({
showPanel: boolean
setShowPanel: (showPanel: boolean) => void
}) => {
const { t } = useTranslation('common')
const { t } = useTranslation(['common', 'search'])
return (
<div
className={`fixed bottom-0 z-30 h-96 w-full overflow-hidden rounded-t-3xl bg-th-bkg-2 px-4 transition duration-300 ease-in-out ${
@ -125,6 +126,11 @@ const MoreMenuPanel = ({
path="/stats"
icon={<ChartBarIcon className="h-5 w-5" />}
/>
<MoreMenuItem
title={t('search:search-accounts')}
path="/search"
icon={<MagnifyingGlassIcon className="h-5 w-5" />}
/>
<MoreMenuItem
title={t('learn')}
path="https://docs.mango.markets/"

View File

@ -0,0 +1,205 @@
import ButtonGroup from '@components/forms/ButtonGroup'
import Input from '@components/forms/Input'
import Label from '@components/forms/Label'
import Button from '@components/shared/Button'
import SheenLoader from '@components/shared/SheenLoader'
import {
ChevronRightIcon,
MagnifyingGlassIcon,
NoSymbolIcon,
UserIcon,
} from '@heroicons/react/20/solid'
import { PublicKey } from '@solana/web3.js'
import { useTranslation } from 'next-i18next'
import { ChangeEvent, useState } from 'react'
import { abbreviateAddress } from 'utils/formatting'
import { notify } from 'utils/notifications'
const SEARCH_TYPES = [
'mango-account',
'mango-account-name',
'profile-name',
'wallet-pk',
]
const SearchPage = () => {
const { t } = useTranslation('search')
const [loading, setLoading] = useState(false)
const [searchString, setSearchString] = useState('')
const [searchResults, setSearchResults] = useState([])
const [searchType, setSearchType] = useState('mango-account')
const [showNoResults, setShowNoResults] = useState(false)
const handleSearch = async () => {
try {
setLoading(true)
const response = await fetch(
`https://mango-transaction-log.herokuapp.com/v4/user-data/profile-search?search-string=${searchString}&search-method=${searchType}`
)
const data = await response.json()
setSearchResults(data)
if (!data.length) {
setShowNoResults(true)
}
} catch {
notify({
title: t('search-failed'),
type: 'error',
})
} finally {
setLoading(false)
}
}
const isAccountSearch =
searchType === 'mango-account' || searchType === 'mango-account-name'
return (
<div className="grid grid-cols-12 p-8 pb-20 md:pb-16 lg:p-10">
<div className="col-span-12 lg:col-span-8 lg:col-start-3">
<h1 className="mb-4">{t('search-accounts')}</h1>
<div className="mb-4">
<Label text={t('search-by')} />
<ButtonGroup
activeValue={searchType}
onChange={(t) => setSearchType(t)}
names={SEARCH_TYPES.map((val) => t(val))}
values={SEARCH_TYPES}
/>
</div>
<Label text={t('search-for')} />
<div className="flex space-x-2">
<Input
type="text"
name="search"
id="search"
value={searchString}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setSearchString(e.target.value)
}
/>
<Button
className="flex items-center"
onClick={() => handleSearch()}
disabled={!searchString}
size="large"
>
<MagnifyingGlassIcon className="mr-2 h-5 w-5" />
{t('search')}
</Button>
</div>
<div className="space-y-2 pt-8">
{searchResults.length || showNoResults ? (
<h2 className="mb-4 border-t border-th-bkg-3 pt-4 text-base">
{t('results')}
</h2>
) : (
''
)}
{loading ? (
<>
<SheenLoader className="flex flex-1">
<div className="h-20 w-full rounded-md bg-th-bkg-2" />
</SheenLoader>
<SheenLoader className="flex flex-1">
<div className="h-20 w-full rounded-md bg-th-bkg-2" />
</SheenLoader>
</>
) : searchResults.length ? (
searchResults.map((r) =>
isAccountSearch ? (
<MangoAccountItem item={r} type={searchType} />
) : (
<WalletItem item={r} />
)
)
) : showNoResults ? (
<div className="flex flex-col items-center rounded-md border border-th-bkg-3 p-4">
<NoSymbolIcon className="mb-1 h-6 w-6 text-th-fgd-3" />
<p className="mb-0.5 text-base font-bold text-th-fgd-2">
{t('no-results')}
</p>
<p>{t('no-results-desc')}</p>
</div>
) : null}
</div>
</div>
</div>
)
}
interface MangoAccountItem {
mango_account_name: string
mango_account_pk: string
wallet_pk: string
profile_name: string
}
const MangoAccountItem = ({
item,
type,
}: {
item: MangoAccountItem
type: 'mango-account' | 'mango-account-name'
}) => {
const { mango_account_name, mango_account_pk, profile_name } = item
return (
<a
className="default-transition flex items-center justify-between rounded-md border border-th-bkg-3 p-4 md:hover:border-th-bkg-4"
href={`/?address=${mango_account_pk}`}
rel="noopener noreferrer"
target="_blank"
>
<div className="">
<p className="mb-1 text-base font-bold text-th-fgd-2">
{type === 'mango-account'
? abbreviateAddress(new PublicKey(mango_account_pk))
: mango_account_name}
</p>
<div className="flex items-center space-x-1.5">
<UserIcon className="h-4 w-4 text-th-fgd-3" />
<p className="capitalize">{profile_name}</p>
</div>
</div>
<ChevronRightIcon className="h-6 w-6 text-th-fgd-3" />
</a>
)
}
interface WalletItem {
mango_accounts: { mango_account_name: string; mango_account_pk: string }[]
owner: string
profile_name: string
}
const WalletItem = ({ item }: { item: WalletItem }) => {
const { mango_accounts, profile_name } = item
return (
<>
{mango_accounts.length
? mango_accounts.map((a) => (
<a
className="default-transition flex items-center justify-between rounded-md border border-th-bkg-3 p-4 md:hover:border-th-bkg-4"
href={`/?address=${a.mango_account_pk}`}
rel="noopener noreferrer"
target="_blank"
key={a.mango_account_pk}
>
<div className="">
<p className="mb-1 text-base font-bold text-th-fgd-2">
{a.mango_account_name}
</p>
<div className="flex items-center space-x-1.5">
<UserIcon className="h-4 w-4 text-th-fgd-3" />
<p className="capitalize">{profile_name}</p>
</div>
</div>
<ChevronRightIcon className="h-6 w-6 text-th-fgd-3" />
</a>
))
: null}
</>
)
}
export default SearchPage

View File

@ -8,6 +8,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
...(await serverSideTranslations(locale, [
'common',
'profile',
'search',
'settings',
])),
// Will be passed to the page component as props

View File

@ -22,6 +22,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
'common',
'onboarding',
'profile',
'search',
'settings',
'token',
'trade',

View File

@ -10,6 +10,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
'onboarding',
'onboarding-tours',
'profile',
'search',
'settings',
'swap',
'trade',

21
pages/search.tsx Normal file
View File

@ -0,0 +1,21 @@
import SearchPage from '@components/search/SearchPage'
import type { NextPage } from 'next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
export async function getStaticProps({ locale }: { locale: string }) {
return {
props: {
...(await serverSideTranslations(locale, [
'common',
'profile',
'search',
])),
},
}
}
const Search: NextPage = () => {
return <SearchPage />
}
export default Search

View File

@ -14,6 +14,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
'common',
'onboarding',
'profile',
'search',
'settings',
])),
},

View File

@ -9,6 +9,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
'common',
'onboarding',
'profile',
'search',
'settings',
'token',
'trade',

View File

@ -10,6 +10,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
'onboarding',
'onboarding-tours',
'profile',
'search',
'settings',
'swap',
'settings',

View File

@ -9,6 +9,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
...(await serverSideTranslations(locale, [
'common',
'profile',
'search',
'settings',
'token',
])),

View File

@ -5,7 +5,6 @@ import {
} from '@blockworks-foundation/mango-v4'
import TradeAdvancedPage from '@components/trade/TradeAdvancedPage'
import mangoStore, { DEFAULT_TRADE_FORM } from '@store/mangoStore'
// import mangoStore from '@store/mangoStore'
import type { NextPage } from 'next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { useRouter } from 'next/router'
@ -20,6 +19,7 @@ export async function getStaticProps({ locale }: { locale: string }) {
'onboarding',
'onboarding-tours',
'profile',
'search',
'settings',
'trade',
])),

View File

@ -0,0 +1,14 @@
{
"mango-account": "Account Address",
"mango-account-name": "Account Name",
"no-results": "No Results",
"no-results-desc": "Try another search. Searches are case-sensitive execpt for profile names which are lowercase",
"profile-name": "Profile Name",
"results": "Results",
"search": "Search",
"search-accounts": "Search Accounts",
"search-by": "Search By",
"search-for": "Search For",
"search-failed": "Failed to search",
"wallet-pk": "Wallet Address"
}

View File

@ -0,0 +1,14 @@
{
"mango-account": "Account Address",
"mango-account-name": "Account Name",
"no-results": "No Results",
"no-results-desc": "Try another search. Searches are case-sensitive execpt for profile names which are lowercase",
"profile-name": "Profile Name",
"results": "Results",
"search": "Search",
"search-accounts": "Search Accounts",
"search-by": "Search By",
"search-for": "Search For",
"search-failed": "Failed to search",
"wallet-pk": "Wallet Address"
}

View File

@ -0,0 +1,14 @@
{
"mango-account": "Account Address",
"mango-account-name": "Account Name",
"no-results": "No Results",
"no-results-desc": "Try another search. Searches are case-sensitive execpt for profile names which are lowercase",
"profile-name": "Profile Name",
"results": "Results",
"search": "Search",
"search-accounts": "Search Accounts",
"search-by": "Search By",
"search-for": "Search For",
"search-failed": "Failed to search",
"wallet-pk": "Wallet Address"
}

View File

@ -0,0 +1,14 @@
{
"mango-account": "Account Address",
"mango-account-name": "Account Name",
"no-results": "No Results",
"no-results-desc": "Try another search. Searches are case-sensitive execpt for profile names which are lowercase",
"profile-name": "Profile Name",
"results": "Results",
"search": "Search",
"search-accounts": "Search Accounts",
"search-by": "Search By",
"search-for": "Search For",
"search-failed": "Failed to search",
"wallet-pk": "Wallet Address"
}

View File

@ -0,0 +1,14 @@
{
"mango-account": "Account Address",
"mango-account-name": "Account Name",
"no-results": "No Results",
"no-results-desc": "Try another search. Searches are case-sensitive execpt for profile names which are lowercase",
"profile-name": "Profile Name",
"results": "Results",
"search": "Search",
"search-accounts": "Search Accounts",
"search-by": "Search By",
"search-for": "Search For",
"search-failed": "Failed to search",
"wallet-pk": "Wallet Address"
}